經典游戲制作教程


 

經典游戲制作教程
1.游戲制作的主要流程
-------------------------------------------------------------------------------

電腦游戲開發小組中的任何一個人(這個角色通常有策划擔任),只要有了一個新的想法 或念頭,就孕育着一個新游戲的誕生。在這個創意被充分討論之后,再加上對其操作過程的趣味性及市場銷售的可行性的預測等因素的准確判斷,一個完整的策划方案才可能產生。在經過充分的討論后,策划人員必須將討論的重點寫成文字,也就是提出完整的策划方案,經決策者同意認可后,才能進下一步的工作。這份策划方案就像一部電影的劇本,它必須完整地涵蓋整個游戲的故事、流程、內容、方式、游戲畫面、角色造型、 場景規划、人工智能、硬件配備、市場評估等。對整個游戲過程的詳細描述及實施規划都應 記錄在案。 當進入創作過程之后,策划還必須隨時和美術設計師和程序設計員保持聯系,以免游戲程序的編寫失控。策划應能對游戲設置的內容與精神了如指掌,與各個小組及時溝通,並且控制整個游戲制作的進程。

 

2.游戲設計基本論
-------------------------------------------------------------------------------   

   要設計一個游戲,首先你必須要確定幾個重要方針,第一是你要設計的游戲是屬於那一種類型,第二是時代背景,第三是模式,第四是程式技術,第五是表現手法,第六是市場定位,第七是研發時間,在掌握上述七個方針之後,你就可以再做詳細的規划內容及調配資源,那麽何謂是七項方針呢? 筆者以范例來說明之!
一、類型:
   所謂的類型是指這個游戲所着眼的一個游戲方式,通過這個方式來使玩者達到娛樂的目的,這個游戲方式有專有名詞來各別予以命名,茲如下述:

(1) RGP角色扮演:
   這個類型的游戲以通過故事劇情牽引來使玩家能溶入主角所存在的一個世界,這類型態的游戲多半透過戰斗升級系統及人物對話的方式來一步步完成設計者所布下的劇情路線,最具代表的作品有日本史克威爾所設計的 "太空戰士系列" 及國內大宇資訊所設計的"仙劍奇俠傳",當然還有很多部作品例如"神奇傳說"等也是此中的佼佼者。
   在RGP的類型中,在近幾年來又分支了幾個類似的型態,例如說Blizzard的"暗黑破壞神"Dirblo"被定位為"動作RPG",因其動作成分相當高所至,而"神奇傳說"、"超時空英雄傳說"則被定位盡"戰略RPG",只因戰略成分比重較高所以又有別於傳統RPG。

(2) SLG戰略:
   談起戰略游戲,大家最耳熟能詳的應是日本光榮公司所出品的"三個系列",KOEI的三國志風靡東亞,從一代進化到現階段的六代皆為玩家們所津津樂道,而所謂的戰略游戲則是透過經營→戰爭→擴大領土三個手段來蠃得游戲最終目標,一般而言動態成分少,最較偏重於花費腦力的游戲,但從WestWood的新型態戰略游戲"沙丘魔堡"問世之後,戰略游戲也有了重大的分野,一是以KOEI代表的三國志系列被稱為回合制戰略游戲,一是以WestWood代表的C&C及Blizzard所代表的魔獸爭霸被稱為即時制戰略游戲,和回合制所不同的是,即時制擁有較多可由玩家與電腦互動的機會,比較不花費腦力,所進行的 方式是建設→生產→攻擊→殲滅,在業界有句俏皮話是這樣說的:「玩回合制游戲像是自己當了個大將軍(元首),運籌帷幄決勝千里之外,而玩即時制游戲則像是個士官長(部隊指揮官),只能一味的打打殺殺」由此你可以了解到這兩個型態的異同的了。

 

(3) ACT動作:
   所謂的動作游戲其實就完全靠玩家的反應來做過關的條件,較有名的像DOOM、古墓奇兵、QUAKEⅡ 等,在動作游戲中也分支了相當多的類型,例如快打旋風、鐵拳Ⅲ等被定位為格斗型態,主要游戲方式就是二人到四人互相對打一直到分出勝負為止,而DOOM、古墓奇兵則被定位為3D動作冒險游戲,主要目的為殺敵闖關,再來像阿比逃亡記、黑暗之心被定位為橫向卷軸游戲,游戲方式就是以攻擊跳躍等動作來走過一連串的關卡,表現方式多為2D卷動畫面的方式在進行,再如飛龍騎士、極上瘋狂大射擊則被定為動作射擊游戲,游戲方式就是閃躲射擊沖過火網進而殲滅守關魔王為止,這些分支型態有共 通特點卻又那樣的不同,這也是動作游戲吸引人的重要原因。

(4) PZL益智:
   這類型的游戲以趣味性的思考為游戲的主軸,內容可以包羅萬,思維模式也可朝物理性及邏輯性方向着眼,具代表性的是大宇資訊的"台灣十六張麻將"、"大富翁"、"倉庫番"等,而棋盤式的思考方式着名的有"決戰中國象棋"及光譜資訊的"五子棋大師"等,這些游戲入手容易且不分男女老少皆喜歡的特性,使得益智型態的開發較有市場,成本也較低。

(5) ADV冒險:
   冒險游戲的內涵多半脫離不了解謎的成分,是的!這類型的游戲讓玩家抽絲剖繭的找出設在游戲背後暗藏的謎底,以順利完成游戲,具代表作有惡靈古堡、異星搜奇、幽魂等,這類型的游戲年齡層較高,比較不適合國內廠商來研發。
   當你在構思一個新的游戲企划時即應預先想的所屬意的類型,然後才進行下一步的計划,一般而言國內市場接受度最高的莫過於 RPG角色扮演類型,這也是為何國內廠商會如此的大力研發RPG型態的游戲。

二、時代背景:
   對於游戲美術來說是一個很重要的方針,因為決定一個時代背景所意味的是資料的搜尋工作方便與否,與美術人員在制定造形時需依據的范例;以國內市場來說多半能接受中國古代時代背景,基本上時代背景有好幾種,例如說WestWood的紅色警戒架構在公元2000年左右的未來,而魔獸爭霸則定在虛幻的歐洲中古世紀中,三國志定位在漢朝末年,星海爭霸架構在外太空世界,軒轅劍則定在春秋戰國時代等。
時代背景絕對是企划人員在第一階段規划整個游戲時已先決定好了,如此美術人員才能放心的去搜集資料。

三、模式:
   當決定好類型及時代背景之後,再來就開始要去構思游戲中所要呈現的模式,如假設你的背景訂在古代中國,而類型是定為即時戰略,這時你必去思考出游戲內容的進行方式,可能你的游戲需要生產的因素,這個因素是什麽? 可以是糧食、礦產及木材,也可以是火山能源、石油、太陽能或天然氣等,隨着你故事情節上的需要而去制定項目,在作戰方式上你所設計的模式可能會去考慮到地形因素、天候因素及資源因素,而且會大量運用到各種戰術及攻擊方法等,因為如此所以同一種類型的游戲雖多,但模式上卻各有特色各有偏重的游戲路線,也各自聚集了擁護者,這就是模式設定的一個重要性,切記千萬不可去抄襲他人所定的模式,因為這樣一來,當你所設計的游戲完成之後,眼尖的玩家們會把你的產品以過時抄襲為由而棄如敝履,這在這劇烈競爭的國內市場而言是無法存活太久的。

四、程式技術:
   無論你對一個游戲想得多好,架構設計多龐大,如果程式人員本身的技術無法配合的話,那其實一切還是流於空談,所以在設計一個游戲之前必要先去徵詢程式人員的意見,在現在這個環境中不僅程式人員要會Windows98及Wi-ndows NT相關技術,一個完整的系統分析及系統規划是不可缺少的,如此可以避免掉在程式中不可預期的錯誤出現,而且在一個游戲設計中最好有二個程式人員在運作,一個負責內部程式 (游戲核心引擎) ,一個負責外部程式(介面程式),這樣方可發揮完整的戰力。

五、表現手法:
      在這個環節中,企划人員、程式人員、美術人員要做完善的溝通及討論,一般我們知道大部份的電腦游戲是256 色的系統,在這些游戲中對於色盤的控制有相當嚴苛的要求,為了達到最好的視覺效果,美術人員通常會向程式人員要求多重色盤的資源,而程式人員則會考量到切換時的狀況及記憶體配置是否能完全充份,在系統上的問題確定之後,企划人員會提出呈現效果的建議,例如說爆炸效果的表現方式,由內而外擴張到消失的火焰激烈型或包容大量煙霧的燃燒型,這要由企划人員依故事內容來給予定義,同時以物理性邏輯給予美術人員一個建議,再由美術人員前去繪制。
   還有一個例子,以"C&C之紅色警戒"與"AGO Empir世紀帝國"的海岸來說明,在"AGO Empir 世紀帝國"的海岸表現是靜止的,海水不會流動,最多只有魚在海中央跳躍,而"C&C"之紅色警戒"的海岸表現手法是會流動的,但海中沒有任何的特異之處,這兩種表現手法各有各的好處及考量,但以筆者而言仍較偏愛"C&C之紅色警戒"。
    游戲內容的表現手法通常伴隨着同類型游戲間的相異處而有不同的評價及支持者,而不光是美術效果的表現手法,企划人員構思的游戲玩法及程式人員的程式表現都有密切的關系。

六、市場定位:
   不論你所設計的游戲構想如何的好,如果你沒有去清楚的定位出你的市場走向,那麽到時制作完成的游戲軟體可能會面臨到銷售不佳的窘狀,所以在設計游戲之前你得知道你所定位的族群在那里,從下表中你可作一個市場定位的叄考:
年齡層               教育程度適合的類型內容

7~12歲 國小動作、益智較多趣味性、教學性
13~18歲 國中、高中動作、益智、 較多思考性質、圖形精美化同 角色扮演、戰略時又較多反射
19歲以上 低知識水平益智、動作較暴力及冒險、趣味性質,操 作簡單
19~30歲 大專、大學以上角色扮演、戰略富含多重思維性,可以影射周 、冒險、模擬、 遭事物,解謎及創造性運動 

七、研發時間:
      這是企划人員在初步規划中的最後一個項目,針對上述的制作方針你必須對美術人員及程式人員安排一個完整的SCHEDULE,從這個SCHEDULE中去研判律發時間,從企划的角度來說,為了不使良好的點子被其他游戲公司搶先推出,同時也要避免推出後模式已落伍,一個游戲的研發最好在一年內,最多不可超過18個月,以成本控制的角度來說比較符合獲利標准。
       假設你規划這個游戲需要一年的時間,那麽你就要去區分出美術制作時間 (第一線)及程式制作時(第二線)間的差異,並考量推出DEMO 版及游戲完成的時間,在適當時機打出游戲知名度,為游戲銷售上打下一記商機。

制作流程
   一個游戲的制作如果不能充份控制整個作業程序,那即有delay 的危險,大家都知道游戲軟體delay對於銷售上的影響會有多大,所以如何盡量避免de-lay是每個游戲設計者應極力去避免的,而要去避免游戲開發作業上delay的情況最重要的是嚴密控管作業流程及計划表。
   那麽究竟游戲制作流程是一個什麽樣的情形呢? 首先企划人員在執行制作的前一個月即要定出企划大綱及搜集可用資源,並經程式人員及美術人員確認後開始執行,我們以一個即時戰略的游戲來說明,在制作分期程式人員即投入地圖編輯器的撰寫,而地圖編輯器的邏輯設定要由企划人員先期規划,然後程式人員才根據企划人員的規划而進行程式寫作。
   在此同一時期美術人員即開始分工合作,一般一個游戲工作小組會有四位美術人員,他們分別負責造形、人物動作、介面、地圖四個部分來制作,但這只粗分法,國內游戲公司較常使用這樣的組合,在國外美術人員分為造形、人物動作、介面、地圖、片頭、過場、後制分鏡、場景等九大部份,每個部份皆可能都有二人以上在作業,並有一名監制在執行風格及水准的品質控管,這些人統一由後制人員來與程式人員做交圖及配合修圖等溝通上的交流,所以說後制作美術人員的成敗實關系到整個游戲品質的高低。
