From a5c7fe05f74625b0bfc97e0973d091e10259f1de Mon Sep 17 00:00:00 2001 From: goynov Date: Wed, 28 Jan 2026 13:55:49 +0200 Subject: [PATCH] #35 --- public/static/textures/play.webp | Bin 0 -> 22550 bytes .../InteractiveObjects/VideoPlayer.js | 28 +++++--- src/lib/Dashboard.js | 62 +++++++++++++----- src/lib/EventManager.js | 7 ++ src/lib/GameEngine.js | 12 ++++ src/mixins/GameEnvironmentMixin.js | 26 +++++--- 6 files changed, 101 insertions(+), 34 deletions(-) create mode 100644 public/static/textures/play.webp diff --git a/public/static/textures/play.webp b/public/static/textures/play.webp new file mode 100644 index 0000000000000000000000000000000000000000..728443cc0331b5581cae7119fd001634a337effb GIT binary patch literal 22550 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 U000h9Vr5qW5C8@MWB>pF06*)QS^xk5 literal 0 HcmV?d00001 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)