Difference between revisions of "Tuto tiny-ecs beatemup Part 8 Breakables"

From GiderosMobile
(wip)
 
Line 1: Line 1:
 
__TOC__
 
__TOC__
  
== Enemies ==
+
== The last ones ==
The next entities we are going to create: the enemies.
+
We have two entities left to make. Some breakable objects entities which will spawn collectible entities.
  
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, ...
+
As I have mentioned before components are shared amongst entities and this is exactly the case here too. The breakable objects and the collectibles share the same components as our previous actors and the structure is almost the same as well.
  
== eNme1.lua ==
+
== eDestructibleObject.lua ==
The first enemy actor, please create a file "'''eNme1.lua'''" in the '''"_E"''' folder and the code:
+
'''Excuse my english, I am not an english native speaker. I called the breakable objects destructibles!'''
 +
 
 +
Please create a file "'''eDestructibleObject.lua'''" in the '''"_E"''' folder and the code:
 
<syntaxhighlight lang="lua">
 
<syntaxhighlight lang="lua">
ENme1 = Core.class()
+
EDestructibleObject = Core.class()
  
function ENme1:init(xspritelayer, xpos, dx, dy, xbgfxlayer)
+
function EDestructibleObject:init(xspritelayer, xpos)
 
-- ids
 
-- ids
self.isnme = true
+
self.isdestructibleobject = true
-- sprite layers
+
-- sprite layer
 
self.spritelayer = xspritelayer
 
self.spritelayer = xspritelayer
self.bgfxlayer = xbgfxlayer
 
 
-- params
 
-- params
 
self.pos = xpos
 
self.pos = xpos
self.sx = 1.4
+
self.positionystart = self.pos.y -- for sprite sorting
 +
self.sx = 1
 
self.sy = self.sx
 
self.sy = self.sx
self.totallives = 2
+
self.flip = math.random(100)
 +
if self.flip > 50 then self.flip = 1
 +
else self.flip = -1
 +
end
 +
self.totallives = 1
 
self.totalhealth = 2
 
self.totalhealth = 2
-- recovery
 
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 = 1
+
self.totalhealth *= 0.5
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
 
end
 +
self.currlives = self.totallives
 +
self.currhealth = self.totalhealth
 +
-- recovery
 +
self.washurt = 0
 +
self.wasbadlyhurt = 0
 +
self.recovertimer = 10
 
self.hitfx = Bitmap.new(Texture.new("gfx/fx/1.png"))
 
self.hitfx = Bitmap.new(Texture.new("gfx/fx/1.png"))
 
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/nmes/Ravi-Rigged_m_0001.png"
+
local texpath = "gfx/breakable/Barrel_02_0012.png"
local framerate = 1/10 -- 1/12
+
local framerate = 1
self.animation = CAnimation.new(texpath, 9, 6, framerate, 0, 0, self.sx, self.sy)
+
self.animation = CAnimation.new(texpath, 1, 1, framerate, 0, 0, self.sx, self.sy)
 
self.sprite = self.animation.sprite
 
self.sprite = self.animation.sprite
 +
self.sprite:setScale(self.sx*self.flip, self.sy) -- for the flip
 
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 animations: CAnimation:createAnim(xanimname, xstart, xfinish)
self.animation:createAnim(g_ANIM_DEFAULT, 1, 18)
+
self.animation:createAnim(g_ANIM_DEFAULT, 1, 1)
self.animation:createAnim(g_ANIM_IDLE_R, 1, 18) -- fluid is best
+
self.animation:createAnim(g_ANIM_IDLE_R, 1, 1)
self.animation:createAnim(g_ANIM_WALK_R, 19, 29) -- fluid is best
+
-- clean up
self.animation:createAnim(g_ANIM_HURT_R, 48, 48) -- fluid is best
+
self.animation.myanimsimgs = nil
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)
 
