Construct 2教學-第4節
HJ Online Learning Center
APP開發線上教學
  • Register

Construct 2 教學-第4節

第4節-敵人角色設計

 

大家好,我是傅老師。歡迎來到我的Construct 2教學。

上一節我們設計了自己的外太空地圖,包括自製的地面、斜波、浮台、跳穿平台。此外我們利用WebGL的特效做出了熱燄四射的太陽,以及滾燙的火海背 景,讓畫面栩栩如生。 這一節我們要放上遊戲中最重要的成份,也就是敵人角色。本節開始進入到程式的編寫,其中最重要的概念是條件判斷式,以及For迴圈的應用。

 
 
 
 


 

4-1 碰撞的概念(collision)

在平台遊戲中,所呈現出最重要的物理現象,就是兩個物體的碰撞。我們必須不斷的偵測所有物件的碰撞狀況,來決定遊戲該如何進行下去,舉例來說:偵測 到主角與敵人的碰撞,就要讓主角損血; 偵測到主角跟斜坡的碰撞,就要讓主角順勢向上走;偵測到主角跟寶物的碰撞,就要讓主角撿起寶物。我們可以說,平台遊戲根本是一直不停的處理著各個物件的碰 撞,碰撞處理的程式效率、合理性、 趣味性,是一個遊戲工程師必須積年累月取得的經驗,他會直接貢獻在遊戲的手感上。

本節我們會基於前一節的作品,加入精簡的代碼,讓您以最快的時間來實作基本碰撞。

在遊戲裡,每個Sprite都有各自的碰撞檢測框(collision box,後面簡稱碰撞框),更細膩的作品則會用更耗資源的碰撞檢測多邊形(collision polygon,後面簡稱碰撞多邊形)。 碰撞檢測的實現是不斷地檢查佈局上的碰撞框是否有重疊,(overlap),所以檢查碰撞的演算法是引擎的核心技術之一。幸運的是使用者不需要瞭解演算法 的細節,我們只需瞭解碰撞在邏輯上 的意義即可。以下讓我們以簡單的長方型碰撞框為例解釋碰撞邏輯:

碰撞檢測示意圖

在上圖左方,蛋蛋老師跟怪物各自有自己的碰撞框,兩者均以藍色標示。接著怪物向右持續前進,追上了蛋蛋老師,兩者的碰撞框發生重疊(粉紅色斜線 區)。一但發生重疊,C2就會判定出現碰撞關係, 立刻觸發(trigger)碰撞事件通知我們。觸發是一次性的,所以即使怪物繼續向前進而重疊部份持續增大,C2也不會再觸發事件通知我們。得要等到兩者 完全分離,C2才會重新開始對兩者的碰撞 關係產生觸發。

接下來讓我們加入一個敵人來實作碰撞框的概念。

 


 

4-2 加入敵人Sprite

請下載下面的Sprite包,解壓縮後會看到以e1為開頭的3組動畫---<e1_stand>、<e1_walk>、以 及<e1_stand>。 滑鼠左鍵雙擊編輯區空白處,加入一個新的<Srite>物件,將其名稱設為[E1](意指Enemy1)。接著請參照2-3節的步驟,以e1 系列動畫包分別為[E1]創建 "stand"、"walk"、"dead",共3組動畫。(請記得裁切、設置參數、修改原點)。另外Sprite包內提供了4組新的蛋蛋老師動畫--- <egg_stand>、<egg_walk>、<egg_throw>、以及<egg_dead>, 一樣比照2-3節的步驟將之載入至[Player]物件內(前兩組動畫與前幾節教學課所提供的不 同,務必要更新)。

http://www.memoryabc.com/c2_tutorial/L4_sprite.zip

stand與walk動畫需將無限循環(loop)設為"YES",否則播完一輪角色就會呆住不動。

為stand及walk動畫設置無限循環

動畫載入後依下面設置重調位置與尺寸,將[Player]初始動畫設為"stand",[E1]初始動畫設為"walk"。完畢

調整位置與尺寸

鏘鏘~現在我們有一個靶子敵人了,什麼?你說他是史瑞克?喔不,他只是長的像而已,我們叫他小史好了。現在按<F5>預覽,疑~為什麼 敵人浮在空中掉不下來?這是因為我們尚未替小史加入平台特性(Platform), 所以他完全不受"地心引力"的影響。請參考3-5的步驟,為[E1]加入behavior,設置如下圖。

