157 lines
5.9 KiB
GDScript
157 lines
5.9 KiB
GDScript
# Tests for res://scripts/player.gd (minimal tree: CharacterBody3D + NavigationAgent3D).
|
|
extends GdUnitTestSuite
|
|
|
|
const PLAYER_SCRIPT: Script = preload("res://scripts/player.gd")
|
|
|
|
|
|
func _make_player() -> CharacterBody3D:
|
|
var body := CharacterBody3D.new()
|
|
var nav := NavigationAgent3D.new()
|
|
nav.name = "NavigationAgent3D"
|
|
body.add_child(nav)
|
|
body.set_script(PLAYER_SCRIPT)
|
|
auto_free(body)
|
|
add_child(body)
|
|
return body
|
|
|
|
|
|
func test_snap_to_server_resets_goal_position_and_velocity() -> void:
|
|
var p := _make_player()
|
|
var target := Vector3(1.0, 0.9, -2.0)
|
|
p.snap_to_server(target)
|
|
assert_that(p.global_position).is_equal(target)
|
|
assert_that(p.velocity).is_equal(Vector3.ZERO)
|
|
assert_that(p.get("_has_walk_goal")).is_false()
|
|
|
|
|
|
func test_set_authoritative_nav_goal_sets_goal_and_target() -> void:
|
|
var p := _make_player()
|
|
p.set("_idle_anchor_active", true)
|
|
var goal := Vector3(10.0, 0.0, 3.0)
|
|
p.set_authoritative_nav_goal(goal)
|
|
assert_that(p.get("_has_walk_goal")).is_true()
|
|
assert_that(p.get("_idle_anchor_active")).is_false()
|
|
assert_that(p.get("_auth_walk_goal")).is_equal(goal)
|
|
var nav: NavigationAgent3D = p.get_node("NavigationAgent3D") as NavigationAgent3D
|
|
assert_that(nav.target_position).is_equal(goal)
|
|
|
|
|
|
func test_clear_nav_goal_clears_velocity_and_nav_target() -> void:
|
|
var p := _make_player()
|
|
p.velocity = Vector3(1.0, 0.0, 0.0)
|
|
p.set_authoritative_nav_goal(Vector3(5.0, 0.0, 5.0))
|
|
p.clear_nav_goal()
|
|
assert_that(p.velocity).is_equal(Vector3.ZERO)
|
|
assert_that(p.get("_has_walk_goal")).is_false()
|
|
assert_that(p.get("_idle_anchor_active")).is_false()
|
|
var nav: NavigationAgent3D = p.get_node("NavigationAgent3D") as NavigationAgent3D
|
|
assert_that(nav.target_position).is_equal(p.global_position)
|
|
|
|
|
|
func test_idle_support_is_stable_on_flat_floor_without_wall_contacts() -> void:
|
|
var slide_normals: Array[Vector3] = [Vector3.UP]
|
|
var stable: bool = PLAYER_SCRIPT.idle_support_is_stable(true, Vector3.UP, slide_normals, 0)
|
|
assert_that(stable).is_true()
|
|
|
|
|
|
func test_idle_support_is_stable_false_when_floor_is_not_flat_enough() -> void:
|
|
var slide_normals: Array[Vector3] = [Vector3.UP]
|
|
var stable: bool = PLAYER_SCRIPT.idle_support_is_stable(
|
|
true, Vector3(0.02, 0.995, 0.0).normalized(), slide_normals, 0
|
|
)
|
|
assert_that(stable).is_false()
|
|
|
|
|
|
func test_idle_support_is_stable_true_when_flat_floor_has_extra_contacts() -> void:
|
|
var slide_normals: Array[Vector3] = [Vector3.UP, Vector3(0.0, 0.6, 0.8).normalized()]
|
|
var stable: bool = PLAYER_SCRIPT.idle_support_is_stable(true, Vector3.UP, slide_normals, 0)
|
|
assert_that(stable).is_true()
|
|
|
|
|
|
func test_idle_support_is_stable_false_when_loose_ticks_active() -> void:
|
|
var slide_normals: Array[Vector3] = [Vector3.UP]
|
|
var stable: bool = PLAYER_SCRIPT.idle_support_is_stable(true, Vector3.UP, slide_normals, 12)
|
|
assert_that(stable).is_false()
|
|
|
|
|
|
func test_idle_support_is_stable_false_when_not_on_floor() -> void:
|
|
var slide_normals: Array[Vector3] = [Vector3.UP]
|
|
var stable: bool = PLAYER_SCRIPT.idle_support_is_stable(false, Vector3.UP, slide_normals, 0)
|
|
assert_that(stable).is_false()
|
|
|
|
|
|
func test_hold_idle_anchor_sets_anchor_first_then_clamps_xz() -> void:
|
|
var p := _make_player()
|
|
p.global_position = Vector3(1.0, 0.5, 2.0)
|
|
p.call("_hold_idle_anchor")
|
|
assert_that(p.get("_idle_anchor_active")).is_true()
|
|
assert_that(p.get("_idle_anchor_xz")).is_equal(Vector2(1.0, 2.0))
|
|
|
|
p.global_position = Vector3(1.2, 0.7, 2.3)
|
|
p.call("_hold_idle_anchor")
|
|
assert_that(p.global_position).is_equal(Vector3(1.0, 0.7, 2.0))
|
|
|
|
|
|
func test_snap_to_server_clears_idle_anchor() -> void:
|
|
var p := _make_player()
|
|
p.set("_idle_anchor_active", true)
|
|
p.set("_idle_anchor_xz", Vector2(9.0, 9.0))
|
|
p.snap_to_server(Vector3(3.0, 0.9, 4.0))
|
|
assert_that(p.get("_idle_anchor_active")).is_false()
|
|
|
|
|
|
func test_capsule_feet_y_uses_body_origin_minus_half_height() -> void:
|
|
var feet_y: float = PLAYER_SCRIPT.capsule_feet_y(0.545678, 0.5)
|
|
assert_that(absf(feet_y - 0.045678)).is_less(0.000001)
|
|
|
|
|
|
func test_vertical_arrival_error_uses_capsule_feet_height() -> void:
|
|
var goal_y := 0.045678
|
|
var body_origin_y := 0.545678
|
|
var err: float = PLAYER_SCRIPT.vertical_arrival_error(goal_y, body_origin_y, 0.5)
|
|
assert_that(absf(err)).is_less(0.000001)
|
|
|
|
|
|
func test_vertical_arrival_error_stays_large_for_origin_height_only_match() -> void:
|
|
var goal_y := 0.545678
|
|
var body_origin_y := 0.545678
|
|
var err: float = PLAYER_SCRIPT.vertical_arrival_error(goal_y, body_origin_y, 0.5)
|
|
assert_that(absf(err - 0.5)).is_less(0.000001)
|
|
|
|
|
|
func test_arrival_vert_err_large_for_step_goal_from_floor() -> void:
|
|
# Player at floor height (body Y=0.9). Goal at step surface (Y=0.3).
|
|
# Using actual capsule bottom (CAPSULE_HALF_HEIGHT + PLAYER_CAPSULE_RADIUS) the error
|
|
# must exceed VERT_ARRIVE_EPS so the arrival check does NOT fire prematurely.
|
|
var goal_y := 0.3
|
|
var body_y := 0.9
|
|
var full_half: float = (
|
|
PLAYER_SCRIPT.get("CAPSULE_HALF_HEIGHT") as float
|
|
+ PLAYER_SCRIPT.get("PLAYER_CAPSULE_RADIUS") as float
|
|
)
|
|
var err: float = PLAYER_SCRIPT.vertical_arrival_error(goal_y, body_y, full_half)
|
|
assert_that(err).is_greater(PLAYER_SCRIPT.get("VERT_ARRIVE_EPS") as float)
|
|
|
|
|
|
func test_arrival_vert_err_zero_when_standing_on_step() -> void:
|
|
# Player body centred at goal_y + total_half_height (i.e. actually standing on the step).
|
|
# Error must be within VERT_ARRIVE_EPS so arrival fires correctly once the player is up.
|
|
var goal_y := 0.3
|
|
var full_half: float = (
|
|
PLAYER_SCRIPT.get("CAPSULE_HALF_HEIGHT") as float
|
|
+ PLAYER_SCRIPT.get("PLAYER_CAPSULE_RADIUS") as float
|
|
)
|
|
var body_y: float = goal_y + full_half
|
|
var err: float = PLAYER_SCRIPT.vertical_arrival_error(goal_y, body_y, full_half)
|
|
assert_that(err).is_less_equal(PLAYER_SCRIPT.get("VERT_ARRIVE_EPS") as float)
|
|
|
|
|
|
func test_flat_floor_goal_is_not_below_feet_height_margin() -> void:
|
|
var goal_y := 0.0
|
|
var body_origin_y := 0.4
|
|
var descend_margin := PLAYER_SCRIPT.get("DESCEND_GOAL_Y_MARGIN") as float
|
|
var below_feet_margin: bool = (
|
|
goal_y < PLAYER_SCRIPT.capsule_feet_y(body_origin_y, 0.5) - descend_margin
|
|
)
|
|
assert_that(below_feet_margin).is_false()
|