I'm making a movement-based, first-person 3D game where the player should be able to gain a lot of horizontal speed over time with WASD, but also will stop due to friction very quickly the moment they release their movement key. The player's current horizontal movement is a velocity stored in a 2-dimensional vector.
Due to the requirement of needing to stop fast, I have a high amount of friction applied (the velocity is multiplied by 0.7
.) However, in order to allow gaining high amounts of horizontal speed, the friction is much, much lower when the player is moving, 0.99
.
I intend to separate the friction for forward/back from the friction for left/right, as this allows the player finer control over the current directions they're moving in.
Unfortunately, my code is broken, resulting in it working exactly as I intend only when you're facing in a cardinal direction — every other time, you have far too much friction. At this point, I realise that I'm not really sure what I'm doing, and my guesses aren't panning out. How do I correctly apply friction separately to the forwards/backwards and side directions?
I don't really want to include it in the question, but in case it's necessary, here's my current code — JavaScript, with a custom Vec2 class.
powXY
is v1.x **= v2.x; v1.y **= v2.y
. Everything else should be self-explanatory.
const direction = this.CAMERA.rotation.y;
const forwardBackVector = Vec2.fromDirection(direction).abs();
const leftRightVector = Vec2.fromDirection(direction + Math.PI / 2).abs();
let forwardBackInput = 0;
let leftRightInput = 0;
if (this.KEYBOARD.isDown("KeyE"))
forwardBackInput++;
if (this.KEYBOARD.isDown("KeyD"))
forwardBackInput--;
if (this.KEYBOARD.isDown("KeyS"))
leftRightInput++;
if (this.KEYBOARD.isDown("KeyF"))
leftRightInput--;
let forwardBackFriction = new Vec2(this.FRICTION).powXY(...forwardBackVector.xy);
let leftRightFriction = new Vec2(this.FRICTION).powXY(...leftRightVector.xy);
const acceleration = Vec2.ZERO;
if (forwardBackInput) {
const normalisedVector = Vec2.fromDirection(direction + (forwardBackInput === 1 ? 0 : Math.PI));
const dot = this.horizontalVelocity.dot(normalisedVector) * this.horizontalVelocity.length();
acceleration.add(normalisedVector
.multiply(dot < this.SPEED_HORIZONTAL_CAP ? this.SPEED_HORIZONTAL_FAST : this.SPEED_HORIZONTAL)
.multiply(delta)
.multiply(leftRightInput ? 0.5 : 1));
forwardBackFriction = new Vec2(this.FRICTION_MOVE).powXY(...forwardBackVector.xy);
}
if (leftRightInput) {
const normalisedVector = Vec2.fromDirection(direction + Math.PI / 2 * leftRightInput);
const dot = this.horizontalVelocity.dot(normalisedVector) * this.horizontalVelocity.length();
acceleration.add(normalisedVector.copy()
.multiply(dot < this.SPEED_HORIZONTAL_CAP ? this.SPEED_HORIZONTAL_FAST : this.SPEED_HORIZONTAL)
.multiply(delta)
.multiply(forwardBackInput ? 0.5 : 1));
leftRightFriction = new Vec2(this.FRICTION_MOVE).powXY(...leftRightVector.xy);
}
this.horizontalVelocity.add(acceleration);
this.CAMERA.position.x -= this.horizontalVelocity.y * delta;
this.CAMERA.position.z -= this.horizontalVelocity.x * delta;
for (const friction of [forwardBackFriction, leftRightFriction]) {
this.horizontalVelocity.x *= friction.x ** delta;
this.horizontalVelocity.y *= friction.y ** delta;
}
```