Golang 實現坦克世界

Golang 實現坦克世界

項目地址: https://github.com/gofish2020/tankgame 歡迎 Fork && Star

遊戲效果

本項目基於遊戲引擎 Ebitengine 開發,這裏有很多的實例 https://ebiten-zh.vercel.app/examples/ 便於邊學邊用

程序下載到本地,直接 go run main.go即可看效果。

代碼結構

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