一起用Go做一个小游戏(中)

本文继续介绍使用Go语言开发小游戏的过程,包括限制飞船活动范围、发射子弹、处理碰撞检测、增加主界面和结束界面,以及判断游戏胜负。通过这篇文章,读者将了解Go语言在游戏开发中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

限制飞船的活动范围

上一篇文章还留了个尾巴,细心的同学应该发现了:飞船可以移动出屏幕!!!现在我们就来限制一下飞船的移动范围。我们规定飞船可以左右超过半个身位,如下图所示:

b123297befac40c6f9efa84b2cff7d2c.png

很容易计算得出,左边位置的x坐标为:

x = -W2/2

右边位置的坐标为:

x = W1 - W2/2

修改input.go的代码如下:

func (i *Input) Update(ship *Ship, cfg *Config) {
  if ebiten.IsKeyPressed(ebiten.KeyLeft) {
    ship.x -= cfg.ShipSpeedFactor
    if ship.x < -float64(ship.width)/2 {
      ship.x = -float64(ship.width) / 2
    }
  } else if ebiten.IsKeyPressed(ebiten.KeyRight) {
    ship.x += cfg.ShipSpeedFactor
    if ship.x > float64(cfg.ScreenWidth)-float64(ship.width)/2 {
      ship.x = float64(cfg.ScreenWidth) - float64(ship.width)/2
    }
  }
}

运行结果如下:

24643b354fe73ad77fc5aaeec43341f0.gif

发射子弹

我们不用另外准备子弹的图片,直接画一个矩形就ok。为了可以灵活控制,我们将子弹的宽、高、颜色以及速率都用配置文件来控制:

{
  "bulletWidth": 3,
  "bulletHeight": 15,
  "bulletSpeedFactor": 2,
  "bulletColor": {
    "r": 80,
    "g": 80,
    "b": 80,
    "a": 255
  }
}

新增一个文件bullet.go,定义子弹的结构类型和New方法:

type Bullet struct {
  image       *ebiten.Image
  width       int
  height      int
  x           float64
  y           float64
  speedFactor float64
}

func NewBullet(cfg *Config, ship *Ship) *Bullet {
  rect := image.Rect(0, 0, cfg.BulletWidth, cfg.BulletHeight)
  img := ebiten.NewImageWithOptions(rect, nil)
  img.Fill(cfg.BulletColor)

  return &Bullet{
    image:       img,
    width:       cfg.BulletWidth,
    height:      cfg.BulletHeight,
    x:           ship.x + float64(ship.width-cfg.BulletWidth)/2,
    y:           float64(cfg.ScreenHeight - ship.height - cfg.BulletHeight),
    speedFactor: cfg.BulletSpeedFactor,
  }
}

首先根据配置的宽高创建一个rect对象,然后调用ebiten.NewImageWithOptions创建一个*ebiten.Image对象。

子弹都是从飞船头部发出的,所以它的横坐标等于飞船中心的横坐标,左上角的纵坐标=屏幕高度-飞船高-子弹高。

随便增加子弹的绘制方法:

func (bullet *Bullet) Draw(screen *ebiten.Image) {
  op := &ebiten.DrawImageOptions{}
  op.GeoM.Translate(bullet.x, bullet.y)
  screen.DrawImage(bullet.image, op)
}

我们在Game对象中增加一个map来管理子弹:

type Game struct {
  // -------省略-------
  bullets map[*Bullet]struct{}
}

func NewGame() *Game {
  return &Game{
    // -------省略-------
    bullets: make(map[*Bullet]struct{}),
  }
}

然后在Draw方法中,我们需要将子弹也绘制出来:

func (g *Game) Draw(screen *ebiten.Image) {
  screen.Fill(g.cfg.BgColor)
  g.ship.Draw(screen)
  for bullet := range g.bullets {
    bullet.Draw(screen)
  }
}

子弹位置如何更新呢?在Game.Update中更新,与飞船类似,只是飞船只能水平移动,而子弹只能垂直移动。

func (g *Game) Update() error {
  for bullet := range g.bullets {
    bullet.y -= bullet.speedFactor
  }
  // -------省略-------
}

子弹的更新、绘制逻辑都完成了,可是我们还没有子弹!现在我们就来实现按空格发射子弹的功能。我们需要在Input.Update方法中判断空格键是否按下,由于该方法需要访问Game对象的多个字段,干脆传入Game对象:

func (i *Input) Update(g *Game) {
  if ebiten.IsKeyPressed(ebiten.KeyLeft) {
    // -------省略-------
  } else if ebiten.IsKeyPressed(ebiten.KeyRight) {
    // -------省略-------
  } else if ebiten.IsKeyPressed(ebiten.KeySpace) {
    bullet := NewBullet(g.cfg, g.ship)
    g.addBullet(bullet)
  }
}

给Game对象增加一个addBullet方法:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值