GameMaker|打方塊-04|球的移動|球的反彈

GameMaker|打方塊-04|球的移動|球的反彈

更新於 發佈於 閱讀時間約 1 分鐘

這篇內容,主要會講解,關於球的相關設定,包括了如何讓玩家發球、如何讓球移動、如何讓球反彈、如何改變球的大小,等等的相關功能。

本篇教學所使用的系統是Windows 10,如果有按鍵相關的說明,會用Windows的系統來解釋。如果你用的是Mac系統,再麻煩自行換成相對應的按鍵。

raw-image


前置知識


在學習本篇內容之前,最好具備以下的前置知識,以免看不懂這篇教學的內容。


目標講解


本次的版本,會新增以下的功能:

  • 按A鍵,能讓遊戲暫停。
  • 按空白建,能改變球的大小。
  • 按Z/X/C鍵,能夠讓玩家發球。
  • 球在撞到牆壁或玩家時,會進行反彈。
  • 球在碰到out,或是飛出房間時,會自動銷毀。


另外,本次的版本,會出現以下的Bug:

  • 球會卡在玩家裡面。
  • 球會卡在牆壁裡面。
  • 不良的牆壁設定,會導致球穿牆。

由於篇幅的關係,Bug的部分,會留到下一篇的教學,再來講解處理方案。

範例版本展示

範例版本展示


事前準備


這邊要先請各位,準備一個寬高為80*80的矩形,以及一個直徑為160的圓形,尺寸的單位是pixel。

我個人是習慣使用PS,來製作2D素材,接著再導入專案當中。當然,你也可以使用Sprite的繪圖功能,來把素材製作出來。


設定暫停的變數


建立一個Script,將他命名成「scr_global_var」,這個腳本(Script)將會放置,所有的全域變數(Global Variable)。

在scr_global_var裡面,放入以下的程式碼:

//遊戲的狀態參數
global.game = {
//遊戲的播放狀態,0=暫停,1=遊戲中
play : 1 ,
}


這邊分享一個簡單的邏輯:「如果有兩個以上的物件,需要使用同一個變數,那就把這個變數,設定成全域變數。」

這個play的變數,不管是玩家、球、計時器,都會使用到,因此就需要把他,設定成全域變數。

scr_global_var的設定

scr_global_var的設定


這邊我使用了結構體(Struct)的資料型態(Data Type),來幫全域變數進行命名。

結構體的部分,我之前還沒講解過,各位可以把他理解成「資料夾」的功能,如果用上面的程式碼來解釋,game就是資料夾(結構體),而play就是資料夾裡面的變數。

由於目前只有這個變數,所以各位可能還無法理解,結構體的好處,等慢慢增加之後,會比較能理解結構體的用途。

這邊附上官方的說明書,之後如果有機會,我會在寫相關的教學:

結構體(官方的說明書)


設定out


建立一個Sprite,將他命名成「spr_out」,接著把80*80的素材匯入(Import)到spr_out裡面。

這個out的功能,是用來偵測球是否出界,如果觸碰到球,球就會被消除。

spr_out的設定,與spr_wall相同,一樣的碰撞遮罩,以及Nine Slice的設定。

spr_out的設定

spr_out的設定


建立一個Object,將他命名成「obj_out」,這表示out的物件。接著把Sprite的參數,設定成「spr_out」。

obj_out的設定

obj_out的設定


叫出rm_stage_01的房間,並在Instances的圖層上,建立out的實體,方法就跟牆壁一樣,放上去後拉長就行了。

建立out的實體

建立out的實體


設定球


建立一個Sprite,將他命名成「spr_ball_big」,接著把直徑為160的素材匯入到spr_ball_big裡面。

雖然說球有大中小三種尺寸,但在這個專案中,我們會透過改變圖片的Scale(縮放比例),來控制球的尺寸,因此這邊只需要準備,大球的尺寸即可。

碰撞遮罩需要把Type的參數,修改成Ellipse (Slow)。然後把Fps設定成0,確保這個Sprite不會產生動畫。

原點則是設定成Middle Centre,這表示物體的正中心。

spr_ball_big的設定

spr_ball_big的設定


