Golang 實現坦克世界
Golang 實現坦克世界
項目地址: https://github.com/gofish2020/tankgame 歡迎 Fork && Star
遊戲效果
本項目基於遊戲引擎 Ebitengine
開發,這裏有很多的實例 https://ebiten-zh.vercel.app/examples/ 便於邊學邊用
程序下載到本地,直接 go run main.go
即可看效果。
-
開發使用的
go1.20
版本 -
如果你是
Linux
orMacOS
需要安裝C
語言編譯器,安裝教程 https://ebiten-zh.vercel.app/documents/install.html
代碼結構
tankgame
├── go.mod
├── go.sum
├── main.go 入口函數
├── package
│ ├── game
│ │ └── game.go 基於全局參數,遊戲進度邏輯處理
│ ├── monitor
│ │ └── screen.go 顯示器的寬+高獲取
│ ├── tank
│ │ ├── barrier.go 障礙物的創建 + 繪製
│ │ ├── check.go 碰撞檢測
│ │ ├── gameover.go 遊戲結束畫面
│ │ ├── keyboard.go 坦克周圍字體繪製
│ │ ├── menu.go 遊戲開始界面
│ │ ├── npcmove.go npc的移動 + 敵人搜索
│ │ ├── name.go 坦克的名字
│ │ └── tank.go 每個坦克的繪製 + 邏輯處理
│ └── utils
│ ├── sound
│ │ ├── sound.go 音頻處理
│ ├── utils.go 扇形繪製
│ └── variable.go 全局參數(控制遊戲進度)
├── resource 圖片資源
代碼中有大量註釋, 直接從 type Game struct
結構體中的 Update
(負責數據的更新) 和Draw
(負責界面繪製)兩個函數開始看
// 更新數據
func (g *Game) Update() error {
enemyCount := 0
// 遊戲重啓
g.Restart()
// 播放 bgm
sound.PlayBGM()
// 分離 player 和 npc 坦克
var playerPosition tank.TankPosition
var npcPositions []tank.TankPosition
// 檢測存活的坦克
liveTanks := []*tank.Tank{}
for _, tk := range g.tks {
// 更新坦克
tk.Update()
// 檢測碰撞
tk.CheckCollisions(g.tks, g.barriers)
// 限制坦克運動範圍
tk.LimitTankRange(tank.MinXCoordinates, tank.MinYCoordinates, float64(monitor.ScreenWidth)-30, float64(monitor.ScreenHeight)-30)
// 記錄下坦克當前的位置
if tk.TkType == tank.TankTypePlayer {
playerPosition.X = tk.X
playerPosition.Y = tk.Y
playerPosition.TK = tk
if tk.HealthPoints == 0 {
tank.UpdateNameList(tk.Name)
utils.GameProgress = "over"
sound.PlaySound("yiwai")
break
}
} else {
// 記錄npc的位置
if tk.HealthPoints == 0 {
tank.UpdateNameList(tk.Name)
utils.KilledCount++
tk.DeathSound()
} else {
enemyCount++
npcPositions = append(npcPositions, tank.TankPosition{X: tk.X, Y: tk.Y, TK: tk})
}
}
if tk.HealthPoints != 0 {
liveTanks = append(liveTanks, tk)
}
}
// 更新 g.tks,剩餘的坦克
g.tks = liveTanks
// 初始界面
if utils.GameProgress == "init" || utils.GameProgress == "pass" {
tank.MenuUpdate(g.tks) // 按鈕移動 + 炮彈和按鈕碰撞
} elseif utils.GameProgress == "play" {
// 移動 npc 坦克,並檢測攻擊範圍內敵人
tank.MoveAndFineEnemyTank(playerPosition, npcPositions)
}
if utils.GameProgress == "play" && enemyCount == 0 { // 全部消滅
utils.GameProgress = "next"// 下一關
}
// 遊戲結束,檢測按鍵消息
tank.GameOverUpdate()
returnnil
}
// 界面繪製
func (g *Game) Draw(screen *ebiten.Image) {
screen.Fill(color.RGBA{240, 222, 180, 215})
if utils.GameProgress == "init" || utils.GameProgress == "pass" {
// 起始界面
tank.MenuDraw(screen)
}
x, y := 0.0, 0.0
// 繪製每個坦克
for _, tk := range g.tks {
tk.Draw(screen)
// 繪製按鍵
if tk.TkType == tank.TankTypePlayer {
tank.KeyPressDrawAroundTank(tk, screen)
x, y = tk.X, tk.Y // 以player的視角
}
}
// 繪製戰爭迷霧 + 障礙物
if utils.GameProgress == "play" {
tank.DrawWarFogAndBarriers(screen, x, y, g.barriers)
}
// 繪製死亡名單
tank.DrawNameList(screen)
// 遊戲結束界面
tank.GameOverDraw(screen)
}
數學知識補充
這些做碰撞檢測需要用的知識點
旋轉矩陣用來計算座標點經過旋轉後的新的座標
叉積公式
可以用來判斷點是否在多邊形內部;
代碼的中的座標是向下爲 Y 軸,向右爲 X 軸,所以這裏的左邊(逆時針),變成順時針;這裏的右邊(順時針),變成了逆時針。
點積公式
用來做向量投影計算
分離軸定理用來做矩形是否相交的算法
找多邊形的每條邊的法向量(然後作爲軸),讓每個多邊形的頂點向量(計算點積)向軸進行投影;判斷投影是否有重疊。。只要出現一個不重疊的軸,就說明多邊形不相交
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/1ja1PhQaEiLUDfgjOHiGfA