Godot Version
v4.6.2
Question
Hi, I’m new to Godot and programming as a whole and I need help. I feel like the answer to this is very obvious but I just can’t figure it out.
My game an animal ecosystem based on a grid - though it’s not technically on a grid but I sort of ‘simulate’ a grid by only allowing the player to move the exact tile size and spawning them in the direct center of the tile. There’s no move_and_slide or physics (at least I don’t think?), because my player character is an Area2D shape and moves a specified amount towards x or y based on input. The player reads a Move/Input script to move. Here’s what it looks like:
The only collisions in the entire game will be with the walls on the outside of the map - which are on the same mask/layer as the player.
The MapBorder is a RigidBody2D and for the collision I used a polygon.

My problem is this:
I need a way for my player character to not move the direction the raycast is facing if the raycast is colliding. The raycast is doing a great job - it tells me when it collides with the map boundary but I am at a loss for how to tell it to specifically not move the direction the raycast is colliding in.
Disclaimer: I know Area2D shapes don’t collide - they just recognize other shapes, so an easy solution would be to switch the player to a CharacterBody2D but it didn’t work when I tried (though I might’ve done it wrong) and I want to give the Area2D a shot.
My code for the Movement + Input is below. It’s located inside that MoveInputComponent node in the first image.
My trouble is towards the bottom of the code, under the func change_ray, specifically in the ‘if ray is colliding’ section
class_name MoveInputComponent extends Node
#coded specifically for player movement
@onready var area: Area2D = $".."
@onready var sprite
@onready var ray: RayCast2D = $"../RayCast2D"
var last_direction = Vector2.RIGHT #starting position of raycast
var tile_size = 64
var SPECIES_COOLDOWN: float
#CODE THAT I KNOW WORKS IS BELOW THIS LINE
func _input(event):
if event.is_action_pressed("left"):
area.global_position.x -= tile_size # move left on map
move_delay() #delay movement by species amount
#sprite.flip_h = true
if event.is_action_pressed("right"):
area.global_position.x += tile_size
move_delay()
#sprite.flip_h = false
if event.is_action_pressed("up"):
area.global_position.y -= tile_size
move_delay()
if event.is_action_pressed("down"):
area.global_position.y += tile_size
move_delay()
change_ray()
func change_ray(): #updating ray position + collision
var input_dir = Input.get_vector("left", "right", "up", "down") #get position facing
if input_dir != Vector2.ZERO: #if input is not nothing:
last_direction = input_dir #last_direction is last direction
ray.target_position = last_direction * tile_size #ray faces last direction
ray.force_raycast_update() #raycast updates position
if !ray.is_colliding():
pass #don't know what to do here
func move_delay(): #creates a delay between when player can press the input again
if not (move_and_collide(motion, true)): (insert code to move)
the above piece of code can be used to check for collisions. First you just declare a variable that stores the direction (i called it motion here), then use the check above.
var motion: Vector2.Zero
if Input.is_action_pressed(“up”):
motion.y = -1
moved_recently()
elif Input.is_action_pressed(“down”):
motion.y = 1
moved_recently()
elif Input.is_action_pressed(“left”):
motion.x = -1
moved_recently()
elif Input.is_action_pressed(“right”):
motion.x = 1
moved_recently()
if motion == Vector2.ZERO: return
if not (move_and_collide(motion, true))
area.global_position += motion * tile_size
i’m new myself so the code might not run without bugs, but the basic idea is to implement the check before the actual movement.
1 Like
try if $Raycast2D.is_colliding():
–whatever to make movement stop
rather than a while not.
1 Like
see my code for movement in my own game;
func _physics_process(_delta: float) -> void:
#Animator
animation_controller()
if Snes.hero_movable == true:
if sliding_true:
return
if Input.is_action_pressed("UP"):
dir = "up"
if COLLIDE_UP.is_colliding():
return
else:
moving = true
move(Vector2(0, -Snes.tilesize))
elif Input.is_action_pressed("DOWN"):
dir = "down"
if COLLIDE_DOWN.is_colliding():
return
else:
moving = true
move(Vector2(0, Snes.tilesize))
elif Input.is_action_pressed("LEFT"):
dir = "left"
if COLLIDE_LEFT.is_colliding():
return
else:
moving = true
move(Vector2(-Snes.tilesize, 0))
elif Input.is_action_pressed("RIGHT"):
dir = "right"
if COLLIDE_RIGHT.is_colliding():
return
else:
moving = true
move(Vector2(Snes.tilesize, 0))
else:
moving = false
body.speed_scale = 1
arms.speed_scale = 1
func move(offset: Vector2):
if Snes.hero_movable == true:
sliding_true = true
var target_pos = (position + offset).snapped(Vector2(Snes.tilesize, Snes.tilesize))
var tween = create_tween()
body.speed_scale = 2
arms.speed_scale = 2
tween.tween_property(self, "position", target_pos, speed).set_trans(Tween.TRANS_LINEAR)
tween.tween_callback(func(): sliding_true = false)
And as for CharacterBody2ds, yes, grid based collision wont work with thems, i still use them for the heck of it,
and yes it is probably innefficient that i am using 4 raycasts, but there is a reason i did that one—not to say you should
#Snes.tilesize is a global integer variable of value 8, just FIY
1 Like
Thank you both you were such a huge help! Seeing yall’s code helped a bunch.
I added a check for seeing if the ray was colliding before every single input and if it was, then return. That did the trick but then if you tapped fast enough, you could go sideways thru the wall - so I moved the function that changed the ray to BEFORE that.
And it’s finally working holy cow. Thank you so much.
I can’t believe I had the check for the ray collision in the wrong spot the whole time haha!
Here’s what it ended up looking like:
if event.is_action_pressed("left"):
change_ray()
if ray.is_colliding():
return
else:
player.global_position.x -= tile_size
1 Like
Do you think it’s worth it to set up an actual grid and snap my character to it or is just moving them by the tile size everywhere fine?
1 Like
Happy to help!
As for grids, I am not sure how you would go about setting a LITERAL grid,
I just make the grid-based movement and I paired it by making my game a low resolution, but that is also because it is designed with Super NES in mind,
If your game is a pixel game that has no modern special effects, then go for it, but i would recommend calculating the ratio based off of the current screensize in that situation.
If you want higher resolution game, then just be careful about moving the player 
Note, moving the character out of the grid will offput the collisions and everything, not a big deal generally, but it is something to consider.
1 Like
This has nothing to do with the actual question, but you might want to look up TileMapLayer node eventually. It will let you build your entire world easily and fits the grid thing perfectly. It’s a bit tough at first, but quite powerful if you stick with it.
Another thing you can do right now is to set up a sort of global class to hold all your game constants. The tile size variable or constant is going to get really annoying for you to keep constant throughout your project. This also has the advantage of letting you change your tile size for everything in the game in one place. If you don’t already know it, you set this up by right clicking the file dock, then add new script, then at the top you write class_name GameConstants (or whatever name you like)
class_name GameConstants
const TILE_SIZE: int = 64
When you want it in any script, you just go GameConstants.TILE_SIZE. It’s very convenient.
Yes! I do use TileMapLayers because I saw TileMap was deprecated, and I found I liked how I could layer the environment with them. Right now all my art/tilesets are from itch.io but I would like to eventually get some sort of grid going later - even if it’s just a mostly transparent TileMapLayer grid. The game I’m creating is inspired by a Java grid game from the early 2000s - so it’s not necessary for me to have a grid but something I would like.
As for the constants - thank you that is a great idea actually and I will be implementing it as soon as possible.
1 Like