Difference between revisions of "Tuto tiny-ecs beatemup Part 10 Systems 2"
(wip) |
|||
Line 604: | Line 604: | ||
* finally we set the actor position and flip its bitmap in the direction it is going | * 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 ;-) | * I experimented with '''asyncCall''' to test if we could gain some frames per second ;-) | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
== Next? == | == 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 9 Systems]]</br> | Prev.: [[Tuto tiny-ecs beatemup Part 9 Systems]]</br> | ||
− | '''Next: [[Tuto tiny-ecs beatemup Part 11 | + | '''Next: [[Tuto tiny-ecs beatemup Part 11 Systems 3]]''' |
'''[[Tutorial - tiny-ecs beatemup]]''' | '''[[Tutorial - tiny-ecs beatemup]]''' | ||
{{GIDEROS IMPORTANT LINKS}} | {{GIDEROS IMPORTANT LINKS}} |
Latest revision as of 20:33, 23 November 2024
The Systems 2
We continue adding systems to our game.
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
Time to animate our actors. Please create a file "sAnimation.lua" in the "_S" folder and the code:
SAnimation = Core.class()
function SAnimation:init(xtiny)
xtiny.processingSystem(self) -- called once on init and every frames
self.sndstepgrass = Sound.new("audio/sfx/footstep/Grass02.wav")
self.channel = self.sndstepgrass:play(0, false, true)
end
function SAnimation:filter(ent) -- tiny function
return ent.animation
end
function SAnimation:onAdd(ent) -- tiny function
end
function SAnimation:onRemove(ent) -- tiny function
ent.animation = nil -- free some memory?
end
local checkanim
function SAnimation:process(ent, dt) -- tiny function
-- a little boost?
local anim = ent.animation
-- checkanim = anim.curranim -- if you are sure all animations are set else use below ternary operator code
-- luau ternary operator (no end at the end), it's a 1 liner and seems fast?
checkanim = if anim.anims[ent.animation.curranim] then anim.curranim else g_ANIM_DEFAULT
-- print(#anim.anims[checkanim])
if not ent.doanimate then return end
anim.animtimer -= dt
if anim.animtimer < 0 then
anim.frame += 1
anim.animtimer = anim.animspeed
if checkanim == g_ANIM_DEFAULT then
if anim.frame > #anim.anims[checkanim] then
anim.frame = 1
end
elseif checkanim == g_ANIM_LOSE1_R or checkanim == g_ANIM_STANDUP_R then
if anim.frame >= #anim.anims[checkanim] then
anim.frame = #anim.anims[checkanim]
end
elseif checkanim == g_ANIM_PUNCH_ATTACK1_R then
ent.headhitbox = ent.headhitboxattack1
if #anim.anims[checkanim] == 1 then -- 1 frame animation
anim.frame = 1
ent.headhitboxattack1.isactive = true
ent.isactionpunch1 = false
else -- multi frames animation
if anim.frame > #anim.anims[checkanim] then
-- anim.frame = 1
anim.frame = #anim.anims[checkanim]
ent.headhitboxattack1.isactive = false
ent.isactionpunch1 = false
elseif anim.frame > ent.headhitbox.hitendframe then
ent.headhitboxattack1.isactive = false
elseif anim.frame >= ent.headhitbox.hitstartframe then
ent.headhitboxattack1.isactive = true
end
end
elseif checkanim == g_ANIM_PUNCH_ATTACK2_R then
ent.headhitbox = ent.headhitboxattack2
if anim.frame > #anim.anims[checkanim] then
-- anim.frame = 1
anim.frame = #anim.anims[checkanim]
ent.headhitboxattack2.isactive = false
ent.isactionpunch2 = false
elseif anim.frame > ent.headhitbox.hitendframe then
ent.headhitboxattack2.isactive = false
elseif anim.frame >= ent.headhitbox.hitstartframe then
ent.headhitboxattack2.isactive = true
end
elseif checkanim == g_ANIM_KICK_ATTACK1_R then
ent.spinehitbox = ent.spinehitboxattack1
if #anim.anims[checkanim] == 1 then -- 1 frame animation
anim.frame = 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
anim.bmp:setTextureRegion(anim.anims[checkanim][anim.frame])
end
end
What the System does:
- it runs every frame
- it affects only entities with an animation id
- it checks which animation to play
- it decreases the animtimer
- and increases the current frame in the animation by 1
- 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
"sDynamicBodies.lua" in the "_S" folder. The code:
SDynamicBodies = Core.class()
local random = math.random
function SDynamicBodies:init(xtiny, xmapdef) -- tiny function
self.tiny = xtiny -- to access self.tiny variables
self.tiny.processingSystem(self) -- called once on init and every update
self.mapdef = xmapdef
end
function SDynamicBodies:filter(ent) -- tiny function
return ent.body -- only actors with body component
end
function SDynamicBodies:onAdd(ent) -- tiny function
end
function SDynamicBodies:onRemove(ent) -- tiny function
end
local randomdir = 0 -- for nmes
function SDynamicBodies:process(ent, dt) -- tiny function
-- is dead or hurt?
if (ent.washurt and ent.washurt > 0) or (ent.wasbadlyhurt and ent.wasbadlyhurt > 0) or ent.currlives <= 0 then
ent.body.vx = 0
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
-- actions
if ent.body.isonfloor then -- GROUND
if ent.isactionpunch1 then
ent.animation.curranim = g_ANIM_PUNCH_ATTACK1_R
ent.body.vx = 0 -- *= 0.1*dt, you choose
ent.body.vy = 0 -- *= 0.1*dt, you choose
elseif ent.isactionpunch2 then
ent.animation.curranim = g_ANIM_PUNCH_ATTACK2_R
ent.body.vx = 0 -- *= 0.1*dt, you choose
ent.body.vy = 0 -- *= 0.1*dt, you choose
elseif ent.isactionkick1 then
ent.animation.curranim = g_ANIM_KICK_ATTACK1_R
ent.body.vx = 0 -- *= 0.1*dt, you choose
ent.body.vy = 0 -- *= 0.1*dt, you choose
elseif ent.isactionkick2 then
ent.animation.curranim = g_ANIM_KICK_ATTACK2_R
ent.body.vx = 0 -- *= 0.1*dt, you choose
ent.body.vy = 0 -- *= 0.1*dt, you choose
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
if ent.isactionjump1 and not (ent.isactionjumppunch1 or ent.isactionjumpkick1) then -- JUMP ONLY
ent.animation.curranim = g_ANIM_JUMP1_R
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.body.isgoingup then ent.body.vy -= ent.body.currjumpspeed end
if ent.pos.y < ent.positionystart - ent.h*0.5 then -- higher apex, you choose
-- ent.body.vx = ent.body.currspeed*dt*4*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)
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
-- catches the sometimes bug!
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
This System is reponsible for moving the actors:
- runs once on init and every game loop (process)
- in init we add the map definition to constraint the actors movement
- 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
"sHitboxHurtboxCollision.lua" in the "_S" folder. The code:
SHitboxHurtboxCollision = Core.class()
function SHitboxHurtboxCollision:init(xtiny) -- tiny function
xtiny.processingSystem(self) -- called once on init and every update
self.spriteslist = xtiny.spriteslist
end
function SHitboxHurtboxCollision:filter(ent) -- tiny function
return ent.headhitbox or ent.spinehitbox or ent.headhurtbox or ent.spinehurtbox
end
function SHitboxHurtboxCollision:onAdd(ent) -- tiny function
end
function SHitboxHurtboxCollision:onRemove(ent) -- tiny function
end
function SHitboxHurtboxCollision:process(ent, dt) -- tiny function
local function checkY(actor1y, actor2y, prange)
return (-(actor1y-actor2y)<>(actor1y-actor2y)) < prange
end
local function checkCollision(box1x, box1y, box1w, box1h, box2x, box2y, box2w, box2h)
return not (box1x - box1w/2 > box2x + box2w/2 or -- is box1 on the right side of box2?
box1y - box1h/2 > box2y + box2h/2 or -- is box1 under box2?
box1x + box1w/2 < box2x - box2w/2 or -- is box1 on the left side of box2?
box1y + box1h/2 < box2y - box2h/2) -- is box1 above box2?
end
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
elseif ent.spinehitbox and ent.spinehitbox.isactive then -- SPINE
for k, _ in pairs(self.spriteslist) do
-- 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 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
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.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
end
end
end
This System checks if a hitbox and a hurtbox overlap then deals damages to the hurt actor:
- runs once on init and every game loop (process)
- in init we pass the list of all the actors (spriteslist declared in LevelX)
- 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
"sCollision.lua" in the "_S" folder. The code:
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
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 9 Systems
Next: Tuto tiny-ecs beatemup Part 11 Systems 3