punch animation - keypressed, keyreleased

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
Rickfg
Prole
Posts: 6
Joined: Tue Nov 14, 2023 2:23 pm

punch animation - keypressed, keyreleased

Post by Rickfg »

Hello,
I am new to game developing as am doing as final project for a course.
I managed to animate everything as I am doing a small 2d beat 'em up type of game.
The issue I am facing is that I set the key "u" to be a punch button. When key "u" is pressed (using keypressed) I set the character attribute of self.jab to true. That will stop the idle and walking sprite from being drawn and the sprite of the punch will be drawn.
I then set the keyreleased to set self.jab = false so the idle or waking sprite to be drawn again.
The issue I am facing is that it all works on press, however, if I hold key "u" down, the idle or walking sprite are never drawn, only when I release the key "u".
I have tried to add logic using isrepeat, love.keyboard.isDown("u") and nothing makes it work. The default attribute for self.jab is set to false.

I have attached the full code for limbs.lua, which creates the limbs (arms and legs) and their properties.
They directly related to the character, so I attached that as well.

keypressed and keyreleased logic are in character.lua.

Hope you can help!
Thanks in advance!
Attachments
limbs.lua
(3.25 KiB) Downloaded 41 times
character.lua
(8.7 KiB) Downloaded 47 times
RNavega
Party member
Posts: 251
Joined: Sun Aug 16, 2020 1:28 pm

Re: punch animation - keypressed, keyreleased

Post by RNavega »

Hi. That's a fun but also very complicated part of coding your game, when you're taking input events and translating them into behaviors in your game, so as to solve the problem of "when I press a key I want to trigger this thing to happen, and this thing will go on by the next few frames regardless of whether that key is still pressed or not".

You need to learn about a design pattern called "Finite State Machine", which is a way of organizing the behaviors of things in your game. You can use it for animation, for enemy AI, for player actions etc., with many FSMs handling different aspects of your game.

You're already trying to do that with that "self.jab = true" logic. That is a form of state. With an FSM you can make it much more organized to support many different actions (jab, kick, run, walk, crawl, jump). You do that by taking an object-oriented approach, with all states sharing the same interface, so your code can go "update the current state, whatever it is".

When you press U, the character changes to the "jab" state, and in that state, per-frame, they will proceed to do whatever they have to do to complete that jab state, like advancing a frame of animation and checking if their fist hit an enemy sprite. By the end of it (like when the jab animation finishes, or whatever other rule that you chose), they will change to whatever state that they should change to next, usually the "idle" state.
So once you press U and the jab action begins, it doesn't matter whether the U key is still pressed while the jab is being performed, as what triggered the jab was the pressing of U, not the holding or the release of it. This would solve your "jab won't end until I release the key" problem.

You need to look for tutorials on implementing FSMs in your game. Here are some:
- https://gamedevelopertips.com/finite-st ... evelopers/
- https://code.tutsplus.com/finite-state- ... dev-11867t
Rickfg
Prole
Posts: 6
Joined: Tue Nov 14, 2023 2:23 pm

Re: punch animation - keypressed, keyreleased

Post by Rickfg »

RNavega wrote: Tue Nov 14, 2023 11:23 pm Hi. That's a fun but also very complicated part of coding your game, when you're taking input events and translating them into behaviors in your game, so as to solve the problem of "when I press a key I want to trigger this thing to happen, and this thing will go on by the next few frames regardless of whether that key is still pressed or not".

You need to learn about a design pattern called "Finite State Machine", which is a way of organizing the behaviors of things in your game. You can use it for animation, for enemy AI, for player actions etc., with many FSMs handling different aspects of your game.

You're already trying to do that with that "self.jab = true" logic. That is a form of state. With an FSM you can make it much more organized to support many different actions (jab, kick, run, walk, crawl, jump). You do that by taking an object-oriented approach, with all states sharing the same interface, so your code can go "update the current state, whatever it is".

