Spacewar! 23 - Missiles
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).