建立一個Object,將他命名成「obj_ball」,這表示球的物件。接著把Sprite的參數,設定成「obj_ball」。

obj_ball的設定

obj_ball的設定


在obj_ball的物件裡,加入Create事件,並放入以下的程式碼:

//移動速度
move_speed = 30 ;

//設定移動速度
id.speed = move_speed ;


id.speed是內建變數(Built-in Variable),可以用來控制,實體的移動速度。

至於移動的方向,我們會透過玩家的物件,來進行控制,因此這邊不用加入。

設定obj_ball的Create事件

設定obj_ball的Create事件


除了移動速度之外,我們還需要改變球的大小,因此打開scr_global_var,放入以下的程式碼:

//球的參數
global.ball = {
//球的大小,"s"=小球,"n"=正常,"b"=大球
size : "n" ,
}


加入程式碼後的參考圖:

增加scr_global_var的變數

增加scr_global_var的變數


在obj_ball的Create事件裡,放入以下的程式碼:

//設定大小
if (global.ball.size == "n") {
id.image_xscale = 0.5 ;
id.image_yscale = 0.5 ;
} else if (global.ball.size == "b") {
id.image_xscale = 1 ;
id.image_yscale = 1 ;
} else if (global.ball.size == "s") {
id.image_xscale = 0.25 ;
id.image_yscale = 0.25 ;
}


在obj_ball的物件裡,加入Step事件,並放入以下的程式碼:

//遊戲中
if (global.game.play == 1) {
id.speed = move_speed ;
//設定大小
if (global.ball.size == "n") {
id.image_xscale = 0.5 ;
id.image_yscale = 0.5 ;
} else if (global.ball.size == "b") {
id.image_xscale = 1 ;
id.image_yscale = 1 ;
} else if (global.ball.size == "s") {
id.image_xscale = 0.25 ;
id.image_yscale = 0.25 ;
}
//除了遊戲中的狀態之外,其他狀態一律停止動作
} else {
id.speed = 0 ;
}


加入程式碼後的參考圖:


id.image_xscaleid.image_yscale是內建變數,可以用來改變,實體的圖片比例。

依照程式碼的設定,普通球("n")的直徑是80,大球("b")的直徑是160,小球("s")的直徑是40。

用來改變global.ball.size的程式碼,將會放在玩家的物件裡面。在玩家還沒改變大小的狀況下,應該會產生普通球,因為global.ball.size的初始值是"n"


補充說明一下,其實在Step事件裡面,撰寫改變大小的程式碼,並不是最優解,因為這樣每個step,都會去改變球的大小,如果球的數量太多,效能就會下降。

比較好的做法,應該是在「碰撞」到道具的時候,執行「一次性」的程式碼,來改變所有球的大小。

這邊是為了方便測試,所以才將他放在Step事件裡面。這在之後的版本中,會在進行修正。


接著要來設定,關於反彈的程式碼。

在obj_ball的物件裡,加入與obj_wall碰撞的事件,並放入以下的程式碼:

//遊戲中
if (global.game.play == 1) {
//微調反彈的角度,不然球的路徑,會一直保持不變
id.direction += irandom_range(-5 , 5) ;
//反彈
move_bounce_all(false) ;
}


在obj_ball的物件裡,加入與obj_player碰撞的事件,並放入以下的程式碼:

//遊戲中
if (global.game.play == 1) {
//微調反彈的角度,不然球的路徑,會一直保持不變
id.direction += irandom_range(-5 , 5) ;
//反彈
move_bounce_all(false) ;
}


加入程式碼後的參考圖:


當實體碰撞到實體時,就會觸發碰撞(Collision)事件。上面的設定,表示碰撞到obj_wall和obj_player時,就會觸發事件裡面的程式碼。


id.direction是內建變數,可以用來改變,實體的移動方向。irandom_range(-5 , 5)的用途,是隨機產生-5~5之間的整數。

id.direction += irandom_range(-5 , 5)的意思是:讓移動的方向,隨機加上-5~5的整數。

如果不加入這行程式碼,球依然能正常移動,只是路徑會變得「非常固定」,我個人覺得會有點無聊。