When you press U, the character changes to the "jab" state, and in that state, per-frame, they will proceed to do whatever they have to do to complete that jab state, like advancing a frame of animation and checking if their fist hit an enemy sprite. By the end of it (like when the jab animation finishes, or whatever other rule that you chose), they will change to whatever state that they should change to next, usually the "idle" state.
So once you press U and the jab action begins, it doesn't matter whether the U key is still pressed while the jab is being performed, as what triggered the jab was the pressing of U, not the holding or the release of it. This would solve your "jab won't end until I release the key" problem.

You need to look for tutorials on implementing FSMs in your game. Here are some:
- https://gamedevelopertips.com/finite-st ... evelopers/
- https://code.tutsplus.com/finite-state- ... dev-11867t
Thank you very much for your input! I will surely have a look at these tutorials! :)
Rickfg
Prole
Posts: 6
Joined: Tue Nov 14, 2023 2:23 pm

Re: punch animation - keypressed, keyreleased

Post by Rickfg »

The tutorials are great but I think it would take me too much time given my deadline to apply all of that. As I would have to learn and likely refactor lots of my code.
That being said, I was trying to find a work around and I do not understand why the following does not work:
function love.keypressed(key, isrepeat)
love.keyboard.setKeyRepeat(true, 2, 0)
if key == "u" then
P1_punch.jab = true
P1.jab = true
P1_punch.punchActive = love.timer.getTime()
end
if key == "u" and isrepeat then
P1_punch.jab = false
P1.jab = false
print("repeat")
end
The code above would print "repeat" as soon as key u is pressed. Shouldn't it wait 2 seconds to do so as per setKeyRepeat function states?

Or is add it into a ifelse statement, it never prints "repeat". So it always reverts back to self.jab = true
Character punch mechanics
if key == "u" then
P1_punch.jab = true
P1.jab = true
P1_punch.punchActive = love.timer.getTime()
elseif key == "u" and isrepeat then
P1_punch.jab = false
P1.jab = false
print("repeat")
end
User avatar
BrotSagtMist
Party member
Posts: 614
Joined: Fri Aug 06, 2021 10:30 pm

Re: punch animation - keypressed, keyreleased

Post by BrotSagtMist »

love.keypressed( key, scancode, isrepeat ) Is the definition of that function.
You can not just left values out.
You should read up on how to actually call a function and pass arguments.
I also see zero reason why anyone would need to dabble with repeats for controlling stuff on the screen.
There is a fundamental error in what you think you need to do here, the key repeat is pretty much only useful for gui elements, text input and menus.
All you get here is pretty bad autofire buttons.
obey
RNavega
Party member
Posts: 251
Joined: Sun Aug 16, 2020 1:28 pm

Re: punch animation - keypressed, keyreleased

Post by RNavega »

Rickfg wrote: Tue Nov 14, 2023 2:35 pm I am new to game developing as am doing as final project for a course.
Hm, now that I think about it, isn't it odd that your course asked you to do a project but didn't teach you the fundamentals needed to make that project?

Anyway, you need two switches / flags: one that tells if the character 'is currently punching', and another that tells if the character 'can begin punching'.
Also, I'd suggest separating the responsibilities of functions in your code. The keypressed() handler shouldn't change stuff about the player directly, but rather just read the key-presses and translate them into "what the human player wants the game to do". After that, in other parts of your code outside of keypressed(), that information will be read and reacted to -- which keypressed() will know nothing about, since it doesn't care about that.

Code: Select all

local actionInputs = {
    jab = false,
    moveRight = false,
    moveLeft = false,
}

local P1 = {
    isJabbing = false, -- Whether the player character is doing a jab.
    canJab = false, -- Whether the player character can START a jab.
}


function love.keypressed(key, isrepeat)
    if key == 'u' then
        actionInputs.jab = true
    end
    if key == 'right' then
        actionInputs.moveRight = true
    end
    if key == 'left' then
        actionInputs.moveLeft = true
    end
end


