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

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
Mad_Man
Prole
Posts: 2
Joined: Wed Dec 11, 2019 6:39 am

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

Post 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.
monolifed
Party member
Posts: 188
Joined: Sat Feb 06, 2016 9:42 pm

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

Post 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
User avatar
ivan
Party member
Posts: 1911
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

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

Post 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
User avatar
HDPLocust
Citizen
Posts: 65
Joined: Thu Feb 19, 2015 10:56 pm
Location: Swamp
Contact:

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

Post 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.
Science and violence
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

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

Post 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.
User avatar
HDPLocust
Citizen
Posts: 65
Joined: Thu Feb 19, 2015 10:56 pm
Location: Swamp
Contact:

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

Post 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.
Science and violence
Post Reply

Who is online

Users browsing this forum: Google [Bot] and 51 guests