因此才加入這行程式碼,讓球路發生些微的改變,這樣遊戲的體驗,會變得更好一點。


呼叫move_bounce_all(false)的函式(Function),能讓球的移動路徑,變成反彈後的數值。講白話文就是讓球反彈。

接著要來設定,「讓球消失」的相關程式碼。


在obj_ball的物件裡,加入與obj_out碰撞的事件,並放入以下的程式碼:

//銷毀球
instance_destroy() ;


在obj_ball的物件裡,加入Outside Room事件,並放入以下的程式碼:

//防呆裝置,如果球還是不小心飛出房間(沒碰到out),那就自動銷毀
instance_destroy() ;


加入程式碼後的參考圖:


呼叫instance_destroy()的函式,能夠銷毀呼叫函式的實體,簡單來說就是自毀。

Outside Room的事件,會在實體超出房間後觸發。在「正常」的情況下,球應該只有在接觸到obj_out時,才會消失。

但為了防止意外的發生,所以才會在Outside Room的事件裡面,加入銷毀球的功能。


設定玩家


在obj_player的物件裡,加入Key Press - A事件,並放入以下的程式碼:

if (global.game.play == 0) {
global.game.play = 1 ;
} else if (global.game.play == 1) {
global.game.play = 0 ;
}


加入這段程式碼之後,我們就可以透過鍵盤上的A,來決定遊戲是否要暫停。

設定obj_player的Key Press - A事件

設定obj_player的Key Press - A事件


接著要修改一下,玩家移動的程式碼,把「暫停」的功能給加進去。

打開obj_player裡的Key Down - Left事件,並把程式碼,修改成以下的樣子:

//遊戲中
if (global.game.play == 1) {
//沒碰到牆就移動
if (!place_meeting(id.x - move_speed , id.y , obj_wall)) {
//往左移動move_speed的距離
id.x -= move_speed ;
} else {
//小於move_speed的距離,會出現隱形牆,把移動條件改成撞到牆為止
while (!place_meeting(id.x - 0.1 , id.y , obj_wall)) {
id.x -= 0.1 ;
}
}
}


打開obj_player裡的Key Down - Right事件,並把程式碼,修改成以下的樣子:

//遊戲中
if (global.game.play == 1) {
//沒碰到牆就移動
if (!place_meeting(id.x + move_speed , id.y , obj_wall)) {
//往右移動move_speed的距離
id.x += move_speed ;
} else {
//小於move_speed的距離,會出現隱形牆,把移動條件改成撞到牆為止
while (!place_meeting(id.x + 0.1 , id.y , obj_wall)) {
id.x += 0.1 ;
}
}
}


修改後的參考圖:


在obj_player的物件裡,加入Key Press - Space事件,並放入以下的程式碼:

//遊戲中
if (global.game.play == 1) {
//改變球的大小,變化的順序為小中大
if (global.ball.size == "n") {
global.ball.size = "b" ;
} else if (global.ball.size == "b") {
global.ball.size = "s" ;
} else if (global.ball.size == "s") {
global.ball.size = "n" ;
}
}


加入這段程式碼之後,我們就可以透過空白鍵,來改變球的大小。

設定obj_player的Key Press - Space事件

設定obj_player的Key Press - Space事件


在obj_player的物件裡,加入Key Press - Z事件,並放入以下的程式碼:

//遊戲中
if (global.game.play == 1) {
//用球的大小,來決定y座標的偏移量
var _move_y = 0 ;
if (global.ball.size == "n") {
_move_y = 80 ;
} else if (global.ball.size == "b") {
_move_y = 120 ;
} else if (global.ball.size == "s") {
_move_y = 80 ;
}
//向130~140度發球,單發射擊
var _ball = instance_create_depth(id.x , id.y - _move_y , 0 , obj_ball) ;
_ball.direction = 135 ;
_ball.direction += irandom_range(-5 , 5) ;
}


加入程式碼後的參考圖:

設定obj_player的Key Press - Z事件

設定obj_player的Key Press - Z事件


instance_create_depth(id.x , id.y - _move_y , 0 , obj_ball)的意思是:在座標(id.x , id.y-_move_y)的位置上,建立一個obj_ball的實體,深度設定為0。

