2D character movement bug

I have a simple 2D top-down game. I want to be able to move my character up down left and right with the directional keys and I want the most recently pressed key to supersede any earlier key being held. I found this code that creates a kind of key press “queue” (array direction_history) and appends the most recently pressed key direction to the “front” of the “queue” while removing earlier elements when a key is released:

const PLAYER_SPEED = 175
const MOVEMENTS = {
	'ui_up': Vector2.UP,
	'ui_left': Vector2.LEFT,
	'ui_right': Vector2.RIGHT,
	'ui_down': Vector2.DOWN
var direction_history = []
velocity = Vector2.ZERO

func _process(delta):
	for direction in MOVEMENTS.keys():
		if Input.is_action_just_released(direction):
			var index = direction_history.find(direction)
			if index != -1:
				direction_history.remove_at(index)
		if Input.is_action_just_pressed(direction):
			direction_history.append(direction)
	if direction_history.size():
		var direction = direction_history[direction_history.size() - 1]
		velocity = MOVEMENTS[direction]
	velocity = velocity.normalized() * PLAYER_SPEED
	move_and_collide(velocity * delta)

I’ll be honest, I am kind of a moron and don’t totally understand how this works. If someone can explain it that would be helpful.

Anyway, the real problem is sometimes it bugs out and gets stuck permanently going one direction. I suspect this is because for some reason godot didn’t register a key release and remove_at the previous key entry from the direction_history array. But I don’t understand exactly why this is happening or how to fix it.

Any help would be appreciated! I am also open to alternate code/solutions. Thanks :slight_smile:

you can use a more simple code instead of this.

like :

var velocity=Vector2(0,0)
var PLAYER_SPEED=175
func _process(delta):
    if Input.is_action_just_pressed(ui_up):
        velocity.y=-PLAYER_SPEED
    if Input.is_action_just_pressed(ui_right):
        velocity.x=PLAYER_SPEED
    if Input.is_action_just_pressed(ui_left):
        velocity.x=-PLAYER_SPEED
    if Input.is_action_just_pressed(ui_down):
        velocity.y=PLAYER_SPEED

Hi, this isn’t working - I want the player to be able to hold down the movement key not have to press them over and over again.

Edit: changed “is_action_just_pressed” to “is_action_pressed” - but the problem now is I do not want diagonal movement. I just want the most recently pressed directional key to supersede whatever was happening before

This is very simple smooth movement with acceleration:

@export var speed = 100
@export var accel = 10

func _physics_process(delta):
	var direction: Vector2 = Input.get_vector("MoveLeft", "MoveRight", "MoveUp", "MoveDown")
	
	velocity.x = move_toward(velocity.x, speed * direction.x, accel)
	velocity.y = move_toward(velocity.y, speed * direction.y, accel)

	move_and_slide()

if you dont want the acceleration functionality, replace the

velocity.x = move_toward(velocity.x, speed * direction.x, accel)
velocity.y = move_toward(velocity.y, speed * direction.y, accel)

with this:

velocity = speed * direction

Thanks for your reply, I do not need acceleration, also I do not want diagonal movement.

This is not working for me for some reason - what am I doing wrong?

const PLAYER_SPEED = 175
var direction: Vector2 = Input.get_vector(“MoveLeft”, “MoveRight”, “MoveUp”, “MoveDown”)

func _process(delta):
velocity = PLAYER_SPEED * direction
move_and_collide(velocity * delta)

if you want to remember the previous direction you can do the following:

@export var speed = 100

var direction_name: String = "ui_up"
var direction : Vector2

func _physics_process(delta):
	if not Input.is_action_pressed(direction_name):
		get_new_input()

	velocity = direction * speed
	move_and_slide()

func get_new_input():
	if Input.is_action_pressed("ui_up"):
		direction = Vector2(0, 1)
		direction_name = "ui_up"
	elif Input.is_action_pressed("ui_right"):
		direction = Vector2(1, 0)
		direction_name = "ui_right"
	elif Input.is_action_pressed("ui_left"):
		direction = Vector2(-1, 0)
		direction_name = "ui_left"
	elif Input.is_action_pressed("ui_down"):
		direction = Vector2(0, -1)
		direction_name = "ui_down"
	else:
		direction = Vector2.ZERO

This should do the trick

1 Like

The reason why yours doesnt work is because first of all you dont call Input.get_vector every frame so your input wont be updated. second of all you call physics-code in the process-method instead of the physics_process method

My last message should be what you are looking for

I actually got this to work - how do I prevent diagonal movement?

This should be easily fixed by using just_pressed instead of pressed:

var speed = 100

var direction_name = "ui_up"
var direction: Vector2

func _physics_process(delta):
	get_new_input()

	velocity = direction * speed
	move_and_slide()

func get_new_input():
	if Input.is_action_just_pressed("ui_up"):
		direction = Vector2(0, 1)
		direction_name = "ui_up"
	elif Input.is_action_just_pressed("ui_right"):
		direction = Vector2(1, 0)
		direction_name = "ui_right"
	elif Input.is_action_just_pressed("ui_left"):
		direction = Vector2(-1, 0)
		direction_name = "ui_left"
	elif Input.is_action_just_pressed("ui_down"):
		direction = Vector2(0, -1)
		direction_name = "ui_down"
	elif not Input.is_action_pressed(direction_name):
		direction = Vector2.ZERO

actually this still has a problem
Just edited. Try this version

yes i think so

This works great! Thank you so much.

One additional nuance (more of a nice to have) - when a player presses a second directional key, instead of moving diagonally your code stops the old key entry and prioritizes the new key entry (which is good). If, however, the new key is released, I actually want the direction to go back to the previous key entry that is still being held down. With your function the player just stops moving even though the prior key is still being held down. I suspect that is what the former code I found was trying to accomplish by retaining a queue of key presses.

In this case you would have to remember all key presses
EDIT:
no sorry you have to check in the last elif in any keys are currently pressed

What do you mean by “check if any key is current pressed”? What would that code look like?

Edit: NVM I understand now. Here is the code I appended, maybe there is a better way but this works for me:

func get_new_input():
	if Input.is_action_just_pressed("ui_up"):
		direction = Vector2(0, -1)
		direction_name = "ui_up"
	elif Input.is_action_just_pressed("ui_right"):
		direction = Vector2(1, 0)
		direction_name = "ui_right"
	elif Input.is_action_just_pressed("ui_left"):
		direction = Vector2(-1, 0)
		direction_name = "ui_left"
	elif Input.is_action_just_pressed("ui_down"):
		direction = Vector2(0, 1)
		direction_name = "ui_down"
	elif not Input.is_action_pressed(direction_name):
		if Input.is_action_pressed("ui_up"):
			direction = Vector2(0, -1)
			direction_name = "ui_up"
		elif Input.is_action_pressed("ui_right"):
			direction = Vector2(1, 0)
			direction_name = "ui_right"
		elif Input.is_action_pressed("ui_left"):
			direction = Vector2(-1, 0)
			direction_name = "ui_left"
		elif Input.is_action_pressed("ui_down"):
			direction = Vector2(0, 1)
			direction_name = "ui_down"
		else:
			direction = Vector2.ZERO
1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.