限制飞船的活动范围
上一篇文章还留了个尾巴,细心的同学应该发现了:飞船可以移动出屏幕!!!现在我们就来限制一下飞船的移动范围。我们规定飞船可以左右超过半个身位,如下图所示:
很容易计算得出,左边位置的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)
}
}
给Game对象增加一个addBullet
方法: