Notice: This is a mirrored post from
/lua.
Today's task was similar to yesterday - making things shoot at other things then make those things disappear. So all we do is recycle the same bits of code that make those things happen to
enemies.lua
. However if I just left the similar bits of code in as it is, the bullets that the enemies shoot are technically the same ones that the hero ship is shooting which in turn, will remove the enemies themselves. What we need to do next is make different sets of bullets for different sets of things; in this case - the enemies.
For
bullet.setup
:
it.flava=opt.flava or "ship"
I've added a new variable
flava
and set its state to whatever I set it to later when it's called otherwise, it defaults back to the ship. This will make it easier to assign bullets to different items later on; ie. end of level boss, different types of enemies, more hero ships and so forth. Also, since I will be referencing the ship functions here, I will add
local ship=oven.rebake(oven.modgame..".ship")
to
M.bake=function
.
For
bullet.update
:
if it.flava=="ship" then
for i,v in ipairs(enemies.tab) do
local dx=it.px-v.px
local dy=it.py-v.py
if dx*dx+dy*dy<=40*40 then
bullets.remove(it)
enemies.remove(v)
enemies.add({px=math.random(0,512), py=-math.random(0,512)})
sscores.add(23)
return
end
end
elseif it.flava=="enemy" then
local dx=it.px-ship.px
local dy=it.py-ship.py
if dx*dx+dy*dy<=40*40 then
bullets.remove(it)
ship.die()
return
end
end
I shove the previous code for the bullets into an if statement to check if the ship was shooting and if it was the enemy instead, a different set of instructions will be carried out; namely to remove the bullets and retire the hero. I've also changed the distance of collision for the
"enemy"
where it is now the difference between the bullet and the ship instead. The other thing I had to change was omit the second
enemies.add
line that was previously there as not only did it overload the program with assets, the game got ridiculously hard.
At this point, the next thing to do is make sure the bullets that the enemies shoot look different than the hero ship ones. Of course, you can always keep them the same colour if you wanted.
For
bullet.draw
:
if it.flava=="ship" then
gl.Color(1,0,1/2,1)
elseif it.flava=="enemy" then
gl.Color(math.random(),math.random(),math.random(),1)
end
The code is pretty obvious - I assign the different profiles accordingly.
Now we head on to
ship.lua
and add a new variable
ship.state="alive"
to the
ship.setup
function which brings us to the next bit - adding the new
ship.die
function.
ship.die=function()
if ship.state=="dead" then return end
ship.state="dead"
ship.dead=0
end
Here we tell the program to check that when the ship is "dead", it should just stay where it is and not do anything. At this point, I have also defined the state of the ship at this point to be, well, "dead" and that the value of it is
0
. Or rather, this
ship.die
function to mean as such. See, it's at moments like these that the whole programming logic just seem gobbledygook to me. Anyway, the point of this is to make a "paused" state after the hero ship is destroyed so that I can put in some sort "game over" screen instead of going straight to the main menu.
For
ship.update
:
if ship.state=="dead" then
ship.dead=ship.dead+1
ship.rz=ship.dead
if ship.dead>240 then
main.next=oven.rebake(oven.modgame..".main_menu")
end
return
end
I place this at the top most part of the code as it is a priority check. Here I tell the program to wait 4 seconds before going to the main menu screen, where it counts up a frame
(ship.dead+1)
until it reaches 241 frames (240 being 240/60 seconds). As an added bonus, I also tell the program to rotate the ship during this waiting period.
At this point, the next thing to do is to assign the right bullets to the right item.
if ship.fire then
if ship.cool<=0 then
bullets.add{px=ship.px,py=ship.py-32,vy=-8+math.random(),flava="ship"}
ship.cool=16
end
end
for i,v in ipairs(enemies.tab) do
local dx=ship.px-v.px
local dy=ship.py-v.py
if dx*dx+dy*dy<=32*32 then
ship.die()
return
end
end
Again, pretty obvious what the code does. All I did was add in the
flava
variable and set its state to
ship
to the end of that table. I've also edited the next bit of code to call the
ship.die
function when the game is over instead of going to the main menu screen.
Finally, we head over to the
enemies.lua
file and add
local bullets=oven.rebake(oven.modgame..".bullets")
to
M.bake=function
since we will be referring to the bullets function.
Right, so I add a cooldown variable to the
enemy.setup
function but set it to random like so -
it.cool=math.random(100,200)
. This is more like a timer so the enemies don't shoot straightaway.
For
enemy.update
:
it.cool=it.cool-1
if it.cool<=0 then
local dx=ship.px-it.px
local dy=ship.py-it.py
local dd=dx*dx+dy*dy
local d=math.sqrt(dd)
if d==0 then d=1 end
bullets.add{px=it.px,py=it.py+32,vy=8*((dy/d)+math.random()-0.5),vx=8*((dx/d)+math.random()-0.5),flava="enemy"}
it.cool=math.random(100,200)
end
Here is where that similar bit of code we did yesterday comes back in but instead of the enemy ship firing only when a spacebar is held down, the enemies just start firing away on their own accord. Well, not technically since I've explicitly told the program to randomly assign the shooting to happen during a time period between 100 to 200 frames.
Velocity is a unit vector; a magnitude with direction whose length is
1
. As we are working in 2 dimensional space, there are just two parts to the vector -
8
and
(dy/d,dx/d)
, speed and direction respectively. Since I wanted the enemies to shoot at the direction of the ship, wherever it may be at the time, I've enlisted the help of Pythogoras to figure out the direction and distance between the ship from the enemies to direct the enemy bullets at the hero with
8
frames being the speed of the bullet.
At first I made it so the enemies literally tracked me with bullets but this really made the game too hardcore for me. Instead, I have "soften" the blow with a little randomness.
Probably the most vital bit for this code and the whole game to not break is
if d==0 then d=1 end
because we all know what happens if you divide by zero.
Before the end, I edited the
enemies.setup
function like so:
local sx=85
local sy=64
local sya=-128
enemies.tab = {}
enemies.add({px=0*sx, py=sya+1*sy})
enemies.add({px=1*sx, py=sya+2*sy})
enemies.add({px=2*sx, py=sya+3*sy})
enemies.add({px=3*sx, py=sya+4*sy})
enemies.add({px=4*sx, py=sya+3*sy})
enemies.add({px=5*sx, py=sya+2*sy})
enemies.add({px=6*sx, py=sya+1*sy})
I added the variable
sya
and adjusted the vertical positions of all the enemies since they were far too low down on the screen at startup.
The code lives
here if you want a poke.