由於程式人員在設計地圖編輯器時需要利用到一些圖素來做測試,所以地圖圖素設計人員要先一步繪制出程式人員所需要的圖素,
   在程式人員測試通過之後方可進入大量生產的階段,由於地圖編輯器的設計者多半直接負責游戲引擎的制作,所以在初期企划人員便開始着手人工智慧AI的邏輯判斷作詳細的敘述,以期在程式人員撰寫地圖編輯器之後能立即作人工智慧AI的撰寫,而在此同時負責撰寫介面的程式人員亦與負責介面設計的美術人員作密切的配合,開始着手制作各個介面,因為介面不僅在游戲中是一個主司控制整個游戲的操作盤,同時也是一個游戲的外觀,一個擁有優良創意的介面是很受 玩家喜歡的。 在測試地圖編輯器時,程式人員亦需要利用移動物件(人)來測試地圖上的障礙物判斷及最短路徑搜尋法,所以設計人物動作的美術人員在此時要先去做出一組人物動作供程式人員作測試,待程式人員把地圖編輯器制作出來之後,人物動作設計的美術人員則只要不斷的做並不斷的把圖給程式人員即可。

 


3.游戲設計十誡律
-------------------------------------------------------------------------------
 

Travis S. Casey


1. 編寫你所喜愛的游戲
不要人雲亦雲。只要你和你的朋友喜愛就可以了。 同樣道理,不要編寫某個游戲主題僅僅因為它當前流行而已。編寫你所喜歡的題材, 這樣才能激發你的熱情。

2. 經驗是最好的老師
學習游戲編程最好的方法就是閱讀大量的游戲程序。玩和分析這些游戲,然后設計 你自己的游戲或擴展游戲。我最主要的經驗都是角色扮演類游戲,我的許多游戲范 例也來自它們,但思路卻適用於所有類型的游戲。 我閱讀過大量的RPG類游戲,粗略算算大約有七十多個。其中大部份我都玩過,精 通四十多個。不但玩和精通這些游戲,我還分析它們。是什么使得這些游戲好或者 不好?我如何修改它?哪些部份表現的出色?哪些部份不盡人意?為什么? 玩和分析過其它游戲后,我把這些知識用於我自己的游戲。比如在“超級英雄”類 游戲中,“斗士”和“英雄”在使用了“指數特性的換算法”取得較好的效果。如 果我想設計“超級英雄”類游戲時,我就知道“指數換算法”很可取。這樣的分析 能給你許多被驗證過的思想用於你的工作中。

3. 測試、測試、再測試
測試你的游戲,盡可能多次的玩。最好當你不在場的情況下,讓別人來玩,過后再 告訴他們。(讓別人當你不在的時候玩游戲稱為:“盲測”) 還有,推敲你的規則。考慮假設情況,解決概率復雜性。比如,如果你正在設計一 個RPG,試着找出平均人們用弓箭從一米、五米、十米、五十米和百米范圍內射中 人形大小目標的百分機率。對於二戰游戲,檢測你的監視器和解決一個小步兵摧毀 一輛坦克的機率。反復計算在不同的條件下,如:不同的地形、夜間等等。這將有 助於你找到在數學中出錯的地方或建立了一個不好的假設。

4.學習背景知識
如果你想編一個中世紀的神奇游戲,就要去讀中世紀的文學和歷史。讀有關魔法的 書及現有的中世紀傳奇游戲。對其它類型的游戲也是如此,如果你想做一個越南戰 爭的游戲,就應去讀有關戰爭的正史及野史,特別是戰略、戰術的分析。 所有的背景知識可以用於幾種途徑:首先,能幫助你創造出逼真的角色。另外它能 減少你在術語及背景知識方面,出現重大錯誤的可能性。當然,資料應該本身就很 有趣。如果對於所要學的都不感興趣,那為什么還要編寫這方面的游戲呢?

5. 正規教育
選一門介紹概率和統計的課。試着讀一些游戲方面的數學理論。你可能覺得那沒什 么用,但它能幫助透視算法的掌握。斟酌你的英語(或其它你發表游戲所用的語言) 當游戲描寫得好的時侯,就更容易學,至少不能有大量的語法錯誤。 如果你想制作電腦游戲而沒上過任何編程課時,不妨學幾門。你可能學不到什么編 程序的具體東西,但一門好的課程可以教會你如何組織程序使之更容易維護和發現 錯誤。 建立一個“參考文獻庫”,它是一系列和你所制作游戲工程相關的游戲、書籍資料。 當你清晨三點突發靈感而圖書館卻已關門的時侯,它將是非常有用的。

6. 抽取些時間
一個游戲就像孩子,當它剛出生時,它的父母總認為它是完美的。從你的游戲中抽 出些時間去得到一些新的觀點,避免都耗在上面。一遍遍的重復這一過程。

7. 保留記錄
確定你有一份以上的游戲拷貝。如果你是在電腦上輸入的,就各保持一份硬盤和軟 盤拷貝,另外再打印出一份清楚的最近版本(如每月打印一份,如果你干得快的話每 星期打印一份)。你不會覺得拷貝太多,因為你的好朋友們會來向你借或者想擁有拷 貝。而且這些拷貝能夠減少你因為硬盤癱瘓或丟失筆記本等原因造成的丟失機會。 同樣道理,保留舊版本的拷貝是有好處的。如果你在游戲測試時發現新的辦法還不 如舊的好,而你卻已將舊版本的扔掉了,這該怎么辦?至少保留一份最后版本之前 的拷貝,同你當前版本的拷貝放在一起。

8. 其它注意事項
優秀的規划和書寫是好的,但精美的視覺化說明對你的銷售大有益處。如果你要自 己動手,就學一些桌面出版,或找一些現成的插圖(比如:剪輯藝術或政府出版物) 或找別人幫你畫些插圖。 找個從事印刷業的人,和他探討一些盡可能廉價的方法。低價格可以有助於銷售, 低成本則有利於你的收益。

9. 記住這只是個游戲
不要因為制作游戲而忽略你的現實生活。如果有人不喜歡你的游戲,別介意。不用 擔心別人竊取你的創意。記住第一條誡律,從你的所做中得到樂趣就行了。

10.沒有第十條了 :-)

另外,這里有一些來自湯姆(“棱鏡游戲”的主持者)的額外忠告,感謝湯姆!:
1.不斷的創新是非常好的。如果你游戲中的所有東西都令人似曾相識,就好像是 偷來的。如果所有東西都與眾不同又會讓人感到陌生。常見題材單一獨到的構思是 好的,但會使得你的游戲看來像個“變體”,而兩個熟悉題材精明的創意則會使游 戲有新鮮感同時容易上手。因此不要試圖徹底從新發明某樣東西,而應把當前所擁 有的主意清晰化、簡潔化的用於擴展關鍵觀念的創新和趣味方面上。
2. 修正和雕琢你的游戲創意。測試不僅僅為了清除游戲和規則介紹中的錯誤,而 且就像一個討論會,對照他們已經取得的東西,游戲設計師能夠發現什么是真正所 要表達的。如果你將測試留到最后,這一發現將對你沒有什么好處。如果你進行早 期的測試和經常從眼前試着發現這一游戲真正要表達的,你就能常常很大的改進這 一游戲。 “ Alpha”測試就像在問:“是否真有這個游戲?”“我得到它了嗎?”“ Beta ” 測試看來在問:“是否用了最好的方法達到這一效果?”,“這是游戲的精煉嗎? 或者它是否能被簡化或刪除?”“是否所有主要的游戲系統協同工作,給予了我所 期待的游戲體驗?”“ Gamma ”測試又像在問“如何才能改善游戲的收支平衡 和介紹呢?”許多設計師停留在Alpha(生產一個吸引人但卻是次品的游戲)之后或 者從Alpha直接到Gamma,跳過了Beta(生產一個好的但還不夠完美的游戲)。 通常有必要靠你的親密朋友/游戲小組及早的進行足夠的批評性分析,幫助你發現如 何才能改進一個已經相當不錯的游戲。

我的一些其它建議:

在我制作游戲過程中,從沒有清晰明顯的測試“階段”。我傾向於每個階段都做一 點。我修改一些系統,拋棄或替換一部分,改善其它的收支平衡和介紹,差不多是 在同時做的。這些部分來自於我所從事的主要游戲類型的設計-宇宙類RPG,你必須 在一個時間內干一個部分的事情。 關鍵在於去尋求達到你最好的作品。用不同的方法嘗試,直到找到適合你的方法, 然后用它鑽研下去。

 

4.游戲的劇情
-------------------------------------------------------------------------------


游戲劇情的重要是不言而喻的,特別是RPG游戲,相信廣大玩家對"仙劍奇俠傳" 熟得不能再熟了,這個游戲以劇情取勝(他的音樂也相當不錯),各大媒體對他的 評價也都是以劇情為主,這個我就不多說了.
我也曾看過許多文章提到劇情的重要,這些文章說的相當好,相當有價值,但基 本上講述的是劇情的要點及注意事項.而我將從另一個方面去分析游戲的劇情.
游戲的劇情是游戲的靈魂(當然除少數不需要劇情的游戲,如體育類,賽車等), 游戲通過各種各樣的方法讓玩家融入到設定的劇情上以打動玩家,但如果游戲 的劇情不吸引人,那么無論游戲的表現手法有多好也不能達到目的,但是怎樣 的劇情才吸引人呢.
事實上中國與外國玩家有着很大的文化差異,這一點可以從各地所出產的游戲 上看得出來,歐美的游戲大多重視人物與場景的直實性,看上去就像電影,而東 方的游戲普遍追求漫畫式的效果.但這兩種風格哪種適合我們呢?答案是兩種 都適合.不要忘記我們的玩家基本上是青年一代,對新事物的接受是相當快的, 對歐美的游戲我們的玩家接受得很快,從這可以看出國外的游戲制作是相當出 色的,就拿我自己舉個例子,前兩天我得到一款"生化危機2"的游戲,游戲開始 的動畫可以說是扣人心弦,並且很好的襯托出了主題.而第二種風格的代表作 品就是"仙劍"了.
游戲所要表現的內容必須能夠被玩家接受,而且還要有創新,這樣的劇情才說 得過去.現在我們有一個很好的觀察點,那就是電影,外國的制作精良的大片 擠進中國后給我們的沖擊多么大,而中國傳統題材的作品也給我們留下難忘 的印象.
我們不得不承認,在技術上我們與國外的游戲制作公司相比還差了一截,這 使得許多很好的題材我們不敢用,因為以現有的技術還不能很好的將他表現 出來,如果勉強還可能會起反作用(這是有例子的).
在這里我只是起一個開頭的作用,什么樣的劇情適合大家也不是一下能說清 楚,希望廣大游戲愛好者能積極討論這個問題,這也是我們中國游戲業現階斷 的一個重要的有待解決的問題.

 

5.角色扮演游戲的升級系統研究
-------------------------------------------------------------------------------

 


