SleakEngine 1.0.0
C++23 multi-backend game engine
Loading...
Searching...
No Matches
FirstPersonController.cpp
Go to the documentation of this file.
4#include <Core/Window.hpp>
5#include <SDL3/SDL.h>
6#include <Events/Event.hpp>
7#include <Math/Math.hpp>
8#include <Math/Quaternion.hpp>
9#include <cmath>
10
11namespace Sleak {
12
22
27
30 return false;
31
32 m_rigidbody = owner->GetComponent<RigidbodyComponent>();
33
34 if (camera) {
35 Math::Vector3D forward = camera->GetDirection();
36 m_yaw = atan2(forward.GetX(), forward.GetZ());
37 m_pitch = -asin(forward.GetY());
38 m_pitch = Math::Clamp(m_pitch,
39 static_cast<float>(m_pitchRange.GetX() * D2R),
40 static_cast<float>(m_pitchRange.GetY() * D2R));
41 }
42
43 return true;
44}
45
46void FirstPersonController::Update(float deltaTime) {
47 if (!bIsInitialized || !isEnabled) return;
48
49 UpdateInput(deltaTime);
50 UpdateCamera(deltaTime);
51}
52
54 bool cursor_set = enabled ? SDL_ShowCursor() : SDL_HideCursor();
55 (void)cursor_set;
56}
57
58void FirstPersonController::UpdateInput(float deltaTime) {
59 float x, y;
60 SDL_GetRelativeMouseState(&x, &y);
61
62 if (m_firstFrame) {
63 m_firstFrame = false;
64 return;
65 }
66
67 // Direct mouse input — no smoothing, 1:1 like UE
68 m_yaw += x * sensitivity;
69 m_pitch += y * sensitivity;
70
71 m_pitch = Math::Clamp(m_pitch,
72 static_cast<float>(m_pitchRange.GetX() * D2R),
73 static_cast<float>(m_pitchRange.GetY() * D2R));
74}
75
76void FirstPersonController::UpdateCamera(float deltaTime) {
77 if (!camera) return;
78
79 Math::Quaternion yawRotation = Math::Quaternion(Math::Vector3D::Up(), m_yaw);
80 Math::Quaternion pitchRotation = Math::Quaternion(Math::Vector3D::Right(), m_pitch);
81 Math::Quaternion combinedRotation = yawRotation * pitchRotation;
82
83 Math::Vector3D forward = combinedRotation * Math::Vector3D::Forward();
84
85 // XZ-projected movement axes (ground-locked)
86 Math::Vector3D flatForward(forward.GetX(), 0.0f, forward.GetZ());
87 if (flatForward.Magnitude() > 0.001f) {
88 flatForward = flatForward.Normalized();
89 } else {
90 flatForward = Math::Vector3D::Forward();
91 }
92 Math::Vector3D right = flatForward.Cross(Math::Vector3D::Up()).Normalized();
93
94 Math::Vector3D inputDir = right * translationInput.GetX() + flatForward * translationInput.GetZ();
95 bool hasInput = inputDir.Magnitude() > 0.001f;
96 if (hasInput) {
97 inputDir = inputDir.Normalized();
98 }
99
100 bool isGrounded = m_rigidbody && m_rigidbody->IsGrounded();
101
102 // Horizontal speed + accel (per mode)
103 float currentMaxSpeed;
104 float effectiveAccel;
105 float effectiveBraking;
106 if (m_flying) {
107 currentMaxSpeed = m_sprinting ? m_maxFlySpeed * m_flySprintMultiplier
108 : m_maxFlySpeed;
109 float walkRef = (m_maxWalkSpeed > 0.001f) ? m_maxWalkSpeed : 1.0f;
110 float speedRatio = currentMaxSpeed / walkRef;
111 effectiveAccel = m_maxAcceleration * speedRatio;
112 effectiveBraking = m_brakingDeceleration * speedRatio;
113 } else {
114 currentMaxSpeed = m_sprinting ? m_maxWalkSpeed * m_sprintMultiplier
115 : m_maxWalkSpeed;
116 effectiveAccel = m_maxAcceleration;
117 if (!isGrounded) effectiveAccel *= m_airControl;
118 effectiveBraking = m_brakingDeceleration;
119 if (isGrounded) effectiveBraking *= m_groundFriction;
120 }
121
122 float speed = m_velocity.Magnitude();
123
124 if (hasInput) {
125 m_velocity = m_velocity + inputDir * effectiveAccel * deltaTime;
126
127 float newSpeed = m_velocity.Magnitude();
128 if (newSpeed > currentMaxSpeed) {
129 m_velocity = m_velocity * (currentMaxSpeed / newSpeed);
130 }
131 } else {
132 if (speed > 0.01f) {
133 float drop = effectiveBraking * deltaTime;
134 float newSpeed = speed - drop;
135 if (newSpeed < 0.0f) newSpeed = 0.0f;
136 m_velocity = m_velocity * (newSpeed / speed);
137 } else {
138 m_velocity = Math::Vector3D::Zero();
139 }
140 }
141
142 // Wall sliding: cancel velocity pushing into walls
143 if (m_rigidbody && m_rigidbody->HadWallCollision()) {
144 Math::Vector3D wallNormal = m_rigidbody->GetWallNormal();
145 float dot = m_velocity.Dot(wallNormal);
146 if (dot < 0.0f) {
147 m_velocity = m_velocity - wallNormal * dot;
148 }
149 }
150
151 // Vertical: jump (ground) or fly
152 if (m_flying) {
153 if (m_flyVerticalInput > 0.001f || m_flyVerticalInput < -0.001f) {
154 m_flyVerticalVelocity += m_flyVerticalInput * effectiveAccel * deltaTime;
155 if (m_flyVerticalVelocity > currentMaxSpeed) m_flyVerticalVelocity = currentMaxSpeed;
156 if (m_flyVerticalVelocity < -currentMaxSpeed) m_flyVerticalVelocity = -currentMaxSpeed;
157 } else {
158 float vSpeed = std::fabs(m_flyVerticalVelocity);
159 if (vSpeed > 0.01f) {
160 float drop = effectiveBraking * deltaTime;
161 float newSpeed = vSpeed - drop;
162 if (newSpeed < 0.0f) newSpeed = 0.0f;
163 m_flyVerticalVelocity *= (newSpeed / vSpeed);
164 } else {
165 m_flyVerticalVelocity = 0.0f;
166 }
167 }
168 Math::Vector3D moveVel(m_velocity.GetX(),
169 m_flyVerticalVelocity,
170 m_velocity.GetZ());
171 camera->AddPosition(moveVel * deltaTime);
172 } else {
173 camera->AddPosition(m_velocity * deltaTime);
174
175 if (m_rigidbody && isGrounded) {
176 if (translationInput.GetY() > 0.0f) {
177 Math::Vector3D vel = m_rigidbody->GetVelocity();
178 vel = Math::Vector3D(vel.GetX(), m_jumpZVelocity, vel.GetZ());
179 m_rigidbody->SetVelocity(vel);
180 m_rigidbody->SetGrounded(false);
181 translationInput.SetY(0.0f);
182 }
183 }
184 }
185
186 // Update look target
187 Math::Vector3D lookTarget = camera->GetPosition() + forward;
188 camera->SetLookTarget(lookTarget);
189}
190
192 if (!isEnabled) return;
193
194 switch (e.GetKeyCode()) {
195 case Input::KEY_CODE::KEY__W: translationInput.SetZ(1.0f); break;
196 case Input::KEY_CODE::KEY__S: translationInput.SetZ(-1.0f); break;
197 case Input::KEY_CODE::KEY__A: translationInput.SetX(+1.0f); break;
198 case Input::KEY_CODE::KEY__D: translationInput.SetX(-1.0f); break;
200 if (!e.IsRepeat()) {
201 translationInput.SetY(1.0f);
202 }
203 break;
206 m_sprinting = true;
207 break;
208 default: break;
209 }
210}
211
213 if (!isEnabled) return;
214
215 switch (e.GetKeyCode()) {
217 case Input::KEY_CODE::KEY__S: translationInput.SetZ(0.0f); break;
219 case Input::KEY_CODE::KEY__D: translationInput.SetX(0.0f); break;
221 translationInput.SetY(0.0f);
222 break;
225 m_sprinting = false;
226 break;
227 default: break;
228 }
229}
230
232 if (m_flying == flying) return;
233 m_flying = flying;
234 m_flyVerticalVelocity = 0.0f;
235 m_flyVerticalInput = 0.0f;
236 translationInput.SetY(0.0f);
237}
238
241
242 if (!camera) return;
243
244 auto* app = Application::GetInstance();
245 if (app) app->GetWindow().SetRelativeMouseMode(enabled);
246 ToggleCursor(!enabled);
247
248 if (enabled) {
249 m_velocity = Math::Vector3D::Zero();
251 m_firstFrame = true;
252
253 Math::Vector3D forward = camera->GetDirection();
254 m_yaw = atan2(forward.GetX(), forward.GetZ());
255 m_pitch = -asin(forward.GetY());
256 m_pitch = Math::Clamp(m_pitch,
257 static_cast<float>(m_pitchRange.GetX() * D2R),
258 static_cast<float>(m_pitchRange.GetY() * D2R));
259 }
260}
261
262} // namespace Sleak
#define D2R
Definition Math.hpp:8
static Application * GetInstance()
The one Application for this process, or null before construction.
virtual void SetEnabled(bool enabled)
CameraController(GameObject *object)
virtual bool Initialize()
One-time setup, called after the component is attached and the owner is initialized.
GameObject * owner
Definition Component.hpp:81
static void UnregisterEvent(EventType type, std::string id)
Removes the single handler with matching id from type's handler list.
Definition Event.hpp:163
static std::string RegisterEventHandler(T *instance, void(T::*memberFunction)(const EventT &))
Registers a member-function handler bound to instance, returning an ID for later unregistration.
Definition Event.hpp:149
void OnKeyPressed(const Events::Input::KeyPressedEvent &e)
Updates translation/sprint input state from a key-down event.
void Update(float deltaTime) override
bool Initialize() override
Grabs the sibling RigidbodyComponent and derives initial yaw/pitch from the camera's facing.
void SetEnabled(bool enabled) override
Toggles relative mouse mode and, when re-enabling, resets velocity and re-syncs yaw/pitch.
void OnKeyReleased(const Events::Input::KeyReleasedEvent &e)
Clears translation/sprint input state from a key-up event.
void ToggleCursor(bool enable) override
Shows or hides the OS cursor and, typically, toggles relative mouse mode.
void SetFlying(bool flying)
Switches between grounded movement and free-flight, resetting vertical velocity.
float GetX() const
Definition Vector.hpp:216
float GetY() const
Definition Vector.hpp:217
static Vector3D Right()
Definition Vector.hpp:499
float GetY() const
Definition Vector.hpp:361
float GetX() const
Definition Vector.hpp:360
float GetZ() const
Definition Vector.hpp:362
static Vector3D Forward()
Definition Vector.hpp:501
static Vector3D Zero()
Definition Vector.hpp:495
static Vector3D Up()
Definition Vector.hpp:497
constexpr const T & Clamp(const T &value, T min, T max)
Clamps value into [min, max].
Definition Math.hpp:15
Root namespace for everything the engine exposes.
Definition Camera.hpp:10