Difference between revisions of "Tuto tiny-ecs beatemup Part 11 Systems 3"

From GiderosMobile
(wip)
 
(wip)
Line 1: Line 1:
 
__TOC__
 
__TOC__
  
== The Systems 2 ==
+
== The Systems 3 ==
We continue adding systems to our game.
+
A couple more systems to add and we are done. Those systems apply on the breakable objects and the collectibles.
A System is a wrapper around function callbacks for manipulating Entities. Systems are implemented as tables that contain at least one method; an update function that takes parameters like so:
 
 
'''function system:update(dt)'''
 
 
There are also a few other optional callbacks:
 
*'''function system:filter(entity)''' Returns true if this System should include this Entity, otherwise should return false. If this isn't specified, no Entities are included in the System.
 
*'''function system:onAdd(entity)''' Called when an Entity is added to the System.
 
*'''function system:onRemove(entity)''' Called when an Entity is removed from the System.
 
*'''function system:onModify(dt)''' Called when the System is modified by adding or removing Entities from the System.
 
*'''function system:onAddToWorld(world)''' Called when the System is added to the World, before any entities are added to the system.
 
*'''function system:onRemoveFromWorld(world)''' Called when the System is removed from the world, after all Entities are removed from the System.
 
*'''function system:preWrap(dt)''' Called on each system before update is called on any system.
 
*'''function system:postWrap(dt)''' Called on each system in reverse order after update is called on each system.
 
 
'''Please see [[Tiny-ecs#System_functions]] for more information'''
 
  
== sAnimation.lua ==
+
== sDestructibleObjects.lua ==
Time to animate our actors. Please create a file "'''sAnimation.lua'''" in the '''"_S"''' folder and the code:
+
Please create a file "'''sDestructibleObjects.lua'''" in the '''"_S"''' folder and the code:
 
<syntaxhighlight lang="lua">
 
<syntaxhighlight lang="lua">
SAnimation = Core.class()
+
SDestructibleObjects = Core.class()
  
function SAnimation:init(xtiny)
+
local random, cos, sin = math.random, math.cos, math.sin
xtiny.processingSystem(self) -- called once on init and every frames
+
local insert = table.insert
self.sndstepgrass = Sound.new("audio/sfx/footstep/Grass02.wav")
+
 
self.channel = self.sndstepgrass:play(0, false, true)
+
function SDestructibleObjects:init(xtiny, xbworld) -- tiny function
 +
self.tiny = xtiny -- ref so we can remove entities from tiny system
 +
self.tiny.processingSystem(self) -- called once on init and every update
 +
self.bworld = xbworld
 +
-- sfx
 +
self.snd = Sound.new("audio/sfx/footstep/Forest02.wav")
 +
self.channel = self.snd:play(0, false, true)
 
end
 
end
  
function SAnimation:filter(ent) -- tiny function
+
function SDestructibleObjects:filter(ent) -- tiny function
return ent.animation
+
return ent.isdestructibleobject
 
end
 
end
  
function SAnimation:onAdd(ent) -- tiny function
+
function SDestructibleObjects:onAdd(ent) -- tiny function
 
end
 
end
  
function SAnimation:onRemove(ent) -- tiny function
+
function SDestructibleObjects:onRemove(ent) -- tiny function
ent.animation = nil -- free some memory?
+
-- spawn a random collectible: ECollectible:init(xspritelayer, xpos)
 +
local function fun()
 +
local el = ECollectible.new(ent.spritelayer, ent.pos+vector(ent.collbox.w/4, -1*ent.collbox.h))
 +
self.tiny.tworld:addEntity(el)
 +
self.bworld:add(el, el.pos.x, el.pos.y, el.collbox.w, el.collbox.h)
 +
Core.yield(1)
 +
end
 +
Core.asyncCall(fun)
 +
self.bworld:remove(ent) -- remove collision box from cbump world here!
 
end
 
end
  
local checkanim
+
function SDestructibleObjects:process(ent, dt) -- tiny function
function SAnimation:process(ent, dt) -- tiny function
+
local function EffectExplode(s, scale, pos, r, speed, texture)
-- a little boost?
+
local p = Particles.new()
local anim = ent.animation
+
p:setPosition(pos)
 
