diff --git a/public/static/textures/play.webp b/public/static/textures/play.webp new file mode 100644 index 0000000..728443c Binary files /dev/null and b/public/static/textures/play.webp differ diff --git a/src/components/InteractiveObjects/VideoPlayer.js b/src/components/InteractiveObjects/VideoPlayer.js index 6e66465..b71df82 100644 --- a/src/components/InteractiveObjects/VideoPlayer.js +++ b/src/components/InteractiveObjects/VideoPlayer.js @@ -12,9 +12,9 @@ class VideoPlayer extends EventManager { return new Promise((resolve, reject)=>{ vi = document.createElement('video'); vi.src = engine.assetPath + data.$go.asset.name; - vi.addEventListener('loadedmetadata', ()=>{ + vi.addEventListener('loadedmetadata', async ()=>{ this.aspect = vi.videoWidth / vi.videoHeight; - let geometry = new PlaneGeometry( this.aspect, 1 ); + let geometry = new PlaneGeometry( this.aspect * 0.88, 0.88 ); let map = new VideoTexture( vi ); map.colorSpace = SRGBColorSpace; let material = new MeshBasicMaterial( { @@ -34,26 +34,38 @@ class VideoPlayer extends EventManager { } }); - vi.addEventListener('play', ()=>{ - material.opacity = 1 + const onPlay = ()=>{ + material.opacity = 1; if (data.playInHud && engine.dashboard?.active){ engine.dashboard.attach(plane, { skipTransition: data.skipTransition }); } - }) - vi.addEventListener('pause', ()=>{ - //material.opacity = 0.5; + } + + const onPause = ()=>{ if (data.playInHud){ engine.dashboard?.detach(plane, { skipTransition: data.skipTransition }); } - }) + } + + vi.addEventListener('play', onPlay); + vi.addEventListener('pause', onPause); + vi.addEventListener('ended', ()=>{ this.dispatchEvent({type:'finish'}) }) this.video = vi; + + this.play = function(){ + try { + vi.play(); + }catch(err){ + //TODO: show play button!!! + } + } resolve(this); }) diff --git a/src/lib/Dashboard.js b/src/lib/Dashboard.js index f8203f9..b90e8b1 100644 --- a/src/lib/Dashboard.js +++ b/src/lib/Dashboard.js @@ -1,24 +1,14 @@ import { - PlaneGeometry, CylinderGeometry, CanvasTexture, Group, + PlaneGeometry, CylinderGeometry, CanvasTexture, Group, SphereGeometry, Mesh, MeshStandardMaterial, MeshBasicMaterial, DoubleSide } from "three"; import { Text } from "troika-three-text"; -class DashBoard { +import { EventManager } from "./EventManager"; +class DashBoard extends EventManager { #points = 0; constructor(engine) { - let svg = p=>` - - - - - - - -${p.hint || ''} -Points -`; - + super(); let img = new Image(), url, levelProgress; let canvas = document.createElement('canvas'); let ctx = canvas.getContext('2d'); @@ -36,7 +26,7 @@ class DashBoard { const dash = new Group(), hud = new Group(), hudTarget = new Group(); hud.visible = false; - let hudAnimation, hudPlane, textPlane; + let hudAnimation, hudPlane, textPlane, startBtn; this.group = dash; dash.add(hud); hud.add(hudTarget) @@ -68,8 +58,9 @@ class DashBoard { loadingPlane.add(loadingProgress.object); dash.add(loadingPlane); - (async()=>{ + this.ready = new Promise(async(resolve, reject)=>{ let map = await engine.loadTexture('/static/textures/hud.png', ''); + let start = await engine.loadTexture('/static/textures/play.webp', ''); hudPlane = new Mesh( new PlaneGeometry(dashWidth * 0.96, dashHeight * 0.9), new MeshBasicMaterial({ @@ -97,7 +88,19 @@ class DashBoard { // fix #44 textPlane.material.depthTest = false; //hudPlane.material.depthTest = false; - })() + startBtn = new Mesh( + new SphereGeometry(dashWidth * 0.11), + new MeshStandardMaterial({ + color: 0xffffff, + roughnessMap: start, + metalness: 1 + }) + ) + startBtn.visible = false; + startBtn.rotation.y = Math.PI/6; + dash.add(startBtn); + resolve(); + }); const text = new Text() Object.assign(text, { @@ -127,7 +130,18 @@ class DashBoard { dash.translateZ(-0.75/Math.tan(engine.camera.fov/2 * Math.PI/180) * engine.camera.zoom); }) + this.initScene = function(startBtnCallback){ + this.loading(0,0); + startBtn.visible = false; + engine.clickable.add(startBtn, ()=>{ + startBtnCallback(); + startBtn.visible = false; + engine.motionQueue.clear(startBtn) + }); + } + this.updateText = function(t, textScrolledCallback){ + if (!textPlane) return; textPlane.visible = !!t; engine.motionQueue.clear(text); text.text = t; @@ -208,7 +222,10 @@ class DashBoard { o:hudPlane, a:{scale:{y:1}}, t:.4, d:.4, }]) } - if (occupied) return false; + if (occupied) { + //console.log('Sorry, occupied!') + return false; + } object._hud = { parent: object.parent, placement: { @@ -248,6 +265,7 @@ class DashBoard { } this.detach = (object, opts = {})=>{ + //console.log('detaching', object) engine.motionQueue.remove(hudAnimation); object._hud.parent?.attach(object); hud.rotation.y = 0; @@ -265,6 +283,14 @@ class DashBoard { this.loading = function(progress, tt){ loadingPlane.visible = progress > 0 && progress < 1; loadingProgress.update(progress, tt) + if (progress == 1){ + startBtn.visible = true; + engine.motionQueue.add( + { o: startBtn, a:{ + scale: (k,s)=>s.setScalar(1+0.05*Math.sin(4*k*Math.PI)) + }, r: true, t: 3 }, + ) + } } this.loading(0,0); } diff --git a/src/lib/EventManager.js b/src/lib/EventManager.js index 557d867..d22de62 100644 --- a/src/lib/EventManager.js +++ b/src/lib/EventManager.js @@ -6,6 +6,13 @@ class EventManager extends EventDispatcher{ this.addEventListener(e, object.dispatchEvent.bind(object)) }) } + once(type, listener){ + let wrapper = function(event){ + listener.call(this, event); + this.removeEventListener(event.type, wrapper); + } + this.addEventListener(type, wrapper); + } } export { EventManager } \ No newline at end of file diff --git a/src/lib/GameEngine.js b/src/lib/GameEngine.js index 4dde180..3fb3876 100644 --- a/src/lib/GameEngine.js +++ b/src/lib/GameEngine.js @@ -594,6 +594,18 @@ class GameEngine extends THREE.EventDispatcher{ this.ambientSound.play(); } + showBackground(show){ + if (show){ + this.motionQueue.add({ + o: this.scene, + a: { backgroundIntensity: 1}, + t: 15 + }) + }else{ + this.scene.backgroundIntensity = 0; + } + } + static textureLoader = new THREE.TextureLoader(); static audioLoader = new THREE.AudioLoader(); diff --git a/src/mixins/GameEnvironmentMixin.js b/src/mixins/GameEnvironmentMixin.js index d7ce9a1..1076de9 100644 --- a/src/mixins/GameEnvironmentMixin.js +++ b/src/mixins/GameEnvironmentMixin.js @@ -121,11 +121,19 @@ export default { */ async loadEnvironment(scene, target){ //await gameEngine.loadPanorama(`/asset/default/43.webp`); + let intro; gameEngine.clearScene(); gameEngine.activeObjects.visible = false; - gameEngine.dashboard?.loading(0,0); + await gameEngine.dashboard.ready; + gameEngine.dashboard?.initScene(async ()=>{ + if (this.scene.data.$audio){ + await gameEngine.playAmbientSound(this.scene.data.$audio.asset.name); + gameEngine.ambientSound.setVolume( 0.5 ); + } + intro?.play(); + }); await this.expandScenarioData(scene); - gameEngine.dashboard?.loading(0.1); + gameEngine.dashboard?.loading(0.05); gameEngine.orbitControls.enableRotate = this.env == 'GameDesigner' @@ -135,10 +143,7 @@ export default { let l = target.objects; if (this.scene.data.$environment){ await gameEngine.loadPanorama(this.scene.data.$environment.asset.name); - } - if (this.scene.data.$audio){ - await gameEngine.playAmbientSound(this.scene.data.$audio.asset.name); - gameEngine.ambientSound.setVolume( 0.5 ); + gameEngine.showBackground(false); } if (this.scene.data.$scene){ let env = await gameEngine.load(this.scene.data.$scene.asset.name); @@ -217,16 +222,21 @@ export default { } if (this.scene.data.$intro && this.env != 'GameDesigner'){ - let intro = await new VideoPlayer(gameEngine, {$go: this.scene.data.$intro, skipTransition: true, playInHud: true}); + intro = await new VideoPlayer(gameEngine, { + $go: this.scene.data.$intro, + skipTransition: true, + playInHud: true + }); gameEngine.activeObjects.add(intro.object); intro.video.addEventListener('pause',()=>{ intro.object.removeFromParent(); gameEngine.clickable.remove(intro.object); //TODO!!!! gameEngine.activeObjects.visible = true; + gameEngine.showBackground(true); }); - intro.video.play(); }else{ gameEngine.activeObjects.visible = true; + gameEngine.showBackground(true); } gameEngine.dashboard?.loading(1)