未加入Platform行為的[E1]浮在空中

加入平台行為特性(Platform behavior)

加入平台行為特性(Platform behavior)

加完後按<F5>再次預覽,[E1]正確掉落至平台了。至此成功加入敵人Sprite。

 



4-3 基本敵人AI - 以碰撞框實現

在玩平台遊戲時,你一定碰到過一種超級"肉腳"的敵人,比如說像超級瑪莉的蘑菇,他除了在平台上"折返跑"之外什麼也不會。是的~折返跑正是最簡單 的敵人AI,接下來就幫小史添加折返跑的初級技能吧!

構思AI有一個不錯的方式,那就是用親身經驗來展開聯想。折返跑的邏輯可以參考小時候體育課的經驗來設計:小時候在體育課教到折返跑時, 老師會先在跑道上指定兩條邊界線。有了邊界線之後,老師會再給出折返跑唯一的一條規則:凡是碰到邊界線的人,就得開始朝相反方向奔跑。就這麼一條規則,真 是清楚又簡單。現在換到平台遊戲上,將上述規則以小史為主角進行邏輯化:

 

步驟1. 在平台上擺設兩個物件,分別代表左界線與右界線。(如圖例中之紅黃箭頭)
步驟2. 讓小史在左右界線間的任一點誕生,誕生後朝向初始方向前進。(圖例中小史的初始方向朝右)
步驟3. 當小史與任一界限碰撞時,依碰撞的界限來轉向。(撞到右界線要往左轉,撞到左界線要往右轉)

 

小史的第一個AI就是完成以上三個步驟,讓我們來逐步編入程式中。

使用轉向指示物件讓角色折返跑

步驟1

步驟1是創建兩個放置於邊界上的物件,傅老師選擇將此二物件製作為正方形,因為正方形便於繪製、便於擺放。請佈局清單選擇"Game"圖層,於編輯 區空白處雙擊左鍵,選擇新增Sprite物件,建立一個大小為48x48的紅色方塊,命名為[turn_R];再於同一圖層上建立一個大小為48x48的 綠色方塊,命名為[turn_L],依下圖為兩個物件設置參數。 爾後我們視[turn_R]為左界線,視[turn_L]為右界線。OK,步驟1完成。

設置左右界線

步驟2

步驟2是讓小史代表的[E1]物件具有自動向左右前進的功能,並且出生時預設為向右走。為了調整行進方向,[E1]必須知道並紀錄下自己目前行進方 向,這可透過對[E1]增加變數來紀錄此方向。右上角專案區點擊[E1],看到參數設定區,點擊<Instance variables>進入實件變數設定區。

修改實件變數

按下新增按鈕,新增一個實 件變數{dir}(dir為direction的縮寫),將其類型設為文字(text),預設值設為"right"。 未來我們想用程式來調整或讀取[E1]行進方向時,只要先檢查這個文字變數,就知道[E1]目前朝哪個方向走。

新增實件變數

新增實件變數dir

在繼續講課之前傅老師必須先補充一下物件(Object)與實件(Instance)的定義。實作遊戲的時候,常常需要讓某一類型(Type)的敵 人在佈局上重覆出現,但我們不會幫每一個敵人都獨立撰寫他自己的程式代碼,因為那樣太浪費系統資源。務實的方法是將常需重覆使用的資源以及程式碼提取出 來,統一設置給某個抽像物件。 而實際使用時再由抽像物件分身成為散落各處的實件,實件各自以獨立變數紀錄自己的狀態。(*1)

以下圖為例,因為[E1]物件帶有折返跑AI邏輯,所以他的每個實件分身都會折返跑。唯一不同的是,每個實件分身在瞬間跑的方向並不一樣,透過我們 剛才設置的實件變數{dir}, 就可以把每個實件各自的瞬間方向儲存起來。實件變數之初始值會在實件出生時便自動代入,本例中[E1]實例出生時會自動將{dir}設為"right"。

物件與實件示意圖

{dir}設為"right"只是一個標誌,我們需要製作一個無限迴圈來檢查此標誌並照著標誌移動[E1],For each迴圈是個不錯的選擇。C2的For each迴圈專門用來在選取範圍內逐一挑出實件, 分別進行後續動作。接下來請先依照指示步驟添加程式,添加完畢後我們再完整地進行解說。

