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
留言分享你的想法!
avatar-img
開心做遊戲 Happy Making Game
11會員
68內容數
免費、開心、簡單,這是我做教學的理念,我希望透過我的行動,讓人們找回自我學習的快樂,讓那些資源稀少的自學者們,也能朝著自己的夢想來前進。讓我們一起開心的做出遊戲吧!
2024/09/02
這篇內容,主要會講解,要如何規劃房間、相機,以及各種物件的尺寸大小。
Thumbnail
2024/09/02
這篇內容,主要會講解,要如何規劃房間、相機,以及各種物件的尺寸大小。
Thumbnail
2024/09/02
這篇內容,主要會講解,如何讓玩家進行移動,在解釋的過程中,會接觸到碰撞遮罩、Nine Slice、鍵盤事件,等等的相關設定。
Thumbnail
2024/09/02
這篇內容,主要會講解,如何讓玩家進行移動,在解釋的過程中,會接觸到碰撞遮罩、Nine Slice、鍵盤事件,等等的相關設定。
Thumbnail
2024/09/02
這是一篇前導文章,主要解釋「打方塊」的實戰範例,可以學習到哪方面的技巧,讀完之後,各位可以決定,要不要繼續學習這個專案。
Thumbnail
2024/09/02
這是一篇前導文章,主要解釋「打方塊」的實戰範例,可以學習到哪方面的技巧,讀完之後,各位可以決定,要不要繼續學習這個專案。
Thumbnail
看更多
你可能也想看
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
全球科技產業的焦點,AKA 全村的希望 NVIDIA,於五月底正式發布了他們在今年 2025 第一季的財報 (輝達內部財務年度為 2026 Q1,實際日曆期間為今年二到四月),交出了打敗了市場預期的成績單。然而,在銷售持續高速成長的同時,川普政府加大對於中國的晶片管制......
Thumbnail
全球科技產業的焦點,AKA 全村的希望 NVIDIA,於五月底正式發布了他們在今年 2025 第一季的財報 (輝達內部財務年度為 2026 Q1,實際日曆期間為今年二到四月),交出了打敗了市場預期的成績單。然而,在銷售持續高速成長的同時,川普政府加大對於中國的晶片管制......
Thumbnail
這篇內容,將透過實戰教學,介紹GameMaker中的Event。包括Event的簡介、Create的講解、Step的講解、Events的基本操作。
Thumbnail
這篇內容,將透過實戰教學,介紹GameMaker中的Event。包括Event的簡介、Create的講解、Step的講解、Events的基本操作。
Thumbnail
這篇內容,將透過實戰教學,介紹GameMaker中的Sprite。包括建立新的Sprite、重新命名及刪除、建議的命名方式、編輯圖像、調整圖像大小、動畫的概述、原點設置、碰撞遮罩的概述。
Thumbnail
這篇內容,將透過實戰教學,介紹GameMaker中的Sprite。包括建立新的Sprite、重新命名及刪除、建議的命名方式、編輯圖像、調整圖像大小、動畫的概述、原點設置、碰撞遮罩的概述。
Thumbnail
這篇文章介紹瞭如何使用Raspberry Pi PICO等材料製作自己的GAME&WATCH大金剛掌機,以及接線圖、程式碼下載和安裝遊戲的教學,並且分享了製作過程中的一些心得和回憶。如果您對童年的遊戲回憶感興趣,這篇文章值得一讀。
Thumbnail
這篇文章介紹瞭如何使用Raspberry Pi PICO等材料製作自己的GAME&WATCH大金剛掌機,以及接線圖、程式碼下載和安裝遊戲的教學,並且分享了製作過程中的一些心得和回憶。如果您對童年的遊戲回憶感興趣,這篇文章值得一讀。
Thumbnail
這篇文章是CONTORL直播的第三部分,介紹了遊戲的內容和心得。作者表示遊戲中的戰鬥逐漸熟練,敵人種類增加,場景也更加精彩。他使用了變聲器增加了娛樂效果,並提到遊戲的難度設計很平衡,場景設計獨特,劇情引人入勝。心得部分,作者表示遊戲很好玩,戰鬥有挑戰性但不過分,劇情和配音也很出色。
Thumbnail
這篇文章是CONTORL直播的第三部分,介紹了遊戲的內容和心得。作者表示遊戲中的戰鬥逐漸熟練,敵人種類增加,場景也更加精彩。他使用了變聲器增加了娛樂效果,並提到遊戲的難度設計很平衡,場景設計獨特,劇情引人入勝。心得部分,作者表示遊戲很好玩,戰鬥有挑戰性但不過分,劇情和配音也很出色。
Thumbnail
「蛤?!居然當機!」瞪著畫面凍結的螢幕,心裡一面嘀嘀咕咕,一面敲著鍵盤,企圖死馬當活馬醫,看看能不能免去重開機的麻煩。 一切的努力都是徒然,這是徹底的當機!滑鼠、鍵盤完全失去作用,只餘關電源強迫關機一條路可走。 在重開機的當兒,一面看著螢幕有沒有顯示異常的訊息,一面開始分析可能的當機原因。
Thumbnail
「蛤?!居然當機!」瞪著畫面凍結的螢幕,心裡一面嘀嘀咕咕,一面敲著鍵盤,企圖死馬當活馬醫,看看能不能免去重開機的麻煩。 一切的努力都是徒然,這是徹底的當機!滑鼠、鍵盤完全失去作用,只餘關電源強迫關機一條路可走。 在重開機的當兒,一面看著螢幕有沒有顯示異常的訊息,一面開始分析可能的當機原因。
Thumbnail
Hello!大家好我是茄諾,今天要來幫自主研發的掌機TBoy寫款可以連線對戰的遊戲,不知道TBoy的朋友可以連至以下網址看看: 因為要連線對戰,所以需要準備兩台TBoy掌機,請參考以上文章製作;接下來要挑選適合連線對戰的遊戲,個人偏愛Pong,這款由Atari開發的投幣式大型電玩對戰遊戲非常適合與家
Thumbnail
Hello!大家好我是茄諾,今天要來幫自主研發的掌機TBoy寫款可以連線對戰的遊戲,不知道TBoy的朋友可以連至以下網址看看: 因為要連線對戰,所以需要準備兩台TBoy掌機,請參考以上文章製作;接下來要挑選適合連線對戰的遊戲,個人偏愛Pong,這款由Atari開發的投幣式大型電玩對戰遊戲非常適合與家
Thumbnail
前言 嗨,各位懷舊遊戲愛好者!今天要跟大家分享一個有趣的主題:如何利用React和Pixi.js這兩大神兵利器,重塑我們那個年代的經典紅白機打磚塊遊戲! 先跟大家簡單科普一下,React是一個超級火爆的前端框架,能讓我們輕鬆創建可重用的UI組件,組件間的狀態管理也相當方便。。。
Thumbnail
前言 嗨,各位懷舊遊戲愛好者!今天要跟大家分享一個有趣的主題:如何利用React和Pixi.js這兩大神兵利器,重塑我們那個年代的經典紅白機打磚塊遊戲! 先跟大家簡單科普一下,React是一個超級火爆的前端框架,能讓我們輕鬆創建可重用的UI組件,組件間的狀態管理也相當方便。。。
Thumbnail
各位安好,歡迎收看奶遊專欄,我是主編螌貓,今天再來專欄嘍! (挨打)唉!別打我(挨打),其他的在生了,快了,別 別 別 (逃~)
Thumbnail
各位安好,歡迎收看奶遊專欄,我是主編螌貓,今天再來專欄嘍! (挨打)唉!別打我(挨打),其他的在生了,快了,別 別 別 (逃~)
Thumbnail
1點選右下皮卡丘的圖示 這邊有木仁老師預先寫好的程式內容 2點選控制 3拖拉重複無限次的積木程式 4在點選動作 5拖拉(定位到隨機位置)進去 6將隨機跟改為鼠標 (鼠標意思就是滑鼠的游標,也就是實際滑鼠的位置方位) 7再給他組合上去就初步完成了!!!
Thumbnail
1點選右下皮卡丘的圖示 這邊有木仁老師預先寫好的程式內容 2點選控制 3拖拉重複無限次的積木程式 4在點選動作 5拖拉(定位到隨機位置)進去 6將隨機跟改為鼠標 (鼠標意思就是滑鼠的游標,也就是實際滑鼠的位置方位) 7再給他組合上去就初步完成了!!!
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News