Trouble with multiple enemies

General discussion about LÖVE, Lua, game development, puns, and unicorns.
SlyXPG
Prole
Posts: 7
Joined: Sun Apr 19, 2020 1:50 am

Trouble with multiple enemies

Post by SlyXPG »

Hi, I'm currently creating a platformer/rpg game to develop better skills with programming. My current issues is with multiple enemies, when I have one enemy spawned, everything works as intended, but if I spawn more than one, they start going nutty.

One enemy spawned
one.gif
one.gif (2.26 MiB) Viewed 8342 times
I'm not sure where the problem lies, here is a snippet of my physics callbacks that may be the culprit.

Code: Select all

function beginContact(a, b, coll)
  x,y = coll:getNormal()

  local userDataA = a:getUserData()
  local userDataB = b:getUserData()
  if userDataA == 'Platforms' and userDataB == 'player' then
    player.grounded = true
  end
  if a:getUserData() then
    if b:getUserData() then
      if a:getUserData() == 'player' and b:getUserData() == 'enemy' then
        for _, v in pairs(enemy) do
          v.touchingPlayer = true
        end
      elseif a:getUserData() == 'Platforms' and b:getUserData() == 'enemy' then
        for _, v in pairs(enemy) do
          v.grounded = true
        --  v.jumping = false
        end
      end
    end
  end


end

function endContact(a, b, coll)
  x,y = coll:getNormal()
  local userDataA = a:getUserData()
  local userDataB = b:getUserData()
  if userDataA == 'Platforms' and userDataB == 'player' then
    player.grounded = false
  end

if a:getUserData() then
  if b:getUserData() then
      if a:getUserData() == 'player' and b:getUserData() == 'enemy' then
        for _, v in pairs(enemy) do
          v.touchingPlayer = false
        end
      elseif a:getUserData() == 'Platforms' and b:getUserData() == 'enemy' then
        for _, v in pairs(enemy) do
          v.grounded = false
        --  v.jumping = true
        end
      end
    end
  end
end
Here is a copy of my .love file
test.love.zip
(218.49 KiB) Downloaded 251 times
I also have all this in a github for easy access
https://github.com/Sly-XP/Atom-Mobile

Any and all help/criticism/advice is appreciated, thanks!
Attachments
test.love.zip
(218.49 KiB) Downloaded 249 times
Last edited by SlyXPG on Fri May 15, 2020 10:55 pm, edited 1 time in total.
SlyXPG
Prole
Posts: 7
Joined: Sun Apr 19, 2020 1:50 am

Re: Trouble with multiple enemies

Post by SlyXPG »

Not sure why this attachment didn't show up but this is with multiple enemies spawned
multiple.gif
multiple.gif (1.13 MiB) Viewed 8340 times
Also a snippet of my enemy jump function

Code: Select all

function enemyJump()
  for k,v in pairs(enemy) do
    enemyTimer:after(3, function() v.jumping = true
      enemyTimer:after(0.00001, function() v.jumping = false
        enemyTimer:clear() end)
    end)
  end
end

function moveEnemy(dt)
  for k,v in pairs(enemy) do
    local enemyX, enemyY, playerX, playerY = v.body:getX(), v.body:getY(), player.body:getX(), player.body:getY()
    distanceToPlayer = distanceBetween(playerX, playerY, enemyX, enemyY)
    if v.jumping == true and distanceToPlayer > 64 and v.direction == 'left' then
        local xMovement = -distanceToPlayer * 15 * dt
      v.body:applyLinearImpulse(xMovement, -125)
    elseif  v.jumping == true and distanceToPlayer > 64 and v.direction == 'right' then
        local xMovement = distanceToPlayer * 15 * dt
        v.body:applyLinearImpulse(xMovement, -125)
    end
  end
end
These are both called in the main love.update function.
Ross
Party member
Posts: 100
Joined: Tue Mar 13, 2018 12:12 pm
Contact:

Re: Trouble with multiple enemies

Post by Ross »

The most basic way to debug things is to and add `print()` statements to your code where you think thing's might be going wrong and run it with a console open. You know that jumping is messed up, so add some prints to your jumping code.

In this case, I added `print("startJump", v)` and `print("endJump", v)` inside your jumping timer functions. If you watch the console, you'll see two "startJump"s, but only one "endJump". You're calling `enemyTimer:clear()`, which I'm kills all timers for all enemies, so the second enemy's jump never ends and it flies into the stratosphere. I don't know why you're calling clear.


Just a note, you're using global variables eeeeeeverywhere. All of your functions and almost half of your regular variables. That's going to give you serious problems sooner or later. You'll happen to use the same name in a different script, and all of a sudden something completely unrelated will be broken and it will be pretty hard to figure out why. For example you have `currentAnimation` as a global variable. Imagine you add a menu with buttons, and each button has a `currentAnimation`. Well, then you'll completely crash your player script even though you didn't touch it.