在一般的角色扮演游戲中,人物的成長是一件相當重要的事,無論是角色扮演游戲或是目前熱門的策略型角色
扮演游戲(簡稱RSLG),這些升級系統都是游戲的一個重要部份。不過在一般的角色扮演游戲中,人物的升級
以及成長卻有着很多種的處理方式。在本文中,筆者將為各位介紹各種角色扮演游戲中常用的升級方式,並且
分析各種作法的優缺點。

在一般的角色扮演游戲中,最常用的升級方式就是亂數式的成長方式。在這種模式中,當一名角色獲得升級的
時候,程式會使用亂數來決定升級的各項指數,也就是說所有的升級數值都不是在控制中,而是依據一個亂數
表來決定提升的數值。這種升級的方式是如何處理的呢?

當人物到達升級的標准時,就會進入處理升級的副程式中,在這個副程式中程式會依設計者所定出的一個亂數
范圍,來計算出這名角色所得到的升級指數,然後將這個數值加到需要增加的屬性上。

在這種亂數決定升級的情況下,玩者所能夠獲得的升級數值,完全是由設計者訂定的范圍中求出,無論是升級
的上限或是下限都是在這個范圍內,絕對不會有意外的情況發生,就算是設計者如何提高上限與下限,都不會
改變這些。這種作法雖然可以讓設計者很輕松的訂出升級的上下限,但是卻不能控制升級時的不利因素,那就
是亂數的成份實在是太高了。若是有一名角色因為運氣不好一直只有獲得下限的升級數值,那麽它可能會比一
個一次就獲得上限升級數值的角色要弱。舉例來說,當這個亂數的范圍是一到五的時候,若是角色甲和角色乙
分別獲得上限和下限的升級數值,那麽會發生以下的狀況:

┏ ┳ ┳ ┓
角色甲 角色乙
┣ ╋ ╋ ┫
LV1 10 10
LV2 15 11
LV3 20 12
LV4 25 13
LV5 30 14
LV6 35 15
┗ ┻ ┻ ┛

各位看看上表,是不是可以看到角色甲在第二級時的數值就已經和角色乙第六級的數值是相同了。由於亂數式
的升級方式會有這種不公平的情況發生,因此常會使得玩者的努力需要有一些運氣的成份在里面;若是運氣不
好,可能原本的努力都無法發揮所要的功效。

由於亂數式的升級方式有這樣的缺點,因此有兩種不同的改進辦法,首先就是百分比制的升級方式。在這一種
辦法里,角色在升級的時候還是使用亂數來進行,不過在每一個數字的出現比例上卻做了一些調整。例如同樣
的升級的范圍還是從一到五,但是每一個數字的出現比例調整如下:

┏ ┳ ┓
數值 出現比例
┣ ╋ ┫
1 10%
2 20%
3 40%
4 20%
5 10%
┗ ┻ ┛

各位從上表中可以看到,在這一種處理方式上,每一個數字出現的比例做了一些調整。原本的亂數式中,每一
個數字的出現比例都是相同的,就以上面的例子來說,每個數字出現比例是百分之二時,因此上限和下限的數
值比較容易出現,發生不幸的情況比較多;但是在這樣子調整後,上限和下限的數值出現的機會就減低了不少
,會發生不幸的情況就降低了。

雖然這樣的作法可以降低不幸的發生機會,但是還是無法完全的克服所有的狀況,因為還是有可能會發生相同
的狀況,使得玩者陷入屬性不佳的情況中。因此另外一種改良的方式~修正值升級方式就這麽出現了。

其實修正值的升級方式和原本的亂數處理法在計算的時候是完全相同的,只不過是它在升級到一個程度的時候
,會來做一次計算並且取出一個修正值,以免玩者因為運氣不好無法達到升級的功效。

在這種作法上,上半部和亂數式的做法完全相同,唯一的不同是下半部的副程式。而這個副程式的作用就是在
幫一些升級時運氣比較不好的玩者取得一點修正值。

我們就以前面所說的升級的數值是從一到五來做個例子,讓玩者每升五級時就可以取得一點修正值。因此若是
一名角色在五次升級中都只有獲得一點的升級值,那麽目前它的數值就是:

10 + 1 + 1 + 1 + 1 + 1 = 15

不過在我們的升級表內中等的數值是三,因此當角色升了五級之後,應該可以獲得以下的數值:

10 + 3 + 3 + 3 + 3 + 3 = 25

這麽說來這名角色因為前五級的升級運氣不好,因此少獲得了十點的升級指數,所以我們就在這一次把這個缺
少的數值以修正值的方式補足,從修正值的計算式中可以得出:

25 - 15 = 10

就將這個數值加到角色的屬性中,讓角色不會因為運氣太差而有不利的情況。若是角色在升級中都獲得比較高
的數值,那麽修正值就是負的,也就表示不需要有修正值的存在了。

這種作法完全是為了不讓玩者因為升級時運氣不好使得屬性太低,因此只能算是修正部份數值的作法,雖然不
能完全解決亂數式的問題,但是可以將不利的因素降低,因此在某些游戲里的確有采用這樣的作法。

除了亂數式的作法外,還有一種是表列式的升級方式。在這種升級方式中,每一名角色的升級數值都是設計者
已經訂好的,完全不會有任何的變動。它的好處是設計者可以完全掌控所有的升級狀況,但是相對的這樣子的
表格需要占掉較多的程式空間。

舉例來說,某個游戲若是采用這種升級方式,那麽在它的記憶體中就需要有這樣子的升級表格。若是游戲中有
七項屬性會獲得升級,等級共有一百級的變化,那麽基本上它就需要有七百個不同的數值表放在程式中。若是
一個數值用了兩個位元(BYTE),那麽就需要用到1K左右的記憶體。如果說游戲中有四名角色,它們升級情況
又都是不同,那麽占掉的記憶體就將近有5K了。這麽算起來各位可能覺得不會很多,但是當這種資料越來越多
的時候,記憶體的消耗也就越來越多,使得程式的空間也越來越小了。

由於表列式的作法會使得升級的情況比較單調,因此大多數的游戲並不願意采用這種作法,再加上這一類的作
法對於記憶體的占用空間也比較高,因此如果不是必要,大多數都不會用這種作法。

除了以上這些作法之外,還有一種就是指數型的升級方式。這種作法其實就是表列式的改良,因為它將升級的
表格簡化成一個叄數,在升級的時候就依這個叄數來計算能夠獲得的升級值。現在我就舉一個例子來示范。目
前有一名角色的屬性以及升級指數如下:

o 生命:10 生命指數:10
o 法力:10 法力指數:10
o 力量: 3 力量指數: 2
o 智慧: 2 智慧指數: 2
o 反應: 2 反應指數: 2
o 體能: 4 體能指數: 2
o 運氣: 1 運氣指數: 3

那麽當他獲得升級的時候,程式就會依這個升級指數來計算升到下一級時的各項屬性值。因此在升了一級之後
,各項屬性的數值就是以下的數字:

o 生命:20
o 法力:20
o 力量: 5
o 智慧: 4
o 反應: 4
o 體能: 6
o 運氣: 4

用這種作法,在程式內不需要復雜的升級屬性表,只需要幾個簡單的叄數就可以,若是能將各項叄數之間的關
系加以變化,並作一些運算,那麽可以使升級時的變化更多。舉例來說生命的增加和體能有關,或是法力的增
加和智慧有關,那麽在計算起來時會有比較多的變化,使得整個升級的表現不會太單純。

以上這些作法大部份的變化程度都不會很多,沒有辦法表現出一個人的成長情況。就像我們有時候會形容一個
人「大器晚成」或是說他「小時了了」這樣子的情況都不能表現出來。因此後來又有一種成長曲線的升級方式
。在這種升級方式中,我們首先要訂出幾種不同的升級情況。像是:

A. 平衡成長
B. 大器晚成
C. 小時了了

要達成這種效果,我們需要將升級的總等級數分成幾個階段。我們以一個可以升到一百級的游戲來說,將每十
級分成一個區塊,就可以訂出這三種成長情況各要給它多少的數值。

其實這種曲線式的升級方式,在處理上和指數式的作法差不多,只不過指數式的作法一個人物每一種屬性只會
有一個數值,這個數值是不會改變的。但是在曲線式的作法中,會依不同階段有不同的升級指數,才可以造出
不同的成長情況。我們就以一名「大器晚成」的角色來說,這一類的角色在開始成長的比較慢,但是當人物成
長到一個階段後,成長的速度就會加快,因此我們可能在前兩個階段只給他們一點的升級指數,後面幾個階段
再給他們較高的升級指數,使這名角色會在游戲後期升得比較快。

反過來說,若是要設計一名「小時了了」的角色,那麽我們在初期可以給他較高的升級指數,但是到了後期就
要給它較低的指數,如此一來就可以表現出這樣的情況。

事實上,在游樂器中的「光明與黑暗續戰篇」就曾經用過這一種作法,使得游戲中的每個角色都有各自不同的
特色。特別是有些屬於大器晚成的角色,曾經因為初期成長的速度太慢而被玩者拋棄,但是後來知道這名角色
的特性之後,再回過頭來訓練的這種情況,正是這種曲線式升級的特色。這種作法使得角色除了單純的數字屬
性之外,還增加了一些隱藏的特性,會讓游戲更有味道。

如果以筆者個人的喜好來說,我是比較欣賞曲線式的升級方式,因為這種方式比較可以隱藏角色的特色,也不
會因為數字的變化太過單調而讓玩者覺得過死板。比起亂數式的不定性和升級指數式的單純來說,這一種作法
可以說是兼具了兩種的特色,同時還有全新的表現,是一種不錯的升級方式。只惜目前國內的游戲很少使用這
種作法,大多還是采用亂數式的作法,對於國內玩游戲的玩者來說,實在是有些可惜,因為大家沒有辦法體會
到這種作法的優點。

 

6.游戲中的智能系統處理
-------------------------------------------------------------------------------

 

游戲中的智能對手
 AI在游戲中最普通的形式是創建計算機控制的對手。因為大多數游戲是單人游戲,所以要設計游戲者在游戲中必須戰勝對手。為了達到這個目的,你可以使用某種類似A*搜索的簡單AI算法,以幫助對手穿過迷宮向游戲者發起進行。你也可以使用簡單的算法預測游戲者的反應。
 但是,記住沒有必要創建世界上最強大的對手。你的對手只要能給游戲者提供足夠的挑戰性就可以了。還有,要注意游戲的內容。例如:一個戰棋式RPG游戲中策略占的是主要地位;而在純RPG中故事情節和角色開發就更重要一些。千萬不要因為計算機對手太強大而讓游戲者們陷入失敗的泥沼。

游戲中的非智能對手
 通常,在游戲開發中AI技術是與計算機對手緊緊聯系在一起的。這是因為早期的大部分類似角棋的游戲是一對一的。但是,任何好的探險游戲或RPG 游戲開發者都知道,AI同樣可以用於非對手角色。例如:如果你正在建立一個RPG游戲並且你想讓你的世界活起來,這就是說,讓城市里的人以智能的方式活動,那么你可以使用某種算法確定在一天中的某個時候,角色應該在那里。你可以使用類似於AI算法如A*來輔助你將一個對象從一處移動到另一處並繞過障礙物。

