上一篇文章还留了个尾巴,细心的同学应该发现了:飞船可以移动出屏幕!!!现在我们就来限制一下飞船的移动范围。我们规定飞船可以左右超过半个身位,如下图所示:
很容易计算得出,左边位置的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
- }
- }
- }
运行结果如下:
我们不用另外准备子弹的图片,直接画一个矩形就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)
- }