In general, you should never make any global variables. You only need a global variable for something that every single script in your game may need access too, because that's what exactly what making it global means. Otherwise, use modules. Any script can require and use them, similar to a global variable, but are much easier to keep track of and almost never cause naming collisions.

Some references about modules (or "packages"): http://lua-users.org/wiki/ModulesTutorial, or https://www.lua.org/pil/15.html, or https://www.tutorialspoint.com/lua/lua_modules.htm


There were some other weird things I noticed. In love.update (in main.lua), you're calling playerAnimUpdate (in player.lua). Inside playerAnimUpdate you're calling newCheckAttack and inside that you're defining love.mousepressed and love.mousereleased. Those are generally functions you define once and they never change, you definitely don't need to redefine them every frame.
SlyXPG
Prole
Posts: 7
Joined: Sun Apr 19, 2020 1:50 am

Re: Trouble with multiple enemies

Post by SlyXPG »

Ross wrote: Sat May 16, 2020 1:20 am The most basic way to debug things is to and add `print()` statements to your code where you think thing's might be going wrong and run it with a console open. You know that jumping is messed up, so add some prints to your jumping code.

In this case, I added `print("startJump", v)` and `print("endJump", v)` inside your jumping timer functions. If you watch the console, you'll see two "startJump"s, but only one "endJump". You're calling `enemyTimer:clear()`, which I'm kills all timers for all enemies, so the second enemy's jump never ends and it flies into the stratosphere. I don't know why you're calling clear.


Just a note, you're using global variables eeeeeeverywhere. All of your functions and almost half of your regular variables. That's going to give you serious problems sooner or later. You'll happen to use the same name in a different script, and all of a sudden something completely unrelated will be broken and it will be pretty hard to figure out why. For example you have `currentAnimation` as a global variable. Imagine you add a menu with buttons, and each button has a `currentAnimation`. Well, then you'll completely crash your player script even though you didn't touch it.

In general, you should never make any global variables. You only need a global variable for something that every single script in your game may need access too, because that's what exactly what making it global means. Otherwise, use modules. Any script can require and use them, similar to a global variable, but are much easier to keep track of and almost never cause naming collisions.

Some references about modules (or "packages"): http://lua-users.org/wiki/ModulesTutorial, or https://www.lua.org/pil/15.html, or https://www.tutorialspoint.com/lua/lua_modules.htm


There were some other weird things I noticed. In love.update (in main.lua), you're calling playerAnimUpdate (in player.lua). Inside playerAnimUpdate you're calling newCheckAttack and inside that you're defining love.mousepressed and love.mousereleased. Those are generally functions you define once and they never change, you definitely don't need to redefine them every frame.
I really appreciate the reply, all good advice/criticism. My usage of global variables is pretty rampant, not a great idea long term for sure. I don't know if this is an issue with how I’m calling the timer function, or if its an issue with HUMP.timer but without enemyTimer:clear(), after the timers went on and off for the first time, it would just start making v.jumping true and false without any delay as fast as it possibly could I assume due to being called in love.update. I had no clue why this was happening so I tried throwing the enemyTimer:clear() in there thinking it may clear that timer instance after each function. All the HUMP documentation says I should be able to call these timers in my main love.load function but i've had no success with that all.

current code looks like

Code: Select all

function enemyJump()
  for k,v in pairs(enemy) do
    enemyTimer:after(3,
    function() v.jumping = true
      print('Startjump', v)
      enemyTimer:clear()
      enemyTimer:after(0.00001,
      function() v.jumping = false
        print('EndJump', v)
        enemyTimer:clear() end)
    end)
  end
end

Prints out startJump and endJump only once per jump, however, only one enemy can jump at a time.
Ross
Party member
Posts: 100
Joined: Tue Mar 13, 2018 12:12 pm
Contact:

Re: Trouble with multiple enemies

Post by Ross »

Oh, yeah, you're adding a new timer for every enemy every frame. That will definitely cause issues. You just want them to jump every 3 seconds, right? So you just need a repeating, 3-second timer that tells the enemy to jump, and only cancel it when the enemy dies. Hump.timer has Timer.every for that. https://hump.readthedocs.io/en/latest/t ... imer.every
You'll want to start one every time you spawn an enemy, and cancel it when it dies.

You were also calling `moveEnemy(dt)` three times every frame?!

I messed around and reworked your enemy.lua to something like how I would do it (and a few things in main.lua and player.lua to use the changes). No global variables, a self-contained module that other scripts can't break (easily). In several places you were applying things to ALL of the enemies, rather than dealing with them individually. Your beginContact & endContact code for example. Also, you don't need to handle everything on update. That's the point of using timers, all you need to do is timer:update(dt) every update, and everything else just fires once when it needs to. There was no reason to do jumping stuff on update, just apply an impulse once at the moment of the jump.
SlyXPG Platformer Modified.zip
(220.69 KiB) Downloaded 251 times
Ross
Party member
Posts: 100
Joined: Tue Mar 13, 2018 12:12 pm
Contact:

Re: Trouble with multiple enemies

Post by Ross »

SlyXPG wrote: Sat May 16, 2020 3:14 am I don't know if this is an issue with how I’m calling the timer function, or if its an issue with HUMP.timer...
Hehe, well, when in doubt, using a popular library like that, it's your own code that is wrong, not someone else's. (this applies to me as well)
User avatar
zorg
Party member
Posts: 3465
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Trouble with multiple enemies

Post by zorg »

Ross wrote: Sat May 16, 2020 1:20 am ...Otherwise, use modules. Any script can require and use them, similar to a global variable, but are much easier to keep track of and almost never cause naming collisions.
Do make sure to not use the module keyword/function/whatever though, since it's deprecated; just use and return a local table in a file, and load that with require.
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.
SlyXPG
Prole
Posts: 7
Joined: Sun Apr 19, 2020 1:50 am

Re: Trouble with multiple enemies

Post by SlyXPG »

Ross wrote: Sat May 16, 2020 3:10 pm Oh, yeah, you're adding a new timer for every enemy every frame. That will definitely cause issues. You just want them to jump every 3 seconds, right? So you just need a repeating, 3-second timer that tells the enemy to jump, and only cancel it when the enemy dies. Hump.timer has Timer.every for that. https://hump.readthedocs.io/en/latest/t ... imer.every
You'll want to start one every time you spawn an enemy, and cancel it when it dies.

You were also calling `moveEnemy(dt)` three times every frame?!

I messed around and reworked your enemy.lua to something like how I would do it (and a few things in main.lua and player.lua to use the changes). No global variables, a self-contained module that other scripts can't break (easily). In several places you were applying things to ALL of the enemies, rather than dealing with them individually. Your beginContact & endContact code for example. Also, you don't need to handle everything on update. That's the point of using timers, all you need to do is timer:update(dt) every update, and everything else just fires once when it needs to. There was no reason to do jumping stuff on update, just apply an impulse once at the moment of the jump.
SlyXPG Platformer Modified.zip
No idea why I added that in 3 times, I'm embarrassed with how messy that is lol. You're code is super pretty compared to the jumbled mess of global variables and how many times I was repeating myself. I really appreciate you doing all that work and the annotations, it's clear, concise and a huge help. Looking at what you did, it makes it very clear where I was applying things to all enemies (which was almost every for loop I did) instead of the better solution you implemented. comparatively now, player.lua needs a rework badly :death: . Thank you.
Ross wrote: Sat May 16, 2020 3:14 pm
SlyXPG wrote: Sat May 16, 2020 3:14 am I don't know if this is an issue with how I’m calling the timer function, or if its an issue with HUMP.timer...
Hehe, well, when in doubt, using a popular library like that, it's your own code that is wrong, not someone else's. (this applies to me as well)
This sounds about right. :D
Ross
Party member
Posts: 100
Joined: Tue Mar 13, 2018 12:12 pm
Contact:

Re: Trouble with multiple enemies

Post by Ross »

Hahaha, no worries! You don't want to see my code from a couple years ago. Cleaning up code is actually quite fun, I'm glad I could help a bit at the same time. :P Figuring out how to organize stuff is the hardest part. Good luck!
SlyXPG
Prole
Posts: 7
Joined: Sun Apr 19, 2020 1:50 am

Re: Trouble with multiple enemies

Post by SlyXPG »

Ross wrote: Sun May 17, 2020 12:21 pm Hahaha, no worries! You don't want to see my code from a couple years ago. Cleaning up code is actually quite fun, I'm glad I could help a bit at the same time. :P Figuring out how to organize stuff is the hardest part. Good luck!
Organisation is definitely not my strong suit, I think getting better with modules though, makes everything pretty tidy. I've implemented floating platforms and was going to pm you but figured I should make another reply just in-case anybody has encountered this same issue, which I'm sure they have, I can't find it however.
Ycollision.gif
Ycollision.gif (2.46 MiB) Viewed 8138 times
Player is grounded when touching but as soon as the platform moves down along the Y-axis, player is no longer grounded.

I'm curious how you would handle the collision here with a physics body moving up and down along the Y-axis, I've experimented with changing the mass and gravityscale when the player is touching them but it seems like a pretty wonky fix, probably not stable. Anyway thanks for the time!

Github
https://github.com/Sly-XP/Atom-Mobile/t ... Platformer

current .love
Platformer.love.zip
(241.8 KiB) Downloaded 248 times
Post Reply

Who is online

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