游戲中的智能系統
游戲中的AI在本質上是最具有模仿性的,但它們基本上是依賴一些AI要素。 你可以將所有具有決策功能的對象在一個游戲中融合為一個整體。例如:在一個戰爭游戲中,你的各個部分可以依據各自所處的具體環境來作出各自的AI決策。
  使用這種方法,你得把精力集中於怎樣在各個獨立的決策個體之間建立聯系,以及這些聯系怎樣才能使游戲成為融會貫通的整體。是用一個高級決策影 響其它決策,還是各個決策個體之間平等地互相影響呢?舉個戰爭游戲來說, 你有十輛坦克,它們的思維模式基本相同。所以它們都決定去攻擊敵人陣營中 HP值最低的一輛坦克。但是這時其中一輛坦克說:“這個敵人歸我了!”那么 剩下的九輛坦克就應該依據這條信息各自調整下一步的攻擊目標。當你建立智能個體時,要考慮在一個智能系統整體環境下,它應該如何行動。

 


7.電腦游戲中的人工智能制作
-------------------------------------------------------------------------------


 電腦游戲隨着硬件執行效率與顯示解析度等大幅提升,以往很多不可能或非常難以實現的電腦游戲如此都得以順利完成。雖然電腦游戲的呈現是那么地多樣化,然而卻與我們今日所要探討的主題,人工智能幾乎都有着密不可分的關系。
 在角色扮演游戲中,程序員與企划人員需要精確地在電腦上將一個個所謂的“怪物”在戰門過程中栩栩如生地制作出來;所以半獸人受了重傷懂得逃跑,法師懂得施展攻性法術。
 目前能讓人立刻想到與人工智能有密切關系的游戲有兩種:
一是所謂的戰棋/策略模擬游戲,二則是棋弈游戲。人工智能的比重與深淺度,在不同的游戲類型中各有不一。有的電腦游戲非標榜着高人工智能不可,不然沒有人買;有的則是幾乎渺茫到讓玩家無法感覺有任何人工智能的存在。            

 導向式思考

 AI最容易制作的的方式,同時也是早期游戲AI發展的主要方向就是規則導向或稱之為假設導向。在一些比較簡單的電腦游戲中,程序員可以好不困難地將游戲中的規則與設定轉化成一條條的規則,然后將它們寫成電腦程序。讓我們以角色扮演游戲為例。決大多數的企畫在設定所謂電腦怪物時,所設定的屬性通常有以下幾種:

  生命值 攻擊力 防御力 法力  屬性

 最后一個“屬性”是我在設定時喜歡增加的項目之一。透過這項屬性的設定,我可以把怪物設定成“貪生怕死的”,也可以把戰士設定為“視死如歸”。以目前我們所掌握的資料,在戰門系統中的大綱如是誕生了:                          

規則一

if (生命值< 10) // 邊臨死亡了嗎 
{  if (屬性== 貪生怕死)               
   結果 = 試圖逃跑               
  if (有任何恢復生命值的物品或法術可用)      
   結果 = 使用或施展相關物品或法術       
}                                                 

規則二
 
if (可施攻擊性法術 && 有足夠法力)
{                        
   結果 = 施展攻攻擊性法術             
}                        

 由以上一連串的“如果--就--”規則設定,建立了最基本的AI。說這樣的制方式只能建立基本AI其實並不當然正確。只要建立足夠及精確的規則,這樣的方式仍然有一定水准的表現。
 規則導向的最大優點就是易學易用。在沒有深奧的理論概念的前提下,仍有廣大的使用群。所以很多老道的玩家常常沒兩下就摸清楚敵人的攻擊策略,移動方式等等。

 推論式思考

 相信曾經接觸過電腦語言課程,或是自習過相關書籍的朋友們,都曾曾經聽過一個著名的程序,那就是井字游戲。用井字游戲作為討論AI的入門教材,我個人覺得是最適當的例子。或許有人還不知道井字游戲怎么玩。只要任何一方在三乘三的方格中先先成一線便勝利了。我們在前面談過的規則導向,在這里也可以派得上用場。

 if任何一線已有我方兩子&&另外一格仍空//我方即將成一線嗎
  結果 = 該空格                     
 if任何一線已有敵方兩子&&另外一格仍空//防止敵方作成一線 
  結果 = 該空格                     
 if任何一線已有我方一子&&另外兩格仍空//作成兩子    
  結果 = 該空格

 有一次我在某本電腦書上,同樣地也看到某些以井字游戲為介紹的范例。不同的是,我幾乎看不到任何規則導向的影子。但在仔細分析該程序碼后,我得到了極大的啟發,原來AI是可以不用這么多規則來制作的。它用的方法正是在電腦AI課程中重要的概念:極大極小法。我在這里只說明這法則的概念。繼續以井字游戲為例,電腦先在某處下子,接着會以假設的方式,替對方下子,當然,必須假設對方下的是最佳位置,否則一切則毫無意義。在假設對方下子的過程中,自然又需要假設我方的下一步回應,如此一來一往,直到下完整局游戲為止。 底下是節錄書中的程序片段:                       
 
