Cocos2D-X for XNA遊戲開發指南

pamxy發表於2013-11-10

轉自:http://elvisco.de/2012/08/利用cocos2d-x-for-xna快速開發windows-phone遊戲/

注:本文已發表在《程式設計師》雜誌第六期,參見:http://www.programmer.com.cn/11833/

新生的移動平臺Windows phone

說Windows phone是一個新生的移動平臺,已經不那麼準確了,早在2010年微軟就正式公佈了windows phone 7作業系統,但對於國內市場來講,今年才是windows phone正式進入中國市場的開始。2012年3月21日,微軟正式在北京召開了windows phone 7.5釋出會,宣佈windows phone正式登陸中國市場。隨後諾基亞和中國電信也宣佈了Lumia手機將在中國全面上市。

Windows phone擁有全新的Metro風格,硬體標準化以及以程式設計方式對其進行訪問的標準化,深度整合的社交功能,以及對Office的支援等等。也是微軟重拳進入移動網際網路的重要武器。截止5月,WP7的Marketplace已經有8萬多款應用,毫無疑問已經建立起了手機應用的第三大生態圈,僅次於Android和ios平臺。

Windows phone應用開發

我們知道,在Windows phone系統上開發應用程式有兩種選擇:Silverlight for Windows phone和XNA Framework。

Silverlight是一個基於XAML的事件驅動的應用程式框架,XAML是一種描述Silverlight介面元件的標記語言,利用Expression Blend工具可以設計出非常絢麗的應用程式外觀。Silverlight整合了一些傳統的控制元件,高品質的文字,向量圖,媒體動畫效果及資料繫結等功能。Silverlight適用於開發具有固定頁面外觀的應用程式,如新浪微博。

XNA Framework的設計初衷是為了讓遊戲開發變得更簡單,XNA主要用來編寫高效能遊戲,它同時支援2D和3D。使用XNA,你不必考慮建立視窗以及管理它,你需要關注的就是你的遊戲邏輯,XNA提供了圖形裝置訪問的介面,另一方面XNA對於遊戲中很重要的資源的管理做了統一的優化處理,內容管道將會為你處理內容的匯入,編譯和載入。同時,XNA也是一個跨平臺的遊戲框架,你可以輕鬆的移植遊戲到Xbox360,PC和Windows phone裝置上。

本篇,我們主要討論基於遊戲輪詢的XNA遊戲開發。

Cocos2d遊戲引擎進入Windows phone平臺

我們看到,XNA已經幫助我們處理了很多事情,大大的降低了遊戲開發的難度,例如不需要管理視窗,統一的內容模型,以及使用託管的C#語言開發遊戲。然而在遊戲邏輯之外,XNA只是提供了一些基本的程式設計介面,我們還需要維護遊戲介面各種元素的層級關係,實現精靈在遊戲中的各種炫酷的動作,甚至於建立一個複雜的地圖,使用粒子效果等。這些都是一個遊戲引擎實現的功能,然而Windows phone平臺一直缺乏比較好的遊戲引擎。

在iOS平臺,cocos2d是一個被公認的優秀的2D開源遊戲引擎,截止目前,已知的有3600多款遊戲採用了該引擎。在各大移動終端百花齊開的時代,跨平臺已經成為遊戲廠商重要的關注點,cocos2d-x正是基於coco2d-iphone採用C++移植的版本,支援包括iOS,Android,Bada,BlackBerry Playbook,Windows XP,Windows 7,Linux等多個平臺。使遊戲開發者能夠以同一套API、甚至同一套程式碼將遊戲分發至多個遊戲平臺,大大減少多個平臺之間的研發和維護成本。目前已經有200多款使用cocos2d-x實現跨平臺的遊戲,其中不乏Zynga,Glu,Disney Mobile,空中網,網龍,Chillingo各手遊大廠的作品,和大量榮登AppStore Top10的中小團隊乃至個人開發者作品,去年以來國內大熱的《捕魚達人》也正是基於cocos2d-x引擎開發。

正是在此背景下,cocos2d-x社群聯合國內知名的Windows phone社交遊戲平臺OpenXLive移植了coco2d-x for XNA版本,大大降低了在Windows phone平臺上開發遊戲的難度。

Cocos2d-x for XNA概述

2012年2月17日,cocos2d-x for XNA釋出了第一個beta版本,目前已經是0.1.1的release版。

在移植過程中,不可避免的是,XNA版本會有比較大的變化,因為它不僅僅是由C++翻譯為C#,底層的繪製介面也由OpenGL變換為XNA。即便如此,在翻譯過程中我們還是保持了大部分介面一致,以至於大部分的程式碼都可以由cocos2d-x輕鬆1:1轉換為cocos2d-x for XNA。

相信大家也看過不少cocos2d-x引擎相關的教程文件,cocos2d引擎各個版本保持介面一致,這裡我就不重點介紹引擎結構,我將結合移植過程給大家分享一些cocos2d-x在XNA版本的一些變化及注意事項。後面我會以一個例項帶大家體驗cocos2d-x for XNA快速開發一個簡單的小遊戲。

我將沿著主要類的變化來分析XNA版本的變化,在每個變化的類裡,我將說明一些變化的地方,以及為什麼會有這樣一些變化,主要涉及的類包括:

  1. CCApplication
  2. CCNode
  3. CCSprite
  4. CCSpriteBatchNode
  5. CCImage
  6. CCRenderTexture
  7. CCSAXParser
  8. Zip Support

CCApplication的變化

關於CCApplication,主要有三方面的變化:

一方面是因為XNA中去掉了視窗管理,所以去掉了CCEGLView相關的類。

第二是遊戲輪詢結構的變化。在XNA中,CCApplication是繼承於DrawableGameComponent,所以去掉了run方法中實現的迴圈,因為GameComponent本身就實現了遊戲迴圈結構。在XNA版本中,CCApplication的Update過載方法處理Touch事件,Draw過載方法呼叫CCDirector的mainLoop方法進入遊戲遞迴繪製元素。

另一個比較重要的變化是,CCApplication增加了一些XNA繪製相關的全域性屬性,包括:

  1. ContentManager:用於管理資源的載入。
  2. SpriteBatch:用於繪製2D的紋理。
  3. WorldMatrix:當前著色器的世界矩陣。
  4. ViewMatrix:當前著色器的檢視矩陣。
  5. ProjectionMatrix:當前著色器的投影矩陣。
  6. basicEffect:用來繪製元素的著色器。

這些屬性主要用於繪製,它們會被CCSprite,CCSpriteBatchNode以及CCTextureAtlas等與繪製相關的類使用。如果你的遊戲中需要自己繪製2D或者3D的元素,也可以直接呼叫這些屬性進行繪製。

CCNode的變化

在cocos2d中,CCNode提供了cocos2d中各個元素的一些公共屬性,節點的層級結構,以及主要實現元素遞迴繪製的邏輯。由於cocos2d中每個節點的位置和變換是相對於父節點的,因此CCNode還承擔重要責任,那就是調整著色器的世界矩陣變換。

在OpenGL中,有一個當前矩陣的概念,並且有一個矩陣棧,當開始繪製CCNode的時候,將當前矩陣壓入棧,然後根據當前CCNode的位置,縮放,旋轉,扭曲等屬性進行一個變換計算,然後將當前矩陣左乘這個變換,就可以實現相對父元素繪製節點。

然後在XNA中沒有當前矩陣的概念,因此需要模擬一個這樣的概念,因此在XNA版本中做了一點變化。

只不過這裡並沒有實現一個矩陣棧,因為在這裡的矩陣棧都是通過一些列的左乘形成的,只要按相反的順序左乘對應矩陣的逆矩陣,就可以返回棧中的矩陣。所以這裡新增一個Matrix型別的變數m_tCCNodeTransform來儲存每個CCNode的變換,然後:

  1. 在transform方法的末尾,將著色器的世界矩陣左乘以該矩陣,以將著色器變換到當前元素相對於父元素的位置: app.basicEffect.World = m_tCCNodeTransform * app.basicEffect.World;
  2. 在visit方法的末尾,當前元素繪製完畢時,將著色器的世界矩陣左乘該矩陣的逆矩陣,以將著色器變換回到上一級元素的位置: asicEffect.World = Matrix.Invert(m_tCCNodeTransform) * CCApplication.sharedApplication().basicEffect.World;

除此之外,CCNode沒有其他變化。

XNA中的繪製

在XNA中繪製3D(注意,cocos2d雖然是2D引擎,但底層是採用3D繪製的),歸納起來有以下幾個步驟:

  1. 構造一個basicEffect,這在CCApplication的初始化中構造。
  2. 設定攝像機的檢視矩陣和投影矩陣,這在CCDirector中的Projection屬性賦值時設定。
  3. 給basicEffect的Texture屬性賦值一個紋理Texture2D。
  4. 構造頂點陣列VertexPositionColorTexture,描述每個點的位置,顏色等資訊。
  5. 呼叫DrawUserIndexedPrimitives或相應的方法進行繪製。

繪製的部分,幾乎都體現在CCSprite.Draw方法中,當然在Draw方法中還涉及到許多引數的設定,比如混合因子,透明度等等。

