Missiles - a spike

I’ve got a few minutes here. I think I’ll see about firing some missiles. Planning:

Well, they move and draw. So they’ll register Drawn. They can’t be touched, they are too hot. Presumably they are fired out the front of the ship, at slightly greater than the ship’s velocity. They last a while, then burn out. They probably don’t fire continuously at full speed, though that might be interesting to watch. Probably you have a finite supply of missiles.

So I think I’ll just do something like if the fire button is pressed, add a missile to the Drawn list. A missile will be an instance of a class called … Missile. Here goes:

The best place I can see in Ship to do this is in move, which is where all the other buttons are checked. So …

function Ship:move()
    local turn
    local thrust
    
    if     self.controls.left.pressed  and not self.controls.right.pressed then turn =  self.TurnAmount
    elseif self.controls.right.pressed and not self.controls.left.pressed  then turn = -self.TurnAmount
    else turn = 0 end
    self.heading = self.heading + turn
    
    if self.controls.thrust.pressed then thrust = self.ThrustAmount else thrust = vec2(0,0) end
    self.vel = self:adjustedVelocity(self.vel, self.heading, thrust)
    self.pos = clip_to_screen(self.pos + self.vel)
    
    if self.controls.fire.pressed then self:fireMissile() end
end

Done! Well, some details need to be worked out in fireMissile.

function Ship:fireMissile() {
    Missile(self)
end

I figure the Missile will create itself, as that seems to be our style, and that it’ll refer to the ship for initial conditions. That may not work but we’ll try it.

Missile = class()

function Missile:init(ship)
    self.pos = ship.pos
    addDrawnAtTop(self)
end

function Missile:move()
    -- nothing yet
end

function Missile:draw()
    pushMatrix()
    pushStyle()
    strokeWidth(1)
    stroke(255,0,0,255)
    fill(255,0,0,255)
    translate(self.pos)
    ellipse(0,0,3)
    popStyle()
    popMatrix()
end

I expect that this will drop a red dot when you press the fire button. However, in fact it does nothing. Hmm … addition of a quick print statement tells me that the missile is being created and drawn. It must be invisible. Something about my ellipse? A closer look at the screen shows me a red dot at zero. Is self.pos broken? No, it has a non-zero pos.

I’m really glad I started easy, because I’m not sure why it’s going to zero. Ah, there’s your problem right there. The translate function doesn’t take a vector, it takes x and y. ‘translate(self.pos.x, self.pos.y)` does the job. Now the ship lays red eggs across the screen as it drifts. And if you hold the button down, a lot of them:

In the picture above, the top ship is drifting left and dropping missiles behind. They’re tiny red dots: you may find them hard to see.

We note that the missile doesn’t move, as planned, and that it emanates from the center of the ship, where we put it. But we might want it to come out the front. Nonetheless, if we wanted space mines instead of missiles, this would be pretty close. I think I’ll drop a lot just to see whether the drawing bogs down.

Response looks pretty good even with a ridiculous number of static missiles on the screen. Of course they’ll be more burdensome when we make them move.

function Missile:init(ship)
    self.pos = ship.pos
    self.vel = ship.vel
    addDrawnAtTop(self)
end

function Missile:move()
    self.pos = clip_to_screen(self.pos + self.vel)
end

I copy the ship’s velocity, duplicate the move logic from ship. (And note the really odd name of the clip-to-value function, and that it is global. Need a FlyingObject superclass? We’ll see.

Now when we fly about, the ship lays eggs that follow it. If it accelerates, it gets a string of them behind it:

This makes perfect sense so far. It remains to give the missile a little extra velocity out the front of the ship at the time of firing, and of course to limit the rate of fire and other refinements. Let’s see about that starting velocity. The ship’s MaxSpeed is 3. Probably missiles can go faster than that. I’ll guess 5, and I’ll guess that they want to go faster by 1 than the ship at the time of firing. So we’ll adjust their velocity at creation by the vector (1,0) in the direction of the ship’s rotation.

function Missile:init(ship)
    self.pos = ship.pos
    local velocity = vec2(1,0):rotate(ship.heading)
    self.vel = ship.vel + velocity
    addDrawnAtTop(self)
end

The missiles go off at an angle! This may be a degrees / radians thing. Sure enough, drawing rotate is in degrees, but vector rotate is in radians. We’ll have a quick conversion, please: local velocity = vec2(1,0):rotate(math.rad(ship.heading)). The bullet obligingly shoots directly out of the right side of the ship. Ah. Ship is Y-forward, not X-forward. So:

function Missile:init(ship)
    self.pos = ship.pos
    local velocity = vec2(0,1):rotate(math.rad(ship.heading))
    self.vel = ship.vel + velocity
    addDrawnAtTop(self)
end

And missiles now shoot obligingly out of the front of the ship (still starting from the middle).