在所有的圖形引擎中,繪制都是最基礎的部分,本文將介紹在XNA框架中與繪制相關的基礎知識。
在XNA中,我們使用SpriteBatch來進行繪制。首先,我們需要使用SpriteBatch來繪制什麼了?是精靈 Sprite,對。
那麼Sprite通過什麼來表現了?是紋理,比如2D紋理Texture2D。嗯,你可以把紋理想象成Sprite的外 表,比如我們制作的一幅精靈圖片,就是一個紋理。
我們要如何才能把一幅圖片加載到我們的游戲中來作為一個Sprite的紋理了?這要通過素材管道 Content Pipeline。所謂素材,包括我們游戲中要用到的圖片、模型、聲音等,比如一個紋理圖片就是素 材的一種。素材管道就很容易理解了,它可以把我們需要的素材導入到我們的游戲中,以XNA能夠識別的 正確的格式。而且,格式的識別與轉換是在編譯期完成的。
在新建的XNA項目中,會有一個默認的Content文件夾,通常,我們會把所有的素材放在這個地方,並 且根據素材的種類我們會在其下創建一些子文件夾,比如Image子文件夾用來存放所有的圖片素材,Audio 文件夾用來存放聲音素材等。
當一個物件別被識別為素材後,就會有一個唯一的資產名稱AssetName來標記它,我們可以通過素材的 屬性頁來查看素材的AssetName屬性並修改它。
下面我們可以用素材管理器ContentManager將一個素材導入到游戲中:
Texture2D texture;
texture = Content.Load<Texture2D>(@"Image\logo"); //在上文提到的LoadContent方法中調 用
很多時候,我們的圖片需要是透明的,那麼如何在SpriteBatch繪制的時候將圖片繪制為透明了?有兩 種方法:要麼圖片具有透明的背景,要麼在制作圖片時將需要透明的部分設置為純正的洋紅色(255,0, 255)。除此之外,還需要注意,SpriteBlendMode模式必須為AlphaBlend(這也是默認模式,稍後會提到 ),才能達到我們期望的透明效果。
在進行渲染時,我們還需要注意層深度(Layer Depth)。所謂Layer Depth,指的是你需要將目標 Sprite繪制在哪一個深度的層次。默認情況下,SpriteBatch會根據你調用的順序來決定Layer Depth的相 對值,比如,你先調用SpriteBatch繪制Sprite A,然後調用SpriteBatch在同一位置繪制Sprite B,那麼 ,Sprite B就會把Sprite A擋住。如果我們依靠調用SpriteBatch繪制的順序來決定Sprite的深度,那就 太不靈活了,為此,調用SpriteBatch繪制方法時,你可以指定一個代表層次深度的參數(范圍0~1)來指 示SpriteBatch按照我們希望的順序來繪制對象。
有了上面的這些基礎之後,我們可以詳細講解一下XNA中的渲染過程。每次渲染的過程都類似下面的模 式:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
spriteBatch.Draw(……);
spriteBatch.End();
base.Draw(gameTime);
}
(1)GraphicsDevice.Clear()方法用於清除屏幕背景。
(2)SpriteBatch.Begin()方法用於通知顯卡准備繪制場景。
(3)輪流調用SpriteBatch.Draw()繪制所有組件。
(4)SpriteBatch.End()方法用於告訴顯卡繪制已經完成。
(5)最後,顯卡顯示繪制的結果。
SpriteBatch.Begin()方法有一個接受參數的重載:
void Begin(SpriteBlendMode blendMode, SpriteSortMode sortMode, SaveStateMode stateMode, Matrix transformMatrix) ;
SpriteBlendMode 參數決定了Sprite的顏色與背景顏色的混合模式,默認為AlphaBlend,我們剛才提 到過。
SpriteSortMode參數決定了Sprite的排序模型,即與前面的Layer Depth關聯。
SaveStateMode參數用於控制是否保存圖形設備的狀態。
TransformMatrix參數用於控制旋轉、縮放等。
接下來我們看最重要的繪制Draw方法:
void Draw(Texture2D texture, Vector2 position, Rectangle? sourceRectangle, Color color, float rotation, Vector2 origin, float scale, SpriteEffects effects, float layerDepth);
第一個Texture2D參數,表示要繪制Sprite的紋理。
第二個Vector2參數,表示要繪制的Sprite的左上角的位置在哪裡 。
第三個Rectangle參數,表示要繪制源紋理的哪一個區域,如果要繪制紋理的全部部分,則傳null。
第四個Color參數,表示繪制時所調制的顏色通道。
第五個參數rotation,表示要旋轉的角度。
第六個參數origin,表示圍繞哪個位置進行旋轉。
第七個參數scale,表示繪制時要縮放的比例。
第八個參數effects,表示是否進行翻轉。SpriteEffects.FlipHorizontally -- 水平翻轉; SpriteEffects.FlipVertically -- 垂直翻轉。
最後一個參數layerDepth,就是我們前面說過的要在哪一個深度層次繪制對象。
最後,要解釋一下,為什麼需要在每次Draw調用時,都先調用GraphicsDevice.Clear()方法清除屏幕 然後再重新繪制所有物件了?這樣性能不是很差嗎?而且有時候我們可能只有極小的一部分需要重新繪制 。
假設我們每次只重新繪制變動的那一部分,那我們就需要一個復雜的管理器來追蹤每一個部分的變動 ,這樣我們才有可能知道要重繪哪一部分。你可以想象一下,這個追蹤的過程是相當復雜的。而且還存在 這樣的情況,那就是當一個Sprite移動時,它後面原先被擋住的Sprite會露出來,而這個Sprite可能又會 擋住另外一個Sptrite的一部分,這樣一來就更加復雜了。所以,每次重繪整個屏幕並不是一個壞的idea 。
今天就講到這裡,下一節我們將講述與FrameRate相關的知識。