function love.keyreleased(key)
    if key == 'u' then
        actionInputs.jab = false
    end
    if key == 'right' then
        actionInputs.moveRight = false
    end
    if key == 'left' then
        actionInputs.moveLeft = false
    end
end



function playerUpdate(dt)
    if P1.isJabbing then
        -- Update the jabbing animation, check for enemy collisions etc.
        -- When the animation is over, set P1.isJabbing = false
    else
        if actionInputs.jab then
            if P1.canJab then
                P1.isJabbing = true
                P1.canJab = false
                -- Initialize the punchActive timer and other stuff that this
                -- function "playerUpdate" doesn't need to know about.
                P1_punch.startJab()
            end
        else
            P1.canJab = true
        end
    end
    
    if actionInputs.moveRight then
        P1.x = P1.x + P1.speed
        -- Test for collisions etc.
    end
end
Rickfg
Prole
Posts: 6
Joined: Tue Nov 14, 2023 2:23 pm

Re: punch animation - keypressed, keyreleased

Post by Rickfg »

RNavega wrote: Wed Nov 15, 2023 10:23 pm
Rickfg wrote: Tue Nov 14, 2023 2:35 pm I am new to game developing as am doing as final project for a course.
Hm, now that I think about it, isn't it odd that your course asked you to do a project but didn't teach you the fundamentals needed to make that project?

Anyway, you need two switches / flags: one that tells if the character 'is currently punching', and another that tells if the character 'can begin punching'.
Also, I'd suggest separating the responsibilities of functions in your code. The keypressed() handler shouldn't change stuff about the player directly, but rather just read the key-presses and translate them into "what the human player wants the game to do". After that, in other parts of your code outside of keypressed(), that information will be read and reacted to -- which keypressed() will know nothing about, since it doesn't care about that.

Code: Select all

local actionInputs = {
    jab = false,
    moveRight = false,
    moveLeft = false,
}

local P1 = {
    isJabbing = false, -- Whether the player character is doing a jab.
    canJab = false, -- Whether the player character can START a jab.
}


function love.keypressed(key, isrepeat)
    if key == 'u' then
        actionInputs.jab = true
    end
    if key == 'right' then
        actionInputs.moveRight = true
    end
    if key == 'left' then
        actionInputs.moveLeft = true
    end
end


function love.keyreleased(key)
    if key == 'u' then
        actionInputs.jab = false
    end
    if key == 'right' then
        actionInputs.moveRight = false
    end
    if key == 'left' then
        actionInputs.moveLeft = false
    end
end



function playerUpdate(dt)
    if P1.isJabbing then
        -- Update the jabbing animation, check for enemy collisions etc.
        -- When the animation is over, set P1.isJabbing = false
    else
        if actionInputs.jab then
            if P1.canJab then
                P1.isJabbing = true
                P1.canJab = false
                -- Initialize the punchActive timer and other stuff that this
                -- function "playerUpdate" doesn't need to know about.
                P1_punch.startJab()
            end
        else
            P1.canJab = true
        end
    end
    
    if actionInputs.moveRight then
        P1.x = P1.x + P1.speed
        -- Test for collisions etc.
    end
end
Thank you very much for another helpful insight! I will refactor my code and try to move things to my character file instead of separating the limbs for now as it makes less confusion in my head. I will certainly try and implemented as you suggested! :)

As for the course, it is CS50, so it's more a computer science course rather than a game development specific one. The "build a game with Lua in love2d" was one of their suggestions as to what I could try as a final project, which I choose given I am really keen on video games.
thanks again, hopefully I will figure this out and make it work soon!
Rickfg
Prole
Posts: 6
Joined: Tue Nov 14, 2023 2:23 pm

Re: punch animation - keypressed, keyreleased

Post by Rickfg »

So I tried to implement the below based on what you recommended. I added a print statement and although as soon as I press u it does print "true", as soon I press I get an error for the draw() function stating that expects a quad but got nil.
mind you that self.jab should be doing the job of P1.jabbing on your code.
-- jab animation

