Page 1 of 2

Trouble with multiple enemies

Posted: Fri May 15, 2020 5:46 am
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 8344 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 252 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!

Re: Trouble with multiple enemies

Posted: Fri May 15, 2020 10:54 pm
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 8342 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.

Re: Trouble with multiple enemies

Posted: Sat May 16, 2020 1:20 am
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.

Re: Trouble with multiple enemies

Posted: Sat May 16, 2020 3:14 am
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.

Re: Trouble with multiple enemies

Posted: Sat May 16, 2020 3:10 pm
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 252 times

Re: Trouble with multiple enemies

Posted: Sat May 16, 2020 3:14 pm
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)

Re: Trouble with multiple enemies

Posted: Sat May 16, 2020 4:46 pm
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.

Re: Trouble with multiple enemies

Posted: Sat May 16, 2020 6:49 pm
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

Re: Trouble with multiple enemies

Posted: Sun May 17, 2020 12:21 pm
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!

Re: Trouble with multiple enemies

Posted: Tue May 19, 2020 5:54 am
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 8140 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