Difference between revisions of "Tuto tiny-ecs beatemup Part 7 Enemies"
(wip) |
|||
(One intermediate revision by the same user not shown) | |||
Line 1: | Line 1: | ||
__TOC__ | __TOC__ | ||
− | == | + | == Enemies == |
− | + | The next entities we are going to create: the enemies. | |
− | The | + | As I have mentioned before components are shared amongst entities and this is exactly the case here. The enemies share the same components as our player1. The structure of an enemy Entity is almost the same as the player1 Entity. I will however show the code for each one of them because their attributes have different values, like the graphics, the animations, their speed, strengths, ... |
− | The code: | + | == eNme1.lua == |
+ | The first enemy actor, please create a file "'''eNme1.lua'''" in the '''"_E"''' folder and the code: | ||
<syntaxhighlight lang="lua"> | <syntaxhighlight lang="lua"> | ||
− | + | ENme1 = Core.class() | |
− | function | + | function ENme1:init(xspritelayer, xpos, dx, dy, xbgfxlayer) |
-- ids | -- ids | ||
− | self. | + | self.isnme = true |
− | |||
-- sprite layers | -- sprite layers | ||
self.spritelayer = xspritelayer | self.spritelayer = xspritelayer | ||
Line 19: | Line 19: | ||
-- params | -- params | ||
self.pos = xpos | self.pos = xpos | ||
− | + | self.sx = 1.4 | |
− | self.sx = 1 | ||
self.sy = self.sx | self.sy = self.sx | ||
− | self. | + | self.totallives = 2 |
− | self. | + | self.totalhealth = 2 |
− | self. | + | -- recovery |
− | self. | + | self.recovertimer = 8 |
+ | self.recoverbadtimer = 30 | ||
+ | self.actiontimer = math.random(32, 96) | ||
if g_difficulty == 0 then -- easy | if g_difficulty == 0 then -- easy | ||
− | self.totallives = 5 | + | self.totallives = 1 |
− | self. | + | self.totalhealth = 1 |
− | self. | + | self.recovertimer *= 0.5 |
+ | self.recoverbadtimer *= 0.5 | ||
+ | self.actiontimer *= 2 | ||
elseif g_difficulty == 2 then -- hard | elseif g_difficulty == 2 then -- hard | ||
− | self. | + | self.totallives = 2 |
− | + | self.totalhealth = 3 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
self.recovertimer *= 2 | self.recovertimer *= 2 | ||
self.recoverbadtimer *= 2 | self.recoverbadtimer *= 2 | ||
− | + | self.actiontimer *= 0.5 | |
− | self. | ||
− | |||
end | end | ||
− | + | self.hitfx = Bitmap.new(Texture.new("gfx/fx/1.png")) | |
− | self.hitfx = Bitmap.new(Texture.new("gfx/fx/ | ||
self.hitfx:setAnchorPoint(0.5, 0.5) | self.hitfx:setAnchorPoint(0.5, 0.5) | ||
-- COMPONENTS | -- COMPONENTS | ||
-- ANIMATION: CAnimation:init(xspritesheetpath, xcols, xrows, xanimspeed, xoffx, xoffy, sx, sy) | -- ANIMATION: CAnimation:init(xspritesheetpath, xcols, xrows, xanimspeed, xoffx, xoffy, sx, sy) | ||
− | local texpath = "gfx/ | + | local texpath = "gfx/nmes/Ravi-Rigged_m_0001.png" |
− | local framerate = 1/10 | + | local framerate = 1/10 -- 1/12 |
− | self.animation = CAnimation.new(texpath, | + | self.animation = CAnimation.new(texpath, 9, 6, framerate, 0, 0, self.sx, self.sy) |
self.sprite = self.animation.sprite | self.sprite = self.animation.sprite | ||
self.animation.sprite = nil -- free some memory | self.animation.sprite = nil -- free some memory | ||
− | self.w, self.h = self.sprite:getWidth(), self.sprite:getHeight( | + | self.w, self.h = self.sprite:getWidth(), self.sprite:getHeight() |
− | |||
-- create basics animations: CAnimation:createAnim(xanimname, xstart, xfinish) | -- create basics animations: CAnimation:createAnim(xanimname, xstart, xfinish) | ||
− | self.animation:createAnim(g_ANIM_DEFAULT, 1, | + | self.animation:createAnim(g_ANIM_DEFAULT, 1, 18) |
− | self.animation:createAnim(g_ANIM_IDLE_R, 1, | + | self.animation:createAnim(g_ANIM_IDLE_R, 1, 18) -- fluid is best |
− | self.animation:createAnim(g_ANIM_WALK_R, | + | self.animation:createAnim(g_ANIM_WALK_R, 19, 29) -- fluid is best |
− | + | self.animation:createAnim(g_ANIM_HURT_R, 48, 48) -- fluid is best | |
− | self.animation:createAnim(g_ANIM_HURT_R, | + | self.animation:createAnim(g_ANIM_STANDUP_R, 30, 35) -- fluid is best |
− | self.animation:createAnim(g_ANIM_STANDUP_R, | + | self.animation:createAnim(g_ANIM_LOSE1_R, 33, 33) -- fluid is best |
− | self.animation:createAnim(g_ANIM_LOSE1_R, | ||
-- BODY: CBody:init(xspeed, xjumpspeed) | -- BODY: CBody:init(xspeed, xjumpspeed) | ||
− | self.body = CBody.new( | + | if g_difficulty == 0 then -- easy |
+ | self.body = CBody.new(math.random(64, 128)*32, 1) -- xspeed, xjumpspeed | ||
+ | else | ||
+ | self.body = CBody.new(math.random(128, 160)*32, 1) -- xspeed, xjumpspeed | ||
+ | end | ||
-- COLLISION BOX: CCollisionBox:init(xcollwidth, xcollheight) | -- COLLISION BOX: CCollisionBox:init(xcollwidth, xcollheight) | ||
− | local collw, collh = self.w | + | local collw, collh = self.w/3, 8*self.sy |
self.collbox = CCollisionBox.new(collw, collh) | self.collbox = CCollisionBox.new(collw, collh) | ||
− | -- | + | -- hurt box |
− | + | local hhbw, hhbh = self.w/2, self.h/3 | |
− | local hhbw, hhbh = self.w | ||
self.headhurtbox = { | self.headhurtbox = { | ||
isactive=false, | isactive=false, | ||
− | x= | + | x=6*self.sx, |
y=0*self.sy-self.h/2-self.collbox.h*2, | y=0*self.sy-self.h/2-self.collbox.h*2, | ||
w=hhbw, | w=hhbw, | ||
h=hhbh, | h=hhbh, | ||
} | } | ||
− | + | local shbw, shbh = self.w/2, self.h/3 -- magik XXX | |
− | local shbw, shbh = self.w | ||
self.spinehurtbox = { | self.spinehurtbox = { | ||
isactive=false, | isactive=false, | ||
Line 92: | Line 84: | ||
} | } | ||
-- create attacks animations: CAnimation:createAnim(xanimname, xstart, xfinish) | -- create attacks animations: CAnimation:createAnim(xanimname, xstart, xfinish) | ||
− | + | self.animation:createAnim(g_ANIM_PUNCH_ATTACK1_R, 37, 41) -- no or low anticipation / quick hit / no or low overhead is best | |
− | + | self.animation:createAnim(g_ANIM_PUNCH_ATTACK2_R, 48, 51) -- no or low anticipation / quick hit / no or low overhead is best | |
− | |||
− | |||
− | |||
− | self.animation:createAnim(g_ANIM_PUNCH_ATTACK2_R, | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
-- clean up | -- clean up | ||
self.animation.myanimsimgs = nil | self.animation.myanimsimgs = nil | ||
-- hit box | -- hit box | ||
− | self.headhitboxattack1 = { | + | self.headhitboxattack1 = { |
isactive=false, | isactive=false, | ||
hitstartframe=2, | hitstartframe=2, | ||
hitendframe=3, | hitendframe=3, | ||
damage=1, | damage=1, | ||
− | x=self.collbox.w* | + | x=self.collbox.w*1.1, |
− | y=-self.h*0. | + | y=-self.h*0.7+collh*0.5, |
w=20*self.sx, | w=20*self.sx, | ||
− | h= | + | h=20*self.sy, |
} | } | ||
− | self.headhitboxattack2 = { -- | + | self.headhitboxattack2 = { |
+ | isactive=false, | ||
+ | hitstartframe=2, | ||
+ | hitendframe=3, | ||
+ | damage=1, | ||
+ | x=self.collbox.w*1.1, | ||
+ | y=-self.h*0.7+collh*0.5, | ||
+ | w=20*self.sx, | ||
+ | h=20*self.sy, | ||
+ | } | ||
+ | -- AI: CAI:init(xstartpos, dx, dy) | ||
+ | self.ai = CAI.new(self.pos, dx, dy) | ||
+ | -- SHADOW: CShadow:init(xparentw, xshadowsx, xshadowsy) | ||
+ | self.shadow = CShadow.new(self.w*0.75) | ||
+ | end | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | This is almost exactly the same code as the player1, there are three main differences: | ||
+ | * some variables were moved to the enemy System, this makes the code for our enemies shorter | ||
+ | * we adjust the enemy speed parameters based on the level and the difficulty of the game | ||
+ | * we have an extra Component: the '''AI Component''' | ||
+ | |||
+ | === AI === | ||
+ | In order for the enemies to attack the player we give them an artificial intelligence ability (Component). You can create a file "'''cAI.lua'''" in the '''"_C"''' folder. The code: | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | CAI = Core.class() | ||
+ | |||
+ | function CAI:init(xstartpos, dx, dy) | ||
+ | self.startpos = xstartpos | ||
+ | self.dx = dx -- delta x | ||
+ | self.dy = dy -- delta y | ||
+ | end | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | It stores the enemy starting position and some range constraints (delta). This Component will be used in an AI System. | ||
+ | |||
+ | '''We have now created all the components for our game!''' | ||
+ | |||
+ | == eNme2.lua == | ||
+ | "'''eNme2.lua'''" in the '''"_E"''' folder. The code: | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | ENme2 = Core.class() | ||
+ | |||
+ | function ENme2:init(xspritelayer, xpos, dx, dy, xbgfxlayer) | ||
+ | -- ids | ||
+ | self.isnme = true | ||
+ | -- sprite layers | ||
+ | self.spritelayer = xspritelayer | ||
+ | self.bgfxlayer = xbgfxlayer | ||
+ | -- params | ||
+ | self.pos = xpos | ||
+ | self.sx = 1.3 | ||
+ | self.sy = self.sx | ||
+ | self.totallives = 2 | ||
+ | self.totalhealth = 2 | ||
+ | -- recovery | ||
+ | self.recovertimer = 8 | ||
+ | self.recoverbadtimer = 30 | ||
+ | self.actiontimer = math.random(32, 96) | ||
+ | if g_difficulty == 0 then -- easy | ||
+ | self.totallives = 1 | ||
+ | self.totalhealth = 1 | ||
+ | self.recovertimer *= 0.5 | ||
+ | self.recoverbadtimer *= 0.5 | ||
+ | self.actiontimer *= 2 | ||
+ | elseif g_difficulty == 2 then -- hard | ||
+ | self.totallives = 2 | ||
+ | self.totalhealth = 3 | ||
+ | self.recovertimer *= 2 | ||
+ | self.recoverbadtimer *= 2 | ||
+ | self.actiontimer *= 0.5 | ||
+ | end | ||
+ | self.hitfx = Bitmap.new(Texture.new("gfx/fx/1.png")) | ||
+ | self.hitfx:setAnchorPoint(0.5, 0.5) | ||
+ | -- COMPONENTS | ||
+ | -- ANIMATION: CAnimation:init(xspritesheetpath, xcols, xrows, xanimspeed, xoffx, xoffy, sx, sy) | ||
+ | local texpath = "gfx/nmes/Chad_m_0001.png" | ||
+ | local framerate = 1/10 -- 1/12 | ||
+ | self.animation = CAnimation.new(texpath, 10, 6, framerate, 0, 0, self.sx, self.sy) | ||
+ | self.sprite = self.animation.sprite | ||
+ | self.animation.sprite = nil -- free some memory | ||
+ | self.w, self.h = self.sprite:getWidth(), self.sprite:getHeight() | ||
+ | -- create basics animations: CAnimation:createAnim(xanimname, xstart, xfinish) | ||
+ | self.animation:createAnim(g_ANIM_DEFAULT, 1, 18) | ||
+ | self.animation:createAnim(g_ANIM_IDLE_R, 1, 18) -- fluid is best | ||
+ | self.animation:createAnim(g_ANIM_WALK_R, 19, 27) -- fluid is best | ||
+ | self.animation:createAnim(g_ANIM_HURT_R, 56, 58) -- fluid is best | ||
+ | self.animation:createAnim(g_ANIM_STANDUP_R, 29, 31) -- fluid is best | ||
+ | self.animation:createAnim(g_ANIM_LOSE1_R, 56, 58) -- fluid is best | ||
+ | -- BODY: CBody:init(xspeed, xjumpspeed) | ||
+ | if g_difficulty == 0 then -- easy | ||
+ | self.body = CBody.new(math.random(64, 128)*32, 1) -- xspeed, xjumpspeed | ||
+ | else | ||
+ | self.body = CBody.new(math.random(128, 160)*32, 1) -- xspeed, 1, xjumpspeed | ||
+ | end | ||
+ | -- COLLISION BOX: CCollisionBox:init(xcollwidth, xcollheight) | ||
+ | local collw, collh = self.w*0.4, 8*self.sy | ||
+ | self.collbox = CCollisionBox.new(collw, collh) | ||
+ | -- hurt box | ||
+ | local hhbw, hhbh = self.w/2, self.h/3 | ||
+ | self.headhurtbox = { | ||
+ | isactive=false, | ||
+ | x=-3*self.sx, | ||
+ | y=0*self.sy-self.h/2-self.collbox.h/2, | ||
+ | w=hhbw, | ||
+ | h=hhbh, | ||
+ | } | ||
+ | local shbw, shbh = self.w/2, self.h/3 | ||
+ | self.spinehurtbox = { | ||
+ | isactive=false, | ||
+ | x=-3*self.sx, | ||
+ | y=0*self.sy-shbh/2+self.collbox.h/2, | ||
+ | w=shbw, | ||
+ | h=shbh, | ||
+ | } | ||
+ | -- create attacks animations: CAnimation:createAnim(xanimname, xstart, xfinish) | ||
+ | self.animation:createAnim(g_ANIM_PUNCH_ATTACK1_R, 28, 34) -- no or low anticipation / quick hit / no or low overhead is best | ||
+ | self.animation:createAnim(g_ANIM_KICK_ATTACK1_R, 41, 46) -- no or low anticipation / quick hit / no or low overhead is best | ||
+ | self.animation:createAnim(g_ANIM_PUNCHJUMP_ATTACK1_R, 35, 40) -- no or low anticipation / quick hit / no or low overhead is best | ||
+ | self.animation:createAnim(g_ANIM_KICKJUMP_ATTACK1_R, 47, 55) -- no or low anticipation / quick hit / no or low overhead is best | ||
+ | -- clean up | ||
+ | self.animation.myanimsimgs = nil | ||
+ | -- hit box | ||
+ | self.headhitboxattack1 = { | ||
isactive=false, | isactive=false, | ||
hitstartframe=1, | hitstartframe=1, | ||
+ | hitendframe=6, | ||
+ | damage=1, | ||
+ | x=self.collbox.w*0.7, | ||
+ | y=-self.h*0.5+collh*0.5, | ||
+ | w=20*self.sx, | ||
+ | h=self.h | ||
+ | } | ||
+ | self.spinehitboxattack1 = { | ||
+ | isactive=false, | ||
+ | hitstartframe=3, | ||
hitendframe=4, | hitendframe=4, | ||
− | damage= | + | damage=1, |
x=self.collbox.w*0.75, | x=self.collbox.w*0.75, | ||
− | y=-self.h*0. | + | y=-self.h*0.4, |
w=32*self.sx, | w=32*self.sx, | ||
− | h= | + | h=48*self.sy, |
} | } | ||
− | self. | + | self.headhitboxjattack1 = { |
isactive=false, | isactive=false, | ||
hitstartframe=2, | hitstartframe=2, | ||
− | hitendframe= | + | hitendframe=4, |
− | damage= | + | damage=2, |
x=self.collbox.w*0.7, | x=self.collbox.w*0.7, | ||
− | y=-self.h*0. | + | y=-self.h*0.6, |
w=40*self.sx, | w=40*self.sx, | ||
− | h=self. | + | h=40*self.sy, |
} | } | ||
− | self. | + | self.spinehitboxjattack1 = { |
isactive=false, | isactive=false, | ||
− | hitstartframe= | + | hitstartframe=6, |
− | hitendframe= | + | hitendframe=8, |
damage=2, | damage=2, | ||
− | x=self.collbox.w*0. | + | x=self.collbox.w*0.5, |
y=-self.h*0.25+collh*0.5, | y=-self.h*0.25+collh*0.5, | ||
− | w= | + | w=48*self.sx, |
h=self.h*0.5, | h=self.h*0.5, | ||
} | } | ||
− | self. | + | -- AI: CAI:init(xstartpos, dx, dy) |
+ | self.ai = CAI.new(self.pos, dx, dy) | ||
+ | -- SHADOW: CShadow:init(xparentw, xshadowsx, xshadowsy) | ||
+ | self.shadow = CShadow.new(self.w*0.75) | ||
+ | end | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == eNme3.lua == | ||
+ | "'''eNme3.lua'''" in the '''"_E"''' folder. The code: | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | ENme3 = Core.class() | ||
+ | |||
+ | function ENme3:init(xspritelayer, xpos, dx, dy, xbgfxlayer) | ||
+ | -- ids | ||
+ | self.isnme = true | ||
+ | -- sprite layers | ||
+ | self.spritelayer = xspritelayer | ||
+ | self.bgfxlayer = xbgfxlayer | ||
+ | -- params | ||
+ | self.pos = xpos | ||
+ | self.sx = 1.2 | ||
+ | self.sy = self.sx | ||
+ | self.totallives = 2 | ||
+ | self.totalhealth = 3 | ||
+ | -- recovery | ||
+ | self.recovertimer = 16 | ||
+ | self.recoverbadtimer = 30 | ||
+ | self.actiontimer = math.random(32, 64) | ||
+ | if g_difficulty == 0 then -- easy | ||
+ | self.totallives = 1 | ||
+ | self.totalhealth = 1 | ||
+ | self.recovertimer *= 0.5 | ||
+ | self.recoverbadtimer *= 0.5 | ||
+ | self.actiontimer *= 2 | ||
+ | elseif g_difficulty == 2 then -- hard | ||
+ | self.totallives = 2 | ||
+ | self.totalhealth = 4 | ||
+ | self.recovertimer *= 2 | ||
+ | self.recoverbadtimer *= 2 | ||
+ | self.actiontimer *= 0.5 | ||
+ | end | ||
+ | self.hitfx = Bitmap.new(Texture.new("gfx/fx/1.png")) | ||
+ | self.hitfx:setAnchorPoint(0.5, 0.5) | ||
+ | -- COMPONENTS | ||
+ | -- ANIMATION: CAnimation:init(xspritesheetpath, xcols, xrows, xanimspeed, xoffx, xoffy, sx, sy) | ||
+ | local texpath = "gfx/nmes/65bf1add77c22d1745a4790bM_0019.png" | ||
+ | local framerate = 1/8 -- 1/10 | ||
+ | self.animation = CAnimation.new(texpath, 9, 6, framerate, 0, 0, self.sx, self.sy) | ||
+ | self.sprite = self.animation.sprite | ||
+ | self.animation.sprite = nil -- free some memory | ||
+ | self.w, self.h = self.sprite:getWidth(), self.sprite:getHeight() | ||
+ | -- create basics animations: CAnimation:createAnim(xanimname, xstart, xfinish) | ||
+ | self.animation:createAnim(g_ANIM_DEFAULT, 1, 14) | ||
+ | self.animation:createAnim(g_ANIM_IDLE_R, 1, 14) -- fluid is best | ||
+ | self.animation:createAnim(g_ANIM_WALK_R, 15, 20) -- fluid is best | ||
+ | self.animation:createAnim(g_ANIM_HURT_R, 49, 53) -- fluid is best | ||
+ | self.animation:createAnim(g_ANIM_STANDUP_R, 51, 53) -- fluid is best | ||
+ | self.animation:createAnim(g_ANIM_LOSE1_R, 50, 51) -- fluid is best | ||
+ | -- BODY: CBody:init(xspeed, xjumpspeed) | ||
+ | if g_difficulty == 0 then -- easy | ||
+ | self.body = CBody.new(math.random(64, 128)*32, math.random(40, 56)/100) -- xspeed, xjumpspeed | ||
+ | else | ||
+ | self.body = CBody.new(math.random(128, 160)*32, math.random(40, 56)/100) -- xspeed, xjumpspeed | ||
+ | end | ||
+ | -- COLLISION BOX: CCollisionBox:init(xcollwidth, xcollheight) | ||
+ | local collw, collh = self.w*0.4, 8*self.sy | ||
+ | self.collbox = CCollisionBox.new(collw, collh) | ||
+ | -- hurt box | ||
+ | local hhbw, hhbh = self.w/2, self.h/3 | ||
+ | self.headhurtbox = { | ||
+ | isactive=false, | ||
+ | x=-3*self.sx, | ||
+ | y=0*self.sy-self.h/2-self.collbox.h/2, | ||
+ | w=hhbw, | ||
+ | h=hhbh, | ||
+ | } | ||
+ | local shbw, shbh = self.w/2, self.h/3 | ||
+ | self.spinehurtbox = { | ||
isactive=false, | isactive=false, | ||
− | hitstartframe= | + | x=-0*self.sx, |
− | hitendframe= | + | y=0*self.sy-shbh/2+self.collbox.h/2, |
− | damage= | + | w=shbw, |
− | x=self.collbox.w*0. | + | h=shbh, |
− | y=-self.h*0. | + | } |
− | w= | + | -- create attacks animations: CAnimation:createAnim(xanimname, xstart, xfinish) |
− | h=self. | + | self.animation:createAnim(g_ANIM_PUNCH_ATTACK1_R, 23, 28) -- no or low anticipation / quick hit / no or low overhead is best |
+ | self.animation:createAnim(g_ANIM_KICK_ATTACK1_R, 38, 43) -- no or low anticipation / quick hit / no or low overhead is best | ||
+ | self.animation:createAnim(g_ANIM_KICKJUMP_ATTACK1_R, 40, 42) -- no or low anticipation / quick hit / no or low overhead is best | ||
+ | -- clean up | ||
+ | self.animation.myanimsimgs = nil | ||
+ | -- hit box | ||
+ | self.headhitboxattack1 = { | ||
+ | isactive=false, | ||
+ | hitstartframe=2, | ||
+ | hitendframe=3, | ||
+ | damage=1, | ||
+ | x=self.collbox.w*0.6, | ||
+ | y=-self.h*0.6, | ||
+ | w=20*self.sx, | ||
+ | h=20*self.sy, | ||
} | } | ||
− | self. | + | self.spinehitboxattack1 = { |
isactive=false, | isactive=false, | ||
hitstartframe=3, | hitstartframe=3, | ||
hitendframe=5, | hitendframe=5, | ||
+ | damage=1, | ||
+ | x=self.collbox.w*0.75, | ||
+ | y=-self.h*0.4, | ||
+ | w=32*self.sx, | ||
+ | h=48*self.sy, | ||
+ | } | ||
+ | self.spinehitboxjattack1 = { | ||
+ | isactive=false, | ||
+ | hitstartframe=1, | ||
+ | hitendframe=3, | ||
damage=3, | damage=3, | ||
x=self.collbox.w*0.5, | x=self.collbox.w*0.5, | ||
− | y=-self.h*0.25 | + | y=-self.h*0.25, |
− | w= | + | w=60*self.sx, |
− | h=self. | + | h=60*self.sy, |
} | } | ||
+ | -- AI: CAI:init(xstartpos, dx, dy) | ||
+ | self.ai = CAI.new(self.pos, dx, dy) | ||
-- SHADOW: CShadow:init(xparentw, xshadowsx, xshadowsy) | -- SHADOW: CShadow:init(xparentw, xshadowsx, xshadowsy) | ||
− | self.shadow = CShadow.new(self.w*0. | + | self.shadow = CShadow.new(self.w*0.75) |
end | end | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | == | + | == eNme4.lua == |
− | + | "'''eNme4.lua'''" in the '''"_E"''' folder. The code: | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
<syntaxhighlight lang="lua"> | <syntaxhighlight lang="lua"> | ||
− | + | ENme4 = Core.class() | |
− | function | + | function ENme4:init(xspritelayer, xpos, dx, dy, xbgfxlayer) |
− | -- | + | -- ids |
− | self. | + | self.isnme = true |
− | self. | + | -- sprite layers |
− | self. | + | self.spritelayer = xspritelayer |
− | self. | + | self.bgfxlayer = xbgfxlayer |
− | -- | + | -- params |
− | + | self.pos = xpos | |
− | + | self.sx = 1.4 | |
− | + | self.sy = self.sx | |
− | + | self.totallives = 2 | |
− | + | self.totalhealth = 3 | |
− | + | -- recovery | |
− | + | self.recovertimer = 16 | |
− | + | self.recoverbadtimer = 30 | |
− | + | self.actiontimer = math.random(32, 64) | |
− | + | if g_difficulty == 0 then -- easy | |
+ | self.totallives = 1 | ||
+ | self.totalhealth = 1 | ||
+ | self.recovertimer *= 0.5 | ||
+ | self.recoverbadtimer *= 0.5 | ||
+ | self.actiontimer *= 2 | ||
+ | elseif g_difficulty == 2 then -- hard | ||
+ | self.totallives = 2 | ||
+ | self.totalhealth = 4 | ||
+ | self.recovertimer *= 2 | ||
+ | self.recoverbadtimer *= 2 | ||
+ | self.actiontimer *= 0.5 | ||
end | end | ||
− | -- | + | self.hitfx = Bitmap.new(Texture.new("gfx/fx/1.png")) |
− | self. | + | self.hitfx:setAnchorPoint(0.5, 0.5) |
− | -- | + | -- COMPONENTS |
− | self. | + | -- ANIMATION: CAnimation:init(xspritesheetpath, xcols, xrows, xanimspeed, xoffx, xoffy, sx, sy) |
− | self. | + | local texpath = "gfx/nmes/mh_fatty01m_0001.png" |
− | self. | + | local framerate = 1/8 |
− | -- | + | self.animation = CAnimation.new(texpath, 9, 5, framerate, 0, 0, self.sx, self.sy) |
− | self. | + | self.sprite = self.animation.sprite |
− | -- | + | self.animation.sprite = nil -- free some memory |
− | self. | + | self.w, self.h = self.sprite:getWidth(), self.sprite:getHeight() |
− | self. | + | -- create basics animations: CAnimation:createAnim(xanimname, xstart, xfinish) |
− | + | self.animation:createAnim(g_ANIM_DEFAULT, 1, 23) | |
− | + | self.animation:createAnim(g_ANIM_IDLE_R, 1, 23) -- fluid is best | |
− | + | self.animation:createAnim(g_ANIM_WALK_R, 24, 34) -- fluid is best | |
− | self. | + | self.animation:createAnim(g_ANIM_JUMP1_R, 35, 43) -- fluid is best |
− | + | self.animation:createAnim(g_ANIM_HURT_R, 5, 7) -- fluid is best | |
− | self. | + | self.animation:createAnim(g_ANIM_STANDUP_R, 39, 42) -- fluid is best |
+ | self.animation:createAnim(g_ANIM_LOSE1_R, 12, 14) -- fluid is best | ||
+ | -- BODY: CBody:init(xspeed, xjumpspeed) | ||
+ | if g_difficulty == 0 then -- easy | ||
+ | self.body = CBody.new(math.random(64, 128)*32, math.random(40, 56)/100) -- xspeed, xjumpspeed | ||
+ | else | ||
+ | self.body = CBody.new(math.random(128, 160)*32, math.random(40, 56)/100) -- xspeed, xjumpspeed | ||
end | end | ||
+ | -- COLLISION BOX: CCollisionBox:init(xcollwidth, xcollheight) | ||
+ | local collw, collh = self.w*0.25, 8*self.sy | ||
+ | self.collbox = CCollisionBox.new(collw, collh) | ||
+ | -- hurt box | ||
+ | local hhbw, hhbh = self.w/2, self.h/3 -- magik XXX | ||
+ | self.headhurtbox = { | ||
+ | isactive=false, | ||
+ | x=-2*self.sx, | ||
+ | y=0*self.sy-self.h/2-self.collbox.h/2, | ||
+ | w=hhbw, | ||
+ | h=hhbh, | ||
+ | } | ||
+ | local shbw, shbh = self.w/2, self.h/3 -- magik XXX | ||
+ | self.spinehurtbox = { | ||
+ | isactive=false, | ||
+ | x=-0*self.sx, | ||
+ | y=0*self.sy-shbh/2+self.collbox.h/2, | ||
+ | w=shbw, | ||
+ | h=shbh, | ||
+ | } | ||
+ | -- create attacks animations: CAnimation:createAnim(xanimname, xstart, xfinish) | ||
+ | self.animation:createAnim(g_ANIM_PUNCH_ATTACK1_R, 35, 43) -- no or low anticipation / quick hit / no or low overhead is best | ||
+ | self.animation:createAnim(g_ANIM_PUNCHJUMP_ATTACK1_R, 35, 43) -- no or low anticipation / quick hit / no or low overhead is best | ||
+ | -- clean up | ||
+ | self.animation.myanimsimgs = nil | ||
+ | -- hit box | ||
+ | self.headhitboxattack1 = { | ||
+ | isactive=false, | ||
+ | hitstartframe=1, | ||
+ | hitendframe=8, | ||
+ | damage=1, | ||
+ | x=self.collbox.w*0.4*self.sx, | ||
+ | y=-self.h*0.5+collh*0.5, | ||
+ | w=24*self.sx, | ||
+ | h=self.h, | ||
+ | } | ||
+ | self.headhitboxjattack1 = { | ||
+ | isactive=false, | ||
+ | hitstartframe=1, | ||
+ | hitendframe=8, | ||
+ | damage=2, | ||
+ | x=self.collbox.w*0.7*self.sx, | ||
+ | y=-self.h*0.5*self.sy, | ||
+ | w=40*self.sx, | ||
+ | h=self.h*0.5, | ||
+ | } | ||
+ | -- AI: CAI:init(xstartpos, dx, dy) | ||
+ | self.ai = CAI.new(self.pos, dx, dy) | ||
+ | -- SHADOW: CShadow:init(xparentw, xshadowsx, xshadowsy) | ||
+ | self.shadow = CShadow.new(self.w*0.75) | ||
end | end | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | + | That's it, we have all our enemies! | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | ''' | + | '''Please note: our enemies have different attacks, some have 2 kind of attacks, some have 3, some can kick, some can jump attack, ...''' |
== Next? == | == Next? == | ||
− | + | Before tackling all the systems that will tie our game together, we will create the last two entities of our game: the '''breakable objects''' and the '''collectibles'''. | |
Prev.: [[Tuto tiny-ecs beatemup Part 6 ECS Components]]</br> | Prev.: [[Tuto tiny-ecs beatemup Part 6 ECS Components]]</br> | ||
− | '''Next: [[Tuto tiny-ecs beatemup Part 8 | + | '''Next: [[Tuto tiny-ecs beatemup Part 8 Breakables]]''' |
'''[[Tutorial - tiny-ecs beatemup]]''' | '''[[Tutorial - tiny-ecs beatemup]]''' | ||
{{GIDEROS IMPORTANT LINKS}} | {{GIDEROS IMPORTANT LINKS}} |
Latest revision as of 08:08, 20 November 2024
Enemies
The next entities we are going to create: the enemies.
As I have mentioned before components are shared amongst entities and this is exactly the case here. The enemies share the same components as our player1. The structure of an enemy Entity is almost the same as the player1 Entity. I will however show the code for each one of them because their attributes have different values, like the graphics, the animations, their speed, strengths, ...
eNme1.lua
The first enemy actor, please create a file "eNme1.lua" in the "_E" folder and the code:
ENme1 = Core.class()
function ENme1:init(xspritelayer, xpos, dx, dy, xbgfxlayer)
-- ids
self.isnme = true
-- sprite layers
self.spritelayer = xspritelayer
self.bgfxlayer = xbgfxlayer
-- params
self.pos = xpos
self.sx = 1.4
self.sy = self.sx
self.totallives = 2
self.totalhealth = 2
-- recovery
self.recovertimer = 8
self.recoverbadtimer = 30
self.actiontimer = math.random(32, 96)
if g_difficulty == 0 then -- easy
self.totallives = 1
self.totalhealth = 1
self.recovertimer *= 0.5
self.recoverbadtimer *= 0.5
self.actiontimer *= 2
elseif g_difficulty == 2 then -- hard
self.totallives = 2
self.totalhealth = 3
self.recovertimer *= 2
self.recoverbadtimer *= 2
self.actiontimer *= 0.5
end
self.hitfx = Bitmap.new(Texture.new("gfx/fx/1.png"))
self.hitfx:setAnchorPoint(0.5, 0.5)
-- COMPONENTS
-- ANIMATION: CAnimation:init(xspritesheetpath, xcols, xrows, xanimspeed, xoffx, xoffy, sx, sy)
local texpath = "gfx/nmes/Ravi-Rigged_m_0001.png"
local framerate = 1/10 -- 1/12
self.animation = CAnimation.new(texpath, 9, 6, framerate, 0, 0, self.sx, self.sy)
self.sprite = self.animation.sprite
self.animation.sprite = nil -- free some memory
self.w, self.h = self.sprite:getWidth(), self.sprite:getHeight()
-- create basics animations: CAnimation:createAnim(xanimname, xstart, xfinish)
self.animation:createAnim(g_ANIM_DEFAULT, 1, 18)
self.animation:createAnim(g_ANIM_IDLE_R, 1, 18) -- fluid is best
self.animation:createAnim(g_ANIM_WALK_R, 19, 29) -- fluid is best
self.animation:createAnim(g_ANIM_HURT_R, 48, 48) -- fluid is best
self.animation:createAnim(g_ANIM_STANDUP_R, 30, 35) -- fluid is best
self.animation:createAnim(g_ANIM_LOSE1_R, 33, 33) -- fluid is best
-- BODY: CBody:init(xspeed, xjumpspeed)
if g_difficulty == 0 then -- easy
self.body = CBody.new(math.random(64, 128)*32, 1) -- xspeed, xjumpspeed
else
self.body = CBody.new(math.random(128, 160)*32, 1) -- xspeed, xjumpspeed
end
-- COLLISION BOX: CCollisionBox:init(xcollwidth, xcollheight)
local collw, collh = self.w/3, 8*self.sy
self.collbox = CCollisionBox.new(collw, collh)
-- hurt box
local hhbw, hhbh = self.w/2, self.h/3
self.headhurtbox = {
isactive=false,
x=6*self.sx,
y=0*self.sy-self.h/2-self.collbox.h*2,
w=hhbw,
h=hhbh,
}
local shbw, shbh = self.w/2, self.h/3 -- magik XXX
self.spinehurtbox = {
isactive=false,
x=-0*self.sx,
y=0*self.sy-shbh/2+self.collbox.h/2,
w=shbw,
h=shbh,
}
-- create attacks animations: CAnimation:createAnim(xanimname, xstart, xfinish)
self.animation:createAnim(g_ANIM_PUNCH_ATTACK1_R, 37, 41) -- no or low anticipation / quick hit / no or low overhead is best
self.animation:createAnim(g_ANIM_PUNCH_ATTACK2_R, 48, 51) -- no or low anticipation / quick hit / no or low overhead is best
-- clean up
self.animation.myanimsimgs = nil
-- hit box
self.headhitboxattack1 = {
isactive=false,
hitstartframe=2,
hitendframe=3,
damage=1,
x=self.collbox.w*1.1,
y=-self.h*0.7+collh*0.5,
w=20*self.sx,
h=20*self.sy,
}
self.headhitboxattack2 = {
isactive=false,
hitstartframe=2,
hitendframe=3,
damage=1,
x=self.collbox.w*1.1,
y=-self.h*0.7+collh*0.5,
w=20*self.sx,
h=20*self.sy,
}
-- AI: CAI:init(xstartpos, dx, dy)
self.ai = CAI.new(self.pos, dx, dy)
-- SHADOW: CShadow:init(xparentw, xshadowsx, xshadowsy)
self.shadow = CShadow.new(self.w*0.75)
end
This is almost exactly the same code as the player1, there are three main differences:
- some variables were moved to the enemy System, this makes the code for our enemies shorter
- we adjust the enemy speed parameters based on the level and the difficulty of the game
- we have an extra Component: the AI Component
AI
In order for the enemies to attack the player we give them an artificial intelligence ability (Component). You can create a file "cAI.lua" in the "_C" folder. The code:
CAI = Core.class()
function CAI:init(xstartpos, dx, dy)
self.startpos = xstartpos
self.dx = dx -- delta x
self.dy = dy -- delta y
end
It stores the enemy starting position and some range constraints (delta). This Component will be used in an AI System.
We have now created all the components for our game!
eNme2.lua
"eNme2.lua" in the "_E" folder. The code:
ENme2 = Core.class()
function ENme2:init(xspritelayer, xpos, dx, dy, xbgfxlayer)
-- ids
self.isnme = true
-- sprite layers
self.spritelayer = xspritelayer
self.bgfxlayer = xbgfxlayer
-- params
self.pos = xpos
self.sx = 1.3
self.sy = self.sx
self.totallives = 2
self.totalhealth = 2
-- recovery
self.recovertimer = 8
self.recoverbadtimer = 30
self.actiontimer = math.random(32, 96)
if g_difficulty == 0 then -- easy
self.totallives = 1
self.totalhealth = 1
self.recovertimer *= 0.5
self.recoverbadtimer *= 0.5
self.actiontimer *= 2
elseif g_difficulty == 2 then -- hard
self.totallives = 2
self.totalhealth = 3
self.recovertimer *= 2
self.recoverbadtimer *= 2
self.actiontimer *= 0.5
end
self.hitfx = Bitmap.new(Texture.new("gfx/fx/1.png"))
self.hitfx:setAnchorPoint(0.5, 0.5)
-- COMPONENTS
-- ANIMATION: CAnimation:init(xspritesheetpath, xcols, xrows, xanimspeed, xoffx, xoffy, sx, sy)
local texpath = "gfx/nmes/Chad_m_0001.png"
local framerate = 1/10 -- 1/12
self.animation = CAnimation.new(texpath, 10, 6, framerate, 0, 0, self.sx, self.sy)
self.sprite = self.animation.sprite
self.animation.sprite = nil -- free some memory
self.w, self.h = self.sprite:getWidth(), self.sprite:getHeight()
-- create basics animations: CAnimation:createAnim(xanimname, xstart, xfinish)
self.animation:createAnim(g_ANIM_DEFAULT, 1, 18)
self.animation:createAnim(g_ANIM_IDLE_R, 1, 18) -- fluid is best
self.animation:createAnim(g_ANIM_WALK_R, 19, 27) -- fluid is best
self.animation:createAnim(g_ANIM_HURT_R, 56, 58) -- fluid is best
self.animation:createAnim(g_ANIM_STANDUP_R, 29, 31) -- fluid is best
self.animation:createAnim(g_ANIM_LOSE1_R, 56, 58) -- fluid is best
-- BODY: CBody:init(xspeed, xjumpspeed)
if g_difficulty == 0 then -- easy
self.body = CBody.new(math.random(64, 128)*32, 1) -- xspeed, xjumpspeed
else
self.body = CBody.new(math.random(128, 160)*32, 1) -- xspeed, 1, xjumpspeed
end
-- COLLISION BOX: CCollisionBox:init(xcollwidth, xcollheight)
local collw, collh = self.w*0.4, 8*self.sy
self.collbox = CCollisionBox.new(collw, collh)
-- hurt box
local hhbw, hhbh = self.w/2, self.h/3
self.headhurtbox = {
isactive=false,
x=-3*self.sx,
y=0*self.sy-self.h/2-self.collbox.h/2,
w=hhbw,
h=hhbh,
}
local shbw, shbh = self.w/2, self.h/3
self.spinehurtbox = {
isactive=false,
x=-3*self.sx,
y=0*self.sy-shbh/2+self.collbox.h/2,
w=shbw,
h=shbh,
}
-- create attacks animations: CAnimation:createAnim(xanimname, xstart, xfinish)
self.animation:createAnim(g_ANIM_PUNCH_ATTACK1_R, 28, 34) -- no or low anticipation / quick hit / no or low overhead is best
self.animation:createAnim(g_ANIM_KICK_ATTACK1_R, 41, 46) -- no or low anticipation / quick hit / no or low overhead is best
self.animation:createAnim(g_ANIM_PUNCHJUMP_ATTACK1_R, 35, 40) -- no or low anticipation / quick hit / no or low overhead is best
self.animation:createAnim(g_ANIM_KICKJUMP_ATTACK1_R, 47, 55) -- no or low anticipation / quick hit / no or low overhead is best
-- clean up
self.animation.myanimsimgs = nil
-- hit box
self.headhitboxattack1 = {
isactive=false,
hitstartframe=1,
hitendframe=6,
damage=1,
x=self.collbox.w*0.7,
y=-self.h*0.5+collh*0.5,
w=20*self.sx,
h=self.h
}
self.spinehitboxattack1 = {
isactive=false,
hitstartframe=3,
hitendframe=4,
damage=1,
x=self.collbox.w*0.75,
y=-self.h*0.4,
w=32*self.sx,
h=48*self.sy,
}
self.headhitboxjattack1 = {
isactive=false,
hitstartframe=2,
hitendframe=4,
damage=2,
x=self.collbox.w*0.7,
y=-self.h*0.6,
w=40*self.sx,
h=40*self.sy,
}
self.spinehitboxjattack1 = {
isactive=false,
hitstartframe=6,
hitendframe=8,
damage=2,
x=self.collbox.w*0.5,
y=-self.h*0.25+collh*0.5,
w=48*self.sx,
h=self.h*0.5,
}
-- AI: CAI:init(xstartpos, dx, dy)
self.ai = CAI.new(self.pos, dx, dy)
-- SHADOW: CShadow:init(xparentw, xshadowsx, xshadowsy)
self.shadow = CShadow.new(self.w*0.75)
end
eNme3.lua
"eNme3.lua" in the "_E" folder. The code:
ENme3 = Core.class()
function ENme3:init(xspritelayer, xpos, dx, dy, xbgfxlayer)
-- ids
self.isnme = true
-- sprite layers
self.spritelayer = xspritelayer
self.bgfxlayer = xbgfxlayer
-- params
self.pos = xpos
self.sx = 1.2
self.sy = self.sx
self.totallives = 2
self.totalhealth = 3
-- recovery
self.recovertimer = 16
self.recoverbadtimer = 30
self.actiontimer = math.random(32, 64)
if g_difficulty == 0 then -- easy
self.totallives = 1
self.totalhealth = 1
self.recovertimer *= 0.5
self.recoverbadtimer *= 0.5
self.actiontimer *= 2
elseif g_difficulty == 2 then -- hard
self.totallives = 2
self.totalhealth = 4
self.recovertimer *= 2
self.recoverbadtimer *= 2
self.actiontimer *= 0.5
end
self.hitfx = Bitmap.new(Texture.new("gfx/fx/1.png"))
self.hitfx:setAnchorPoint(0.5, 0.5)
-- COMPONENTS
-- ANIMATION: CAnimation:init(xspritesheetpath, xcols, xrows, xanimspeed, xoffx, xoffy, sx, sy)
local texpath = "gfx/nmes/65bf1add77c22d1745a4790bM_0019.png"
local framerate = 1/8 -- 1/10
self.animation = CAnimation.new(texpath, 9, 6, framerate, 0, 0, self.sx, self.sy)
self.sprite = self.animation.sprite
self.animation.sprite = nil -- free some memory
self.w, self.h = self.sprite:getWidth(), self.sprite:getHeight()
-- create basics animations: CAnimation:createAnim(xanimname, xstart, xfinish)
self.animation:createAnim(g_ANIM_DEFAULT, 1, 14)
self.animation:createAnim(g_ANIM_IDLE_R, 1, 14) -- fluid is best
self.animation:createAnim(g_ANIM_WALK_R, 15, 20) -- fluid is best
self.animation:createAnim(g_ANIM_HURT_R, 49, 53) -- fluid is best
self.animation:createAnim(g_ANIM_STANDUP_R, 51, 53) -- fluid is best
self.animation:createAnim(g_ANIM_LOSE1_R, 50, 51) -- fluid is best
-- BODY: CBody:init(xspeed, xjumpspeed)
if g_difficulty == 0 then -- easy
self.body = CBody.new(math.random(64, 128)*32, math.random(40, 56)/100) -- xspeed, xjumpspeed
else
self.body = CBody.new(math.random(128, 160)*32, math.random(40, 56)/100) -- xspeed, xjumpspeed
end
-- COLLISION BOX: CCollisionBox:init(xcollwidth, xcollheight)
local collw, collh = self.w*0.4, 8*self.sy
self.collbox = CCollisionBox.new(collw, collh)
-- hurt box
local hhbw, hhbh = self.w/2, self.h/3
self.headhurtbox = {
isactive=false,
x=-3*self.sx,
y=0*self.sy-self.h/2-self.collbox.h/2,
w=hhbw,
h=hhbh,
}
local shbw, shbh = self.w/2, self.h/3
self.spinehurtbox = {
isactive=false,
x=-0*self.sx,
y=0*self.sy-shbh/2+self.collbox.h/2,
w=shbw,
h=shbh,
}
-- create attacks animations: CAnimation:createAnim(xanimname, xstart, xfinish)
self.animation:createAnim(g_ANIM_PUNCH_ATTACK1_R, 23, 28) -- no or low anticipation / quick hit / no or low overhead is best
self.animation:createAnim(g_ANIM_KICK_ATTACK1_R, 38, 43) -- no or low anticipation / quick hit / no or low overhead is best
self.animation:createAnim(g_ANIM_KICKJUMP_ATTACK1_R, 40, 42) -- no or low anticipation / quick hit / no or low overhead is best
-- clean up
self.animation.myanimsimgs = nil
-- hit box
self.headhitboxattack1 = {
isactive=false,
hitstartframe=2,
hitendframe=3,
damage=1,
x=self.collbox.w*0.6,
y=-self.h*0.6,
w=20*self.sx,
h=20*self.sy,
}
self.spinehitboxattack1 = {
isactive=false,
hitstartframe=3,
hitendframe=5,
damage=1,
x=self.collbox.w*0.75,
y=-self.h*0.4,
w=32*self.sx,
h=48*self.sy,
}
self.spinehitboxjattack1 = {
isactive=false,
hitstartframe=1,
hitendframe=3,
damage=3,
x=self.collbox.w*0.5,
y=-self.h*0.25,
w=60*self.sx,
h=60*self.sy,
}
-- AI: CAI:init(xstartpos, dx, dy)
self.ai = CAI.new(self.pos, dx, dy)
-- SHADOW: CShadow:init(xparentw, xshadowsx, xshadowsy)
self.shadow = CShadow.new(self.w*0.75)
end
eNme4.lua
"eNme4.lua" in the "_E" folder. The code:
ENme4 = Core.class()
function ENme4:init(xspritelayer, xpos, dx, dy, xbgfxlayer)
-- ids
self.isnme = true
-- sprite layers
self.spritelayer = xspritelayer
self.bgfxlayer = xbgfxlayer
-- params
self.pos = xpos
self.sx = 1.4
self.sy = self.sx
self.totallives = 2
self.totalhealth = 3
-- recovery
self.recovertimer = 16
self.recoverbadtimer = 30
self.actiontimer = math.random(32, 64)
if g_difficulty == 0 then -- easy
self.totallives = 1
self.totalhealth = 1
self.recovertimer *= 0.5
self.recoverbadtimer *= 0.5
self.actiontimer *= 2
elseif g_difficulty == 2 then -- hard
self.totallives = 2
self.totalhealth = 4
self.recovertimer *= 2
self.recoverbadtimer *= 2
self.actiontimer *= 0.5
end
self.hitfx = Bitmap.new(Texture.new("gfx/fx/1.png"))
self.hitfx:setAnchorPoint(0.5, 0.5)
-- COMPONENTS
-- ANIMATION: CAnimation:init(xspritesheetpath, xcols, xrows, xanimspeed, xoffx, xoffy, sx, sy)
local texpath = "gfx/nmes/mh_fatty01m_0001.png"
local framerate = 1/8
self.animation = CAnimation.new(texpath, 9, 5, framerate, 0, 0, self.sx, self.sy)
self.sprite = self.animation.sprite
self.animation.sprite = nil -- free some memory
self.w, self.h = self.sprite:getWidth(), self.sprite:getHeight()
-- create basics animations: CAnimation:createAnim(xanimname, xstart, xfinish)
self.animation:createAnim(g_ANIM_DEFAULT, 1, 23)
self.animation:createAnim(g_ANIM_IDLE_R, 1, 23) -- fluid is best
self.animation:createAnim(g_ANIM_WALK_R, 24, 34) -- fluid is best
self.animation:createAnim(g_ANIM_JUMP1_R, 35, 43) -- fluid is best
self.animation:createAnim(g_ANIM_HURT_R, 5, 7) -- fluid is best
self.animation:createAnim(g_ANIM_STANDUP_R, 39, 42) -- fluid is best
self.animation:createAnim(g_ANIM_LOSE1_R, 12, 14) -- fluid is best
-- BODY: CBody:init(xspeed, xjumpspeed)
if g_difficulty == 0 then -- easy
self.body = CBody.new(math.random(64, 128)*32, math.random(40, 56)/100) -- xspeed, xjumpspeed
else
self.body = CBody.new(math.random(128, 160)*32, math.random(40, 56)/100) -- xspeed, xjumpspeed
end
-- COLLISION BOX: CCollisionBox:init(xcollwidth, xcollheight)
local collw, collh = self.w*0.25, 8*self.sy
self.collbox = CCollisionBox.new(collw, collh)
-- hurt box
local hhbw, hhbh = self.w/2, self.h/3 -- magik XXX
self.headhurtbox = {
isactive=false,
x=-2*self.sx,
y=0*self.sy-self.h/2-self.collbox.h/2,
w=hhbw,
h=hhbh,
}
local shbw, shbh = self.w/2, self.h/3 -- magik XXX
self.spinehurtbox = {
isactive=false,
x=-0*self.sx,
y=0*self.sy-shbh/2+self.collbox.h/2,
w=shbw,
h=shbh,
}
-- create attacks animations: CAnimation:createAnim(xanimname, xstart, xfinish)
self.animation:createAnim(g_ANIM_PUNCH_ATTACK1_R, 35, 43) -- no or low anticipation / quick hit / no or low overhead is best
self.animation:createAnim(g_ANIM_PUNCHJUMP_ATTACK1_R, 35, 43) -- no or low anticipation / quick hit / no or low overhead is best
-- clean up
self.animation.myanimsimgs = nil
-- hit box
self.headhitboxattack1 = {
isactive=false,
hitstartframe=1,
hitendframe=8,
damage=1,
x=self.collbox.w*0.4*self.sx,
y=-self.h*0.5+collh*0.5,
w=24*self.sx,
h=self.h,
}
self.headhitboxjattack1 = {
isactive=false,
hitstartframe=1,
hitendframe=8,
damage=2,
x=self.collbox.w*0.7*self.sx,
y=-self.h*0.5*self.sy,
w=40*self.sx,
h=self.h*0.5,
}
-- AI: CAI:init(xstartpos, dx, dy)
self.ai = CAI.new(self.pos, dx, dy)
-- SHADOW: CShadow:init(xparentw, xshadowsx, xshadowsy)
self.shadow = CShadow.new(self.w*0.75)
end
That's it, we have all our enemies!
Please note: our enemies have different attacks, some have 2 kind of attacks, some have 3, some can kick, some can jump attack, ...
Next?
Before tackling all the systems that will tie our game together, we will create the last two entities of our game: the breakable objects and the collectibles.
Prev.: Tuto tiny-ecs beatemup Part 6 ECS Components
Next: Tuto tiny-ecs beatemup Part 8 Breakables