+
p:setTexture(texture)
-- checkanim = anim.curranim -- if you are sure all animations are set else use below ternary operator code
+
p:setScale(scale)
-- luau ternary operator (no end at the end), it's a 1 liner and seems fast?
+
s:addChild(p)
checkanim = if anim.anims[ent.animation.curranim] then anim.curranim else g_ANIM_DEFAULT
+
local parts = {}
-- print(#anim.anims[checkanim])
+
for i = 1, 6 do -- 8
 
+
local a = random()*6.3
if not ent.doanimate then return end
+
local dx, dy = cos(a), sin(a)
 
+
local sr = random()*r
anim.animtimer -= dt
+
local px, py = dx*sr, dy*sr
if anim.animtimer < 0 then
+
local ss = (random()+0.5) * (speed or 1)
anim.frame += 1
+
insert(parts,
anim.animtimer = anim.animspeed
+
{
if checkanim == g_ANIM_DEFAULT then
+
x = px, y = py,
if anim.frame > #anim.anims[checkanim] then
+
speedX = dx * ss,
anim.frame = 1
+
speedY = dy * ss,
end
+
speedAngular = random()*4 - 2,
elseif checkanim == g_ANIM_LOSE1_R or checkanim == g_ANIM_STANDUP_R then
+
decayAlpha = random()*0.04 + 0.95,
if anim.frame >= #anim.anims[checkanim] then
+
ttl = 32, -- 500
anim.frame = #anim.anims[checkanim]
+
size = random()*10 + 20,
end
+
}
elseif checkanim == g_ANIM_PUNCH_ATTACK1_R then
+
)
ent.headhitbox = ent.headhitboxattack1
+
end
if #anim.anims[checkanim] == 1 then -- 1 frame animation
+
p:addParticles(parts)
anim.frame = 1
+
Core.yield(1)
ent.headhitboxattack1.isactive = true
+
p:removeFromParent()
ent.isactionpunch1 = false
+
end
else -- multi frames animation
+
-- hurt fx
if anim.frame > #anim.anims[checkanim] then
+
if ent.washurt and ent.washurt > 0 then
-- anim.frame = 1
+
ent.washurt -= 1
anim.frame = #anim.anims[checkanim]
+
if ent.washurt < ent.recovertimer/2 then
ent.headhitboxattack1.isactive = false
+
if ent.hitfx then ent.hitfx:setVisible(false) end
ent.isactionpunch1 = false
+
end
elseif anim.frame > ent.headhitbox.hitendframe then
+
if ent.washurt <= 0 then
ent.headhitboxattack1.isactive = false
+
ent.sprite:setColorTransform(1, 1, 1, 1)
elseif anim.frame >= ent.headhitbox.hitstartframe then
+
end
ent.headhitboxattack1.isactive = true
+
end
end
+
if ent.isdirty then -- hit
end
+
self.channel = self.snd:play()
elseif checkanim == g_ANIM_PUNCH_ATTACK2_R then
+
if self.channel then self.channel:setVolume(g_sfxvolume*0.01) end
ent.headhitbox = ent.headhitboxattack2
+
ent.hitfx:setVisible(true)
if anim.frame > #anim.anims[checkanim] then
+
ent.hitfx:setPosition(ent.pos + vector(ent.headhurtbox.x+4, ent.headhurtbox.y))
-- anim.frame = 1
+
ent.spritelayer:addChild(ent.hitfx)
anim.frame = #anim.anims[checkanim]
+
ent.currhealth -= ent.damage
ent.headhitboxattack2.isactive = false
+
ent.washurt = ent.recovertimer -- timer for a flash effect
ent.isactionpunch2 = false
+
ent.sprite:setColorTransform(1, 1, 1, 3) -- a flash effect
elseif anim.frame > ent.headhitbox.hitendframe then
+
ent.isdirty = false
ent.headhitboxattack2.isactive = false
+
if ent.currhealth <= 0 then
elseif anim.frame >= ent.headhitbox.hitstartframe then
+
--EffectExplode(s, scale, pos, r, speed, texture)
ent.headhitboxattack2.isactive = true
+
Core.asyncCall(EffectExplode, ent.spritelayer, 2,
end
+
ent.pos+vector(ent.collbox.w/2, -ent.h/2), 4, 2,
elseif checkanim == g_ANIM_KICK_ATTACK1_R then
+
Texture.new("gfx/fx/fxBarrel_02_0011.png"))
ent.spinehitbox = ent.spinehitboxattack1
+
ent.spritelayer:removeChild(ent.hitfx)
if #anim.anims[checkanim] == 1 then -- 1 frame animation
+
self.tiny.tworld:removeEntity(ent) -- sprite is removed in SDrawable
anim.frame = 1
+
self.tiny.numberofdestructibleobjects -= 1
ent.spinehitboxattack1.isactive = true
 
ent.isactionkick1 = false
 
else -- multi frames animation
 
if anim.frame > #anim.anims[checkanim] then
 
-- anim.frame = 1
 
anim.frame = #anim.anims[checkanim]
 
ent.spinehitboxattack1.isactive = false
 
ent.isactionkick1 = false
 
elseif anim.frame > ent.spinehitbox.hitendframe then
 
ent.spinehitboxattack1.isactive = false
 
elseif anim.frame >= ent.spinehitbox.hitstartframe then
 
ent.spinehitboxattack1.isactive = true
 
end
 
end
 
elseif checkanim == g_ANIM_KICK_ATTACK2_R then
 
ent.spinehitbox = ent.spinehitboxattack2
 
if anim.frame > #anim.anims[checkanim] then
 
-- anim.frame = 1
 
anim.frame = #anim.anims[checkanim]
 
ent.spinehitboxattack2.isactive = false
 
ent.isactionkick2 = false
 
elseif anim.frame > ent.spinehitbox.hitendframe then
 
ent.spinehitboxattack2.isactive = false
 
elseif anim.frame >= ent.spinehitbox.hitstartframe then
 
ent.spinehitboxattack2.isactive = true
 
end
 
elseif checkanim == g_ANIM_JUMP1_R then -- only jump, no attacks
 
if anim.frame > #anim.anims[checkanim] then
 
anim.frame = #anim.anims[checkanim]
 
end
 
elseif checkanim == g_ANIM_PUNCHJUMP_ATTACK1_R then
 
ent.headhitbox = ent.headhitboxjattack1
 
if anim.frame > #anim.anims[checkanim] then
 
anim.frame = #anim.anims[checkanim]
 
ent.headhitboxjattack1.isactive = false
 
-- ent.isactionjumppunch1 = false -- don't set to false here otherwise BUGGGZZZZ!!!
 
else
 
ent.headhitboxjattack1.isactive = true
 
end
 
elseif checkanim == g_ANIM_KICKJUMP_ATTACK1_R then
 
ent.spinehitbox = ent.spinehitboxjattack1
 
if anim.frame > #anim.anims[checkanim] then
 
anim.frame = #anim.anims[checkanim]
 
ent.spinehitboxjattack1.isactive = false
 
-- ent.isactionjumpkick1 = false -- don't set to false here otherwise BUGGGZZZZ!!!
 
else
 
ent.spinehitboxjattack1.isactive = true
 
end
 
else
 
-- player1 steps sound fx
 
if ent.isplayer1 and
 
(anim.curranim == g_ANIM_WALK_R or anim.curranim == g_ANIM_RUN_R) and
 
(anim.frame == 4 or anim.frame == 9) then
 
self.channel = self.sndstepgrass:play()
 
if self.channel then self.channel:setVolume(g_sfxvolume*0.01) end
 
end
 
-- loop animations
 
if anim.frame > #anim.anims[checkanim] then
 
anim.frame = 1
 
end
 
 
end
 
end
anim.bmp:setTextureRegion(anim.anims[checkanim][anim.frame])
 
 
end
 
end
 
end
 
end
 
</syntaxhighlight>
 
</syntaxhighlight>
  
What the System does:
+
The System removes a breakable object when destroyed and spawn a collectible in the '''onRemove''' function:
 
* it runs every frame
 
* it runs every frame
* it affects only entities with an ''animation'' id
+
* it affects only entities with an ''isdestructibleobject'' id
* it checks which animation to play
+
* on hurt adds an hit fx
* it decreases the ''animtimer''
+
* on destroyed adds particles
* and increases the current frame in the animation by 1
+
* '''onRemove''' spawns a collectible
* if an attack animation is playing it activates/deactivates the corresponding actor '''hit boxes'''
 
* it eventually plays a sound (player1 steps sound fx)
 
* it finally sets the current frame in the animation as the actor Bitmap
 
  
== sDynamicBodies.lua ==
+
== sCollectible.lua ==
"'''sDynamicBodies.lua'''" in the '''"_S"''' folder. The code:
+
"'''sCollectible.lua'''" in the '''"_S"''' folder. The code:
 
<syntaxhighlight lang="lua">
 
<syntaxhighlight lang="lua">
SDynamicBodies = Core.class()
+
SCollectible = Core.class()
  
 
local random = math.random
 
local random = math.random
  
function SDynamicBodies:init(xtiny, xmapdef) -- tiny function
+
function SCollectible:init(xtiny, xbump, xplayer1) -- tiny function
self.tiny = xtiny -- to access self.tiny variables
+
self.tiny = xtiny -- make a class ref
 
self.tiny.processingSystem(self) -- called once on init and every update
 
self.tiny.processingSystem(self) -- called once on init and every update
self.mapdef = xmapdef
+
self.bworld = xbump
 +
self.player1 = xplayer1
 +
-- sfx
 +
self.snd = Sound.new("audio/sfx/sfx_coin_double1.wav")
 +
self.channel = self.snd:play(0, false, true)
 
end
 
end
  
function SDynamicBodies:filter(ent) -- tiny function
+
function SCollectible:filter(ent) -- tiny function
return ent.body -- only actors with body component
+
return ent.iscollectible
 
end
 
end
  
function SDynamicBodies:onAdd(ent) -- tiny function
+
function SCollectible:onAdd(ent) -- tiny function
 
end
 
end
  
function SDynamicBodies:onRemove(ent) -- tiny function
+
function SCollectible:onRemove(ent) -- tiny function
 +
self.bworld:remove(ent) -- remove collision box from cbump world here!
 
end
 
end
  
local randomdir = 0 -- for nmes
+
function SCollectible:process(ent, dt) -- tiny function
function SDynamicBodies:process(ent, dt) -- tiny function
+
if ent.isdirty then -- hit
-- is dead or hurt?
+
local function map(v, minSrc, maxSrc, minDst, maxDst, clampValue)
if (ent.washurt and ent.washurt > 0) or (ent.wasbadlyhurt and ent.wasbadlyhurt > 0) or ent.currlives <= 0 then
+
local newV = (v - minSrc) / (maxSrc - minSrc) * (maxDst - minDst) + minDst
ent.body.vx = 0
+
return not clampValue and newV or clamp(newV, minDst >< maxDst, minDst <> maxDst)
ent.body.vy = 0
 
return
 
-- TO REVISIT
 
end
 
-- movements
 
if ent.isleft and not ent.isright and ent.pos.x > self.mapdef.l then -- LEFT
 
ent.animation.curranim = g_ANIM_WALK_R
 
ent.body.vx = -ent.body.currspeed*dt
 
ent.flip = -1
 
elseif ent.isright and not ent.isleft and ent.pos.x < self.mapdef.r - ent.w*0.5 then -- RIGHT
 
ent.animation.curranim = g_ANIM_WALK_R
 
ent.body.vx = ent.body.currspeed*dt
 
ent.flip = 1
 
else -- IDLE
 
ent.animation.curranim = g_ANIM_IDLE_R
 
ent.body.vx = 0
 
end
 
if ent.isup and not ent.isdown and ent.body.isonfloor and ent.pos.y > self.mapdef.t then -- UP
 
ent.animation.curranim = g_ANIM_WALK_R
 
ent.body.vy = -ent.body.currspeed*0.015*dt -- 0.01, you choose
 
elseif ent.isdown and not ent.isup and ent.body.isonfloor and ent.pos.y < self.mapdef.b then -- DOWN
 
ent.animation.curranim = g_ANIM_WALK_R
 
ent.body.vy = ent.body.currspeed*0.015*dt -- 0.01, you choose
 
else
 
if ent.body.isonfloor then
 
ent.body.vy = 0
 
 
end
 
end
end
+
self.channel = self.snd:play()
-- actions
+
if self.channel then self.channel:setVolume(g_sfxvolume*0.01) end
if ent.body.isonfloor then -- GROUND
+
-- ent.hitfx:setVisible(true)
if ent.isactionpunch1 then
+
-- ent.hitfx:setPosition(ent.pos.x+ent.headhurtbox.x, ent.y+ent.headhurtbox.y)
ent.animation.curranim = g_ANIM_PUNCH_ATTACK1_R
+
-- ent.spritelayer:addChild(ent.hitfx)
ent.body.vx = 0 -- *= 0.1*dt, you choose
+
-- ent.currhealth -= ent.damage
ent.body.vy = 0 -- *= 0.1*dt, you choose
+
-- ent.washurt = ent.recovertimer -- timer for a flash effect
elseif ent.isactionpunch2 then
+
if random(100) > 50 then
ent.animation.curranim = g_ANIM_PUNCH_ATTACK2_R
+
if self.player1.currjumps < 0 then self.player1.currjumps = 0 end
ent.body.vx = 0 -- *= 0.1*dt, you choose
+
self.player1.currjumps += 3
ent.body.vy = 0 -- *= 0.1*dt, you choose
+
self.tiny.hudcurrjumps:setText("JUMPS: "..self.player1.currjumps)
elseif ent.isactionkick1 then
+
else
ent.animation.curranim = g_ANIM_KICK_ATTACK1_R
+
self.player1.currhealth += 1
ent.body.vx = 0 -- *= 0.1*dt, you choose
+
-- hud
ent.body.vy = 0 -- *= 0.1*dt, you choose
+
local hudhealthwidth = map(self.player1.currhealth, 0, self.player1.totalhealth, 0, 100)
elseif ent.isactionkick2 then
+
self.tiny.hudhealth:setWidth(hudhealthwidth)
ent.animation.curranim = g_ANIM_KICK_ATTACK2_R
+
if self.player1.currhealth < self.player1.totalhealth/3 then self.tiny.hudhealth:setColor(0xff0000)
ent.body.vx = 0 -- *= 0.1*dt, you choose
+
elseif self.player1.currhealth < self.player1.totalhealth/2 then self.tiny.hudhealth:setColor(0xff5500)
ent.body.vy = 0 -- *= 0.1*dt, you choose
+
else self.tiny.hudhealth:setColor(0x00ff00)
end
 
else -- AIR
 
if ent.isactionpunch1 then
 
if ent.isplayer1 and ent.currjumps > 0 then
 
ent.isactionjumppunch1 = true
 
end
 
elseif ent.isactionkick1 then
 
if ent.isplayer1 and ent.currjumps > 0 then
 
ent.isactionjumpkick1 = true
 
 
end
 
end
 
end
 
end
if ent.isactionjump1 and not (ent.isactionjumppunch1 or ent.isactionjumpkick1) then -- JUMP ONLY
+
ent.sprite:setColorTransform(0, 2, 0, 3) -- the flash effect (a bright color)
ent.animation.curranim = g_ANIM_JUMP1_R
+
ent.isdirty = false
if ent.isplayer1 then ent.body.vx *= 2 end -- 3, acceleration, you choose
+
--[[
ent.body.currjumpspeed = ent.body.jumpspeed*1.1 -- higher jump
+
if ent.currhealth <= 0 then
if ent.body.isgoingup then ent.body.vy -= ent.body.currjumpspeed end
+
ent.hitfx:setColorTransform(3, 0, 0, random(1, 3)/10)
if ent.pos.y < ent.positionystart - ent.h*0.5 then -- higher apex, you choose
+
ent.hitfx:setY(ent.hitfx:getY()+ent.h/1.1) -- magik XXX
-- ent.body.vx = ent.body.currspeed*dt*4*ent.flip -- acceleration? you choose
+
ent.hitfx:setRotation(random(360))
ent.body.vy += ent.body.currjumpspeed*1.2 -- falling
+
ent.hitfx:setScale(random(5, 10)/10)
ent.body.isgoingup = false
+
ent.bgfxlayer:addChild(ent.hitfx)
end
+
self.tiny.tworld:removeEntity(ent) -- sprite is removed in SDrawable
if not ent.body.isgoingup and ent.pos.y >= ent.positionystart then -- grounded
+
-- self.tiny.numberofnmes -= 1
ent.body.vy = 0
 
-- ent.pos.y = ent.positionystart
 
ent.pos = vector(ent.pos.x, ent.positionystart)
 
ent.body.isonfloor = true
 
ent.isactionjump1 = false -- sometimes bug! XXX
 
end
 
end
 
if ent.isactionjumppunch1 then -- JUMP PUNCH
 
ent.animation.curranim = g_ANIM_PUNCHJUMP_ATTACK1_R
 
if ent.isplayer1 then ent.body.vx *= 2 end -- acceleration, you choose
 
ent.body.currjumpspeed = ent.body.jumpspeed
 
if ent.body.isgoingup then ent.body.vy -= ent.body.currjumpspeed end
 
if ent.pos.y < ent.positionystart - ent.h*0.45 then -- apex, you choose
 
-- ent.body.vx = ent.body.currspeed*dt*3*ent.flip -- acceleration? you choose
 
ent.body.vy += ent.body.currjumpspeed*1.2 -- falling
 
ent.body.isgoingup = false
 
end
 
if not ent.body.isgoingup and ent.pos.y >= ent.positionystart then -- grounded
 
ent.body.vy = 0
 
-- ent.pos.y = ent.positionystart
 
ent.pos = vector(ent.pos.x, ent.positionystart)
 
if ent.isnme then
 
randomdir = random(100)
 
if randomdir < 50 then ent.isleft = false ent.isright = true
 
else ent.isleft = true ent.isright = false
 
end
 
end
 
if ent.isplayer1 then
 
ent.currjumps -= 1
 
if ent.currjumps < 0 then ent.currjumps = 0 end
 
self.tiny.hudcurrjumps:setText("JUMPS: "..ent.currjumps)
 
end
 
ent.body.isonfloor = true
 
ent.isactionjump1 = false
 
ent.isactionpunch1 = false -- TEST
 
ent.isactionjumppunch1 = false -- sometimes bug! XXX
 
end
 
elseif ent.isactionjumpkick1 then -- JUMP KICK
 
ent.animation.curranim = g_ANIM_KICKJUMP_ATTACK1_R
 
if ent.isplayer1 then ent.body.vx *= 2 end -- acceleration, you choose
 
ent.body.currjumpspeed = ent.body.jumpspeed
 
if ent.body.isgoingup then ent.body.vy -= ent.body.currjumpspeed end
 
if ent.pos.y < ent.positionystart - ent.h*0.5 then -- apex, you choose
 
-- ent.body.vx = ent.body.currspeed*dt*3*ent.flip -- acceleration? you choose
 
ent.body.vy += ent.body.currjumpspeed*1.25 -- falling
 
ent.body.isgoingup = false
 
end
 
if not ent.body.isgoingup and ent.pos.y >= ent.positionystart then -- grounded
 
ent.body.vy = 0
 
-- ent.pos.y = ent.positionystart
 
ent.pos = vector(ent.pos.x, ent.positionystart)
 
if ent.isnme then
 
randomdir = random(100)
 
if randomdir < 50 then ent.isleft = false ent.isright = true
 
else ent.isleft = true ent.isright = false
 
end
 
end
 
if ent.isplayer1 then
 
ent.currjumps -= 1
 
if ent.currjumps < 0 then ent.currjumps = 0 end
 
self.tiny.hudcurrjumps:setText("JUMPS: "..ent.currjumps)
 
end
 
ent.body.isonfloor = true
 
ent.isactionjump1 = false
 
ent.isactionkick1 = false -- TEST
 
ent.isactionjumpkick1 = false -- sometimes bug!
 
end
 
 
end
 
end
end
+
]]
-- catches the sometimes bug!
+
self.tiny.tworld:removeEntity(ent) -- sprite is removed in SDrawable
if not ent.body.isonfloor and ent.body.vy == 0 then
 
print("bug", dt)
 
ent.body.vy += ent.body.currjumpspeed*16 -- makes the actor touch the ground
 
 
end
 
end
 
end
 
end
 
</syntaxhighlight>
 
</syntaxhighlight>
  
This System is reponsible for moving the actors:
+
This System spawns a collectible:
 
* runs once on init and '''every game loop''' (''process'')
 
* runs once on init and '''every game loop''' (''process'')
* in ''init'' we add the map definition to constraint the actors movement
+
* there are two kind of collectibles: health and jump attacks (updated in the HUD)
* if an actor is hurt we stop its movement
 
* if an actor is within the map definition we move it (player with keyboard, enemies with AI)
 
* then there are the attacks: ground attacks and jump attacks
 
* finally sometimes an actor doesn't land after a jump attack so we force it to do so
 
  
== sHitboxHurtboxCollision.lua ==
+
== sSpritesSorting.lua.lua ==
"'''sHitboxHurtboxCollision.lua'''" in the '''"_S"''' folder. The code:
+
"'''sSpritesSorting.lua.lua'''" in the '''"_S"''' folder. The code:
 
<syntaxhighlight lang="lua">
 
<syntaxhighlight lang="lua">
SHitboxHurtboxCollision = Core.class()
+
SSpritesSorting = Core.class()
  
function SHitboxHurtboxCollision:init(xtiny) -- tiny function
+
function SSpritesSorting:init(xtiny) -- tiny function
 
xtiny.processingSystem(self) -- called once on init and every update
 
xtiny.processingSystem(self) -- called once on init and every update
 
self.spriteslist = xtiny.spriteslist
 
self.spriteslist = xtiny.spriteslist
 
end
 
end
  
function SHitboxHurtboxCollision:filter(ent) -- tiny function
+
function SSpritesSorting:filter(ent) -- tiny function
return ent.headhitbox or ent.spinehitbox or ent.headhurtbox or ent.spinehurtbox
+
return ent.sprite
 
end
 
end
  
function SHitboxHurtboxCollision:onAdd(ent) -- tiny function
+
function SSpritesSorting:onAdd(ent) -- tiny function
 +
-- print("SSpritesSorting added", ent)
 +
self.spriteslist[ent] = true
 
end
 
end
  
function SHitboxHurtboxCollision:onRemove(ent) -- tiny function
+
function SSpritesSorting:onRemove(ent) -- tiny function
 +
-- print("SSpritesSorting removed", ent)
 +
self.spriteslist[ent] = nil
 
end
 
end
  
function SHitboxHurtboxCollision:process(ent, dt) -- tiny function
+
local p1rangetoofar = myappwidth*0.5 -- save some CPU
local function checkY(actor1y, actor2y, prange)
+
function SSpritesSorting:process(ent, dt) -- tiny function
return (-(actor1y-actor2y)<>(actor1y-actor2y)) < prange
+
local function fun()
end
+
for k, _ in pairs(self.spriteslist) do
local function checkCollision(box1x, box1y, box1w, box1h, box2x, box2y, box2w, box2h)
+
if ent.currlives <= 0 or k.currlives <= 0 then -- don't sort if dead
return not (box1x - box1w/2 > box2x + box2w/2 or -- is box1 on the right side of box2?
+
return
  box1y - box1h/2 > box2y + box2h/2 or -- is box1 under box2?
+
end
  box1x + box1w/2 < box2x - box2w/2 or -- is box1 on the left side of box2?
+
if k.isplayer1 then -- don't sort out of range actors to save frames
  box1y + box1h/2 < box2y - box2h/2) -- is box1 above box2?
+
if -(k.pos.x-ent.pos.x)<>(k.pos.x-ent.pos.x) > p1rangetoofar then
end
+
return
if ent.headhitbox and ent.headhitbox.isactive then -- HEAD
 
for k, _ in pairs(self.spriteslist) do -- k = entity, v = true
 
-- filter out unwanted collisions (player1 vs player1, nme vs nme, ... (you choose!)
 
if (ent.isplayer1 and k.isplayer1) or
 
ent.isdestructibleobject or -- destructible objects only receive damage
 
(ent.iscollectible or k.iscollectible) or -- don't check for collectibles
 
(ent.isnme and k.isnme) or
 
(ent.isnme and k.isdestructibleobject) then -- nmes don't destroy objects, you choose
 
-- nothing here!
 
-- check head collisions according to actors action and use actor y or actor positionystart
 
-- this prevents collisions between wrong hitbox and hurtbox (actors too far away on y axis)
 
elseif (ent.isactionjumppunch1 or ent.isactionjump1) and (k.isactionjumppunch1 or k.isactionjumpkick1 or k.isactionjump1) then
 
if checkY(ent.positionystart, k.positionystart, ent.collbox.h+k.collbox.h) and checkCollision(
 
ent.pos.x+ent.collbox.w/2+(ent.headhitbox.x*ent.flip), ent.pos.y+ent.headhitbox.y,
 
ent.headhitbox.w, ent.headhitbox.h,
 
k.pos.x+k.collbox.w/2+(k.headhurtbox.x*k.flip), k.pos.y+k.headhurtbox.y,
 
k.headhurtbox.w, k.headhurtbox.h) and
 
k.washurt <= 0 and k.wasbadlyhurt <= 0 then -- <= here!
 
k.isdirty = true
 
k.damage = ent.headhitbox.damage
 
if k.animation then k.animation.frame = 0 end
 
ent.headhitbox.isactive = false
 
else
 
k.isdirty = false
 
ent.headhitbox.isactive = false
 
end
 
elseif (ent.isactionjumppunch1 or ent.isactionjump1) and not (k.isactionjumppunch1 or k.isactionjumpkick1 or k.isactionjump1) then
 
if checkY(ent.positionystart, k.pos.y, ent.collbox.h + k.collbox.h) and checkCollision(
 
ent.pos.x+ent.collbox.w/2+(ent.headhitbox.x*ent.flip), ent.pos.y+ent.headhitbox.y,
 
ent.headhitbox.w, ent.headhitbox.h,
 
k.pos.x+k.collbox.w/2+(k.headhurtbox.x*k.flip), k.pos.y+k.headhurtbox.y,
 
k.headhurtbox.w, k.headhurtbox.h) and
 
k.washurt <= 0 and k.wasbadlyhurt <= 0 then -- <= here!
 
k.isdirty = true
 
k.damage = ent.headhitbox.damage
 
if k.animation then k.animation.frame = 0 end
 
ent.headhitbox.isactive = false
 
else
 
k.isdirty = false
 
ent.headhitbox.isactive = false
 
end
 
elseif not (ent.isactionjumppunch1 or ent.isactionjump1) and (k.isactionjumppunch1 or k.isactionjumpkick1 or k.isactionjump1) then
 
if checkY(ent.pos.y, k.positionystart, ent.collbox.h + k.collbox.h) and checkCollision(
 
ent.pos.x+ent.collbox.w/2+(ent.headhitbox.x*ent.flip), ent.pos.y+ent.headhitbox.y,
 
ent.headhitbox.w, ent.headhitbox.h,
 
k.pos.x+k.collbox.w/2+(k.headhurtbox.x*k.flip), k.pos.y+k.headhurtbox.y,
 
k.headhurtbox.w, k.headhurtbox.h) and
 
k.washurt <= 0 and k.wasbadlyhurt <= 0 then -- <= here!
 
k.isdirty = true
 
k.damage = ent.headhitbox.damage
 
if k.animation then k.animation.frame = 0 end
 
ent.headhitbox.isactive = false
 
else
 
k.isdirty = false
 
ent.headhitbox.isactive = false
 
end
 
else
 
if checkY(ent.pos.y, k.pos.y, ent.collbox.h+k.collbox.h) and checkCollision(
 
ent.pos.x+ent.collbox.w/2+(ent.headhitbox.x*ent.flip), ent.pos.y+ent.headhitbox.y,
 
ent.headhitbox.w, ent.headhitbox.h,
 
k.pos.x+k.collbox.w/2+(k.headhurtbox.x*k.flip), k.pos.y+k.headhurtbox.y,
 
k.headhurtbox.w, k.headhurtbox.h) and
 
k.washurt <= 0 and k.wasbadlyhurt <= 0 then -- <= here!
 
k.isdirty = true
 
k.damage = ent.headhitbox.damage
 
if k.animation then k.animation.frame = 0 end
 
ent.headhitbox.isactive = false
 
else
 
k.isdirty = false
 
ent.headhitbox.isactive = false
 
 
end
 
end
 
end
 
end
end
+
if not ent.body.isonfloor then
elseif ent.spinehitbox and ent.spinehitbox.isactive then -- SPINE
+
if ent.positionystart < k.positionystart and -- ent is behind
for k, _ in pairs(self.spriteslist) do
+
ent.spritelayer:getChildIndex(ent.sprite) > k.spritelayer:getChildIndex(k.sprite) then -- sprite is in front
-- filter out unwanted collisions (player1 vs player1, nme vs nme, ... (you choose!)
+
ent.spritelayer:swapChildren(ent.sprite, k.sprite)
if (ent.isplayer1 and k.isplayer1) or
 
ent.isdestructibleobject or -- destructible objects only receive damage
 
(ent.iscollectible or k.iscollectible) or -- don't check for collectibles
 
(ent.isnme and k.isnme) or
 
(ent.isnme and k.isdestructibleobject) then -- nmes don't destroy objects, you choose
 
-- nothing here!
 
-- check spine collisions according to actors action and use actor y or actor positionystart
 
-- this prevents collisions between wrong hitbox and hurtbox (actors too far away on y axis)
 
elseif (ent.isactionjumpkick1 or ent.isactionjump1) and (k.isactionjumppunch1 or k.isactionjumpkick1 or k.isactionjump1) then
 
if checkY(ent.positionystart, k.positionystart, ent.collbox.h + k.collbox.h) and checkCollision(
 
ent.pos.x+ent.collbox.w/2+(ent.spinehitbox.x*ent.flip), ent.pos.y+ent.spinehitbox.y,
 
ent.spinehitbox.w, ent.spinehitbox.h,
 
k.pos.x+k.collbox.w/2+(k.spinehurtbox.x*k.flip), k.pos.y+k.spinehurtbox.y,
 
k.spinehurtbox.w, k.spinehurtbox.h) and
 
k.washurt <= 0 and k.wasbadlyhurt <= 0 then -- <= here!
 
k.isdirty = true
 
k.damage = ent.spinehitbox.damage
 
if k.animation then k.animation.frame = 0 end
 
ent.spinehitbox.isactive = false
 
else
 
k.isdirty = false
 
ent.spinehitbox.isactive = false
 
end
 
elseif (ent.isactionjumpkick1 or ent.isactionjump1) and not (k.isactionjumppunch1 or k.isactionjumpkick1 or k.isactionjump1) then
 
if checkY(ent.positionystart, k.pos.y, ent.collbox.h + k.collbox.h) and checkCollision(
 
ent.pos.x+ent.collbox.w/2+(ent.spinehitbox.x*ent.flip), ent.pos.y+ent.spinehitbox.y,
 
ent.spinehitbox.w, ent.spinehitbox.h,
 
k.pos.x+k.collbox.w/2+(k.spinehurtbox.x*k.flip), k.pos.y+k.spinehurtbox.y,
 
k.spinehurtbox.w, k.spinehurtbox.h) and
 
k.washurt <= 0 and k.wasbadlyhurt <= 0 then -- <= here!
 
k.isdirty = true
 
k.damage = ent.spinehitbox.damage
 
if k.animation then k.animation.frame = 0 end
 
ent.spinehitbox.isactive = false
 
else
 
k.isdirty = false
 
ent.spinehitbox.isactive = false
 
end
 
elseif not (ent.isactionjumpkick1 or ent.isactionjump1) and (k.isactionjumppunch1 or k.isactionjumpkick1 or k.isactionjump1) then
 
if checkY(ent.pos.y, k.positionystart, ent.collbox.h + k.collbox.h) and checkCollision(
 
ent.pos.x+ent.collbox.w/2+(ent.spinehitbox.x*ent.flip), ent.pos.y+ent.spinehitbox.y,
 
ent.spinehitbox.w, ent.spinehitbox.h,
 
k.pos.x+k.collbox.w/2+(k.spinehurtbox.x*k.flip), k.pos.y+k.spinehurtbox.y,
 
k.spinehurtbox.w, k.spinehurtbox.h) and
 
k.washurt <= 0 and k.wasbadlyhurt <= 0 then -- <= here!
 
k.isdirty = true
 
k.damage = ent.spinehitbox.damage
 
if k.animation then k.animation.frame = 0 end
 
ent.spinehitbox.isactive = false
 
else
 
k.isdirty = false
 
ent.spinehitbox.isactive = false
 
 
end
 
end
 
else
 
else
if checkY(ent.pos.y, k.pos.y, ent.collbox.h + k.collbox.h) and checkCollision(
+
if ent.pos.y < k.pos.y and -- ent is behind
ent.pos.x+ent.collbox.w/2+(ent.spinehitbox.x*ent.flip), ent.pos.y+ent.spinehitbox.y,
+
ent.spritelayer:getChildIndex(ent.sprite) > k.spritelayer:getChildIndex(k.sprite) then -- sprite is in front
ent.spinehitbox.w, ent.spinehitbox.h,
+
ent.spritelayer:swapChildren(ent.sprite, k.sprite)
k.pos.x+k.collbox.w/2+(k.spinehurtbox.x*k.flip), k.pos.y+k.spinehurtbox.y,
 
k.spinehurtbox.w, k.spinehurtbox.h) and
 
k.washurt <= 0 and k.wasbadlyhurt <= 0 then -- <= here!
 
k.isdirty = true
 
k.damage = ent.spinehitbox.damage
 
if k.animation then k.animation.frame = 0 end
 
ent.spinehitbox.isactive = false
 
else
 
k.isdirty = false
 
ent.spinehitbox.isactive = false
 
 
end
 
end
 
end
 
end
 
end
 
end
 +
Core.yield(0.5)
 
end
 
end
 +
Core.asyncCall(fun) -- profiler seems to be faster without asyncCall (because of pairs traversing?)
 
end
 
end
 
</syntaxhighlight>
 
</syntaxhighlight>
  
This System checks if a hitbox and a hurtbox overlap then deals damages to the hurt actor:
+
Finally this System sorts the actors on the y axis:
 
* runs once on init and '''every game loop''' (''process'')
 
* runs once on init and '''every game loop''' (''process'')
* in ''init'' we pass the list of all the actors (''spriteslist'' declared in '''LevelX''')
+
* there is a distinction between the actor being on floor and jumping
* first we check if the actors are within range on the y axis with '''checkY'''
 
* then we check if a hitbox and a hurtbox collide with '''checkCollision'''
 
* when a hitbox is active, meaning an actor is in attack mode, we check if it's a head hit or a body hit
 
* we also separate the ground attacks from the jump attacks
 
  
== sCollision.lua ==
+
== XXX.lua ==
 
"'''sCollision.lua'''" in the '''"_S"''' folder. The code:
 
"'''sCollision.lua'''" in the '''"_S"''' folder. The code:
 
<syntaxhighlight lang="lua">
 
<syntaxhighlight lang="lua">
SCollision = Core.class()
 
 
function SCollision:init(xtiny, xbworld) -- tiny function
 
xtiny.processingSystem(self) -- called once on init and every update
 
self.bworld = xbworld
 
end
 
 
function SCollision:filter(ent) -- tiny function
 
return ent.collbox and ent.body
 
end
 
 
function SCollision:onAdd(ent) -- tiny function
 
end
 
 
function SCollision:onRemove(ent) -- tiny function
 
end
 
 
local col -- cbump perfs?
 
function SCollision:process(ent, dt) -- tiny function
 
local function fun()
 
-- collision filter
 
local function collisionfilter(item, other) -- "touch", "cross", "slide", "bounce"
 
if item.isactionjump1 or other.isactionjump1 or
 
item.isactionjumppunch1 or other.isactionjumppunch1 or
 
item.isactionjumpkick1 or other.isactionjumpkick1 or
 
item.isdead or other.isdead then return nil
 
elseif item.iscollectible or other.iscollectible then return "cross"
 
end return "slide"
 
end
 
-- cbump
 
local goalx = ent.pos.x + ent.body.vx * dt
 
local goaly = ent.pos.y + ent.body.vy
 
local nextx, nexty, collisions, len = self.bworld:move(ent, goalx, goaly, collisionfilter)
 
-- collisions
 
for i = 1, len do
 
col = collisions[i]
 
if col.item.iscollectible and col.other.isplayer1 then col.item.isdirty = true
 
elseif col.item.isplayer1 and col.other.iscollectible then col.other.isdirty = true
 
end
 
end
 
-- move & flip
 
ent.pos = vector(nextx, nexty)
 
ent.sprite:setPosition(ent.pos + vector(ent.collbox.w/2, -ent.h/2+ent.collbox.h/2))
 
ent.animation.bmp:setScale(ent.sx * ent.flip, ent.sy)
 
Core.yield(0.1)
 
end
 
Core.asyncCall(fun)
 
end
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  

Revision as of 02:46, 24 November 2024

The Systems 3

A couple more systems to add and we are done. Those systems apply on the breakable objects and the collectibles.

sDestructibleObjects.lua

Please create a file "sDestructibleObjects.lua" in the "_S" folder and the code:

SDestructibleObjects = Core.class()

local random, cos, sin = math.random, math.cos, math.sin
local insert = table.insert

function SDestructibleObjects:init(xtiny, xbworld) -- tiny function
	self.tiny = xtiny -- ref so we can remove entities from tiny system
	self.tiny.processingSystem(self) -- called once on init and every update
	self.bworld = xbworld
	-- sfx
	self.snd = Sound.new("audio/sfx/footstep/Forest02.wav")
	self.channel = self.snd:play(0, false, true)
end

function SDestructibleObjects:filter(ent) -- tiny function
	return ent.isdestructibleobject
end

function SDestructibleObjects:onAdd(ent) -- tiny function
end

function SDestructibleObjects:onRemove(ent) -- tiny function
	-- spawn a random collectible: ECollectible:init(xspritelayer, xpos)
	local function fun()
		local el = ECollectible.new(ent.spritelayer, ent.pos+vector(ent.collbox.w/4, -1*ent.collbox.h))
		self.tiny.tworld:addEntity(el)
		self.bworld:add(el, el.pos.x, el.pos.y, el.collbox.w, el.collbox.h)
		Core.yield(1)
	end
	Core.asyncCall(fun)
	self.bworld:remove(ent) -- remove collision box from cbump world here!
end

function SDestructibleObjects:process(ent, dt) -- tiny function
	local function EffectExplode(s, scale, pos, r, speed, texture)
		local p = Particles.new()
		p:setPosition(pos)
		p:setTexture(texture)
		p:setScale(scale)
		s:addChild(p)
		local parts = {}
		for i = 1, 6 do -- 8
			local a = random()*6.3
			local dx, dy = cos(a), sin(a)
			local sr = random()*r
			local px, py = dx*sr, dy*sr
			local ss = (random()+0.5) * (speed or 1)
			insert(parts,
				{
					x = px, y = py,
					speedX = dx * ss,
					speedY = dy * ss,
					speedAngular = random()*4 - 2,
					decayAlpha = random()*0.04 + 0.95,
					ttl = 32, -- 500
					size = random()*10 + 20,
				}
			)
		end
		p:addParticles(parts)
		Core.yield(1)
		p:removeFromParent()
	end
	-- hurt fx
	if ent.washurt and ent.washurt > 0 then
		ent.washurt -= 1
		if ent.washurt < ent.recovertimer/2 then
			if ent.hitfx then ent.hitfx:setVisible(false) end
		end
		if ent.washurt <= 0 then
			ent.sprite:setColorTransform(1, 1, 1, 1)
		end
	end
	if ent.isdirty then -- hit
		self.channel = self.snd:play()
		if self.channel then self.channel:setVolume(g_sfxvolume*0.01) end
		ent.hitfx:setVisible(true)
		ent.hitfx:setPosition(ent.pos + vector(ent.headhurtbox.x+4, ent.headhurtbox.y))
		ent.spritelayer:addChild(ent.hitfx)
		ent.currhealth -= ent.damage
		ent.washurt = ent.recovertimer -- timer for a flash effect
		ent.sprite:setColorTransform(1, 1, 1, 3) -- a flash effect
		ent.isdirty = false
		if ent.currhealth <= 0 then
			--EffectExplode(s, scale, pos, r, speed, texture)
			Core.asyncCall(EffectExplode, ent.spritelayer, 2,
				ent.pos+vector(ent.collbox.w/2, -ent.h/2), 4, 2,
				Texture.new("gfx/fx/fxBarrel_02_0011.png"))
			ent.spritelayer:removeChild(ent.hitfx)
			self.tiny.tworld:removeEntity(ent) -- sprite is removed in SDrawable
			self.tiny.numberofdestructibleobjects -= 1
		end
	end
end

The System removes a breakable object when destroyed and spawn a collectible in the onRemove function:

  • it runs every frame
  • it affects only entities with an isdestructibleobject id
  • on hurt adds an hit fx
  • on destroyed adds particles
  • onRemove spawns a collectible

sCollectible.lua

"sCollectible.lua" in the "_S" folder. The code:

SCollectible = Core.class()

local random = math.random

function SCollectible: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.new("audio/sfx/sfx_coin_double1.wav")
	self.channel = self.snd:play(0, false, true)
end

function SCollectible:filter(ent) -- tiny function
	return ent.iscollectible
end

function SCollectible:onAdd(ent) -- tiny function
end

function SCollectible:onRemove(ent) -- tiny function
	self.bworld:remove(ent) -- remove collision box from cbump world here!
end

function SCollectible: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
		self.channel = self.snd:play()
		if self.channel then self.channel:setVolume(g_sfxvolume*0.01) end
--		ent.hitfx:setVisible(true)
--		ent.hitfx:setPosition(ent.pos.x+ent.headhurtbox.x, ent.y+ent.headhurtbox.y)
--		ent.spritelayer:addChild(ent.hitfx)
--		ent.currhealth -= ent.damage
--		ent.washurt = ent.recovertimer -- timer for a flash effect
		if random(100) > 50 then
			if self.player1.currjumps < 0 then self.player1.currjumps = 0 end
			self.player1.currjumps += 3
			self.tiny.hudcurrjumps:setText("JUMPS: "..self.player1.currjumps)
		else
			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
		end
		ent.sprite:setColorTransform(0, 2, 0, 3) -- the flash effect (a bright color)
		ent.isdirty = false
--[[
		if ent.currhealth <= 0 then
			ent.hitfx:setColorTransform(3, 0, 0, random(1, 3)/10)
			ent.hitfx:setY(ent.hitfx:getY()+ent.h/1.1) -- magik XXX
			ent.hitfx:setRotation(random(360))
			ent.hitfx:setScale(random(5, 10)/10)
			ent.bgfxlayer:addChild(ent.hitfx)
			self.tiny.tworld:removeEntity(ent) -- sprite is removed in SDrawable
--			self.tiny.numberofnmes -= 1
		end
]]
		self.tiny.tworld:removeEntity(ent) -- sprite is removed in SDrawable
	end
end

This System spawns a collectible:

  • runs once on init and every game loop (process)
  • there are two kind of collectibles: health and jump attacks (updated in the HUD)

sSpritesSorting.lua.lua

"sSpritesSorting.lua.lua" in the "_S" folder. The code:

SSpritesSorting = Core.class()

function SSpritesSorting:init(xtiny) -- tiny function
	xtiny.processingSystem(self) -- called once on init and every update
	self.spriteslist = xtiny.spriteslist
end

function SSpritesSorting:filter(ent) -- tiny function
	return ent.sprite
end

function SSpritesSorting:onAdd(ent) -- tiny function
--	print("SSpritesSorting added", ent)
	self.spriteslist[ent] = true
end

function SSpritesSorting:onRemove(ent) -- tiny function
--	print("SSpritesSorting removed", ent)
	self.spriteslist[ent] = nil
end

local p1rangetoofar = myappwidth*0.5 -- save some CPU
function SSpritesSorting:process(ent, dt) -- tiny function
	local function fun()
		for k, _ in pairs(self.spriteslist) do
			if ent.currlives <= 0 or k.currlives <= 0 then -- don't sort if dead
				return
			end
			if k.isplayer1 then -- don't sort out of range actors to save frames
				if -(k.pos.x-ent.pos.x)<>(k.pos.x-ent.pos.x) > p1rangetoofar then
					return
				end
			end
			if not ent.body.isonfloor then
				if ent.positionystart < k.positionystart and -- ent is behind
					ent.spritelayer:getChildIndex(ent.sprite) > k.spritelayer:getChildIndex(k.sprite) then -- sprite is in front
					ent.spritelayer:swapChildren(ent.sprite, k.sprite)
				end
			else
				if ent.pos.y < k.pos.y and -- ent is behind
					ent.spritelayer:getChildIndex(ent.sprite) > k.spritelayer:getChildIndex(k.sprite) then -- sprite is in front
					ent.spritelayer:swapChildren(ent.sprite, k.sprite)
				end
			end
		end
		Core.yield(0.5)
	end
	Core.asyncCall(fun) -- profiler seems to be faster without asyncCall (because of pairs traversing?)
end

Finally this System sorts the actors on the y axis:

  • runs once on init and every game loop (process)
  • there is a distinction between the actor being on floor and jumping

XXX.lua

"sCollision.lua" in the "_S" folder. The code:

This System uses the Bump plugin to check for collisions between the actors collision boxes:

  • runs once on init and every game loop (process)
  • in init we pass the Bump world the actors live in
  • we define Bump collision filter
  • we check if player1 collides with a collectible actor and tag the collectible as dirty
  • finally we set the actor position and flip its bitmap in the direction it is going
  • I experimented with asyncCall to test if we could gain some frames per second ;-)

Next?

Next we add the systems for the breakable objects and the collectibles and we are almost done with the game!


Prev.: Tuto tiny-ecs beatemup Part 10 Systems 2
Next: Tuto tiny-ecs beatemup Part 12 XXX


Tutorial - tiny-ecs beatemup