bestMove(int p, int*v)
{   int i; 
   int lastTie;                  
   int lastMove;                 
   int subV;                                   
/*First, check for a tie*/            
    if (isTie()) {              
     *v=0;               
     return(0);              
   };
/*If not a tie, try each potential move*/
 for (*v=-1, lastTie=lastMove=-1,i=0;i<9;i++)
  {
   /*If this isn't a possible, skip it*/          
   if (board[i]!=0) continue;
   /* Make the move. */
    lastMove=i; 
    board[i]=p;                             
   /* Did it win? */                       
    if (hasWon(p)) *v=1;                     
    else{                             
   /*If not, find out how good the other side can do*/
     bestMove(-p,&subV);                      
   /* If they can only lose, this is still a win.*/
      if (subV==-1) *v=1;       
   /* Or, if it's a tie, remember it. */         
       else if (subV==0){                 
          *v=0;       
          lastTie=i; 
          };                          
       };                              
/* Take back the move. */           
           board[i]=0;          
/*If we found a win, return immediately
     (can't do any better than that)*/     
  if (*v==1) return(i);                     
/*If we didn't find any wins, return a tie move.*/         
  if (*v==0) return(lastTie);                      
/*If there weren't even any ties, return a loosing move.*/     
  else return(lastMove); 
};    

 國外的一些論壇曾舉行過256字節的游戲設計比賽。作品非常多,其中有一件作品正巧也是井字游戲。作者用區區兩百多行就寫了與上述程序演算方式完全相同的作品,可見功力確實了的。另外,我也很希望類似的活動能在國內推展起來。對了,在這樣的比賽條件限制下,除了匯編語言外,幾乎沒有其它的選擇了。       .386c                        
  code      segment byte public use16      
          assume cs:code, ds:code      
                            
          org   100h            
                            
  tictac     proc  far             
                            
  start:                       
          push  cs             
          pop   ds             
          mov   ax,0B800h     ; 清除屏幕
          mov   es,ax       ;    
          xor   di,di       ;    
          mov   cx,7D0h      ;    
          mov   ax,0F20h      ;    
          rep   stosw       ;    
          xor   cx,cx       ;    
          mov   dl,5            
  loc_1:                       
          call  printBoard         
  loc_2:                       
          mov   ah,8        ; 等待按鍵
          int   21h             
                            
          movzx  bx,al            

          sub   bl,31h       ; 如果不是1..9
          jc   loc_2       ; 則重新輸入 
          cmp   bl,8              
          ja   loc_2              
          cmp   data_1[bx],al          
          jne   loc_2              
          mov   byte ptr data_1[bx],'x'     
          dec   dl               
          jz   short loc_3           
          mov   al,'o'             
          call  bestMove            
          mov   [si],al             
          call  isWin   ; 判斷是否已取得勝利 
          jnc   loc_1              
  loc_3:                          
          call  printBoard           
          mov   ax,4C00h            
          int   21h               
                              
  data_1     db   '12'              
  data_2     db   '3456789'            
  data_3     db   0                
                              
  tictac     endp                  
                              
                              
  printBoard   proc  near              
          mov   si,offset data_1        
          mov   di,548h             
          mov   cl,3              
                              
  locloop_4:                       
          movsb                  
          add   di,5              
          movsb                  
          add   di,5              
          movsb                  
          add   di,133h             
          loop  locloop_4            
                              
          retn                  
  printBoard   endp                  
                              
                              
  isWin      proc  near              
          mov   bx,1              
          mov   bp,3              
          call  sub_3    ; 檢查橫向是否完成 
          inc   bx               
          inc   bx               
          dec   bp               
          dec   bp               
          call  sub_3    ; 檢查縱向是否完成 
          call  sub_4    ; 檢查斜向是否完成
          clc
          retn                  
  isWin      endp                  
                              
  loc_5:                         
          stc                   
          retn                  
                                                            
  sub_3      proc  near              
          mov   ah,3              
          mov   si,offset data_1        
  loc_6:                         
          mov   di,si              
          call  sub_5              
          add   si,bp             
          dec   ah               
          jnz   loc_6              
          retn                  
  sub_3      endp                  
                             
  sub_4      proc  near              
          mov   di,offset data_1       
          inc   bx              
          call  sub_5             
          mov   di,offset data_2        
          dec   bx               
          dec   bx               
          call  sub_5              
          retn                  
  sub_4      endp                  
                              
                              
  sub_5      proc  near              
          mov   cl,3              
                              
  locloop_7:                       
          cmp   [di],al             
          jne   short loc_ret_8         
          add   di,bx              
          loop  locloop_7            
                              
          add   sp,4              
          jmp   short loc_5           
                              
  loc_ret_8:                       
          retn                      
  sub_5      endp                      
                                  
  bestMove    proc  near                  
          mov   bx,31FEh                
          mov   cl,9                  
          mov   di,offset data_1            
                                  
  locloop_9:                           
          cmp   [di],bh     ; #empty?        
          jne   short loc_12  ; #no, skip       
          mov   [di],al                 
          pusha                      
          call  isWin      ; #CY: Win       
          popa          ;            
          jnc   short loc_10  ;            
          mov   bl,1                  
          mov   si,di                  
          mov   [di],bh                 
          retn                      
  loc_10:                             
          pusha                      
          xor   al,17h ; good! toggle 'o' / 'x'
          call  bestMove                
          mov   data_3,bl                
          popa                      
          mov   ah,data_3                
          neg   ah                   
          cmp   ah,bl                  
          jle   short loc_11              
          mov   bl,ah                  
          mov   si,di                  
  loc_11:                             
          mov   [di],bh                 
  loc_12:                             
          inc   bh                   
          inc   di                   
          loop  locloop_9                
                                  
          cmp   bl,0FEh                 
          jne   short loc_ret_13            
          xor   bl,bl                  
                                  
  loc_ret_13:                           
          retn                      
  bestMove    endp                      
  code      ends

          end   start

 


7.游戲引擎 小謝
-------------------------------------------------------------------------------

 

早想寫一點游戲設計的文章與大家交流,一是經驗的問題,二是公司正在緊張的游戲制作期,實 在抽不出多少時間,一直沒有動手,今天忽然頭腦發熱,寫了一段,以后准備陸續寫一些游戲創意,策 划,制作,流程管理,和制作工具等方面的文章供大家參考.

我們的游戲設計經驗主要是冒險游戲和角色扮演游戲,但我們設計游戲工具時盡量適應其余題 材,不過是否可行未經檢驗. 寫這篇文章的意圖一是想為游戲界做點事,拋磚引玉吧,另外是公司正在尋找志同道合的戰友, 我寫一點文章交一交朋友,許多東西僅僅是我們的經驗,不一定很好.參考而已吧

游戲設計工具包括游戲編輯工具和游戲引擎兩塊;
編輯工具:交互編輯游戲數據,生成游戲引擎所需的數據文件,包括以下幾個功能塊: 圖像編輯,場景編輯,物品編輯,動畫編輯,人物編輯,事件編輯等,具體介紹在以后的 文章介紹.

先從游戲引擎說起.
語言:VC5.0
操作系統:WIN95
圖像引擎:DIRECT X 5.0
支持游戲風格:各種類型和視角以及多層次的冒險游戲和角色扮演游戲

整個游戲引擎包括以下功能塊:

資源管理:圖像庫CIMGLIB,聲音庫CSOUNDLIB,通過編輯工具形成的資源文件來定義,每種資源 包括定義管理和一些操作接口.圖像庫圖"像包括多種格式(BMP,GIF.AVI,FLC等)以及他形成的內存 格式定義,子圖定義(每一張圖片包括許多小圖,需定義它的小圖位置,當然可以自動生成),游戲需 要的特殊定義,比如行走,身體性質,中心定位點,觸發區,可以根據自己的要求擴充各種性質定義. 圖像最好允許圖像組合定義.聲音庫包括WAV和 MID的定義和再現. 資源由IMGLIB.DAT和 SOUNGIMG.DAT定義,調試版本中最好不要將資源打包,而是指向正常的 文件名,發行版本中再打包,這樣修改和不同工作人員協調容易一些,否則最好有一個自己的資源 管理器.我們在調試版本中數據文件采用文本描述格式.許多數據可以手工編寫而不需要專門的 編輯工具.
資源管理對象還包括內存管理,比如設置時間閥釋放長期不用的資源.

聲音管理:CSOUND,包括Creat(),Sound(char *fileName...),SetPos(),等,DirectSound有一 些函數,我們要做的是封裝簡化,減少對外的接口.
窗口系統:接管標准窗口系統,一個完善的游戲引擎最好有一個自己的界面系統,至於簡單還是 復雜根據自己情況具體分析,一個具備基本功能的界面系統1000行程序就可以對付下來,需要窗口 系統的原因是一般的圖像引擎不支持標准窗口,二是可以便於移植到別的操作系統.在我們的游戲 引擎中,游戲只是窗口系統的一種特殊控件(CWINGMCTR),因此可以實現多窗口游戲等特殊要求.
CWINGMCTR是一種特殊控件,通過他來控制游戲.包括控制和顯示.

圖像引擎:所使用的圖像引擎的管理,我們使用的是DirectX,包括Creat(),CreatSurface(), OutToScr(),Bilt()等對外接口;他不是游戲的重點,我們盡量將圖像引擎細節封裝起來.

圖像管理:這是處理圖像的中心,一般處理游戲顯示喜歡以某種圖像引擎為中心來設計,我覺得 最好設計自己的對象來封裝別人的圖像引擎,這樣不會因某引擎而受限制,移植也比較容易,我們雖 然使用的DIRECT X ,但實際上對外的接口是一種CPICPAGE的界面,他不但包括DIRECT X 的surface, 也包括標准的位圖,AVI界面,GIF動畫界面,以及自定義的格式,他將各種類型的圖像統一起來,對外 使用統一操作,比如DRAWTEXT,BILT,LINE等標准圖形圖像操作,以及擴充的ALPHA通道,透明度等操 作.為了減少內存的需求,特別是16M高彩,不要將全部圖像使用DIRECT X的表面,對一些刷新不多的 圖像,比如背景,可以使用標准256色位圖,甚至一種GIF表面,需要時再解壓,我們還使用一種單色位 圖用來從背景中摳圖,比如一所場景中一棵巨大的樹,只要不是動畫,我們可以用單色摳圖的形式從 背景中扣除來作為另外一層,這樣我們可以大大降低圖像的內存需求.因此采用全部手繪(或3D場景), 而不是小圖拼貼的場景成為可能.通過各種手段可以節約60%的內存需求.
CPICPAGE可以通過TimeTrace()以及多線程來改寫內容,比如AVI的改變.

游戲控制:這部分包括顯示和控制,由CGAME->CGAMEPAGE->CGMOBJ對象組成,CGAME是總控對象, 包括許多CGAMEPAGE游戲頁,CGAMEPAGE是一個具有連續場景的游戲片斷,有點類似於游戲的一關, CGAMEPAGE由一系列CGMOBJ組成,CGMOBJ是游戲的基本對象,由他繼承出地圖,物體,物品,人物,武器, 動畫,觸發器,多媒體按鈕等特殊

游戲對象,這是一個根據游戲要求不斷豐富和改寫的部分,對外的 接口是:SendDraw(),Draw(),TimeTrace(),AcceptMsg(),SendNetMsg(),AcceptNetMsg()等,他是通 過CWINGMCTR來調用,每種對象有許多控制參數,對象之間允許通訊,以及有自己的生長死亡發展的 控制,盡量做到對象與外界減少直接接口,通過消息實現交流.

對象分為兩類:景色對象和活動對象,
景色對象定義了組成場景的元素,包括背景和前景兩層,可以是由整個圖片組成或由RPG常用的 圖片拼貼法的組成,它的特點只作為背景或前景,活動物是在他們的之間活動,一般定義后不做改變, 也不做控制,由於支持圖像界面多格式,所以我們可以方便地使用AVI或 GIF動畫作為背景來增加場 景的效果和真實性.景色還包括了行走性質定義,我們采用的是8x8為一單元,每個單元定義了一種 性質,比如平地,草地,障礙物等.

活動對象是在背景和前景之間活動,他們之間有相互的位置關系,前后關系隨着位置改變會不斷 改變,因此他在所屬的CGAMEPAGE中次序是動態的.對象的關系一般是由Y軸定義,由於要支持斜視角和 復雜的地形結構,光靠Y軸是不夠的,我們引入了地基線的概念,通過在地基線之上還是之下來判斷前 后關系,地基線的定義在圖像定義中描述.活動對象有復雜的參數,可以接受外界消息,可以有自己的 各種反應.我們在引擎中使用了一種描述語言來描述他們的反應,比如對鼠標擊打,人物經過等產生 參數改變,發聲,對話等的回應.描述語言將作為專門的一章來介紹. 游戲顯示過程是這樣的, 在每次刷新期時窗口的游戲控件調用他所屬的游戲頁 CGAMEPAGE->SendDraw(); 游戲頁將要顯示的對象按前后次序送往窗口,同時注明此對象是否改變,窗口分析改寫的區 域,調用每個對象的Draw()接口來刷新活動的區域,為了增加速度,並不是顯示所有的區域,而是只改 寫活動區域,因此我們設計了一個CCLIP的對象來管理刷新定義,它的原理是將表面分為16*16的單元, 最終顯示時計算出優化后的多個剪切區域.整個窗口系統和每個游戲控件擁有自己的CCLIP對象.另 外一項增加速度的方案是游戲控件擁有一個比顯示窗大兩倍的顯示頁,這樣場景滾動時只要將顯示 位置改變即可 ,不用刷新所有區域. 游戲控制的過程是這樣的:AcceptMsg()來接受各種消息調用腳本來改變自己參數和狀態並影響 別的對象,另外每次時鍾來時,調用每個對象的TimeTrace()來改變狀態,比如動畫改變,運動軌跡改變, 觀察周圍的對象做出反應等.

系統控制模塊:對系統的參數做出反應.不同的題材控制不一樣,比如即時戰略等.只要改寫這部 分以及擴充游戲對象,引擎便能支持不同的題材.至於人工智能,智能行走,只是對象的方法,比較簡 單,只是需要時間. 游戲控制部分比較復雜,每一種游戲對象都有許多控制的細節,在這篇文章里不做具體描述,以后 再說吧.
最后一個是網絡模塊:我們正在開發的是國內第一個圖形化MUD游戲,網絡是它的核心部分,介紹 網絡的內容很多,需專門文章.我們使用的不是DirectPlay,使用的是WinSoct,考慮的是UNIX作為服務 器的需求.網絡要解決的難點是安全,同步和數據壓縮,這里要用到許多技巧.

游戲是通過數據文件來定義:

數據文件格式:數據文件包括資源定義文件和游戲定義,界面定義文件,文件的數據格式我們采用 的是文本形式,類似於WEB的文本,這樣的好處一是版本升級容易處理,二是可以減少前期對編輯工具的 功能要求,因為我們可以用文字編輯器處理大部分數據,然后有時間再設計一個強大的工具比較現實, 當然,最終提供給用戶的是處理后的數據文件.他中間有一個轉換模塊.
游戲的運行流程描述(不是真正的過程,按DOS格式描述):
CreatGameWindow();//初始化window窗口
CreatDraw(hWnd); //初始化圖像引擎
CreatSound();//初始化聲音引擎
CreatAvi()//初始化AVI引擎
CreatNet();//初始化網絡引擎
LoadGameData();//讀取游戲定義數據,包括資源定義文件和游戲定義,界面定義
While(1)
{
WINTraceMsg();//處理系統消息,比如鼠標,鍵盤等
GameTimeTarce();//處理活動的游戲頁的時間反應
WinPaint();//刷新游戲顯示
OutToScr();
}

我們這里介紹的是單線程結構,許多部分可以用多線來加快游戲速度,但結構是一樣的,就不多介紹了.
游戲引擎的系統分析是游戲設計技術方面的成功關鍵,是最容易走彎路的部分,希望我們的文 章能給大家一點啟發,由於今天的游戲趨向於多類型綜合,設計引擎時一定不要拘泥於某一單項題材, 我們在策划這套引擎時要求他支持的游戲非常廣,甚至支持多媒體設計,這套引擎只要擴充或改寫參 數管理以及游戲對象,便能支持各種風格的2D類游戲.將來我們要做的是一套可以交互設計各種游戲 的開發平台,當然不是<<游戲工廠>>似的玩具.

今天就寫到這里,這只是對引擎結構的大概介紹,其中每一點將來都有詳細的描述,歡迎同志商討. 我們盡量回答朋友們的意見,歡迎加入我們的隊伍.

 

 

8.腳本語言淺介
-------------------------------------------------------------------------------

 

  今天,我想先以一套游戲使用的技術為藍本,導入實務設計的范疇。你聽過“ZZT”這套共享游戲嗎。

  如果沒有,別太在意,這是91年發行的文字模式角色扮演游 戲。我們不談游戲本身,而把焦點放在游戲的開發系統當中。ZZ T雖然老,所引用的“物件導向”設計概念可是一點都不含糊。

  為什么需要額外設計一套游戲專屬的程序開發系統?利用現有的程序語言難到不可以嗎?首先,現今常見的程序語言中,大 多半都是通用語言,當然也有不少專用語言,嚴格說來,沒有一 套游戲設計的專用語言。(不過,早在八位電腦時代,已有一套 名為“游戲制作者”的軟件,它允許使用內建的語言來開發游戲 )就目前游戲主要使用的C語言以及匯編語言來說,程序員必須 從無到有,制作出游戲的引擎系統。

  如果有一套游戲開發系統,制作游戲(特別是續集)會大幅 度降低程序設計制作游戲所花費的時間與精力。讓我們來看看幾個實際的例子:Origin的創世紀系列(人物行程管理、對話皆有 專屬系統)等。只要游戲開發系統規划得適宜,一旦制作完成, 對日后制作的游戲程序,絕對有大補益。

  我不想在這里就編輯器、編譯器之間的差異以及設計的詳細原理做討論。雖然這些都與設計一套游戲開發系統有密切關系, 但這些都屬於“系統程序”課程的范疇,我們只需了解就行了。   將程序與游戲資料獨立安排有許多好處。你不會因為僅需要修改 游戲資料,就必須重新進行“修改代碼、編譯、連接”等繁瑣的 過程。讓我們來看一段ZZT游戲中,某商店老板的“控制”程序            

@Vendor
#end                      
:touch      “事件”觸發(接觸該“物體”)
   "Hello, you must be new to town!   /
   If you want to be successful     訊息顯示
   around here, you'll need to buy  
   some supplies!"           /
                     
!ba;Ammunition, 3 shots.........1 gem   /
!bt;Torch.......................1 gem    選項控制
!bx;Advice......................Free   /
#end                   
                     
:b             選擇第一項:購買彈葯
#take gems 1 ca             
#give ammo 3               
"Here's your ammo... Happy hunting!"   
#end                   
 
:bt             選擇第二項:購買火把
#take gems 1 ca             
#give torches 1             
"Here's your torch..."          
#end                   
                     
:bx             選擇第三項:提出忠告
"It is whispered that soon        
if we all call the tune,        
then the piper will lead us to reason. 
                     
"And a new day will dawn         
for those who stand long        
and the forests will echo with laughter."
                     
          -Led Zeppelin     
#end                   
                     
:ca             錯誤處理(無力購買)
"You can't afford it!"          
#end                   

以上是ZZT程序片段
  你是否了解上面的程序,這類經過大量簡化后的控制程序,我們把它稱做腳本語言。腳本語言除了接近口語化的設計外,另 加上簡化后的語法。(除了內建的命令外,通常僅需簡單的邏輯 判斷與數值計算即可勝任)因此用腳本語言制作游戲,不在是再 是非程序員不可的工作(除了系統本身的修訂),企畫人員也可 以很快地進入狀態。另外,如果將來需要將游戲移植到其他平台 時,比起程序與資料的盤根錯節的設計,利用腳本語言來開發的 游戲,只需要修改系統本身,腳本語言部分本身毋須更動,相形 之下出現問題的機會與范圍就縮小了很多。      
  這次以ZZT游戲為例,就是想對腳本語言部分的設計作了概念性的介紹。

 

9.RPG游戲的對話系統
-------------------------------------------------------------------------------

 

我要和各位談談角色扮 演游戲中的最重要的一個部份~那就是交談系統。若是各位 玩者接觸的角色扮演游戲夠多的話,應該會知道在角色扮演 游戲中有着單句式以及關鍵字兩種完全不同的交談系統,今 天我們就來看看這兩個不同系統。

要怎麽分辨單句式和關鍵字系統呢?我們可以簡單的從在 游戲中玩者和游戲中的人物交談時是如何獲得訊息來看。在 一般國內的角色扮演游戲中,經常使用的就是單句式的交談 系統,也就是那種玩者只要和角色對話,對方就會一股腦的 將所有的消息告訴你,而玩者也就可以獲得所有的訊息,這 種就是屬於單句式系統(如大宇的軒轅劍系列、仙劍奇俠傳 等等)。就算是有的游戲用比較刁鑽的方式,會要求玩者多 問幾次才會出現真正需要的訊息(像精訊的亂世伏魔錄), 也不過是這一種系統的延伸使用罷了。

那麽關鍵字系統呢?我想最有名的應該就是創世紀系列, 在創世紀的前幾代中,玩者需要自行輸入各個關鍵字 ,來 詢問各式各樣的問題,隨着玩者輸入不同的關鍵字,就會有 不一樣的答案。但是也因為這個原因,使得一 些英文程度 不好的玩者在接觸那幾代的游戲時相當的辛苦(反過來說, 也有一些玩者因為玩這個游戲而使得英文程度大增),也使 得許多的玩者不願意接觸創世紀。但是到了創世紀七代的時 候,由於采用了滑鼠來進行游戲,因此整個關鍵字系統就做 了很大規模的改進了,玩者可以由畫面選擇要問的關鍵字, 因此不再會有找不到關鍵字來問的狀況發生了。

那麽我們來看看國內的幾個采用關鍵字系統的角色扮演游 戲吧。在筆者的印象中,好像只有精訊的聊齋志異、大宇的 妖魔道、以及軟體世界早期推出的一個俠影記而已。在這幾 個游戲中,筆者覺得精訊聊齋志異的關鍵字系統是最成功的, 至於為什麽筆者會這麽認為,絕對不是因為筆者自己曾經叄 與過這個游戲的制作,而是從關鍵字系統的結構來看,這個 游戲才是真的使用到關鍵字系統的精髓。

所謂的關鍵字系統,正如同它的名稱一樣,是一個以關鍵 字來串連游戲劇情的交談系統,因此玩者必需要能夠從某一 個游戲中人物的口中獲得某個關鍵字後,再使用此一關鍵字 到另一名人物的口中發揮作用,這才能說是使用了關鍵字的 妙用。在聊齋志異這個游戲中,玩者要獲得第一座塔~金形 塔的八字真言,就必需要先從一位書生的口中獲得四字真言, 然後再到酒店里找一位酒女,用這前四個字的真言向她詢問 後四個字的真言。而所 謂的關鍵字系統的巧妙,也就是當玩者若是沒有獲得前一 個關鍵字的時候,在後面的那個人口中是無法取得下一個關 鍵字的。在聊齋志異這個游戲中,若是玩者不能先由書生的 口中套出前四個字的真言,那麽在酒女的對話選單中是不會 出現那四個字供玩者選擇,因此玩者就無法獲得後四個字了。 也就是說,關鍵字系統是一個憑着玩者選項不同會有不同情 況的對話系統。

那麽我們回過頭來看看另外兩個游戲的關鍵字系統吧,首先我們來看看大宇的妖魔道吧。在這個游戲中也有關鍵字系統,但是它的關鍵字系統所發揮的作用並不是在對 話上,而是當某些任務還沒有解決時,這些關鍵字就會出現 在對話的選單中,讓玩者可以選擇它取得所需的資料,當這 一項任務或是事件解決了後,這個關鍵字就會從選項中消失。 這種作法並沒有真正發揮「關鍵字」的作用,只是用它來提 供玩者多次獲得相同的問題解答而已。

接下來我們看看俠影記這個游戲,這可以說是關鍵字系統 使用的錯誤范例,因為在這個游戲中,提供玩者選擇的關鍵 字並沒有影響其他地方事件的作用,反而把原來簡單的可以 在單句中表現的訊息切成一段一段的文字,讓玩者需要將所 有的關鍵字選擇完之後才可以知道全部的訊息。再加上一些 關鍵字所得到的訊息又都是沒有用的訊息,使得這個游戲的 交談系統完全沒有表現出關鍵字系統應有的特色。

現在我們就來探討一下這兩種系統在資料結構上究竟有什 麽不一樣的地方。首先我們來看看單句式的資料結構 ,記 得在討論劇情結構的那一篇文章中(第52期),筆者曾經 舉過一個單線式劇情的范例嗎?那種的結構就 是單句式交談系統的標准結構。因為在單句式交談系統中, 在玩者遭遇一名游戲中的角色時,角色會依據當時 游戲中的劇情發展情況,決定出要說那一句話,若是在劇 情沒有改變的情況下,就會重復的使用這一句話。

在這種系統中,當玩者控制的角色和游戲中的角色交談時, 程式會依據這名角色所相對的幾個旗標,判斷出目前該顯示 出什麽樣的訊息。若是同時有幾個旗標都成立的話,也只會 以最優先的旗標為准。在這種系統之下,訊息的結構相當的 單純,程式也只要將每一句對話編上相對的代碼,然後用判 斷式就可以決定出要顯示出那一句訊息。同樣的,這種系統 的資料結構也比較單純。

接下來我們看看關鍵字系統的資料結構吧。在前面說明單 句式系統的時候,筆者曾經說過要以旗標來判斷顯示那一句 訊息,其實這個所謂旗標就是游戲中控制劇情目前發展到那 里的指標。同樣的在關鍵字系統中這些旗標也存在着,只不 過除了它們之外還要再加上一組關鍵字的旗標,而這些關鍵 字的旗標就是這種交談系統的重心 。

在游戲中,當玩者從其中一位角色的口中獲得一個關鍵字 之後,這個關鍵字的旗標就會打開,之後若是有遇到對這個 關鍵字有所了解的角色時,在交談的選單中就會出現這個關鍵字。

在這種系統中,交談的時候,程式會先判斷目前玩者已經 獲得那些關鍵字,然後再看這一名交談的對象對於那些關鍵 字會有反應,接下來在在選單中就會出現這些有反應的關鍵 字讓玩者選擇。在選擇了某個關鍵字之後,相關的訊息就會 顯示在畫面上,同時若是有某個關鍵字會因為問了這一句話 之後打開,也會在訊息顯示之後將 此一關鍵字的旗標打開。接下來我們看看下面這個例子:

在這個例子中,會有某甲以及某乙兩個人,在游戲的過程 中玩者必需先從某甲的口中獲得一句暗號,然後用這 個暗號向某乙詢問,才可以獲得所需的資料。因此在游戲中某甲的資料如下:

某甲【無關鍵字】:喔,是你呀。我相信你一定是想要加入這個秘密的組織,不過若是沒有組織的口令的話,是不可能加入的。

◎在這句訊息後關鍵字「口令」打開

某甲【口令 】:你想要知道口令?好吧,那我就告訴你,我們的口令就是神出鬼沒。

◎在這句訊息後關鍵字「神出鬼沒」打開

某甲【神出鬼沒】:不對不對,這句口令不是對我說的,必需要找到我們組織的某乙,然後對他說出這個口
令,就可以加入我們的組織了。

因此在這個地方我們可以看到,在開始的時候玩者和某甲交談時會先獲得「口令」這個關鍵字,接下來這個關
鍵字就會出現在交談的選單中。而玩者選擇口令這個關鍵字之後,就可以再獲得進一步的資料以及「神出鬼沒
」這個關鍵字。之後再以神出鬼沒這個關鍵字向某甲詢問,就可以得知這個關鍵字的使用方式。接下來我們再
看看某乙方面的資料:

某乙【無關鍵字】:對不起,我什麽事都不知道,
請你不要來打擾我好嗎。

某乙【神出鬼沒】:你知道我們的口令,那麽你可以加入我們的組織了。我們的任務是要推翻目前的政府, 希望你也為這件事盡一份力。

◎成功的加入判亂組織了

從這里我們可以看到,若是玩者在還沒有獲得神出鬼沒 這個關鍵字的時候來找某乙交談,那麽是無法從某乙的 口中獲得任何的消息。但是當玩者從某甲的口中獲得了「神出鬼沒」這個關鍵字之後,在交談的選單中就會出 現這個選項,選擇後從某乙處就可以得到回應,也就可 以加入判亂組織了。這一種的交談訊息結構,也就是關 鍵字系統真正能夠發揮作用的地方。

在設計關鍵字系統的時候有一件事要注意,那就是需要 適時的將某些沒有作用的關鍵字關掉。就以上面的例子 來說,當玩者成功的加入了判亂組織之後,這一部份的 事件可以說是已經結束了,此時就可以將「口令」以及 「神出鬼沒」這兩個關鍵字關閉。不過若是這兩個關鍵 字還有作用的話,就不可以這麽輕率的關閉它。

說到這里也許有人會說其實這種劇情不需要用關鍵字系 統,就算是單純的單句式結構就可以處理了。但是請各位 想想看,這里的劇情若是當玩者一和某甲交談,某甲就將 所有的消息一股腦的都說了出來,那麽不是很沒有 意思嗎?除此之外,關鍵字系統在玩者被游戲卡 住的時候,還可以讓玩者重復詢問相同的關鍵字來獲得訊息, 比起單句式結構那種不小心沒有看到就會被卡住的情況來說, 是不是也顯得比較人性化呢?

前面所說的都是關鍵字系統的好處,但是為什麽目前的游 戲很少使用關鍵字系統呢?那都是因為要設計關鍵字系統的 話交談的資料結構就繪變得比較復雜了。各位想想看,原本 在規划交談的訊息資料時只要針對每個游戲 中的角色,配 合不同的劇情段落配上訊息就可以。但是在關鍵字系統中, 設計者必需要將關鍵字妥善的分配到
各地的劇情對話中,才能使關鍵字系統真正的發揮作用, 因此在設計劇情的時候有比較多的困難。 除此之外,目前 國內的公司大多有將游戲推向國外的計划。由於中國字並不 是拼音系的文字,因此這些關鍵字 一但翻譯成其它國家的 語言(像是韓文、英文等等)時,就會發生翻譯出來的單字 變成好長一串的文字,若是 勉強的將一串文字刪成適度的 長度時,又會有可能會文不對題的問題(這種情況就好像 魔法門三代中的問題被變成中文的問題那樣奇怪),或是 因此要增加文字顯示的區域。也因為這些原因,使得國內 的公司盡量的不去 碰這樣的系統,以免自找麻煩。

事實上關鍵字系統其實是個擴展性相當大的系統,就連游樂器的游戲中也曾經有采用過這樣的系統。正因為它對於劇情的掌握比單句式的要強,以及它更結構化的 資料結構,它絕對是一個完美的交談系統。

 

 

10 完成制作的關鍵:推動力
-------------------------------------------------------------------------------

我最近聽到一個說法:每開始制作50個游戲,最終只有一個游戲能制作完成。這個說法可以由幾個原因來解釋。制作者可能太超前了以致於在技術上完成不了;項目太復雜了或制作者們在協同工作時有分歧並導致開發停止。還有更多的原因顯示了這種可能性:為什么98%的游戲不能最終完成。

然而,我堅信以上的這些問題的影響都比不上在進度中失去推動力。成功的游戲制作者與失敗的游戲制作者之間的區別並不是因為前一組成員更有才能,更聰明或更有經驗。僅僅是因為前一組成員能完成他們的游戲。


1.聰明,才智和經驗
聰明,才智和經驗在你的游戲制作過程中和生活中將幫助你克服困難。讓我們看看,簡單地擊鍵動作並不需要大腦的思考活動,但它需要一些你正在使用的計算機語言的知識,對操作系統的一些概念。大多數知識都能通過閱讀幫助文件,相關的書籍,一些示例的源代碼來獲得,並且你並不需要透徹了解每樣事件是如何運行的。

舉個現實生活中的例子,錄像機有很多復雜的功能,你可能並不會用這么多的功能,但你仍能很好地使用錄像機,使用遙控板上的放,前進,后退這幾個鍵來操作就足夠了。其它功能你並不需要掌握如何操作,那么你並不需要。編程也一樣,做很多事情並不需要透徹地了解每個方面。當你進入一個新的領域,通常會閱讀相關的資料來獲得所需的信息。我學習一門計算機語言的方法是:簡單地運行示例程序並不斷地修改它,運行它,看看運行結果,來最終了解這種語言的。

我並不暗示你不需要去努力學習,只是舉例說明這些知識並不是決定性的。如果你不了解你的游戲軟件和操作系統將怎樣配合工作,那么你就需要去了解操作系統本身。盡可能多地去了解你的操作環境,直到你感到能夠自如地工作,只會偶爾遇到幾個問題時為止。


2.麻煩和推動力
社會的各行各業中,一個項目的完成都需要參與者的努力工作,項目完成所需的時間越長,人們就越可能會遇到更多的麻煩。游戲制作過程也不例外。

游戲制作工業具有多變的本質,大眾對游戲畫面的要求,對某種游戲類型的喜愛,對游戲趣味性的要求,會經常發生變化。所以在制作期間及時了解業界的最新動態和互相交流是必須的。否則當你開始制作一個游戲,經歷過一個較長的開發周期后,你制作的游戲可能已經落伍了。

當你是獨立制作游戲時,每次當你遇到一個問題時都可能會落入困境,特別是沒有外部的支持時。制作中若有一個階段達不到預期的目標,都可能會令你泄氣。這對獨立游戲制作來說是致命的。

對不同的人來說,解決困難的動機是不同的。對有些人來說可能是需要成功以得到其它人的尊敬,或者得到一些報酬,金錢或其它形式的。對其它人來說,動機僅僅是一種創作自己心目中游戲的渴望。無論你的動機具體是怎樣的,你都需要依賴它,在制作過程解決遇到的各項麻煩。忽視麻煩或中止開發一段時間,這簡直和取消項目是一樣的。所以制作者必須習慣於面對各類難題。

保持推動力的一個方法是不斷地嘗試制作新的事物。每當你制作完成一個作品后,無論對你的自信心還是工作經驗來說,都會更上一個台階。如果你做了大量的工作但不能及時地看到結果,這會令你很不滿意,並逐漸喪失推動力。如果你能及時地看到你的工作對作品的改變,那么制作人與作品之間的這種互動性很容易就能使你保持較高的推動力。這就是為什么強烈建議那些游戲制作的新人從一個小項目開始的原因。長周期的大項目往往需要一周或一個月的時間才能看到變化,這往往使新手變得沮喪,並最終不能完成他們的第一個游戲。


3.中斷一會
在工作中適當地中斷一會是十分重要的。如果你不適當地休息一會兒,你的注意力會漸漸變得分散,工作效率會下降。我曾看過關於注意力方面的研究報告,它指出大多數人在40分鍾持續的做某件事后注意力將變得分散。很明顯,每隔40分鍾就休息15分鍾將會浪費大量的時間。然而僅僅是去拿些飲料喝或去察看有沒有新郵件到來,或者做任何別的事,都會幫助你長久地保持對工作的熱情。

在高強度的工作后,最好中斷較長的一段時間。如果你遇到了一個特別困難的問題,那么最好在你開始工作前先休息一會,再解決它。短暫的休息能使你集中精力來工作,但是在你處理一件事的過程中最好不要中斷,要一鼓作氣。如果這個事件處理的過程很長,那么在完成一個階段后再休息,並且做好中斷點的記錄,以便於下次能容易地接下做。當你在工作時,適當地中斷一會對保持你工作的動力是很重要的。長時間的工作而不休息更有可能使你的游戲半途而廢。

現在來了解什么時候該休息,什么時候該繼續工作。如果你處在一個工作氣氛很濃的環境中,而且制作的進度很快,那么這時你最好盡你的所能持續地工作,把你所有的熱情都投入其中,渴望產品的最終完成日。產品的最終完成日就像燈塔的頂部一樣,在那里,你能夠回想起你在制作過程中所有的努力。要記住,當你離開你的工作時必須確定,這個中斷點在你回來后能夠很容易地繼續下去。這意味着如果你是一個程序員,若你的代碼在編譯時有一些錯誤,那么最好不要把這些錯誤留到明天再去解決,因為這會使你忘記是什么原因導致了這些錯誤。


結論
在你制作游戲時會遇到很多的障礙,你必須學會面對它們。推動力確實是完成你游戲的關鍵所在,要牢記,成功的游戲制作者首先必須先完成他們的游戲。

 

11 十分鍾學會 DirectX 中 的 COM 技術
-------------------------------------------------------------------------------


DirectX 是按照微軟的COM(Component Object Model) 搭起來的。設計COM是希望它 能提供一個更安全,易升級,可移植的軟件模塊。COM用的面向對象的模式比一般的 C++更嚴格。例如,COM只能永遠通過成員函數(member function)進行訪問,並且不 能擁有公用數據成員(public data members)

COM異常嚴格地對待對象(object)和接口(interface). 而且就不讓你直接訪問對象, COM竟然都不給對象起名字,讓你老老實實通過接口來訪問對象。在DirectDraw編程 中,我們講:“訪問對象”,其實都是在訪問接口。叫慣了也就不改了。

所有的COM接口都從 IUnknown 接口中派生出來。“I”打頭是COM接口的標識(I for Interface). 所有的DirectDraw接口都以“I”打頭,但莫明其妙的是在很多編程手 冊中都把“I”略去。所以看書時要做到心中有愛(I)。

IUnknown 接口提供三個成員函數,其余所有的COM接口,都繼承這三個函數。
AddRef()
Release()
QueryInterface()

AddRef() 和 Release() 支持COM的一個特色功能,名字叫“活着封裝”(lifetime encapsulation). “活着封裝”是一個協議,用來讓對象在崩潰時(destruction)自 己負責自己的后事。

“活着的時候”(lifetime) 每個對象內部有一個值用來跟蹤記載自己用過的指針(pointer) ,或引用(reference)。當這個對象建立時,該值為1。然后隨着對它的調用/被調用, 該值遞增。反之遞減。當本身崩潰時(destroy itself),該值為0。

AddRef()就是用來加計數器的。你可能不用親自調用它。你在用DirectDraw API 時, AddRef()就被自動調用了。

Release()則是對着干。減值。你常常要親自用到它,因為程序可能會異常退出AddRef的 作用域(scope)。如出錯控制中。

QueryInterface()用來問COM一個接口是否可用。如可用,則返回一個相應接口的指 針。

問對象是否支持一個接口用QueryInterface, 那么怎么問?當然要知道接口的ID。 我們用GUID來表示,GUID = Globally Unique IDentifier. (全局單一證認)。GUID是 一個128bits的數。所有DirectX中接口的GUID值都可在DirectX的頭文件中找到。

這就是全部了。怎么樣,有概念了吧:-) he he he.

 