首先增加For each事件。到頁面指示區切換至<Eventsheet1>點選<Add event>,選擇<System>物件,找到底下的<For Each>事件,以滑鼠左鍵雙擊點選,將目標物件指派給[E1], For each迴圈完成。

For each迴圈事件

指派For each迴圈之目標物件為E1

接下來在For each迴圈內加入子事件(sub-event)。在For each事件前面有個綠色回旋箭頭,以滑鼠左鍵單擊此位置,可選取此事件。選取好後按下鍵盤"s",會出現子事件選取畫面。

增加子事件

我們希望一進子事件就比對該實件的{dir}變數,比對變數可透過實件的<Compare instance variable>完成。滑鼠左鍵雙擊[E1],選取<Compare instance variable>事件,會跳出該事件的編輯窗口。將欲比較的變數設為{dir},條件式設為<=Equal to>,數值設為"right"(雙引號不可省略*2)。

比較實例變數之值

設置子事件參數

前述的子事件可判斷出[E1]實件目標方向是否向右,若為是,則我們應該移動實件,並且設置影像是否該翻轉。點選右方Add action,添加[E1]-><Simulate control>-><Right>,以及[E1]-><Set mirrored>-><Not mirrored>如下圖。

子事件添加完畢

方向的判斷結果可能是向右,也是可能是向左,接下來加入判斷向左的對應程式碼。以滑鼠左鍵單擊第15條事件前方空白處,將該事件全選,按下< ctrl+c>將事件複製,再按下<ctrl+v> 貼上。雙擊第15行子事件,將條件式改為<dir = "left">。再雙擊其右方動作,改為<simulate Platform pressing Left>、<Set Mirrored>(請注意兩個子事件是否對齊,若無對齊表示擺放層級有誤)。OK~步驟2完成。

步驟2程式代碼

以下是程式碼的解釋:第14行宣告了一個For each迴圈,會逐一挑選佈局中物件類型為[E1]的實件,每次挑選到實件後就會進入子事件繼續執行。第一個子事件會檢測剛剛挑選出的[E1]實件其 {dir}字串變數是否等於"right",若成立就指揮該[E1]實件向右移動,同時令動畫取消鏡像映射;相對的,第二個子事件會檢測{dir}是否為 "left",成立則[E1]向左移 動,同時令動畫做鏡像映射。

步驟3

步驟3是偵測小史與左右界線的碰撞,並設置碰撞後的反應。在C2下偵測某物件與其他物件的碰撞,可使用該物件的<On collision with another object>事件。先為右界線[turn_L]加入與[E1]的碰撞偵測,<Add Event>-><turn_L>-><E1>。

On collision with another object

選擇碰撞偵測目標物件

偵測到碰撞後要調整[E1]實件的行進方向,這裡可以透過改變[E1]實件的變數{dir}來達成。改變實件變數可使用實件自帶的<Set value>個動作。<Add action>-><Set value>,設置如下:

Set value動作

Set value動作

設完[turn_L]後再以對稱的方式設置[turn_R],完成後如下:

折返跑AI完成

OK~現在按<F5>來預覽一下。遊戲一開始只看到小史不停的來來回回走動~Good!可是....他怎麼沒有換成走路的動畫呢?記憶 猶新的朋友們,還記得在第一節的最後,我們的蛋蛋老師也是碰到一樣的問題嗎?這是因為我們還未把 <Platform>行為特性的5個自動動畫觸發設定好。小史目前只需設置<On moved>、<On stopped>,請將[E1]的<On moved>綁定至<walk>動畫,將<On stopped>綁定至<stand>動畫。

E1動畫綁定

再次<F5>預覽,OK~小史現在會折返跑了。加了第二個角色後,我們的遊戲越來越熱鬧囉~

 



4-4 第二套敵人AI - 以計時器實現

身為一個敵人,光只有一套AI是不夠的,敵人太笨遊戲就不好玩。接下來傅老師要教您如何加入第二套AI,並切使用計時器來切換兩套AI。我們構思的 第二套AI叫做"暫停",也就是讓小史走一段 固定的時間後會停止下來,暫時一會兒,接著再繼續向前走。兩套AI如果要搭配使用,就得有個計算時間的機制,以固定時間間隔切換AI。整體AI流程圖如 下:

AI流程圖

