Page 1 of 1

Selecting multiple functions in one class depending on the modificator/table index

Posted: Wed Dec 11, 2019 7:17 am
by Mad_Man
Hey people, I've been lurking the forum for some time and I've been playing around in Love2D for a little bit already. I finally decided to create an account since I hit a brick wall and can't find a solution to my problem.

I have a class that spawns enemies and depending of the enemy type they have different behavior. I want to store this behavior in class functions, like enemy:walking(), enemy:attacking() etc. Now the kicker is that I don't want all enemies to run all functions with every update, I also want to be able to dynamically add function to an enemy with player actions. The way I'd like to do it is to store all names of the functions in the table and then depending of the enemy type, select specific functions to run. Right now I have unelegant if, elseif statements that run through all enemy instances

Code: Select all

function enemy:update()

if self.typ == 1 then

self:Throwable(self.typ)
self:Bounce()
self:Gravity()
elseif self.typ == 2 then

self:BounceAndRicochet() 
self:Gravity()

elseif self.typ == 3 then
self:Chase()
self:Gravity()
elseif self.typ == 4 then

	self:NoGravity()
end

end
This code works, but with every update I look if I have specific enemy typ, and if I have I run specific functions declared in class. I checked Lua switch - case but I couldn't make any of presented solutions work with self: . If I have 10 enemy instances and I have 10 enemy types I have to do 100 comparisons, and I want to avoid that. Ideally it should look like that in pseudocode

Code: Select all

functionTable = {
    {self:Throwable(self.typ)}, {self:Bounce()},{self:Gravity()},
    {self:BounceAndRicochet(),self:Gravity()},
    {self:Chase(), self:Gravity()},
    {self:NoGravity()}
    }

function enemy:update()
    for i = 1, #functionTable do
        functionTable[self.typ][i]
    end
end
I have a feeling that it will never work that way but I'll take next best option.

Re: Selecting multiple functions in one class depending on the modificator/table index

Posted: Wed Dec 11, 2019 12:39 pm
by monolifed

Code: Select all

local functionTable = {
	function(enemy)
		enemy:Throwable(enemy.type)
		enemy:Bounce()
		enemy:Gravity()
	end,
	function(enemy)
		enemy:BounceAndRicochet() 
		enemy:Gravity()
	end,
	function(enemy)
		enemy:Chase() 
		enemy:Gravity()
	end,
	function(enemy)
		enemy:NoGravity()
	end,
}

function enemy:update()
	functionTable[self.type](self)
end

Code: Select all

-- better solution. Right after you set enemy.type during init
enemy.update = functionTable[enemy.type]
You might also use a class library

Re: Selecting multiple functions in one class depending on the modificator/table index

Posted: Wed Dec 11, 2019 2:41 pm
by ivan
dynamically add function to an enemy with player actions
This doesn't sound good, and it would be very hard to debug.
If I have 10 enemy instances and I have 10 enemy types I have to do 100 comparisons, and I want to avoid that
Yes, there is a "better" way to do this. But it's important to emphasize that it's "better" in terms of maintenance not because of the performance cost. One very simple approach is using finite state machines:

Code: Select all

function enemy:update(dt)
  self.state:update()
end
You will need to define all of your states upfront which is much easier to maintain.
Check out my tutorial on FSMs: https://2dengine.com/?p=fsm

Re: Selecting multiple functions in one class depending on the modificator/table index

Posted: Tue Jan 07, 2020 7:39 am
by HDPLocust
I think, the better way it's simple OOP inheritance.

Code: Select all

local Enemy = Class('enemy') -- main class
function Enemy:init(...) ... end
function Enemy:update(dt) ... end -- basic behavior
function Enemy:draw() ... end

local FlyingEnemy = Class(Enemy, 'estrong') -- children class
function FlyingEnemy:init(...) ... end
function FlyingEnemy:update(dt) ... end -- flying behavior

enemies = {}
enemies[1] = Enemy(x, y, hp, ...)
enemies[2] = FlyingEnemy(x, y, hp, ...)

function love.update(dt)
  for i, v in ipairs(enemies) do
    -- update function depends by class
    if v.update then v:update(dt) end
  end
end

function love.draw(dt)
  for i, v in ipairs(enemies) do
    -- draw too
    if v.draw then v:draw(dt) end
  end
end
Also states etc. But selecting main behavior by state it's weird solution, I think.

Re: Selecting multiple functions in one class depending on the modificator/table index

Posted: Tue Jan 07, 2020 3:27 pm
by raidho36
A side note, inheritance should be emulated through mixins - duplicate fields in child class bear virtually no negative performance impact, but having to navigate up the metatable tree is rather costly.

Re: Selecting multiple functions in one class depending on the modificator/table index

Posted: Wed Jan 08, 2020 3:44 am
by HDPLocust
raidho36 wrote: Tue Jan 07, 2020 3:27 pm but having to navigate up the metatable tree is rather costly.
Not as costly as it seems.
Image

Just do not make >3-5 layers of abstractions.