12 windows 游戲設計時的單任務與多任務處理
-------------------------------------------------------------------------------

單 任 務 處 理

Windows 最 傑 出 的 功 能 之 一 是 能 夠 同 時 運 行 多 個 程 序, 但 有 時 也 會 讓 人 感 到 頭 疼, 特 別 是 對 於 那 些 習 慣 於 完 全 控 制 計 算 機 甚 至 時 鍾 頻 率、 非 常 自 信 的 游 戲 程 序 員( 當 然, 我 們 的 確 在 乎 那 些 沒 禮 貌 的、 在 退 出 時 不 恢 復 正 確 的 系 統 時 間 的 游 戲。 但 是 幸 好, 現 在 我 們 可 以 忘 掉 這 些 了)。

---- 在 多 任 務 環 境 下, 游 戲 程 序 員 需 要 注 意 三 個 大 的 負 效 應:

當 游 戲 失 去 焦 點 而 進 入 后 台 后, 其 執 行 不 得 不 被 掛 起( 可 以 在 Moby Dick Windows 中 使 用“ 中 止 的” 變 量 觀 察 它 是 如 何 工 作 的)。 如 果 是 一 個 實 時 游 戲, 程 序 員 當 然 希 望 它 被 懸 掛。 但 在 回 合 制 游 戲 中, 當 玩 家 去 做 其 它 事 情 時, 程 序 員 可 能 不 希 望 計 算 機 一 方 作 任 何 動 作, 但 希 望 后 台 的 人 工 智 能(AI) 運 算 依 舊 執 行。