這個函式在使用的時候,會順便回傳,這個新建實體的ID。因此,我們可以透過這種特性,來改變球的移動方向。

改變方向的方法很簡單,就是先建立一個局部變數(Local Variable):_ball,並把函式回傳的ID放入_ball

接著我們就可以透過_ball,來改變移動方向的參數,例如:_ball.direction = 135,就是把球的移動的方向,設定成135度。

(GameMaker是用移動方向的直線,與基準線的夾角,來表示實體的移動方向。0代表右側,90代表上方,180代表左側,270代表下方。)


_move_y的用途,是讓球生成的位置,變成在玩家的原點(Origin)上方。

原本是直接用120的數值,但後來發現,大球可以用120,但其他的兩個尺寸,用120的間距會太大,看起來並不合適。

因此才決定加入,偏移量的程式碼,讓大球的距離是120,剩下的兩個,則是使用80。


X鍵和C鍵的發球,跟Z鍵的邏輯是一樣的,只是做了一些微調。

X鍵的發射方向是40~50度,而C鍵則是連發模式,按著就能連續發射,發射的方向則是在45~135度之間。

下面是X鍵和C鍵的設定方法。


在obj_player的物件裡,加入Key Press - X事件,並放入以下的程式碼:

//遊戲中
if (global.game.play == 1) {
//用球的大小,來決定y座標的偏移量
var _move_y = 0 ;
if (global.ball.size == "n") {
_move_y = 80 ;
} else if (global.ball.size == "b") {
_move_y = 120 ;
} else if (global.ball.size == "s") {
_move_y = 80 ;
}
//向40~50度發球,單發射擊
var _ball = instance_create_depth(id.x , id.y - _move_y , 0 , obj_ball) ;
_ball.direction = 45 ;
_ball.direction += irandom_range(-5 , 5) ;
}


在obj_player的物件裡,加入Key Down - C事件,並放入以下的程式碼:

//遊戲中
if (global.game.play == 1) {
//用球的大小,來決定y座標的偏移量
var _move_y = 0 ;
if (global.ball.size == "n") {
_move_y = 80 ;
} else if (global.ball.size == "b") {
_move_y = 120 ;
} else if (global.ball.size == "s") {
_move_y = 80 ;
}
//向45~135度發球,連發射擊
var _ball = instance_create_depth(id.x , id.y - _move_y , 0 , obj_ball) ;
_ball.direction = 90 ;
_ball.direction += irandom_range(-45 , 45) ;
}


加入程式碼後的參考圖:


善心提醒,C鍵是使用「Key Down事件」,這樣子才可以按著連發,而用來單發的X鍵,則是使用Key Press事件,記得不要設定錯誤了。




🔔如果內容對你有幫助,可以按個喜歡,這樣就能讓更多人,接觸到這些棒棒的內容🔔


✨祝各位也能開心的做出好遊戲✨

avatar-img
開心做遊戲 Happy Making Game
10會員
68內容數
免費、開心、簡單,這是我做教學的理念,我希望透過我的行動,讓人們找回自我學習的快樂,讓那些資源稀少的自學者們,也能朝著自己的夢想來前進。讓我們一起開心的做出遊戲吧!
留言
avatar-img
留言分享你的想法!
這篇內容,主要會講解,要如何規劃房間、相機,以及各種物件的尺寸大小。
這篇內容,主要會講解,如何讓玩家進行移動,在解釋的過程中,會接觸到碰撞遮罩、Nine Slice、鍵盤事件,等等的相關設定。
這是一篇前導文章,主要解釋「打方塊」的實戰範例,可以學習到哪方面的技巧,讀完之後,各位可以決定,要不要繼續學習這個專案。
這篇內容,主要會講解,要如何規劃房間、相機,以及各種物件的尺寸大小。
這篇內容,主要會講解,如何讓玩家進行移動,在解釋的過程中,會接觸到碰撞遮罩、Nine Slice、鍵盤事件,等等的相關設定。
這是一篇前導文章,主要解釋「打方塊」的實戰範例,可以學習到哪方面的技巧,讀完之後,各位可以決定,要不要繼續學習這個專案。