extends CharacterBody3D const SPEED = 5.0 const JUMP_VELOCITY = 4.5 const SENSITIVITY = 0.005 const FOV_DEFAULT: float = 75.0 const FOV_SPRINT: float = 90.0 const FOV_LERP = 0.2 var gravity = ProjectSettings.get_setting("physics/3d/default_gravity") @onready var head = $Head @onready var camera = $Head/PlayerCamera @onready var player_debug = $Head/PlayerCamera/PlayerDebug @onready var player_stats = $PlayerStats @onready var anim_player = $AnimationPlayer @onready var muzzle_flash = $Head/PlayerCamera/pistol/MuzzleFlash @onready var pistol_raycast = $Head/PlayerCamera/pistol/RayCast3D @onready var bullet_decal = preload("res://bullet_decal.tscn") func _ready() -> void: Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) func _unhandled_input(event: InputEvent) -> void: if event is InputEventMouseMotion: head.rotate_y(-event.relative.x * SENSITIVITY) camera.rotate_x(-event.relative.y * SENSITIVITY) camera.rotation.x = clamp(camera.rotation.x, deg_to_rad(-40), deg_to_rad(80)) if Input.is_action_just_pressed("shoot") and anim_player.current_animation != "shoot": play_shoot_effect() if Input.is_action_just_pressed("ui_cancel"): if Input.mouse_mode == Input.MOUSE_MODE_VISIBLE: get_tree().quit(0) Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) if Input.is_action_just_pressed("debug_overlay"): player_debug.visible = !player_debug.visible func _physics_process(delta: float) -> void: if not is_on_floor(): velocity.y -= gravity * delta if Input.is_action_just_pressed("jump") and is_on_floor(): velocity.y = JUMP_VELOCITY var input_dir = Input.get_vector("move_left", "move_right", "move_forward", "move_backward") var direction = (head.transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized() if direction: velocity.x = direction.x * player_stats.speed velocity.z = direction.z * player_stats.speed else: velocity.x = 0.0 velocity.z = 0.0 if Input.is_action_pressed("sprint"): velocity.x = velocity.x * player_stats.sprint_multiplier velocity.z = velocity.z * player_stats.sprint_multiplier camera.fov = lerp(camera.fov, FOV_SPRINT, FOV_LERP) else: camera.fov = lerp(camera.fov, FOV_DEFAULT, FOV_LERP) # Animations if input_dir != Vector2.ZERO and is_on_floor() and anim_player.current_animation != "shoot": anim_player.play("move") else: anim_player.play("idle") if player_debug.visible: player_debug.add_stat("vel_x", str(velocity.x)) player_debug.add_stat("vel_y", str(velocity.y)) player_debug.add_stat("vel_z", str(velocity.z)) player_debug.add_stat("vel_tot", str(absf(velocity.z) + absf(velocity.x) + absf(velocity.y))) player_debug.add_stat("hp", str(player_stats.current_health)) move_and_slide() func play_shoot_effect(): anim_player.stop() anim_player.play("shoot") muzzle_flash.restart() muzzle_flash.emitting = true var collider = pistol_raycast.get_collider() var col_point = pistol_raycast.get_collision_point() if col_point and collider: var b = bullet_decal.instantiate() pistol_raycast.get_collider().add_child(b) b.global_transform.origin = col_point b.look_at(col_point + pistol_raycast.get_collision_normal(), Vector3.UP)