其 它 的 任 務 占 用 CPU 時 間, 結 果 造 成 我 們 不 能 一 直 控 制 游 戲 中 事 情 發 生 時 的 速 度。 我 們 將 在 后 面 討 論 這 個 痛 苦 的 問 題。


每 當 游 戲 回 到 前 台, 程 序 員 不 得 不 重 畫 游 戲 窗 口。Windows 並 不 負 責 記 憶 它 所 覆 蓋 或 隱 藏 的 窗 口 的 內 容; 它 所 能 做 的 最 多 是 通 知 一 個 窗 口 需 要 重 畫 其 客 戶 區 域。 這 在 有 關 Windows 的 文 章 中 都 有 論 述( 參 見 WM_PAINT 的 內 容), 我 們 在 這 里 就 不 討 論 了。 事 實 上,Moby Dick Windows 並 不 恢 復 其 自 己 的 窗 口; 我 們 將 在 講 到 DirectDraw 下 的 雙 緩 沖 時 看 它 是 如 何 實 現 的。

程 序 中 的 多 任 務

---- 盡 管 Moby Dick DOS 在 使 用 中 斷 處 理 程 序 時 展 示 了 內 部 多 任 務( 或 者 說 多 線 程) 的 一 種 原 始 形 式, 但 是 該 程 序 仍 然 沒 有 突 破 DOS 的 單 主 題 特 性, 即 在 一 個 時 間 只 做 一 件 事 情。 有 些 DOS 程 序 的 確 作 到 了 真 正 的 多 線 程, 但 是 那 需 要 非 常 巨 大 的 編 程 工 作。Windows 95 SDK 使 這 項 工 作 簡 單 了 許 多, 把 線 程 放 進 每 一 個 游 戲 開 發 者 的“ 錦 囊” 之 中( 如 果 讀 者 還 不 熟 悉 這 個 概 念, 那 么 簡 單 說 明 一 下, 一 個 線 程 就 是 程 序 的 一 部 分, 它 執 行 時 獨 立 於 其 它 的 部 分, 並 且 不 需 要 與 其 它 部 分 同 步。 線 程 不 是 由 中 斷 來 驅 動 的; 它 們 只 是 在 每 一 次 Windows 給 它 們 CPU 時 間 時 繼 續 其 執 行。)