-- BODY: CBody:init(xspeed, xjumpspeed)
if g_difficulty == 0 then -- easy
+
self.body = CBody.new(0, 0) -- xspeed, xjumpspeed
self.body = CBody.new(math.random(64, 128)*32, 1) -- xspeed, xjumpspeed
+
self.body.defaultmass = 1
else
+
self.body.currmass = self.body.defaultmass
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/3, 8*self.sy
+
local collw, collh = self.w*1, 8*self.sy
 
self.collbox = CCollisionBox.new(collw, collh)
 
self.collbox = CCollisionBox.new(collw, collh)
 
-- hurt box
 
-- hurt box
local hhbw, hhbh = self.w/2, self.h/3
+
local hhbw, hhbh = self.w, 1*self.h/2
 
self.headhurtbox = {
 
self.headhurtbox = {
 
isactive=false,
 
isactive=false,
x=6*self.sx,
+
x=0*self.sx,
y=0*self.sy-self.h/2-self.collbox.h*2,
+
y=-self.h+hhbh*0.5+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, 3*self.h/4
 
self.spinehurtbox = {
 
self.spinehurtbox = {
 
isactive=false,
 
isactive=false,
x=-0*self.sx,
+
x=0*self.sx,
y=0*self.sy-shbh/2+self.collbox.h/2,
+
y=self.collbox.h/2-shbh/2,
 
w=shbw,
 
w=shbw,
 
h=shbh,
 
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)
 
-- SHADOW: CShadow:init(xparentw, xshadowsx, xshadowsy)
self.shadow = CShadow.new(self.w*0.75)
+
self.shadow = CShadow.new(self.w*1.1)
 
end
 
end
 
</syntaxhighlight>
 
</syntaxhighlight>
  
This is almost exactly the same code as the player1, there are three main differences:
+
The main differences:
* some variables were moved to the enemy System, this makes the code for our enemies shorter
+
* it is a single frame animation
* we adjust the enemy speed parameters based on the level and the difficulty of the game
+
* no attacks
* we have an extra Component: the '''AI Component'''
+
* no AI
  
=== AI ===
+
Once a breakable object is destroyed by the player, it spawns a collectible.
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)
+
== eCollectible.lua ==
self.startpos = xstartpos
+
"'''eCollectible.lua'''" in the '''"_E"''' folder. The code:
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">
 
<syntaxhighlight lang="lua">
ENme2 = Core.class()
+
ECollectible = Core.class()
  
function ENme2:init(xspritelayer, xpos, dx, dy, xbgfxlayer)
+
function ECollectible:init(xspritelayer, xpos)
 
-- ids
 
-- ids
self.isnme = true
+
self.iscollectible = true
-- sprite layers
+
-- sprite layer
 
self.spritelayer = xspritelayer
 
self.spritelayer = xspritelayer
self.bgfxlayer = xbgfxlayer
 
 
-- params
 
-- params
 
self.pos = xpos
 
self.pos = xpos
self.sx = 1.3
+
self.positionystart = self.pos.y -- for sprite sorting
 +
self.sx = 1
 
self.sy = self.sx
 
self.sy = self.sx
self.totallives = 2
+
self.flip = math.random(100)
self.totalhealth = 2
+
if self.flip > 80 then self.flip = 1
-- recovery
+
else self.flip = -1
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
 
end
self.hitfx = Bitmap.new(Texture.new("gfx/fx/1.png"))
+
self.totallives = 1
self.hitfx:setAnchorPoint(0.5, 0.5)
+
self.currlives = self.totallives
 
-- 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/nmes/Chad_m_0001.png"
+
local texpath = "gfx/collectible/Dragon Eggs 1.png"
local framerate = 1/10 -- 1/12
+
local framerate = 1
self.animation = CAnimation.new(texpath, 10, 6, framerate, 0, 0, self.sx, self.sy)
+
self.animation = CAnimation.new(texpath, 1, 1, framerate, 0, 0, self.sx, self.sy)
 
self.sprite = self.animation.sprite
 
self.sprite = self.animation.sprite
 +
self.sprite:setScale(self.sx*self.flip, self.sy)
 
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 animations: CAnimation:createAnim(xanimname, xstart, xfinish)
self.animation:createAnim(g_ANIM_DEFAULT, 1, 18)
+
self.animation:createAnim(g_ANIM_DEFAULT, 1, 1)
self.animation:createAnim(g_ANIM_IDLE_R, 1, 18) -- fluid is best
+
self.animation:createAnim(g_ANIM_IDLE_R, 1, 1)
self.animation:createAnim(g_ANIM_WALK_R, 19, 27) -- fluid is best
+
-- clean up
self.animation:createAnim(g_ANIM_HURT_R, 56, 58) -- fluid is best
+
self.animation.myanimsimgs = nil
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)
 
-- BODY: CBody:init(xspeed, xjumpspeed)
if g_difficulty == 0 then -- easy
+
self.body = CBody.new(0, 0) -- xspeed, xjumpspeed
self.body = CBody.new(math.random(64, 128)*32, 1) -- xspeed, xjumpspeed
+
self.body.defaultmass = 1
else
+
self.body.currmass = self.body.defaultmass
self.body = CBody.new(math.random(128, 160)*32, 1) -- xspeed, 1, xjumpspeed
 
end
 
 
-- COLLISION BOX: CCollisionBox:init(xcollwidth, xcollheight)
 
-- COLLISION BOX: CCollisionBox:init(xcollwidth, xcollheight)
local collw, collh = self.w*0.4, 8*self.sy
+
local collw, collh = self.w*0.6, self.h*0.6
 
self.collbox = CCollisionBox.new(collw, collh)
 
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)
 