首先看到AI無限迴圈的部份,我們把這個部份做在前一節For each的迴圈裡,而且剛才已經實作了AI#1(折返跑),所以圖上標淺色斜線的部份已經做好囉~這個區塊要做的重頭戲是AI#2(暫停)。 再來AI切換設置部份,我們要在加個計時器在小史身上,每個小史實例一出生就開始計時。哪個小史身上的計時到時,就切換那個小史的AI模式。

由上可知,本節有四個項目要做:

項目1. 修改AI無限迴圈
項目2. 實作AI#2(暫停)
項目3. 加入計時器
項目4. 處理計時器觸發

項 目1

先由項目1開始,為了讓小史知道並紀錄自己目前使用的AI,我們為[E1]增加一個新的變數叫做"AI_mode",請依4-3的步驟以下圖設置增 加變數。

增加AI_mode實例變數

依照流程圖來看,現在進到<For each>後,第一件事就是檢查此[E1]實例的{AI_mode}是否設為"walk"。如同4-3節所提及,檢查實例變數可用該實例之< Compare instance variable>。回到14行以滑鼠左鍵單擊選取該行<For each>事件,按下鍵盤"s"新增子事件。選擇[E1]-><compare instance variable>,依下圖設置。

設置AI_mode檢查子事件

接下來把原本AI#1的程式碼搬到新的{AI_mode}檢查事件下。按住鍵盤"ctrl"鍵,先點取&ltdir = right>這行左方的空白處來選取整個事件,"ctrl"不放, 再點取&ltdir = left>這行左方的空白處來加選第二個事件,確實選到第二個事件後,鬆開"ctrl"鍵。接著左擊拖曳畫面上黃底區域,朝向第17行< AI_mode = walk>搬動,會看到底下浮現黑線。這時稍微將滑鼠再向右拖曳,會看到黑線前端出現箭頭,此時才鬆開左手放下。形成一個三層的事件結構。

搬移AI#1

C2程式執行的順序有先後規則。執行緒一但進入子事件,就必須依序執行完底下所有動作才會跳出子事件,朝向下一個子事件邁進(*3)。以圖中程式為 例,選取到[E1]實例後,先檢測{AI_mode}的值來判斷目前該執行那一套AI。若為"walk",則進入下一層的子事件內執行走路的AI。進入 後,第一個子事件用來判斷目前走向是否向右,若{dir}等於"right"字串,其附帶的動作才會開始執行,不等於的話就全部跳過,轉向檢測第二個子事 件。以上就是修改<For each>架構後的無限AI迴圈,小史的AI#1(折返跑)也正確地放置在<AI_mode = walk>子事件下。項目1完成。

項 目2

接下來我們要實作AI#2(暫停)。點選第14行<For each>,按下鍵盤"s"鍵,跳出增加子事件視窗。選擇[E1]-><compare instance variable>,依下圖設置。

增加子事件

事件區新增了一條<AI_mode = "stand">,我們在子事件下擺入設計好的暫停AI。等一下...暫停要做什麼事情嗎?叮咚叮咚~答對了,什麼事都不用做,因要停止不動嘛~項 目2完成。

項 目3

項目3要為每一個[E1]實件添加計時器(timer)。就如同之前我們講述的物件與實件的關係,這個計時器不應該是我們一個一個去加在 [E1]實件裡,而是直接加入[E1]物件裡。請至右上角專案區,以滑鼠左鍵單擊[E1]物件,於畫面左側叫出參數設置區。計時器是一個隸屬於物件的行為 特性(behavior),請將其加入,如下圖。

增加計時器行為

增加計時器行為

OK,現在所有[E1]的分身實例都有計時器功能了。為了定時切換AI,我們可以讓小史在"誕生"時打開計時器倒數5秒,5秒一到就切換AI模式。

至事件區底下點擊<Add event>,選取[E1]-><On created>,這個事件會在[E1]實件誕生時觸發。點擊其右方<Add action>,選擇[E1]-><Start timer>,新增一個叫"E1_AI"的計時器,項目3完成。如下圖:

增加E1_AI計時器

增加E1_AI計時器
欄位 功能
Duration 計時的時間長度。
Type 類型。設 為Once只會觸發一次;設為Regular則時間一到會從頭開始計算。
Tag 識別此計 時器的名字。

項 目4

最後項目4是處理計時器的觸發,可用<On timer>來擷取觸發。點擊<Add event>,選取[E1]-><On timer>,計時器的名字輸入"E1_AI"。

On timer事件

輸入計時器名稱

加入On timer事件