以上幾乎是cocos2d-x for XNA所有繪製的部分,所不同的是在CCSpriteBatchNode和CCTextureAtlas中是使用一個紋理同時繪製多個精靈。

而所有跟繪製相關的變化,基本上都封裝在Draw方法中,比如將cocos2d中的Quads陣列轉化為VertexPositionCOlorTexture陣列,cocos2d中的混合因子轉化為XNA中的混合因子,這樣使得其他方法基本上不發生變化。

關於管道和資源

在win32或者大多數開發場景中,資源(聲音,音效,圖片,3D模型等)一般都是以檔案流的形式在執行時載入到程式中,然後由一些內容匯入器對特定格式的檔案進行解析處理,系統往往提供一些受支援的格式,另外一些特殊或自定義格式檔案則需要開發者自行處理。並且不同的平臺處理內容的方式還不太一樣,例如在Xbox 360和Windows phone上對聲音的支援都不一樣。

在XNA中,為了簡化遊戲內容的載入,這部分被抽象成一種叫做內容模型(Content Pipeline)。當你把檔案新增到XNA Studio專案中,他們自動被處理,編譯成適合當前選定平臺的輸出格式。不過這樣比較麻煩的是它要求每一個原始內容檔案格式都要被支援。

由於cocos2d主要是由iphone發展而來,它支援的許多都是ios中常用的格式,如.plist,.tmx等,這些格式的檔案在XNA中是原生不受支援的,因此需要實現自定義的管道用於編譯和載入這些型別的檔案。

所以在cocos2d-x for XNA中新增了一個cocos2d.Content.Pipeline.Importers的工程,用來處理這些格式的檔案。但是這裡做得比較簡單,僅僅是直接轉化成文字,所有的解析都需要在執行時完成。

如圖,我們看到選中了自己的匯入器和處理器TextImporter和TextProcessor。所以在開發者自己新增plist或者tmx等格式中,都需要按照tests專案中該類檔案一樣的設定,以保證能夠被引擎正確解析。

不過,這種處理方式並不標準,它只能用於引擎內部使用,如果開發者使用自定義的plist檔案,開發者不得不在程式中自己去解析,就像引擎所做的那樣,所以後續我們也會考慮在管道里直接做得通用一些,因為像自定義plist的檔案使用非常頻繁。

另外一點需要注意的是,在win32中,。.fnt,.plist,.tmx等格式檔案中對應的圖片資源基本上都是和該描述檔案處於同一目錄,但是在XNA中,資源的識別不包括檔名擴充套件,所以這三類描述檔案對應的圖片資源都需要放在同級目錄的images資料夾下才能被引擎正確解析。

關於集合和陣列

Cocos2d中實現了對集合和陣列的處理,例如CCArray和CCSet。其實對於其他一些型別如CCPoint,CCDictionary,CCSet等cocos2d也有自己的封裝,這樣做的好處是在不同平臺對上層的介面保持一致,同時避免不同平臺這些型別的實現差異,將這些差異隱藏在引擎內部而使介面保持一致。

儘管如此,當初在移植的時候我們還是選擇了用C#本身的Array和List型別代替了cocos2d中的CCArray和CCSet,這樣做的目的是當時為了趕進度,然而有很多開發者反應對這些封裝的改變導致移植上需要改變很多地方,所以後期我們還是儘量改回cocos2d原生的封裝型別上去。

然而這裡需要強調的是開發者自己在移植過程中需要注意的地方,這也是cocos2d-x for XNA引擎在移植過程中遇到比較大的問題,那就是關於C++在對陣列上應用指標來索引。

例如我們看看C++中初始化陣列的方法: m_pVertices = malloc((m_sGridSize.x+1) * (m_sGridSize.y+1) * sizeof(ccVertex3F));

在C#中對應則是: m_pVertices = newccVertex3F[(m_sGridSize.x + 1) * (m_sGridSize.y + 1)];

在C++中我們可以通過指標定位到陣列的不同位置,如果你知道某個指標的資料型別,你可以轉化為對應的型別進行使用,同一個指標可以轉化為不同型別的陣列。而C#中則不能使用指標(在Windows phone中不能使用不安全程式碼),這些通過指標索引和操作的方式,全部需要改為基於陣列索引和使用強型別,所以這會導致移植上的一些工作量。

其他變化

前面我們總結了一些cocos2d-x for XNA版本中比較大的變化和注意事項,剩下還有其他一些小的細節,比如CCSAParser中採用XMLElement流式讀取文件的方式解析資料,對於gzip和zlib的支援採用了SharpZipLib和zlib兩個類庫,同時將get和set方法改為C#中的屬性等等,大家可以慢慢去發現。


相關文章