-- SHADOW: CShadow:init(xparentw, xshadowsx, xshadowsy)
 
self.shadow = CShadow.new(self.w*0.75)
 
self.shadow = CShadow.new(self.w*0.75)
 +
-- IDEA: CREATE AN OSCILLATION COMPONENT using body xspeed, xjumpspeed!
 
end
 
end
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
You get the idea ;-)
 +
 +
'''We are done making all our entities!!!'''
  
 
== 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'''.
+
The time has come to tackle the '''systems'''. I will try to make it easy :-)
  
  

Revision as of 18:29, 20 November 2024

The last ones

We have two entities left to make. Some breakable objects entities which will spawn collectible entities.

As I have mentioned before components are shared amongst entities and this is exactly the case here too. The breakable objects and the collectibles share the same components as our previous actors and the structure is almost the same as well.

eDestructibleObject.lua

Excuse my english, I am not an english native speaker. I called the breakable objects destructibles!

Please create a file "eDestructibleObject.lua" in the "_E" folder and the code:

EDestructibleObject = Core.class()

function EDestructibleObject:init(xspritelayer, xpos)
	-- ids
	self.isdestructibleobject = true
	-- sprite layer
	self.spritelayer = xspritelayer
	-- params
	self.pos = xpos
	self.positionystart = self.pos.y -- for sprite sorting
	self.sx = 1
	self.sy = self.sx
	self.flip = math.random(100)
	if self.flip > 50 then self.flip = 1
	else self.flip = -1
	end
	self.totallives = 1
	self.totalhealth = 2
	if g_difficulty == 0 then -- easy
		self.totalhealth *= 0.5
	end
	self.currlives = self.totallives
	self.currhealth = self.totalhealth
	-- recovery
	self.washurt = 0
	self.wasbadlyhurt = 0
	self.recovertimer = 10
	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/breakable/Barrel_02_0012.png"
	local framerate = 1
	self.animation = CAnimation.new(texpath, 1, 1, framerate, 0, 0, self.sx, self.sy)
	self.sprite = self.animation.sprite
	self.sprite:setScale(self.sx*self.flip, self.sy) -- for the flip
	self.animation.sprite = nil -- free some memory
	self.w, self.h = self.sprite:getWidth(), self.sprite:getHeight()
	-- create animations: CAnimation:createAnim(xanimname, xstart, xfinish)
	self.animation:createAnim(g_ANIM_DEFAULT, 1, 1)
	self.animation:createAnim(g_ANIM_IDLE_R, 1, 1)
	-- clean up
	self.animation.myanimsimgs = nil
	-- BODY: CBody:init(xspeed, xjumpspeed)
	self.body = CBody.new(0, 0) -- xspeed, xjumpspeed
	self.body.defaultmass = 1
	self.body.currmass = self.body.defaultmass
	-- COLLISION BOX: CCollisionBox:init(xcollwidth, xcollheight)
	local collw, collh = self.w*1, 8*self.sy
	self.collbox = CCollisionBox.new(collw, collh)
	-- hurt box
	local hhbw, hhbh = self.w, 1*self.h/2
	self.headhurtbox = {
		isactive=false,
		x=0*self.sx,
		y=-self.h+hhbh*0.5+self.collbox.h/2,
		w=hhbw,
		h=hhbh,
	}
	local shbw, shbh = self.w, 3*self.h/4
	self.spinehurtbox = {
		isactive=false,
		x=0*self.sx,
		y=self.collbox.h/2-shbh/2,
		w=shbw,
		h=shbh,
	}
	-- SHADOW: CShadow:init(xparentw, xshadowsx, xshadowsy)
	self.shadow = CShadow.new(self.w*1.1)
end

The main differences:

  • it is a single frame animation
  • no attacks
  • no AI

Once a breakable object is destroyed by the player, it spawns a collectible.

eCollectible.lua

"eCollectible.lua" in the "_E" folder. The code:

ECollectible = Core.class()

function ECollectible:init(xspritelayer, xpos)
	-- ids
	self.iscollectible = true
	-- sprite layer
	self.spritelayer = xspritelayer
	-- params
	self.pos = xpos
	self.positionystart = self.pos.y -- for sprite sorting
	self.sx = 1
	self.sy = self.sx
	self.flip = math.random(100)
	if self.flip > 80 then self.flip = 1
	else self.flip = -1
	end
	self.totallives = 1
	self.currlives = self.totallives
	-- COMPONENTS
	-- ANIMATION: CAnimation:init(xspritesheetpath, xcols, xrows, xanimspeed, xoffx, xoffy, sx, sy)
	local texpath = "gfx/collectible/Dragon Eggs 1.png"
	local framerate = 1
	self.animation = CAnimation.new(texpath, 1, 1, framerate, 0, 0, self.sx, self.sy)
	self.sprite = self.animation.sprite
	self.sprite:setScale(self.sx*self.flip, self.sy)
	self.animation.sprite = nil -- free some memory
	self.w, self.h = self.sprite:getWidth(), self.sprite:getHeight()
	-- create animations: CAnimation:createAnim(xanimname, xstart, xfinish)
	self.animation:createAnim(g_ANIM_DEFAULT, 1, 1)
	self.animation:createAnim(g_ANIM_IDLE_R, 1, 1)
	-- clean up
	self.animation.myanimsimgs = nil
	-- BODY: CBody:init(xspeed, xjumpspeed)
	self.body = CBody.new(0, 0) -- xspeed, xjumpspeed
	self.body.defaultmass = 1
	self.body.currmass = self.body.defaultmass
	-- COLLISION BOX: CCollisionBox:init(xcollwidth, xcollheight)
	local collw, collh = self.w*0.6, self.h*0.6
	self.collbox = CCollisionBox.new(collw, collh)
	-- SHADOW: CShadow:init(xparentw, xshadowsx, xshadowsy)
	self.shadow = CShadow.new(self.w*0.75)
	-- IDEA: CREATE AN OSCILLATION COMPONENT using body xspeed, xjumpspeed!
end

You get the idea ;-)

We are done making all our entities!!!

Next?

The time has come to tackle the systems. I will try to make it easy :-)


Prev.: Tuto tiny-ecs beatemup Part 7 Enemies
Next: Tuto tiny-ecs beatemup Part 9 XXX


Tutorial - tiny-ecs beatemup