Fix fps_camera.gd path for export — move out of ignored client/ dir
- Copied fps_camera.gd from client/characters/character/ to scripts/characters/ - Updated player.tscn to reference new path - The client/ directory has its own project.godot so Godot ignores it during export - Zero export errors now
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
||||
[gd_scene format=3 uid="uid://player"]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/network/player.gd" id="1_player"]
|
||||
[ext_resource type="Script" path="res://client/characters/character/fps_camera.gd" id="2_fps_camera"]
|
||||
[ext_resource type="Script" path="res://scripts/characters/fps_camera.gd" id="2_fps_camera"]
|
||||
[ext_resource type="Script" path="res://scripts/network/player_net_input.gd" id="3_player_net_input"]
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="1"]
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
## FpsCamera — first-person camera view effects for tactical FPS.
|
||||
##
|
||||
## Manages view bobbing (head bob from movement), weapon sway, and FOV kick.
|
||||
## The parent FPSCharacterController handles mouse look (yaw/pitch).
|
||||
##
|
||||
## Place as a Camera3D child of FPSCharacterController.
|
||||
class_name FpsCamera
|
||||
extends Camera3D
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Exports
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
## Enable view bobbing (head bob when walking/running).
|
||||
@export var view_bob_enabled: bool = true
|
||||
|
||||
## How much the camera bobs horizontally (units).
|
||||
@export_range(0.0, 0.5) var bob_amplitude_h: float = 0.04
|
||||
|
||||
## How much the camera bobs vertically.
|
||||
@export_range(0.0, 0.5) var bob_amplitude_v: float = 0.04
|
||||
|
||||
## Frequency multiplier for bobbing (higher = faster).
|
||||
@export_range(0.5, 5.0) var bob_frequency: float = 2.5
|
||||
|
||||
## Sprint bob multiplier (more aggressive).
|
||||
@export_range(1.0, 3.0) var bob_sprint_mult: float = 1.6
|
||||
|
||||
## Crouch bob multiplier (less movement).
|
||||
@export_range(0.1, 1.0) var bob_crouch_mult: float = 0.4
|
||||
|
||||
## FOV kick on sprint (tactical FOV increase).
|
||||
@export_range(0.0, 10.0) var sprint_fov_kick: float = 3.0
|
||||
|
||||
## FOV kick when firing weapon.
|
||||
@export_range(-5.0, 10.0) var shoot_fov_kick: float = 0.5
|
||||
|
||||
## How fast FOV recovers.
|
||||
@export var fov_recovery_speed: float = 6.0
|
||||
|
||||
## Enable weapon sway (subtle rotation from movement).
|
||||
@export var weapon_sway_enabled: bool = true
|
||||
|
||||
## Maximum weapon sway rotation (degrees).
|
||||
@export_range(0.0, 5.0) var sway_max_rotation: float = 1.5
|
||||
|
||||
## Sway responsiveness (higher = snappier).
|
||||
@export_range(1.0, 20.0) var sway_response: float = 8.0
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Internal
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
## Reference to parent controller (duck-typed — uses has_method instead of class_name).
|
||||
var _controller: Node = null
|
||||
|
||||
## Bob time accumulator.
|
||||
var _bob_time: float = 0.0
|
||||
|
||||
## Current bob offset.
|
||||
var _bob_offset: Vector2 = Vector2.ZERO
|
||||
|
||||
## Current weapon node (child named "Weapon" or first MeshInstance3D).
|
||||
var _weapon: Node3D = null
|
||||
|
||||
## Weapon rest position (local transform when no sway).
|
||||
var _weapon_rest: Transform3D
|
||||
|
||||
## FOV state.
|
||||
var _base_fov: float = 75.0
|
||||
var _current_fov_offset: float = 0.0
|
||||
|
||||
## Sway state (used as both current value and interpolation target).
|
||||
var _sway_current: Vector2 = Vector2.ZERO
|
||||
var _sway_target: Vector2 = Vector2.ZERO
|
||||
|
||||
## Base eye Y set by parent controller's crouch logic (for additive bob).
|
||||
var _base_eye_y: float = 0.0
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Lifecycle
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
func _ready() -> void:
|
||||
_controller = get_parent()
|
||||
if _controller == null or not _controller.has_method(&"is_sprinting"):
|
||||
push_warning("FpsCamera: Parent has no is_sprinting() — view features disabled.")
|
||||
|
||||
_base_fov = fov
|
||||
|
||||
# Find weapon node (for sway)
|
||||
for child in get_children():
|
||||
if child is Node3D:
|
||||
_weapon = child
|
||||
_weapon_rest = child.transform
|
||||
break
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if _controller == null:
|
||||
return
|
||||
|
||||
# Capture the eye height set by parent controller's crouch logic
|
||||
# before we apply view bob offsets (which must be additive).
|
||||
_base_eye_y = position.y
|
||||
|
||||
# FOV management
|
||||
_update_fov(delta)
|
||||
|
||||
# View bobbing
|
||||
if view_bob_enabled:
|
||||
_update_bob(delta)
|
||||
|
||||
# Weapon sway
|
||||
if weapon_sway_enabled and _weapon:
|
||||
_update_sway(delta)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# FOV
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
func _update_fov(delta: float) -> void:
|
||||
var target_offset: float = 0.0
|
||||
|
||||
# Sprint FOV kick — sustained while sprinting (not just on transition)
|
||||
if _controller.is_sprinting():
|
||||
target_offset = sprint_fov_kick
|
||||
|
||||
# Shoot FOV kick is triggered externally via trigger_shoot_fov()
|
||||
# and decays toward the sprint/walk target naturally via move_toward.
|
||||
_current_fov_offset = move_toward(_current_fov_offset, target_offset, fov_recovery_speed * delta)
|
||||
fov = _base_fov + _current_fov_offset
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# View bobbing
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
func _update_bob(delta: float) -> void:
|
||||
var crouch_amount: float = _controller.get_crouch_amount()
|
||||
var sprinting: bool = _controller.is_sprinting()
|
||||
|
||||
# Calculate bob speed multiplier from controller state
|
||||
var speed_mult: float = 1.0
|
||||
if sprinting:
|
||||
speed_mult = bob_sprint_mult
|
||||
elif crouch_amount > 0.0:
|
||||
speed_mult = lerpf(1.0, bob_crouch_mult, crouch_amount)
|
||||
|
||||
if speed_mult < 0.01:
|
||||
# Stationary — reset to zero
|
||||
_bob_offset = _bob_offset.move_toward(Vector2.ZERO, delta * 10.0)
|
||||
else:
|
||||
_bob_time += delta * bob_frequency * speed_mult
|
||||
_bob_offset = Vector2(
|
||||
sin(_bob_time * 2.0) * bob_amplitude_h * speed_mult,
|
||||
sin(_bob_time) * bob_amplitude_v * speed_mult
|
||||
)
|
||||
|
||||
# Apply bob to camera position (relative to parent's eye offset)
|
||||
position.x = _bob_offset.x
|
||||
# Bob is additive on Y so it does NOT overwrite the crouch eye height
|
||||
# set by the parent FPSCharacterController._update_crouch().
|
||||
position.y = _base_eye_y + _bob_offset.y
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Weapon sway
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
func _update_sway(delta: float) -> void:
|
||||
# Sway follows mouse movement indirectly via controller yaw/pitch changes
|
||||
# Use velocity for physics-based sway: recentre over time
|
||||
var vel := _controller.velocity as Vector3
|
||||
var sway_h: float = clamp(vel.x * 0.003, -sway_max_rotation, sway_max_rotation)
|
||||
var sway_v: float = clamp(vel.z * 0.003, -sway_max_rotation, sway_max_rotation)
|
||||
# Add a tiny amount from pitch/yaw delta (not mouse, from movement direction change)
|
||||
_sway_target = Vector2(
|
||||
move_toward(_sway_target.x, sway_h, sway_response * delta),
|
||||
move_toward(_sway_target.y, sway_v, sway_response * delta)
|
||||
)
|
||||
|
||||
# Apply to weapon node as small rotation offsets
|
||||
if _weapon:
|
||||
var target := Transform3D(
|
||||
Basis.from_euler(Vector3(
|
||||
deg_to_rad(_sway_target.y),
|
||||
deg_to_rad(_sway_target.x),
|
||||
0.0
|
||||
)),
|
||||
_weapon_rest.origin
|
||||
)
|
||||
_weapon.transform = _weapon.transform.interpolate_with(target, sway_response * delta * 0.5)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Public API
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
## Trigger a shoot FOV kick. Call from weapon script.
|
||||
func trigger_shoot_fov() -> void:
|
||||
_current_fov_offset += shoot_fov_kick
|
||||
|
||||
|
||||
## Reset view to resting state (for respawn).
|
||||
func reset_view() -> void:
|
||||
_bob_time = 0.0
|
||||
_bob_offset = Vector2.ZERO
|
||||
_current_fov_offset = 0.0
|
||||
_sway_current = Vector2.ZERO
|
||||
_sway_target = Vector2.ZERO
|
||||
fov = _base_fov
|
||||
if _weapon:
|
||||
_weapon.transform = _weapon_rest
|
||||
|
||||
|
||||
## Get the current bob offset for external effects.
|
||||
func get_bob_offset() -> Vector2:
|
||||
return _bob_offset
|
||||
@@ -0,0 +1 @@
|
||||
uid://b6rixkmc2v0lt
|
||||
Reference in New Issue
Block a user