Skip to content
This repository was archived by the owner on Jan 25, 2026. It is now read-only.

Commit 20b6c67

Browse files
committed
Good car camera controller
1 parent d0fd46b commit 20b6c67

File tree

2 files changed

+90
-14
lines changed

2 files changed

+90
-14
lines changed

src/scenes/nfs.cpp

Lines changed: 73 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "constants.hpp"
2828

2929
#include "input/raw/keyboard.hpp"
30+
#include "input/raw/mouse.hpp"
3031
#include "resources/time.hpp"
3132

3233
static std::expected<void, std::string> startup(/* clang-format off */
@@ -84,6 +85,7 @@ static std::expected<void, std::string> startup(/* clang-format off */
8485
static std::expected<void, std::string> update(/* clang-format off */
8586
std::shared_ptr<entt::registry> registry,
8687
std::shared_ptr<Renderer> renderer,
88+
std::shared_ptr<scenes::nfs::components::CameraState> cameraState,
8789
resources::Time& time
8890
) { /* clang-format on */
8991

@@ -178,28 +180,86 @@ static std::expected<void, std::string> update(/* clang-format off */
178180
angularVelocity.value.z = targetAngularVelocity;
179181
}
180182

181-
// Third-person camera system - follow the car from behind
182-
const float cameraDistance = 10.0f; // Distance behind the car
183-
const float cameraHeight = 3.0f; // Height above the car
183+
// GTA 5-style camera system
184+
auto carView = registry->view<components::Position, components::Rotation>();
185+
for (auto carEntity : carView) {
186+
auto& carPosition = carView.get<components::Position>(carEntity);
187+
auto& carRotation = carView.get<components::Rotation>(carEntity);
184188

185-
// Calculate camera position: behind the car + height offset
186-
glm::vec3 cameraOffset = forward * cameraDistance + glm::vec3(0.0f, 0.0f, cameraHeight);
187-
glm::vec3 cameraPos = position.value + cameraOffset;
189+
// Get car forward direction
190+
glm::vec3 carForward = carRotation.value * glm::vec3(0.0f, 1.0f, 0.0f);
188191

189-
// Camera looks at the car (slightly ahead of it)
190-
glm::vec3 lookTarget = position.value - forward * 2.0f;
191-
glm::vec3 cameraDir = glm::normalize(lookTarget - cameraPos);
192+
// Handle mouse input for camera rotation
193+
glm::vec2 mouseDelta = input::Mouse::getPositionDelta();
194+
bool hasMouseInput = glm::length(mouseDelta) > 0.01f;
192195

193-
// Update renderer camera
194-
renderer->setCameraPos(cameraPos);
195-
renderer->setCameraDir(cameraDir);
196+
if (hasMouseInput) {
197+
cameraState->yaw -= mouseDelta.x * cameraState->mouseSensitivity;
198+
cameraState->pitch += mouseDelta.y * cameraState->mouseSensitivity; // Fixed inversion
199+
200+
// Clamp pitch to prevent camera flipping
201+
cameraState->pitch = std::clamp(cameraState->pitch, -1.4f, 0.5f); // About -80° to +30°
202+
203+
cameraState->isUserControlling = true;
204+
cameraState->timeSinceLastInput = 0.0f;
205+
} else {
206+
cameraState->timeSinceLastInput += time.deltaTime;
207+
208+
// Start auto-centering after delay
209+
if (cameraState->timeSinceLastInput > cameraState->autoReturnDelay) {
210+
cameraState->isUserControlling = false;
211+
212+
// Calculate target yaw (behind the car)
213+
glm::vec3 carForwardXY = glm::normalize(glm::vec3(carForward.x, carForward.y, 0.0f));
214+
cameraState->targetYaw = atan2(carForwardXY.y, carForwardXY.x) + constants::PI; // Behind the car
215+
216+
// Smoothly interpolate back to target position
217+
float returnSpeed = cameraState->autoReturnSpeed * time.deltaTime;
218+
219+
// Handle yaw wrapping (shortest rotation path)
220+
float yawDiff = cameraState->targetYaw - cameraState->yaw;
221+
while (yawDiff > glm::pi<float>())
222+
yawDiff -= 2.0f * glm::pi<float>();
223+
while (yawDiff < -glm::pi<float>())
224+
yawDiff += 2.0f * glm::pi<float>();
225+
226+
cameraState->yaw += yawDiff * returnSpeed;
227+
cameraState->pitch += (cameraState->targetPitch - cameraState->pitch) * returnSpeed;
228+
}
229+
}
230+
231+
// Calculate camera position based on yaw, pitch, and distance
232+
float cosYaw = cos(cameraState->yaw);
233+
float sinYaw = sin(cameraState->yaw);
234+
float cosPitch = cos(cameraState->pitch);
235+
float sinPitch = sin(cameraState->pitch);
236+
237+
// Camera offset in spherical coordinates (negative distance to position behind car)
238+
glm::vec3 cameraOffset;
239+
cameraOffset.x = -cameraState->distance * cosYaw * cosPitch;
240+
cameraOffset.y = -cameraState->distance * sinYaw * cosPitch;
241+
cameraOffset.z = cameraState->height + cameraState->distance * sinPitch;
242+
243+
glm::vec3 cameraPos = carPosition.value + cameraOffset;
244+
245+
// Camera looks at the car (with slight forward offset)
246+
glm::vec3 lookTarget = carPosition.value + carForward * 1.0f + glm::vec3(0.0f, 0.0f, 1.0f);
247+
glm::vec3 cameraDir = glm::normalize(lookTarget - cameraPos);
248+
249+
// Update renderer camera
250+
renderer->setCameraPos(cameraPos);
251+
renderer->setCameraDir(cameraDir);
252+
253+
// Only handle the first car entity for now
254+
break;
255+
}
196256
}
197257

198258
return {};
199259
}
200260

201261
void scenes::nfs::NFS::build(Game& game) {
202-
std::shared_ptr<scenes::nfs::components::CameraState> cameraState;
262+
auto cameraState = std::make_shared<scenes::nfs::components::CameraState>();
203263
game.addResource(cameraState);
204264

205265
game.addSystem(Schedule::Startup, startup);

src/scenes/nfs.hpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,23 @@ class Game;
55

66
namespace scenes::nfs {
77
namespace components {
8-
struct CameraState {};
8+
struct CameraState {
9+
float yaw = 0.0f; // Horizontal rotation around the car
10+
float pitch = -0.1f; // Vertical angle (looking down slightly at the car)
11+
float distance = 10.0f; // Distance from the car
12+
float height = 3.0f; // Height offset
13+
14+
float mouseSensitivity = 0.003f; // Mouse sensitivity
15+
float autoReturnSpeed = 2.0f; // Speed of auto-centering
16+
float timeSinceLastInput = 0.0f; // Time since last mouse input
17+
float autoReturnDelay = 2.0f; // Delay before auto-centering starts
18+
19+
bool isUserControlling = false; // Whether user is actively controlling camera
20+
21+
// Target values for smooth auto-centering
22+
float targetYaw = 0.0f; // Target yaw (behind the car)
23+
float targetPitch = -0.1f; // Target pitch (slightly down)
24+
};
925
};
1026

1127
struct NFS {

0 commit comments

Comments
 (0)