Tuto tiny-ecs 2d platformer Part 11 Systems 3
The Systems 3
A couple more systems to add and we are done. We also add the Debug Systems to help us visualize the collision boxes.
sCollectibles.lua
Let's start with the collectibles, you know those things you can pick up 😊.
Please create a file "sCollectibles.lua" in the "_S" folder and the code:
SCollectibles = Core.class()
function SCollectibles:init(xtiny, xbump, xplayer1) -- tiny function
self.tiny = xtiny -- make a class ref
self.tiny.processingSystem(self) -- called once on init and every update
self.bworld = xbump
self.player1 = xplayer1
-- sfx
self.snd = { sound=Sound.new("audio/sfx/sfx_coin_double1.wav"), time=0, delay=0.2, }
end
function SCollectibles:filter(ent) -- tiny function
return ent.iscollectible
end
function SCollectibles:onAdd(ent) -- tiny function
end
function SCollectibles:onRemove(ent) -- tiny function
self.bworld:remove(ent) -- remove collision box from cbump world here!
end
function SCollectibles:process(ent, dt) -- tiny function
if ent.isdirty then -- hit
local function map(v, minSrc, maxSrc, minDst, maxDst, clampValue)
local newV = (v - minSrc) / (maxSrc - minSrc) * (maxDst - minDst) + minDst
return not clampValue and newV or clamp(newV, minDst >< maxDst, minDst <> maxDst)
end
local snd = self.snd -- sfx
local curr = os.timer()
local prev = snd.time
if curr - prev > snd.delay then
local channel = snd.sound:play()
if channel then channel:setVolume(g_sfxvolume*0.01) end
snd.time = curr
end
if ent.eid == "coins" then -- coins
self.tiny.numberofcoins += 1
self.tiny.hudcoins:setText("COINS: "..self.tiny.numberofcoins)
elseif ent.eid == "lives" then -- hearts
self.player1.currhealth += 1
-- hud
local hudhealthwidth = map(self.player1.currhealth, 0, self.player1.totalhealth, 0, 100)
self.tiny.hudhealth:setWidth(hudhealthwidth)
if self.player1.currhealth < self.player1.totalhealth/3 then self.tiny.hudhealth:setColor(0xff0000)
elseif self.player1.currhealth < self.player1.totalhealth/2 then self.tiny.hudhealth:setColor(0xff5500)
else self.tiny.hudhealth:setColor(0x00ff00)
end
elseif ent.eid == "dash" then -- ???
self.player1.body.candash = true
end
self.tiny.tworld:removeEntity(ent) -- sprite is removed in SDrawable
end
end
What it does:
- depending on the item collected (coin, live, ...) it increases a value
- the UI is updated as well to reflect the new values
- the collectible is removed from the scene in the onRemove function
sDoor.lua
Doors will work in tandem with sensors and keys collectible. A door can open in all directions: up, down, left, right and diagonals.
Please create a file "sDoor.lua" in the "_S" folder and the code:
SDoor = Core.class()
function SDoor:init(xtiny, xbworld, xplayer1)
xtiny.processingSystem(self) -- called once on init and every frames
self.tiny = xtiny
self.bworld = xbworld -- cbump world
self.player1 = xplayer1
end
function SDoor:filter(ent) -- tiny function
return ent.isdoor
end
function SDoor:onAdd(ent) -- tiny function
-- print("SDoor:onAdd")
end
function SDoor:onRemove(ent) -- tiny function
-- print("SDoor:onRemove")
end
local p1rangetoofarx = myappwidth*1 -- disable systems to save some CPU, magik XXX
local p1rangetoofary = myappheight*1 -- disable systems to save some CPU, magik XXX
function SDoor:process(ent, dt) -- tiny function
local function fun()
-- OUTSIDE VISIBLE RANGE
if (ent.pos.x > self.player1.pos.x + p1rangetoofarx or
ent.pos.x < self.player1.pos.x - p1rangetoofarx) or
(ent.pos.y > self.player1.pos.y + p1rangetoofary or
ent.pos.y < self.player1.pos.y - p1rangetoofary) then
ent.doanimate = false
return
end
ent.isactive = false
ent.isup = false ent.isdown = false
ent.isleft = false ent.isright = false
if ent.eid == self.player1.insensor then -- player1 inside door sensor, open
if ent.eid:find("Z") or -- no key needed for eid containing 'Z'
(self.tiny.player1inventory[ent.eid]) then -- player1 has key in inventory
-- left/right opening
if ent.dir:match("L") then
if ent.pos.x > ent.distance.startpos.x - ent.distance.dx then -- move left
ent.isleft = true ent.isright = false
ent.isactive = true
end
elseif ent.dir:match("R") then
if ent.pos.x < ent.distance.startpos.x + ent.distance.dx then -- move right
ent.isright = true ent.isleft = false
ent.isactive = true
end
end
-- up/down opening
if ent.dir:match("U") then
if ent.pos.y > ent.distance.startpos.y - ent.distance.dy then -- move up
ent.isup = true ent.isdown = false
ent.isactive = true
end
elseif ent.dir:match("D") then
if ent.pos.y < ent.distance.startpos.y + ent.distance.dy then -- move down
ent.isdown = true ent.isup = false
ent.isactive = true
end
end
end
else -- player1 outside door sensor, close
-- left/right closing
if ent.dir:match("L") then
if ent.pos.x < ent.distance.startpos.x then -- move right
ent.isright = true ent.isleft = false
ent.isactive = true
end
elseif ent.dir:match("R") then
if ent.pos.x > ent.distance.startpos.x then -- move left
ent.isleft = true ent.isright = false
ent.isactive = true
end
end
-- up/down closing
if ent.dir:match("U") then
if ent.pos.y < ent.distance.startpos.y then -- move down
ent.isdown = true ent.isup = false
ent.isactive = true
end
elseif ent.dir:match("D") then
if ent.pos.y > ent.distance.startpos.y - ent.distance.dy then -- move up
ent.isup = true ent.isdown = false
ent.isactive = true
end
end
end
-- if player1 acquires key, illuminate (cutscenes, ...)
if (ent.eid:find("Z") and not ent.highlight) or -- no key needed for eid containing 'Z'
(self.tiny.player1inventory[ent.eid] and not ent.highlight) then -- player1 has key
ent.sprite:setAlpha(1.7) -- 1.6, 2
ent.highlight = true -- only highlight once
end
-- action
if ent.isactive then
local function collisionfilter(item, other) -- "touch", "cross", "slide", "bounce"
if other.isnme or other.isplayer1 then return "slide" end -- ok
return nil -- "cross"
end
-- _____ ____ _ _ __ __ _____
-- / ____| _ \| | | | \/ | __ \
--| | | |_) | | | | \ / | |__) |
--| | | _ <| | | | |\/| | ___/
--| |____| |_) | |__| | | | | |
-- \_____|____/ \____/|_| |_|_|
local goalx = ent.pos.x + ent.body.vx * dt
local goaly = ent.pos.y + ent.body.vy * dt
local nextx, nexty = self.bworld:move(ent, goalx, goaly, collisionfilter)
-- _____ _ ___ _______ _____ _____ _____
-- | __ \| | | \ \ / / ____|_ _/ ____|/ ____|
-- | |__) | |__| |\ \_/ / (___ | || | | (___
-- | ___/| __ | \ / \___ \ | || | \___ \
-- | | | | | | | | ____) |_| || |____ ____) |
-- |_| |_| |_| |_| |_____/|_____\_____|_____/
if ent.isleft and not ent.isright then
ent.body.vx = -ent.body.speed
elseif ent.isright and not ent.isleft then
ent.body.vx = ent.body.speed
end
if ent.isup and not ent.isdown then
ent.body.vy = -ent.body.upspeed
elseif ent.isdown and not ent.isup then
ent.body.vy = ent.body.upspeed
end
-- move
ent.pos = vector(nextx, nexty)
ent.sprite:setPosition(ent.pos + vector(ent.collbox.w/2, -ent.h/2+ent.collbox.h))
end
Core.yield(1)
end
Core.asyncCall(fun)
end
What it does:
- first we check if the player1 is in the door sensor by checking the door id and the player1 sensor id
- then we check if the door needs a key to open or not
- if the door needs a key, we check the player1 inventory for that key
- when the player1 is in the door sensor, we open the door in the given direction
- when the player1 goes outside the door sensor, we close the door
- finally cBump uses the state the door is in and moves the door open or close
sSensor.lua
Here is the sensor System. It is used for doors but can work for anything.
Please create a file "sSensor.lua" in the "_S" folder and the code:
SSensor = Core.class()
function SSensor:init(xtiny, xbworld, xplayer1)
xtiny.processingSystem(self) -- called once on init and every frames
self.bworld = xbworld -- cbump world
self.player1 = xplayer1
end
function SSensor:filter(ent) -- tiny function
return ent.issensor
end
function SSensor:onAdd(ent) -- tiny function
-- print("SSensor:onAdd")
end
function SSensor:onRemove(ent) -- tiny function
-- print("SSensor:onRemove")
end
local p1rangetoofarx = myappwidth*1 -- disable systems to save some CPU, magik XXX
local p1rangetoofary = myappheight*1 -- disable systems to save some CPU, magik XXX
function SSensor:process(ent, dt) -- tiny function
local function fun()
-- OUTSIDE VISIBLE RANGE
if (ent.pos.x > self.player1.pos.x + p1rangetoofarx or
ent.pos.x < self.player1.pos.x - p1rangetoofarx) or
(ent.pos.y > self.player1.pos.y + p1rangetoofary or
ent.pos.y < self.player1.pos.y - p1rangetoofary) then
ent.doanimate = false
return
end
local function collisionfilter2(item) -- only one param: "item", return true, false or nil
if item.isplayer1 then return true end
end
--local items, len = world:queryRect(l,t,w,h, filter)
local items, len2 = self.bworld:queryRect(
ent.pos.x, ent.pos.y, ent.w, ent.h, collisionfilter2
)
for i = 1, len2 do
items[i].insensor = ent.eid -- add an extra var to player1
end
Core.yield(1)
end
Core.asyncCall(fun)
end
What it does:
- it uses cBump queryRect function to check if the player1 is within the sensor boundaries
- the sensor collision filter (collisionfilter2) is where we add the player1 as a potential candidate for collision detection with the sensor
- when the player1 is "in" the sensor, we set the player1 insensor variable to the id of the sensor. We can then querry that variable in the game to trigger events
sMvpf.lua
A quick one, the moving platforms. Please create a file "sMvpf.lua" in the "_S" folder and the code:
SMvpf = Core.class()
function SMvpf:init(xtiny, xbworld)
xtiny.processingSystem(self) -- called once on init and every frames
self.bworld = xbworld -- cbump world
end
function SMvpf:filter(ent) -- tiny function
return ent.ismvpf -- both mvpf and ptmvpf
end
function SMvpf:onAdd(ent) -- tiny function
-- print("SMvpf:onAdd")
end
function SMvpf:onRemove(ent) -- tiny function
-- print("SMvpf:onRemove")
end
function SMvpf:process(ent, dt) -- tiny function
local function fun()
local function collisionfilter(item, other) -- "touch", "cross", "slide", "bounce"
return "cross"
end
-- cbump
local goalx = ent.pos.x + ent.body.vx * dt
local goaly = ent.pos.y + ent.body.vy * dt
local nextx, nexty, _collisions, _len = self.bworld:move(ent, goalx, goaly, collisionfilter)
-- motion ai x
if ent.pos.x >= ent.distance.startpos.x + ent.distance.dx then
ent.isleft, ent.isright = true, false
elseif ent.pos.x <= ent.distance.startpos.x then
ent.isleft, ent.isright = false, true
end
-- motion ai y
if ent.dir == "SE" then -- initial movement going down to the right
if ent.pos.y >= ent.distance.startpos.y + ent.distance.dy then
ent.isup, ent.isdown = true, false
elseif ent.pos.y <= ent.distance.startpos.y then
ent.isup, ent.isdown = false, true
end
else
if ent.pos.y <= ent.distance.startpos.y - ent.distance.dy then
ent.isup, ent.isdown = false, true
elseif ent.pos.y >= ent.distance.startpos.y then
ent.isup, ent.isdown = true, false
end
end
-- movement vx
if ent.isleft and not ent.isright then -- LEFT
ent.flip = -1
ent.body.vx = -ent.body.speed
elseif ent.isright and not ent.isleft then -- RIGHT
ent.flip = 1
ent.body.vx = ent.body.speed
end
-- movement vy
if ent.isup and not ent.isdown then -- UP
ent.body.vy = -ent.body.upspeed
elseif ent.isdown and not ent.isup then -- DOWN
ent.body.vy = ent.body.upspeed
end
-- move & flip
ent.pos = vector(nextx, nexty)
ent.sprite:setPosition(ent.pos + vector(ent.collbox.w/2, -ent.h/2+ent.collbox.h))
Core.yield(1)
end
Core.asyncCall(fun)
end
What it does:
- we use cBump to move the platform in the physics world
- we set the default platform collision to cross using collisionfilter
- we check the position of the platform and set its direction (up, down, left, right) relative to the distance it can travel
- we set the moving platform velocity (speed)
- we move the platform body (cBump) along with the platform sprite
sOscillation.lua
Another quick one, the System that moves things up and down like the collectibles. Please create a file "sOscillation.lua" in the "_S" folder and the code:
SOscillation = Core.class()
function SOscillation:init(xtiny, xbworld, xplayer1)
xtiny.processingSystem(self) -- called once on init and every frames
self.bworld = xbworld -- cbump world
self.player1 = xplayer1
end
function SOscillation:filter(ent) -- tiny function
return ent.oscillation
end
function SOscillation:onAdd(ent) -- tiny function
-- print("SOscillation:onAdd")
ent.angle = 0 -- add an extra params, important XXX
end
function SOscillation:onRemove(ent) -- tiny function
-- print("SOscillation:onRemove")
end
local p1rangetoofarx = myappwidth*1 -- disable systems to save some CPU, magik XXX
local p1rangetoofary = myappheight*1 -- disable systems to save some CPU, magik XXX
function SOscillation:process(ent, dt) -- tiny function
local function fun()
ent.doanimate = true
-- OUTSIDE VISIBLE RANGE
if (ent.pos.x > self.player1.pos.x + p1rangetoofarx or
ent.pos.x < self.player1.pos.x - p1rangetoofarx) or
(ent.pos.y > self.player1.pos.y + p1rangetoofary or
ent.pos.y < self.player1.pos.y - p1rangetoofary) then
ent.doanimate = false
return
end
local function collisionfilter(item, other) -- "touch", "cross", "slide", "bounce"
return nil -- "cross"
end
-- cbump
local goalx = ent.pos.x + ent.oscillation.vx * dt
local goaly = ent.pos.y + ent.oscillation.vy * dt
local nextx, nexty = self.bworld:move(ent, goalx, goaly, collisionfilter)
-- oscillation
ent.angle += ent.oscillation.speed
ent.oscillation.vx = math.cos(^<ent.angle)*ent.oscillation.amplitudex
ent.oscillation.vy = math.sin(^<ent.angle)*ent.oscillation.amplitudey
-- move & flip
ent.pos = vector(nextx, nexty)
ent.sprite:setPosition(ent.pos + vector(ent.collbox.w/2, -ent.h/2+ent.collbox.h))
Core.yield(1)
end
Core.asyncCall(fun)
end
What it does:
- this is to give some juice to our game, we move collectibles up and down or in circles
- we use cBump to move the body in the physics world using sinus and cosinus math functions 😊
sProjectiles.lua
Finally the projectiles everybody can throw. Please create a file "sProjectiles.lua" in the "_S" folder and the code:
SProjectiles = Core.class()
function SProjectiles:init(xtiny, xbworld) -- tiny function
xtiny.processingSystem(self) -- called once on init and every update
self.tiny = xtiny
self.bworld = xbworld
end
function SProjectiles:filter(ent) -- tiny function
return ent.isprojectile
end
function SProjectiles:onAdd(ent) -- tiny function
end
function SProjectiles:onRemove(ent) -- tiny function
self.bworld:remove(ent) -- remove collision box from cbump world here!
end
local col -- cbump perfs?
function SProjectiles:process(ent, dt) -- tiny function
-- hurt
if ent.washurt > 0 and ent.wasbadlyhurt <= 0 and ent.currlives > 0 then -- lose 1 nrg
ent.washurt -= 1
ent.isdirty = false
ent.animation.curranim = g_ANIM_HURT_R
if ent.washurt <= 0 then
ent.sprite:setColorTransform(1, 1, 1, 1) -- reset color transform
elseif ent.washurt < ent.recovertimer*0.5 then
ent.hitfx:setVisible(false)
end
elseif ent.wasbadlyhurt > 0 and ent.currlives > 0 then -- lose 1 life
ent.wasbadlyhurt -= 1
ent.isdirty = false
ent.animation.curranim = g_ANIM_LOSE1_R
if ent.wasbadlyhurt <= 0 then
ent.sprite:setColorTransform(1, 1, 1, 1) -- reset color transform
elseif ent.wasbadlyhurt < ent.recoverbadtimer*0.5 then
ent.hitfx:setVisible(false)
ent.animation.curranim = g_ANIM_STANDUP_R
ent.animation.frame = 0 -- start animation at frame 0
end
end
-- hit
if ent.isdirty then
ent.isdirty = false
ent.currlives -= 1
end
-- deaded
if ent.currlives <= 0 or
ent.pos.x > (ent.dist.startpos.x+ent.dist.dx) or
ent.pos.x < (ent.dist.startpos.x-ent.dist.dx) or
ent.pos.y > (ent.dist.startpos.y+ent.dist.dy) or
ent.pos.y < (ent.dist.startpos.y-ent.dist.dy) then
-- stop all movements
ent.isleft = false
ent.isright = false
ent.isup = false
ent.isdown = false
-- play dead sequence
ent.isdirty = false
self.tiny.tworld:removeEntity(ent) -- sprite is removed in SDrawable
end
-- already deaded
if ent.currlives <= 0 or
ent.pos.x > (ent.dist.startpos.x+ent.dist.dx) or
ent.pos.x < (ent.dist.startpos.x-ent.dist.dx) or
ent.pos.y > (ent.dist.startpos.y+ent.dist.dy) or
ent.pos.y < (ent.dist.startpos.y-ent.dist.dy) then
return
end
local function fun()
-- _____ ____ _ _ _____ _____ _____ ____ _ _
-- / ____/ __ \| | | | |_ _|/ ____|_ _/ __ \| \ | |
--| | | | | | | | | | | | (___ | || | | | \| |
--| | | | | | | | | | | \___ \ | || | | | . ` |
--| |___| |__| | |____| |____ _| |_ ____) |_| || |__| | |\ |
-- \_____\____/|______|______|_____|_____/|_____\____/|_| \_|
-- ______ _____ _ _______ ______ _____
--| ____|_ _| | |__ __| ____| __ \
--| |__ | | | | | | | |__ | |__) |
--| __| | | | | | | | __| | _ /
--| | _| |_| |____| | | |____| | \ \
--|_| |_____|______|_| |______|_| \_\
local function collisionfilter(item, other) -- "touch", "cross", "slide", "bounce"
return "cross"
end
-- _____ ____ _ _ __ __ _____
-- / ____| _ \| | | | \/ | __ \
--| | | |_) | | | | \ / | |__) |
--| | | _ <| | | | |\/| | ___/
--| |____| |_) | |__| | | | | |
-- \_____|____/ \____/|_| |_|_|
local goalx = ent.pos.x + ent.body.vx * dt
local goaly = ent.pos.y + ent.body.vy * dt
local nextx, nexty, collisions, len = self.bworld:move(ent, goalx, goaly, collisionfilter)
-- _____ ____ _ _ _____ _____ _____ ____ _ _ _____
-- / ____/ __ \| | | | |_ _|/ ____|_ _/ __ \| \ | |/ ____|
--| | | | | | | | | | | | (___ | || | | | \| | (___
--| | | | | | | | | | | \___ \ | || | | | . ` |\___ \
--| |___| |__| | |____| |____ _| |_ ____) |_| || |__| | |\ |____) |
-- \_____\____/|______|______|_____|_____/|_____\____/|_| \_|_____/
-- we handle ALL collisions here!
for i = 1, len do
col = collisions[i]
if col.other.isplayer1 and col.item.eid > 1 then -- nme bullet vs player1
if col.other.body.currdashtimer <= 0 then
col.other.damage = 1
col.other.isdirty = true
col.item.damage = 1
col.item.isdirty = true
-- don't destroy bullet if it is persistent
if col.item.ispersistant then col.item.isdirty = false end
end
elseif col.other.isnme and col.item.eid == 1 then -- player1 bullet vs nmes
if col.other.body.currdashtimer <= 0 then
col.item.damage = 1
col.other.damage = 1
col.item.isdirty = true
if col.item.ispersistant then col.item.isdirty = false end
col.other.isdirty = true
end
elseif col.other.isdoor then -- bullets vs door
col.item.damage = 1
col.item.isdirty = true
-- elseif col.other.isfloor then -- bullets vs floor
-- col.item.damage = 1
-- col.item.isdirty = true
end
end
-- _____ _ ___ _______ _____ _____ _____
-- | __ \| | | \ \ / / ____|_ _/ ____|/ ____|
-- | |__) | |__| |\ \_/ / (___ | || | | (___
-- | ___/| __ | \ / \___ \ | || | \___ \
-- | | | | | | | | ____) |_| || |____ ____) |
-- |_| |_| |_| |_| |_____/|_____\_____|_____/
-- gravity
-- ent.body.vy += 1*8 * ent.body.currmass -- 3*8, gravity, magik XXX
-- move & flip
ent.pos = vector(nextx, nexty)
ent.sprite:setPosition(ent.pos + vector(ent.collbox.w/2, -ent.h/2+ent.collbox.h))
if ent.animation then
ent.animation.bmp:setScale(ent.sx*ent.flip, ent.sy)
end
Core.yield(1)
end
Core.asyncCall(fun)
end
What it does:
- first we check if the projectile has hit its target and remove the projectile from the scene
- else we use cBump to move the projectile and detect collisions
- on collision we inflict damage to the appropriate actor (player1, enemies, ...)
- as usual we move the projectile sprite along its body in the cBump physics world
I think we have all our systems 👍.
DEBUG
To make it easy to debug collisions in the physics world and actually see them, let's add some quick debug systems.
To see the collision boxes attached to an Entity, please create a file "sDebugCollision.lua" in the "_S" folder and the code:
SDebugCollision = Core.class()
function SDebugCollision:init(xtiny) -- tiny function
xtiny.processingSystem(self) -- called once on init and every update
end
function SDebugCollision:filter(ent) -- tiny function
return ent.collbox
end
function SDebugCollision:onAdd(ent) -- tiny function
local debugcolor = 0x9b009b
if ent.isplayer1 then debugcolor = 0xff00ff end
ent.debug = Pixel.new(debugcolor, 0.5, ent.collbox.w, ent.collbox.h)
ent.spritelayer:addChild(ent.debug)
end
function SDebugCollision:onRemove(ent) -- tiny function
ent.debug:removeFromParent()
end
function SDebugCollision:process(ent, dt) -- tiny function
local function fun()
ent.debug:setPosition(ent.pos)
Core.yield(1)
end
Core.asyncCall(fun)
end
To see the origin position of collision boxes attached to an Entity, please create a file "sDebugPosition.lua" in the "_S" folder and the code:
SDebugPosition = Core.class()
function SDebugPosition:init(xtiny) -- tiny function
xtiny.processingSystem(self) -- called once on init and every update
end
function SDebugPosition:filter(ent) -- tiny function
return ent.pos
end
function SDebugPosition:onAdd(ent) -- tiny function
local debugcolor = 0x0000a5
if ent.isplayer1 then debugcolor = 0x0000ff end
ent.debug = Pixel.new(debugcolor, 2, 5, 5)
ent.spritelayer:addChild(ent.debug)
end
function SDebugPosition:onRemove(ent) -- tiny function
ent.debug:removeFromParent()
end
function SDebugPosition:process(ent, dt) -- tiny function
local function fun()
ent.debug:setPosition(ent.pos)
Core.yield(1)
end
Core.asyncCall(fun)
end
To see the shield collision boxes attached to an Entity, please create a file "sDebugShield.lua" in the "_S" folder and the code:
SDebugShield = Core.class()
function SDebugShield:init(xtiny) -- tiny function
xtiny.processingSystem(self) -- called once on init and every update
end
function SDebugShield:filter(ent) -- tiny function
return ent.shield
end
function SDebugShield:onAdd(ent) -- tiny function
--ent.pos + vector(ent.collbox.w/2, 0) + ent.shield.offset*vector(ent.flip, 1)
local pw, ph = ent.shield.sprite:getWidth(), ent.shield.sprite:getHeight()
ent.debug = Pixel.new(0x5500ff, 0.2, pw, ph)
ent.debug:setAnchorPoint(0.5, 0.5)
ent.spritelayer:addChild(ent.debug)
-- querry rect
ent.debugqr = Pixel.new(0xaaff00, 0.3, pw, ph) -- querry rectangle
ent.debugqr:setAnchorPoint(0, 0.01) -- a little offset
ent.spritelayer:addChild(ent.debugqr)
end
function SDebugShield:onRemove(ent) -- tiny function
ent.debug:removeFromParent()
ent.debugqr:removeFromParent()
end
function SDebugShield:process(ent, dt) -- tiny function
local function fun()
if ent.currlives > 0 then
ent.debug:setPosition(
ent.pos +
vector(ent.collbox.w/2, 0) +
ent.shield.offset*vector(ent.shield.sprite.sx*ent.flip, ent.shield.sprite.sy)
)
local pw, ph = ent.shield.sprite:getWidth(), ent.shield.sprite:getHeight()
ent.debugqr:setPosition(
ent.pos +
ent.shield.offset*vector(ent.shield.sprite.sx*ent.flip, ent.shield.sprite.sy) -
vector(pw*0.5, ph*0.5) + vector(ent.collbox.w*0.5, 0)
)
end
Core.yield(1)
end
Core.asyncCall(fun)
end
Next?
Congratulations! We have finished our game.
Let's add the final scene: YOU WIN!
Prev.: Tuto tiny-ecs 2d platformer Part 10 Collision System
Next: Tuto tiny-ecs 2d platformer Part 12 You Win
Tutorial - tiny-ecs 2d platformer