当前位置: 首页 > backend >正文

Go语言Ebiten坦克大战

下载依赖

 go get github.com/hajimehoshi/ebiten/v2

package mainimport ("image/color""math/rand""strconv""time""github.com/hajimehoshi/ebiten/v2""github.com/hajimehoshi/ebiten/v2/ebitenutil""github.com/hajimehoshi/ebiten/v2/inpututil"
)const (screenWidth  = 640screenHeight = 480tankSize     = 32bulletSize   = 4wallSize     = 32
)// 方向定义
const (Up = iotaRightDownLeft
)// 坦克结构
type Tank struct {x, y          float64dx, dy        float64direction     intspeed         float64isPlayer      boolshootCooldown time.DurationlastShot      time.Time
}// 子弹结构
type Bullet struct {x, y   float64dx, dy float64active boolowner  *Tank
}// 墙体结构
type Wall struct {x, y         float64destructible bool
}// 爆炸效果
type Explosion struct {x, y      float64radius    float64maxRadius float64active    bool
}// 游戏状态
type Game struct {player     *Tankenemies    []*Tankbullets    []*Bulletwalls      []*Wallexplosions []*Explosionscore      intgameOver   bool
}// 初始化游戏
func NewGame() *Game {g := &Game{bullets:    make([]*Bullet, 0, 10),enemies:    make([]*Tank, 0, 5),walls:      make([]*Wall, 0),explosions: make([]*Explosion, 0),}// 创建玩家坦克g.player = &Tank{x:             screenWidth / 2,y:             screenHeight - tankSize*2,direction:     Up,speed:         2.0,isPlayer:      true,shootCooldown: 500 * time.Millisecond,}// 创建墙壁g.createWalls()// 创建初始敌人g.spawnEnemies(3)return g
}// 创建墙壁
func (g *Game) createWalls() {// 边界墙for x := 0; x < screenWidth; x += wallSize {g.walls = append(g.walls, &Wall{x: float64(x), y: 0, destructible: false})g.walls = append(g.walls, &Wall{x: float64(x), y: screenHeight - wallSize, destructible: false})}for y := wallSize; y < screenHeight-wallSize; y += wallSize {g.walls = append(g.walls, &Wall{x: 0, y: float64(y), destructible: false})g.walls = append(g.walls, &Wall{x: screenWidth - wallSize, y: float64(y), destructible: false})}// 随机内部墙壁rand.Seed(time.Now().UnixNano())for i := 0; i < 70; i++ {x := float64(rand.Intn(screenWidth/wallSize-2)*wallSize + wallSize)y := float64(rand.Intn(screenHeight/wallSize-2)*wallSize + wallSize)// 避免玩家初始位置有墙if x > g.player.x-tankSize*2 && x < g.player.x+tankSize*2 &&y > g.player.y-tankSize*2 && y < g.player.y+tankSize*2 {continue}destructible := rand.Float64() < 0.7 // 70%可破坏墙g.walls = append(g.walls, &Wall{x: x, y: y, destructible: destructible})}
}// 生成敌人
func (g *Game) spawnEnemies(count int) {for i := 0; i < count; i++ {x := float64(rand.Intn(screenWidth/tankSize-4)*tankSize + tankSize*2)y := float64(rand.Intn(screenHeight/tankSize-6)*tankSize + tankSize*2)enemy := &Tank{x:             x,y:             y,direction:     Down,speed:         1.0,isPlayer:      false,shootCooldown: 1500 * time.Millisecond,}g.enemies = append(g.enemies, enemy)}
}// 发射子弹
func (g *Game) shoot(t *Tank) {now := time.Now()if now.Sub(t.lastShot) < t.shootCooldown {return}t.lastShot = nowb := &Bullet{owner:  t,active: true,}// 根据坦克方向设置子弹初始位置和速度switch t.direction {case Up:b.x = t.x + tankSize/2 - bulletSize/2b.y = t.yb.dy = -5.0case Right:b.x = t.x + tankSizeb.y = t.y + tankSize/2 - bulletSize/2b.dx = 5.0case Down:b.x = t.x + tankSize/2 - bulletSize/2b.y = t.y + tankSizeb.dy = 5.0case Left:b.x = t.xb.y = t.y + tankSize/2 - bulletSize/2b.dx = -5.0}g.bullets = append(g.bullets, b)
}// 创建爆炸效果
func (g *Game) createExplosion(x, y float64) {g.explosions = append(g.explosions, &Explosion{x:         x,y:         y,radius:    2,maxRadius: 20,active:    true,})
}// 检查碰撞
func checkCollision(x1, y1, w1, h1, x2, y2, w2, h2 float64) bool {return x1 < x2+w2 &&x1+w1 > x2 &&y1 < y2+h2 &&y1+h1 > y2
}// 更新游戏状态
func (g *Game) Update() error {if g.gameOver {// 游戏结束后按R键重新开始if inpututil.IsKeyJustPressed(ebiten.KeyR) {*g = *NewGame()}return nil}// 玩家控制g.handlePlayerInput()// 更新玩家位置g.updateTankPosition(g.player)// 敌人AI和更新g.updateEnemies()// 更新子弹g.updateBullets()// 更新爆炸效果g.updateExplosions()// 检查是否所有敌人都被消灭if len(g.enemies) == 0 {g.spawnEnemies(5) // 生成新一轮敌人}return nil
}// 处理玩家输入
func (g *Game) handlePlayerInput() {// 重置移动方向g.player.dx = 0g.player.dy = 0// 方向控制if ebiten.IsKeyPressed(ebiten.KeyUp) || ebiten.IsKeyPressed(ebiten.KeyW) {g.player.direction = Upg.player.dy = -g.player.speed} else if ebiten.IsKeyPressed(ebiten.KeyRight) || ebiten.IsKeyPressed(ebiten.KeyD) {g.player.direction = Rightg.player.dx = g.player.speed} else if ebiten.IsKeyPressed(ebiten.KeyDown) || ebiten.IsKeyPressed(ebiten.KeyS) {g.player.direction = Downg.player.dy = g.player.speed} else if ebiten.IsKeyPressed(ebiten.KeyLeft) || ebiten.IsKeyPressed(ebiten.KeyA) {g.player.direction = Leftg.player.dx = -g.player.speed}// 射击控制if ebiten.IsKeyPressed(ebiten.KeySpace) {g.shoot(g.player)}
}// 更新坦克位置(包括碰撞检测)
func (g *Game) updateTankPosition(t *Tank) {// 保存当前位置用于碰撞检测失败时恢复oldX, oldY := t.x, t.y// 移动坦克t.x += t.dxt.y += t.dy// 边界碰撞检测if t.x < 0 {t.x = 0} else if t.x+tankSize > screenWidth {t.x = screenWidth - tankSize}if t.y < 0 {t.y = 0} else if t.y+tankSize > screenHeight {t.y = screenHeight - tankSize}// 墙壁碰撞检测collided := falsefor _, w := range g.walls {if checkCollision(t.x, t.y, tankSize, tankSize, w.x, w.y, wallSize, wallSize) {collided = truebreak}}// 坦克之间的碰撞检测for _, e := range g.enemies {if t != e && checkCollision(t.x, t.y, tankSize, tankSize, e.x, e.y, tankSize, tankSize) {collided = truebreak}}// 如果发生碰撞,恢复到原来的位置if collided {t.x, t.y = oldX, oldY}
}// 更新敌人
func (g *Game) updateEnemies() {// 随机改变敌人方向和射击for _, e := range g.enemies {// 随机改变方向if rand.Float64() < 0.01 { // 1%概率改变方向e.direction = rand.Intn(4)switch e.direction {case Up:e.dx, e.dy = 0, -e.speedcase Right:e.dx, e.dy = e.speed, 0case Down:e.dx, e.dy = 0, e.speedcase Left:e.dx, e.dy = -e.speed, 0}}// 随机射击if rand.Float64() < 0.002 { // 0.2%概率射击g.shoot(e)}// 更新敌人位置g.updateTankPosition(e)}
}// 更新子弹
func (g *Game) updateBullets() {// 过滤掉不活跃的子弹activeBullets := make([]*Bullet, 0, len(g.bullets))for _, b := range g.bullets {if !b.active {continue}// 移动子弹b.x += b.dxb.y += b.dy// 检查是否超出屏幕if b.x < 0 || b.x > screenWidth || b.y < 0 || b.y > screenHeight {b.active = falsecontinue}// 检查是否击中墙壁wallHit := falsefor i, w := range g.walls {if w.destructible && checkCollision(b.x, b.y, bulletSize, bulletSize, w.x, w.y, wallSize, wallSize) {// 创建爆炸效果g.createExplosion(w.x+wallSize/2, w.y+wallSize/2)// 移除被击中的墙g.walls = append(g.walls[:i], g.walls[i+1:]...)b.active = falsewallHit = truebreak} else if !w.destructible && checkCollision(b.x, b.y, bulletSize, bulletSize, w.x, w.y, wallSize, wallSize) {// 击中不可破坏的墙g.createExplosion(b.x, b.y)b.active = falsewallHit = truebreak}}if wallHit {continue}// 检查是否击中坦克if b.owner.isPlayer {// 玩家子弹击中敌人for i, e := range g.enemies {if checkCollision(b.x, b.y, bulletSize, bulletSize, e.x, e.y, tankSize, tankSize) {g.createExplosion(e.x+tankSize/2, e.y+tankSize/2)b.active = false// 移除被击中的敌人g.enemies = append(g.enemies[:i], g.enemies[i+1:]...)g.score += 100 // 加分break}}} else {// 敌人子弹击中玩家if checkCollision(b.x, b.y, bulletSize, bulletSize, g.player.x, g.player.y, tankSize, tankSize) {g.createExplosion(g.player.x+tankSize/2, g.player.y+tankSize/2)b.active = falseg.gameOver = true // 游戏结束break}}if b.active {activeBullets = append(activeBullets, b)}}g.bullets = activeBullets
}// 更新爆炸效果
func (g *Game) updateExplosions() {activeExplosions := make([]*Explosion, 0, len(g.explosions))for _, e := range g.explosions {if !e.active {continue}// 增大爆炸半径e.radius += 1.5// 如果爆炸达到最大半径,标记为不活跃if e.radius >= e.maxRadius {e.active = false} else {activeExplosions = append(activeExplosions, e)}}g.explosions = activeExplosions
}// 绘制游戏
func (g *Game) Draw(screen *ebiten.Image) {// 填充背景色screen.Fill(color.RGBA{30, 30, 30, 255})// 绘制墙壁for _, w := range g.walls {var wallColor color.RGBA // 注意这里的正确语法if w.destructible {wallColor = color.RGBA{0, 128, 0, 255} // 绿色可破坏墙} else {wallColor = color.RGBA{128, 0, 0, 255} // 红色不可破坏墙}ebitenutil.DrawRect(screen, w.x, w.y, wallSize, wallSize, wallColor)}// 绘制玩家坦克ebitenutil.DrawRect(screen, g.player.x, g.player.y, tankSize, tankSize, color.RGBA{0, 0, 255, 255}) // 蓝色玩家坦克// 绘制坦克炮管g.drawCannon(screen, g.player)// 绘制敌人坦克for _, e := range g.enemies {ebitenutil.DrawRect(screen, e.x, e.y, tankSize, tankSize, color.RGBA{255, 0, 0, 255}) // 红色敌人坦克g.drawCannon(screen, e)}// 绘制子弹for _, b := range g.bullets {ebitenutil.DrawRect(screen, b.x, b.y, bulletSize, bulletSize, color.White)}// 绘制爆炸效果for _, e := range g.explosions {// 绘制渐变爆炸效果for r := 1.0; r <= e.radius; r += 2 {alpha := uint8(255 * (1 - r/e.maxRadius))c := color.RGBA{255, 165, 0, alpha} // 橙色爆炸ebitenutil.DrawCircle(screen, e.x, e.y, r, c)}}// 绘制分数ebitenutil.DebugPrint(screen, "Score: "+strconv.Itoa(g.score))// 如果游戏结束,显示游戏结束信息if g.gameOver {ebitenutil.DebugPrintAt(screen, "GAME OVER", screenWidth/2-50, screenHeight/2)ebitenutil.DebugPrintAt(screen, "Press R to restart", screenWidth/2-80, screenHeight/2+20)}
}// 绘制坦克炮管
func (g *Game) drawCannon(screen *ebiten.Image, t *Tank) {// 炮管颜色cannonColor := color.RGBA{200, 200, 200, 255}// 炮管位置和大小var cx, cy, cw, ch float64switch t.direction {case Up:cx = t.x + tankSize/2 - 2cy = t.y - 8cw = 4ch = 12case Right:cx = t.x + tankSize - 4cy = t.y + tankSize/2 - 2cw = 12ch = 4case Down:cx = t.x + tankSize/2 - 2cy = t.y + tankSize - 4cw = 4ch = 12case Left:cx = t.x - 8cy = t.y + tankSize/2 - 2cw = 12ch = 4}ebitenutil.DrawRect(screen, cx, cy, cw, ch, cannonColor)
}// 布局设置
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {return screenWidth, screenHeight
}func main() {ebiten.SetWindowSize(screenWidth, screenHeight)ebiten.SetWindowTitle("坦克大战")// 初始化随机数生成器rand.Seed(time.Now().UnixNano())game := NewGame()if err := ebiten.RunGame(game); err != nil {panic(err)}
}

http://www.xdnf.cn/news/17170.html

相关文章:

  • mysql 索引失效分析
  • 【数据结构】二叉树练习
  • 从BaseMapper到LambdaWrapper:MyBatis-Plus的封神之路
  • 【Unity3D实例-功能-镜头】第三人称视觉-镜头优化
  • Oracle 12c + Pl/Sql windows系统下表空间创建、迁移,dmp备份导入,数据库字符集更改
  • Oracle exp imp expdp impdp 命令详解
  • 如何快速开发符合Matter标准的智能家居设备?
  • 一个程序通过 HTTP 协议调用天气 API,解析 JSON 格式的天气数据,提取关键信息并格式化输出:日期、天气状况、温度范围、风向、湿度等核心气象数据。
  • 锡膏种类多,不同的锡膏有什么区别,该如何正确选择?
  • JAVA第六学:数组的使用
  • k8s中pod如何调度?
  • 读取了错误数据导致STM32 单片机Hard Fault
  • [特殊字符] 2025年生成式大模型部署与推理优化全景解析
  • WebSocket 在多线程环境下处理 Session并发
  • 《Day3-PyTorch 自动微分入门:从计算图到梯度下降的实践指南》
  • Tiger任务管理系统-10
  • 基于Spring Cloud Stream与Kafka的事件驱动微服务架构设计与实战指南
  • Dify 从入门到精通(第 20/100 篇):Dify 的自动化测试与 CI/CD
  • 【Kafka系列】第二篇| Kafka 的核心概念、架构设计、底层原理
  • 关于vue2中对接海康摄像头以及直播流rtsp或rtmp,后台ffmpeg转码后通过ws实现
  • 企业家 IP 发展态势剖析|创客匠人
  • Kong vs. NGINX:从反向代理到云原生网关的全景对比
  • Linux第一阶段练习
  • 一篇文章入门TCP与UDP(保姆级别)
  • 栅栏密码的加密解密原理
  • 动手学深度学习13.11. 全卷积网络 -笔记练习(PyTorch)
  • Modbus转Profinet网关与西门子PLC的互联配置案例:用于永宏品牌变频器的控制实现
  • 数据标注之数据集的类型与如何标注
  • 【数据结构——并查集】
  • Renesas Electronics RZ/V2N 评估套件