WIP checkpoint: PC2: Basic visible player character representation
This commit is contained in:
@@ -0,0 +1,70 @@
|
|||||||
|
[gd_scene load_steps=1 format=3]
|
||||||
|
|
||||||
|
[node name="TestLevel" type="Node3D"]
|
||||||
|
|
||||||
|
[node name="Ground" type="StaticBody3D" parent="."]
|
||||||
|
|
||||||
|
[node name="GroundMesh" type="MeshInstance3D" parent="Ground"]
|
||||||
|
mesh = SubResource("mesh_ground")
|
||||||
|
|
||||||
|
[node name="Props" type="Node3D" parent="."]
|
||||||
|
|
||||||
|
[node name="Prop1" type="StaticBody3D" parent="Props"]
|
||||||
|
|
||||||
|
[node name="Prop1Mesh" type="MeshInstance3D" parent="Props/Prop1"]
|
||||||
|
mesh = SubResource("mesh_box1")
|
||||||
|
|
||||||
|
[node name="Prop2" type="StaticBody3D" parent="Props"]
|
||||||
|
|
||||||
|
[node name="Prop2Mesh" type="MeshInstance3D" parent="Props/Prop2"]
|
||||||
|
mesh = SubResource("mesh_cylinder")
|
||||||
|
|
||||||
|
[node name="Prop3" type="StaticBody3D" parent="Props"]
|
||||||
|
|
||||||
|
[node name="Prop3Mesh" type="MeshInstance3D" parent="Props/Prop3"]
|
||||||
|
mesh = SubResource("mesh_sphere")
|
||||||
|
|
||||||
|
[node name="Prop4" type="StaticBody3D" parent="Props"]
|
||||||
|
|
||||||
|
[node name="Prop4Mesh" type="MeshInstance3D" parent="Props/Prop4"]
|
||||||
|
mesh = SubResource("mesh_box2")
|
||||||
|
|
||||||
|
[node name="Prop5" type="StaticBody3D" parent="Props"]
|
||||||
|
|
||||||
|
[node name="Prop5Mesh" type="MeshInstance3D" parent="Props/Prop5"]
|
||||||
|
mesh = SubResource("mesh_box3")
|
||||||
|
|
||||||
|
[node name="Sun" type="DirectionalLight3D" parent="."]
|
||||||
|
rotation_degrees = Vector3(-45.0, 0.0, 0.0)
|
||||||
|
shadow_enabled = true
|
||||||
|
|
||||||
|
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
||||||
|
environment = SubResource("env_bg")
|
||||||
|
|
||||||
|
[node name="PlayerSpawn" type="Marker3D" parent="."]
|
||||||
|
position = Vector3(0, 1.0, 0)
|
||||||
|
|
||||||
|
[sub_resource type="BoxMesh" id="mesh_ground"]
|
||||||
|
size = Vector3(40, 1, 40)
|
||||||
|
|
||||||
|
[sub_resource type="BoxMesh" id="mesh_box1"]
|
||||||
|
size = Vector3(1.5, 2.0, 1.5)
|
||||||
|
|
||||||
|
[sub_resource type="CylinderMesh" id="mesh_cylinder"]
|
||||||
|
top_radius = 0.5
|
||||||
|
bottom_radius = 0.5
|
||||||
|
height = 2.0
|
||||||
|
|
||||||
|
[sub_resource type="SphereMesh" id="mesh_sphere"]
|
||||||
|
radius = 0.75
|
||||||
|
|
||||||
|
[sub_resource type="BoxMesh" id="mesh_box2"]
|
||||||
|
size = Vector3(1.0, 3.0, 1.0)
|
||||||
|
|
||||||
|
[sub_resource type="BoxMesh" id="mesh_box3"]
|
||||||
|
size = Vector3(2.0, 1.0, 2.0)
|
||||||
|
|
||||||
|
[sub_resource type="Environment" id="env_bg"]
|
||||||
|
background_mode = 1
|
||||||
|
background_color = Color(0.4, 0.6, 0.9, 1)
|
||||||
|
ambient_light_energy = 0.6
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
class_name Player
|
||||||
|
extends CharacterBody3D
|
||||||
|
## The player character controller.
|
||||||
|
##
|
||||||
|
## A simple CharacterBody3D with WASD/arrow locomotion, gravity, and a
|
||||||
|
## first-person Camera3D. 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.
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Movement constants
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const WALK_SPEED: float = 5.0
|
||||||
|
const JUMP_VELOCITY: float = -4.5
|
||||||
|
const GRAVITY: float = 9.8
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Input state
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
var _input_direction := Vector2.ZERO
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# _ready — spawn at PlayerSpawn marker
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
# Per §3 of pc2_test_level.md: look up PlayerSpawn from the current
|
||||||
|
# scene root. The lookup walks the full subtree (recursive=true) so
|
||||||
|
# it finds the marker whether it is a direct child of the level or
|
||||||
|
# nested deeper.
|
||||||
|
var spawn := get_tree().current_scene.find_child("PlayerSpawn", true, false)
|
||||||
|
if spawn is Node3D:
|
||||||
|
global_position = spawn.global_position
|
||||||
|
# Align player forward with spawn yaw (optional polish).
|
||||||
|
var spawn_yaw := spawn.rotation.y
|
||||||
|
rotation.y = spawn_yaw
|
||||||
|
else:
|
||||||
|
push_warning("player.gd: no PlayerSpawn marker found; spawning at origin")
|
||||||
|
global_position = Vector3(0, 1.0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# _unhandled_input — capture keyboard / mouse for movement
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func _unhandled_input(event: InputEvent) -> void:
|
||||||
|
if event is InputEventKey:
|
||||||
|
var key := event as InputEventKey
|
||||||
|
if key.pressed:
|
||||||
|
_update_input_direction(key)
|
||||||
|
else:
|
||||||
|
_clear_input_direction(key)
|
||||||
|
elif event is InputEventMouseMotion:
|
||||||
|
_handle_mouse_look(event as InputEventMouseMotion)
|
||||||
|
|
||||||
|
|
||||||
|
func _update_input_direction(key: InputEventKey) -> void:
|
||||||
|
match key.keycode:
|
||||||
|
KEY_W, KEY_UP:
|
||||||
|
_input_direction.y += 1.0
|
||||||
|
KEY_S, KEY_DOWN:
|
||||||
|
_input_direction.y -= 1.0
|
||||||
|
KEY_A, KEY_LEFT:
|
||||||
|
_input_direction.x -= 1.0
|
||||||
|
KEY_D, KEY_RIGHT:
|
||||||
|
_input_direction.x += 1.0
|
||||||
|
|
||||||
|
|
||||||
|
func _clear_input_direction(key: InputEventKey) -> void:
|
||||||
|
match key.keycode:
|
||||||
|
KEY_W, KEY_UP:
|
||||||
|
_input_direction.y -= 1.0
|
||||||
|
KEY_S, KEY_DOWN:
|
||||||
|
_input_direction.y += 1.0
|
||||||
|
KEY_A, KEY_LEFT:
|
||||||
|
_input_direction.x += 1.0
|
||||||
|
KEY_D, KEY_RIGHT:
|
||||||
|
_input_direction.x -= 1.0
|
||||||
|
|
||||||
|
|
||||||
|
func _handle_mouse_look(event: InputEventMouseMotion) -> void:
|
||||||
|
rotation.y -= event.relative.x * 0.002
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Physics — gravity, jump, move_and_slide
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func _physics_process(delta: float) -> void:
|
||||||
|
# Apply gravity.
|
||||||
|
if not is_on_floor():
|
||||||
|
velocity.y -= GRAVITY * delta
|
||||||
|
|
||||||
|
# Jump.
|
||||||
|
if is_on_floor() and Input.is_action_just_pressed("ui_accept"):
|
||||||
|
velocity.y = JUMP_VELOCITY
|
||||||
|
return
|
||||||
|
|
||||||
|
# Build movement direction from input.
|
||||||
|
var direction := (_input_direction.x * transform.basis.x +
|
||||||
|
_input_direction.y * transform.basis.z).normalized()
|
||||||
|
velocity.x = direction.x * WALK_SPEED
|
||||||
|
velocity.z = direction.z * WALK_SPEED
|
||||||
|
|
||||||
|
move_and_slide()
|
||||||
Reference in New Issue
Block a user