NEON-29: Fix walk steering when goal shares XZ; air gravity; player mask
- _set_horizontal_velocity_toward: optional fallback xz when delta.xz ~ 0 (avoids +X hack). - Direct-approach branch: if horiz_dist to goal is ~0 but not arrived vertically, steer using the first navigation path point with horizontal separation. - Apply get_gravity()*delta while walking and not on_floor so capsules land and Jolt registers floor contacts. - Set Player collision_mask=3 explicitly; README collision bullet updated.pull/41/head
parent
ae66b6b313
commit
6b471b2102
|
|
@ -98,7 +98,7 @@ The main scene includes a **prototype terminal** at the map center (same world *
|
||||||
|
|
||||||
**`player.gd`** uses **`NavigationAgent3D.get_next_path_position()`** + **`move_and_slide()`** for horizontal motion when following the mesh; it may steer **directly at the goal** in xz when the descend bypass applies (NEON-8). **`snap_to_server()`** remains for **boot** (and would apply for any future hard reconcile).
|
**`player.gd`** uses **`NavigationAgent3D.get_next_path_position()`** + **`move_and_slide()`** for horizontal motion when following the mesh; it may steer **directly at the goal** in xz when the descend bypass applies (NEON-8). **`snap_to_server()`** remains for **boot** (and would apply for any future hard reconcile).
|
||||||
|
|
||||||
- The avatar is on **physics layer 2** with **mask 1** (it scans walkables on layer **1**). Environment **StaticBody3D** nodes stay on **collision_layer** **1** but use **collision_mask** **3** (layers **1** and **2**) so they also scan the player layer—otherwise Godot never pairs the capsule with the floor and **`is_on_floor()`** / **`move_and_slide()`** contacts break. The pick ray uses **mask 1** so clicks pass through the avatar and hit the floor.
|
- The avatar is on **physics layer 2** with **collision_mask** **3** (scans layers **1** and **2** so all walkables/occluders pair reliably). Environment **StaticBody3D** nodes stay on **collision_layer** **1** with **collision_mask** **3** as well. The pick ray uses **mask 1** only, so clicks pass through the avatar and hit the floor.
|
||||||
|
|
||||||
### Manual check (remnants)
|
### Manual check (remnants)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -308,6 +308,7 @@ surface_material_override/0 = SubResource("Mat_marker_base")
|
||||||
[node name="Player" type="CharacterBody3D" parent="World" unique_id=352931696]
|
[node name="Player" type="CharacterBody3D" parent="World" unique_id=352931696]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5, 0.9, -5)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5, 0.9, -5)
|
||||||
collision_layer = 2
|
collision_layer = 2
|
||||||
|
collision_mask = 3
|
||||||
floor_max_angle = 0.872665
|
floor_max_angle = 0.872665
|
||||||
floor_snap_length = 0.22
|
floor_snap_length = 0.22
|
||||||
safe_margin = 0.055
|
safe_margin = 0.055
|
||||||
|
|
|
||||||
|
|
@ -231,15 +231,44 @@ func _after_walk_move_and_slide() -> void:
|
||||||
move_and_slide()
|
move_and_slide()
|
||||||
|
|
||||||
|
|
||||||
func _set_horizontal_velocity_toward(point: Vector3) -> void:
|
func _set_horizontal_velocity_toward(point: Vector3, fallback_dir_xz: Vector3 = Vector3.ZERO) -> void:
|
||||||
var pos := global_position
|
var pos := global_position
|
||||||
var dh: Vector3 = Vector3(point.x - pos.x, 0.0, point.z - pos.z)
|
var dh: Vector3 = Vector3(point.x - pos.x, 0.0, point.z - pos.z)
|
||||||
if dh.length_squared() < 1e-10:
|
if dh.length_squared() < 1e-10:
|
||||||
dh = Vector3(1.0, 0.0, 0.0)
|
var fb2 := Vector2(fallback_dir_xz.x, fallback_dir_xz.z)
|
||||||
velocity = dh.normalized() * MOVE_SPEED
|
if fb2.length_squared() > 1e-10:
|
||||||
|
fb2 = fb2.normalized()
|
||||||
|
dh = Vector3(fb2.x, 0.0, fb2.y)
|
||||||
|
else:
|
||||||
|
dh = Vector3(1.0, 0.0, 0.0)
|
||||||
|
else:
|
||||||
|
dh = dh.normalized()
|
||||||
|
velocity = dh * MOVE_SPEED
|
||||||
velocity.y = 0.0
|
velocity.y = 0.0
|
||||||
|
|
||||||
|
|
||||||
|
## While walking, integrate gravity when airborne so Jolt can register floor contacts and
|
||||||
|
## `move_and_slide` is not stuck with vy=0 above the surface (expanded district / terraces).
|
||||||
|
func _apply_walk_air_gravity(delta: float) -> void:
|
||||||
|
if is_on_floor():
|
||||||
|
return
|
||||||
|
velocity += get_gravity() * delta
|
||||||
|
|
||||||
|
|
||||||
|
## When the authoritative goal shares XZ with the capsule (e.g. terrace above), horizontal
|
||||||
|
## `full_to_goal` is zero and steering must follow the baked path toward a ramp/step, not +X.
|
||||||
|
func _set_horizontal_velocity_from_nav_path_or_goal(fallback_goal_xz: Vector3) -> void:
|
||||||
|
var pos: Vector3 = global_position
|
||||||
|
var path: PackedVector3Array = _nav_agent.get_current_navigation_path()
|
||||||
|
for i: int in range(path.size()):
|
||||||
|
var p: Vector3 = path[i]
|
||||||
|
var dh: Vector3 = Vector3(p.x - pos.x, 0.0, p.z - pos.z)
|
||||||
|
if dh.length_squared() > 0.0025:
|
||||||
|
_set_horizontal_velocity_toward(p, fallback_goal_xz)
|
||||||
|
return
|
||||||
|
_set_horizontal_velocity_toward(_auth_walk_goal, fallback_goal_xz)
|
||||||
|
|
||||||
|
|
||||||
static func capsule_feet_y(body_origin_y: float, capsule_half_height: float) -> float:
|
static func capsule_feet_y(body_origin_y: float, capsule_half_height: float) -> float:
|
||||||
return body_origin_y - capsule_half_height
|
return body_origin_y - capsule_half_height
|
||||||
|
|
||||||
|
|
@ -484,7 +513,7 @@ func _snap_capsule_upright() -> void:
|
||||||
global_transform = Transform3D(Basis.IDENTITY, p)
|
global_transform = Transform3D(Basis.IDENTITY, p)
|
||||||
|
|
||||||
|
|
||||||
func _physics_process(_delta: float) -> void:
|
func _physics_process(delta: float) -> void:
|
||||||
_debug_trace_frame += 1
|
_debug_trace_frame += 1
|
||||||
var use_loose_floor_angle: bool = _has_walk_goal or _floor_angle_loose_ticks > 0
|
var use_loose_floor_angle: bool = _has_walk_goal or _floor_angle_loose_ticks > 0
|
||||||
if not _has_walk_goal:
|
if not _has_walk_goal:
|
||||||
|
|
@ -530,6 +559,7 @@ func _physics_process(_delta: float) -> void:
|
||||||
|
|
||||||
var full_to_goal: Vector3 = _auth_walk_goal - global_position
|
var full_to_goal: Vector3 = _auth_walk_goal - global_position
|
||||||
var horiz_dist: float = Vector2(full_to_goal.x, full_to_goal.z).length()
|
var horiz_dist: float = Vector2(full_to_goal.x, full_to_goal.z).length()
|
||||||
|
var want_goal_h: Vector3 = Vector3(full_to_goal.x, 0.0, full_to_goal.z)
|
||||||
# Use actual capsule bottom (CAPSULE_HALF_HEIGHT + PLAYER_CAPSULE_RADIUS = 0.9) so that a
|
# Use actual capsule bottom (CAPSULE_HALF_HEIGHT + PLAYER_CAPSULE_RADIUS = 0.9) so that a
|
||||||
# step-surface goal (e.g. Y=0.3) is never considered "arrived" while the player is still at
|
# step-surface goal (e.g. Y=0.3) is never considered "arrived" while the player is still at
|
||||||
# floor height. Using only CAPSULE_HALF_HEIGHT (0.5) gives code-feet at Y=0.4, which is too
|
# floor height. Using only CAPSULE_HALF_HEIGHT (0.5) gives code-feet at Y=0.4, which is too
|
||||||
|
|
@ -557,7 +587,8 @@ func _physics_process(_delta: float) -> void:
|
||||||
|
|
||||||
var nav_map: RID = _nav_agent.get_navigation_map()
|
var nav_map: RID = _nav_agent.get_navigation_map()
|
||||||
if NavigationServer3D.map_get_iteration_id(nav_map) == 0:
|
if NavigationServer3D.map_get_iteration_id(nav_map) == 0:
|
||||||
_set_horizontal_velocity_toward(_auth_walk_goal)
|
_set_horizontal_velocity_toward(_auth_walk_goal, want_goal_h)
|
||||||
|
_apply_walk_air_gravity(delta)
|
||||||
floor_snap_length = WALK_STEP_ASSIST_SNAP if _step_assist_active else FLOOR_SNAP_MOVING
|
floor_snap_length = WALK_STEP_ASSIST_SNAP if _step_assist_active else FLOOR_SNAP_MOVING
|
||||||
move_and_slide()
|
move_and_slide()
|
||||||
_after_walk_move_and_slide()
|
_after_walk_move_and_slide()
|
||||||
|
|
@ -577,7 +608,8 @@ func _physics_process(_delta: float) -> void:
|
||||||
# using FLOOR_SNAP_MOVING to pull the capsule back to the step after every assist lift.
|
# using FLOOR_SNAP_MOVING to pull the capsule back to the step after every assist lift.
|
||||||
var feet_y: float = capsule_feet_y(global_position.y, CAPSULE_HALF_HEIGHT + PLAYER_CAPSULE_RADIUS)
|
var feet_y: float = capsule_feet_y(global_position.y, CAPSULE_HALF_HEIGHT + PLAYER_CAPSULE_RADIUS)
|
||||||
if _auth_walk_goal.y < feet_y - DESCEND_GOAL_Y_MARGIN:
|
if _auth_walk_goal.y < feet_y - DESCEND_GOAL_Y_MARGIN:
|
||||||
_set_horizontal_velocity_toward(_auth_walk_goal)
|
_set_horizontal_velocity_toward(_auth_walk_goal, want_goal_h)
|
||||||
|
_apply_walk_air_gravity(delta)
|
||||||
floor_snap_length = WALK_STEP_ASSIST_SNAP if _step_assist_active else FLOOR_SNAP_MOVING
|
floor_snap_length = WALK_STEP_ASSIST_SNAP if _step_assist_active else FLOOR_SNAP_MOVING
|
||||||
move_and_slide()
|
move_and_slide()
|
||||||
_after_walk_move_and_slide()
|
_after_walk_move_and_slide()
|
||||||
|
|
@ -591,17 +623,24 @@ func _physics_process(_delta: float) -> void:
|
||||||
return
|
return
|
||||||
|
|
||||||
if horiz_dist <= DIRECT_APPROACH_RADIUS:
|
if horiz_dist <= DIRECT_APPROACH_RADIUS:
|
||||||
_set_horizontal_velocity_toward(_auth_walk_goal)
|
if want_goal_h.length_squared() > 1e-12:
|
||||||
|
_set_horizontal_velocity_toward(_auth_walk_goal, want_goal_h)
|
||||||
|
else:
|
||||||
|
_set_horizontal_velocity_from_nav_path_or_goal(want_goal_h)
|
||||||
else:
|
else:
|
||||||
var next_path_position: Vector3 = _nav_agent.get_next_path_position()
|
var next_path_position: Vector3 = _nav_agent.get_next_path_position()
|
||||||
var to_next_h: Vector3 = Vector3(
|
var to_next_h: Vector3 = Vector3(
|
||||||
next_path_position.x - global_position.x, 0.0, next_path_position.z - global_position.z
|
next_path_position.x - global_position.x, 0.0, next_path_position.z - global_position.z
|
||||||
)
|
)
|
||||||
|
# When the next mesh point is almost under the capsule (same XZ), still steer using the
|
||||||
|
# segment from agent to next point if it has length; else fall back to full goal direction
|
||||||
|
# (not arbitrary +X) so vertical-offset goals and nav edges resolve.
|
||||||
if to_next_h.length_squared() > 0.0025:
|
if to_next_h.length_squared() > 0.0025:
|
||||||
_set_horizontal_velocity_toward(next_path_position)
|
_set_horizontal_velocity_toward(next_path_position, want_goal_h)
|
||||||
else:
|
else:
|
||||||
_set_horizontal_velocity_toward(_auth_walk_goal)
|
_set_horizontal_velocity_toward(_auth_walk_goal, want_goal_h)
|
||||||
|
|
||||||
|
_apply_walk_air_gravity(delta)
|
||||||
floor_snap_length = WALK_STEP_ASSIST_SNAP if _step_assist_active else FLOOR_SNAP_MOVING
|
floor_snap_length = WALK_STEP_ASSIST_SNAP if _step_assist_active else FLOOR_SNAP_MOVING
|
||||||
move_and_slide()
|
move_and_slide()
|
||||||
_after_walk_move_and_slide()
|
_after_walk_move_and_slide()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue