摘要:以xna為基礎的遊戲可以利用3d模型為遊戲加入動畫效果,也可以利用簡單的程式技巧将2維圖檔顯示成動畫。雖然2維動畫相對3維動畫來說簡單一些,但是制作出來的遊戲其趣味性和挑戰性也絕不遜色。今天我們就一塊學習一下在使用xna framework開發2d遊戲時的一些基礎知識和注意事項。
主要内容:
1.2d遊戲動畫的基本原理
2.動畫素材的準備
3.一個簡單的2d動畫
在xna中制作2d動畫的過程很像翻卡通小人書,首先繪制好各種角色造型,然後以固定的時間間隔來顯示不同的圖檔,當時間間隔小到一定程度時人眼就難以區分,進而形成一種動畫效果。如果做過flash或者使用fireworks做過gif的朋友可能更容易了解,它就像flash中的逐幀動畫一樣,每幀顯示一個圖檔,然後連起來就形成了動畫效果。由于在xna中有spritebatch可以繪制圖檔精靈,這樣一來我們隻需事先做好不同的角色造型的圖檔,每次在執行update方法的時候改變繪制的精靈圖檔就可以制作出動畫效果。
xna開發2d動畫的原理比較容易了解,我也就不過多的贅餘了,現在我們就開始準備遊戲素材。xna的spritebatch的繪制方法有點類似于樣式表css的background屬性,可以通過設定參數來顯示一個圖檔的其中一部分,這樣一來我們為了友善管理遊戲角色就隻需要将同一角色的不同造型放到同一張圖檔中,每次更新時顯示圖檔的不同部分就可以了。例如下面這幅圖檔就是如此:
在這幅圖檔中我們将22個不同造型的角色放到同一幅圖檔中,事先規劃好每個小圖檔的大小,保證各個圖檔連接配接起來播放時能夠形成一組連貫的動作。當然,為了讓整個效果看起來更生動一些我們再來準備一張背景圖檔:
有了上面的遊戲素材,我們就來簡單模拟一下植物大戰僵屍中的一個場景。在下面的demo中表現了兩種運動:其一就是我們要控制22個小圖檔之間的切換,以此産生角色行動的動畫(在原地);其二就是控制圖檔切換的同時我們需要移動圖檔的位置進而形成角色在水準方向上行走動作。具體代碼如下:
1 using system;
2 using system.collections.generic;
3 using system.linq;
4 using microsoft.xna.framework;
5 using microsoft.xna.framework.audio;
6 using microsoft.xna.framework.content;
7 using microsoft.xna.framework.gamerservices;
8 using microsoft.xna.framework.graphics;
9 using microsoft.xna.framework.input;
10 using microsoft.xna.framework.input.touch;
11 using microsoft.xna.framework.media;
12 namespace _2danimation
13 {
14 public class mygame : microsoft.xna.framework.game
15 {
16 graphicsdevicemanager graphics;
17 spritebatch spritebatch;
18 private texture2d corpse;//圖檔資源
19 private point framesize;//每個小圖檔大小
20 private point sheetsize;//小圖檔個數
21 private point currentframe;//目前小圖檔位置
22 private vector2 postion;//角色所在位置
23 private float speed;//角色行走速度
24 private texture2d background;
25 public mygame()
26 {
27 graphics = new graphicsdevicemanager(this);
28 content.rootdirectory = "content";
29 graphics.preferredbackbufferwidth = 800;//設定遊戲視窗大小
30 graphics.preferredbackbufferheight = 480;
31 targetelapsedtime = timespan.fromticks(333333*2);
32 }
33 protected override void initialize()
34 {
35 framesize = new point(100, 125);
36 sheetsize = new point(11, 2);
37 currentframe = new point(0,0);
38 postion = new vector2(graphics.preferredbackbufferwidth-100,graphics.preferredbackbufferheight - 125-150);
39 speed = 3;
40 base.initialize();
41 }
42 protected override void loadcontent()
43 {
44 spritebatch = new spritebatch(graphicsdevice);
45 background = this.content.load<texture2d>("background");//加載遊戲背景
46 corpse = this.content.load<texture2d>("transparentcorpse");//加載整張大圖檔(透明背景)
47 }
48 protected override void unloadcontent()
49 {
50 }
51 protected override void update(gametime gametime)
52 {
53 if (gamepad.getstate(playerindex.one).buttons.back == buttonstate.pressed)
54 this.exit();
55 ++currentframe.x;
56 if (currentframe.x >= sheetsize.x)
57 {
58 currentframe.x = 0;
59 ++currentframe.y;
60 if (currentframe.y >= sheetsize.y)
61 {
62 currentframe.y = 0;
63 }
64 }
65 postion.x -= speed;
66 if (postion.x < 20)
67 {
68 this.exit();//角色進入房屋後退出遊戲
69 }
70 base.update(gametime);
71 }
72 protected override void draw(gametime gametime)
73 {
74 graphicsdevice.clear(color.cornflowerblue);
75 spritebatch.begin(spritesortmode.backtofront,blendstate.nonpremultiplied);//所有的繪制工作均在sprite的begin和end中間進行
76 spritebatch.draw(
77 corpse,//要繪制的精靈
78 postion,//繪制的位置
79 new rectangle(currentframe.x * framesize.x, currentframe.y * framesize.y, framesize.x, framesize.y),//繪制精靈的哪一部分
80 color.white, //顔色
81 0, //旋轉角度
82 vector2.zero,//旋轉參考點
83 1, //縮放倍數
84 spriteeffects.none, //翻轉
85 0 //層深
86 );
87 spritebatch.draw(background, vector2.zero, color.white);
88 spritebatch.end();
89 base.draw(gametime);
90 }
91 }
92 }
代碼總體來說還是比較簡單,主要通過在update方法中動态修改currentframe來形成圖檔切換,當然之是以這樣是因為draw方法的第三個參數,通過它我們才能做到一張圖檔的局部顯示(具體來說就是在rectangle中指定corpse精靈的起始坐标和要顯示的圖檔寬度和高度)。圖檔的水準移動主要通過在update中動态修改position變量的值,當然這裡需要注意圖檔超出遊戲視窗的情況。下面是運作效果:
截圖效果:
動畫效果:
值得一提的是,我們這裡牽扯到了圖檔顯示的層深問題,如果注意看代碼的朋友會發現我們并沒有首先繪制遊戲背景,而是先繪制了遊戲角色(當然我是為了說明層深問題而故意這麼做的)。預設情況下載下傳xna會按照程式調用順序繪制精靈,這樣我們就看不到角色了,因為它被背景圖檔遮住了。如果說遇到了這種情況(事實上遊戲開發中經常遇到)我們就需要設定圖檔的層深,也就是我們上面代碼中draw方法的最後一個參數。它是float類型,其取值範圍是0-1。具體0是代表最先繪制還是最後繪制則完全取決于spritebatch的begin方法中的第一個參數,這個參數是spritesortmode,他是一個枚舉類型,有五種模式:
取值
描述
deferred
這是預設模式,此模式下隻有在spritebatch調用end方法時才會被繪制,如果繪制多個精靈則按照調用次序依次進行。
texture
同deferred類似,在繪制之前會按照紋理排序,對于相同深度而不互相重疊的情況下效果較好。
backtofront
在end方法執行後繪制精靈,繪制精靈的順序按照層深度由前往後排序(先繪制層深值較小的)。
fronttoback
在end方法執行後繪制精靈,繪制精靈的順序按照層深度由後往前排序(先繪制層深值較大的)。
immediate
調用begin方法後就會立即繪制精靈(這也是五種模式中唯一在end方法之前就進行繪制的),同一時間隻有一個spritebatch被使用。
另一點需要注意的是,我們的角色是透明的,在xna game開發中如果想要讓圖檔透明有兩種方式:第一就是圖檔本省就是透明,此時當然需要選用能夠存儲圖像alpha的圖檔格式來儲存圖檔(例如png格式可以将圖像的alpha存儲到圖檔資訊中,我們上的例子中就是這麼做的。事實上gif也可以,但是在xna中預設不支援gif);第二種方式就是将圖檔需要顯示成透明的部分設定為洋紅色(rgb:255,0,255),這樣xna會自動将洋紅色部分渲染成透明。
ok,最後附上代碼下載下傳: