UNPKG

@polygonjs/polygonjs

Version:

node-based WebGL 3D engine https://polygonjs.com

206 lines (205 loc) 6.64 kB
"use strict"; import { CapsuleSopOperation } from "./../../engine/operations/sop/Capsule"; import { Vector3 } from "three"; import { Box3 } from "three"; import { Line3 } from "three"; import { Matrix4 } from "three"; import { MathUtils } from "three"; const tmpGravity = new Vector3(0, 0, 0); const upVector = new Vector3(0, 1, 0); const tempVector1 = new Vector3(); const tempVector2 = new Vector3(); const tempVector3 = new Vector3(); const tempVector4 = new Vector3(); const tempVector5 = new Vector3(); const tempBox = new Box3(); const tempMat = new Matrix4(); const tempSegment = new Line3(); const startRotationRadians = new Vector3(); export class CorePlayer { constructor(options) { this._pressed = { forward: false, backward: false, left: false, right: false }; this._onGround = false; this._velocity = new Vector3(); this.capsuleInfo = { radius: CapsuleSopOperation.DEFAULT_PARAMS.radius, segment: new Line3( new Vector3(0, 0, 0), new Vector3( 0, -(CapsuleSopOperation.DEFAULT_PARAMS.height - 2 * CapsuleSopOperation.DEFAULT_PARAMS.radius), 0 ) ) }; this.startPosition = new Vector3(0, 5, 0); this.startRotation = new Vector3(0, 0, 0); this.jumpAllowed = true; this.jumpStrength = 10; this.runAllowed = true; this.runSpeedMult = 2; this._running = false; this.speed = 10; this.physicsSteps = 5; this.gravity = new Vector3(0, -30, 0); this._azimuthalAngle = 0; this._resetYMax = -25; this._resetRequiredCallback = () => { return this.object.position.y < this._resetYMax; }; this.setOptions(options); } setOptions(options) { this._setObject(options.object); this.setCollider(options.collider); } _setObject(object) { this.object = object; this.object.matrixAutoUpdate = true; } setCollider(collider) { this.collider = collider; } setCapsule(capsuleOptions) { this.capsuleInfo.radius = capsuleOptions.radius; this.capsuleInfo.segment.end.y = -(capsuleOptions.height - 2 * capsuleOptions.radius); } reset() { this.stop(); this.object.position.copy(this.startPosition); startRotationRadians.copy(this.startRotation).multiplyScalar(MathUtils.DEG2RAD); this.object.rotation.setFromVector3(startRotationRadians); this.object.updateMatrix(); this.object.updateWorldMatrix(true, true); this.object.updateMatrixWorld(true); } stop() { this._pressed.forward = false; this._pressed.backward = false; this._pressed.left = false; this._pressed.right = false; this._running = false; this._velocity.set(0, 0, 0); } setResetRequiredCallback(callback) { this._resetRequiredCallback = callback; } setAzimuthalAngle(angle) { this._azimuthalAngle = angle; } update(delta) { const deltaBounded = Math.min(delta, 0.1); for (let i = 0; i < this.physicsSteps; i++) { this._updateStep(deltaBounded / this.physicsSteps); } } _updateStep(delta) { if (!this._onGround) { tmpGravity.copy(this.gravity).multiplyScalar(delta); this._velocity.add(tmpGravity); } this.object.position.addScaledVector(this._velocity, delta); const angle = this._azimuthalAngle; const speed = this.speed * delta * (this._running ? this.runSpeedMult : 1); tempVector2.set(0, 0, 0); if (this._pressed.forward) { tempVector1.set(0, 0, -1).applyAxisAngle(upVector, angle); tempVector2.add(tempVector1); } if (this._pressed.backward) { tempVector1.set(0, 0, 1).applyAxisAngle(upVector, angle); tempVector2.add(tempVector1); } if (this._pressed.left) { tempVector1.set(-1, 0, 0).applyAxisAngle(upVector, angle); tempVector2.add(tempVector1); } if (this._pressed.right) { tempVector1.set(1, 0, 0).applyAxisAngle(upVector, angle); tempVector2.add(tempVector1); } tempVector2.normalize().multiplyScalar(speed); this.object.position.add(tempVector2); this.object.updateMatrixWorld(); const capsuleInfo = this.capsuleInfo; tempBox.makeEmpty(); tempMat.copy(this.collider.matrixWorld).invert(); tempSegment.copy(capsuleInfo.segment); tempSegment.start.applyMatrix4(this.object.matrixWorld).applyMatrix4(tempMat); tempSegment.end.applyMatrix4(this.object.matrixWorld).applyMatrix4(tempMat); tempBox.expandByPoint(tempSegment.start); tempBox.expandByPoint(tempSegment.end); tempBox.min.addScalar(-capsuleInfo.radius); tempBox.max.addScalar(capsuleInfo.radius); const intersectsBounds = (box, isLeaf, score, depth, nodeIndex) => { return box.intersectsBox(tempBox); }; const intersectsTriangle = (tri) => { const triPoint = tempVector3; const capsulePoint = tempVector4; const distance = tri.closestPointToSegment(tempSegment, triPoint, capsulePoint); if (distance < capsuleInfo.radius) { const depth = capsuleInfo.radius - distance; const direction = capsulePoint.sub(triPoint).normalize(); tempSegment.start.addScaledVector(direction, depth); tempSegment.end.addScaledVector(direction, depth); } }; this.collider.geometry.boundsTree.shapecast({ intersectsBounds, intersectsTriangle }); const newPosition = tempVector5; newPosition.copy(tempSegment.start); newPosition.applyMatrix4(this.collider.matrixWorld); const deltaVector = tempVector2; deltaVector.subVectors(newPosition, this.object.position); this._onGround = deltaVector.y > Math.abs(delta * this._velocity.y * 0.25); const offset = Math.max(0, deltaVector.length() - 1e-5); deltaVector.normalize().multiplyScalar(offset); this.object.position.add(deltaVector); if (!this._onGround) { deltaVector.normalize(); this._velocity.addScaledVector(deltaVector, -deltaVector.dot(this._velocity)); } else { this._velocity.set(0, 0, 0); } if (this._resetRequiredCallback()) { this.reset(); } } setForward(state) { this._pressed.forward = state; } setBackward(state) { this._pressed.backward = state; } setLeft(state) { this._pressed.left = state; } setRight(state) { this._pressed.right = state; } jump() { if (this._onGround && this.jumpAllowed) { this._velocity.y = this.jumpStrength; } } setRun(state) { if (state) { if (this._onGround && this.runAllowed) { this._running = true; } } else { this._running = false; } } running() { return this._running; } }