diff --git a/src/components/InteractiveObjects/InteractiveObject.js b/src/components/InteractiveObjects/InteractiveObject.js index 25f946e..a70fbfe 100644 --- a/src/components/InteractiveObjects/InteractiveObject.js +++ b/src/components/InteractiveObjects/InteractiveObject.js @@ -1,4 +1,5 @@ -import { Group, EventDispatcher, MeshStandardMaterial, Mesh, SphereGeometry } from "three"; +import { Group, MeshStandardMaterial, Mesh, SphereGeometry, Vector3 } from "three"; +import { EventManager } from '@/lib/EventManager'; import { GenericObject } from "./GenenricObject"; import { TextObject } from "./TextObject"; @@ -15,16 +16,17 @@ import { ClassicPuzzle } from "./ClassicPuzzle"; // import { Game6 } from "./games/Game6"; import { MazeQuizGame } from "./MazeQuizGame/MazeQuizGame"; import { Particles } from "./Particles"; -import { assignMaterial, assignParams, wrapInGroup, getBoundingBoxMaxLength } from "@/lib/MeshUtils"; -import { GameEngine } from "@/lib/GameEngine"; +import { SceneSwitcher } from "./SceneSwitcher"; +import { assignParams, wrapInGroup, getBoundingBoxMaxLength } from "@/lib/MeshUtils"; +import { GameEngine } from "@/lib/GameEngine"; const InteractiveObjectsImports = { - GenericObject, CharacterObject, TextObject, ImageObject, GltfObject, VideoPlayer, Particles, + GenericObject, CharacterObject, TextObject, ImageObject, GltfObject, VideoPlayer, Particles, SceneSwitcher, PuzzleGame1, PuzzleGame2, PuzzleGame4, MazeQuizGame, ClassicPuzzle }; -class InteractiveObject extends EventDispatcher{ - constructor(gameEngine, obj) { +class InteractiveObject extends EventManager{ + constructor(engine, obj) { super(); this.name = obj.name; return new Promise(async (resolve, reject) => { @@ -32,7 +34,7 @@ class InteractiveObject extends EventDispatcher{ case 'Group': this.object = new Group(); for (let g of obj.group){ - let gameMesh = await new InteractiveObject(gameEngine, g); + let gameMesh = await new InteractiveObject(engine, g); this.object.add(gameMesh.object); } break; @@ -47,38 +49,72 @@ class InteractiveObject extends EventDispatcher{ case 'MazeQuizGame': case 'ClassicPuzzle': case 'Particles': - this.io = await new InteractiveObjectsImports[obj.type](gameEngine, obj); + case 'SceneSwitcher': + this.io = await new InteractiveObjectsImports[obj.type](engine, obj); this.source = this.io.source || this.io; this.object = this.io.object; this.emits = this.io.emits; - this.io.emits?.forEach(event=>{ - this.io.addEventListener?.(event, this.dispatchEvent.bind(this)) - }) + // this.io.emits?.forEach(event=>{ + // this.io.addEventListener?.(event, this.dispatchEvent.bind(this)) + // }) + this.io.forwardEvents?.(this); break; } if (obj.shouldBeLocked){ this.object = wrapInGroup(this.object) - this.locker = new Locker(gameEngine, this.object); - this.locker.lock(); + this.activator = new (obj.activationType == 'unlock' ? LockActivator : VisibilityActivator)(engine, this.object); + this.activator.deactivate(); } assignParams(this.object, obj); if (obj.motion){ - gameEngine.motionQueue.add({ + engine.motionQueue.add({ o: this.object, ...obj.motion }); } + + if (obj.distance) { + const o = this.object; + let dstm = obj.distance; + let v = new Vector3(); + o.visible = false; + engine.addEventListener('beforeRender', function () { + o.getWorldPosition(v); + var dst = engine.camera.position.distanceTo(v); + if (dst <= dstm && !o.visible) { + o.visible = true; + }else if (dst > dstm && o.visible){ + o.visible = false; + } + }); + } resolve(this); }); } } -class Locker{ +class VisibilityActivator{ + constructor(engine, group){ + this.deactivate = function(){ + group.visible = false; + group.__active = false; + } + this.activate = function(){ + group.visible = true; + group.__active = true; + } + this.isActive = function(){ + return group.__active; + } + } +} + +class LockActivator{ static materialLocked = new MeshStandardMaterial({ transparent: true, opacity:1, color: 0xaaaaaa }) constructor(engine, group){ const bckGeometry = new SphereGeometry(getBoundingBoxMaxLength(group.userData.bbox)/2,8,8) - const bckMesh = new Mesh(bckGeometry, Locker.materialLocked); + const bckMesh = new Mesh(bckGeometry, LockActivator.materialLocked); bckMesh.visible = false; group.add(bckMesh) function animate(){ @@ -91,24 +127,29 @@ class Locker{ ) } this.object = bckMesh; - this.lock = function(){ + this.deactivate = function(){ bckMesh.visible = true; //bckMesh.material.needsUpdate = true; engine.motionQueue.clear(bckMesh); - group.__locked = true; + group.__active = false; animate(); } - this.unlock = function(){ + this.activate = function(){ bckMesh.visible = false; engine.motionQueue.clear(bckMesh); - group.__locked = false; + group.__active = true; + } + this.isActive = function(){ + return group.__active; } } } -GameEngine.loadTexture('locked.webp', '/static/textures/', null, [Locker.materialLocked, 'alphaMap']) +GameEngine.loadTexture('locked.webp', '/static/textures/', null, [LockActivator.materialLocked, 'alphaMap']) const InteractiveObjectTypes = [ { + id: 'GenericObject', name: 'Generic Game Object' + },{ id: 'CharacterObject', name: 'Character' }, { id: 'PuzzleGame1', name: 'Puzzle Game 1' @@ -122,6 +163,8 @@ const InteractiveObjectTypes = [ id: 'VideoPlayer', name: 'Video Player' },{ id: 'Particles', name: 'Particles' + },{ + id: 'SceneSwitcher', name: 'Scene Switcher' } ]; diff --git a/src/components/InteractiveObjects/MazeQuizGame/MazeObject.js b/src/components/InteractiveObjects/MazeQuizGame/MazeObject.js index 795a5a8..50dad0e 100644 --- a/src/components/InteractiveObjects/MazeQuizGame/MazeObject.js +++ b/src/components/InteractiveObjects/MazeQuizGame/MazeObject.js @@ -126,6 +126,7 @@ class MazeObject { go.object.scale.multiplyScalar(wallSize) go.object.position.multiplyScalar(wallSize) go.object.applyMatrix4(def.matrix); + go.forwardEvents?.(params.io); root.add(go.object); }); diff --git a/src/components/InteractiveObjects/MazeQuizGame/MazeQuizGame.js b/src/components/InteractiveObjects/MazeQuizGame/MazeQuizGame.js index bd89212..38b41c7 100644 --- a/src/components/InteractiveObjects/MazeQuizGame/MazeQuizGame.js +++ b/src/components/InteractiveObjects/MazeQuizGame/MazeQuizGame.js @@ -1,6 +1,6 @@ import { MazeObject } from "./MazeObject"; import Utils from "@/lib/Utils"; -import { EventDispatcher } from "three"; +import { EventManager } from '@/lib/EventManager'; const params = { scale: 3, @@ -17,7 +17,7 @@ const imgParams = { } const textParams = { - type: 'TextObject', width:0.44, + type: 'TextObject', maxWidth:0.44, fontPath:'/static/fonts/Montserrat-Regular.ttf', fontSize:0.025, distance: params.wallSize*2 } @@ -37,12 +37,14 @@ const defaults = { const tl = 4; -class MazeQuizGame extends EventDispatcher { - emits = ['finish'] +class MazeQuizGame extends EventManager { + emits = ['finish', 'sceneSwitch'] constructor(engine, data) { super(); + this.data = data; data.noPhysics = true; params.mazeFile = data.style || 'quiz-s2.gltf'; + params.io = this; return new Promise(async (resolve, reject)=>{ let questions = data.shuffle ? Utils.shuffleArray(data.questions) : data.questions; let def = this.generate(questions); @@ -101,11 +103,16 @@ class MazeQuizGame extends EventDispatcher { len, userData: { finish: true }, objects:[ + // { + // type: 'GltfObject', + // position:[0,.22,len + .52], scale: [0.33, 0.33, 0.33], rotation: [0, Math.PI/4, 0], + // value: 'trophy.glb', path: imgParams.path, distance: params.wallSize*2, + // motion: { a:{rotation: { y: k=>k*Math.PI*2 }}, r: true, t: 4 } + // } { - type: 'GltfObject', - position:[0,.22,len + .52], scale: [0.33, 0.33, 0.33], rotation: [0, Math.PI/4, 0], - value: 'trophy.glb', path: imgParams.path, distance: params.wallSize*2, - motion: { a:{rotation: { y: k=>k*Math.PI*2 }}, r: true, t: 4 } + type: 'SceneSwitcher', switchScene: this.data.switchScene, switchType: this.data.switchType, + position:[0,.22,len + .52], scale: [0.33, 0.33, 0.33], + distance: params.wallSize*2 } ] }; @@ -116,7 +123,7 @@ class MazeQuizGame extends EventDispatcher { len, userData: { question, qid }, objects:[ { - ...textParams, text: question.q, fontSize:0.033, width:0.55, position:[0,.55,len + .96], rotation:[0,Math.PI, 0] + ...textParams, text: question.q, fontSize:0.033, maxWidth:0.55, position:[0,.55,len + .96], rotation:[0,Math.PI, 0] } ] } @@ -149,7 +156,7 @@ class MazeQuizGame extends EventDispatcher { len: 3, objects:[ { - ...textParams, color:0xc71414, text: question.h, fontSize:0.033, width:0.66, position:[0,.44,3+.96], rotation:[0,Math.PI, 0] + ...textParams, color:0xc71414, text: question.h, fontSize:0.033, maxWidth:0.66, position:[0,.44,3+.96], rotation:[0,Math.PI, 0] },{ ...imgParams, value:'x.webp', position:[0,.33,3+.96], rotation:[0,Math.PI, 0] } diff --git a/src/components/InteractiveObjects/MazeQuizGame/MazeQuizGame.vue b/src/components/InteractiveObjects/MazeQuizGame/MazeQuizGame.vue index 3ace2c1..8055afa 100644 --- a/src/components/InteractiveObjects/MazeQuizGame/MazeQuizGame.vue +++ b/src/components/InteractiveObjects/MazeQuizGame/MazeQuizGame.vue @@ -39,10 +39,13 @@ - + + \ No newline at end of file diff --git a/src/components/SceneDesigner/GameObject.vue b/src/components/SceneDesigner/GameObject.vue index 965ff05..d06e32a 100644 --- a/src/components/SceneDesigner/GameObject.vue +++ b/src/components/SceneDesigner/GameObject.vue @@ -16,12 +16,14 @@ + + + - - - + @@ -35,10 +37,10 @@ + -