方向鍵遊戲 - 碁峰資訊epaper.gotop.com.tw/pdf/aeg000300.pdf ·...
TRANSCRIPT
03方向鍵遊戲3-1 前言
3-2 四方向
3-3 八方向
3-4 荒島尋寶遊戲
3-5 迷你賽車遊戲
3-2
3-1 前言
上一章介紹以滑鼠為操作介面的遊戲,相對於滑鼠的另一種輸入工具
“鍵盤”,也可以用來玩遊戲,只是數量似乎比較少,畢竟鍵盤沒有滑鼠的
游標,無法快速移動及定位。但是若用滑鼠來玩「小精靈」、「炸彈超人」或
「貪吃蛇」等類型的遊戲,感覺又似乎怪怪的,更不要說拼字、打字遊戲,滑
鼠絕對沒有鍵盤來的方便。
所以重點不在於使用那一種工具,而是要針對遊戲的特性來發揮,甚至
兩者都使用也可以。本章的主題“方向鍵遊戲”,將介紹鍵盤做為為操作介
面的遊戲,尤其是針對鍵盤的上、下、左、右四個方向鍵。就玩遊戲而言,
無不希望讓玩家有最符合人性化的操作,簡單是其中最基本的要素,以近幾
年Wii橫掃遊戲機市場最堪稱代表。鍵盤的按鍵那麼多,如果每個遊戲的按
鍵功能設定都不一樣,玩家不瘋掉才怪,所以幾乎鍵盤遊戲都使用方向鍵讓
玩家操控遊戲,手機遊戲更不用說。
用四個方向鍵進行遊戲,基本的類型可從上述的「小精靈」等三個遊戲
看出端倪,也就是每按一次鍵依照按鍵方向移動一個單位距離的遊戲。這類
型遊戲最簡單的設計方法是事先在舞台上安排固定的“格子”(位置 ),讓玩
家操控的主角物件在格子裡“游走”,將在 3-2用模擬手機介面的程式範例
說明。
3-3的範例則介紹利用四個方向鍵組合成 8個方向的玩法,增加了四個
斜角的行進方向,讓玩家操控的主角不再受限於舞台上固定死的格子位置,
能夠 8個方向自由的移動。範例程式裡游走的範圍將包括規則矩形區域和不
規則的任意封閉區域兩種。
3-4和 3-5是本章的重頭戲,將以上的概念製作成完整的遊戲。3-4利用
3-3的範例發展成「荒島尋寶」遊戲,更進一步讓遊戲主角游走的範圍不受
限於舞台大小,能在舞台好幾倍大的小島上尋找隨機位置的寶物。
3-3
01
02
03
04
05
06
07
08
09
10
方向鍵遊戲
3-5將 8個方向行進方式再進化,利用簡單的三角函數計算 (不用緊張,
只有 sin和 cos而已 ),讓玩家操控的遊戲主角可以任意方向行進;前進的速
度也不再是固定,而是完全受到玩家方向按鍵的影響;游走的範圍沿用 3-4
的大地圖原理,只需要改變障礙物圖像及位置,用它們來圍成一個跑道。最
適合如此形容的遊戲,莫過於 3-5節的「迷你賽車」遊戲。
3-2 四方向
CH03_key4.fla示範 ActionScript 3取得按鍵碼的方法,利用模擬手機介
面的範例來說明。上圖手機的 12個按鍵因為需要命名,使用影片片段實體,
事先置放於舞台上,依照由左至右、由上至下的次序命名:
"block_mc" + 水平位置編號 + "_" +垂直位置編號
例如按鍵 8實體名稱為 block_mc2_3。本書有許多方塊類型遊戲,方塊
實體的命名都採用這種方式,也大致是前言所謂在舞台上安排固定格子的方
法。方塊以水平、垂直的方式整齊排列,本書慣例將水平方向方塊總行數變
數命名為 x_num,垂直方向方塊總列數變數命名為 y_num,在後續的許多遊
戲將可發現這兩個變數非常的重要。在本範例這種四個方向游走的遊戲裡,
它們扮演向右、向下方向的最大值,當然 1則是向左、向上方向共同的最小
值。
3-4
使用上述方式設計,當玩家按下一個方向鍵,等於要遊戲主角往該方向
鍵的方向移動一個方塊位置。通常程式碼會預設主角“位置”編號的初值,
做為出發的初始位置,請注意是位置編號而不是座標,這個編號指的就是方
塊實體名稱的水平位置編號與垂直位置編號。要讓主角根據方向鍵方向移
動,只要遞增或遞減該方向的位置編號,以新位置編號命名的方塊實體的座
標就是主角的新座標。位置編號的遞增或遞減計算中,通常因為不能讓主角
跑出舞台外,所以它需要限制邊界範圍,也就是上述的最大值 (x_num或 y_
num)與最小值 (1),當超出範圍時,視需要維持原值 (不移動 )或變成相對邊
界的編號 (移至另外一邊 )。
由於鍵盤沒有滑鼠的游標,通常會利用一個物件來標示目前的位置,如
上圖的紅色框實體 actor_mc,它的圖層必須在全部按鍵實體之上。當玩家按
下方向鍵,紅色框即往該方向移動,超出邊界範圍時,又從另外一邊出現。
ActionScript 3有兩種取得按鍵碼的方法,為了比較兩種方法的差異,本
範例除了使用方向鍵移動紅色框外,也可以直接按數字鍵及#、*鍵,紅色
框將直接跳至該按鍵處,就像真的手機按鍵操作。
const x_num:int = 3;01
const y_num:int = 4;02
var actor_x:int = 1;03
var actor_y:int = 1;04
actor_mc.x = this["block_mc" + actor_x + "_" + actor_y].x;05
actor_mc.y = this["block_mc" + actor_x + "_" + actor_y].y;06
stage.addEventListener(KeyboardEvent.KEY_DOWN, playKeyDown);07
stop();08
function playKeyDown(event:KeyboardEvent) {09
var codeC:int = event.charCode;10
if ( codeC == 42 ) { // *11
actor_x = 1;12
actor_y = 4;13
} else if ( codeC == 35 ) { // #14
3-5
01
02
03
04
05
06
07
08
09
10
方向鍵遊戲
actor_x = 3;15
actor_y = 4;16
} else {17
var codeK:int = event.keyCode;18
if (codeK == 38 ) { // up19
actor_y --;20
if ( actor_y < 1 ) {21
actor_y = y_num;22
}23
} else if (codeK == 40) { // down24
actor_y ++;25
if ( actor_y > y_num ) {26
actor_y = 1;27
}28
} else if (codeK == 37) { // left29
actor_x --;30
if ( actor_x < 1 ) {31
actor_x = x_num;32
}33
} else if (codeK == 39) { // right34
actor_x ++;35
if ( actor_x > x_num ) {36
actor_x = 1;37
}38
} else if (codeK <= 57 && codeK >=48) { // 0-939
var digit:int = codeK-48;40
if ( digit == 0 ) {41
actor_x = 2;42
actor_y = 4;43
} else {44
actor_x = digit % x_num;45
if ( actor_x == 0 ) {46
actor_x = x_num;47
}48
actor_y = (digit - actor_x)/x_num + 1;49
}50
3-6
}51
}52
actor_mc.x = this["block_mc" + actor_x + "_" + actor_y].x;53
actor_mc.y = this["block_mc" + actor_x + "_" + actor_y].y;54
}55
解說
行 01∼02:x_num為水平方向方塊總行數,y_num為垂直方向方塊
總列數。將它們設定為不變的常數,通常在方塊遊戲裡這兩個數值
不能變,而且程式一開始就必需設定。
行 03∼04:分別設定紅色指示框的水平位置編號與垂直位置編號的
初值,決定紅色框的開始位置。
行 05∼06:如上述設定紅色框的 x與 y座標,即相同位置編號命名的
方塊實體的座標。
行 07:在 stage註冊鍵盤事件的 KEY_DOWN屬性偵聽處理函數
playKeyDown(),處理鍵盤按鍵被按下的程序。鍵盤事件一定要註
冊在 stage上,因為鍵盤沒有游標,無法像滑鼠可直接選取有註冊
事件的個別物件,所以 stage成了它唯一方便辨識的註冊物件;鍵
盤事件另一屬性 KEY_UP也是如此,下節即可看到。
行 08:主場景時間軸停止,等候鍵盤事件偵聽處理函數下達指令。
行 0 9∼5 5:自 定 函 數 p l a y K e y D o w n ( ),傳 遞 的 參 數 是
event:KeyboardEvent。負責根據取得被按下按鍵碼的處理程序。
ActionScript 3可用鍵盤事件的兩種屬性 charCode和 keyCode取得
按鍵碼,兩方法有差異,不完全一樣。通常四個方向鍵按鍵碼的取
得使用 keyCode方法,若用 charCode方法完全不行;但是#和*
按鍵一定要用 charCode方法,因為這兩鍵在鍵盤上須要先按 shift
鍵才能取得,而 keyCode方法則無法分辨加 shif的按鍵。
3-7
01
02
03
04
05
06
07
08
09
10
方向鍵遊戲
行 10:使用 charCode屬性取得按鍵的字元碼值。如上述這是特別針
對#和*兩鍵碼設定的。
行 11∼17:取得#和*按鍵碼後,分別取得該兩鍵在舞台上按鍵的水
平位置與垂直位置編號,並指定給紅色指示框的水平與垂直位置編
號。
行 18∼51:使用 keyCode屬性取得按鍵碼值。
行 19∼38:取得四個方向鍵按鍵碼值的狀況。如上述根據按鍵碼遞增
或遞減該方向的位置編號,並限制其範圍,如超出範圍時,變成相
對邊界的編號。
行 39∼50:直接按 0∼9數字按鍵的狀況。如果使用 keyCode屬性
取得按鍵碼值介於 48∼57之間,將此值減去 48(行 40),可得到
按鍵代表的實際數字,由此計算按鍵在舞台上按鍵的水平位置與垂
直位置編號,並指定給紅色指示框的水平與垂直位置編號,其中數
字 0因屬例外直接給值 (行 42、43),1∼9可用公式計算 (行 45∼
49)。
行 53∼54:經過以上程序取得紅色指示框的水平與垂直位置編號後,
將相同位置編號命名的方塊實體的座標設定為紅色框的 x與 y座標,
即完成紅色框的移動。
以上說明的鍵盤被按下處理函數的程式碼似乎很多,其實只是為了說明
charCode和 keyCode兩方法取得按鍵碼的差異,再加上非方向鍵的許多程
序,如果只是單純的方向鍵就非常簡單,看下一節就知道了。
3-8
3-3 八方向
上節範例四個方向的設計方式,玩家只能在事先放置而且有限的位置上
移動。雖然是很簡單的方式,卻是許多按鈕類型遊戲的設計模式,因為只需
要單純的四個方向,算是最直覺的操控方式,尤其是沒有滑鼠的手機,最適
合的遊戲就是這種既不傷腦筋又殺時間的簡單類型。
本節範例 CH03_key8.fla將改良上節的作法,用四個方向的組合,變成
八個方向,也就是增加 45度斜角的行進方向。雖然八個方向還是可以在固
定格子內游走,但是若再使用相同的作法完全沒有意義,況且本範例除了規
則的矩形區域游走外,還要介紹不規則的封閉區域游走方法,這種方法可是
用固定格子方法很難做到的。所以除了八個方向游走外,將採用自由任意的
移動方式,在設定的區域內幾乎可以到處走透透。
舞台上藍色的“海”(底圖 )上有一陸地實體 land_mc,共有 2個影格,
各有一個形狀不同的陸地,可以從舞台下面的 2個按鈕切換。左按鈕 land_
btn1負責將陸地切換至左圖的矩形陸地 (影格 1),右按鈕 land_btn2則負責
切換至右圖的不規則陸地 (影格 2)。玩家使用方向鍵移動遊戲主角 actor_mc
在陸地上游走,能夠走動的區域僅限於陸地,遇到藍色的海就不能前進。游
走的方向除了 90度垂直的四個方向外,還可以組合相鄰 90度的兩個方向,
產生 45度斜角的行進方向,所以共有八個方向可以游走。actor_mc實體配
3-9
01
02
03
04
05
06
07
08
09
10
方向鍵遊戲
合八個方向走動,也有 8個關鍵影格,每個影格各放置一個呈現不同角度圖
像的影片片段實體 actor,每個 actor實體內有「停止」與「走動」影格,詳
細請參考下節的特殊影片片段說明。
主場景時間軸共有 3個關鍵影格:
影格 1「初值」,設定全域變數、註冊鍵盤事件及其偵聽處理函數、註冊
按鈕滑鼠事件及其偵聽處理函數。
影格 2「遊戲中」,根據方向鍵組合執行自定的主角移動函數。
影格 3只是簡單的返回影格 2形成影格迴圈。
影格 1,「初值」 ►
actor_mc.gotoAndStop("向下 ");01
var dx:int = 5;02
var dy:int = 5;03
var landType:int = 1;04
var dir_now, dir_old:int;05
var dir1:Boolean = false;06
var dir2:Boolean = false;07
var dir3:Boolean = false;08
var dir4:Boolean = false;09
stage.addEventListener(KeyboardEvent.KEY_DOWN, playKeyDown);10
stage.addEventListener(KeyboardEvent.KEY_UP, playKeyUp);11
land_btn1.addEventListener(MouseEvent.CLICK, land1);12
land_btn2.addEventListener(MouseEvent.CLICK, land2);13
function playKeyDown(event:KeyboardEvent) {14
var codeK:int = event.keyCode;15
if ( codeK >= 37 && codeK <= 40 ) {16
this["dir" + (codeK - 36)] = true;17
getDirection();18
}19
}20
function playKeyUp(event:KeyboardEvent) {21
3-10
var codeK:int = event.keyCode;22
if ( codeK >= 37 && codeK <= 40 ) {23
this["dir" + (codeK - 36)] = false;24
getDirection();25
}26
}27
function getDirection() {28
if ( dir1 && !dir2 && !dir3 && !dir4 ) {29
dir_now = 1; //左30
} else if ( dir1 && dir2 && !dir3 && !dir4 ) {31
dir_now = 2; //左上32
} else if ( !dir1 && dir2 && !dir3 && !dir4 ) {33
dir_now = 3; //上34
} else if ( !dir1 && dir2 && dir3 && !dir4 ) {35
dir_now = 4; //右上36
} else if ( !dir1 && !dir2 && dir3 && !dir4 ) {37
dir_now = 5; //右38
} else if ( !dir1 && !dir2 && dir3 && dir4 ) {39
dir_now = 6; //右下40
} else if ( !dir1 && !dir2 && !dir3 && dir4 ) {41
dir_now = 7; //下42
} else if ( dir1 && !dir2 && !dir3 && dir4 ) {43
dir_now = 8; //左下44
}45
if ( dir_now != dir_old ) {46
actor_mc.gotoAndStop(dir_now);47
dir_old = dir_now;48
}49
}50
function land1(event:MouseEvent) {51
landType = 1;52
actor_mc.x = 320;53
actor_mc.y = 200;54
land_mc.gotoAndStop(landType);55
}56
function land2(event:MouseEvent) {57
3-11
01
02
03
04
05
06
07
08
09
10
方向鍵遊戲
landType = 2;58
actor_mc.x = 320;59
actor_mc.y = 200;60
land_mc.gotoAndStop(landType);61
}62
解說
行 01:遊戲開始 actor_mc停止在「向下」影格,讓它的圖像朝正前方
(下 )。
行 02∼03:設定 actor_mc水平與垂直移動的單位距離,這是指每按
一次方向鍵 actor_mc向該方向移動的距離,也就是說 actor_mc移
動速度是固定的。
行 04:設定陸地種類 int值,初值為 1。這個數值也就是 land_mc實
體停留的影格,由兩個按鈕切換 1或 2。
行 05:宣告 2個預定存放 actor_mc實體方向的 int值,dir_now為最
新 (目前 )方向,dir_old為前次 (舊 )方向,用來判斷 actor_mc是
否須要改變方向。
行 06∼09:依左、上、右、下次序設定四個方向指標 Boolean值,用
來判斷該方向鍵是否按下,初值均為 false(未按下 ),若被按下則改
為 true。
行 10:同上節,在 stage註冊鍵盤事件的 KEY_DOWN屬性偵聽處理
函數 playKeyDown(),處理鍵盤按鍵被按下的程序。
行 11:在 s t a g e註冊鍵盤事件的 K E Y _ U P屬性偵聽處理函數
playKeyUp(),處理鍵盤按鍵被放開的程序。
行 12∼13:分別在 land_btn1按鈕及 land_btn2按鈕註冊滑鼠事件的
滑鼠按下屬性偵聽處理函數 land1()及 land2(),處理該按鈕被按下
後的程序。
3-12
行 1 4∼2 0:自 定 函 數 p l a y K e y D o w n ( ),傳 遞 的 參 數 是
event:KeyboardEvent。和上節相同的取得被按下按鍵碼的處理程
序,但是處理方式不同。
行 15:使用 keyCode屬性取得按鍵碼值。
行 16∼19:如果取得的按鍵碼值介紹 37∼40,也就是四個方向鍵其
中一個被按下,將該方向指標設定為 true(按下,行 17),並呼叫執
行 getDirection()自定函數,取得最新的方向值 (行 18)。
行 2 1∼2 7:自 定 函 數 p l a y K e y U p ( ),傳 遞 的 參 數 是
event:KeyboardEvent。取得被放開按鍵碼的處理程序。
行 22∼26:如果取得的按鍵碼值介紹 37∼40,也就是四個方向鍵其
中一個被放開,將該方向指標設定為 false(放開,行 24),並呼叫執
行 getDirection()自定函數,取得最新的方向值 (行 25)。
行 28∼50:自定函數 getDirection()。根據四個方向指標的組合取得
最新 actor_mc的方向值的程序。
行 29∼45:由四個方向指標的 true或 false判斷最新的方向值,只允
許單一方向指標為 true(行 30、34、38、42),或 90度夾角的兩方
向指標同時為 true(行 32、36、40、44)。
3-13
01
02
03
04
05
06
07
08
09
10
方向鍵遊戲
行 46∼49:如果得到的最新方向值和先前記錄的方向值不同,表示方
向已改變,所以讓 actor_mc改變到最新方向影格 (行 47),並將新
方向值取代先前方向值,以備下次比較 (行 48)。
行 51∼56:自定函數 land1(),傳遞的參數是 event:MouseEvent。
執行 land_btn1按鈕被按下的處理程序。設定陸地種類為 1(規則陸
地,行 52),重新設定 actor_mc的座標在陸地中央 (行 53、54),
land_mc實體顯示正確的陸地圖像 (行 55)。
行 57∼62:自定函數 land2(),傳遞的參數是 event:MouseEvent。執
行 land_btn2按鈕被按下的處理程序。除設定陸地種類為 2(不規則
陸地,行 58)外,其餘均與 land1()函數相同。
影格 2,「遊戲中」 ►
if ( dir1 || dir2 || dir3 || dir4 ) {01
actor_mc.actor.play();02
moveActor();03
} else {04
actor_mc.actor.gotoAndStop(1);05
}06
function moveActor() {07
var new_x, new_y:Number;08
var mx:int = 0;09
var my:int = 0;10
if ( dir_now == 1 ) {11
mx = -dx;12
} else if ( dir_now == 2 ) {13
mx = -dx;14
my = -dy;15
} else if ( dir_now == 3 ) {16
my = -dy;17
} else if ( dir_now == 4 ) {18
3-14
mx = dx;19
my = -dy;20
} else if ( dir_now == 5 ) {21
mx = dx;22
} else if ( dir_now == 6 ) {23
mx = dx;24
my = dy;25
} else if ( dir_now == 7 ) {26
my = dy;27
} else if ( dir_now == 8 ) {28
mx = -dx;29
my = dy;30
}31
new_x = actor_mc.x + mx;32
new_y = actor_mc.y + my;33
if ( landType == 1 ) {34
if ( new_x>=100 && new_x<=540 && new_y>=60 && new_y<=340 ) {35
actor_mc.x = new_x;36
actor_mc.y = new_y;37
}38
} else {39
if ( land_mc.hitTestPoint(new_x, new_y, true)) {40
actor_mc.x = new_x;41
actor_mc.y = new_y;42
}43
}44
}45
解說
行 01∼06:如果有任何一個方向指標為 true,也就是任一個方向鍵目
前被玩家按下中,actor_mc應該在走動中,所以讓 actor_mc實體
影格的 actor實體播放 (行 02,進入「走動」影格呈現走動動畫 ),
並呼叫執行 moveActor()函數 (行 03),決定 actor_mc能否移動到
新的位置。
3-15
01
02
03
04
05
06
07
08
09
10
方向鍵遊戲
行 05:全部方向指標為 false的情況,也就是四個方向鍵都是放開狀
態,actor_mc應該停止走動,所以讓 actor_mc實體影格的 actor實
體回到影格 1(「停止」影格,站立不動 )。
行 07∼45:自定函數 moveActor(),根據最新方向值決定移動的新位
置座標,並判斷該位置是否在限制區域內可以前往。
行 08:宣告 2個 Number值變數,作為 actor_mc的新位置座標暫時
值。
行 09∼10:預設 2個 int值變數,作為 actor_mc的水平與垂直方位移
值,初值均為 0。
行 11∼31:根據新方向值,取得該往方向的水平與垂直方位移值 (初
值設定的固定值 ),往左、上為負值,往右、下為正值。
行 32∼33:將 actor_mc的水平與垂直座標分別加上述取得之位移值,
得到新位置座標的暫時值。
行 34∼44:判斷新位置座標的暫時值是否在限制區域內。這裡示範兩
種方法,第一種是規則區域方法,行 35∼38;另一種是不規則區域
方法,行 40∼43。其實都用第二種方法也可以,但是此處故意使用
兩種方法作為比較。
行 35∼38:規則陸地的判斷。直接比較座標值是否在限制區域內的方
法,只適合矩形區域。如果成立,表示 actor_mc可以前往新位置,
因此將新位置座標暫時值設定為 actor_mc的座標 (行 36、37)。
行 40∼44:不規則陸地的判斷。使用前章的 hitTestPoint()方法判斷,
要測試的物件為陸地實體 land_mc,測試點為新位置座標,可以適
用任何封閉區域。如果成立,表示 actor_mc可以前往新位置,因此
將新位置座標暫時值設定為 actor_mc的座標 (行 41、42)。
3-16
影格 3 ►
和前一影格「遊戲中」形成迴圈,不斷執行該影格內程式碼,持續的偵
測遊戲的狀況。
gotoAndPlay("遊戲中 ");01
解說
行 01:回到「遊戲中」影格形成迴圈。
3-4 荒島尋寶遊戲
3-17
01
02
03
04
05
06
07
08
09
10
方向鍵遊戲
前言提到讓主角游走的範圍不受限於舞台大小,正是此節在比舞台大好
幾倍的小島上尋找寶物的遊戲 CH03_1.fla。不僅如此,游走的地圖上增加許
多遊戲主角不能直接穿越的障礙物,如地圖上的草叢。舞台右上角有提示寶
物地點 (紅色小點 )的縮小地圖,大地圖的寶物地點一般時候是隱藏的,當
遊戲主角靠近至一個範圍內才會顯示 (上左圖 ),讓主角從上面通過代表尋獲
寶物。如果預設的所有寶物全部找到,遊戲成功 (上右圖 )。
3-4-1 遊戲流程
遊戲流程非常簡單,和前一章的「空中大戰」類似,沒有失敗程序,也就
是說此遊戲沒有任何限制,只是讓玩家任意的游走尋寶。如果需要設限,
計時可能是最佳方法,讓玩家在有限的時間內找出全部寶物,否則遊戲失
敗。
本遊戲使用鍵盤操作,所以「說明畫面」和「成功」程序裡遊戲暫停讓玩
家開始 (或再玩 )的互動介面不使用滑鼠的按鈕,改讓玩家按 <Enter>鍵
繼續,因為不同操作介面容易造成玩家困擾,所以遊戲設計時應列入考
慮。又因兩程序使用相同的偵聽處理函數,所以執行後流程都跑到「初值
化」。
3-18
「初值化」和上節相同,設定方向指標等變數,另外增加一般完整遊戲需
要的初值,如得分、目標 (寶物 )總數等。
「遊戲開始」建立寶物隨機隱藏地點及縮小地圖物件的處理,並同上節在
stage分別註冊鍵盤事件的 KEY_DOWN與 KEY_UP屬性偵聽處理函數。
「遊戲中」同樣和下一個影格形成迴圈,讓玩家能持續的進行遊戲,同時
也不斷偵測遊戲預設的成功條件。
「成功」影格遊戲告一段落,主場景時間軸停止,讓玩家按下 <Enter>鍵
可再玩遊戲,這裡沒有未設定結束的功能。
3-4-2 舞台圖層配置
上圖紅色框範圍是實際的舞台區域,可見讓遊戲主角游走的小島是非常的
大,上面另外放置了許多做為障礙物的草叢。
3-19
01
02
03
04
05
06
07
08
09
10
方向鍵遊戲
大地圖實體 map_mc包含遊戲主角實體 actor_mc、障礙物實體 block_
mc,和只是圖像的陸地做為底圖。當遊戲開始後置放的寶物也是使用
map_mc實體做為顯示容器。為何如此設計?由於地圖比舞台大,很顯
然上節直接在舞台上游走的方式行不通。改變的方式是讓 actor_mc在
map_mc上游走,可是又不能讓它超出舞台外,所以讓它“始終”在舞台
中央位置。實際作法是當 actor_mc改變在 map_mc上的位置時,我們也
立即變更 map_mc位置,使得 actor_mc能夠維持在舞台中央的位置。為
了要變更 map_mc實體的位置,所以我們將上述那麼多的物件全部放置
在 map_mc實體上,才能方便同步變更位置,也因此在程式碼裡,將會看
到許多物件都前置“map_mc”,舞台上除了小地圖實體外,其實只有一個
大地圖實體 map_mc。
map_mc大過舞台許多,將它置放於被遮色的「大地圖」圖層,上有與舞
台大小相同的「地圖遮色片」圖層,使得遊戲進行時只能看見舞台內的部
份。
舞台上方有已找到寶物記錄的動態文字 score_txt。
舞台右上角縮小地圖實體 smallMap_mc,遊戲進行時會顯示遊戲主角的
小藍點,會隨著大地圖上的遊戲主角在地圖上同步移動;另外用小紅點指
示寶物在地圖上的隱藏地點。小地圖實體和大地圖實體使用相同的影片片
段,兩者大小有比例關係。
寶物是遊戲開始後隱藏置放於大地圖的隨機位置,所以舞台配置無法看到。
其他未在圖上顯示的角色為「說明畫面」和「成功」影格的全景遮罩實體,
只是作為遊戲停止時半透明黑色遮住舞台美觀用,對程序沒有影響;因為
使用鍵盤操作,所以沒有按鈕物件。
3-20
3-4-3 主時間軸程式
影格 1,「載入進度」 ►
和 2-4遊戲影格 1說明相同,不再贅述。
影格 2,「說明畫面」 ►
map_mc.actor_mc.visible = false;01
stage.addEventListener(KeyboardEvent.KEY_DOWN, gameStartKey);02
stop();03
function gameStartKey(event:KeyboardEvent) {04
var codeK:int = event.keyCode;05
if ( codeK == 13 ) {06
gotoAndPlay("初值化 ");07
}08
}09
解說
行 01:在「說明畫面」有遊戲主角大的圖像顯示,暫時將 map_mc實
體上的 actor_mc實體隱藏。
行 02:因為採用鍵盤操作不提供按鈕,所以在 stage註冊鍵盤事件的
按鍵按下屬性處理函數 gameStartKey()。請注意使用鍵盤操作的遊
戲在測試階段時,應將測試視窗控制選項的“停用鍵盤快速鍵”項
目鉤選起來,避免按鍵與 Flash預設快速鍵衝突。
行 03:主場景時間軸暫停,直到玩家按下 <Enter>鍵。
行 04∼09:按鍵按下處理函數 gameStartKey()。使用 keyCode屬性
取得按鍵碼值 (行 05),如果該值等於 13,也就是 <Enter>鍵的按
鍵碼值,主場景時間軸開始播放到「初值化」影格 (行 07)。
3-21
01
02
03
04
05
06
07
08
09
10
方向鍵遊戲
影格 3,「初值化」 ►
stage.removeEventListener(KeyboardEvent.KEY_DOWN, gameStartKey);01
map_mc.actor_mc.visible = true;02
map_mc.actor_mc.gotoAndStop("向下 ");03
smallMap_mc.actor_mc.visible = false;04
var spots:int = 5;05
var score:int = 0;06
var dx:int = 5;07
var dy:int = 5;08
var dir_now, dir_old:int;09
var dir1:Boolean = false;10
var dir2:Boolean = false;11
var dir3:Boolean = false;12
var dir4:Boolean = false;13
解說
行 01:移除在 stage註冊的鍵盤事件處理函數 gameStartKey()。
行 02:將隱藏的 map_mc實體上的 actor_mc實體顯示出來。
行 03: 將 actor_mc停止在「向下」影格,讓它朝正前方。
行 04:將小地圖實體 smallMap_m上的 actor_mc實體隱藏起來,因
為在小地圖上將用小藍點取代 actor_mc。
行 05:設定寶物總數 int值變數,這個數值也就是遊戲成功的條件。
行 06:設定玩家已找到寶物的數目,int值,初值為 0。
行 07∼13:和上節相同的移動單位距離、新舊方向值、四個方向指標
等變數初值設定。
影格 4,「遊戲開始」 ►
for (var i:int = 1; i<=spots; i++ ) {01
while ( true ) {02
3-22
var mx:Number = Math.floor(Math.random()*map_mc.width);03
var my:Number = Math.floor(Math.random()*map_mc.height);04
if ( !map_mc.block_mc.hitTestPoint(mx+map_mc.x, my+map_mc.y, true) && 05
!map_mc.actor_mc.hitTestPoint(mx+map_mc.x, my+map_mc.y)) {
this["spot_mc" + i] = new spot_mc();06
this["spot_mc" + i].x = mx;07
this["spot_mc" + i].y = my;08
this["spot_mc" + i].num = i;09
this["spot_mc" + i].gotoAndPlay("遊戲中 ");10
map_mc.addChild(this["spot_mc" + i]);11
this["redDot_mc" + i] = new redDot_mc();12
this["redDot_mc" + i].x = smallMap_mc.x + (mx)/10;13
this["redDot_mc" + i].y = smallMap_mc.y + (my)/10;14
stage.addChild(this["redDot_mc" + i]);15
break;16
}17
}18
}19
centerMap();20
map_mc.setChildIndex(map_mc.actor_mc, map_mc.numChildren-1);21
var blueDot:blueDot_mc = new blueDot_mc();22
stage.addChild(blueDot);23
stage.addEventListener(KeyboardEvent.KEY_DOWN, playKeyDown);24
stage.addEventListener(KeyboardEvent.KEY_UP, playKeyUp);25
function centerMap() {26
map_mc.x = 320 - map_mc.actor_mc.x;27
map_mc.y = 240 - map_mc.actor_mc.y;28
}29
function playKeyDown(event:KeyboardEvent) {30
var codeK:int = event.keyCode;31
if ( codeK >= 37 && codeK <= 40 ) {32
this["dir" + (codeK - 36)] = true;33
getDirection();34
}35
}36
3-23
01
02
03
04
05
06
07
08
09
10
方向鍵遊戲
function playKeyUp(event:KeyboardEvent) {37
var codeK:int = event.keyCode;38
if ( codeK >= 37 && codeK <= 40 ) {39
this["dir" + (codeK - 36)] = false;40
getDirection();41
}42
}43
function getDirection() {44
if ( dir1 && !dir2 && !dir3 && !dir4 ) {45
dir_now = 1; //左46
} else if ( dir1 && dir2 && !dir3 && !dir4 ) {47
dir_now = 2; //左上48
} else if ( !dir1 && dir2 && !dir3 && !dir4 ) {49
dir_now = 3; //上50
} else if ( !dir1 && dir2 && dir3 && !dir4 ) {51
dir_now = 4; //右上52
} else if ( !dir1 && !dir2 && dir3 && !dir4 ) {53
dir_now = 5; //右54
} else if ( !dir1 && !dir2 && dir3 && dir4 ) {55
dir_now = 6; //右下56
} else if ( !dir1 && !dir2 && !dir3 && dir4 ) {57
dir_now = 7; //下58
} else if ( dir1 && !dir2 && !dir3 && dir4 ) {59
dir_now = 8; //左下60
}61
if ( dir_now != dir_old ) {62
map_mc.actor_mc.gotoAndStop(dir_now);63
dir_old = dir_now;64
}65
}66
解說
行 01∼19:使用迴圈設定全部寶物在 map_mc實體上的位置,同時
在 smallMap_mc實體相對的位置置放小紅點實體。
3-24
行 02∼18:因為置放寶物的位置是在 map_mc實體上的隨機位置,
因此使用 while陳述式的迴圈結構來尋找,直到找到適當位置才離
開迴圈。
行 03∼04:分別使用亂數取得 x與 y座標兩個 Number變數值,x座
標變數值介於 0與 map_mc實體寬度之間;y座標變數值介於 0與
map_mc實體高度之間。
行 05∼17:上述座標變數值必須符合兩個條件,第一,不能在 map_
mc實體上的 block_mc實體內,也就是不能將寶物放置於障礙物內,
否則 actor_mc根本無法接觸;第二,當然不能是 actor_mc所在的
位置。這兩個條件都可以用 hitTestPoint()方法來測試,前一章的方
法再度派上用場。還記得 2-3這個方法的使用說明:“測試點的座標
指相對於舞台 stage上的座標,而不是包含要測試的物件的顯示物
件容器”?所以在此的測試點 (mx,my)都必須加上 map_mc的座標,
才是相對於舞台 stage上的座標。如果兩個 hitTestPoint()方法傳回
值都是 false,上述取得的座標變數值便符合條件,可以做為置放寶
物的位置。
行 06:從元件庫的寶物地點類別 spot_mc宣告建立命名為“spot_mc+
迴圈值”的寶物地點實體。
行 07∼08:將上述取得的座標變數值分別設定為寶物地點實體的 x與
y座標。
行 09:設定寶物地點實體的編號變數,作為大小地圖之間寶物編號的
關聯,變數值使用迴圈值即可。
行 10:新建立的寶物地點實體到「遊戲中」影格播放,也就是開始執
行偵測與 actor_mc碰撞的任務。
行 11:將新建立的寶物地點實體加到 map_mc實體上,用 map_mc
實體做為它的顯示容器。
3-25
01
02
03
04
05
06
07
08
09
10
方向鍵遊戲
行 12:從元件庫的小紅點類別 redDot_mc宣告建立命名為“ redDot_
mc+迴圈值”的小紅點實體。
行 13∼14:分別設定小紅點實體的 x與 y座標。公式為取得的座標
值除以 10(因為 smallMap_mc實體是 map_mc實體的 10%),加上
smallMap_mc實體的座標 (顯示在 smallMap_mc上面相對位置 )。
行 15:將新建立的小紅點實體加到 stag上,用 stage做為它的顯示容
器。
行 16:寶物地點已找到並置放,離開 while陳述式的迴圈。
行 20:呼叫執行 centerMap()函數,將 actor_mc實體置放於舞台中
央位置。遊戲從開始到結束,actor_mc始終置放於舞台中央。
行 21:使用 setChildIndex()方法將 actor_mc實體的圖層移到 map_
mc實體上全部子物件的最上層。因為新建立的寶物地點實體圖層會
在最上層,利用這個方法將 actor_mc實體調到最上層,寶物地點才
能在“地面上”讓 actor_mc通過。 setChildIndex()方法需要 2個
參數,第 1個是要改變圖層的物件,第 2個是變動後圖層編號,利
用物件的 numChildren屬性取得該物件的子物件數量減去 1(因為圖
層編號從 0開始 ),即為該物件全部子物件的最上層。
行 22:從元件庫的小藍點類別 blueDot_mc宣告建立命名為 blueDot
的小藍點實體,作為 smallMap_mc實體上顯示 actor_mc實體的相
對位置使用。
行 23:將新建立的小藍點實體加到 stag上,用 stage做為它的顯示容
器。至於小藍點實體的座標將與 actor_mc實體的座標同步異動,在
「遊戲中」影格設定。
行 24∼25:同上節在 stage分別註冊鍵盤事件的 KEY_DOWN與
KEY_UP屬性偵聽處理函數 playKeyDown()與 playKeyUp(),處理
鍵盤按鍵被按下及放開的程序。
3-26
行 26∼29:使 actor_mc實體置放於舞台中央位置的自定函數
centerMap()。作法很簡單,只要把 map_mc的座標設定為舞台中央
座標 (320,240)與 actor_mc座標差距值的位置即可。
行 30∼36:同上節的自定函數 playKeyDown()。
行 37∼43:同上節的自定函數 playKeyUp()。
行 44∼66:同上節的自定函數 getDirect ion()。唯一差異是改變
actor_mc實體的方向,需要前置 map_mc(行 63),因為 actor_mc
實體現在是 map_mc實體下一層級的子實體。
影格 5,「遊戲中」 ►
updateScore();01
if ( dir1 || dir2 || dir3 || dir4 ) {02
map_mc.actor_mc.actor.play();03
moveActor();04
centerMap();05
} else {06
map_mc.actor_mc.actor.gotoAndStop(1);07
}08
blueDot.x = smallMap_mc.x + (map_mc.actor_mc.x)/10;09
blueDot.y = smallMap_mc.y + (map_mc.actor_mc.y)/10;10
function updateScore() {11
score_txt.text = score;12
}13
function moveActor() {14
var new_x, new_y:Number;15
var mx:int = 0;16
var my:int = 0;17
if ( dir_now == 1 ) {18
mx = -dx;19
} else if ( dir_now == 2 ) {20
3-27
01
02
03
04
05
06
07
08
09
10
方向鍵遊戲
mx = -dx;21
my = -dy;22
} else if ( dir_now == 3 ) {23
my = -dy;24
} else if ( dir_now == 4 ) {25
mx = dx;26
my = -dy;27
} else if ( dir_now == 5 ) {28
mx = dx;29
} else if ( dir_now == 6 ) {30
mx = dx;31
my = dy;32
} else if ( dir_now == 7 ) {33
my = dy;34
} else if ( dir_now == 8 ) {35
mx = -dx;36
my = dy;37
}38
new_x = map_mc.actor_mc.x + mx;39
new_y = map_mc.actor_mc.y + my;40
if ( !map_mc.block_mc.hitTestPoint(new_x+map_mc.x, new_y+map_mc.y, true)) {41
map_mc.actor_mc.x = new_x;42
map_mc.actor_mc.y = new_y;43
}44
}45
解說
行 01:隨時可能有成績記錄異動,因此持續呼叫更新舞台上成績的自
定函數。
行 02∼08:同上節的判斷有無任何一個方向指標為 true的情況,如
果成立則,讓 actor_mc走動 (行 03),並呼叫執行 moveActor()函
數 (行 04),決定 actor_mc能否移動到新位置。因為 actor_mc可能
改變位置,所以多了一個呼叫使 actor_mc置放舞台中央的函數陳述
式 (行 05)。
3-28
行 09∼10:用 actor_mc實體的最新位置座標來更新小藍點實體的座標。
行 11∼13:更新舞台上方成績記錄的自定函數 updateScore(),本遊
戲的成績項目只有找到寶物的數量,所以只需要異動一個 score_txt
動態文字。
行 14∼45:同上節的自定函數 moveActor()。唯一差異是最後的
hitTestPoint()方法判斷,要測試的物件改為 map_mc實體上的
block_mc實體,即大地圖上的障礙物;測試點一樣是新位置座標,
但是傳回值改為否定的,也就是變成測試“新位置座標不能與大地
圖上的障礙物碰撞”,如果成立,則 actor_mc可以前往新位置。做
這樣的改變是因為現在地圖上增加了許多草叢障礙物,至於小島的
周圍呢?沒有障礙物圍成封閉區域不會走到“海裡”去嗎?答案請
看大地圖實體的特殊影片片段說明。
影格 6 ►
if ( score == spots ) {01
gotoAndStop("成功 ");02
} else {03
gotoAndPlay("遊戲中 ");04
}05
解說
行 01∼05:如果已找到寶物數等於預設的全部寶物數量,則時間軸跳
到「成功」影格 (行 02),否則回到「遊戲中」影格形成迴圈繼續尋
寶 (行 04)。
影格 7,「成功」 ►
map_mc.actor_mc.visible = false;01
stage.removeChild(blueDot);02
stage.removeEventListener(KeyboardEvent.KEY_DOWN, playKeyDown);03
3-29
01
02
03
04
05
06
07
08
09
10
方向鍵遊戲
stage.removeEventListener(KeyboardEvent.KEY_UP, playKeyUp);04
stage.addEventListener(KeyboardEvent.KEY_DOWN, gameStartKey);05
stop();06
解說
行 01:成功完成遊戲後在此有遊戲主角大的圖像顯示,因此暫時將
map_mc實體內的 actor_mc實體隱藏。
行 02:移除加在 stage上的小藍點實體,已經不需要指示了。
行 03∼04:移除註冊在 stage上的鍵盤事件偵聽處理函數。
行 05:在 stage註冊鍵盤事件的按鍵按下處理函數 gameStartKey(),
使玩家按下 <Enter>鍵可以重玩遊戲。
行 06:主場景時間軸停止,等待玩家選擇。
3-4-4 特殊影片片段
大地圖影片片段
3-30
這個影片片段雖然只有 1個關鍵影個格,而且只有圖像物件沒有任何程
式碼,但是我還是將它特別提出來說明,因為真的很特別。
此影片片段有 3圖層。最上層置放遊戲主角實體,也就是 actor_mc,置
放的位置“任意”(障礙物區域除外 ),反正遊戲開始後就會將它“置中”,把
主角永遠擺在鏡頭 (舞台 )中央,這任意位置只不過是當作遊戲開始主角游
走的出發點而已。
中間的圖層是障礙物,置放障礙物實體 block_mc,也就是和 actor_mc
移動“準”新座標點做 hitTestPoint碰撞測試的物件,正常你只會看到四處
放置的草叢,但是如果將此圖層顯示成外框 (如上圖的全部藍色框線 ),可以
看到除了許多圓圈 (草叢外框 )外,最外圍有一個矩形封閉區域,內部有形
狀和小島相似的挖空區域,這個封閉區域使用透明度 alpha值 0%的顏色,
因此除非顯示成外框才看得到。現在應該明白為什麼我們眼睛看到的障礙物
(草叢 )並沒有將小島周圍團團圍住,可是 actor_mc卻能在小島的岸邊停住,
正是這個看不見的外圍區域的作用。為什麼要讓它不能被看見呢?看上圖就
明瞭,因為不讓 actor_mc太靠近陸地邊緣,此障礙區域和陸地邊緣有重疊部
份,而這個圖層是在陸地圖層之上 (為什麼?),如果不把這個區域透明隱藏
起來,那⋯豈不是見光死?
另外值得一提的是草叢 (或其他東西亦可 )的放置,你可以自由的發揮創
意設計障礙來刁難玩家,唯一限制除了必須要留路給遊戲主角走動外,草叢可
以圍成封閉區域,但是區域內不能有太大的空白區域,因為前面程式說明提
到,隨機的寶物地點也是使用 hitTestPoint方法和 block_mc測試找出的,如
果草叢的封閉區域內有足夠的空間允許置放寶物地點,試想這個地點 actor_
mc能進得去嗎?進不去遊戲就永遠無法完成,除非⋯你是故意整玩家!
最底下圖層是陸地,只要單純的陸地圖像即可。
至於藍色的“海”?使用遊戲舞台背景顏色即可。如果你真的要使用一
個海的圖像當然也可以,只是它必須非常的大,大到大地圖移動到最邊緣仍
然不會漏餡,出現不該出現的東西,有興趣可以自己試看看。
3-31
01
02
03
04
05
06
07
08
09
10
方向鍵遊戲
寶物地點影片片段
寶物地點影片片段實體是經由主場景時間軸「遊戲開始」影格的程式碼
尋找大地圖實體的隨機位置放置。被放置後隨即播放,在「遊戲中」影格迴
圈不斷偵測與 actor_mc實體的碰撞,也就是有無被找到。如果被找到進入
「找到」程序,移除實體結束流程。
【Action & 標籤】圖層
影格 1,「初值」: ►
var num:int;01
stop();02
解說
行 01:宣告此實體的編號變數,實際數值在被放置於大地圖實體時設
定。
行 02:停止待命狀態,在被放置後再啟動播放。
影格 2,「遊戲中」: ►
在「遊戲中」影格迴圈內,一般情況是隱藏起來不讓玩家看到,當
actor_mc實體靠近至一個範圍內才會顯示圖像,同時也不斷偵測與 actor_
mc實體的碰撞。
3-32
if (Point.distance(new Point(this.x, this.y), new Point(parent.actor_mc.x,parent.actor_01
mc.y)) < 70 ) {
this.visible = true;02
} else {03
this.visible = false;04
}05
if ( this.hitTestPoint(parent.actor_mc.x+parent.parent.map_mc.x, parent.actor_06
mc.y+parent.parent.map_mc.y, true) ) {
this.gotoAndPlay("找到 ");07
}08
解說
行 01∼05:使用 distance()方法傳回此實體與 actor_mc實體之間的
距離。distance()方法使用 2個參數,即兩個點,在此為兩個實體的
註冊點。如果兩點距離小於 70,即 actor_mc靠近至寶物地點 70點
的範圍內時,寶物地點實體將顯示 (行 02),否則將隱藏 (行 04)。
注意 actor_mc實體是寶物地點實體的父輩物件。
行 06∼08:使用 hitTestPoint()方法斷偵測與 actor_mc實體的碰撞。
偵測點是 actor_mc實體的註冊點,但必須是相對於 stage的座標,
所以 x與 y座標都分別需要加上 map_mc實體的座標值。如果碰撞
成立,時間軸進入「找到」程序 (行 07)。
影格 3: ►
gotoAndPlay("遊戲中 ");01
解說
行 01:和「遊戲中」影格形成迴圈不斷執行偵測任務。
3-33
01
02
03
04
05
06
07
08
09
10
方向鍵遊戲
影格 4,「找到」: ►
此關鍵影格沒有程式碼,只播放預置於影格的寶物音效,告知此處寶物
已被找到。一直到影格 6才有處理的程式碼,目的一方面是播放音效,另一
方面是讓寶物地點圖像稍做停留,若瞬間消失的話玩家根本看不到。
影格 6: ►
parent.parent.score ++;01
stage.removeChild(parent.parent["redDot_mc" + num]);02
parent.removeChild(this);03
stop();04
解說
行 01:父父輩得分變數累加 1。
行 02:因為此地點寶物被找到,小地圖的相對小紅點實體也應移除,
寶物編號就是在此時派上用場。
行 03:移除此寶物地點實體。
行 04:結束流程停止播放。
主角影片片段
3-34
我們的主角維持一貫的作風,沒什麼腳本台詞,移動靠玩家及主場景時
間軸腳本;找寶物靠寶物地點自行偵測;尊貴的主角光靠擺幾個 pose就可
以一直霸佔鏡頭的中央位置。只是這回 pose數還不少,上左圖 8個方向的
actor實體,從編號 1到 8分別置放於 actor_mc實體的 8個關鍵影格,根據
方向鍵組合來擺 pose。每一個 actor實體的內容如上右圖 (向左方向 ),影格
1為停止外,其餘影格負責走動的動畫表現,影格 10回到影格 2形成不斷走
動的動畫。有一點請注意,actor_mc實體表現的是人的造型,因此它的註冊
點最好不是影片片段中心點的位置,而是最下面的中間,這樣才不至於中心
點以下的雙腿部份跑到障礙物內。
3-5 迷你賽車遊戲