Spacewar! 32 - Plan Then Do.
Plan Then Do
We’ve refactored to have a Universe. Tozier is with me today. We need to figure out what to do, and then do it.
Notes
- Missiles might time out, not distance out.
- Should ships be immune to their own missiles? I think not.
- Make things explode, at least Ships. (Better explosion)
More notes
- rotation too slow
- Missile capacity
- Acceleration too slow?
- Max speed?
- Buttons
- Hard to see
- Hard to keep finger on
- Bad position
- Put in corners
- hyperspace
- BUG: Firing too fast kills missiles
- If you press fire too fast should it flash yellow or something? (Or be yellow when can’t fire?)
- sun / gravity
- star field
- Ships bigger? Improve design? More like classic? More like sprites?
- Limit game flight space to stay off keys
- Hit count / damage amount
- settings console
- Information display (bullets left, score, …)
- BUG: For loop exploded with error from
next
(fixed, one thinks) - Asteroids (frogger, etc)
- Game recycle
- Player ID
We’re thinking about beefing up the explosion. The canonical explosion was just a bunch of sparkles. We have something more spectacular in mind. The ship should explode into fragments that fly off. Tozier just suggested that the fragments should be able to do damage to the remaining ship: sort of your death curse.
Some issues come to mind. Should the explosion have an intrinsic velocity related to the ship’s velocity at the time it explodes, or should it be as if the ship stopped and then exploded? I’m inclined to the former because it will be more interesting to do and to watch. And should the explosion be one thing or a bunch of separate fragments? If they’re going to do damage, I’m inclined to make them separate fragments.
We’ll give it a go. We have an explosion class:
Explosion = class()
function Explosion:init(ship)
U:addObject(self)
self.name = "blammo"
self.timeOut = 100
self.pos = ship.pos
self.cannotCollide = true
end
function Explosion:draw()
self.timeOut = self.timeOut - 1
if self.timeOut <= 0 then self:die() return end
pushMatrix()
pushStyle()
translate(self.pos:unpack())
text("Blammo!")
popStyle()
popMatrix()
end
function Explosion:die()
U:kill(self)
end
function Explosion:move()
end
I’m thinking that the Explosion is made up of fragments, which we’ll generate in our Explosion class and set them free. Something like this:
function Explosion:init(ship)
for i = 1, 5 do
Fragment(i, ship)
end
U:addObject(self)
self.name = "blammo"
self.timeOut = 100
self.pos = ship.pos
self.cannotCollide = true
end
We had a little debate about leaving the existing Explosion object or removing all its behavior. I was leaning toward the latter, just coding my new intentions. Tozier points out that if we leave the “blammo” we can better see what’s going on. In that spirit, let’s make a null Fragment class:
Fragment = class()
function Fragment:init(fragmentNumber, ship)
end
function Fragment:draw()
end
The program still works, since the Fragments, like the Goggles, do nothing. To make the fragments draw themselves, we’ll need a draw, and we’ll need to register them with Universe. And we’d better give them a timeout soon. And a move ability. And …
Tozier thinks of an interesting step. What if the next explosion is just a bunch of missiles flying away. (Or, he says, all your remaining missiles!)
Fragment = class()
function Fragment:init(fragmentNumber, ship)
Missile(ship, self:randomVelocityAddition(), self:randomStartPosition())
end
function Fragment:draw()
end
function Fragment:randomVelocityAddition()
local x = math.random()*2 - 1
local y = math.random()*2 - 1
return vec2(x,y)
end
function Fragment:randomStartPosition()
local basis = vec2(0,50)
local angle = math.random()*math.pi*2
return basis:rotate(angle)
end
This looks a bit odd because the random velocity can be inward:
INSERT MOVIE
This also makes the missiles prone to kill each other as they converge on the ship. We need to condition the position and velocity together, based on the random rotation and the ship’s rotation. (This is screwing around a bit ore than we should, since we’re not really going to use Missile to do this, but we’re learning some stuff. The code now:
Fragment = class()
function Fragment:init(fragmentNumber, ship)
local angle = math.random()*math.pi*2
Missile(ship, self:randomVelocityAddition(angle), self:randomStartPosition(ship,angle))
end
function Fragment:draw()
end
function Fragment:randomVelocityAddition(angle)
return vec2(0,1):rotate(angle)
end
function Fragment:randomStartPosition(ship, angle)
local basis = vec2(0, 50)
return basis:rotate(angle + math.rad(ship.heading))
end
MOVIE HERE
OK, that was fun. We learned that setting fragments flying needs to consider ship rotation. The Missile applies the ship velocity, so the explosion tracks the killed ship’s motion. (Not shown, trust us.) We also noticed that the randomly placed missiles tend to kill each other. If Fragments are to be a Death Curse, they shouldn’t kill each other, just the other ship. So we’ve learned enough, we hope, to build a real Fragment.
I’m inclined to make the real Fragment start at ship position, with a random velocity that includes the ship’s velocity at the time of death. The Fragment can collide, but needs not to collide with other Fragments. (Note that the current collision logic sends die
to anyone in a collision, so until we do something about that, our new Fragments will be able to kill each other. We’ll deal with that as we move forward.
Tozier’s not watching, so I’ll code up a Fragment by intention:
Fragment = class()
function Fragment:init(fragmentNumber, ship)
self.cannotCollide = true
self.pos = ship.pos
local outwardVelocity = vec2(0,1):rotate(math.random()*math.pi*2)
self.vel = ship.vel + outwardVelocity
U:addObject(self)
end
function Fragment:move()
self.pos = U:clip_to_screen(self.pos + self.vel)
end
function Fragment:draw()
pushMatrix()
pushStyle()
translate(self.pos:unpack())
strokeWidth(1)
rectMode(CENTER)
rect(0,0,10,10)
popStyle()
popMatrix()
end
MOVIE HERE
Why are there so many, you might ask. Because our collision logic encounters the ship and the bullet that kills it, and then encounters the bullet and the ship it kills. It always happens twice. I’m sure we’ve written that down somewhere.
Tozier wants to give them a lifetime, but he wants to give them an angular velocity even more. I buy into that because surely when we draw the real fragments, they’ll spin.
Fragment = class()
function Fragment:init(fragmentNumber, ship)
self.cannotCollide = true
self.pos = ship.pos
local outwardVelocity = vec2(0,1):rotate(math.random()*math.pi*2)
self.vel = ship.vel + outwardVelocity
self.angularVelocity = math.random()*10-5
self.heading = 0
U:addObject(self)
end
function Fragment:move()
self.pos = U:clip_to_screen(self.pos + self.vel)
self.heading = self.heading + self.angularVelocity
end
function Fragment:draw()
pushMatrix()
pushStyle()
translate(self.pos:unpack())
rotate(self.heading)
self:drawFragment()
popStyle()
popMatrix()
end
function Fragment:drawFragment()
strokeWidth(2)
line(0,0,10,6)
line(0,0,0,20)
end
MOVIE HERE
OK, now all our fragments are little seven-shaped chunks that go spinning about. They spin around the point of the angle. We can fiddle with the look as need be. We’ll call it a day.
I’m thinking that starting with Missile was a waste of time, but I know the code well and could have coded the Fragment by intention. But Tozier does not know the code. It is my practice, sometimes, to take the course my pair recommends, especially if it’s more conservative and I’m going to be doing the coding. And we did gain some general learning from the Missile.
Before us now is dealing with collisions