finish events
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { Group, Vector3, Matrix4, Mesh, Quaternion, PlaneGeometry, MeshStandardMaterial, DoubleSide } from 'three';
|
||||
import { InteractiveObject } from '../InteractiveObject';
|
||||
import { ActiveEvents } from '@dimforge/rapier3d';
|
||||
|
||||
class MazeObject {
|
||||
constructor(engine, def, params = {}){
|
||||
@@ -32,16 +33,21 @@ class MazeObject {
|
||||
|
||||
let o = {};
|
||||
|
||||
function addPhysics(matrix, position, size, placement = 'side'){
|
||||
function addPhysics(matrix, position, size, placement = 'side', isSensor = false, userData){
|
||||
let quat = new Quaternion().setFromRotationMatrix(matrix);
|
||||
let v = new Vector3(...position).applyMatrix4(matrix);
|
||||
if (typeof size == 'number'){
|
||||
size = { width: 0.1, height:1, depth:size/2 }
|
||||
}
|
||||
let po = engine.phy.add(
|
||||
{position: v}, 'fixed', false, undefined, 'cuboid',{ width: 0.01, height:1, depth:size/2 }
|
||||
{position: v}, 'fixed', false, undefined, 'cuboid',
|
||||
{ ...size, isSensor, userData }
|
||||
)
|
||||
if (placement == 'front') {
|
||||
quat.multiply(new Quaternion(0, 0.7071068, 0, 0.7071068)) //rotate by 90deg
|
||||
}
|
||||
po.rigidBody.setRotation(quat, true)
|
||||
return po;
|
||||
}
|
||||
|
||||
function addRoom(elements, def, offsetZ){
|
||||
@@ -105,6 +111,9 @@ class MazeObject {
|
||||
// mazeMeshes.push(e);
|
||||
addRoom(['floor', 'wall', 'wall', 'door', 'wall'], def, -context.wallSize)
|
||||
}
|
||||
if (def.userData?.answer !== undefined){
|
||||
addPhysics(def.matrix, [0,0,0], context.wallSize, 'front', true, def.userData)
|
||||
}
|
||||
for (let i = 0; i < def.len; i++) {
|
||||
let t = o.tunnel.clone();
|
||||
t.position.set(0, 0, i * context.tubeSize);
|
||||
@@ -125,8 +134,10 @@ class MazeObject {
|
||||
addPhysics(def.matrix, [-context.tubeSize / 2, 0.6, offsetZ/2], offsetZ)
|
||||
|
||||
addRoom(['floor', 'door', def.r ? 'door' : 'wall', def.f ? 'door' : 'wall', def.l ? 'door' : 'wall'], def, offsetZ)
|
||||
|
||||
console.log('loadingggg', def.objects)
|
||||
if (def.userData?.qid !== undefined || def.userData?.finish){
|
||||
addPhysics(def.matrix, [0,0,offsetZ + context.wallSize/2], { width: context.wallSize/2, height: context.wallSize/2, depth: context.wallSize/2}, 'side', true, def.userData)
|
||||
}
|
||||
//console.log('loadingggg', def.objects)
|
||||
def.objects?.forEach(async obj => {
|
||||
obj.room = room;
|
||||
// let go = new GameObject(obj, context);
|
||||
@@ -172,7 +183,7 @@ class MazeObject {
|
||||
// //engine.phy.add(mesh, 'fixed')
|
||||
// })
|
||||
|
||||
console.log(bbox, 'bbox')
|
||||
//console.log(bbox, 'bbox')
|
||||
const floorGeometry = new PlaneGeometry(bbox.r - bbox.l + 10*scale, bbox.f + 10*scale);
|
||||
const floor = new Mesh(floorGeometry,new MeshStandardMaterial({
|
||||
roughness: 0, metalness:1, color: 0x00ffff, side: DoubleSide
|
||||
|
||||
@@ -20,6 +20,24 @@ class MazeQuizGame {
|
||||
constructor(engine, context, questions) {
|
||||
let def = this.generate(questions);
|
||||
this.mazeObject = new MazeObject(engine, def)
|
||||
engine.addEventListener('collision', async e=>{
|
||||
let ud = engine.phy.world.getCollider(e.handle2)?.parent()?.userData;
|
||||
if (ud?.finish && engine.hero?.animationsMap?.win){
|
||||
if (e.started){
|
||||
engine.hero.animationsMap._idle = engine.hero.animationsMap.idle
|
||||
engine.hero.animationsMap.idle = engine.hero.animationsMap.win
|
||||
engine.hero.characterControls.cameraDelta = Math.PI;
|
||||
await Utils.wait(1000);
|
||||
engine.hero.characterControls.direction += Math.PI;
|
||||
await Utils.wait(10000);
|
||||
this.onfinish?.()
|
||||
}else{
|
||||
engine.hero.animationsMap.idle = engine.hero.animationsMap._idle
|
||||
engine.hero.characterControls.cameraDelta = 0
|
||||
}
|
||||
}
|
||||
//console.log(e, ud, engine.hero?.animationsMap);
|
||||
})
|
||||
}
|
||||
|
||||
async load(){
|
||||
@@ -32,7 +50,7 @@ class MazeQuizGame {
|
||||
let question = questions[qid]
|
||||
if (!question) return {
|
||||
len:3,
|
||||
finish: true,
|
||||
userData: { finish: true },
|
||||
objects:[
|
||||
{
|
||||
type: 'gltf',
|
||||
@@ -52,7 +70,7 @@ class MazeQuizGame {
|
||||
let directions = Utils.shuffleArray( ['l', 'r', 'f'] )
|
||||
|
||||
let mo = {
|
||||
len, question, qid,
|
||||
len, userData: { question, qid },
|
||||
objects:[
|
||||
{
|
||||
type: 'text', text: question.q, fontSize:0.033, width:0.5, position:[0,.33,len + .96], rotation:[0,Math.PI, 0]
|
||||
@@ -74,11 +92,13 @@ class MazeQuizGame {
|
||||
}
|
||||
if (i == 0){
|
||||
mo[d] = {
|
||||
len: 3,
|
||||
answer: i,
|
||||
len: 4,
|
||||
[dd]: this.generate(questions, qid + 1, 3)
|
||||
}
|
||||
}else{
|
||||
mo[d] = {
|
||||
userData: { question, qid, answer: i },
|
||||
len: 4,
|
||||
[dd]: {
|
||||
len: 2,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as THREE from 'three'
|
||||
|
||||
export const CONTROLLER_BODY_RADIUS = 0.28;
|
||||
import { QueryFilterFlags } from '@dimforge/rapier3d';
|
||||
|
||||
export class CharacterControls {
|
||||
|
||||
@@ -33,7 +32,7 @@ export class CharacterControls {
|
||||
// // 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.setApplyImpulsesToDynamicBodies(true);
|
||||
// this.characterController.setCharacterMass(50);
|
||||
this.po = po;
|
||||
this.pointerControls = pointerControls
|
||||
@@ -47,20 +46,14 @@ export class CharacterControls {
|
||||
this.actionStart = 0;
|
||||
this.cameraDelta = 0;
|
||||
this.cameraIdleDelta = 0;
|
||||
//this.toggleRun = true
|
||||
}
|
||||
|
||||
switchRunToggle() {
|
||||
this.toggleRun = !this.toggleRun
|
||||
}
|
||||
|
||||
update(world, delta, pointerControls) {
|
||||
const directionPressed = pointerControls.moving
|
||||
let input = this.getInput(pointerControls)
|
||||
|
||||
let play = this.currentAction || 'idle', velocity = this.walkVelocity;
|
||||
this.fadeDuration = 0.2;
|
||||
if (input[1] && this.toggleRun) {
|
||||
if (input[1] && pointerControls.running) {
|
||||
play = 'run';
|
||||
velocity = this.runVelocity
|
||||
} else if (input[1] > 0) {
|
||||
@@ -111,23 +104,13 @@ export class CharacterControls {
|
||||
|
||||
this.walkDirection.x = this.walkDirection.y = this.walkDirection.z = 0
|
||||
|
||||
if (directionPressed) {
|
||||
if (pointerControls.motion) {
|
||||
this.directionVelocity = this.directionVelocity * 2.5 * Math.abs(input[0])
|
||||
this.direction += input[0] * delta * 2.5 //this.directionVelocity;
|
||||
this.model.rotation.y = this.direction;
|
||||
this.walkDirection.set(0, 0, input[1])
|
||||
this.walkDirection.applyAxisAngle(this.rotateAngle, this.direction)
|
||||
this.walkDirection.normalize();
|
||||
|
||||
|
||||
//let dst = Math.sqrt(Math.pow(cameraPosition.x - this.model.position.x, 2) + Math.pow(cameraPosition.z - this.model.position.z, 2));
|
||||
//cameraPosition.y = 8 - dst;
|
||||
//if (dst >0.52){
|
||||
//}
|
||||
//this.camera.zoom = dst
|
||||
//this.camera.updateProjectionMatrix();
|
||||
|
||||
// run/walk velocity
|
||||
}
|
||||
|
||||
this.walkDirection.x = this.walkDirection.x * velocity * delta// + this.model.position.x
|
||||
@@ -138,8 +121,15 @@ export class CharacterControls {
|
||||
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.
|
||||
QueryFilterFlags['EXCLUDE_SENSORS']
|
||||
);
|
||||
|
||||
// for (let i = 0; i < this.characterController.numComputedCollisions(); i++) {
|
||||
// let collision = this.characterController.computedCollision(i);
|
||||
// //console.log(collision)
|
||||
// // Do something with that collision information.
|
||||
// }
|
||||
|
||||
let correctedMovement = this.characterController.computedMovement();
|
||||
|
||||
let v = new THREE.Vector3();
|
||||
|
||||
+2
-32
@@ -17,7 +17,7 @@ import { XRControllerModelFactory } from 'three/addons/webxr/XRControllerModelFa
|
||||
import { Physics } from './Physics.js';
|
||||
import { Clickable } from './Clickable.js';
|
||||
|
||||
class GameEngine {
|
||||
class GameEngine extends THREE.EventDispatcher{
|
||||
async init(domNode, opts = {}) {
|
||||
this.w = domNode.clientWidth || 1200, this.h = domNode.clientHeight || 800;
|
||||
this.aspect = this.w / this.h
|
||||
@@ -251,37 +251,7 @@ class GameEngine {
|
||||
}
|
||||
|
||||
async initPhysics() {
|
||||
//const world = new CANNON.World()
|
||||
//world.gravity.set(0, -9.82, 0)
|
||||
// let gravity = { x: 0.0, y: -9.81, z: 0.0 };
|
||||
// let world = new RAPIER.World(gravity);
|
||||
|
||||
// // Create Ground.
|
||||
// let bodyDesc = RAPIER.RigidBodyDesc.fixed();
|
||||
// let body = world.createRigidBody(bodyDesc);
|
||||
// let colliderDesc = RAPIER.ColliderDesc.cuboid(100.0, 0.1, 100.0);
|
||||
// world.createCollider(colliderDesc, body.handle);
|
||||
|
||||
// const groundMaterial = new CANNON.Material('groundMaterial')
|
||||
// const slipperyMaterial = new CANNON.Material('slipperyMaterial')
|
||||
// const slippery_ground_cm = new CANNON.ContactMaterial(
|
||||
// groundMaterial,
|
||||
// slipperyMaterial,
|
||||
// {
|
||||
// friction: 0,
|
||||
// restitution: 0.3,
|
||||
// contactEquationStiffness: 1e10,
|
||||
// contactEquationRelaxation: 30,
|
||||
// }
|
||||
// )
|
||||
// world.addContactMaterial(slippery_ground_cm)
|
||||
// const planeShape = new CANNON.Plane()
|
||||
// const planeBody = new CANNON.Body({ mass: 0, material: groundMaterial })
|
||||
// planeBody.addShape(planeShape)
|
||||
// planeBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2)
|
||||
// world.addBody(planeBody)
|
||||
|
||||
this.phy = new Physics();
|
||||
this.phy = new Physics(this);
|
||||
await this.phy.init();
|
||||
}
|
||||
|
||||
|
||||
+8
-8
@@ -2,7 +2,6 @@ import { AnimationMixer } from 'three';
|
||||
import { PointerControls } from './PointerControls';
|
||||
import { CharacterControls } from './CharacterControls';
|
||||
import * as THREE from 'three';
|
||||
import * as RAPIER from '@dimforge/rapier3d'
|
||||
|
||||
class Hero{
|
||||
|
||||
@@ -18,9 +17,9 @@ class Hero{
|
||||
gameEngine.camera.position.set(0,17,-30)
|
||||
gameEngine.camera.lookAt(new THREE.Vector3(this.model.position.x, 5, this.model.position.z))
|
||||
|
||||
this.heroCamera = new THREE.Object3D()
|
||||
this.model.add(this.heroCamera)
|
||||
this.heroCamera.applyMatrix4(gameEngine.camera.matrix)
|
||||
// this.heroCamera = new THREE.Object3D()
|
||||
// this.model.add(this.heroCamera)
|
||||
// this.heroCamera.applyMatrix4(gameEngine.camera.matrix)
|
||||
|
||||
this.mixer = new AnimationMixer(this.model);
|
||||
gameEngine.mixers.push( this.mixer );
|
||||
@@ -35,6 +34,7 @@ class Hero{
|
||||
|
||||
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 });
|
||||
//po.collider.setActiveEvents(RAPIER.ActiveEvents.COLLISION_EVENTS);
|
||||
|
||||
this.characterControls = new CharacterControls(this.model, this.mixer,
|
||||
this.animationsMap, gameEngine, 'idle', po, this.pointerControls)
|
||||
@@ -53,16 +53,16 @@ class Hero{
|
||||
//return
|
||||
if (this.gameEngine.renderer.xr.isPresenting) return;
|
||||
|
||||
if (this.ready) {
|
||||
if (this.ready && !this.disableInput) {
|
||||
let pc = this.pointerControls;
|
||||
pc.update();
|
||||
let dlt = this.clock.getDelta();
|
||||
this.delta += dlt;
|
||||
if (this.delta > 0.00001){
|
||||
this.gameEngine.phy.step()
|
||||
//if (this.delta > 0.00001){
|
||||
this.characterControls.update(this.gameEngine.phy.world, this.delta, pc)
|
||||
this.gameEngine.phy.step()
|
||||
this.delta = 0;
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+19
-12
@@ -1,16 +1,7 @@
|
||||
import * as RAPIER from '@dimforge/rapier3d'
|
||||
|
||||
// export type PhysicsObject = {
|
||||
// mesh: THREE.Mesh
|
||||
// collider: Rapier.Collider
|
||||
// rigidBody: Rapier.RigidBody
|
||||
// fn?: Function
|
||||
// autoAnimate: boolean
|
||||
// }
|
||||
|
||||
class Physics{
|
||||
constructor(){
|
||||
|
||||
constructor(engine){
|
||||
this.engine = engine
|
||||
}
|
||||
|
||||
init = async ()=>{
|
||||
@@ -25,6 +16,7 @@ class Physics{
|
||||
world.createCollider(colliderDesc, body.handle);
|
||||
this.world = world;
|
||||
this.physicsObjects = [];
|
||||
this.eventQueue = new RAPIER.EventQueue(true);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -34,6 +26,7 @@ class Physics{
|
||||
|
||||
// * Responsible for collision response
|
||||
const rigidBody = this.world.createRigidBody(rigidBodyDesc)
|
||||
rigidBody.userData = colliderSettings.userData || {};
|
||||
|
||||
let colliderDesc
|
||||
|
||||
@@ -73,6 +66,12 @@ class Physics{
|
||||
console.error('Collider Mesh Error: convex mesh creation failed.')
|
||||
}
|
||||
|
||||
if (colliderSettings.isSensor){
|
||||
colliderDesc.setSensor(true);
|
||||
colliderDesc.setActiveEvents(RAPIER.ActiveEvents.COLLISION_EVENTS);
|
||||
colliderDesc.setActiveCollisionTypes(RAPIER.ActiveCollisionTypes.KINEMATIC_FIXED);
|
||||
}
|
||||
|
||||
// * Responsible for collision detection
|
||||
const collider = this.world.createCollider(colliderDesc, rigidBody)
|
||||
|
||||
@@ -84,7 +83,7 @@ class Physics{
|
||||
}
|
||||
|
||||
step(){
|
||||
this.world.step()
|
||||
this.world.step(this.eventQueue)
|
||||
|
||||
for (let po of this.physicsObjects) {
|
||||
const autoAnimate = po.autoAnimate
|
||||
@@ -99,6 +98,14 @@ class Physics{
|
||||
const fn = po.fn
|
||||
fn && fn()
|
||||
}
|
||||
|
||||
this.eventQueue.drainCollisionEvents((handle1, handle2, started) => {
|
||||
/* Handle the collision event. */
|
||||
//console.log(this.world.getCollider(handle2), handle1, started)
|
||||
this.engine.dispatchEvent({
|
||||
type: 'collision', handle1, handle2, started
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -139,7 +139,19 @@ class PointerControls {
|
||||
}
|
||||
|
||||
get moving(){
|
||||
return this.moveForward || this.moveBackward || this.moveLeft || this.moveRight;
|
||||
return this.moveForward || this.moveBackward;
|
||||
}
|
||||
|
||||
get rotating(){
|
||||
return this.rotateLeft || this.rotateRight;
|
||||
}
|
||||
|
||||
get motion(){
|
||||
return this.moving || this.rotating;
|
||||
}
|
||||
|
||||
get running(){
|
||||
return this.moving && this.kb.ShiftLeft;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,5 +58,17 @@ export default {
|
||||
shuffleArray(arr){
|
||||
return arr.map(value => ({ value, sort: Math.random() }))
|
||||
.sort((a, b) => a.sort - b.sort).map(({ value }) => value)
|
||||
},
|
||||
|
||||
async wait(ms){
|
||||
await new Promise((resolve, reject)=>{
|
||||
setTimeout(resolve, ms)
|
||||
})
|
||||
},
|
||||
|
||||
async waitFor(expFn){
|
||||
while (!expFn()){
|
||||
await JsUtils.wait(200);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user