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


    限制飞船的活动范围

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

    b123297befac40c6f9efa84b2cff7d2c.png

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

    x = -W2/2

    右边位置的坐标为:

    x = W1 - W2/2

    修改input.go的代码如下:

    1. func (i *Input) Update(ship *Ship, cfg *Config) {
    2.   if ebiten.IsKeyPressed(ebiten.KeyLeft) {
    3.     ship.x -= cfg.ShipSpeedFactor
    4.     if ship.x < -float64(ship.width)/2 {
    5.       ship.x = -float64(ship.width) / 2
    6.     }
    7.   } else if ebiten.IsKeyPressed(ebiten.KeyRight) {
    8.     ship.x += cfg.ShipSpeedFactor
    9.     if ship.x > float64(cfg.ScreenWidth)-float64(ship.width)/2 {
    10.       ship.x = float64(cfg.ScreenWidth) - float64(ship.width)/2
    11.     }
    12.   }
    13. }

    运行结果如下:

    24643b354fe73ad77fc5aaeec43341f0.gif

    发射子弹

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

    1. {
    2.   "bulletWidth"3,
    3.   "bulletHeight"15,
    4.   "bulletSpeedFactor"2,
    5.   "bulletColor": {
    6.     "r"80,
    7.     "g"80,
    8.     "b"80,
    9.     "a"255
    10.   }
    11. }

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

    1. type Bullet struct {
    2.   image       *ebiten.Image
    3.   width       int
    4.   height      int
    5.   x           float64
    6.   y           float64
    7.   speedFactor float64
    8. }
    9. func NewBullet(cfg *Config, ship *Ship) *Bullet {
    10.   rect := image.Rect(00, cfg.BulletWidth, cfg.BulletHeight)
    11.   img := ebiten.NewImageWithOptions(rect, nil)
    12.   img.Fill(cfg.BulletColor)
    13.   return &Bullet{
    14.     image:       img,
    15.     width:       cfg.BulletWidth,
    16.     height:      cfg.BulletHeight,
    17.     x:           ship.x + float64(ship.width-cfg.BulletWidth)/2,
    18.     y:           float64(cfg.ScreenHeight - ship.height - cfg.BulletHeight),
    19.     speedFactor: cfg.BulletSpeedFactor,
    20.   }
    21. }

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

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

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

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

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

    1. type Game struct {
    2.   // -------省略-------
    3.   bullets map[*Bullet]struct{}
    4. }
    5. func NewGame() *Game {
    6.   return &Game{
    7.     // -------省略-------
    8.     bullets: make(map[*Bullet]struct{}),
    9.   }
    10. }

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

    1. func (g *Game) Draw(screen *ebiten.Image) {
    2.   screen.Fill(g.cfg.BgColor)
    3.   g.ship.Draw(screen)
    4.   for bullet := range g.bullets {
    5.     bullet.Draw(screen)
    6.   }
    7. }

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

    1. func (g *Game) Update() error {
    2.   for bullet := range g.bullets {
    3.     bullet.y -= bullet.speedFactor
    4.   }
    5.   // -------省略-------
    6. }

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

    1. func (i *Input) Update(g *Game) {
    2.   if ebiten.IsKeyPressed(ebiten.KeyLeft) {
    3.     // -------省略-------
    4.   } else if ebiten.IsKeyPressed(ebiten.KeyRight) {
    5.     // -------省略-------
    6.   } else if ebiten.IsKeyPressed(ebiten.KeySpace) {
    7.     bullet := NewBullet(g.cfg, g.ship)
    8.     g.addBullet(bullet)
    9.   }
  • 相关阅读:
    JDBC封装查询单个和查询多个
    asp.net core之依赖注入
    数据结构与算法-(7)---栈的应用-(3)表达式转换
    ES6高级-迭代器与生成器的用法
    信号量的使用
    《向量数据库指南》——向量数据库与 ANN 算法库的区别
    Jmeter-Beanshell取样器中引入自制的java脚本(jar java class)
    BlockingQueue二
    folly::ConcurrentSkipList 详解
    2041. 面试中被录取的候选人
  • 原文地址:https://blog.csdn.net/darjun/article/details/128213155