Skip to content

Commit 07050b6

Browse files
committed
Implement FlyByCameraControl for first-person camera movement and
rotation - Added FlyByCameraControl component to handle camera movement (WASD, Space, Shift) - Implemented mouse-based yaw and pitch rotation with vertical angle clamping - Calculated movement velocity based on keyboard input and applied it to the camera position - Updated target position for the camera to maintain a dynamic "look-at" behavior - Included JavaDoc documentation for class and methods
1 parent cb28e80 commit 07050b6

File tree

1 file changed

+107
-49
lines changed

1 file changed

+107
-49
lines changed

src/main/java/engine/components/FlyByCameraControl.java

Lines changed: 107 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -7,117 +7,175 @@
77
import math.Mathf;
88
import math.Vector3f;
99

10+
/**
11+
* The {@code FlyByCameraControl} class provides first-person camera control functionality, allowing
12+
* the user to move the camera in all directions and adjust its orientation using mouse and keyboard
13+
* input. This control simulates a "fly-by" movement, typically used in 3D games or applications
14+
* that require free-form camera navigation.
15+
*
16+
* <p>The movement is controlled by the following keys:
17+
*
18+
* <ul>
19+
* <li>W: Move forward
20+
* <li>S: Move backward
21+
* <li>A: Move left (strafe)
22+
* <li>D: Move right (strafe)
23+
* <li>SPACE: Move up
24+
* <li>SHIFT: Move down
25+
* </ul>
26+
*
27+
* <p>The mouse controls the camera's yaw (left-right) and pitch (up-down) based on the mouse
28+
* movement, with vertical angle limits set for pitch to prevent excessive rotations.
29+
*/
1030
public class FlyByCameraControl extends AbstractComponent {
1131

32+
private static final float DEFAULT_MOUSE_SENSITIVITY = 10f;
33+
34+
private static final float DEFAULT_MOVE_SPEED = 30f;
35+
36+
private static final float MAX_VERTICAL_ANGLE = 80f;
37+
38+
private static final float MIN_VERTICAL_ANGLE = -80f;
39+
1240
private final Vector3f forward = new Vector3f();
1341

1442
private final Vector3f target = new Vector3f();
1543

16-
private float mouseSensitivity = 10f;
44+
private float mouseSensitivity = DEFAULT_MOUSE_SENSITIVITY;
1745

18-
private float moveSpeed = 30f;
46+
private float moveSpeed = DEFAULT_MOVE_SPEED;
1947

2048
private Input input;
2149

2250
private Camera camera;
2351

24-
// Additional parameters for controlling vertical look limits
25-
private float maxVerticalAngle = 80f; // Max pitch angle in degrees
26-
27-
private float minVerticalAngle = -80f; // Min pitch angle in degrees
28-
52+
/**
53+
* Constructs a new {@code FlyByCameraControl} with the specified input and camera.
54+
*
55+
* @param input The {@link Input} instance to capture user input.
56+
* @param camera The {@link PerspectiveCamera} to control.
57+
* @throws IllegalArgumentException if either {@code input} or {@code camera} is {@code null}.
58+
*/
2959
public FlyByCameraControl(Input input, PerspectiveCamera camera) {
30-
if (input == null) {
31-
throw new IllegalArgumentException("Input cannot be null.");
32-
}
33-
if (camera == null) {
34-
throw new IllegalArgumentException("Camera cannot be null.");
60+
if (input == null || camera == null) {
61+
throw new IllegalArgumentException("Input and Camera cannot be null.");
3562
}
3663
this.input = input;
3764
this.camera = camera;
3865
}
3966

67+
/**
68+
* Updates the camera control state, handling mouse input for camera rotation and keyboard input
69+
* for movement.
70+
*
71+
* <p>This method is typically called once per frame.
72+
*
73+
* @param tpf The time per frame (delta time) used for movement scaling.
74+
*/
4075
@Override
4176
public void update(float tpf) {
4277
float mouseX = input.getMouseDeltaX() * mouseSensitivity * tpf;
4378
float mouseY = input.getMouseDeltaY() * mouseSensitivity * tpf;
4479

80+
handleRotation(mouseX, mouseY);
81+
updateTarget();
82+
83+
Vector3f velocity = calculateVelocity();
84+
if (velocity.length() > 0) {
85+
applyMovement(velocity, tpf);
86+
}
87+
88+
input.center();
89+
}
90+
91+
/**
92+
* Handles camera rotation based on mouse input, applying yaw and pitch to the camera's transform.
93+
* The vertical rotation (pitch) is clamped within defined limits to prevent excessive rotation.
94+
*
95+
* @param mouseX The mouse movement on the X-axis (horizontal movement).
96+
* @param mouseY The mouse movement on the Y-axis (vertical movement).
97+
*/
98+
private void handleRotation(float mouseX, float mouseY) {
99+
// Handle yaw (left-right rotation)
45100
float yaw = mouseX;
46-
// To avoid issues with yaw wrapping at 360 or -360 degrees, we can clamp yaw between -180 and
47-
// 180
48101
yaw = Mathf.clamp(yaw, -180f, 180f);
49-
50102
camera.getTransform().rotate(0, Mathf.toRadians(yaw), 0);
51103

52-
updateTarget();
53-
104+
// Handle pitch (up-down rotation)
54105
Vector3f rotation = camera.getTransform().getRotation();
55-
float currentPitch = rotation.x; // Current pitch around the X-axis
56-
currentPitch += Mathf.toRadians(mouseY); // Adjust based on mouse movement
57-
58-
// Clamp the vertical rotation (pitch) to avoid excessive rotation
106+
float currentPitch = rotation.x;
107+
currentPitch += Mathf.toRadians(mouseY);
59108
currentPitch =
60109
Mathf.clamp(
61-
currentPitch, Mathf.toRadians(minVerticalAngle), Mathf.toRadians(maxVerticalAngle));
110+
currentPitch, Mathf.toRadians(MIN_VERTICAL_ANGLE), Mathf.toRadians(MAX_VERTICAL_ANGLE));
62111

63112
rotation.x = currentPitch;
64113
camera.getTransform().setRotation(rotation);
65-
66-
// Update camera's target based on position and rotation
67-
updateTarget();
68-
69-
Vector3f velocity = calculateVelocity();
70-
71-
if (velocity.length() > 0) {
72-
velocity.normalizeLocal();
73-
Vector3f position = camera.getTransform().getPosition();
74-
position.addLocal(velocity.mult(moveSpeed * tpf));
75-
camera.getTransform().setPosition(position);
76-
updateTarget();
77-
}
78-
79-
input.center();
80114
}
81115

116+
/**
117+
* Calculates the movement velocity vector based on the current keyboard input. The velocity is a
118+
* vector that indicates the desired direction of movement.
119+
*
120+
* @return A {@link Vector3f} representing the movement velocity.
121+
*/
82122
private Vector3f calculateVelocity() {
83123
Vector3f velocity = new Vector3f();
84124
Vector3f forward = camera.getTransform().getForward();
85125
Vector3f right = camera.getTransform().getRight();
86126

127+
// Movement controls (WASD, Space, Shift)
87128
if (input.isKeyPressed(Key.W)) velocity.addLocal(forward);
88129
if (input.isKeyPressed(Key.S)) velocity.addLocal(forward.negate());
89-
if (input.isKeyPressed(Key.A)) velocity.addLocal(right.negate()); // Strafe left
90-
if (input.isKeyPressed(Key.D)) velocity.addLocal(right); // Strafe right
130+
if (input.isKeyPressed(Key.A)) velocity.addLocal(right.negate());
131+
if (input.isKeyPressed(Key.D)) velocity.addLocal(right);
132+
if (input.isKeyPressed(Key.SPACE)) velocity.addLocal(0, -1, 0);
133+
if (input.isKeyPressed(Key.SHIFT)) velocity.addLocal(0, 1, 0);
91134

92-
if (input.isKeyPressed(Key.SPACE)) {
93-
velocity.addLocal(0, -1, 0);
94-
}
95-
if (input.isKeyPressed(Key.SHIFT)) {
96-
velocity.addLocal(0, 1, 0);
97-
}
98135
return velocity;
99136
}
100137

138+
/**
139+
* Applies the calculated velocity to the camera's position, updating the camera's location based
140+
* on user movement input.
141+
*
142+
* @param velocity The movement velocity to apply.
143+
* @param tpf The time per frame (delta time) to scale the movement.
144+
*/
145+
private void applyMovement(Vector3f velocity, float tpf) {
146+
velocity.normalizeLocal();
147+
Vector3f position = camera.getTransform().getPosition();
148+
position.addLocal(velocity.mult(moveSpeed * tpf));
149+
camera.getTransform().setPosition(position);
150+
updateTarget();
151+
}
152+
153+
/**
154+
* Updates the target position for the camera. The target is a point in the scene that the camera
155+
* looks at, and it is updated based on the camera's current position and rotation.
156+
*/
101157
private void updateTarget() {
102158
Vector3f position = camera.getTransform().getPosition();
103159
Vector3f rotation = camera.getTransform().getRotation();
104-
float yaw = rotation.y;
105-
float pitch = rotation.x;
106160

107161
forward
108-
.set(Mathf.cos(yaw) * Mathf.cos(pitch), Mathf.sin(pitch), Mathf.sin(yaw) * Mathf.cos(pitch))
162+
.set(
163+
Mathf.cos(rotation.y) * Mathf.cos(rotation.x),
164+
Mathf.sin(rotation.x),
165+
Mathf.sin(rotation.y) * Mathf.cos(rotation.x))
109166
.normalizeLocal();
110167

111168
target.set(position).addLocal(forward);
112-
113169
camera.setTarget(target);
114170
}
115171

172+
/** This method is called when the component is attached to an entity. Currently not used. */
116173
@Override
117174
public void onAttach() {
118175
// Not used yet
119176
}
120177

178+
/** This method is called when the component is detached from an entity. Currently not used. */
121179
@Override
122180
public void onDetach() {
123181
// Not used yet

0 commit comments

Comments
 (0)