This commit is contained in:
2024-08-13 20:00:21 +02:00
parent 58a9f5ac0b
commit 9bfe173176
6 changed files with 216 additions and 16 deletions

139
src/FirstPersonPlayer.cpp Normal file
View File

@@ -0,0 +1,139 @@
#include "FirstPersonPlayer.h"
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/input.hpp>
#include <godot_cpp/classes/input_event_mouse_motion.hpp>
#include <godot_cpp/classes/input_map.hpp>
#include <godot_cpp/core/math.hpp>
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<Node3D>(find_child("Head"));
DEV_ASSERT(m_Head);
m_Camera = Object::cast_to<Camera3D>(m_Head->find_child("Camera"));
DEV_ASSERT(m_Camera);
}
void FirstPersonPlayer::_unhandled_input(const godot::Ref<godot::InputEvent>& a_Event) {
auto* event = Object::cast_to<InputEventMouseMotion>(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<float>(Math::cos(m_BobTime * BOB_FREQ / 2.0) * BOB_AMP),
static_cast<float>(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<float>(get_velocity().x), static_cast<float>(direction.x * m_Speed),
static_cast<float>(delta * GROUND_FRICTION)),
get_velocity().y,
Math::lerp(static_cast<float>(get_velocity().z), static_cast<float>(direction.z * m_Speed),
static_cast<float>(delta * GROUND_FRICTION))});
}
} else {
set_velocity({Math::lerp(static_cast<float>(get_velocity().x), static_cast<float>(direction.x * m_Speed),
static_cast<float>(delta * AIR_MOVEMENT)),
get_velocity().y,
Math::lerp(static_cast<float>(get_velocity().z), static_cast<float>(direction.z * m_Speed),
static_cast<float>(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

36
src/FirstPersonPlayer.h Normal file
View File

@@ -0,0 +1,36 @@
#pragma once
#include <godot_cpp/classes/camera3d.hpp>
#include <godot_cpp/classes/character_body3d.hpp>
#include <godot_cpp/classes/node3d.hpp>
#include <godot_cpp/classes/input_event_mouse_motion.hpp>
namespace blitz {
class FirstPersonPlayer : public godot::CharacterBody3D {
GDCLASS(FirstPersonPlayer, godot::CharacterBody3D)
protected:
static void _bind_methods();
public:
FirstPersonPlayer();
~FirstPersonPlayer();
// Godot overrides
void _unhandled_input(const godot::Ref<godot::InputEvent>&);
void _physics_process(float delta);
void _ready();
private:
godot::Camera3D* m_Camera;
godot::Node3D* m_Head;
float m_BobTime;
float m_Speed;
void UpdateBobbing(float delta);
void UpdateFOV(float delta);
void UpdateCamera(const godot::InputEventMouseMotion&);
void UpdatePosition(float delta);
};
} // namespace blitz

View File

@@ -2,6 +2,7 @@
#include "Player.h"
#include "SpringArmPivot.h"
#include "FirstPersonPlayer.h"
#include <gdextension_interface.h>
#include <godot_cpp/core/defs.hpp>
@@ -16,6 +17,7 @@ void initialize_example_module(ModuleInitializationLevel p_level) {
ClassDB::register_class<blitz::Player>();
ClassDB::register_class<blitz::SpringArmPivot>();
ClassDB::register_class<blitz::FirstPersonPlayer>();
}
void uninitialize_example_module(ModuleInitializationLevel p_level) {