Code: Select all

   if self.jab then
        jab_elapsedTime = jab_elapsedTime + dt
        if jab_elapsedTime > 0.5 then
            if jab_currentFrame < 3 then
                jab_currentFrame = jab_currentFrame + 1
            else
                jab_currentFrame = 3
            end
            jab_activeFrame = jabFrames[jab_currentFrame]
        end
    else
        if actionInputs.jab then
            if self.canjab then
                self.jab = true
                self.canjab = false
                self.punchActive = love.timer.getTime()
            end
        else
            self.canjab = true
            self.jab = false
        end
        print(self.jab)
    end
    --end of jab animation 
within draw() function I have

Code: Select all

--jab sprit jabbing right
    if self.facing == "right" and self.jab == true and (love.timer.getTime() - self.punchActive) < 0.1 then
        love.graphics.rectangle("line", self.x +30, self.y, self.width, 10)
        love.graphics.draw(P1_jab, jab_activeFrame, self.x, self.y, 0, 2.3, 2.3, (self.width * 1.2) + 10, 23)
    end
Which was working ok back on my original code apart from the issue that I had originally. Which would make the sprite disappear if I held "u" down.

I also updated the keypress() and key release() to only change the actionInputs and not the character attributes
e.g.

Code: Select all

if key == "u" then
        --P1_punch.jab = true
        -- P1.jab = true
        actionInputs.jab = true
        -- P1.punchActive = love.timer.getTime()
    end
    

I have tried many different variations of this logic but could not make it work :(
RNavega
Party member
Posts: 251
Joined: Sun Aug 16, 2020 1:28 pm

Re: punch animation - keypressed, keyreleased

Post by RNavega »

Edit: in the draw function, is P1_jab nil, or is jab_activeFrame?
For the latter to be nil, it means that the jabFrames table is being indexed with some key that leads to no value, therefore it returns nil.
Remember that in Lua tables are usually indexed from 1 and above, see if that's not the cause. You also need to reset jab_currentFrame to 1 right when the jab starts.

PS you shouldn't set self.jab to false in case actionInputs.jab is false. Those are two separate things: what's happening in the game vs. what the player wants the game to do, which won't always happen. For instance, if the player is pressing actionInputs.moveRight so they want to move right, but the character might be facing a wall and won't move -- the game has a final say on what happens (in this case, nothing).
The jab ends when the next frame of animation is outside the range, so you know that all frames were properly displayed before. That's when you set self.jab to false and change state to idle or whatever (unless you're going for something like the character stays with their arm extended until you let go of the key, which would be a different logic)
Rickfg
Prole
Posts: 6
Joined: Tue Nov 14, 2023 2:23 pm

Re: punch animation - keypressed, keyreleased

Post by Rickfg »

RNavega wrote: Fri Nov 17, 2023 7:04 pm Edit: in the draw function, is P1_jab nil, or is jab_activeFrame?
For the latter to be nil, it means that the jabFrames table is being indexed with some key that leads to no value, therefore it returns nil.
Remember that in Lua tables are usually indexed from 1 and above, see if that's not the cause. You also need to reset jab_currentFrame to 1 right when the jab starts.

PS you shouldn't set self.jab to false in case actionInputs.jab is false. Those are two separate things: what's happening in the game vs. what the player wants the game to do, which won't always happen. For instance, if the player is pressing actionInputs.moveRight so they want to move right, but the character might be facing a wall and won't move -- the game has a final say on what happens (in this case, nothing).
The jab ends when the next frame of animation is outside the range, so you know that all frames were properly displayed before. That's when you set self.jab to false and change state to idle or whatever (unless you're going for something like the character stays with their arm extended until you let go of the key, which would be a different logic)
Thank you very much for your help! I have managed to make it work as I intended now :awesome:
I had forgotten to re-set the jab_elapsedTime back to 0 after the frame was drawn.

Thank you once again!
Post Reply

Who is online

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