Calling draw/update functions from a table in for loop?

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.
MummyTheTiger
Prole
Posts: 5
Joined: Thu Aug 15, 2024 6:02 am

Calling draw/update functions from a table in for loop?

Post by MummyTheTiger »

I'm trying to make a table that will have enemies inserted/removed as you're playing, but they all have their own draw and update function, my current code only triggers on the last added table entry but i need them all to be active. Any suggestions?

Code: Select all

        for i = 1, #self.activeEnemies, 1 do 
            self.activeEnemies[i]:draw()
        end
Last edited by MummyTheTiger on Tue Aug 20, 2024 12:26 am, edited 1 time in total.
User avatar
zorg
Party member
Posts: 3468
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Calling draw/update functions from a table in for loop?

Post by zorg »

Hi and welcome to the forums.

That snippet looks right, so the issue is probably elsewhere. Can't really help with just that much.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
MummyTheTiger
Prole
Posts: 5
Joined: Thu Aug 15, 2024 6:02 am

Re: Calling draw/update functions from a table in for loop?

Post by MummyTheTiger »

zorg wrote: Thu Aug 15, 2024 11:09 am Hi and welcome to the forums.

That snippet looks right, so the issue is probably elsewhere. Can't really help with just that much.
The only other snippets that i think could contribute to the issue is:

