2025/04/20
根據回饋我重新做了一些調整。
更新:
- 修正電腦的球拍移動速度、微調追球的方式
- 顯示積分目標
- 調整按鈕樣子
- 修正球會被球拍和牆壁夾住的BUG
這次我做了什麼?
《Pong》是雅達利在1972年11月29日推出的一款投幣式大型電玩遊戲。
這次我挑戰的是用 Godot 複製這款遊戲。
遊戲玩法很簡單,就是桌球的簡化版,只需要兩塊板子和一個球。
我做的版本分成單人模式和電腦對打,以及玩家對打的雙人模式。
為什麼要做這個遊戲?
「The 20 Games Challenge」是一個鼓勵開發者在限定時間內製作 20 款小型遊戲 的計畫,目的是透過反覆實作、快速累積經驗,提升你的開發效率與遊戲設計能力。不需要追求完美,只要完成,就能從每個專案中學到寶貴的技巧。
我認為這是還不錯的練習。剛好最近也不知道要寫什麼。
他幫忙列出製作難度從簡單到困難的遊戲,同時也列出各個遊戲的基本製作目標、延伸目標。
還有附上曾經挑戰的人的展示,例如遊戲、開發日誌、原始碼。
(網頁翻譯,原文為英文)
網站裡面也有提到怎麼開始,詳細都可以到網站觀看。
可以搭配 GMTK 以前做的學習心得影片。
當然開始之前要先決定要使用的遊戲引擎,還有對它的基本操作有一定認識。
我這裡自然就是選擇使用 Godot 來挑戰了。
(有中文字幕)
學到什麼?
雖然《Pong》玩法本身很單純,做的時候也是有學到一些東西。
像是讓球反彈的寫法,以及我花最多時間的是延伸目標中的球拍AI部分。
我先大概說一下其他部分。
首先是場地中間的虛線,是用程式碼畫出來的。
只要這行很簡單就畫出一條虛線了。
然後球拍、球也是用內建的方式,球拍是 StaticBody2D、球是 CharacterBody2D。
先一塊白色的 ColorRect,然後上面疊比較小塊的黑色 ColorRect。
這樣很簡單就做出一些陰影的感覺。
再來就是跟玩法比較相關的部分——球的彈跳。
在遊戲裡球有兩個需要反彈的地方,一種是碰到球拍,另一種是碰到牆壁。
碰到牆壁的反彈比較單純,計算它的反彈方向用到的是 bounce()。
我們可以在球碰到牆壁的時候 get_normal() 取得法向量,放進去 bounce() 計算出方向。
球拍的部分也可以這樣做,但這樣的反彈的方向就比較單調。
所以為了多一些變化,這裡採用手動計算方向的方式。
手動計算的方式是參考影片教學給的。
簡單來說如果球離球拍的中心越遠,反彈的角度就會越大。
這時候就跟球碰到球拍時的夾角沒有關係。
再來是我卡很久的電腦AI回球的部分。
最直接粗暴就是讓它一直跟著球的Y座標跑,但這樣怎麼打都會是電腦贏了。
所以會希望他是跟玩家一樣受到速度限制,而且玩家要有「機會」贏。
底下灰色字的部分也是我參考影片的方式。
當球靠近球拍到一定距離,AI就會開始移動。
和玩家移動的方式很接近,一樣是用 speed 去移動Y座標。
只要AI移動到和球的垂直距離小於球拍的長度就會停下來。
這樣會比單純跟著Y座標自然一些,只是也有缺點。
缺點是球一離開球拍範圍,AI就會移動一點。
這會導致像是一格一格移動的感覺,雖然不影響遊玩,但看起來有點奇怪。
所以我想到如果直接找到,球前進的方向和球拍垂直線的「交點」,移動就能一步到位。
這應該也更接近人類操控的方式。
因此這時候的問題自然是怎麼找到這個交點。
最後我找到了Godot 內建用來計算常見的2D幾何運算。
裡面提供計算兩條直線交點的方法,有相交就會回傳點,沒有就是回傳 null。
最後完成的版本就是有顏色的字的部分。
細節可以不用太仔細看,用自己想得到的方式就好了。
做一個簡單的總結,我認為比較重要的部分。
製作這個遊戲我學到可以用 bounce() 計算反彈方向,以及用 move_and_collide() 實現反彈的效果,
然後 Geometry2D 提供很多內建計算幾何的工具。
🎯 目標:
- 建立一個有左右牆壁和中線的競技場。✅
- 在場地兩端各加入一個板子(paddle)。使用玩家輸入讓板子能上下移動。✅
- 加入一顆球,讓它能在場地中移動,並在碰到牆壁或板子時反彈。✅
- 偵測球是否離開場地,並將得分記給相對應的玩家。✅
- 記錄並顯示每位玩家的得分。✅
🎯 延伸目標:
- 寫一個可以跟隨球的AI腳本,這樣就可以只和一個玩家一起玩。✅
- 讓 AI 完全追著球跑雖然簡單,但會變得太強,幾乎無法擊敗。你可能需要讓 AI 表現得「不那麼完美」。✅
- 加入選單功能,讓玩家可以重新開始遊戲。✅
- 加入一些基本音效。例如:球碰撞時播放音效、玩家得分時播放音效。✅
下個目標?
第二款遊戲我決定是《打磚塊 Breakout》,可以說是單機版的《PONG》。
長期目標是至少做到他寫的 10 款遊戲。
透過不斷模仿和實作,對於脫離無止境的教學影片是有一定幫助的。
也歡迎對於這個挑戰有興趣的人,一起嘗試看看,然後分享到群組裡。