Tuto tiny-ecs beatemup Part 11 Systems 3
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