All Questions

Community

b
yousef bohadi

Character Sliding

Hello,

I seem to have a bug where the character continues to move a few pixels in the last directional input (left or right). This of course makes platforming difficult as the character tends to slide off the edges upon landing on small platforms.. Upon tracing this issue before, it seemed that the issue originated while the character is still in the air; if I jump while holding a direction arrow and the character jumps while running (as intended) then release the direction arrow midair, the character keeps their forward momentum despite the lack of input (this is an issue, as it makes it hard to land on targeted platforms), and as mentioned, upon landing, the character slides in the direction of the last directional input..

I should mention that I have a "Land State" that is separate from the Air State where the character shows a landing animation upon hitting the ground (I incorporated animations in the States shows in the course instead of having a separate AnimatioStateMachine. it seemed more organized to me to have the animations and movement mechanics close instead of separate..

to solve this issue in the past, I tried to implement a similar mechanic to the short-jump mechanic implemented in the beginners path first video (and I have discussed this in another question in the course):

https://www.youtube.com/watch?v=Mc13Z2gboEk

(implementing the short-jump mechanic starts after the 1 hour and 3 minutes mark) below is the code implemented based on the indie course + the short-jump mechanic from the begainners' path (the code works btw):

from the Move State:

func physics_process(delta): 
#for the short jump mechanic: 
var is_jump_interrupted: bool = Input.is_action_just_released("jump") and velocity.y < 0.0 velocity = calculate_velocity(velocity, max_speed, acceleration, delta, get_move_direction(), is_jump_interrupted, is_direction_interrupted_left, is_direction_interrupted_right) velocity = owner. move_and_slide(velocity, owner.FLOOR_NORMAL) 

static func calculate_velocity( old_velocity: Vector2, max_speed: Vector2, acceleration: Vector2, delta: float, move_direction: Vector2, is_jump_interrupted: bool ) -> Vector2: var new_velocity: = old_velocity new_velocity += move_direction * acceleration * delta new_velocity.x = clamp(new_velocity.x, -max_speed.x, max_speed.x) new_velocity.y = clamp(new_velocity.y, -max_speed.y, max_speed.y) 
#if statment below for interrupting the jump: 
if is_jump_interrupted: 
new_velocity.y = 0.0 
return new_velocity 

from the Air State:

func calculate_jump_velocity(impulse: float = 0.0) -> Vector2: var move: State = get_parent() return move.calculate_velocity( move.velocity, move.max_speed, Vector2(0.0, impulse), 1.0, Vector2.UP, false)

When I encountered the issue mentioned above, I implemented the following code at the bottom of the code seen above:

the Move State:

 func physics_process(delta): 
#for interrupting the movement in the x axis while jumping (variables used in the function calculate_velocity() below): 
var is_direction_interrupted_left:bool = Input.is_action_just_released("ui_left") var is_direction_interrupted_right:bool = Input.is_action_just_released("ui_right") 

static func calculate_velocity(... , is_direction_interrupted_left: bool, is_direction_interrupted_right: bool ) -> Vector2:
 . . 
if is_direction_interrupted_left: new_velocity.x = 0.0 if is_direction_interrupted_right: new_velocity.x = 0.0 return new_velocity

in the Air State:

func calculate_jump_velocity(impulse: float = 0.0) -> Vector2: 
..., false, false )

This code worked for a while; the character's momentum would stop midair if I stopped pressing the arrow button and just fall in place.. However, upon building more on the code, I noticed the following error message while playing the game:

_disconnect: Disconnecting nonexistent signal 'jumped', slot:1575:start.

This started appearing around the same time the sliding issue returned while moving the character..

Here is the current structure of the StateMachine in the project:

https://www.dropbox.com/s/qayer6smj50j33i/StateMachine.png?dl=0

I of course am posting this here in hopes of learning how to better organize my code and how to tackle a seemingly entangled issue. I am aware that it is not possible to fully grasp the entirety of the code with just what was written here, and thus it would not be feasible (or fare) to ask for a solution. In short, your input on how to begin tackling this matter would prove to be a valuable learning experience for me.

Thank you

  • Nathan Lovato replied

    It's hard to give you specific pointers right now, as the details of the code matter a lot in those situations.

    Here is some general feedback I can give you based on what I'm seeing.

    Separate concerns as much as you can

    Aim for code that segregates the states as much as possible. Initialize a state when you enter it, reset its values when you exit it, and ensure that as little data as possible transfers from one state to another. For example, for movement, you only want to transfer the velocity from one state to the next.

    Note also how in our code, we sometimes use the dictionary passed between states to do so: we explicitly pass the velocity between selected states.

    If the air movement logic becomes too different from the general Move one, you might want to not use move.physics_process, and completely override the movement in the Air state.

    Avoid logic with if statements whenever you can

    This point relates to the following line:

    var is_direction_interrupted_left := Input.is_action_just_released("ui_left")
    

    Whenever possible, I'd recommend avoiding conditional branches, and to use arithmetic instead. Conditional branches make it harder to track the flow of your code, as it can branch out in different directions, and with a growing number of combinations. You have more lines to run through with the debugger, etc.

    Here, you can calculate the direction like so:

    direction.x *= float(Input.is_action_just_released("ui_left")) * float(Input.is_action_just_released("ui_right"))
    

    Maybe only in the Air state's case. Booleans convert this way to integers or floats:

    • true: 1.0
    • false: 0.0

    Allowing you to do arithmetic with them, and removing the need for if/elif/else blocks in some cases.

    Use the debugger and breakpoints

    When tracking down a bug, you need to know exactly how to reproduce it, down to the lines that cause it. To do so, you often need to step through the code with the debugger.

    In the code editor, press F9 to insert a breakpoint on a given line. When the gdscript compiler reaches that line, it will pause the game and allow you to inspect the values at that point. You can then resume the game with F7, or move forward line by line with the buttons in the debugger bottom panel.

    You want to pinpoint the bug, then think about how to address it: patch the specific problem that causes it, refactor or rewrite some of your code... it depends on how far you want to push the project and how much that code is likely to change. If it's something you might tweak a lot, you'll learn towards refactoring, that is to say, rethinking how the code works. If it's something you'll barely touch again, you can patch it with a simple fix.