What is the simplest way to create more than 8 directions for a Gamepad Controller ?
Hi, I am new in the coding world, I am currently developing a 3D platformer and I try to improve my 3D character controller. Especially for use it with the left stick of a gamepad. I think there is something missing in my code, something around angles but I did not find the answer.
So I am asking you please, how can I do my movement direction not just follows 8 lines/vectors?
To be clear : I want to move my character not only at 45° in diagonal direction, when I turn my stick I would like to take each possible angle directions between 0° and 45°, if my stick points between this two values.
I worked around lerp and clamp but nothing conclusive… Naively I think that the movement direction must be set by a variable angle between the 8 directions. And I do not know how to do.
In advance, I am sorry if this is confusing, coding is still a bit confusing for me, but I want to learn more.
You will find my script bellow and sorry but as a new member of this forum I can not upload a video to put my words into pictures :
extends CharacterBody3D
@onready var animation = $AnimationPlayer
@export var speed = 5
@export var jump_impulse = 20
@export var acceleration = 2
@export var fall_acceleration = 75
func _physics_process(delta):
var direction = Vector3.ZERO
if Input.is_action_pressed("right"):
direction.x += 1.0
if Input.is_action_pressed("left"):
direction.x -= 1.0
if Input.is_action_pressed("down"):
direction.z += 1.0
if Input.is_action_pressed("up"):
direction.z -= 1.0
# Move and rotate the character in direction.
if is_on_floor() and direction != Vector3.ZERO:
direction = direction.normalized()
rotation.y = lerp_angle(rotation.y, atan2(-direction.z, direction.x), delta * 10)
if Input.is_action_pressed("speed"):
animation.play("Run")
else:
animation.play("Walk")
else:
animation.play("Idle")
# Ground velocity.
if Input.is_action_pressed("speed"):
velocity.x = direction.x * speed * acceleration
velocity.z = direction.z * speed * acceleration
else:
velocity.x = direction.x * speed
velocity.z = direction.z * speed
# Jumping.
if is_on_floor() and Input.is_action_just_pressed("jump"):
velocity.y = jump_impulse
# Air velocity.
if not is_on_floor():
velocity.y = velocity.y - (fall_acceleration * delta)
move_and_slide()
You can replace your if-else block checking the directions by Input.get_vector(). This accommodates for both, digital keyboard and analog controller input.
You mean under #Move and rotate the character in direction block? I tried but same issue. If you can tell me more details I will appreciate that so much because I do not know really how to do what you say… Sorry I do not have all the logic!
This should be replaced with get_vector, then converting to a vector3
func _physics_process(delta):
var direction: Vector2 = Input.get_vector("left", "right", "up", "down")
var direction3 := Vector3(direction.x, 0, direction.y)
normalized is ruining your controller support, it set the direction vector length to always be 0.0 or 1.0, no in-between half-tilt movement is possible. It’s also unnecessary when using Input.get_vector. You can use the Vector2.angle instead of atan2 as well. lerp and it’s derivitives should not be used with delta it actually makes it more frame-dependant, the third parameter represents a percentage between a and b which changes every frame.
The rest of your script looks great!
Here’s the changes I’d recommend all together
func _physics_process(delta: float) -> void:
var direction: Vector2 = Input.get_vector("left", "right", "up", "down")
var direction3 := Vector3(direction.x, 0, direction.y)
# Move and rotate the character in direction.
if is_on_floor() and direction != Vector2.ZERO:
rotation.y = lerp_angle(rotation.y, direction.angle(), 0.1)
if Input.is_action_pressed("speed"):
animation.play("Run")
else:
animation.play("Walk")
else:
animation.play("Idle")
# Ground velocity.
if Input.is_action_pressed("speed"):
velocity.x = direction3.x * speed * acceleration
velocity.z = direction3.z * speed * acceleration
else:
velocity.x = direction3.x * speed
velocity.z = direction3.z * speed
# Jumping.
if is_on_floor() and Input.is_action_just_pressed("jump"):
velocity.y = jump_impulse
# Air velocity.
if not is_on_floor():
velocity.y = velocity.y - (fall_acceleration * delta)
move_and_slide()
If you are still recieving only 8-directional inputs then your controller may be gated rather than using potentiometers, like a arcade fight stick.
Thank you so much! It works but now my rotation on the Z axis is inverted. When I move up my character turn back and look at me, inversely if I move down he look at the top.
Edit: when I change the order in the get_vector() entry that change the direction so I switched “up” and “down” but nothing to do for looking rotation
You can flip the get_vector actions for “up” and “down” if an axis is inverted. The angle could be different from what you expect if your player’s visual isn’t facing the forward -z axis.