在<On timer>事件下新增子事件來檢查目前AI模式是否為"walk",若是"walk"就要將其改為"stand"。選取<On timer>事件,按"s"鍵新增子事件:[E1]- ><Compare instance variables>-> {AI_mode} <Equal to> "walk"。

設置事件:檢查AI是否為walk

修改他的AI變成"stand"。於子事件後<Add action>,[E1]->[Set value]->{AI_mode} "stand"。

設置動作:將AI設為stand

完成後請重覆上述步驟,點選<On timer>新增子事件,[E1]- ><Compare instance variables>-> {AI_mode} <Equal to> "stand",於子事件後<Add action>,[E1]->[Set value]->{AI_mode} "walk"。

哪裡寫錯了嗎?

有Bug!

呼~做了好多事情,快來按<F5>預覽一下。疑~小史怎麼停不下來?哪裡寫錯了嗎?是的,有bug,而且是初學者非常容易掉進去的陷 阱!請注意看下面這段程式:

常見之遺漏else的編程問題

假設計時器觸發時<AI_mode>的值是"walk",則第25個事件會被執行,<AI_mode>被改為 "stand"。結束後跳進下一個子事件,檢測<AI_mode>是否為"stand",結果正確,又跳進去將< AI_mode>改回"walk"。於是小史一直維持走路的"walk"AI狀態。

若您會傳統的ANSI C語言便更好理解了。當兩條<if>條件句並排,而我們只希望其中一句被執行的話,必須將第二個<if>改為< else>。<else>的意思就是,只有當前面一個條件其判斷不成立時,<else>所包含的程式才會被執行;若前面一 個條件其判斷成立,則<else>所涵概的程式不會被執行。

同樣的,C2也提供了<Else>事件。讓我們將<Else>事件加進第26條子事件中,以確保每次觸發"E1_AI"計 時器時,第25、26條子事件中只有一條會被執行。滑鼠右鍵單擊第26條子事件,選擇<Add another condition>,選擇<System>-><Else>。加入之<Else>以紅字呈現,這是因 為擺放的順序有誤,<Else>必須在事件的最前端。以滑鼠左鍵拖曳<Else>向上移動, 看到黑色的位置提示線移到第26條頂端時,放開左鍵放置<Else>。

新增條件式

Else事件

調整事件位置

OK~項目4完成。整段AI完成如下:

敵人AI程式完成

按預覽,呦呼~小史現在會自己停下來休息了,真是聰明呀~

跟在後面,看看你會不會發現

 



4-5 增加實件

一個小史實在太寂寞,我們來為了增加幾個分身好朋友。看到右下角物件區,以滑鼠左鍵拖曳[E1],移到編輯區中央,放開左鍵,可以看到 一個小史的實件分身出現了。就如同我們在上一節教大家物件與實件的概念,右下角是這張佈局裡用到的物件,要生成實件很簡單,只要拖曳到編輯區即可。

再多放幾個小史,按<F5>預覽看看。嗯~越來越熱鬧了。

最後我們來把畫面弄漂亮些,把花花綠綠的兩個界限方塊隱藏起來。點擊[turn_R],於參數區將<Initial visibility>設為<Invisible>。完成後將[turn_L]也設為<Invisible>。再次預覽, Good~,一個有智慧的邪惡小史集團出現了!將專案存檔為L4.capx,準備下課囉!

越來越熱鬧了

 



4-6 結語

在本節裡我們學會了添加敵人角色的方法,對於動畫的增加與微調更加熟悉。此外我們使用折返跑與暫停的概念,為敵人添加了兩套AI。遊戲增添了對戰的 氣氛。

 



4-7 傅老師鼓勵你...

 

  1. 在範例中,計時器設為Regular的5秒,所以走路與暫停的時間均為5秒。如何修改才可以讓暫停縮為3秒,而走路維持5秒。
  2. 可否自己構思一套"轉向回頭"的AI,並加入程式內?(提示:請注意<Else>的使用)

 



附註

 

*1。通常在討論C2程式時,Object一詞常被用來替代Object Type。若整個遊戲該Object只會有一個instance,則該Object在討論時可用Object一詞來代替其instance。
*2。設置文字實件變數之初始值時,文字串不可加引號,這是一個少見的例外。一般使用狀況時,文字串都必須加引號。
*3。C2的指令執行順序偶有例外:觸發式(trigger)事件可以插隊執行;帶有wait動作的程式片段即使輪到他也會故意不執行。