[SOLVED] Top Down Character Animation in 4 Different Directions

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
Todespreis
Prole
Posts: 34
Joined: Sat Apr 01, 2023 9:30 pm

[SOLVED] Top Down Character Animation in 4 Different Directions

Post by Todespreis »

Hey there! I'm trying to animate my player character walking in different directions.
So my Code for it is

Code: Select all


local player

local direction


function love.load()
    
    FrameDuration = 0.1
    FrameTimer = 0
    FrameIndex = 1
    FramesWalkingDown =    {
       love.graphics.newImage("going_down_01.png"),
       love.graphics.newImage("going_down_02.png"),
       love.graphics.newImage("going_down_03.png"),
       love.graphics.newImage("going_down_04.png"),
       love.graphics.newImage("going_down_05.png"),
       love.graphics.newImage("going_down_06.png")
    }
    FramesWalkingUp = {
       love.graphics.newImage("going_up01.png"),
       love.graphics.newImage("going_up02.png"),
       love.graphics.newImage("going_up03.png"),
       love.graphics.newImage("going_up04.png"),
       love.graphics.newImage("going_up05.png"),
       love.graphics.newImage("going_up06.png")
    }
    FramesWalkingRight = {
      love.graphics.newImage("going_right01.png"),
      love.graphics.newImage("going_right02.png"),
      love.graphics.newImage("going_right03.png"),
      love.graphics.newImage("going_right04.png"),
      love.graphics.newImage("going_right05.png"),
      love.graphics.newImage("going_right06.png")
    }

    FramesWalkingLeft = {
      love.graphics.newImage("going_left01.png"),
      love.graphics.newImage("going_left02.png"),
      love.graphics.newImage("going_left03.png"),
      love.graphics.newImage("going_left04.png"),
      love.graphics.newImage("going_left05.png"),
      love.graphics.newImage("going_left06.png")
    }
  

    FrameEnabled = {FramesWalkingUp[FrameIndex], FramesWalkingDown[FrameIndex], FramesWalkingRight[FrameIndex], FramesWalkingLeft[FrameIndex]}    

    player = {}
    player.x = 70
    player.y = 70

    player.image = love.graphics.newImage("standing_down.png" )

    


end


Down={}
function love.joystickpressed( joystick, button )
  Down[button]=true
end
function love.joystickreleased( joystick, button )
  Down[button]=nil
end

love.update=function (dt)

    FrameTimer = FrameTimer + dt
    if FrameTimer >= FrameDuration then
        FrameTimer = 0
        FrameIndex = FrameIndex + 1
        
        if FrameIndex > #FramesWalkingDown then
            FrameIndex = 1
        end

        if FrameIndex > #FramesWalkingUp then
            FrameIndex = 1
        end

        if FrameIndex > #FramesWalkingLeft then
            FrameIndex = 1
        end

        if FrameIndex > #FramesWalkingRight then
            FrameIndex = 1
        end
        
        if Down[06] then 
            player.x = player.x + 1
            FramesEnabled = FramesWalkingUp[FrameIndex]
        end

        if Down[04] then 
            player.y = player.y + 1
            FramesEnabled = FramesWalkingDown[FrameIndex]
        end

        if Down[02] then 
            player.x = player.x - 1
            FramesEnabled = FramesWalkingRight[FrameIndex]
        end

        if Down[00] then 
            player.y = player.y - 1
            FrameEnabled = FramesWalkingUp[FrameIndex]
        end
    end

    

end



function love.draw()

   
    love.graphics.draw( FrameEnabled, player.x,         player.y  )
  
end
I get the error in line 121 "Bad argument for draw - drawable expected, got table" So i think, the syntax is correct? Can i put the sprites in an array? But how can i tell the interpreter, that he has to look up in the "Down" functions, so dependent of which button is pressed, he has to draw different sprites?
Last edited by Todespreis on Wed Dec 20, 2023 8:57 pm, edited 1 time in total.
User avatar
BrotSagtMist
Party member
Posts: 614
Joined: Fri Aug 06, 2021 10:30 pm

Re: Character Animation in different directions

Post by BrotSagtMist »

As usual, no idea what you question means.
But here is the simplest animation example i could think of and it should probably fix your drawing issue:

Code: Select all

Right={love.graphics.newImage("r1.png"),love.graphics.newImage("r2.png")}
Left={love.graphics.newImage("l1.png"),love.graphics.newImage("l2.png")}
Dummy=Right
animationspeed=4
love.draw=function()
 love.graphics.draw( Dummy[math.floor((love.timer.getTime()*animationspeed)%2)+1], player.x,         player.y  )
end
love.keypressed=function(butt)
 if butt=="right" then
  Dummy=Right
 else
  Dummy=Left
 end 
end
I hope this is self explaining.
obey
Todespreis
Prole
Posts: 34
Joined: Sat Apr 01, 2023 9:30 pm

Re: Character Animation in different directions

Post by Todespreis »

Thank you very much for the fast reply, but that does'nt work in my case. Because i want to have more than 2 directions (top down). And as always, sorry for my bad english :nyu:
User avatar
pgimeno
Party member
Posts: 3551
Joined: Sun Oct 18, 2015 2:58 pm

Re: Character Animation in different directions

Post by pgimeno »

You're mixing FrameEnabled (without 's') and FramesEnabled (with 's'). But the cause of the error is that you initialize FrameEnabled like this:

Code: Select all

    FrameEnabled = {FramesWalkingUp[FrameIndex], FramesWalkingDown[FrameIndex], FramesWalkingRight[FrameIndex], FramesWalkingLeft[FrameIndex]}    
That's indeed a table. There's nothing in love.update that modifies FrameEnabled during the first frame, therefore that table will reach love.draw(), where the love.graphics.draw(FrameEnabled, ...) call will try to draw a table. That will cause the error you see. Reading the error messages usually helps.
Todespreis
Prole
Posts: 34
Joined: Sat Apr 01, 2023 9:30 pm

Re: [SOLVED] Top Down Character Animation in 4 Different Directions

Post by Todespreis »

Here is the code, that worked:

Code: Select all

local player

local direction


function love.load()
    
    FrameDuration = 0.1
    FrameTimer = 0
    FrameIndex = 1
    FramesWalkingDown =    {
       love.graphics.newImage("going_down_01.png"),
       love.graphics.newImage("going_down_02.png"),
       love.graphics.newImage("going_down_03.png"),
       love.graphics.newImage("going_down_04.png"),
       love.graphics.newImage("going_down_05.png"),
       love.graphics.newImage("going_down_06.png")
    }
    FramesWalkingUp = {
       love.graphics.newImage("going_up01.png"),
       love.graphics.newImage("going_up02.png"),
       love.graphics.newImage("going_up03.png"),
       love.graphics.newImage("going_up04.png"),
       love.graphics.newImage("going_up05.png"),
       love.graphics.newImage("going_up06.png")
    }
    FramesWalkingRight = {
      love.graphics.newImage("going_right01.png"),
      love.graphics.newImage("going_right02.png"),
      love.graphics.newImage("going_right03.png"),
      love.graphics.newImage("going_right04.png"),
      love.graphics.newImage("going_right05.png"),
      love.graphics.newImage("going_right06.png")
    }

    FramesWalkingLeft = {
      love.graphics.newImage("going_left01.png"),
      love.graphics.newImage("going_left02.png"),
      love.graphics.newImage("going_left03.png"),
      love.graphics.newImage("going_left04.png"),
      love.graphics.newImage("going_left05.png"),
      love.graphics.newImage("going_left06.png")
    }
  

    FrameEnabledDown = FramesWalkingDown[FrameIndex]
    FrameEnabledUp = FramesWalkingUp[FrameIndex]
    FrameEnabledRight = FramesWalkingRight[FrameIndex]
    FrameEnabledLeft = FramesWalkingLeft[FrameIndex]

    player = {}
    player.x = 70
    player.y = 70

    player.image = love.graphics.newImage("standing_down.png" )

    


end


Down={}
function love.joystickpressed( joystick, button )
  Down[button]=true
end
function love.joystickreleased( joystick, button )
  Down[button]=nil
end

love.update=function (dt)

    FrameTimer = FrameTimer + dt
    if FrameTimer >= FrameDuration then
        FrameTimer = 0
        FrameIndex = FrameIndex + 1
        
        if FrameIndex > #FramesWalkingDown then
            FrameIndex = 1
        end

        if FrameIndex > #FramesWalkingUp then
            FrameIndex = 1
        end

        if FrameIndex > #FramesWalkingLeft then
            FrameIndex = 1
        end

        if FrameIndex > #FramesWalkingRight then
            FrameIndex = 1
        end
        
        if Down[06] then 
            player.x = player.x + 3
            FrameEnabledRight = FramesWalkingRight[FrameIndex]
        end

        if Down[04] then 
            player.y = player.y + 3
            FrameEnabledDown = FramesWalkingDown[FrameIndex]
        end

        if Down[02] then 
            player.x = player.x - 3
            FrameEnabledLeft = FramesWalkingLeft[FrameIndex]
        end

        if Down[00] then 
            player.y = player.y - 3
            FrameEnabledUp = FramesWalkingUp[FrameIndex]
        end
    end

    

