MarcoFazioRandom / Virtual-Joystick-Godot

A simple virtual joystick for touchscreens, for both 2D and 3D games, with useful options.
MIT License
742 stars 81 forks source link

Only 8 directions when using input actions #65

Closed PinkSerenity closed 8 months ago

PinkSerenity commented 10 months ago

When using input actions, using Input.get_vector() (which apparently got fixed) as well as Input.get_axis() results in vectors that are 8-directional. I updated the following methods to fix it:

func _update_joystick(touch_position: Vector2) -> void:
    var _base_radius = _get_base_radius()
    var center : Vector2 = _base.global_position + _base_radius
    var vector : Vector2 = touch_position - center
    vector = vector.limit_length(clampzone_size)

    if joystick_mode == Joystick_mode.FOLLOWING and touch_position.distance_to(center) > clampzone_size:
        _move_base(touch_position - vector)

    _move_tip(center + vector)

    if vector.length_squared() > deadzone_size * deadzone_size:
        is_pressed = true
        output = (vector - (vector.normalized() * deadzone_size)) / (clampzone_size - deadzone_size)

        if use_input_actions:
            if output.x > 0:
                Input.action_press(action_right, output.x)
                Input.action_release(action_left)
            else:
                Input.action_press(action_left, -output.x)
                Input.action_release(action_right)

            if output.y > 0:
                Input.action_press(action_down, output.y)
                Input.action_release(action_up)
            else:
                Input.action_press(action_up, -output.y)
                Input.action_release(action_down)

    else:
        is_pressed = false
        output = Vector2.ZERO
        if use_input_actions:
            _reset_input()

func _reset():
    is_pressed = false
    output = Vector2.ZERO
    _touch_index = -1
    _tip.modulate = _default_color
    _base.position = _base_default_position
    _tip.position = _tip_default_position
    if use_input_actions:
        _reset_input()

func _reset_input():
    for action in [action_left, action_right, action_down, action_up]:
        Input.action_release(action)
MarcoFazioRandom commented 10 months ago

Hello,

Can you explain better what is the problem? maybe with a example code or screenshots?

BryanWi commented 9 months ago

Hello, I have experienced the same bug, which apparently wasn't a thing in an older version I was using on other project.

In the current version published on the Godot AssetLib the joystick output value is being rounded to pointing at an angle of 0°, 45°, 90°, 135°, 180°, 225°, 270° or 315°. The output is still normalized but the values will always point to one of those angles.

The joystick seems right on the UI but when using the output, either directly or through the Godot Inputs, the movement of players doesn't look continuous as it is changing at 45° intervals.

Using the functions shared by @PinkSerenity seems to fix this problem so far, removing the rounding and making the output more continuous. Thanks for checking this issue :)

PinkSerenity commented 9 months ago

Sorry, I was pretty stressed out because of the university project I had used the asset for, and then unfortunately forgot about it until @BryanWi reminded me again. Code is a simple set_point_position(1, vector * 100) with the coordinates of the starting point being (0,0).

https://github.com/MarcoFazioRandom/Virtual-Joystick-Godot/assets/41524896/b89614cc-c940-406d-88d4-0d82a4c33680

By the way Input.get_vector() seems to be fixed, I didn't notice any difference when using that.

PinkSerenity commented 9 months ago

I could pinpoint the issue in the original code:

func _update_input_action(action:String, value:float):
    if value > InputMap.action_get_deadzone(action):
        Input.action_press(action, value)
    elif Input.is_action_pressed(action):
        Input.action_release(action)

I think the issue is that value already has deadzone_size applied to it (the function is called with output.x or output.y respectively, which are calculated with deadzone_size) and now you check the built-in deadzone variable. if value > 0 fixes that, but then the built-in deadzone is ignored completely. The alternative is ignoring deadzone_size and calculating with Input.action_get_deadzone() instead. I'd argue that if people set deadzone_size in the inspector, they want and expect that one to be applied.