Enemy Behaviour help

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
Artres102
Prole
Posts: 3
Joined: Wed Nov 29, 2023 1:40 pm

Enemy Behaviour help

Post by Artres102 »

So I'm trying to make it so enemy behaviour will change upon meeting certain conditions, this being quite simply if the player is within a certain range from the enemy

Basically, if the player is within this circular body around the enemy, the enemy should stop. This is kinda working, as the enemies already stop when the player is in range, but the thing is, all of them stop and they resume moving whenever a new enemy is spawned, when they are supposed to stop one at a time and not resume moving until the player left their range.
i.e Player gets within range of enemy 1 (enemies[1]) and enemy 1 stops. Player gets within range of enemy 10 (enemies[10]), leaving enemy 1 range so it starts moving and enemy 10 stops.

Code: Select all

local player = require "player"

local enemy = {}

local enemies_spawn = {}
function enemy:enemyLoad(world, x, y, i)
    enemies_spawn = {}
    enemies_spawn.body = love.physics.newBody(world, x, y, "dynamic")
    enemies_spawn.shape = love.physics.newRectangleShape(50, 50)
    enemies_spawn.fixture = love.physics.newFixture(enemies_spawn.body, enemies_spawn.shape, 2)
    enemies_spawn.fixture:setUserData({type = "enemy", index = i, ref = enemies_spawn})
    enemies_spawn.fixture:setFriction(1)
    enemies_spawn.maxvelocity = 150
    enemies_spawn.body:setFixedRotation(true)
    enemies_spawn.health = 100
    enemies_spawn.fixture:setCategory(4)
    enemies_spawn.fixture:setMask(4)
    enemies_spawn.triggerbody = love.physics.newBody(world,x,y,"dynamic")
    enemies_spawn.triggershape = love.physics.newCircleShape(100)
    enemies_spawn.triggerfixture = love.physics.newFixture(enemies_spawn.triggerbody,enemies_spawn.triggershape,0)
    enemies_spawn.joint = love.physics.newRevoluteJoint(enemies_spawn.body,enemies_spawn.triggerbody,enemies_spawn.body:getX(),enemies_spawn.body:getY(),false)
    enemies_spawn.triggerfixture:setSensor(true)
    enemies_spawn.triggerfixture:setUserData({type = "enemyTrigger", ref = enemies_spawn})
    enemies_spawn.attacking = false

    return enemies_spawn
end

function enemy:Move(enemies)
    if enemies_spawn.body and player.body then
        for i = 1, #enemies, 1 do
            local reference = enemies_spawn.fixture:getUserData().ref.attacking
            if not reference then
                if player.body:getX() > enemies[i].body:getX() then
                    enemies[i].body:applyForce(2000, 0)
                end
                if player.body:getX() < enemies[i].body:getX() then
                    enemies[i].body:applyForce(-2000, 0)
                end

                local velocity = vector2.new(enemies[i].body:getLinearVelocity())
                if velocity.x > 0 then
                     enemies[i].body:setLinearVelocity(math.min(velocity.x, enemies[i].maxvelocity), velocity.y)
                else
                    enemies[i].body:setLinearVelocity(math.max(velocity.x, -enemies[i].maxvelocity), velocity.y)
               end
            end
        end
    end
end

function enemy:death(enemies)
    if enemies_spawn.body then
        for i = 1, #enemies, 1 do
            if enemies[i] ~= nil then
                if enemies[i].health <= 0 then
                    enemies[i].body:destroy()
                    table.remove(enemies, i)
                end
            end
        end
    end
end

