Search code examples
godotgdscript

GDScript: Rotation function doesn't work properly


I'm trying to program a "Pong"-like game in Godot 3.5. In contrast to the classic Pong game, i want to establish the possibility to slightly rotate the Pong-bars to get more variety. After the player sets some rotation to the Pong bar, I want to correct it slowly to the starting point with an rotation of 0. Somehow, my correction-function does only work on negative rotation_degrees ("left"). If there are positive rotation_degrees ("right"), no correction applies.

My code looks like this. I was already looking for bugs with the print-method. The function correction is setting the rotation_direction to -0.2 as it should, but somehow the rotation_direction in the line above (see my bug comment) seems to keep the value 0.

extends KinematicBody2D

var rotation_direction = 0
var max_rotation = 10

func _physics_process(delta):

    if Input.is_action_pressed("right"):
        rotation_direction = 0.5
    
    if Input.is_action_pressed("left"):
        rotation_direction = -0.5
    

    rotation_degrees += rotation_direction ##possible bug?
    rotation_degrees = clamp(rotation_degrees, -max_rotation, max_rotation)
    correction()

func correction():
    if rotation_degrees > 0:
        rotation_direction = -0.2
    if rotation_degrees < 0.0:
        rotation_direction = 0.2
    else:
        rotation_direction = 0.0

Solution

  • Wrapping rotation

    This is not the issue at hand. As the issue I address here would only happen on large rotations. Yet, it will help other people with similar problems. And it will dispel your suspicions on the bug.

    General advice is to wrap your angles, like this:

    wrapf(angle, -PI, PI)
    

    or for degrees:

    wrapf(angle_degrees, -180, 180)
    

    So you can let the angle accumulate, and when you need to check it, you wrap it, so you have them representing the rotation the short way around. For example a three quarters turns becomes a negative quarter turn.

    Which is important if you are going to limit the rotation to a range:

    rotation_degrees = clamp(wrapf(rotation_degrees, -180, 180), -max_rotation, max_rotation)
    

    The bug

    In correction, rotation_direction will either be 0.2 or 0.0:

    func correction():
        if rotation_degrees > 0:
            rotation_direction = -0.2
        if rotation_degrees < 0.0:
            rotation_direction = 0.2
        else:
            rotation_direction = 0.0
    

    Yes, you have this if statement:

        if rotation_degrees > 0:
            rotation_direction = -0.2
    

    Yet, no matter what, the second if statement will run, and the execution flow will enter one of it branches:

        if rotation_degrees < 0.0:
            rotation_direction = 0.2
        else:
            rotation_direction = 0.0
    

    Minimal change would be to use elif:

    func correction():
        if rotation_degrees > 0:
            rotation_direction = -0.2
        elif rotation_degrees < 0.0:
            rotation_direction = 0.2
        else:
            rotation_direction = 0.0
    

    However, I would encourage to use sign (signf in Godot 4):

    func correction():
        rotation_direction = -sign(rotation_degrees) * 0.2
    

    Here sign will return either -1, 0 or 1 representing the sign of rotation_degrees. We negate it to get the opposite sign. And once multiplied with 0.2 the possible values are 0.2, 0.0, and -0.2.

    Yes, you might wrap the angle there too:

    func correction():
        rotation_direction = -sign(wrapf(rotation_degrees, -180, 180)) * 0.2
    

    And since that is just one line, you might consider doing it in _physics_process instead of having a separate correction method.