three-game-engine
Version:
Simple light-weight game engine using three.js, three-mesh-ui and rapier
121 lines (120 loc) • 6.41 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const THREE = require("three");
const CharacterController_1 = require("./CharacterController");
const RigidBodyComponent_1 = require("../components/RigidBodyComponent");
const defaultCapsuleOptions = {
halfHeight: 0.45,
radius: 0.4,
density: 500
};
const defaultKinematicCharacterControllerOptions = {
offset: 0.05,
applyImpulsesToDynamicBodies: false
};
/**
* Based off Rapier's CharacterController, but with more functionality.
*/
class KinematicCharacterController extends CharacterController_1.default {
constructor(parent, options, controllerOptions, kinematicChararacterControllerOptions) {
super(parent, {
rigidBody: {
type: 'kinematicPositionBased',
colliders: [
{ type: 'capsule', ...Object.assign({}, defaultCapsuleOptions, ((controllerOptions || {}).capsule || {})) }
],
enabledRotations: { x: false, y: true, z: false }
},
...options // merge with any passed in GameObjectOptions
}, controllerOptions);
this.kinematicChararacterControllerOptions = Object.assign({}, defaultKinematicCharacterControllerOptions, kinematicChararacterControllerOptions);
this.verticalVelocity = 0;
}
afterLoaded() {
const rapierWorld = this.getScene().getRapierWorld();
const offset = this.kinematicChararacterControllerOptions.offset;
this.rapierCharacterController = rapierWorld.createCharacterController(offset);
if (typeof this.kinematicChararacterControllerOptions.maxSlopeClimbAngle != 'undefined') {
this.rapierCharacterController.setMaxSlopeClimbAngle(this.kinematicChararacterControllerOptions.maxSlopeClimbAngle);
}
if (typeof this.kinematicChararacterControllerOptions.minSlopeSlideAngle != 'undefined') {
this.rapierCharacterController.setMinSlopeSlideAngle(this.kinematicChararacterControllerOptions.minSlopeSlideAngle);
}
const { autoStep } = this.kinematicChararacterControllerOptions;
if (autoStep) {
this.rapierCharacterController.enableAutostep(autoStep.maxHeight, autoStep.minWidth, autoStep.includeDynamicBodies);
}
else {
this.rapierCharacterController.disableAutostep();
}
if (this.kinematicChararacterControllerOptions.snapToGroundDistance) {
this.rapierCharacterController.enableSnapToGround(this.kinematicChararacterControllerOptions.snapToGroundDistance);
}
else {
this.rapierCharacterController.disableSnapToGround();
}
this.rapierCharacterController.setApplyImpulsesToDynamicBodies(this.kinematicChararacterControllerOptions.applyImpulsesToDynamicBodies);
}
beforeRender({ deltaTimeInSec, time }) {
if (!this.isLoaded()) {
return;
}
const inputManager = this.getScene().game.inputManager;
const keyboard = inputManager.keyboardHandler;
const yawAngle = this.getDesiredYaw();
const pitchAngle = this.getDesiredPitch();
const desiredRotation = new THREE.Quaternion();
desiredRotation.setFromEuler(new THREE.Euler(pitchAngle, yawAngle, 0, 'YXZ'));
const rigidBodyComponent = this.getComponent(RigidBodyComponent_1.default);
const rapierRigidBody = rigidBodyComponent.getRapierRigidBody();
const yawQuaternion = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, yawAngle, 0));
rapierRigidBody.setRotation(yawQuaternion, true);
let camera = null;
this.threeJSGroup.traverse(obj => {
if (obj instanceof THREE.Camera) {
camera = obj;
}
});
if (camera) {
camera.rotation.set(pitchAngle, 0, 0);
}
const desiredMovementVector = this.getDesiredTranslation(deltaTimeInSec);
// Make it so "forward" is in the same direction as where the character faces
desiredMovementVector.applyAxisAngle(new THREE.Vector3(0, 1, 0), yawAngle);
// Emulate gravity (accelerates verticalVelocity downwards to a max terminal velocity)
const isOnGround = this.isOnGround();
if (isOnGround && this.verticalVelocity < 0) {
this.verticalVelocity = -1; // keeps collider pressed against ground
}
else {
this.verticalVelocity -= (9.8 * deltaTimeInSec);
if (this.verticalVelocity < -10) {
this.verticalVelocity = -10; // terminal velocity
}
}
desiredMovementVector.y += (this.verticalVelocity * deltaTimeInSec);
//console.log(`isOnGround: ${isOnGround} vert velocity: ${this.verticalVelocity}, y: ${this.rapierRigidBody.translation().y}`);
const collider = rapierRigidBody.collider(0);
const computeMovementOptionalArgs = this.kinematicChararacterControllerOptions.computeColliderMovement;
this.rapierCharacterController.computeColliderMovement(collider, desiredMovementVector, computeMovementOptionalArgs === null || computeMovementOptionalArgs === void 0 ? void 0 : computeMovementOptionalArgs.filterFlag, computeMovementOptionalArgs === null || computeMovementOptionalArgs === void 0 ? void 0 : computeMovementOptionalArgs.filterGroups, computeMovementOptionalArgs === null || computeMovementOptionalArgs === void 0 ? void 0 : computeMovementOptionalArgs.filterPredicate);
const correctedMovement = this.rapierCharacterController.computedMovement();
const translation = rapierRigidBody.translation();
rapierRigidBody.setNextKinematicTranslation({
x: translation.x + correctedMovement.x,
y: translation.y + correctedMovement.y,
z: translation.z + correctedMovement.z
});
// Jump mechanics
if (keyboard.isKeyDown(' ')) {
const timeSinceLastJump = time - this.lastJumpTime;
if (timeSinceLastJump > this.controllerOptions.jumpCooldown) {
if (isOnGround) {
// There is ground below the character, so the player can indeed initate a jump
this.verticalVelocity = 5;
this.lastJumpTime = time;
}
}
}
}
}
exports.default = KinematicCharacterController;