OPEN PACMAN

2009年2月9日 星期一

在此說明下閱讀本文之前所需要的如下:

擁有一台健在的電腦、能夠上網的網路、鍵盤滑鼠週邊配備,
以及軟體GameMaker、英漢字典,具備基礎計算機知識還有程式語言。
最後請抱持著你的榮譽感還有熱血以及三百元美金匯入本人的海外帳...(眾毆)

GameMaker請移駕http://www.yoyogames.com/下載,沒有中文版本,目前大陸網友正在努力漢化中。但是GameMaker的英文單字並不算難,所以不用擔心你沒有全民英檢證照。

進行安裝後打開你的軟體,會看到類似如下圖的畫面
逐下說明,請點開圖看。
1 是工具列,負責檔案的儲入存出以及說明等等細項的設定
2 是快捷列
3 是負責管理素材以及程式碼還有物件
4 是負責編輯物件素材

本日的課題是製作小精靈吃豆人,具體的遊玩方法我想沒有人不知道。但是在設計遊戲的時候我們必須先決定遊戲的方向以及內容,必須有現成的想法以書面呈現。雖然現在是個人獨立作業,但是還是可以嘗試寫一份企劃書,這對企劃而言非常有幫助。同時你必須懂些許的程式語言概念,很多人問我程式語言怎麼學,我說你去買本書來看就是了。這裡不需要學會資料結構、非線性函數之類複雜的玩意兒,對於以下語法我還是會加以說明。

首先我們先匯入素材吧。

在3的Sprite資料夾上點右鍵,有個CreateSpirte。Spirte字面上是精靈,在電玩遊戲中人物跑步動作其實是由數張點陣圖連串而成的動畫。我們會看到在4區會有一個新的視窗,決定Name的名稱,這裡建議使用英文。用Load Sprite匯入素材,在GameMaker中已經有內建的素材供您使用,我們選了一張看起來像是小精靈的圖片。
transparent請打勾,那意味著Sprite的透明。Transparent會取最角落的顏色作為透明。因此小精靈週遭的深綠並不會留下。PreciseCollisionChecking是「精準碰撞」我們必須取消,原因在此保留為伏筆,有興趣的讀者可以識著打勾看看。SmoothEdges是邊緣平滑化,皆可。Preload texture為載入紋理與否,皆可。
Origin則是這個Sprite的中心,這跟放置素材有關係,在此我們保留。BoundingBox可就有趣了,我們稱之為碰撞盒。他作為在碰撞時候的範圍限定,如果碰撞盒只在中心一像素,只要不接觸碰撞盒就不算碰撞。這是碰撞盒的定義,在此選擇Automatic(自訂)或者是Full image(全圖),Manual(自訂)以後或許會用到。
當然小精靈如果只有一種圖片那也太無趣了,在上下左右移動時小精靈的開口方向不同才是原味的重現,讀者可以匯入上下左右並且定義名稱。在此我的Sprite是很直接的命名為up、down、right、left,同時注意,精準碰撞都必須取消喔~
接著匯入四隻Ghost、Wall還有Dot的sprite以及定義其屬性,詳細的內容請以上類推。
接著是匯入聲音,我們選擇的是內建素材Beep,讀者可以自行測試看看每種效果,十分有趣。
重點來了,
決定一個遊戲靈魂的Object才是重頭戲,在此點擊3處的Object資料夾創建新的Object,這裡一共要製造Wall、Dot、四隻Ghost還有主角Player,並分別決定他們的Sprite以及屬性。在這裡需要說明的是,GM的特性與C++語言十分相近,也就是物件導向。讓每個物件皆擁有他的行為以及屬性,並且彼此運作著。GM擁有這類的概念,不需要程式語言也可以達到相同的效果。
其中屬性的效果最為顯著,我們可以這麼寫ball.speed球的速度屬性、ball.visible球可不可以看見。Visible指的是「可見」,而Solid指的是「固體」,Parent指的是父親,在程式語言中代表著「父子」的概念,Mask則是採用相同的sprite,後兩者現況皆用不到。
我們將焦點轉向中間的兩個大框,在物件中最重要的概念是Event(事件),一旦物件的事件發生就會產生對應的Action(動作)