end



function love.draw()

    if Down[06] then
    
      love.graphics.draw( FrameEnabledRight, player.x, player.y  )
    
    elseif Down[04] then 
   
      love.graphics.draw( FrameEnabledDown, player.x, player.y  ) 
    
    elseif Down[02] then
    
      love.graphics.draw( FrameEnabledLeft, player.x, player.y  )
    
    elseif Down[00] then
      
      love.graphics.draw( FrameEnabledUp, player.x, player.y  )

    else 
            love.graphics.draw( player.image, player.x, player.y  )
    end
  
end
I had to modify the FrameEnabled for every direction, so that it has only to take the frames from that single frame group. It is simple and a bit too long, i guess. I think, i have to short it somehow and try to add a button to run, to crouch and to jump. Still much left to do. But a small victory for today - it's working :-D
RNavega
Party member
Posts: 251
Joined: Sun Aug 16, 2020 1:28 pm

Re: [SOLVED] Top Down Character Animation in 4 Different Directions

Post by RNavega »

Looking at your code, two things I think would help you a lot:

- To separate responsibilities better. The love.draw() function should not be the one to find out what frame to draw based on the joystick buttons pressed. Like on that last line that you have in love.draw(), it should just grab whatever image is referenced in some table field, like "player.currentImage", and draw that, at whatever coordinates are stored in player.x and player.y.
Then another part of your code completely separate will be responsible for figuring out what image reference that should be placed in that "player.currentImage".

- To study and practice "finite state machines", something that is used a lot to implement behaviors in games and other interactive programs. It can be used to handle animations, enemy A.I., player control etc.

PS I can tell that you're gonna run into problems with this part here of your code:

Code: Select all

love.update=function (dt)

    FrameTimer = FrameTimer + dt
    if FrameTimer >= FrameDuration then
        FrameTimer = 0
        FrameIndex = FrameIndex + 1
        
        if FrameIndex > #FramesWalkingDown then
            FrameIndex = 1
        end

        if FrameIndex > #FramesWalkingUp then
            FrameIndex = 1
        end

        if FrameIndex > #FramesWalkingLeft then
            FrameIndex = 1
        end

        if FrameIndex > #FramesWalkingRight then
            FrameIndex = 1
        end
...because you're checking the same counter ("FrameIndex") against many different limits, all at once.

By using a different table for each animation, that code of yours could become something like this:

Code: Select all

function love.update(dt)
    advanceAnimation(player.currentAnimation, dt)
end


function love.draw()
    love.graphics.draw(getAnimationFrame(player.currentAnimation), player.x, player.y)
end


function resetAnimation(animation)
    animation.timer = 0.0
    animation.index = 1
end


function advanceAnimation(animation, dt)
    animation.timer = animation.timer + dt
    if animation.timer > animation.duration then
        animation.timer = 0
        animation.index = animation.index + 1
        if animation.index > animation.lastIndex then
            animation.index = 1
        end
    end
end


function getAnimationFrame(animation)
    return animation.frames[animation.index]
end


function createAnimation(frameDuration, frames)
    local animation = {timer=0.0, index=1, duration=frameDuration,
                       frames=frames, lastIndex=#frames}
    return animation
end


local walkingDownAnimation = createAnimation(0.1, FramesWalkingDown)
player.currentAnimation = walkingDownAnimation
Each animation table keeps track of their own timer, frame index and frame limit.
Todespreis
Prole
Posts: 34
Joined: Sat Apr 01, 2023 9:30 pm

Re: [SOLVED] Top Down Character Animation in 4 Different Directions

Post by Todespreis »

Thank you very much for your help @RNavega! Yes, i had a problem with it at the beginning, as i gave an array of animation PNG's to a pointer, so i had to tell him, which animation is to be done at what point. And so i came to this solution. I still don't understand many things and i think, you can see my poor programming skills on the big count of if - statements ^^" . Thank you again, have a nice day and great holidays!
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 54 guests