feat: Implement PlayerMovementManager for grid-based movement, collision, and a push mechanic with Stop n Go mode rules.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user