Timer never reaches zero? And also character never moves?

Godot Version

4.3

Question

For my first game, I’m attempting to recreate Pac-Man. In order to recreate the fact that he constantly moves, I had the keys simply change Pac-Man’s direction rather than move him directly, and he automatically moves based on that. That worked well, but I realized that there was no grid snapping like in the original, so it was easy to get caught on walls. The only way I could figure to recreate it was to block the players input for a certain amount of time until Pac-Man was in the center of a tile. I have his speed at 100 and my tiles are 16x16, I set the timer to .16 seconds, and of course set it to one shot.

So every time the player moves, it blocks inputs and starts the timer. When the timer runs out, it’s supposed to unblock the input. But upon starting, not only does Pac-Man no longer move at all, but the input is blocked permanently, and the timer seems to never run out. Here’s the code for movement:

func _physics_process(delta):
	if allow_input == true:
		if direction == 0:
			if ray_cast_right.is_colliding():
				animated_sprite.play("Idle")
			else:
				position.x += SPEED * delta
				animated_sprite.play("Right")
				timer.start()
				allow_input = false
				print("no move")
		if direction == 1:
			if ray_cast_down.is_colliding():
				animated_sprite.play("Idle")
			else:
				position.y += SPEED * delta
				animated_sprite.play("Down")
				timer.start()
				allow_input = false
				print("no move")
		if direction == 2:
			if ray_cast_left.is_colliding():
				animated_sprite.play("Idle")
			else:
				position.x -= SPEED * delta
				animated_sprite.play("Left")
				timer.start()
				allow_input = false
				print("no move")
		if direction == 3:
			if ray_cast_up.is_colliding():
				animated_sprite.play("Idle")
			else:
				position.y -= SPEED * delta
				animated_sprite.play("Up")
				timer.start()
				allow_input = false
				print("no move")
		
	move_and_slide()

And then the timer

func _on_timer_timeout():
	allow_input = true
	print("you can move")

Without seeing the whole script, it’s hard to fully see what’s going on. A couple of questions:

  1. Do you get anything printing out when trying to move?
  2. Is allow-input set to true at the beginning, otherwise unless the timer is set to autostart then it will never be able to be true.
  1. The “no move” print does pop up, but not the “you can move” one
  2. It is set to true by default

Here’s the rest of the code if you need it, all set before the above code

extends CharacterBody2D
@onready var animated_sprite = $AnimatedSprite2D
@onready var ray_cast_right = $RayCastRight
@onready var ray_cast_down = $RayCastDown
@onready var ray_cast_left = $RayCastLeft
@onready var ray_cast_up = $RayCastUp
@onready var timer = $Timer
var allow_input = true

#0 right, 1 down, 2 left, 3 up
var direction = 0
const SPEED = 100

func _process(delta):
	if Input.is_action_just_pressed("right"):
		direction = 0
	if Input.is_action_just_pressed("down"):
		direction = 1
	if Input.is_action_just_pressed("left"):
		direction = 2
	if Input.is_action_just_pressed("up"):
		direction = 3
1 Like

Do you get multiple “no move” messages" if you try to move a few times? Or just one at the beginning?
It also may be worth

  1. deleting and recreating the on_timeout signal connection
  2. moving the timer.start() above the line that plays the animation

Just the one at the beginning.

Neither of your suggestions changed anything.

Try putting the allow_input = false above timer.start() If that doesn’t work then I’m not sure, sorry, maybe someone else knows what is going on.

Still nothing. Oh well, thanks for trying

Hello @PlayerZeroStart !
I’ve been looking at your code and I’m wondering if the player is always colliding. If that’s the case, there is no way you can start the timer because the condition can’t be false. Can you try to print something to check your raycast is working properly? Something like:

if ray_cast_up.is_colliding():
				print("Is colliding")
				animated_sprite.play("Idle")

Add this line to every condition to check if it is colliding or not (my bet is it is always colliding).

Let me know whatever you find.

So I figured that shouldn’t be the issue, cause the code is only checking for collision in the direction Pac-Man is facing. I went ahead and tried it anyways just in case, but indeed, the colliding print never happens.

Ok, well we have somewhere to start working.
Something I’m also thinking is, the Idle status is something supposed to happen when the character is not moving or the player doesn´t interact with the game. But in your case, the Idle status is waiting for the player to interact. I don´t know if that is intended or not, I’m just pointing it out because it has caught my attention.

Let´s try to figure out what is happening.

  • Can you please check if your raycasts lenght is enough to collide with anything?
  • As I don´t know what it is supposed to hit, check if your raycasts Collide with > Areas and Collide with > Bodies are checked (I don´t know if you are trying to hit an StaticBody or an Area)

Can you check those?

So, because of how Pac-Man works, it’s always moving in some direction, and only stops when running into a wall. Initially, I tried to recreate this effect by checking if he was moving, but that didn’t work, so I swapped to this raycast method that worked.

I already know the raycasts work as intended from before I tried to do this timer method, but to answer your questions:

  • Yes, the raycasts are just long enough that while Pac-Man is in the center of a tile, the raycasts reach into the 4 surrounding tiles
  • The raycasts are set to collide with bodies but not with areas.

Wow that seems weird. So raycasts were working properly before you introduced the timer? :thinking:

Can you check if your code prints anything when you modify the direction variable? Like this:

if direction == 0:
			#Add this line
			print(str("Direction is ", direction))
			#-------------
			if ray_cast_right.is_colliding():
				animated_sprite.play("Idle")
			else:
				position.x += SPEED * delta
				animated_sprite.play("Right")
				timer.start()
				allow_input = false
				print("no move")

Maybe the input stopped working for some reason?

Could you send an screenshot of your node tree?

Upon doing that, Pac-Man’s direction is unchanging, it always displays the direction as 0.

The node tree:

image_2024-09-12_201239235

Ok, thats the node tree of the character, but a screenshot of your scene node tree would be useful.

Can you also check if the collision masks of each raycast is properly set up?

I am replicating something similar and it just works fine :thinking:

I haven’t updated the TileMap node to TileMapLayer yet, it seems to work fine anyways so I figured it’d be best to not mess with it.
image_2024-09-12_210030076

For the raycasts, their collision masks are set to 1, which matches with collision layer of the TileMap. Still not sure it’s a collision issue tho.

Can you show me the Timer configuration?

It seems to me that it is something related with physics. I was trying something similar but without tilemaps, tried with bodies and areas and worked great.
Can you toggle the Collide with > Areas as well in all raycasts to see if you get the hit this way? :thinking:

Here’s the timer configuration, everything else about it is on default
image_2024-09-13_083611581

I toggled the collide with areas thing and nothing changed. I even disabled all the collision on Pac-Man, both the Raycasts and the CollisionShape2D, and he’s still stuck in place.

Ok, now I recreated this using a simple Tilemap and my raycast can hit the wall successfuly.

Have you created a physic layer and created the collision shape under the tileset menu?

With this set and the raycasts Collide with > Bodies collisions should be working.