Difference between revisions of "2D Space Shooter Part 6: Enemies"

From GiderosMobile
m (Text replacement - "<source" to "<syntaxhighlight")
Line 7: Line 7:
 
Enemies will come from the top of the screen, so ships will head toward the bottom.
 
Enemies will come from the top of the screen, so ships will head toward the bottom.
  
<source lang="lua">
+
<syntaxhighlight lang="lua">
 
EnemyShip=Core.class(Ship)
 
EnemyShip=Core.class(Ship)
  
Line 39: Line 39:
  
 
In a new file called 'level.lua' we'll define our level manager, starting by our first (and only in this tutorial) level.
 
In a new file called 'level.lua' we'll define our level manager, starting by our first (and only in this tutorial) level.
<source lang="lua">
+
<syntaxhighlight lang="lua">
 
local LEVEL1=[[
 
local LEVEL1=[[
 
..A..0
 
..A..0
Line 61: Line 61:
  
 
Now our loading (init) and sequencing (tick) code:
 
Now our loading (init) and sequencing (tick) code:
<source lang="lua">
+
<syntaxhighlight lang="lua">
 
local SCROLL_DELTA=0.02
 
local SCROLL_DELTA=0.02
  
Line 138: Line 138:
 
== Kicking off our level ==
 
== Kicking off our level ==
 
It is time to see what we just did: let's start our level from main.lua by adding a single line:
 
It is time to see what we just did: let's start our level from main.lua by adding a single line:
<source lang="lua">
+
<syntaxhighlight lang="lua">
 
Level.new(1)
 
Level.new(1)
 
</source>
 
</source>

Revision as of 17:54, 12 July 2023

This won't be a space shooter without enemies to shoot. Let's define our enemies. Again we will subclass our ship class for enemies.

Create a new file named 'enemy.lua'. This new file will depend on the Ship class too, so right click on the file name in gideros studio and access 'Code dependencies'. From there tell Gideros that enemy.lua will depend on ships.lua.

Enemy class

Enemies will come from the top of the screen, so ships will head toward the bottom.

<syntaxhighlight lang="lua"> EnemyShip=Core.class(Ship)

function EnemyShip:init(type,x,y) self.posx=x self.posy=y self:setRotation(180) self.fire=true ENEMIES:addChild(self) end

function EnemyShip:advance(amount) self.posy+=amount end

function EnemyShip:tick(delay) self:setPosition(self.posx,self.posy) Ship.tick(self,delay) end

function EnemyShip:explode() -- Here we could show some explosion animation and play a sound self:destroy() end </source>

Level management

Now we need to make the enemies appear in waves. This part is a bit trickier, as it involves choosing a format to design our levels.

I decided to use a plain text representation: enemies will be placed in up to five positions on each row, possibly spanning multiple rows. So each line of our level definition will be six character long: one character for each possible position, indicating if a ship you be spawn at the location, and which kind of ship, plus a sixth character to control the ship waves: the sixth character with tell how much time to wait before the next row of ship shows up, or whether the row is the last of a wave.

In a new file called 'level.lua' we'll define our level manager, starting by our first (and only in this tutorial) level. <syntaxhighlight lang="lua"> local LEVEL1=[[ ..A..0 .....! .A.A.0 .....! ..A..1 .A...1 ...A.0 .....! ..A..0 .B.B.0 .....! A...A0 ..C..0 .....! ]] local LEVELS={ LEVEL1 } </source> So this is our level data. We can almost see how ships will be shown on screen.

Now our loading (init) and sequencing (tick) code: <syntaxhighlight lang="lua"> local SCROLL_DELTA=0.02

Level=Core.class(Object)

function Level:init(number) -- Will hold our level info self.levelinfo={} -- Will hold our position in the level self.levelline=1 -- Hold all the enemy ships in the current wave self.enemies={} -- Hold the enemy line scroll amount self.scroll=0 -- Fill in level info for level_line in LEVELS[number]:gmatch("[%w%p]+") do assert(#level_line==6,"Level description line is not 6 characters long.") local wait=tonumber(level_line:sub(6)) or 0 local clear=level_line:sub(6)=="!" table.insert(self.levelinfo,{ enemies=level_line:sub(1,5), wait=wait, clear=clear}) end --Register as actor ACTORS[self]=true end

function Level:tick(delay) if self.scroll==0 then --Not started scrolling: build an enemy row local lineinfo=self.levelinfo[self.levelline] for i=1,5 do local type=lineinfo.enemies:sub(i,i) if type~="." then local enemy=EnemyShip.new(type,SHIP_SIZE*i+SCR_LEFT,SCR_TOP-SHIP_SIZE/2) self.enemies[enemy]=true enemy:advance(SCROLL_DELTA*SHIP_SIZE) end end self.scroll=SCROLL_DELTA elseif self.scroll<1 then self.scroll+=SCROLL_DELTA for k,_ in pairs(self.enemies) do if ACTORS[k] then k:advance(SCROLL_DELTA*SHIP_SIZE) end end else local lineinfo=self.levelinfo[self.levelline] if lineinfo then --If no lineinfo, it means that we reached the end of the level if lineinfo.wait>0 then lineinfo.wait-=SCROLL_DELTA else local enemy_count=0 if lineinfo.clear then for k,_ in pairs(self.enemies) do if ACTORS[k] then enemy_count+=1 end end end if enemy_count==0 then self.levelline+=1 if lineinfo.clear then self.enemies={} end if self.levelinfo[self.levelline] then self.scroll=0 else -- Level finished print("FINISHED") end end end end end end </source>

Kicking off our level

It is time to see what we just did: let's start our level from main.lua by adding a single line: <syntaxhighlight lang="lua"> Level.new(1) </source>


2D Space Shooter Part 7: Boosters