UNPKG

three-game-engine

Version:

Simple light-weight game engine using three.js, three-mesh-ui and rapier

121 lines (120 loc) 6.41 kB
"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;