Wall crawling enemy ignores corners

Godot Version

4.7

I want to have a wall crawler who just moves alongside the world (like in mario world or this one of Zelda Oracle of Ages)

. He should only move in the direction he faces and turn at corners, so his feet always touch a surface.

Right now he just moves right (he´s facing right), ignores corners and passes through walls.

(The rays correctly detect ground and walls) Here he should turn. But he doesnt)

extends AnimatableBody2D

@exportexport var speed :=@onready30.0
@onready var ground_ray: RayCast2D = %RayCast2D
@onready var wall_ray: RayCast2D = %RayCast2Dforward

func _ready() → void:
ground_ray.hit_from_inside = true
wall_ray.hit_from_inside = true

func _physics_process(delta: float) → void:
wall_ray.force_raycast_update()
ground_ray.force_raycast_update()


if wall_ray.is_colliding():
	print(">>> turning +90 (wall)")
	turn(90)
elif not ground_ray.is_colliding():
	print(">>> turning -90 (edge)")
	turn(-90)

position += transform.x * speed * delta

func turn(angle_deg: float) → void:
var pivot := wall_ray.global_position if angle_deg > 0 else ground_ray.global_position
rotation_degrees += angle_deg
global_position = pivot + (global_position - pivot).rotated(deg_to_rad(angle_deg))

wall_ray.force_raycast_update()
ground_ray.force_raycast_update()

In the forum post editor you can select your block of code and hit the </>button to format it all real nice.

I think you need to another wall_ray that’s offset into the tile that the wall crawler would move to next.

Hey thank you Ananasblau :slight_smile: I edited the formatting.
So the issue with the wall crawler is, that he ignores the corner (at the bottom-right) and just moves to the right.
Can you see a logic issue with my code?

If you’re using a tilemap, you don’t need raycasts. Just move tile by tile, check the configuration of adjacent tiles and proceed accordingly.

Thanks for your hint normalized :slight_smile: I really appreciate that.

It works a little, but I cant fully solve it. This is where he goes now and where he stops.

I feel like I´m overcomplicating. Can you tell me a simple solution?

extends AnimatableBody2D

@export var speed := 30.0

var tilemaplayer: TileMapLayer
var got_tilemap := false
var cell
var data : TileData
var check_which_side := 3 #left right up down

func _ready() -> void:
	#check_for_special_tiles(Vector2(0,8))
	pass
	
func _process(delta: float) -> void:
	if got_tilemap == false:
		if GlobalSignals.tilemap_is_ready:
			tilemaplayer = get_tree().get_first_node_in_group("tilemap")
			#check_for_special_tiles(Vector2(0,8))
			got_tilemap = true
	else:
		if check_which_side == 0:
			check_for_special_tiles(Vector2(-16, -8)) #check left
			if data != null:
				global_position += Vector2(0, 16)
			else:
				#rotation_degrees += 90
				global_position += Vector2(-16, 32) #why does 32 work and not 0?
				check_which_side = 2
		if check_which_side == 2:
			check_for_special_tiles(Vector2(0, -24)) #check upwards
			if data != null:
				global_position += Vector2(-16, 0)
			else:
				#rotation_degrees += 90
				#global_position += Vector2(-16, 32)
				check_which_side = 1
				pass
		if check_which_side == 3:
			check_for_special_tiles(Vector2(0, 8)) #check underneath
			if data != null:
				global_position += Vector2(16, 0)
			else:
				#rotation_degrees += 90
				global_position += Vector2(0, 16)
				check_which_side = 0

func check_for_special_tiles(extra_coords: Vector2): #extra coords to look at a shifted place
	if tilemaplayer != null:
		var cell := tilemaplayer.local_to_map(tilemaplayer.to_local(global_position + extra_coords))
		data = tilemaplayer.get_cell_tile_data(cell)

		return data

You’ll need to devise a simple algorithm.

Think about it from the perspective of the character. It can make only three moves: turn clockwise, turn counterclockwise or continue forward. Which move will it make depends on the tile configuration in front of it:

  • if the forward tile is occupied - it’s an inner corner - turn 90 deg counterclockwise
  • if the below tile is empty - it’s an outer corner - turn 90 deg clockwise
  • otherwise - continue forward.

To determine forward and down directions you can use node’s global basis axes. If you take global_transform.x to be forward, then global_transform.y will be down.