From 0018d513eba15bb69ed7bd05257d7c271834f7b6 Mon Sep 17 00:00:00 2001 From: goynov Date: Fri, 17 Oct 2025 11:55:39 +0300 Subject: [PATCH] intermediate --- package-lock.json | 8 +- package.json | 2 +- public/static/meshes/maze-reed.bin | Bin 2800 -> 2800 bytes public/static/meshes/maze-reed.gltf | 15 +- .../MazeQuizGame/MazeObject.js | 36 +++- src/lib/CharacterControls.js | 195 +++++++++++++----- src/lib/Hero.js | 94 +-------- src/lib/Physics.js | 3 +- 8 files changed, 195 insertions(+), 158 deletions(-) diff --git a/package-lock.json b/package-lock.json index c1bc719..f46c140 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,7 +31,7 @@ "vuetify": "^3.10.5" }, "devDependencies": { - "@dimforge/rapier3d": "^0.18.2", + "@dimforge/rapier3d": "^0.19.1", "@vitejs/plugin-vue": "^5.0.5", "eslint": "^8.57.0", "eslint-config-standard": "^17.1.0", @@ -283,9 +283,9 @@ } }, "node_modules/@dimforge/rapier3d": { - "version": "0.18.2", - "resolved": "https://registry.npmjs.org/@dimforge/rapier3d/-/rapier3d-0.18.2.tgz", - "integrity": "sha512-VM6f6L/1BVhvglTM67GlHmlI6Bw9NWcXzlLGoiZ7H1dOb86c6HJ9vIa6WDHThAfgN5B0c2oLeTAQ592BDPDzgA==", + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@dimforge/rapier3d/-/rapier3d-0.19.1.tgz", + "integrity": "sha512-pvAJXeeMtEif6OlmS4f+gJ5pj3qnLHTkJaIqiVhqDnqJPanwSkXd4DU12x4BcBPXc16pABr+cA7j17Cj86x1kw==", "dev": true, "license": "Apache-2.0" }, diff --git a/package.json b/package.json index 6de653e..bc285a1 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "vuetify": "^3.10.5" }, "devDependencies": { - "@dimforge/rapier3d": "^0.18.2", + "@dimforge/rapier3d": "^0.19.1", "@vitejs/plugin-vue": "^5.0.5", "eslint": "^8.57.0", "eslint-config-standard": "^17.1.0", diff --git a/public/static/meshes/maze-reed.bin b/public/static/meshes/maze-reed.bin index 3db612fb2714c66f01b769e2aa2c5bd3c995b5e0..274fa6827aec7372f3e122e0df52631b7389c215 100644 GIT binary patch delta 154 zcmew$`ayI<0B8O63;y<8vn1`i_et)rVsf+rv28X=?gxo$&y?H`6Gvu))T}6xw1=rj kRs$0UsgFvRw1=4w69?&ygo*>z@a~66fHZ8r!zscD0H=C4!vFvP delta 154 zcmew$`ayI<0B3#PG6Va>vuEtO_et(=5KyrJv28X=?gxomoH?@(CXUPosaa7XX%AD6 ktOh0yQXiErX%90WCJxdY2^9yb;oS$50BP8Khf{ { @@ -148,7 +166,7 @@ class MazeObject { mazeMeshes.forEach(mesh=>{ //let mesh = new Mesh(mg, o.tunnel.material) root.add(mesh); - engine.phy.add(mesh, 'dynamic') + //engine.phy.add(mesh, 'fixed') }) console.log(o.tunnel) diff --git a/src/lib/CharacterControls.js b/src/lib/CharacterControls.js index d71df03..2e5e9b7 100644 --- a/src/lib/CharacterControls.js +++ b/src/lib/CharacterControls.js @@ -17,27 +17,43 @@ export class CharacterControls { walkVelocity = 3 lerp = (x, y, a) => x * (1 - a) + y * a; - constructor(model, mixer, animationsMap, orbitControl, camera, currentAction, ray, rigidBody, pointerControls) { + constructor(model, mixer, animationsMap, engine, currentAction, po, pointerControls) { this.model = model this.mixer = mixer this.animationsMap = animationsMap this.currentAction = currentAction this.animationsMap[currentAction].play() - this.ray = ray - this.rigidBody = rigidBody + this.characterController = engine.phy.world.createCharacterController(0.1); + this.characterController.setUp({x:0, y:1, z:0}); + po.rigidBody.setTranslation(this.model.position) + // this.characterController.enableSnapToGround(0.5); + // // Don’t allow climbing slopes larger than 45 degrees. + // this.characterController.setMaxSlopeClimbAngle(45 * Math.PI / 180); + // // Automatically slide down on slopes smaller than 30 degrees. + // this.characterController.setMinSlopeSlideAngle(30 * Math.PI / 180); + // this.characterController.enableAutostep(0.5, 0.2, true); + // this.characterController.setApplyImpulsesToDynamicBodies(true); + // this.characterController.setCharacterMass(50); + this.po = po; this.pointerControls = pointerControls - this.orbitControl = orbitControl - this.camera = camera + this.orbitControl = engine.orbitControls + this.camera = engine.camera this.updateCameraTarget(new THREE.Vector3(0,1,5)) + + this._v = new THREE.Vector3(); + this.velocity = new THREE.Vector3(); + this.forceDirection = new THREE.Vector3(); + this.forceAcceleration = 1 / 32; + this.forceSpeedMax = 0.075; } switchRunToggle() { this.toggleRun = !this.toggleRun } - update(world, delta, pointerControls) { + update0(world, delta, pointerControls) { const directionPressed = pointerControls.moving() var play = ''; @@ -86,51 +102,74 @@ export class CharacterControls { velocity = this.currentAction == 'run' ? this.runVelocity : this.walkVelocity } - const translation = this.rigidBody.translation(); - if (translation.y < -1) { - // don't fall below ground - this.rigidBody.setNextKinematicTranslation( { - x: 0, - y: 10, - z: 0 - }); - } else { - const cameraPositionOffset = this.camera.position.sub(this.model.position); - // update model and camera - this.model.position.x = translation.x - this.model.position.y = translation.y - this.model.position.z = translation.z - this.updateCameraTarget(cameraPositionOffset) - - this.walkDirection.y += this.lerp(this.storedFall, -9.81 * delta, 0.10) - this.storedFall = this.walkDirection.y - this.ray.origin.x = translation.x - this.ray.origin.y = translation.y - this.ray.origin.z = translation.z - let hit = world.castRay(this.ray, 0.5, false, 0xfffffffff); - if (hit) { - const point = this.ray.pointAt(hit.timeOfImpact); - let diff = translation.y - ( point.y + CONTROLLER_BODY_RADIUS); - if (diff < 0.0) { - this.storedFall = 0 - this.walkDirection.y = this.lerp(0, -diff, 0.5) - } - } - - this.walkDirection.x = this.walkDirection.x * velocity * delta - this.walkDirection.z = this.walkDirection.z * velocity * delta + this.walkDirection.x = this.walkDirection.x * velocity * delta + this.model.position.x + this.walkDirection.z = this.walkDirection.z * velocity * delta + this.model.position.z - this.rigidBody.setNextKinematicTranslation( { - x: translation.x + this.walkDirection.x, - y: translation.y + this.walkDirection.y, - z: translation.z + this.walkDirection.z - }); - } + //const translation = this.rigidBody.translation(); + //const translation = characterController.computedMovement(); + + this.characterController.computeColliderMovement( + this.po.collider, // The collider we would like to move. + this.walkDirection, // The movement we would like to apply if there wasn’t any obstacle. + ); + + // for (let i = 0; i < this.characterController.numComputedCollisions(); i++) { + // let collision = this.characterController.computedCollision(i); + // console.log('c', collision) + // // Do something with that collision information. + // } + + let correctedMovement = this.characterController.computedMovement(); + + this.po.rigidBody.setNextKinematicTranslation(correctedMovement); + //this.po.rigidBody.setNextKinematicRotation(this.rotateQuarternion); + + // if (translation.y < -1) { + // // don't fall below ground + // this.rigidBody.setNextKinematicTranslation( { + // x: 0, + // y: 10, + // z: 0 + // }); + // } else { + const cameraPositionOffset = this.camera.position.sub(this.model.position); + + this.model.position.copy(correctedMovement) + // // update model and camera + // this.model.position.x = translation.x + // this.model.position.y = translation.y + // this.model.position.z = translation.z + this.updateCameraTarget(cameraPositionOffset, correctedMovement) + + // this.walkDirection.y += this.lerp(this.storedFall, -9.81 * delta, 0.10) + // this.storedFall = this.walkDirection.y + // this.ray.origin.x = translation.x + // this.ray.origin.y = translation.y + // this.ray.origin.z = translation.z + // let hit = world.castRay(this.ray, 0.5, false, 0xfffffffff); + // if (hit) { + // const point = this.ray.pointAt(hit.timeOfImpact); + // let diff = translation.y - ( point.y + CONTROLLER_BODY_RADIUS); + // if (diff < 0.0) { + // this.storedFall = 0 + // this.walkDirection.y = this.lerp(0, -diff, 0.5) + // } + // } + + // this.walkDirection.x = this.walkDirection.x * velocity * delta + // this.walkDirection.z = this.walkDirection.z * velocity * delta + + // this.rigidBody.setNextKinematicTranslation( { + // x: translation.x + this.walkDirection.x, + // y: translation.y + this.walkDirection.y, + // z: translation.z + this.walkDirection.z + // }); + // } } - updateCameraTarget(offset) { + updateCameraTarget(offset, cm) { // move camera - const rigidTranslation = this.rigidBody.translation(); + const rigidTranslation = cm || this.po.rigidBody.translation(); this.camera.position.x = rigidTranslation.x + offset.x this.camera.position.y = rigidTranslation.y + offset.y this.camera.position.z = rigidTranslation.z + offset.z @@ -168,4 +207,66 @@ export class CharacterControls { return directionOffset } + update (world, delta, pointerControls){ + // Calculate input buffer + // if (this.jumpBuffer > 0) { + // this.jumpBuffer -= loop.delta; // ms + + // // Automatically jump if buffer is set + // if (this.allowJump === true) { + // this.jumpBuffer = 0; + // this.jump(); + // } + // } + + // Add fake friction and fake gravity + this.velocity.x *= 0.75; + this.velocity.z *= 0.75; + this.velocity.y -= 0.005; + + // Update force direction from user input + let xDirection = 0; + let zDirection = 0; + if (pointerControls.moveForward) zDirection = -1; + if (pointerControls.moveBackward) zDirection = 1; + if (pointerControls.moveRight) xDirection = 1; + if (pointerControls.moveLeft) xDirection = -1; + + // Set the new force direction + this.forceDirection.copy({ x: xDirection, y: 0, z: zDirection }); // Ex: -1.0 to 1.0 + + // Decrease acceleration if the velocity speed equals the force speed + this._v.copy(this.velocity); + const speed = this._v.dot(this.forceDirection); + const speedNext = speed + this.forceAcceleration; + const speedClamped = Math.max(speed, Math.min(speedNext, this.forceSpeedMax)); + const acceleration = speedClamped - speed; // Ex: 0.5 (or 0 at max speed) + + // Add force to velocity using new acceleration + this.velocity.x += this.forceDirection.x * acceleration; + this.velocity.y += this.forceDirection.y * acceleration; + this.velocity.z += this.forceDirection.z * acceleration; + + // Set the next kinematic translation + if (this.po.rigidBody.numColliders() > 0) { + this.characterController.computeColliderMovement(this.po.collider, this.velocity); + this._v.copy(this.po.rigidBody.translation()); + this._v.add(this.characterController.computedMovement()); + this.po.rigidBody.setNextKinematicTranslation(this._v); + } + + // Calculate 3D object rotation from character translation + this._v.copy(this.po.rigidBody.nextTranslation()); + if (this._v.distanceTo(this.po.rigidBody.translation()) > 0.01) { + this.model.lookAt(this._v.x, this.model.position.y, this._v.z); + this.po.rigidBody.setNextKinematicRotation(this.model.quaternion); + } + + // Set vertical velocity to zero if grounded + // if (this.characterController.computedGrounded()) { + // this.allowJump = true; + // this.entity.velocity.y = 0; + // } + } + } \ No newline at end of file diff --git a/src/lib/Hero.js b/src/lib/Hero.js index 1b0a8f0..32b58c7 100644 --- a/src/lib/Hero.js +++ b/src/lib/Hero.js @@ -15,12 +15,6 @@ class Hero{ init(gameEngine){ this.gameEngine = gameEngine; - //this.gameEngine.orbitControls.object = this.model; - //this.gameEngine.orbitControls.target = this.model.position; - - //this.gameEngine.orbitControls.enabled = false; - - //this.model.add(gameEngine.camera) gameEngine.camera.position.set(0,17,-30) gameEngine.camera.lookAt(new THREE.Vector3(this.model.position.x, 5, this.model.position.z)) @@ -36,84 +30,17 @@ class Hero{ this.animationsMap[a.name] = this.mixer.clipAction(a); }) - // this.actionWalk = this.mixer.clipAction( this.object.animations.find(a=>a.name=='walk') ); - // this.actionIdle = this.mixer.clipAction( this.object.animations.find(a=>a.name=='idle') ); - // this.actionIdle.play(); - // this.activeAction = this.actionIdle; this.pointerControls = new PointerControls(gameEngine.camera, this.model, gameEngine.renderer.domElement); gameEngine.hero = this; - - // Character Collider - // const characterCollider = new THREE.Object3D() - // characterCollider.position.y = 3 - // gameEngine.activeObjects.add(characterCollider) - - // const colliderShape = new CANNON.Sphere(0.5) - // const colliderBody = new CANNON.Body({ mass: 1, material: gameEngine.phy.slipperyMaterial }) - // colliderBody.addShape(colliderShape, new CANNON.Vec3(0, 0.5, 0)) - // colliderBody.addShape(colliderShape, new CANNON.Vec3(0, -0.5, 0)) - // colliderBody.position.set( - // characterCollider.position.x, - // characterCollider.position.y, - // characterCollider.position.z - // ) - // colliderBody.linearDamping = 0.95 - // colliderBody.angularFactor.set(0, 1, 0) // prevents rotation X,Z axis - // //colliderBody.sleepSpeedLimit = 1.0; - // gameEngine.phy.world.addBody(colliderBody) - - let po = gameEngine.phy.add(this.model, 'kinematicPositionBased', false, undefined, 'ball', { radius: 0.28}) - - // let bodyDesc = RAPIER.RigidBodyDesc.kinematicPositionBased().setTranslation( - // // characterCollider.position.x, - // // characterCollider.position.y, - // // characterCollider.position.z - // -1, 3, 1 - // ) - // let rigidBody = gameEngine.phy.world.createRigidBody(bodyDesc); - // let dynamicCollider = RAPIER.ColliderDesc.ball(0.28); - // gameEngine.phy.world.createCollider(dynamicCollider, rigidBody.handle); + let po = gameEngine.phy.add(this.model, 'kinematicPositionBased', false, undefined, 'capsule', { radius: 0.5, halfHeight: 1}) + po.collider.setTranslationWrtParent({ x: 0, y: 2.0, z: 0 }); this.characterControls = new CharacterControls(this.model, this.mixer, - this.animationsMap, gameEngine.orbitControls, gameEngine.camera, 'idle', - new RAPIER.Ray( - { x: 0, y: 0, z: 0 }, - { x: 0, y: -1, z: 0} - ), po.rigidBody, this.pointerControls) - - // this.characterCollider = characterCollider; - // this.colliderBody = colliderBody; + this.animationsMap, gameEngine, 'idle', po, this.pointerControls) this.clock = new THREE.Clock() this.delta = 0 - // this.inputVelocity = new THREE.Vector3() - // this.velocity = new CANNON.Vec3() - // this.quat = new THREE.Quaternion() - // this.v = new THREE.Vector3() - // this.rotation = new THREE.Euler() - // this.targetQuaternion = new THREE.Quaternion() - // this.distance = 0 - // this.canJump = true; - - // this.contactNormal = new CANNON.Vec3() - // this.upAxis = new CANNON.Vec3(0, 1, 0) - - // colliderBody.addEventListener('collide', function (e) { - // const contact = e.contact - // if (contact.bi.id == this.colliderBody.id) { - // contact.ni.negate(this.contactNormal) - // } else { - // this.contactNormal.copy(contact.ni) - // } - // if (this.contactNormal.dot(this.upAxis) > 0.5) { - // if (!this.canJump) { - // setAction(animationActions[1], true) - // } - // this.canJump = true - // } - // }.bind(this)) - this.ready = true; } @@ -122,21 +49,6 @@ class Hero{ this.pointerControls.controls.lock(); } - // setAction(toAction, loop){ - // if (toAction != this.activeAction) { - // let lastAction = this.activeAction; - // this.activeAction = toAction - // lastAction.fadeOut(0.1) - // this.activeAction.reset() - // this.activeAction.fadeIn(0.1) - // this.activeAction.play() - // if (!loop) { - // this.activeAction.clampWhenFinished = true - // this.activeAction.loop = THREE.LoopOnce - // } - // } - // } - update(){ //return if (this.gameEngine.renderer.xr.isPresenting) return; diff --git a/src/lib/Physics.js b/src/lib/Physics.js index 95b0141..3fce5b1 100644 --- a/src/lib/Physics.js +++ b/src/lib/Physics.js @@ -20,7 +20,8 @@ class Physics{ // Create Ground. let bodyDesc = RAPIER.RigidBodyDesc.fixed(); let body = world.createRigidBody(bodyDesc); - let colliderDesc = RAPIER.ColliderDesc.cuboid(100.0, 0.1, 100.0); + let colliderDesc = RAPIER.ColliderDesc.cuboid(100, 0, 100); + //bodyDesc.setTranslation(-50,0,-50); world.createCollider(colliderDesc, body.handle); this.world = world; this.physicsObjects = [];