Building a Tank Battle Game in Go with Ebiten
This article walks through creating a tank‑battle game in Go using the Ebiten engine, covering repository setup, required tools, code organization, the core Update and Draw loops, and the mathematical formulas—rotation matrix, cross product, dot product, and the separating axis theorem—used for collision detection.
Project Repository
Repository: https://github.com/gofish2020/tankgame. After cloning, run go run main.go to start the game.
Engine
Implemented with Ebitengine. A collection of examples for learning is available at https://ebiten-zh.vercel.app/examples/.
Development Environment
Go version: go1.20 Linux or macOS requires a C compiler; installation instructions are at https://ebiten-zh.vercel.app/documents/install.html
Code Structure
tankgame
├── go.mod
├── go.sum
├── main.go // entry point
├── package
│ ├── game
│ │ └── game.go // global parameters and game‑progress logic
│ ├── monitor
│ │ └── screen.go // screen width/height
│ ├── tank
│ │ ├── barrier.go // obstacle creation & drawing
│ │ ├── check.go // collision detection
│ │ ├── gameover.go// game‑over screen
│ │ ├── keyboard.go// tank‑surrounding text drawing
│ │ ├── menu.go // start menu
│ │ ├── npcmove.go // NPC movement & enemy search
│ │ ├── name.go // tank name handling
│ │ └── tank.go // tank drawing & logic
│ └── utils
│ ├── sound // audio handling
│ ├── utils.go // sector drawing
│ └── variable.go// global parameters controlling game progress
├── resource // image assetsUpdate Loop
// Update data
func (g *Game) Update() error {
enemyCount := 0
// Restart game
g.Restart()
// Play background music
sound.PlayBGM()
// Separate player and NPC tanks
var playerPosition tank.TankPosition
var npcPositions []tank.TankPosition
// Detect living tanks
liveTanks := []*tank.Tank{}
for _, tk := range g.tks {
// Update tank
tk.Update()
// Collision detection
tk.CheckCollisions(g.tks, g.barriers)
// Limit tank movement range
tk.LimitTankRange(tank.MinXCoordinates, tank.MinYCoordinates, float64(monitor.ScreenWidth)-30, float64(monitor.ScreenHeight)-30)
// Record tank position
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 {
// Record NPC position
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)
}
}
// Update remaining tanks
g.tks = liveTanks
// Initial screen
if utils.GameProgress == "init" || utils.GameProgress == "pass" {
tank.MenuUpdate(g.tks) // button movement + bullet collision
} else if utils.GameProgress == "play" {
// Move NPC tanks and detect enemies in attack range
tank.MoveAndFineEnemyTank(playerPosition, npcPositions)
}
if utils.GameProgress == "play" && enemyCount == 0 {
utils.GameProgress = "next" // next level
}
// Game over key handling
tank.GameOverUpdate()
return nil
}Draw Loop
// Render screen
func (g *Game) Draw(screen *ebiten.Image) {
screen.Fill(color.RGBA{240, 222, 180, 215})
if utils.GameProgress == "init" || utils.GameProgress == "pass" {
// Start screen
tank.MenuDraw(screen)
}
x, y := 0.0, 0.0
// Draw each tank
for _, tk := range g.tks {
tk.Draw(screen)
// Draw key overlay for player tank
if tk.TkType == tank.TankTypePlayer {
tank.KeyPressDrawAroundTank(tk, screen)
x, y = tk.X, tk.Y // player perspective
}
}
// Draw fog of war and obstacles
if utils.GameProgress == "play" {
tank.DrawWarFogAndBarriers(screen, x, y, g.barriers)
}
// Draw death list
tank.DrawNameList(screen)
// Game over screen
tank.GameOverDraw(screen)
}Mathematical Foundations for Collision Detection
Rotation matrix – computes new coordinates after rotating a point.
Cross‑product formula – determines whether a point lies inside a polygon; the coordinate system uses Y increasing downward, reversing the usual left‑hand (counter‑clockwise) rule.
Dot‑product formula – used for vector projection calculations.
Separating Axis Theorem (SAT) – tests rectangle intersection by projecting polygon vertices onto each edge normal and checking for overlap; any axis without overlap indicates no intersection.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Nullbody Notes
Go backend development, learning open-source project source code together, focusing on simplicity and practicality.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