Code: Select all

    if Mouse:key_down(1) then
        local spawndEnem = self.enemList[1]
        table.insert(self.activeEnemies, spawndEnem)
        spawndEnem:spawn(self.mouseInf.mouseWorldPosX, self.mouseInf.mouseWorldPosY, self.world)
        print(#self.activeEnemies)
    end
User avatar
dusoft
Party member
Posts: 654
Joined: Fri Nov 08, 2013 12:07 am
Location: Europe usually
Contact:

Re: Calling draw/update functions from a table in for loop?

Post by dusoft »

Iterate using pairs or ipairs (if the table does not have empty nils)
MummyTheTiger
Prole
Posts: 5
Joined: Thu Aug 15, 2024 6:02 am

Re: Calling draw/update functions from a table in for loop?

Post by MummyTheTiger »

dusoft wrote: Thu Aug 15, 2024 6:23 pm Iterate using pairs or ipairs (if the table does not have empty nils)
Could you explain this more? I am new to Love and Lua
User avatar
darkfrei
Party member
Posts: 1204
Joined: Sat Feb 08, 2020 11:09 pm

Re: Calling draw/update functions from a table in for loop?

Post by darkfrei »

MummyTheTiger wrote: Thu Aug 15, 2024 6:31 pm
dusoft wrote: Thu Aug 15, 2024 6:23 pm Iterate using pairs or ipairs (if the table does not have empty nils)
Could you explain this more? I am new to Love and Lua
The best part of Lua: pairs

Code: Select all

for index, activeEnemy in pairs (self.activeEnemies) do 
    activeEnemy:draw()
end
It iterates all and each element in the table "self.activeEnemies", even not numerical indices. But the ordering will surprise you.

The second best part of Lua is ipairs:

Code: Select all

for index, activeEnemy in ipairs (self.activeEnemies) do 
    activeEnemy:draw()
end
The code iterates index from 1 to n, where n+1 is first not existing index. All other indices will be ignored.
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
User avatar
dusoft
Party member
Posts: 654
Joined: Fri Nov 08, 2013 12:07 am
Location: Europe usually
Contact:

Re: Calling draw/update functions from a table in for loop?

Post by dusoft »

darkfrei wrote: Fri Aug 16, 2024 10:37 am
MummyTheTiger wrote: Thu Aug 15, 2024 6:31 pm
dusoft wrote: Thu Aug 15, 2024 6:23 pm Iterate using pairs or ipairs (if the table does not have empty nils)
Could you explain this more? I am new to Love and Lua
[snip...]
Also see: https://www.lua.org/manual/5.2/manual.html#3.4.6
Note that a table like

{10, 20, nil, 40}

is not a sequence, because it has the key 4 but does not have the key 3. (So, there is no n such that the set {1..n} is equal to the set of positive numeric keys of that table.)
Regular for loop will not work as the length operator (#) has similar issues as ipairs in your case.
MummyTheTiger
Prole
Posts: 5
Joined: Thu Aug 15, 2024 6:02 am

Re: Calling draw/update functions from a table in for loop?

Post by MummyTheTiger »

darkfrei wrote: Fri Aug 16, 2024 10:37 am The best part of Lua: pairs

Code: Select all

for index, activeEnemy in pairs (self.activeEnemies) do 
    activeEnemy:draw()
end
It iterates all and each element in the table "self.activeEnemies", even not numerical indices. But the ordering will surprise you.

The second best part of Lua is ipairs:

Code: Select all

for index, activeEnemy in ipairs (self.activeEnemies) do 
    activeEnemy:draw()
end
The code iterates index from 1 to n, where n+1 is first not existing index. All other indices will be ignored.
Thanks for the explanation! I have always been trying to avoid pairs but that seems pretty simple. I went back and changed the pool of tables from integer index to use strings, easier for me to call to and now they wont be random like you mentioned. However i still have the same issue, only the last enemy is being called. I checked my table of active enemies with print in it is getting populated with index 1,2,3.. etc.

The only places I can think the problem might be:

Code: Select all

---- spawn enemy
                        local spawndEnem = self.enemList['enemy1']
                        table.insert(self.activeEnemies, spawndEnem)
                        spawndEnem:spawn(self.character.x + spwn.mx, self.character.y + spwn.my, self.world)
Importing, i'm trying to make it somewhat modular so this extra

Code: Select all

function GameRun:requireDirectory( dir , intable) ----directory folder and table to insert all children
    dir = dir or ""
    local entities = love.filesystem.getDirectoryItems(dir)
 
    for k, ents in ipairs(entities) do
       trim = string.gsub( ents, ".lua", "")
       intable[trim] = require(dir .. "/" .. trim)

        for index, data in pairs (intable) do
            print("[LOADED]",index, data)
        end
    end

end
EDIT:
I did change my update and draw functions to the pairs as shown:

Code: Select all

----- update enemies
            for index, actEne in pairs (self.activeEnemies) do 
                actEne:update(dt)
            end

Code: Select all

----- draw enemies
        for index, actEne in pairs (self.activeEnemies) do 
            actEne:draw()
        end
User avatar
pgimeno
Party member
Posts: 3672
Joined: Sun Oct 18, 2015 2:58 pm

Re: Calling draw/update functions from a table in for loop?

Post by pgimeno »

Your problem could be caused by all enemies referencing the same object. This looks suspicious:

Code: Select all

                        local spawndEnem = self.enemList['enemy1']
                        table.insert(self.activeEnemies, spawndEnem)
Note that the first assignment does not make a copy of the enemy1 table: it "points" to the table. So, the self.activeEnemies table ends up containing a single copy of the same enemy many times.

It's hard to give tips on how to fix it without an idea of how you want it to behave, as you've given very little code, but in OOP it's important to differentiate between class (the type of an object) and object (one instance of that class; there could be many). I'm not sure if each element in enemyList should be a different class or they could all be the same class; that depends on your design. But for sure, each element in the self.activeEnemies table should be a different object.
MummyTheTiger
Prole
Posts: 5
Joined: Thu Aug 15, 2024 6:02 am

Re: Calling draw/update functions from a table in for loop?

Post by MummyTheTiger »

pgimeno wrote: Sun Aug 18, 2024 10:28 am Your problem could be caused by all enemies referencing the same object. This looks suspicious:

Code: Select all

                        local spawndEnem = self.enemList['enemy1']
                        table.insert(self.activeEnemies, spawndEnem)
Note that the first assignment does not make a copy of the enemy1 table: it "points" to the table. So, the self.activeEnemies table ends up containing a single copy of the same enemy many times.

It's hard to give tips on how to fix it without an idea of how you want it to behave, as you've given very little code, but in OOP it's important to differentiate between class (the type of an object) and object (one instance of that class; there could be many). I'm not sure if each element in enemyList should be a different class or they could all be the same class; that depends on your design. But for sure, each element in the self.activeEnemies table should be a different object.
Its fixed! This was exactly the problem, it wasn't truly being copied and instead added/moved to the table it was already in. I also found this helpful https://stackoverflow.com/questions/640 ... e-by-value

I condensed my functions so now load is used instead of spawn, here is the new code

Code: Select all

---- spawn enemy
                        local spawndEnem = tableShallowCopy(self.enemList['enemy1'])
                        table.insert(self.activeEnemies, spawndEnem)
                        spawndEnem:load(self.character.x + spwn.mx, self.character.y + spwn.my, self.world)
and the function to copy

Code: Select all

function tableShallowCopy(t)
    local t2 = {}
    for k,v in pairs(t) do
      t2[k] = v
    end
    return t2
end
An easy fix now that i know what i was looking for, thank you all!
Post Reply

Who is online

Users browsing this forum: Bing [Bot] and 5 guests