function enemy:beginContact(fixtureA,fixtureB,contact)
    if fixtureA:getUserData().type  == "enemy" and fixtureB:getUserData().type  == "player" then
        player.health = player.health - 20
        player.regenTimer = 0
        local b = fixtureA:getUserData().ref.body

        if b:getX() > enemies_spawn.body:getX() then
            player.body:applyLinearImpulse(4000, -2000)
        end
        if b:getX() < enemies_spawn.body:getX() then
            player.body:applyLinearImpulse(-4000, -2000)
        end
    end
    if fixtureA:getUserData().type  == "player" and fixtureB:getUserData().type  == "enemy" then
        player.health = player.health - 20
        player.regenTimer = 0
        local b = fixtureB:getUserData().ref.body

        if b:getX() > enemies_spawn.body:getX() then
            player.body:applyLinearImpulse(4000, -2000)
        end
        if b:getX() < enemies_spawn.body:getX() then
            player.body:applyLinearImpulse(-4000, -2000)
        end
    end
    if fixtureA:getUserData().type == "enemyTrigger" and fixtureB:getUserData().type == "player" then
        local reference = fixtureA:getUserData().ref.body
        if not reference.attacking then
            reference.attacking = true
            print("State changed!")
        end
    end
    if fixtureA:getUserData().type == "player" and fixtureB:getUserData().type == "enemyTrigger" then
        local reference = fixtureB:getUserData().ref.body
        if not reference.attacking then
            enemies_spawn.fixture:getUserData().ref.attacking = true
            print("State changed!")
        end
    end
end

function enemy:Draw(enemies)
    for i = 1, #enemies, 1 do
        if enemies[i] then
            love.graphics.polygon("fill", enemies[i].body:getWorldPoints(enemies[i].shape:getPoints()))
            love.graphics.circle("line",enemies[i].body:getX(),enemies[i].body:getY(),enemies_spawn.triggershape:getRadius())
        end
    end
end

return enemy
T
User avatar
Bobble68
Party member
Posts: 160
Joined: Wed Nov 30, 2022 9:16 pm
Contact:

Re: Enemy Behaviour help

Post by Bobble68 »

It's a bit challenging to work out what exactly is going on here, but I've noticed some oddities you might want to look into.

Code: Select all

if fixtureA:getUserData().type == "enemyTrigger" and fixtureB:getUserData().type == "player" then
  local reference = fixtureA:getUserData().ref.body
  if not reference.attacking then
    reference.attacking = true
    print("State changed!")
  end
end
Are you sure you don't mean to do this?

Code: Select all

local reference = fixtureA:getUserData().ref
From what I understand, fixtureA:getUserData().ref.body is the body of the fixture in this context, not the enemy itself, so shouldn't set to attack?

Also, have you considered using ipairs() instead of a numeric for loop? It might make things a little easier to read.

Another thing, are you sure this is correct?

Code: Select all

for i = 1, #enemies, 1 do
      local reference = enemies_spawn.fixture:getUserData().ref.attacking
My best guess is that this is what's causing your issue - enemies_spawn is in scope for the entirity of this module, so you won't be getting the attack state of the enemy you're iterating through, rather you're getting the last enemy that overwrote enemies_spawn. Since I'm also guessing this isn't the intended behaviour, I'd recommend changing the code like this:

Code: Select all

-- I changed it from enemyLoad to newEnemy since loading implies its being read from a file rather than reading it from a file, though this is just my opinion.
function enemy:newEnemy(world, x, y, i)
  --new_enemy is goes out of scope outside of this call, so there is no danger of using it by accident
  local new_enemy = {}
  new_enemy.body = love.physics.newBody(world, x, y, "dynamic")
  new_enemy.shape = love.physics.newRectangleShape(50, 50)
  new_enemy.fixture = love.physics.newFixture(new_enemy.body, new_enemy.shape, 2)
  new_enemy.fixture:setUserData({type = "enemy", index = i, ref = new_enemy})
  new_enemy.fixture:setFriction(1)
  new_enemy.maxvelocity = 150
  new_enemy.body:setFixedRotation(true)
  new_enemy.health = 100
  new_enemy.fixture:setCategory(4)
  new_enemy.fixture:setMask(4)
  new_enemy.triggerbody = love.physics.newBody(world, x, y, "dynamic")
  new_enemy.triggershape = love.physics.newCircleShape(100)
  new_enemy.triggerfixture = love.physics.newFixture(new_enemy.triggerbody, new_enemy.triggershape,0)
  -- newRevoluteJoint default's the final argument to false anyway, so it doesn't need to be used
  new_enemy.joint = love.physics.newRevoluteJoint(new_enemy.body, new_enemy.triggerbody, new_enemy.body:getX(), new_enemy.body:getY())
  new_enemy.triggerfixture:setSensor(true)
  new_enemy.triggerfixture:setUserData({type = "enemyTrigger", ref = new_enemy})
  new_enemy.attacking = false

  return new_enemy
end
(Not sure why the code display is getting the colours wrong)
Dragon
Post Reply

Who is online

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