Difference between revisions of "Tuto tiny-ecs beatemup Part 11 Systems 3"
(wip) |
|||
(5 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
__TOC__ | __TOC__ | ||
− | == The Systems | + | == The Systems 3 == |
− | + | A couple more systems to add and we are done. We also add the '''Debug Systems''' to help us visualize all the boxes (hitbox, hurtbox, collsion box, ...). | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | == | + | == sDestructibleObjects.lua == |
− | + | Please create a file "'''sDestructibleObjects.lua'''" in the '''"_S"''' folder and the code: | |
<syntaxhighlight lang="lua"> | <syntaxhighlight lang="lua"> | ||
− | + | SDestructibleObjects = Core.class() | |
− | function | + | local random, cos, sin = math.random, math.cos, math.sin |
− | xtiny.processingSystem(self) -- called once on init and every | + | local insert = table.insert |
− | self. | + | |
− | self.channel = self. | + | 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 | + | function SDestructibleObjects:filter(ent) -- tiny function |
− | return ent. | + | return ent.isdestructibleobject |
end | end | ||
− | function | + | function SDestructibleObjects:onAdd(ent) -- tiny function |
end | end | ||
− | function | + | function SDestructibleObjects:onRemove(ent) -- tiny function |
− | ent. | + | -- 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 | ||
− | + | function SDestructibleObjects:process(ent, dt) -- tiny function | |
− | 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 | |
− | 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 + 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. | + | ent.spritelayer:removeChild(ent.hitfx) |
− | + | self.tiny.tworld:removeEntity(ent) -- sprite is removed in SDrawable | |
− | + | self.tiny.numberofdestructibleobjects -= 1 | |
− | |||
− | |||
− | |||
− | |||
− | -- | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
− | |||
end | end | ||
end | end | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | + | 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 '' | + | * 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: |
<syntaxhighlight lang="lua"> | <syntaxhighlight lang="lua"> | ||
− | + | SCollectible = Core.class() | |
local random = math.random | local random = math.random | ||
− | function | + | function SCollectible:init(xtiny, xbump, xplayer1) -- tiny function |
− | self.tiny = xtiny -- | + | 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. | + | 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 | + | function SCollectible:filter(ent) -- tiny function |
− | return ent. | + | return ent.iscollectible |
end | end | ||
− | function | + | function SCollectible:onAdd(ent) -- tiny function |
end | end | ||
− | function | + | function SCollectible:onRemove(ent) -- tiny function |
+ | self.bworld:remove(ent) -- remove collision box from cbump world here! | ||
end | end | ||
− | + | function SCollectible:process(ent, dt) -- tiny function | |
− | function | + | if ent.isdirty then -- hit |
− | + | local function map(v, minSrc, maxSrc, minDst, maxDst, clampValue) | |
− | if | + | local newV = (v - minSrc) / (maxSrc - minSrc) * (maxDst - minDst) + minDst |
− | + | return not clampValue and newV or clamp(newV, minDst >< maxDst, minDst <> maxDst) | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | 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) | |
− | |||
− | |||
− | |||
− | if | ||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
end | end | ||
− | + | ent.sprite:setColorTransform(0, 2, 0, 3) -- the flash effect (a bright color) | |
− | + | ent.isdirty = false | |
− | + | --[[ | |
− | ent. | + | 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 | end | ||
− | + | ]] | |
− | + | self.tiny.tworld:removeEntity(ent) -- sprite is removed in SDrawable | |
− | + | end | |
− | + | end | |
− | + | </syntaxhighlight> | |
− | + | ||
− | -- | + | 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) | |
+ | |||
+ | '''There are quite a bit of commented code you can delete as this Entity is immediately removed''' | ||
+ | |||
+ | == sSpritesSorting.lua == | ||
+ | "'''sSpritesSorting.lua'''" in the '''"_S"''' folder. The code: | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | 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 | end | ||
− | if | + | 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 | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
− | + | if not ent.body.isonfloor then | |
− | + | if ent.positionystart < k.positionystart and -- ent is behind | |
− | if | + | ent.spritelayer:getChildIndex(ent.sprite) > k.spritelayer:getChildIndex(k.sprite) then -- sprite is in front |
− | + | ent.spritelayer:swapChildren(ent.sprite, k.sprite) | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
− | if ent. | + | 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 | 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> | ||
− | + | 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'') | ||
− | * | + | * there is a distinction between the actor being on floor and jumping |
− | + | ||
− | + | == '''DEBUG SYSTEMS''' == | |
− | + | It would be difficult to accurately position the hit and hurt boxes, the collision boxes, ... without visual cues. | |
− | |||
− | == | + | Debug systems help us with the above. Fortunately they are small and quite simple. |
− | "''' | + | |
+ | === sDebugCollision.lua === | ||
+ | "'''sDebugCollision.lua'''" in the '''"_S"''' folder. The code: | ||
<syntaxhighlight lang="lua"> | <syntaxhighlight lang="lua"> | ||
− | + | SDebugCollision = Core.class() | |
− | function | + | function SDebugCollision:init(xtiny) -- tiny function |
xtiny.processingSystem(self) -- called once on init and every update | xtiny.processingSystem(self) -- called once on init and every update | ||
− | |||
end | end | ||
− | function | + | function SDebugCollision:filter(ent) -- tiny function |
− | return ent. | + | return ent.collbox |
+ | end | ||
+ | |||
+ | function SDebugCollision:onAdd(ent) -- tiny function | ||
+ | local debugcolor = 0xff00ff | ||
+ | if ent.isplayer1 then debugcolor = 0x00ffff end | ||
+ | ent.debug = Pixel.new(debugcolor, 0.25, ent.collbox.w, ent.collbox.h) | ||
+ | ent.spritelayer:addChild(ent.debug) | ||
end | end | ||
− | function | + | function SDebugCollision:onRemove(ent) -- tiny function |
+ | ent.spritelayer:removeChild(ent.debug) | ||
end | end | ||
− | function | + | function SDebugCollision:process(ent, dt) -- tiny function |
+ | -- ent.debug:setPosition(ent.x, ent.y) | ||
+ | ent.debug:setPosition(ent.pos) | ||
end | end | ||
+ | </syntaxhighlight> | ||
− | function | + | This System displays the actors collision box. |
− | + | ||
− | + | === sDebugHitBoxNme.lua === | |
+ | "'''sDebugHitBoxNme.lua'''" in the '''"_S"''' folder. The code: | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | SDebugHitBoxNme = Core.class() | ||
+ | |||
+ | function SDebugHitBoxNme:init(xtiny, xshowbox) -- tiny function | ||
+ | xtiny.processingSystem(self) -- called once on init and every update | ||
+ | self.showbox = xshowbox | ||
+ | end | ||
+ | |||
+ | function SDebugHitBoxNme:filter(ent) -- tiny function | ||
+ | return (ent.headhitboxattack1 or ent.headhitboxattack2 or ent.spinehitboxattack1 or ent.spinehitboxattack2 or | ||
+ | ent.headhitboxjattack1 or ent.spinehitboxjattack1) and not ent.isplayer1 | ||
+ | end | ||
+ | |||
+ | function SDebugHitBoxNme:onAdd(ent) -- tiny function | ||
+ | -- debugheadhitboxp1 | ||
+ | if ent.headhitboxattack1 then | ||
+ | ent.debugheadhitboxp1 = Pixel.new(0xaa0000, 0.5, ent.headhitboxattack1.w, ent.headhitboxattack1.h) | ||
+ | ent.debugheadhitboxp1:setAnchorPoint(0.5, 0.5) | ||
+ | ent.spritelayer:addChild(ent.debugheadhitboxp1) | ||
+ | ent.debugheadhitboxp1:setVisible(self.showbox[1]) -- granular debugging | ||
+ | end | ||
+ | -- debugheadhitboxp2 | ||
+ | if ent.headhitboxattack2 then | ||
+ | ent.debugheadhitboxp2 = Pixel.new(0xaa5500, 0.5, ent.headhitboxattack2.w, ent.headhitboxattack2.h) | ||
+ | ent.debugheadhitboxp2:setAnchorPoint(0.5, 0.5) | ||
+ | ent.spritelayer:addChild(ent.debugheadhitboxp2) | ||
+ | ent.debugheadhitboxp2:setVisible(self.showbox[2]) -- granular debugging | ||
+ | end | ||
+ | -- debugspinehitboxk1 | ||
+ | if ent.spinehitboxattack1 then | ||
+ | ent.debugspinehitboxk1 = Pixel.new(0x005500, 0.5, ent.spinehitboxattack1.w, ent.spinehitboxattack1.h) | ||
+ | ent.debugspinehitboxk1:setAnchorPoint(0.5, 0.5) | ||
+ | ent.spritelayer:addChild(ent.debugspinehitboxk1) | ||
+ | ent.debugspinehitboxk1:setVisible(self.showbox[3]) -- granular debugging | ||
+ | end | ||
+ | -- debugspinehitboxk2 | ||
+ | if ent.spinehitboxattack2 then | ||
+ | ent.debugspinehitboxk2 = Pixel.new(0x00aa00, 0.5, ent.spinehitboxattack2.w, ent.spinehitboxattack2.h) | ||
+ | ent.debugspinehitboxk2:setAnchorPoint(0.5, 0.5) | ||
+ | ent.spritelayer:addChild(ent.debugspinehitboxk2) | ||
+ | ent.debugspinehitboxk2:setVisible(self.showbox[4]) -- granular debugging | ||
end | end | ||
− | + | -- debugheadhitboxjp1 | |
− | + | if ent.headhitboxjattack1 then | |
− | + | ent.debugheadhitboxjp1 = Pixel.new(0xaaaa00, 0.5, ent.headhitboxjattack1.w, ent.headhitboxjattack1.h) | |
− | + | ent.debugheadhitboxjp1:setAnchorPoint(0.5, 0.5) | |
− | + | ent.spritelayer:addChild(ent.debugheadhitboxjp1) | |
+ | ent.debugheadhitboxjp1:setVisible(self.showbox[5]) -- granular debugging | ||
end | end | ||
− | if ent. | + | -- debugspinehitboxjk1 |
− | + | if ent.spinehitboxjattack1 then | |
− | + | ent.debugspinehitboxjk1 = Pixel.new(0x00ff00, 0.5, ent.spinehitboxjattack1.w, ent.spinehitboxjattack1.h) | |
− | + | ent.debugspinehitboxjk1:setAnchorPoint(0.5, 0.5) | |
− | + | ent.spritelayer:addChild(ent.debugspinehitboxjk1) | |
− | + | ent.debugspinehitboxjk1:setVisible(self.showbox[6]) -- granular debugging | |
− | + | end | |
− | + | end | |
− | + | ||
− | + | function SDebugHitBoxNme:onRemove(ent) -- tiny function | |
− | + | if ent.headhitboxattack1 then ent.spritelayer:removeChild(ent.debugheadhitboxp1) end | |
− | + | if ent.headhitboxattack2 then ent.spritelayer:removeChild(ent.debugheadhitboxp2) end | |
− | + | if ent.spinehitboxattack1 then ent.spritelayer:removeChild(ent.debugspinehitboxk1) end | |
− | + | if ent.spinehitboxattack2 then ent.spritelayer:removeChild(ent.debugspinehitboxk2) end | |
− | + | if ent.headhitboxjattack1 then ent.spritelayer:removeChild(ent.debugheadhitboxjp1) end | |
− | + | if ent.spinehitboxjattack1 then ent.spritelayer:removeChild(ent.debugspinehitboxjk1) end | |
− | + | end | |
− | + | ||
− | + | function SDebugHitBoxNme:process(ent, dt) -- tiny function | |
− | + | local function fun() | |
− | + | if ent.headhitboxattack1 then | |
− | + | ent.debugheadhitboxp1:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.headhitboxattack1.x*ent.flip), ent.headhitboxattack1.y)) | |
− | + | end | |
− | + | if ent.headhitboxattack2 then | |
− | + | ent.debugheadhitboxp2:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.headhitboxattack2.x*ent.flip), ent.headhitboxattack2.y)) | |
− | + | end | |
− | + | if ent.spinehitboxattack1 then | |
− | + | ent.debugspinehitboxk1:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.spinehitboxattack1.x*ent.flip), ent.spinehitboxattack1.y)) | |
− | + | end | |
− | + | if ent.spinehitboxattack2 then | |
− | + | ent.debugspinehitboxk2:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.spinehitboxattack2.x*ent.flip), ent.spinehitboxattack2.y)) | |
− | + | end | |
− | + | if ent.headhitboxjattack1 then | |
− | + | ent.debugheadhitboxjp1:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.headhitboxjattack1.x*ent.flip), ent.headhitboxjattack1.y)) | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
− | + | if ent.spinehitboxjattack1 then | |
− | + | ent.debugspinehitboxjk1:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.spinehitboxjattack1.x*ent.flip), ent.spinehitboxjattack1.y)) | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
+ | Core.yield(1) | ||
end | end | ||
+ | Core.asyncCall(fun) | ||
end | end | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | This System | + | This System displays the enemies hit boxes when available. |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | == | + | === sDebugHitBoxPlayer.lua === |
− | "''' | + | "'''sDebugHitBoxPlayer.lua'''" in the '''"_S"''' folder. The code: |
<syntaxhighlight lang="lua"> | <syntaxhighlight lang="lua"> | ||
− | + | SDebugHitBoxPlayer = Core.class() | |
− | function | + | function SDebugHitBoxPlayer:init(xtiny, xshowbox) -- tiny function |
xtiny.processingSystem(self) -- called once on init and every update | xtiny.processingSystem(self) -- called once on init and every update | ||
− | self. | + | self.showbox = xshowbox |
end | end | ||
− | function | + | function SDebugHitBoxPlayer:filter(ent) -- tiny function |
− | return ent. | + | return (ent.headhitboxattack1 or ent.headhitboxattack2 or ent.spinehitboxattack1 or ent.spinehitboxattack2 or |
+ | ent.headhitboxjattack1 or ent.spinehitboxjattack1) and not ent.isnme | ||
end | end | ||
− | function | + | function SDebugHitBoxPlayer:onAdd(ent) -- tiny function |
+ | -- debugheadhitboxp1 | ||
+ | if ent.headhitboxattack1 then | ||
+ | ent.debugheadhitboxp1 = Pixel.new(0xaa0000, 0.5, ent.headhitboxattack1.w, ent.headhitboxattack1.h) | ||
+ | ent.debugheadhitboxp1:setAnchorPoint(0.5, 0.5) | ||
+ | ent.spritelayer:addChild(ent.debugheadhitboxp1) | ||
+ | ent.debugheadhitboxp1:setVisible(self.showbox[1]) -- granular debugging | ||
+ | end | ||
+ | -- debugheadhitboxp2 | ||
+ | if ent.headhitboxattack2 then | ||
+ | ent.debugheadhitboxp2 = Pixel.new(0xaa5500, 0.5, ent.headhitboxattack2.w, ent.headhitboxattack2.h) | ||
+ | ent.debugheadhitboxp2:setAnchorPoint(0.5, 0.5) | ||
+ | ent.spritelayer:addChild(ent.debugheadhitboxp2) | ||
+ | ent.debugheadhitboxp2:setVisible(self.showbox[2]) -- granular debugging | ||
+ | end | ||
+ | -- debugspinehitboxk1 | ||
+ | if ent.spinehitboxattack1 then | ||
+ | ent.debugspinehitboxk1 = Pixel.new(0x005500, 0.5, ent.spinehitboxattack1.w, ent.spinehitboxattack1.h) | ||
+ | ent.debugspinehitboxk1:setAnchorPoint(0.5, 0.5) | ||
+ | ent.spritelayer:addChild(ent.debugspinehitboxk1) | ||
+ | ent.debugspinehitboxk1:setVisible(self.showbox[3]) -- granular debugging | ||
+ | end | ||
+ | -- debugspinehitboxk2 | ||
+ | if ent.spinehitboxattack2 then | ||
+ | ent.debugspinehitboxk2 = Pixel.new(0x00aa00, 0.5, ent.spinehitboxattack2.w, ent.spinehitboxattack2.h) | ||
+ | ent.debugspinehitboxk2:setAnchorPoint(0.5, 0.5) | ||
+ | ent.spritelayer:addChild(ent.debugspinehitboxk2) | ||
+ | ent.debugspinehitboxk2:setVisible(self.showbox[4]) -- granular debugging | ||
+ | end | ||
+ | -- debugheadhitboxjp1 | ||
+ | if ent.headhitboxjattack1 then | ||
+ | ent.debugheadhitboxjp1 = Pixel.new(0xaaaa00, 0.5, ent.headhitboxjattack1.w, ent.headhitboxjattack1.h) | ||
+ | ent.debugheadhitboxjp1:setAnchorPoint(0.5, 0.5) | ||
+ | ent.spritelayer:addChild(ent.debugheadhitboxjp1) | ||
+ | ent.debugheadhitboxjp1:setVisible(self.showbox[5]) -- granular debugging | ||
+ | end | ||
+ | -- debugspinehitboxjk1 | ||
+ | if ent.spinehitboxjattack1 then | ||
+ | ent.debugspinehitboxjk1 = Pixel.new(0x00ff00, 0.5, ent.spinehitboxjattack1.w, ent.spinehitboxjattack1.h) | ||
+ | ent.debugspinehitboxjk1:setAnchorPoint(0.5, 0.5) | ||
+ | ent.spritelayer:addChild(ent.debugspinehitboxjk1) | ||
+ | ent.debugspinehitboxjk1:setVisible(self.showbox[6]) -- granular debugging | ||
+ | end | ||
end | end | ||
− | function | + | function SDebugHitBoxPlayer:onRemove(ent) -- tiny function |
+ | if ent.headhitboxattack1 then ent.spritelayer:removeChild(ent.debugheadhitboxp1) end | ||
+ | if ent.headhitboxattack2 then ent.spritelayer:removeChild(ent.debugheadhitboxp2) end | ||
+ | if ent.spinehitboxattack1 then ent.spritelayer:removeChild(ent.debugspinehitboxk1) end | ||
+ | if ent.spinehitboxattack2 then ent.spritelayer:removeChild(ent.debugspinehitboxk2) end | ||
+ | if ent.headhitboxjattack1 then ent.spritelayer:removeChild(ent.debugheadhitboxjp1) end | ||
+ | if ent.spinehitboxjattack1 then ent.spritelayer:removeChild(ent.debugspinehitboxjk1) end | ||
end | end | ||
− | + | function SDebugHitBoxPlayer:process(ent, dt) -- tiny function | |
− | function | ||
local function fun() | local function fun() | ||
− | + | if ent.headhitboxattack1 then | |
− | + | ent.debugheadhitboxp1:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.headhitboxattack1.x*ent.flip), ent.headhitboxattack1.y)) | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
− | + | if ent.headhitboxattack2 then | |
− | + | ent.debugheadhitboxp2:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.headhitboxattack2.x*ent.flip), ent.headhitboxattack2.y)) | |
− | + | end | |
− | + | if ent.spinehitboxattack1 then | |
− | + | ent.debugspinehitboxk1:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.spinehitboxattack1.x*ent.flip), ent.spinehitboxattack1.y)) | |
− | + | end | |
− | + | if ent.spinehitboxattack2 then | |
− | + | ent.debugspinehitboxk2:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.spinehitboxattack2.x*ent.flip), ent.spinehitboxattack2.y)) | |
− | + | end | |
− | + | if ent.headhitboxjattack1 then | |
+ | ent.debugheadhitboxjp1:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.headhitboxjattack1.x*ent.flip), ent.headhitboxjattack1.y)) | ||
+ | end | ||
+ | if ent.spinehitboxjattack1 then | ||
+ | ent.debugspinehitboxjk1:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.spinehitboxjattack1.x*ent.flip), ent.spinehitboxjattack1.y)) | ||
end | end | ||
− | -- | + | Core.yield(1) |
− | ent. | + | end |
− | ent. | + | Core.asyncCall(fun) |
− | ent. | + | end |
− | Core.yield( | + | </syntaxhighlight> |
+ | |||
+ | This System displays the player hit boxes when available. | ||
+ | |||
+ | === sDebugHurtBoxNme.lua === | ||
+ | "'''sDebugHurtBoxNme.lua'''" in the '''"_S"''' folder. The code: | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | SDebugHurtBoxNme = Core.class() | ||
+ | |||
+ | function SDebugHurtBoxNme:init(xtiny) -- tiny function | ||
+ | xtiny.processingSystem(self) -- called once on init and every update | ||
+ | end | ||
+ | |||
+ | function SDebugHurtBoxNme:filter(ent) -- tiny function | ||
+ | return (ent.headhurtbox or ent.spinehurtbox) and not ent.isplayer1 | ||
+ | end | ||
+ | |||
+ | function SDebugHurtBoxNme:onAdd(ent) -- tiny function | ||
+ | -- debugheadhurtbox | ||
+ | ent.debugheadhurtbox = Pixel.new(0x5500ff, 0.5, ent.headhurtbox.w, ent.headhurtbox.h) | ||
+ | ent.debugheadhurtbox:setAnchorPoint(0.5, 0.5) | ||
+ | ent.spritelayer:addChild(ent.debugheadhurtbox) | ||
+ | -- debugspinehurtbox | ||
+ | ent.debugspinehurtbox = Pixel.new(0xff00ff, 0.5, ent.spinehurtbox.w, ent.spinehurtbox.h) | ||
+ | ent.debugspinehurtbox:setAnchorPoint(0.5, 0.5) | ||
+ | ent.spritelayer:addChild(ent.debugspinehurtbox) | ||
+ | end | ||
+ | |||
+ | function SDebugHurtBoxNme:onRemove(ent) -- tiny function | ||
+ | if not ent.isdestructibleobject then -- isdestructibleobject is somehow already removed! | ||
+ | ent.spritelayer:removeChild(ent.debugheadhurtbox) | ||
+ | ent.spritelayer:removeChild(ent.debugspinehurtbox) | ||
+ | end | ||
+ | end | ||
+ | |||
+ | function SDebugHurtBoxNme:process(ent, dt) -- tiny function | ||
+ | local function fun() | ||
+ | ent.debugheadhurtbox:setPosition(ent.pos+vector(ent.collbox.w/2+(ent.headhurtbox.x*ent.flip), ent.headhurtbox.y)) | ||
+ | ent.debugspinehurtbox:setPosition(ent.pos+vector(ent.collbox.w/2+(ent.spinehurtbox.x*ent.flip), ent.spinehurtbox.y)) | ||
+ | Core.yield(1) | ||
end | end | ||
Core.asyncCall(fun) | Core.asyncCall(fun) | ||
Line 597: | Line 519: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | This System | + | This System displays the enemies and breakable objects hurt boxes. |
− | + | ||
− | * | + | === sDebugHurtBoxPlayer.lua === |
− | + | "'''sDebugHurtBoxPlayer.lua'''" in the '''"_S"''' folder. The code: | |
− | + | <syntaxhighlight lang="lua"> | |
− | + | SDebugHurtBoxPlayer = Core.class() | |
− | + | ||
+ | function SDebugHurtBoxPlayer:init(xtiny) -- tiny function | ||
+ | xtiny.processingSystem(self) -- called once on init and every update | ||
+ | end | ||
+ | |||
+ | function SDebugHurtBoxPlayer:filter(ent) -- tiny function | ||
+ | return (ent.headhurtbox or ent.spinehurtbox) and not ent.isnme | ||
+ | end | ||
+ | |||
+ | function SDebugHurtBoxPlayer:onAdd(ent) -- tiny function | ||
+ | -- debugheadhurtbox | ||
+ | ent.debugheadhurtbox = Pixel.new(0x5500ff, 0.5, ent.headhurtbox.w, ent.headhurtbox.h) | ||
+ | ent.debugheadhurtbox:setAnchorPoint(0.5, 0.5) | ||
+ | ent.spritelayer:addChild(ent.debugheadhurtbox) | ||
+ | -- debugspinehurtbox | ||
+ | ent.debugspinehurtbox = Pixel.new(0xff00ff, 0.5, ent.spinehurtbox.w, ent.spinehurtbox.h) | ||
+ | ent.debugspinehurtbox:setAnchorPoint(0.5, 0.5) | ||
+ | ent.spritelayer:addChild(ent.debugspinehurtbox) | ||
+ | end | ||
+ | |||
+ | function SDebugHurtBoxPlayer:onRemove(ent) -- tiny function | ||
+ | ent.spritelayer:removeChild(ent.debugheadhurtbox) | ||
+ | ent.spritelayer:removeChild(ent.debugspinehurtbox) | ||
+ | end | ||
+ | |||
+ | function SDebugHurtBoxPlayer:process(ent, dt) -- tiny function | ||
+ | ent.debugheadhurtbox:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.headhurtbox.x*ent.flip), ent.headhurtbox.y)) | ||
+ | ent.debugspinehurtbox:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.spinehurtbox.x*ent.flip), ent.spinehurtbox.y)) | ||
+ | end | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | This System displays the player hurt boxes. | ||
+ | |||
+ | === sDebugSpriteSorting.lua === | ||
+ | "'''sDebugSpriteSorting.lua'''" in the '''"_S"''' folder. The code: | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | SDebugSpriteSorting = Core.class() | ||
+ | |||
+ | function SDebugSpriteSorting:init(xtiny) -- tiny function | ||
+ | xtiny.processingSystem(self) -- called once on init and every update | ||
+ | end | ||
+ | |||
+ | function SDebugSpriteSorting:filter(ent) -- tiny function | ||
+ | return ent.sprite | ||
+ | end | ||
+ | |||
+ | function SDebugSpriteSorting:onAdd(ent) -- tiny function | ||
+ | -- debugposition | ||
+ | ent.debugposition = Pixel.new(0xaa5500, 1, 16, 16) | ||
+ | ent.debugposition:setAnchorPoint(0.5, 0.5) | ||
+ | if ent.isplayer1 then ent.debugposition:setColor(0xaa5500, 1) end | ||
+ | ent.spritelayer:addChild(ent.debugposition) | ||
+ | -- debugstartyposition | ||
+ | ent.debugstartyposition = Pixel.new(0xaa557f, 1, 16, 16) | ||
+ | ent.debugstartyposition:setAnchorPoint(0.5, 0.5) | ||
+ | if ent.isplayer1 then ent.debugstartyposition:setColor(0xaa557f, 1) end | ||
+ | ent.spritelayer:addChild(ent.debugstartyposition) | ||
+ | end | ||
+ | |||
+ | function SDebugSpriteSorting:onRemove(ent) -- tiny function | ||
+ | ent.spritelayer:removeChild(ent.debugposition) | ||
+ | ent.spritelayer:removeChild(ent.debugstartyposition) | ||
+ | end | ||
+ | |||
+ | function SDebugSpriteSorting:process(ent, dt) -- tiny function | ||
+ | ent.debugposition:setPosition(ent.pos) | ||
+ | ent.debugstartyposition:setPosition(ent.pos.x, ent.positionystart) | ||
+ | end | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | This System displays the actors y position. | ||
== Next? == | == Next? == | ||
− | + | '''Congratulations'''! We have finished our game. | |
+ | |||
+ | Let's add the final scene: '''YOU WIN'''! | ||
Prev.: [[Tuto tiny-ecs beatemup Part 10 Systems 2]]</br> | Prev.: [[Tuto tiny-ecs beatemup Part 10 Systems 2]]</br> | ||
− | '''Next: [[Tuto tiny-ecs beatemup Part 12 | + | '''Next: [[Tuto tiny-ecs beatemup Part 12 You Win]]''' |
'''[[Tutorial - tiny-ecs beatemup]]''' | '''[[Tutorial - tiny-ecs beatemup]]''' | ||
{{GIDEROS IMPORTANT LINKS}} | {{GIDEROS IMPORTANT LINKS}} |
Latest revision as of 02:32, 25 November 2024
The Systems 3
A couple more systems to add and we are done. We also add the Debug Systems to help us visualize all the boxes (hitbox, hurtbox, collsion box, ...).
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)
There are quite a bit of commented code you can delete as this Entity is immediately removed
sSpritesSorting.lua
"sSpritesSorting.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
DEBUG SYSTEMS
It would be difficult to accurately position the hit and hurt boxes, the collision boxes, ... without visual cues.
Debug systems help us with the above. Fortunately they are small and quite simple.
sDebugCollision.lua
"sDebugCollision.lua" in the "_S" folder. 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 = 0xff00ff
if ent.isplayer1 then debugcolor = 0x00ffff end
ent.debug = Pixel.new(debugcolor, 0.25, ent.collbox.w, ent.collbox.h)
ent.spritelayer:addChild(ent.debug)
end
function SDebugCollision:onRemove(ent) -- tiny function
ent.spritelayer:removeChild(ent.debug)
end
function SDebugCollision:process(ent, dt) -- tiny function
-- ent.debug:setPosition(ent.x, ent.y)
ent.debug:setPosition(ent.pos)
end
This System displays the actors collision box.
sDebugHitBoxNme.lua
"sDebugHitBoxNme.lua" in the "_S" folder. The code:
SDebugHitBoxNme = Core.class()
function SDebugHitBoxNme:init(xtiny, xshowbox) -- tiny function
xtiny.processingSystem(self) -- called once on init and every update
self.showbox = xshowbox
end
function SDebugHitBoxNme:filter(ent) -- tiny function
return (ent.headhitboxattack1 or ent.headhitboxattack2 or ent.spinehitboxattack1 or ent.spinehitboxattack2 or
ent.headhitboxjattack1 or ent.spinehitboxjattack1) and not ent.isplayer1
end
function SDebugHitBoxNme:onAdd(ent) -- tiny function
-- debugheadhitboxp1
if ent.headhitboxattack1 then
ent.debugheadhitboxp1 = Pixel.new(0xaa0000, 0.5, ent.headhitboxattack1.w, ent.headhitboxattack1.h)
ent.debugheadhitboxp1:setAnchorPoint(0.5, 0.5)
ent.spritelayer:addChild(ent.debugheadhitboxp1)
ent.debugheadhitboxp1:setVisible(self.showbox[1]) -- granular debugging
end
-- debugheadhitboxp2
if ent.headhitboxattack2 then
ent.debugheadhitboxp2 = Pixel.new(0xaa5500, 0.5, ent.headhitboxattack2.w, ent.headhitboxattack2.h)
ent.debugheadhitboxp2:setAnchorPoint(0.5, 0.5)
ent.spritelayer:addChild(ent.debugheadhitboxp2)
ent.debugheadhitboxp2:setVisible(self.showbox[2]) -- granular debugging
end
-- debugspinehitboxk1
if ent.spinehitboxattack1 then
ent.debugspinehitboxk1 = Pixel.new(0x005500, 0.5, ent.spinehitboxattack1.w, ent.spinehitboxattack1.h)
ent.debugspinehitboxk1:setAnchorPoint(0.5, 0.5)
ent.spritelayer:addChild(ent.debugspinehitboxk1)
ent.debugspinehitboxk1:setVisible(self.showbox[3]) -- granular debugging
end
-- debugspinehitboxk2
if ent.spinehitboxattack2 then
ent.debugspinehitboxk2 = Pixel.new(0x00aa00, 0.5, ent.spinehitboxattack2.w, ent.spinehitboxattack2.h)
ent.debugspinehitboxk2:setAnchorPoint(0.5, 0.5)
ent.spritelayer:addChild(ent.debugspinehitboxk2)
ent.debugspinehitboxk2:setVisible(self.showbox[4]) -- granular debugging
end
-- debugheadhitboxjp1
if ent.headhitboxjattack1 then
ent.debugheadhitboxjp1 = Pixel.new(0xaaaa00, 0.5, ent.headhitboxjattack1.w, ent.headhitboxjattack1.h)
ent.debugheadhitboxjp1:setAnchorPoint(0.5, 0.5)
ent.spritelayer:addChild(ent.debugheadhitboxjp1)
ent.debugheadhitboxjp1:setVisible(self.showbox[5]) -- granular debugging
end
-- debugspinehitboxjk1
if ent.spinehitboxjattack1 then
ent.debugspinehitboxjk1 = Pixel.new(0x00ff00, 0.5, ent.spinehitboxjattack1.w, ent.spinehitboxjattack1.h)
ent.debugspinehitboxjk1:setAnchorPoint(0.5, 0.5)
ent.spritelayer:addChild(ent.debugspinehitboxjk1)
ent.debugspinehitboxjk1:setVisible(self.showbox[6]) -- granular debugging
end
end
function SDebugHitBoxNme:onRemove(ent) -- tiny function
if ent.headhitboxattack1 then ent.spritelayer:removeChild(ent.debugheadhitboxp1) end
if ent.headhitboxattack2 then ent.spritelayer:removeChild(ent.debugheadhitboxp2) end
if ent.spinehitboxattack1 then ent.spritelayer:removeChild(ent.debugspinehitboxk1) end
if ent.spinehitboxattack2 then ent.spritelayer:removeChild(ent.debugspinehitboxk2) end
if ent.headhitboxjattack1 then ent.spritelayer:removeChild(ent.debugheadhitboxjp1) end
if ent.spinehitboxjattack1 then ent.spritelayer:removeChild(ent.debugspinehitboxjk1) end
end
function SDebugHitBoxNme:process(ent, dt) -- tiny function
local function fun()
if ent.headhitboxattack1 then
ent.debugheadhitboxp1:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.headhitboxattack1.x*ent.flip), ent.headhitboxattack1.y))
end
if ent.headhitboxattack2 then
ent.debugheadhitboxp2:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.headhitboxattack2.x*ent.flip), ent.headhitboxattack2.y))
end
if ent.spinehitboxattack1 then
ent.debugspinehitboxk1:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.spinehitboxattack1.x*ent.flip), ent.spinehitboxattack1.y))
end
if ent.spinehitboxattack2 then
ent.debugspinehitboxk2:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.spinehitboxattack2.x*ent.flip), ent.spinehitboxattack2.y))
end
if ent.headhitboxjattack1 then
ent.debugheadhitboxjp1:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.headhitboxjattack1.x*ent.flip), ent.headhitboxjattack1.y))
end
if ent.spinehitboxjattack1 then
ent.debugspinehitboxjk1:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.spinehitboxjattack1.x*ent.flip), ent.spinehitboxjattack1.y))
end
Core.yield(1)
end
Core.asyncCall(fun)
end
This System displays the enemies hit boxes when available.
sDebugHitBoxPlayer.lua
"sDebugHitBoxPlayer.lua" in the "_S" folder. The code:
SDebugHitBoxPlayer = Core.class()
function SDebugHitBoxPlayer:init(xtiny, xshowbox) -- tiny function
xtiny.processingSystem(self) -- called once on init and every update
self.showbox = xshowbox
end
function SDebugHitBoxPlayer:filter(ent) -- tiny function
return (ent.headhitboxattack1 or ent.headhitboxattack2 or ent.spinehitboxattack1 or ent.spinehitboxattack2 or
ent.headhitboxjattack1 or ent.spinehitboxjattack1) and not ent.isnme
end
function SDebugHitBoxPlayer:onAdd(ent) -- tiny function
-- debugheadhitboxp1
if ent.headhitboxattack1 then
ent.debugheadhitboxp1 = Pixel.new(0xaa0000, 0.5, ent.headhitboxattack1.w, ent.headhitboxattack1.h)
ent.debugheadhitboxp1:setAnchorPoint(0.5, 0.5)
ent.spritelayer:addChild(ent.debugheadhitboxp1)
ent.debugheadhitboxp1:setVisible(self.showbox[1]) -- granular debugging
end
-- debugheadhitboxp2
if ent.headhitboxattack2 then
ent.debugheadhitboxp2 = Pixel.new(0xaa5500, 0.5, ent.headhitboxattack2.w, ent.headhitboxattack2.h)
ent.debugheadhitboxp2:setAnchorPoint(0.5, 0.5)
ent.spritelayer:addChild(ent.debugheadhitboxp2)
ent.debugheadhitboxp2:setVisible(self.showbox[2]) -- granular debugging
end
-- debugspinehitboxk1
if ent.spinehitboxattack1 then
ent.debugspinehitboxk1 = Pixel.new(0x005500, 0.5, ent.spinehitboxattack1.w, ent.spinehitboxattack1.h)
ent.debugspinehitboxk1:setAnchorPoint(0.5, 0.5)
ent.spritelayer:addChild(ent.debugspinehitboxk1)
ent.debugspinehitboxk1:setVisible(self.showbox[3]) -- granular debugging
end
-- debugspinehitboxk2
if ent.spinehitboxattack2 then
ent.debugspinehitboxk2 = Pixel.new(0x00aa00, 0.5, ent.spinehitboxattack2.w, ent.spinehitboxattack2.h)
ent.debugspinehitboxk2:setAnchorPoint(0.5, 0.5)
ent.spritelayer:addChild(ent.debugspinehitboxk2)
ent.debugspinehitboxk2:setVisible(self.showbox[4]) -- granular debugging
end
-- debugheadhitboxjp1
if ent.headhitboxjattack1 then
ent.debugheadhitboxjp1 = Pixel.new(0xaaaa00, 0.5, ent.headhitboxjattack1.w, ent.headhitboxjattack1.h)
ent.debugheadhitboxjp1:setAnchorPoint(0.5, 0.5)
ent.spritelayer:addChild(ent.debugheadhitboxjp1)
ent.debugheadhitboxjp1:setVisible(self.showbox[5]) -- granular debugging
end
-- debugspinehitboxjk1
if ent.spinehitboxjattack1 then
ent.debugspinehitboxjk1 = Pixel.new(0x00ff00, 0.5, ent.spinehitboxjattack1.w, ent.spinehitboxjattack1.h)
ent.debugspinehitboxjk1:setAnchorPoint(0.5, 0.5)
ent.spritelayer:addChild(ent.debugspinehitboxjk1)
ent.debugspinehitboxjk1:setVisible(self.showbox[6]) -- granular debugging
end
end
function SDebugHitBoxPlayer:onRemove(ent) -- tiny function
if ent.headhitboxattack1 then ent.spritelayer:removeChild(ent.debugheadhitboxp1) end
if ent.headhitboxattack2 then ent.spritelayer:removeChild(ent.debugheadhitboxp2) end
if ent.spinehitboxattack1 then ent.spritelayer:removeChild(ent.debugspinehitboxk1) end
if ent.spinehitboxattack2 then ent.spritelayer:removeChild(ent.debugspinehitboxk2) end
if ent.headhitboxjattack1 then ent.spritelayer:removeChild(ent.debugheadhitboxjp1) end
if ent.spinehitboxjattack1 then ent.spritelayer:removeChild(ent.debugspinehitboxjk1) end
end
function SDebugHitBoxPlayer:process(ent, dt) -- tiny function
local function fun()
if ent.headhitboxattack1 then
ent.debugheadhitboxp1:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.headhitboxattack1.x*ent.flip), ent.headhitboxattack1.y))
end
if ent.headhitboxattack2 then
ent.debugheadhitboxp2:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.headhitboxattack2.x*ent.flip), ent.headhitboxattack2.y))
end
if ent.spinehitboxattack1 then
ent.debugspinehitboxk1:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.spinehitboxattack1.x*ent.flip), ent.spinehitboxattack1.y))
end
if ent.spinehitboxattack2 then
ent.debugspinehitboxk2:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.spinehitboxattack2.x*ent.flip), ent.spinehitboxattack2.y))
end
if ent.headhitboxjattack1 then
ent.debugheadhitboxjp1:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.headhitboxjattack1.x*ent.flip), ent.headhitboxjattack1.y))
end
if ent.spinehitboxjattack1 then
ent.debugspinehitboxjk1:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.spinehitboxjattack1.x*ent.flip), ent.spinehitboxjattack1.y))
end
Core.yield(1)
end
Core.asyncCall(fun)
end
This System displays the player hit boxes when available.
sDebugHurtBoxNme.lua
"sDebugHurtBoxNme.lua" in the "_S" folder. The code:
SDebugHurtBoxNme = Core.class()
function SDebugHurtBoxNme:init(xtiny) -- tiny function
xtiny.processingSystem(self) -- called once on init and every update
end
function SDebugHurtBoxNme:filter(ent) -- tiny function
return (ent.headhurtbox or ent.spinehurtbox) and not ent.isplayer1
end
function SDebugHurtBoxNme:onAdd(ent) -- tiny function
-- debugheadhurtbox
ent.debugheadhurtbox = Pixel.new(0x5500ff, 0.5, ent.headhurtbox.w, ent.headhurtbox.h)
ent.debugheadhurtbox:setAnchorPoint(0.5, 0.5)
ent.spritelayer:addChild(ent.debugheadhurtbox)
-- debugspinehurtbox
ent.debugspinehurtbox = Pixel.new(0xff00ff, 0.5, ent.spinehurtbox.w, ent.spinehurtbox.h)
ent.debugspinehurtbox:setAnchorPoint(0.5, 0.5)
ent.spritelayer:addChild(ent.debugspinehurtbox)
end
function SDebugHurtBoxNme:onRemove(ent) -- tiny function
if not ent.isdestructibleobject then -- isdestructibleobject is somehow already removed!
ent.spritelayer:removeChild(ent.debugheadhurtbox)
ent.spritelayer:removeChild(ent.debugspinehurtbox)
end
end
function SDebugHurtBoxNme:process(ent, dt) -- tiny function
local function fun()
ent.debugheadhurtbox:setPosition(ent.pos+vector(ent.collbox.w/2+(ent.headhurtbox.x*ent.flip), ent.headhurtbox.y))
ent.debugspinehurtbox:setPosition(ent.pos+vector(ent.collbox.w/2+(ent.spinehurtbox.x*ent.flip), ent.spinehurtbox.y))
Core.yield(1)
end
Core.asyncCall(fun)
end
This System displays the enemies and breakable objects hurt boxes.
sDebugHurtBoxPlayer.lua
"sDebugHurtBoxPlayer.lua" in the "_S" folder. The code:
SDebugHurtBoxPlayer = Core.class()
function SDebugHurtBoxPlayer:init(xtiny) -- tiny function
xtiny.processingSystem(self) -- called once on init and every update
end
function SDebugHurtBoxPlayer:filter(ent) -- tiny function
return (ent.headhurtbox or ent.spinehurtbox) and not ent.isnme
end
function SDebugHurtBoxPlayer:onAdd(ent) -- tiny function
-- debugheadhurtbox
ent.debugheadhurtbox = Pixel.new(0x5500ff, 0.5, ent.headhurtbox.w, ent.headhurtbox.h)
ent.debugheadhurtbox:setAnchorPoint(0.5, 0.5)
ent.spritelayer:addChild(ent.debugheadhurtbox)
-- debugspinehurtbox
ent.debugspinehurtbox = Pixel.new(0xff00ff, 0.5, ent.spinehurtbox.w, ent.spinehurtbox.h)
ent.debugspinehurtbox:setAnchorPoint(0.5, 0.5)
ent.spritelayer:addChild(ent.debugspinehurtbox)
end
function SDebugHurtBoxPlayer:onRemove(ent) -- tiny function
ent.spritelayer:removeChild(ent.debugheadhurtbox)
ent.spritelayer:removeChild(ent.debugspinehurtbox)
end
function SDebugHurtBoxPlayer:process(ent, dt) -- tiny function
ent.debugheadhurtbox:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.headhurtbox.x*ent.flip), ent.headhurtbox.y))
ent.debugspinehurtbox:setPosition(ent.pos + vector(ent.collbox.w/2+(ent.spinehurtbox.x*ent.flip), ent.spinehurtbox.y))
end
This System displays the player hurt boxes.
sDebugSpriteSorting.lua
"sDebugSpriteSorting.lua" in the "_S" folder. The code:
SDebugSpriteSorting = Core.class()
function SDebugSpriteSorting:init(xtiny) -- tiny function
xtiny.processingSystem(self) -- called once on init and every update
end
function SDebugSpriteSorting:filter(ent) -- tiny function
return ent.sprite
end
function SDebugSpriteSorting:onAdd(ent) -- tiny function
-- debugposition
ent.debugposition = Pixel.new(0xaa5500, 1, 16, 16)
ent.debugposition:setAnchorPoint(0.5, 0.5)
if ent.isplayer1 then ent.debugposition:setColor(0xaa5500, 1) end
ent.spritelayer:addChild(ent.debugposition)
-- debugstartyposition
ent.debugstartyposition = Pixel.new(0xaa557f, 1, 16, 16)
ent.debugstartyposition:setAnchorPoint(0.5, 0.5)
if ent.isplayer1 then ent.debugstartyposition:setColor(0xaa557f, 1) end
ent.spritelayer:addChild(ent.debugstartyposition)
end
function SDebugSpriteSorting:onRemove(ent) -- tiny function
ent.spritelayer:removeChild(ent.debugposition)
ent.spritelayer:removeChild(ent.debugstartyposition)
end
function SDebugSpriteSorting:process(ent, dt) -- tiny function
ent.debugposition:setPosition(ent.pos)
ent.debugstartyposition:setPosition(ent.pos.x, ent.positionystart)
end
This System displays the actors y position.
Next?
Congratulations! We have finished our game.
Let's add the final scene: YOU WIN!
Prev.: Tuto tiny-ecs beatemup Part 10 Systems 2
Next: Tuto tiny-ecs beatemup Part 12 You Win