Difference between revisions of "2D Space Shooter Part 6: Enemies"
m (Text replacement - "</source" to "</syntaxhighlight") |
|||
Line 1: | Line 1: | ||
− | This won't be a space shooter without enemies to shoot. Let's define our | + | __TOC__ |
− | + | This won't be a space shooter without enemies to shoot at. Let's define our enemies. | |
− | Create a new file named 'enemy.lua'. This new file will depend on the Ship | + | 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 but this time we will use Gideros code dependency by code instead of Gideros project setting (you can do it to the player.lua file as well). | ||
== Enemy class == | == Enemy class == | ||
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. | ||
− | |||
<syntaxhighlight lang="lua"> | <syntaxhighlight lang="lua"> | ||
+ | --!NEEDS:ships.lua | ||
EnemyShip=Core.class(Ship) | EnemyShip=Core.class(Ship) | ||
Line 33: | Line 35: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | == Level | + | == 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. | 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 | + | I decided to use a plain text representation: |
+ | *enemies will be placed in up to five positions on each row, possibly spanning multiple rows | ||
+ | *each line of our level definition will be six character long: one character for each 6 possible position, indicating if a ship will be spawn at that location, 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 | + | In a new file called ''''level.lua'''', we define our level manager, starting by our first (and only in this tutorial) level. |
<syntaxhighlight lang="lua"> | <syntaxhighlight lang="lua"> | ||
local LEVEL1=[[ | local LEVEL1=[[ | ||
Line 56: | Line 61: | ||
.....! | .....! | ||
]] | ]] | ||
+ | |||
local LEVELS={ LEVEL1 } | local LEVELS={ LEVEL1 } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | So this is our level data. We can almost see how ships will be shown on screen. | + | So this is our 1st level data. We can almost see how ships will be shown on screen. |
− | Now our loading (init) and sequencing (tick) code: | + | Now, below LEVELS, we add our loading (init) and sequencing (tick) code: |
<syntaxhighlight lang="lua"> | <syntaxhighlight lang="lua"> | ||
local SCROLL_DELTA=0.02 | local SCROLL_DELTA=0.02 | ||
Line 87: | Line 93: | ||
function Level:tick(delay) | function Level:tick(delay) | ||
− | if self.scroll==0 then | + | if self.scroll==0 then |
--Not started scrolling: build an enemy row | --Not started scrolling: build an enemy row | ||
local lineinfo=self.levelinfo[self.levelline] | local lineinfo=self.levelinfo[self.levelline] | ||
Line 137: | Line 143: | ||
== Kicking off our level == | == Kicking off our level == | ||
− | It is time to see what we just did | + | It is time to see what we just did by adding a single line of code at the very bottom of the "main.lua" file, let's do it: |
<syntaxhighlight lang="lua"> | <syntaxhighlight lang="lua"> | ||
Level.new(1) | Level.new(1) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | |||
+ | And the result: | ||
+ | {{#widget:GApp|app=SpaceShooter_EN1V2.GApp|width=320|height=480|plugins=bump}} | ||
− | + | '''Prev.: [[2D Space Shooter Part 5: Firing]]'''</br> | |
+ | '''Next: [[2D Space Shooter Part 7: Boosters]]''' | ||
− | + | {{GIDEROS IMPORTANT LINKS}} |
Revision as of 21:46, 13 November 2023
This won't be a space shooter without enemies to shoot at. 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 but this time we will use Gideros code dependency by code instead of Gideros project setting (you can do it to the player.lua file as well).
Enemy class
Enemies will come from the top of the screen, so ships will head toward the bottom.
--!NEEDS:ships.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
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
- each line of our level definition will be six character long: one character for each 6 possible position, indicating if a ship will be spawn at that location, 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 define our level manager, starting by our first (and only in this tutorial) level.
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 }
So this is our 1st level data. We can almost see how ships will be shown on screen.
Now, below LEVELS, we add our loading (init) and sequencing (tick) code:
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
Kicking off our level
It is time to see what we just did by adding a single line of code at the very bottom of the "main.lua" file, let's do it:
Level.new(1)
And the result:
Prev.: 2D Space Shooter Part 5: Firing
Next: 2D Space Shooter Part 7: Boosters