#include "FirstPersonPlayer.h" #include #include #include #include #include using namespace godot; namespace blitz { static constexpr float WALK_SPEED = 5.0f; static constexpr float SPRINT_SPEED = 7.0f; static constexpr float JUMP_VELOCITY = 4.5f; static constexpr float GRAVITY = 9.81f; static constexpr float SENSITIVITY = 0.003f; static constexpr float BOB_FREQ = 2.0f; static constexpr float BOB_AMP = 0.08f; static constexpr float AIR_MOVEMENT = 3.0f; static constexpr float GROUND_FRICTION = 7.0f; static constexpr float BASE_FOV = 75.0f; static constexpr float FOV_CHANGE = 1.5f; static constexpr float FOV_TRANSITION = 8.0f; static constexpr float MIN_FOV_VELOCITY = 0.5; static constexpr float MAX_FOV_VELOCITY = SPRINT_SPEED * 2.0f; void FirstPersonPlayer::_bind_methods() {} FirstPersonPlayer::FirstPersonPlayer() : m_BobTime(0) {} FirstPersonPlayer::~FirstPersonPlayer() {} void FirstPersonPlayer::_ready() { InputMap::get_singleton()->load_from_project_settings(); if (!Engine::get_singleton()->is_editor_hint()) { Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED); } m_Head = Object::cast_to(find_child("Head")); DEV_ASSERT(m_Head); m_Camera = Object::cast_to(m_Head->find_child("Camera")); DEV_ASSERT(m_Camera); } void FirstPersonPlayer::_unhandled_input(const godot::Ref& a_Event) { auto* event = Object::cast_to(a_Event.ptr()); if (event) UpdateCamera(*event); // TODO: remove if (Input::get_singleton()->is_action_just_pressed("escape")) { Input::MouseMode current = Input::get_singleton()->get_mouse_mode(); Input::get_singleton()->set_mouse_mode( (current == Input::MOUSE_MODE_CAPTURED) ? Input::MOUSE_MODE_VISIBLE : Input::MOUSE_MODE_CAPTURED); } } void FirstPersonPlayer::_physics_process(float a_Delta) { #if DEBUG_ENABLED if (Engine::get_singleton()->is_editor_hint()) { return; } #endif auto* Input = Input::get_singleton(); if (!is_on_floor()) set_velocity(get_velocity() - Vector3{0, GRAVITY * a_Delta, 0}); if (Input->is_action_pressed("jump") && is_on_floor()) set_velocity({get_velocity().x, JUMP_VELOCITY, get_velocity().z}); m_Speed = Input->is_action_pressed("sprint") ? SPRINT_SPEED : WALK_SPEED; UpdatePosition(a_Delta); UpdateFOV(a_Delta); UpdateBobbing(a_Delta); move_and_slide(); } void FirstPersonPlayer::UpdateBobbing(float a_Delta) { m_BobTime += a_Delta * get_velocity().length() * is_on_floor(); Vector3 newPos{static_cast(Math::cos(m_BobTime * BOB_FREQ / 2.0) * BOB_AMP), static_cast(Math::sin(m_BobTime * BOB_FREQ) * BOB_AMP), 0}; m_Camera->set_transform({m_Camera->get_transform().basis, newPos}); } void FirstPersonPlayer::UpdateCamera(const InputEventMouseMotion& a_Event) { m_Head->rotate_y(-a_Event.get_relative().x * SENSITIVITY); m_Camera->rotate_x(-a_Event.get_relative().y * SENSITIVITY); float rotationX = m_Camera->get_rotation().x; CLAMP(rotationX, Math::deg_to_rad(-40.0), Math::deg_to_rad(60.0)); m_Camera->set_rotation({rotationX, get_rotation().y, get_rotation().z}); } void FirstPersonPlayer::UpdatePosition(float delta) { auto* Input = Input::get_singleton(); Vector2 inputDirection = Input->get_vector("move_left", "move_right", "move_forwards", "move_backwards"); Vector3 direction = (m_Head->get_transform().basis.xform(Vector3(inputDirection.x, 0, inputDirection.y))).normalized(); if (is_on_floor()) { if (!direction.is_zero_approx()) { set_velocity({direction.x * m_Speed, get_velocity().y, direction.z * m_Speed}); } else { set_velocity({Math::lerp(static_cast(get_velocity().x), static_cast(direction.x * m_Speed), static_cast(delta * GROUND_FRICTION)), get_velocity().y, Math::lerp(static_cast(get_velocity().z), static_cast(direction.z * m_Speed), static_cast(delta * GROUND_FRICTION))}); } } else { set_velocity({Math::lerp(static_cast(get_velocity().x), static_cast(direction.x * m_Speed), static_cast(delta * AIR_MOVEMENT)), get_velocity().y, Math::lerp(static_cast(get_velocity().z), static_cast(direction.z * m_Speed), static_cast(delta * AIR_MOVEMENT))}); } } void FirstPersonPlayer::UpdateFOV(float a_Delta) { float velocityClamped = Math::clamp(get_velocity().length(), MIN_FOV_VELOCITY, MAX_FOV_VELOCITY); float targetFOV = BASE_FOV + FOV_CHANGE * velocityClamped; m_Camera->set_fov(Math::lerp(m_Camera->get_fov(), targetFOV, a_Delta * FOV_TRANSITION)); } } // namespace blitz