Third-person camera + fix the avatar customize preview
checkpoint / checkpoint (push) Successful in 1m34s
checkpoint / checkpoint (push) Successful in 1m34s
You couldn't see your own character because the camera was first-person (inside the avatar's head), and the customize screen's 3D preview rendered nothing. - player.tscn: camera now hangs off a SpringArm3D on a CameraPivot, 4 m behind + above the player, pulled in on collision. player.gd: yaw still turns the body, pitch orbits the arm (starts tilted down to frame the avatar). - avatar_customization_ui.gd: the preview SubViewport had own_world_3d=false, so it shared the Control-only main world -- no scenario, nothing rendered. Set own_world_3d=true, add a WorldEnvironment with ambient fill + a key/fill light pair, make the camera current, tweak framing. - smoke_test_player: assert the third-person SpringArm rig (18/18). - smoke_test_enter_world: wait longer for the scene change -- a real POST /join spawns a dedicated server, which is slow (9/9). Client-side change -- rebuild the exe to get it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015jtxAiu5dAW4WxCNdGkrkJ
This commit is contained in:
+21
-17
@@ -2,11 +2,10 @@ class_name Player
|
||||
extends CharacterBody3D
|
||||
## The player character controller.
|
||||
##
|
||||
## A simple CharacterBody3D with WASD/arrow locomotion, gravity, jump, and a
|
||||
## first-person Camera3D with mouse-look. Reads the spawn point from the
|
||||
## current scene's `PlayerSpawn` Marker3D (see docs/architecture/pc2_test_level.md §3).
|
||||
##
|
||||
## No networking, no chunk code, no avatar UI — PC2 is local-only.
|
||||
## A CharacterBody3D with WASD/arrow locomotion, gravity, jump, and a
|
||||
## third-person camera (SpringArm3D behind the player) with mouse-look --
|
||||
## yaw turns the body, pitch orbits the camera. Reads the spawn point from
|
||||
## the current scene's `PlayerSpawn` Marker3D.
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -20,23 +19,25 @@ const GRAVITY: float = 9.8
|
||||
## Mouse-look sensitivity (radians per pixel of relative motion).
|
||||
const MOUSE_SENSITIVITY: float = 0.003
|
||||
|
||||
## Camera pitch is clamped so the player can't flip past looking straight
|
||||
## up/down (which would otherwise let mouse-look invert on itself).
|
||||
const PITCH_LIMIT: float = deg_to_rad(89.0)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Input state
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
var _input_direction := Vector2.ZERO
|
||||
var _camera_pitch: float = 0.0
|
||||
var _camera_pitch: float = -0.25
|
||||
|
||||
## Vertical offset applied to the assembled avatar so its feet (origin y=0)
|
||||
## line up with the bottom of the player's collision capsule.
|
||||
const AVATAR_FEET_OFFSET: float = -0.8
|
||||
|
||||
@onready var _camera: Camera3D = $Camera3D
|
||||
## Third-person pitch limits (radians): a bit up, a lot down (looking over
|
||||
## the avatar's shoulder toward the ground).
|
||||
const PITCH_MIN: float = -1.1
|
||||
const PITCH_MAX: float = 0.45
|
||||
|
||||
@onready var _camera: Camera3D = $CameraPivot/SpringArm3D/Camera3D
|
||||
@onready var _camera_pivot: Node3D = $CameraPivot
|
||||
@onready var _capsule_mesh: MeshInstance3D = $CapsuleMesh
|
||||
|
||||
var _avatar_view: Node3D = null
|
||||
@@ -69,8 +70,11 @@ func _ready() -> void:
|
||||
push_warning("player.gd: no PlayerSpawn marker found; spawning at origin")
|
||||
global_position = Vector3(0, 1.0, 0)
|
||||
|
||||
# Capture the mouse for FPS-style look. Headless/test runs have no real
|
||||
# window to capture input for, so guard against that rather than error.
|
||||
# Start the camera arm tilted down a little so the avatar is framed.
|
||||
_camera_pivot.rotation.x = _camera_pitch
|
||||
|
||||
# Capture the mouse for look. Headless/test runs have no real window to
|
||||
# capture input for, so guard against that rather than error.
|
||||
if DisplayServer.get_name() != "headless":
|
||||
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
||||
|
||||
@@ -143,11 +147,11 @@ func _clear_input_direction(key: InputEventKey) -> void:
|
||||
|
||||
func _handle_mouse_look(event: InputEventMouseMotion) -> void:
|
||||
# Yaw rotates the whole body (so movement direction turns with the
|
||||
# look direction); pitch only tilts the camera, so the capsule itself
|
||||
# never tips over.
|
||||
# look direction); pitch orbits the camera arm around the player, so the
|
||||
# capsule itself never tips over.
|
||||
rotation.y -= event.relative.x * MOUSE_SENSITIVITY
|
||||
_camera_pitch = clampf(_camera_pitch - event.relative.y * MOUSE_SENSITIVITY, -PITCH_LIMIT, PITCH_LIMIT)
|
||||
_camera.rotation.x = _camera_pitch
|
||||
_camera_pitch = clampf(_camera_pitch - event.relative.y * MOUSE_SENSITIVITY, PITCH_MIN, PITCH_MAX)
|
||||
_camera_pivot.rotation.x = _camera_pitch
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user