在GM的內建說明書當中,就已經附有各種情況的範例說明。如果你懶得看英文,可以上網收尋中文的說明範例。

至於這些物件該有怎麼樣的事件以及動作,讓我們先來構思一下整體遊戲的流程。
主角出現,擁有三條性命
吃到dot便獲得10分、dot消失
如果被Ghost碰觸到,便返回起始座標
主角出現
(loop,直到三條性命皆用光)
人說,好的開始是成功的一半。
我們先將主角的事件完成,主角會碰撞的對象有:Dot以及四種顏色的Ghost,還必須有操作系統以及圖片的變換。

【遊戲的開始】
Event:Create
Action:Code
{
gamepoint=3;
score = 0;
sound_play(beep);
}

GM的設計上,與其他語言不同的是不需要宣告。
我們將數值初始化後,以Beep的聲音作為遊戲開始的訊號。

【操作系統】
Event:Keyboard-Anykey
Action:Code
if (keyboard_check(vk_left)&&place_free(player.x-4,player.y))
{
sprite_index =left;
player.image_speed=0.5;
player.hspeed=-4;
player.vspeed=0;
}
if (keyboard_check(vk_right)&&place_free(player.x+4,player.y))
{sprite_index =right;
player.image_speed=0.5;
player.hspeed=+4;
player.vspeed=0;
}
if (keyboard_check(vk_up)&&place_free(player.x,player.y-4))
{
sprite_index =up;
player.image_speed=0.5;
player.hspeed=0;
player.vspeed=-4;
}

if (keyboard_check(vk_down)&&place_free(player.x,player.y+4))
{
sprite_index =down;
player.image_speed=0.5;
player.hspeed=0;
player.vspeed=4;
}

我們逐行說明,這Code只要玩家按下任何案鍵都會執行,那麼怎麼決定控制方向呢?很簡單。if語法代表著「如果條件為真(1),就執行」。 if (keyboard_check(vk_left)&&place_free(player.x-4,player.y)) 這行代表著「如果按下的按鍵為左且玩家當前X座標-4之位置為空位」就執行{}內的語法。詳細的說明在說明文件當中就對語法進行了介紹。此外,GM提供許多遊戲系統所需的變數以及數據,玩家不需要自行定義。

sprite_index =right 是讓當前的sprite索引變換為right這個sprite。
player.image_speed=0.5 而這個是指定Gif的動畫速度。
player.hspeed-=4 將橫向座標速度定義為-4
player.vspeed=0 將垂直座標速度定義為0

※注意:X與Y座標起點皆為畫面的左上角,與人類的方位定義不同。

【碰觸】
將移動系統製作好後,我們還需要碰撞牆壁的事件。
Event:Collision Wall
Action:Code
player.speed=0;
sound_play(beep);

如果碰撞到牆壁就會將玩家速度定義為零,並且撥放聲音。
在這裡筆者將解釋上文中最重要的「精準碰撞」為什麼需要取消的問題。GM的設計者十分厲害,發覺在玩家設計遊戲的途中有許多種不同的遊戲,如果用BoundingBox那麼無論什麼樣的素材都只會變成一個正方形的圖塊,因此精準碰撞的功能便這麼出現了,我們的素材Pacman就能夠以一個圓球的圖形來處理碰撞。但是這樣出現了一個問題,就是在轉角處的時候,圓球會卡在牆壁的尖角,而造成遊戲的不順暢。

筆者為了解決這個問題,曾經花了一整個晚上才發覺精準碰撞的用途。像是炸彈超人、吃豆人這類的益智遊戲並不能使用精準碰撞,因為他的移動方式是「一格一格」的。當筆者發覺問題的時候才恍然大悟。

Event:Collision Ghost(四個皆需要)
Action:CodeMeet
我們在3處的Scripts新增一個函數,並且命為「Meet」,在此筆者將程式碼貼上來。
if (gamepoint>0)
{
gamepoint -=1;
player.x=player.xstart;
player.y=player.ystart;
player.sprite_index=oringin;
speed=0;
}
else
{
gameover = 'gameover' ;
show_message(gameover);
game_restart();
}

每當碰觸到鬼魂的時候,我們會將GamePoint進行減一的動作,並且將玩家的座標回歸於遊戲剛開始的時候,先決條件是GamePoint必須大於0。否則的話,會顯示GameOver的訊號,並且將遊戲重新開始。

【當我吃到Dot的時候】
Event:Collision dot
Action:code
if (instance_place(player.x,player.y,dot)>0)
{
dotid=instance_place(player.x,player.y,dot);
if ((dotid).visible==1)
{
sound_play(beep);
score += 10;
(dotid).visible=0;
}
}

instance_place(x,y,obj)是GM的內建函數,目的在偵測該座標是否有指定物件的存在,如果有的話就會回報其物件的ID。在我們配置房間的時候,就算是同樣的物件也會有不同的ID,以此來控制特例。不至於讓所有物件都進行同樣的事情,這樣的情況多半用在像是PACMAN的點數還有ARPG的眾多對手。instance_place函數如果沒有偵測到會返回None,因此我們需要用if語法來偵測是否有回報ID。並且在函數內定義新的數值dotid同等於該dot的id。

我們不採取銷毀事件的函式,因為筆者製作該遊戲的時候並沒有製造多個關卡,同時銷毀事件可能會讓遊戲變得更複雜,因此我們從Visible來下手。

如果該dot可以看見
分數+10,且將該dot定義為不可見

因此,遊戲中的dot並不是消失而是看不見。至於為什麼要這麼做呢?嘿嘿嘿,答案是─耐玩度!我們接著看下去。

【四個鬼魂的處理】
在終章,筆者先在此恭喜您接近終點了。
在此有兩種方法可以選擇,鬼魂可以擁有AI或者是走固定的路徑。
筆者在此選擇了比較簡單的固定路徑,我們在3處的Path製造了四個路線分別給四隻鬼魂使用,一旦開始就會按照路線行動。

配置路徑的方法請用你的滑鼠,並且將Closed封閉曲線關閉。配置好後記住該路徑屬於哪個鬼魂。
Event:Creat
Action:Set the path for the instance
路徑的詳細設置當中可以決定行進速度以及到終點的時候會採取什麼動作,在此選擇reverse,也就是走回去。
Event:Step
Action:Code cool
我們新增一個新的scripts叫做cool,他的目的在於偵測。
if (collision_circle(x,y,120,player,0,1)>0)
{
if (path_position==1)
{path_speed-=0.1;}
else if (path_position!=0)
{path_speed+=0.1;}
}
collision_circle也是內建函式之一,他能夠偵測方圓內是否有指定物件的存在,一旦方圓120像素內有玩家就會開始加速。但是路徑上有些限制,就是到達終點與前往終點時的速度會有反轉的效果,因此此處需要增加一些判定式。這個函數目前並不夠完善,有的時候會反加速逃離玩家。相對的如果用AI就萬事OK了。雖然路徑是固定的、還有邏輯上的錯誤,但是彼此交錯的細膩網路讓玩家恐怕有些措手不及。
Event:Step
Action:Code Ghostdot
我們新增了一個函數命他為Ghostdot。
dotid=instance_place(x,y,dot);
if (dotid>0)
{
if (dotid.visible==0)
{(dotid).visible=1;}
}
聰明的讀者應該很快就看出這個函式的作用,他與Player的吃dot正好相反,能夠將玩家吃過的dot修補回來,因此能撐越久的玩家分數可能會更高,增加了遊戲的耐玩度。
實際上,筆者為了發現Bug花了一小時。
dotid=instance_place(x,y,dot)這個函數可能會返回none值,意味著「沒有」,筆者曾經很糊塗的忘記檢測,結果dotid就成為了none,自然visible就不存在了。
最後物件處理完後,就是快樂的配置房間的時候!
恭喜你完成了一款遊戲!
最後,如果有人想要本人的GMK以做參考或者對本範例有問題,可以寫信到zin.ze.z0@gmail.com,在這裡要強調的是,本文中許多問題皆可以在網路以及程式內附的範例取得解答。如果你不看說明書來問我會生氣喲~

  © Blogger templates Newspaper by Ourblogtemplates.com 2008

Back to TOP