---- 在 下 列 情 況 下, 可 能 要 考 慮 實 現 獨 立 的 線 程:

允 許 后 台 AI。 就 算 是 用 戶 正 忙 於 來 回 移 動(moving pieces around)、 打 開 對 話 框 等 事 情, 計 算 機 也 能 夠 考 慮 其 下 一 步。 處 理 這 類 線 程 非 常 方 便, 因 為 它 不 需 要 與 其 它 的 事 情 同 步。


預 先 加 載 數 據。 例 如, 當 玩 家 正 努 力 向 下 一 級 奮 斗 時, 使 一 個 線 程 負 責 讀 取 文 件 並 准 備 好 游 戲“ 世 界”。


給 予 時 間 緊 迫(time-critical) 的 任 務 優 先 權。 我 們 將 在 后 面 會 到 這 個 主 題。

游 戲 循 環

---- 游 戲 循 環 的 概 念 在 各 編 程 環 境 下 都 比 較 相 似。 第 一 步, 需 要 獲 取 輸 入: 可 以 是 輪 詢 它, 等 待 它, 或 者 在 它“ 運 行” 時 通 過 中 斷 或 一 個 消 息 隊 列 攔 截 它。 第 二 步, 處 理 該 輸 入, 並 且 把 它 變 成 一 個 在 游 戲 中 有 實 際 意 義 的 動 作, 如: 使 飛 機 傾 斜 飛 行 或 小 卒 向 前 走 一 步。 然 后, 把 結 果 顯 示 出 來。 當 然, 在 這 個 主 題 中 也 要 求 精 雕 細 刻 和“ 變 奏 曲”, 包 括 計 算 AI 的 移 動、 把 控 制 權 從 一 個 玩 家 移 交 給 另 一 個 玩 家、 檢 查 勝 負 等 等。

---- 然 而, 在 Windows 和 DOS 下 實 現 循 環 的 機 制 迥 然 不 同。 每 個 Windows 程 序 都 建 立 於 一 個 消 息 循 環 之 上。 盡 管 一 個 游 戲 循 環 可 以 建 立 在 消 息 循 環 之 上, 但 是 這 兩 者 仍 有 本 質 的 差 別。

Moby Dick DOS 的 循 環

---- Moby Dick DOS 演 示 了 一 種 簡 單 的 游 戲 循 環, 在 這 里 我 們 所 做 的 工 作 是: (a) 檢 查 是 否 有 什 么 東 西 要 移 動, (b) 移 動 它, (c) 顯 示 結 果。

while (!gamedone)

//調用時間程序 -如果時間未到,則沒有任何響應。

AhabMoved = Move_Ahab();

//僅當 Ahab沒有移動時移動 Moby Dick。

//否則它們可能擦肩而過卻不能攔截。

if (!AhabMoved) Move_Moby();

//如果有任何一個移動,更新屏幕,

//並檢查是否有勝利和失敗。

if ((MobyX != OldMobyX) || (MobyY != OldMobyY)

|| (AhabMoved))
{
UpdateScreen();

if ((MobyX == AhabX) && (MobyY == AhabY)


&& (painted[MobyX][MobyY]))
{
gamedone = 1;

cprintf("/a");

cprintf("You win!");

}

if (TimesUp <= 0) { cprintf("/a"); cprintf("Time's up!"); gamedone="1;" } if (raw_key="=" MAKE_ESC) { gamedone="1;" progdone="1;" } } //結束更新 } //結束游戲內部循環 (while !gamedone) Moby Dick Windows的循環 從表面看來,好像沒有多大的差別: do { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message="=" WM_QUIT) break; //唯一的退出循環的出口。 TranslateMessage(&msg); DispatchMessage(&msg); } else { if ((MobyX !="OldMobyX)" || (MobyY !="OldMobyY)" || (AhabMoved)) { UpdateScreen(); if ((AhabX="=" MobyX) && (AhabY="=" MobyY) && (painted[AhabY][AhabX])) { Control="MessageBoxEx(hwnd," "You caught Moby! Play again?", "Call Me Ishmael", MB_ICONQUESTION | MB_YESNO, 0); if (Control="=" IDYES) InitializeGame(); else break; } } //如果有人移動了 } //如果屏幕已更新 } //結束循環 while (TRUE);

---- 前 面 已 經 提 到 過, 這 里 沒 有 檢 查 是 否 運 行 超 時, 請 忽 略 它, 筆 者 在 Windows 版 中 未 實 現 它 是 為 了 避 免 令 人 煩 惱 的 中 斷。 中 斷 並 退 出 無 限 循 環 的 機 制 有 一 點 兒 而 且 並 不 重 要。 我 們 把 精 力 集 中 在 消 息 循 環 本 身, 所 以 把 其 它 的 無 關 代 碼 都 刪 掉, 只 留 下 最 基 本 的 部 分:


do

{

if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))

{

if (msg.message == WM_QUIT) break;

TranslateMessage(&msg);

DispatchMessage(&msg);

}

else DoSomething();

}

while (TRUE)


---- 這 是 一 個 非 常 典 型 的 消 息 循 環。 唯 一 有 點 特 殊 的 地 方 就 是 它 使 用 的 是 PeekMessage 而 不 是 GetMessage。

GetMessage 與 PeekMessage 的 比 較

---- 為 什 么 要 用 PeekMessage 呢 ? 原 因 很 簡 單,GetMessage 等 待 一 個 消 息( 就 像 _getch), 而 PeekMessage 不 是 這 樣( 就 像_kbhit)。 請 考 慮 下 面 的 循 環:


while (GetMessage(&msg, NULL, 0, 0))

{
// 我 們 並 不 進 入 括 號 內 部, 直 到 有 一 個 消 息

TranslateMessage(&msg);

DispatchMessage(&msg);

DoSomething()

}

// 當 GetMessage 返 回 NULL 時, 退 出 該 程 序

return msg.wParam;


---- 在 這 里,DoSomething 不 會 完 成, 除 非 一 個 消 息-- 或 許 多 消 息-- 被 放 入 隊 列 中 並 被 處 理。 如 果 DoSomething 恰 好 產 生 一 個 消 息, 例 如, 如 果 它 更 新 了 屏 幕 並 且 因 此 而 產 生 了 一 個 WM_PAINT 消 息, 那 么 好 了, 水 泵 注 水 后 將 開 始 啟 動 了。 要 使 DoSomething 可 靠 地 完 成 其 工 作, 這 並 不 是 一 個 好 方 法, 它 使 代 碼 有 點 混 淆, 但 它 工 作 的 還 不 錯。

---- 相 比 之 下,PeekMessage 則 無 論 是 否 有 消 息 在 等 待, 只 要 檢 查 一 下 消 息 隊 列 就 完 成 其 操 作(yields the floor)。 在 我 們 的 例 子 中, 我 們 實 際 上 是 使 用 PeekMessage 來 處 理 消 息 的( 通 過 分 發 它 所 找 到 的 每 一 個 消 息 並 使 用 PM_REMOVE 參 數 從 隊 列 中 清 除 它)。 同 下 面 同 樣 有 效 的 代 碼 相 比, 它 要 更 加 直 接:


if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))

{

if (!GetMessage(&msg, NULL, 0, 0)) break;

TranslateMessage(&msg);

DispatchMessage(&msg);

}

else DoSomething();


---- 在 這 里 有 非 常 重 要 的 一 點 要 說 明, 我 們 的 偽 代 碼 DoSomething 是 獨 立 於 消 息 的; 無 論 隊 列 中 送 出 的 什 么 消 息, 甚 至 無 論 有 沒 有 消 息 在 那 兒, 它 都 將 執 行。 在 Moby Dick 中, 我 們 將 屏 幕 更 新 和 勝 利 條 件 的 檢 查 放 在 這 里, 因 為 在 這 里 檢 查 一 個 或 多 個 消 息 被 響 應 后 是 否 需 要 更 新 屏 幕 或 是 否 達 到 勝 利 條 件 很 方 便。

---- 那 么, 消 息 循 環 就 是 游 戲 循 環 嗎 ? 從 抽 象 的 角 度, 是 的, 因 為 它 是 大 的 齒 輪, 帶 動 那 些 小 的 齒 輪。 但 是, 盡 管 把 一 些 函 數 調 用 放 在 此 處 可 能 比 較 方 便,Windows 編 程 規 則 卻 要 求 任 何 響 應 一 個 消 息 的 動 作 都 應 該 放 在 消 息 響 應 程 序 中( 就 是 說, 放 在 窗 口 過 程 中)。 在 一 個 實 時 游 戲 中, 絕 大 多 數 的 動 作 發 生 在 一 個 或 多 個 WM_TIMER 消 息 響 應 程 序 中。 回 合 制 游 戲 則 常 常 在 輸 入 消 息 的 響 應 函 數 中 做 大 量 的 工 作。

---- 事 實 上, 程 序 員 經 常 會 發 現 在 主 消 息 循 環 內, 除 了 標 准 的 翻 譯 和 分 發 消 息 任 務 之 外 什 么 事 情 也 沒 做。 如 果 這 樣, 就 可 以 回 過 頭 來 使 用 GetMessage, 因 為 除 了 響 應 消 息 之 外, 什 么 事 也 不 需 要 發 生。

---- 總 結 一 下, 實 現 循 環 有 兩 個 關 鍵 點:

Windows 消 息 循 環 與 游 戲 循 環 不 相 同。 游 戲 循 環 依 然 存 在( 至 少 在 概 念 上 如 此), 但 是 同 DOS 下 的 情 況 相 比, 它 的 部 件 在 代 碼 中 更 加 分 散。


如 果 想 要 在 消 息 循 環 內 執 行 任 何 獨 立 於 時 鍾 消 息 和 輸 入 消 息 的 代 碼, 請 使 用 PeekMessage 而 不 是 GetMessage。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com