amazing maze

This commit is contained in:
2025-10-20 22:49:08 +03:00
parent 192a900a96
commit 3ff60a1cf4
9 changed files with 140 additions and 70 deletions
+14 -14
View File
@@ -205,23 +205,23 @@ export default {
// gameEngine.camera.scale.copy(l.camera.position) // gameEngine.camera.scale.copy(l.camera.position)
} }
let testGame1 = new Game1(gameEngine, '/static/textures/game1-test.jpg', 2, 3); // let testGame1 = new Game1(gameEngine, '/static/textures/game1-test.jpg', 2, 3);
gameEngine.activeObjects.add(testGame1.object); // gameEngine.activeObjects.add(testGame1.object);
testGame1.object.position.set(0, 1, -15); // testGame1.object.position.set(0, 1, -15);
let testGame2 = new Game2(gameEngine, '/static/textures/game2-test.jpg', 3, 3); // let testGame2 = new Game2(gameEngine, '/static/textures/game2-test.jpg', 3, 3);
gameEngine.activeObjects.add(testGame2.object); // gameEngine.activeObjects.add(testGame2.object);
testGame2.object.position.set(0, 1, 15); // testGame2.object.position.set(0, 1, 15);
testGame2.object.rotation.y += Math.PI; // testGame2.object.rotation.y += Math.PI;
let testGame4 = new Game4(gameEngine, '/static/feathers-game.glb', 3, 4); // let testGame4 = new Game4(gameEngine, '/static/feathers-game.glb', 3, 4);
gameEngine.activeObjects.add(testGame4.object); // gameEngine.activeObjects.add(testGame4.object);
testGame4.object.position.set(15, 1, 5); // testGame4.object.position.set(15, 1, 5);
let vp = new VideoPlayer(gameEngine, this.$refs.videoPlayer, 16, 9); // let vp = new VideoPlayer(gameEngine, this.$refs.videoPlayer, 16, 9);
gameEngine.activeObjects.add(vp.object); // gameEngine.activeObjects.add(vp.object);
vp.object.position.set(37, 5.5, 15); // vp.object.position.set(37, 5.5, 15);
vp.object.rotation.y += -Math.PI/2; // vp.object.rotation.y += -Math.PI/2;
let maze = new MazeQuizGame(gameEngine, {}, [ let maze = new MazeQuizGame(gameEngine, {}, [
{s: 'Кое е най-голямото езеро в България', h: 'Wrong answer', a: true}, {s: 'Кое е най-голямото езеро в България', h: 'Wrong answer', a: true},
@@ -0,0 +1,34 @@
import { TextureLoader, MeshStandardMaterial, MeshBasicMaterial, PlaneGeometry, Mesh } from "three";
import { assignParams } from "@/lib/MeshUtils";
class ImageObject {
constructor(obj, context) {
var t = new TextureLoader().setPath(context.path).load(obj.value);
//t.encoding = sRGBEncoding;
var mp = {
map: t,
alphaTest: 0.5
};
if (obj.nm) {
mp.normalMap = new TextureLoader().setPath(context.path).load(obj.nm);
}
if (obj.em) {
mp.emissiveMap = new TextureLoader().setPath(context.path).load(obj.em);
}
if (obj.am) {
mp.alphaMap = new TextureLoader().setPath(context.path).load(obj.am);
}
obj.material && Object.assign(mp, obj.material);
let geo = new PlaneGeometry(context.wallSize * (obj.w || 1), context.wallSize * (obj.h || 1));
if (obj.uv) {
var uvAttribute = geo.attributes.uv;
for (var i = 0; i < uvAttribute.count; i++) {
uvAttribute.setXY(i, obj.uv[2 * i], obj.uv[2 * i + 1]);
}
}
this.object = new Mesh(geo, mp.metalness ? new MeshStandardMaterial(mp) : new MeshBasicMaterial(mp));
assignParams(this.object, obj);
}
}
export {ImageObject}
@@ -1,8 +1,7 @@
import { ImageObject } from "./ImageObject"; //import { Hint } from "./Hint";
import { Hint } from "./Hint";
import { Group, AnimationMixer, LoopPingPong, Vector3 } from "three"; import { Group, AnimationMixer, LoopPingPong, Vector3 } from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { Utils } from "./Utils"; import { assignMaterial, assignParams } from "@/lib/MeshUtils";
import { Game1 } from "./PuzzleGame1"; import { Game1 } from "./PuzzleGame1";
import { Game2 } from "./PuzzleGame2"; import { Game2 } from "./PuzzleGame2";
// import { Game3 } from "./games/Game3"; // import { Game3 } from "./games/Game3";
@@ -10,6 +9,7 @@ import { Game4 } from "./PuzzleGame4";
// import { Game5 } from "./games/Game5"; // import { Game5 } from "./games/Game5";
// import { Game6 } from "./games/Game6"; // import { Game6 } from "./games/Game6";
import { TextObject } from "./TextObject"; import { TextObject } from "./TextObject";
import { ImageObject } from "./ImageObject";
const games = {Game1, Game2, Game4}; const games = {Game1, Game2, Game4};
@@ -31,7 +31,7 @@ class InteractiveObject {
break; break;
case 'text': case 'text':
let text = new TextObject(obj, context); let text = new TextObject(obj, context);
resolve(text.mesh); resolve(text.object);
break; break;
case 'mesh': case 'mesh':
mesh = obj.value; mesh = obj.value;
@@ -39,12 +39,12 @@ class InteractiveObject {
break; break;
case 'image': case 'image':
let imo = new ImageObject(obj, context); let imo = new ImageObject(obj, context);
mesh = imo.mesh; mesh = imo.object;
resolve(mesh); resolve(mesh);
break; break;
case 'hint': case 'hint':
let hint = new Hint(obj, context); let hint = new Hint(obj, context);
mesh = hint.mesh; mesh = hint.object;
resolve(mesh); resolve(mesh);
break; break;
case 'gltf': case 'gltf':
@@ -58,7 +58,7 @@ class InteractiveObject {
// object.castShadow = true; // object.castShadow = true;
// object.receiveShadow = true; // object.receiveShadow = true;
}); });
Utils.assignMaterial(gltfObj, obj, context); assignMaterial(gltfObj, obj, context);
if (gltf.animations && gltf.animations.length) { if (gltf.animations && gltf.animations.length) {
let mixer = new AnimationMixer(gltfObj); let mixer = new AnimationMixer(gltfObj);
context.mixers.push(mixer); context.mixers.push(mixer);
@@ -71,7 +71,7 @@ class InteractiveObject {
break; break;
case 'asset': case 'asset':
mesh = context.assets[obj.value].clone(); mesh = context.assets[obj.value].clone();
Utils.assignMaterial(mesh, obj, context); assignMaterial(mesh, obj, context);
resolve(mesh); resolve(mesh);
break; break;
case 'Game1': case 'Game1':
@@ -81,7 +81,7 @@ class InteractiveObject {
case 'Game5': case 'Game5':
case 'Game6': case 'Game6':
var game = new games[obj.type](context, obj.args[0], obj.args[1], obj.args[2]); var game = new games[obj.type](context, obj.args[0], obj.args[1], obj.args[2]);
mesh = game.game; mesh = game.object;
mesh.game = game; mesh.game = game;
resolve(mesh); resolve(mesh);
break; break;
@@ -89,14 +89,14 @@ class InteractiveObject {
}); });
this.ready.then((mesh) => { this.ready.then((mesh) => {
mesh.go = {}; mesh.go = {};
let restriction; // let restriction;
if (!context.disableRestrictions && obj.restriction) { // if (!context.disableRestrictions && obj.restriction) {
restriction = { // restriction = {
type: 'deny', // type: 'deny',
a: [obj.room.localToWorld(new Vector3().fromArray(obj.restriction[0])), obj.room.localToWorld(new Vector3().fromArray(obj.restriction[1]))] // a: [obj.room.localToWorld(new Vector3().fromArray(obj.restriction[0])), obj.room.localToWorld(new Vector3().fromArray(obj.restriction[1]))]
}; // };
context.areas.push(restriction); // context.areas.push(restriction);
} // }
mesh.go.finish = () => { mesh.go.finish = () => {
if (obj.finish) { if (obj.finish) {
var f; var f;
@@ -115,15 +115,15 @@ class InteractiveObject {
} }
if (restriction) context.areas.splice(context.areas.indexOf(restriction), 1); if (restriction) context.areas.splice(context.areas.indexOf(restriction), 1);
}; };
if (mesh.game) mesh.game.onfinish = mesh.go.finish; if (mesh.object) mesh.object.onfinish = mesh.go.finish;
Utils.assignParams(mesh, obj); assignParams(mesh, obj);
obj.animation && context.motionEngine.add({ obj.animation && context.motionEngine.add({
o: mesh, o: mesh,
a: obj.animation.motion, a: obj.animation.motion,
r: obj.animation.repeat, r: obj.animation.repeat,
t: obj.animation.duration || 1 t: obj.animation.duration || 1
}); });
this.mesh = mesh; this.object = mesh;
}); });
} }
} }
@@ -1,6 +1,5 @@
import { Group, Vector3, Matrix4, Mesh, Quaternion, BoxGeometry } from 'three'; import { Group, Vector3, Matrix4, Mesh, Quaternion, BoxGeometry } from 'three';
import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'; import { InteractiveObject } from '../InteractiveObject';
import { TextObject } from '../TextObject';
class MazeObject { class MazeObject {
constructor(engine, def, params = {}){ constructor(engine, def, params = {}){
@@ -14,7 +13,8 @@ class MazeObject {
context.wallSize = params.wallSize || 1.2*scale; //half context.wallSize = params.wallSize || 1.2*scale; //half
context.tubeSize = params.tubeSize || 1.2*scale; context.tubeSize = params.tubeSize || 1.2*scale;
context.wallDepth = params.wallDepth || 0*scale; context.wallDepth = params.wallDepth || 0*scale;
context.fontPath = params.fontPath || '/static/fonts/Jura-SemiBold.ttf'; context.fontPath = params.fontPath || '/static/fonts/Montserrat-Regular.ttf';
context.scale = scale;
this.context = context; this.context = context;
let _tf = { let _tf = {
@@ -29,7 +29,6 @@ class MazeObject {
}; };
let o = {}; let o = {};
let areas = [], mazeMeshes = [];
function addPhysics(matrix, position, size, placement = 'side'){ function addPhysics(matrix, position, size, placement = 'side'){
let quat = new Quaternion().setFromRotationMatrix(matrix); let quat = new Quaternion().setFromRotationMatrix(matrix);
@@ -58,7 +57,7 @@ class MazeObject {
let t = o.tunnel.clone(); let t = o.tunnel.clone();
t.position.set(0, 0, i * context.tubeSize); t.position.set(0, 0, i * context.tubeSize);
def.matrix && t.applyMatrix4(def.matrix); def.matrix && t.applyMatrix4(def.matrix);
mazeMeshes.push(t); root.add(t);
} }
offsetZ = def.len * context.tubeSize; offsetZ = def.len * context.tubeSize;
//if (!def.len) offsetZ = -.275; //if (!def.len) offsetZ = -.275;
@@ -70,7 +69,6 @@ class MazeObject {
// console.log(offsetZ, room.localToWorld(new Vector3(context.tubeSize / 2, 0.6, offsetZ/2))) // console.log(offsetZ, room.localToWorld(new Vector3(context.tubeSize / 2, 0.6, offsetZ/2)))
let ofZ = def.len * context.tubeSize
addPhysics(def.matrix, [context.tubeSize / 2, 0.6, offsetZ/2], offsetZ) addPhysics(def.matrix, [context.tubeSize / 2, 0.6, offsetZ/2], offsetZ)
addPhysics(def.matrix, [-context.tubeSize / 2, 0.6, offsetZ/2], offsetZ) addPhysics(def.matrix, [-context.tubeSize / 2, 0.6, offsetZ/2], offsetZ)
@@ -109,18 +107,19 @@ class MazeObject {
} }
e.forEach(g => { e.forEach(g => {
def.matrix && g.applyMatrix4(def.matrix); g.applyMatrix4(def.matrix);
mazeMeshes.push(g); root.add(g);
}); });
def.objects && def.objects.forEach(obj => { def.objects && def.objects.forEach(async obj => {
obj.room = room; obj.room = room;
// let go = new GameObject(obj, context); // let go = new GameObject(obj, context);
let go = new TextObject(obj, context) let go = new InteractiveObject(obj, context)
go.mesh.scale.multiplyScalar(scale) await go.ready;
go.mesh.position.multiply(new Vector3(scale, scale, context.wallSize)) go.object.scale.multiplyScalar(context.wallSize)
go.mesh.applyMatrix4(def.matrix); go.object.position.multiplyScalar(context.wallSize)
root.add(go.mesh); go.object.applyMatrix4(def.matrix);
root.add(go.object);
// go.ready.then(mesh => { // go.ready.then(mesh => {
// room.add(mesh); // room.add(mesh);
// }); // });
@@ -151,11 +150,11 @@ class MazeObject {
// console.log(e, o[e].geometry.boundingBox) // console.log(e, o[e].geometry.boundingBox)
}); });
this.mazeObject(def, room); this.mazeObject(def, room);
mazeMeshes.forEach(mesh=>{ // mazeMeshes.forEach(mesh=>{
//let mesh = new Mesh(mg, o.tunnel.material) // //let mesh = new Mesh(mg, o.tunnel.material)
root.add(mesh); // root.add(mesh);
//engine.phy.add(mesh, 'fixed') // //engine.phy.add(mesh, 'fixed')
}) // })
console.log(o.tunnel) console.log(o.tunnel)
//scene.add(new Mesh(BufferGeometryUtils.mergeGeometries(mazeGeometries, false), o.tunnel.material)); //scene.add(new Mesh(BufferGeometryUtils.mergeGeometries(mazeGeometries, false), o.tunnel.material));
@@ -23,7 +23,15 @@ class MazeQuizGame {
len, len,
objects:[ objects:[
{ {
type: 'text', text: cq.s, position:[0,.44,len], rotation:[0,Math.PI, 0] type: 'text', text: cq.s, position:[0,.4,len+.96], rotation:[0,Math.PI, 0]
},{
type: 'image', value: '/static/textures/arrow.png', position:[-.5,.44,len+.96], rotation:[0,Math.PI, 0], scale: [0.03, 0.03, 0.03]
},{
type: 'text', text: 'Вярно', position:[-.5,.3,len+.9], rotation:[0,Math.PI, 0]
},{
type: 'image', value: '/static/textures/arrow.png', position:[.5,.44,len+.96], rotation:[0,Math.PI, Math.PI], scale: [0.03, 0.03, 0.03]
},{
type: 'text', text: 'Невярно', position:[.5,.3,len+.9], rotation:[0,Math.PI, 0]
} }
], ],
[lrv?'r':'l']:{ [lrv?'r':'l']:{
@@ -32,7 +40,7 @@ class MazeQuizGame {
len: lr, len: lr,
objects:[ objects:[
{ {
type: 'text', text: cq.h, position:[0,.44,lr], rotation:[0,Math.PI, 0] type: 'text', text: cq.h, position:[0,.44,lr+.96], rotation:[0,Math.PI, 0]
} }
] ]
} }
@@ -1,13 +1,13 @@
import { MeshStandardMaterial, Color, Vector3 } from "three"; import { MeshStandardMaterial, Color, Vector3 } from "three";
import { Text } from "troika-three-text"; import { Text } from "troika-three-text";
import Utils from "@/lib/utils"; import { assignParams } from "@/lib/MeshUtils";
class TextObject { class TextObject {
constructor(obj, params) { constructor(obj, params) {
const txt = new Text(); const txt = new Text();
// Set properties to configure: // Set properties to configure:
txt.text = obj.text; txt.text = obj.text;
txt.fontSize = 0.022; txt.fontSize = 0.033;
txt.lineHeight = 1.1; txt.lineHeight = 1.1;
txt.maxWidth = obj.width || params.wallSize * .73; txt.maxWidth = obj.width || params.wallSize * .73;
txt.textAlign = 'center'; txt.textAlign = 'center';
@@ -16,18 +16,19 @@ class TextObject {
txt.anchorY = 'bottom'; txt.anchorY = 'bottom';
txt.curveRadius = 0; txt.curveRadius = 0;
txt.outlineColor = 0xffffff; txt.outlineColor = 0xffffff;
txt.outlineWidth = '15%'; txt.outlineWidth = '1%';
txt.outlineBlur = '50%'; txt.depthOffset = 0.1;
Utils.assignMeshParams(txt, obj) //txt.outlineBlur = '50%';
txt.color = new Color(0xffffff);
assignParams(txt, obj)
let m = new MeshStandardMaterial({ let m = new MeshStandardMaterial({
roughness: .73, roughness: .73,
metalness: .37, metalness: .37,
}); });
txt.material = m; txt.material = m;
txt.color = new Color(0x0);
txt.sync(); txt.sync();
this.txt = txt; this.txt = txt;
this.mesh = txt; this.object = txt;
// if (obj.effect == 'distance') { // if (obj.effect == 'distance') {
// let dstm = .8; // let dstm = .8;
// var oldBR = txt.onBeforeRender; // var oldBR = txt.onBeforeRender;
+30
View File
@@ -0,0 +1,30 @@
import { TextureLoader } from "three";
function assignParams(mesh, params){
['scale', 'rotation', 'position'].forEach(p=>params[p] && mesh[p].fromArray(params[p]));
['visible', 'name'].forEach(p=>{
if (params[p]!==undefined) mesh[p] = params[p];
});
}
function assignMaterial(mesh, params, context){
if (params.name && params.material){
console.log(mesh)
//let mp = params.material.metalness ? new MeshStandardMaterial(params.material) : new MeshBasicMaterial(params.material)
Object.assign(mesh.material, params.material)
if (params.dm){
var dm = new TextureLoader().setPath(context.path).load(params.dm);
mesh.material.map = dm;
}
if (params.nm){
mesh.material.normalMap = new TextureLoader().setPath(context.path).load(params.nm);
}
if (params.em) {
mesh.material.emissiveMap = new TextureLoader().setPath(context.path).load(params.em);
}
//mesh.material = mp;
mesh.material.needsUpdate = true;
}
}
export { assignParams, assignMaterial }
+6 -1
View File
@@ -4,6 +4,7 @@ import { DRACOLoader } from 'three/examples/jsm/Addons.js';
import { OrbitControls } from 'three/examples/jsm/Addons.js'; import { OrbitControls } from 'three/examples/jsm/Addons.js';
//import { Controller as OrbitControls } from './3rd-party/phy/3TH/Controller.js'; //import { Controller as OrbitControls } from './3rd-party/phy/3TH/Controller.js';
import { ViewportGizmo } from "three-viewport-gizmo"; import { ViewportGizmo } from "three-viewport-gizmo";
import Stats from 'three/examples/jsm/libs/stats.module';
//import { AnaglyphEffect } from './three/AnaglyphEffect'; //import { AnaglyphEffect } from './three/AnaglyphEffect';
import { AnaglyphEffect } from 'three/addons/effects/AnaglyphEffect.js'; import { AnaglyphEffect } from 'three/addons/effects/AnaglyphEffect.js';
import { StereoEffect } from 'three/addons/effects/StereoEffect.js'; import { StereoEffect } from 'three/addons/effects/StereoEffect.js';
@@ -23,7 +24,7 @@ class GameEngine {
this.opts = opts; this.opts = opts;
const gameEngine = this; const gameEngine = this;
this.perspectiveCamera = new THREE.PerspectiveCamera(45, this.aspect, 0.01, 1000); this.perspectiveCamera = new THREE.PerspectiveCamera(45, this.aspect, 0.01, 25);
this.raycaster = new THREE.Raycaster(); this.raycaster = new THREE.Raycaster();
this.perspectiveCamera.position.set(0, 0, 10); this.perspectiveCamera.position.set(0, 0, 10);
@@ -163,6 +164,9 @@ class GameEngine {
await this.initPhysics(); await this.initPhysics();
this.stats = new Stats();
document.body.appendChild(this.stats.dom);
if (opts.ar) { if (opts.ar) {
renderer.xr.enabled = true; renderer.xr.enabled = true;
document.body.appendChild(ARButton.createButton(renderer, {})); document.body.appendChild(ARButton.createButton(renderer, {}));
@@ -569,6 +573,7 @@ class GameEngine {
} else { } else {
this.renderer.render(scene, camera); this.renderer.render(scene, camera);
} }
this.stats?.update()
} }
} }
-7
View File
@@ -54,11 +54,4 @@ export default {
rad2deg(rad){ rad2deg(rad){
return rad * 180 / Math.PI; return rad * 180 / Math.PI;
}, },
assignMeshParams(mesh, params){
['scale', 'rotation', 'position'].forEach(p=>params[p] && mesh[p].fromArray(params[p]));
['visible', 'name'].forEach(p=>{
if (params[p]!==undefined) mesh[p] = params[p];
});
},
} }