From f7463d57cd289f8c098836f70b397885e1c3bea1 Mon Sep 17 00:00:00 2001 From: Yogi Wiguna Date: Tue, 24 Mar 2026 16:15:00 +0800 Subject: [PATCH] feat: Implement `PlayerMovementManager` for grid-based movement, collision, and a push mechanic with Stop n Go mode rules. --- scenes/safe_zone_wall.tscn | 2 +- scripts/managers/player_movement_manager.gd | 29 ++++++++++++- scripts/managers/stop_n_go_manager.gd | 48 ++------------------- 3 files changed, 32 insertions(+), 47 deletions(-) diff --git a/scenes/safe_zone_wall.tscn b/scenes/safe_zone_wall.tscn index 09e7d9c..d8bae0c 100644 --- a/scenes/safe_zone_wall.tscn +++ b/scenes/safe_zone_wall.tscn @@ -11,7 +11,7 @@ material = SubResource("StandardMaterial3D_block") size = Vector3(1, 2, 0.1) [sub_resource type="BoxShape3D" id="BoxShape3D_wall"] -size = Vector3(1, 2, 0.1) +size = Vector3(0.5, 2, 0.1) [node name="SafeZoneWall" type="StaticBody3D" unique_id=393858776] collision_mask = 0 diff --git a/scripts/managers/player_movement_manager.gd b/scripts/managers/player_movement_manager.gd index 172fa27..7fe11b2 100644 --- a/scripts/managers/player_movement_manager.gd +++ b/scripts/managers/player_movement_manager.gd @@ -538,11 +538,36 @@ func _is_path_blocked_by_physics(from_grid: Vector2i, to_grid: Vector2i) -> bool if not player.is_inside_tree(): return false var space_state = player.get_world_3d().direct_space_state - - # 1. Path check: Block movement if a wall exists between the current and target tile var from_v3 = Vector3(from_grid.x + 0.5, 0.5, from_grid.y + 0.5) var to_v3 = Vector3(to_grid.x + 0.5, 0.5, to_grid.y + 0.5) + # DIAGONAL Leniency: If moving diagonally, use two offset rays to 'squeeze' past corners + var is_diagonal = (from_grid.x != to_grid.x) and (from_grid.y != to_grid.y) + + if is_diagonal and not player.get("is_invisible"): + var direction = (to_v3 - from_v3).normalized() + var perp = Vector3(-direction.z, 0, direction.x) * 0.2 # Offset by 20cm + + # Ray 1: Offset left + var q1 = PhysicsRayQueryParameters3D.create(from_v3 + perp, to_v3 + perp) + q1.collide_with_bodies = true + var r1 = space_state.intersect_ray(q1) + + # Ray 2: Offset right + var q2 = PhysicsRayQueryParameters3D.create(from_v3 - perp, to_v3 - perp) + q2.collide_with_bodies = true + var r2 = space_state.intersect_ray(q2) + + # If BOTH rays hit something that isn't the player, it's blocked + # If only ONE hits, it might be a thin corner/wall we can skirt around + if r1 and r2: + if r1.collider != player and r2.collider != player: + return true + + # Fall through to standard central ray check for extra safety? + # Or just return false if at least one passed. Let's do one more check. + + # 1. Path check: Block movement if a wall exists between the current and target tile var path_query = PhysicsRayQueryParameters3D.create(from_v3, to_v3) path_query.collide_with_areas = false path_query.collide_with_bodies = true diff --git a/scripts/managers/stop_n_go_manager.gd b/scripts/managers/stop_n_go_manager.gd index 99ce3dc..3244b91 100644 --- a/scripts/managers/stop_n_go_manager.gd +++ b/scripts/managers/stop_n_go_manager.gd @@ -367,6 +367,7 @@ func _apply_arena_setup(): var r2_entrances = [Vector2i(15, 2), Vector2i(17, 1), Vector2i(19, 2), Vector2i(18, 5)] _create_room_with_edge_walls(gridmap, 15, 1, 19, 5, r2_entrances) + gridmap.diagonal_movement = true gridmap.update_grid_data() gridmap.initialize_astar() @@ -380,9 +381,6 @@ func _create_room_with_edge_walls(gridmap: GridMap, x_start: int, z_start: int, if is_north or is_south or is_west or is_east: if not Vector2i(x, z) in entrances: - # We NO LONGER set TILE_OBSTACLE here so you can walk near the walls - # gridmap.set_cell_item(Vector3i(x, 1, z), TILE_OBSTACLE) - # Determine placement SIDE and rotation if is_north: _instantiate_safe_zone_wall(gridmap, x, z, "NORTH") if is_south: _instantiate_safe_zone_wall(gridmap, x, z, "SOUTH") @@ -394,47 +392,6 @@ func _create_room_with_edge_walls(gridmap: GridMap, x_start: int, z_start: int, if current_f0 == -1: gridmap.set_cell_item(Vector3i(x, 0, z), TILE_WALKABLE) -func _disconnect_astar_across_walls(gridmap: Node): - if not gridmap or not gridmap.get("astar"): return - var astar = gridmap.astar - - # We re-calculate the edges and disconnect - var rooms = [ - {"start": Vector2i(7, 6), "end": Vector2i(11, 9), "entrances": [Vector2i(7, 8), Vector2i(9, 6), Vector2i(9, 10), Vector2i(11, 8)]}, - {"start": Vector2i(15, 1), "end": Vector2i(19, 5), "entrances": [Vector2i(15, 2), Vector2i(17, 1), Vector2i(19, 2), Vector2i(18, 5)]} - ] - - for room in rooms: - for x in range(room.start.x, room.end.x + 1): - for z in range(room.start.y, room.end.y + 1): - var pos = Vector2i(x, z) - var is_north = (z == room.start.y) - var is_south = (z == room.end.y) - var is_west = (x == room.start.x) - var is_east = (x == room.end.x) - - if is_north or is_south or is_west or is_east: - if pos in room.entrances: continue - - var current_id = gridmap.get_point_id(Vector3i(x, 0, z)) - - if is_north: - var n_id = gridmap.get_point_id(Vector3i(x, 0, z - 1)) - if astar.has_point(current_id) and astar.has_point(n_id): - astar.disconnect_points(current_id, n_id) - if is_south: - var s_id = gridmap.get_point_id(Vector3i(x, 0, z + 1)) - if astar.has_point(current_id) and astar.has_point(s_id): - astar.disconnect_points(current_id, s_id) - if is_west: - var w_id = gridmap.get_point_id(Vector3i(x - 1, 0, z)) - if astar.has_point(current_id) and astar.has_point(w_id): - astar.disconnect_points(current_id, w_id) - if is_east: - var e_id = gridmap.get_point_id(Vector3i(x + 1, 0, z)) - if astar.has_point(current_id) and astar.has_point(e_id): - astar.disconnect_points(current_id, e_id) - func _instantiate_safe_zone_wall(gridmap: GridMap, x: int, z: int, side: String): var wall_scene = load("res://scenes/safe_zone_wall.tscn") if not wall_scene: return @@ -442,6 +399,9 @@ func _instantiate_safe_zone_wall(gridmap: GridMap, x: int, z: int, side: String) var wall = wall_scene.instantiate() gridmap.add_child(wall) + # Slightly shorten the wall (from 1.0 to 0.95) to allow diagonal raycasts to pass corners + wall.scale.x = 0.95 + var pos = Vector3( x * gridmap.cell_size.x + gridmap.cell_size.x/2, 0.5,