integrate game1 and game2

This commit is contained in:
2025-11-05 09:43:12 +02:00
parent 4236927537
commit 4b722750c3
12 changed files with 354 additions and 278 deletions
@@ -62,14 +62,14 @@ export default{
if (this.forRendering) { if (this.forRendering) {
gameEngine.activeObjects.clear(); gameEngine.activeObjects.clear();
if (this.obj.type == 'panorama2d') { if (this.obj.type == 'panorama2d') {
await gameEngine.loadPanorama(`/asset/default/${this.obj.asset.name}`); await gameEngine.loadPanorama(this.obj.asset.name);
// let t = await gameEngine.loadTexture(`/asset/default/${this.obj.asset.name}`); // let t = await gameEngine.loadTexture(`/asset/default/${this.obj.asset.name}`);
// t.mapping = gameEngine.$.EquirectangularReflectionMapping; // t.mapping = gameEngine.$.EquirectangularReflectionMapping;
// gameEngine.scene.background = t; // gameEngine.scene.background = t;
// gameEngine.scene.environment = t; // gameEngine.scene.environment = t;
// gameEngine.scene.add(gameEngine.camera); // gameEngine.scene.add(gameEngine.camera);
} else { } else {
let gltf = await gameEngine.load(`/asset/default/${this.obj.asset.name}`); let gltf = await gameEngine.load(this.obj.asset.name);
//console.debug('GLTF', gltf); //console.debug('GLTF', gltf);
this.loadedAsset = gltf; this.loadedAsset = gltf;
this.animations = gltf.animations.map(a => ({ this.animations = gltf.animations.map(a => ({
@@ -11,7 +11,7 @@ import { VideoPlayer } from "./VideoPlayer";
import { MazeQuizGame } from "./MazeQuizGame/MazeQuizGame"; import { MazeQuizGame } from "./MazeQuizGame/MazeQuizGame";
import { assignMaterial, assignParams } from "@/lib/MeshUtils"; import { assignMaterial, assignParams } from "@/lib/MeshUtils";
const games = {PuzzleGame1, PuzzleGame2, PuzzleGame4}; const games = {PuzzleGame1, PuzzleGame2, PuzzleGame4, MazeQuizGame};
class InteractiveObject { class InteractiveObject {
constructor(obj, gameEngine, params) { constructor(obj, gameEngine, params) {
@@ -35,7 +35,7 @@ class InteractiveObject {
this.object = this.source.object; this.object = this.source.object;
break; break;
case 'Gltf': case 'Gltf':
let gltf = await gameEngine.load(obj.value); let gltf = await gameEngine.load(obj.value, '');
let gltfObj = gltf.scene; let gltfObj = gltf.scene;
gltfObj.traverse(function (object) { gltfObj.traverse(function (object) {
object.frustumCulled = false; object.frustumCulled = false;
@@ -58,11 +58,9 @@ class InteractiveObject {
this.source = gltf; this.source = gltf;
break; break;
case 'GenericObject': case 'GenericObject':
this.source = await gameEngine.load(`/asset/default/${obj.$go.asset.name}`); this.source = await gameEngine.load(obj.$go.asset.name);
this.object = this.source.scene; this.object = this.source.scene;
break; break;
case 'PuzzleGame1':
case 'PuzzleGame2':
case 'PuzzleGame3': case 'PuzzleGame3':
case 'PuzzleGame4': case 'PuzzleGame4':
case 'PuzzleGame5': case 'PuzzleGame5':
@@ -72,13 +70,14 @@ class InteractiveObject {
this.object.game = game; this.object.game = game;
break; break;
case 'VideoPlayer': case 'VideoPlayer':
let vp = new VideoPlayer(gameEngine, `/asset/default/${obj.$go.asset.name}`); let vp = new VideoPlayer(gameEngine, gameEngine.assetPath + obj.$go.asset.name);
this.source = await vp.ready; this.source = await vp.ready;
this.object = vp.object; this.object = vp.object;
break; break;
case 'PuzzleGame1':
case 'PuzzleGame2':
case 'MazeQuizGame': case 'MazeQuizGame':
this.source = new MazeQuizGame(gameEngine, obj); this.source = await new games[obj.type](gameEngine, obj);
await this.source.load();
this.object = this.source.object; this.object = this.source.object;
break; break;
} }
@@ -91,6 +90,8 @@ class InteractiveObject {
const InteractiveObjectTypes = [ const InteractiveObjectTypes = [
{ {
id: 'PuzzleGame1', name: 'Puzzle Game 1' id: 'PuzzleGame1', name: 'Puzzle Game 1'
},{
id: 'PuzzleGame2', name: 'Puzzle Game 2'
},{ },{
id: 'MazeQuizGame', name: 'Maze Quiz Game' id: 'MazeQuizGame', name: 'Maze Quiz Game'
},{ },{
@@ -144,7 +144,7 @@ class MazeObject {
}; };
this.load = async function(){ this.load = async function(){
let mazeAsset = await engine.load('/static/meshes/quiz.gltf'); let mazeAsset = await engine.load('/static/meshes/quiz.gltf', '');
['tunnel', 'wall', 'door', 'floor'].forEach(e => { ['tunnel', 'wall', 'door', 'floor'].forEach(e => {
o[e] = mazeAsset.scene.getObjectByName(e); o[e] = mazeAsset.scene.getObjectByName(e);
//o[e].frustumCulled = false; //o[e].frustumCulled = false;
@@ -18,6 +18,7 @@ const tl = 4;
class MazeQuizGame { class MazeQuizGame {
constructor(engine, data) { constructor(engine, data) {
return new Promise(async (resolve, reject)=>{
let questions = data.shuffle ? Utils.shuffleArray(data.questions) : data.questions; let questions = data.shuffle ? Utils.shuffleArray(data.questions) : data.questions;
let def = this.generate(questions); let def = this.generate(questions);
this.mazeObject = new MazeObject(engine, def) this.mazeObject = new MazeObject(engine, def)
@@ -51,12 +52,10 @@ class MazeQuizGame {
} }
//console.log(e, ud, engine.hero?.animationsMap); //console.log(e, ud, engine.hero?.animationsMap);
}) })
}
async load(){
await this.mazeObject.load(); await this.mazeObject.load();
this.object = this.mazeObject.object; this.object = this.mazeObject.object;
return this; resolve(this)
})
} }
generate(questions, qid = 0, len){ generate(questions, qid = 0, len){
@@ -1,9 +1,10 @@
import { BoxGeometry, Mesh, MeshBasicMaterial, Group } from 'three'; import { BoxGeometry, Mesh, MeshBasicMaterial, Group } from 'three';
import { TextureLoader } from 'three/src/loaders/TextureLoader';
import { MotionEngine } from '../../lib/MotionEngine'; import { MotionEngine } from '../../lib/MotionEngine';
class PuzzleGame1 { class PuzzleGame1 {
constructor(context, image, w, h) { constructor(engine, data) {
return new Promise(async (resolve, reject)=>{
let w = data.w, h = data.h, wh = w*h;
this.object = new Group(); this.object = new Group();
const aq = new MotionEngine(); const aq = new MotionEngine();
const pr = [[0, -1], [0, 1], [1, 0], [-1, 0], [0, 0], [0, 2]]; const pr = [[0, -1], [0, 1], [1, 0], [-1, 0], [0, 0], [0, 2]];
@@ -12,8 +13,8 @@ class PuzzleGame1 {
let bm = new BoxGeometry(1, 1, 1); let bm = new BoxGeometry(1, 1, 1);
let uv = bm.getAttribute('uv'); let uv = bm.getAttribute('uv');
for (let i = 0; i < 6; i++) { for (let i = 0; i < wh; i++) {
let s = [(i % w) / w, (i % h) / h]; let s = [(i % w) / w, Math.trunc(i / w) / h];
//top left //top left
uv.array[8 * i] = s[0]; uv.array[8 * i] = s[0];
uv.array[8 * i + 1] = s[1] + 1 / h; uv.array[8 * i + 1] = s[1] + 1 / h;
@@ -29,14 +30,14 @@ class PuzzleGame1 {
} }
let material = new MeshBasicMaterial({ let material = new MeshBasicMaterial({
map: new TextureLoader().load(image) map: await engine.loadTexture(data.$go.asset.name)
}); });
//material.map.encoding = sRGBEncoding; //material.map.encoding = sRGBEncoding;
for (let i = 0; i < 6; i++) { for (let i = 0; i < wh; i++) {
let b = bm.clone(); let b = bm.clone();
let mesh = new Mesh(b, material); let mesh = new Mesh(b, material);
mesh.position.set((i % w) * d, (i % h) * d, 0); mesh.position.set((i % w) * d, (Math.trunc(i / w)) * d, 0);
let ri; let ri;
do { do {
ri = Math.floor(Math.random() * 6); ri = Math.floor(Math.random() * 6);
@@ -72,7 +73,7 @@ class PuzzleGame1 {
}; };
this.object.children.forEach(c => { this.object.children.forEach(c => {
context.clickable.add(c, clickFn); engine.clickable.add(c, clickFn);
}); });
this.update = () => { this.update = () => {
@@ -82,14 +83,17 @@ class PuzzleGame1 {
this.object.children.forEach((c, i) => { this.object.children.forEach((c, i) => {
aq.add({ aq.add({
o: c, o: c,
a: { position: { x: i % w, y: i % h } }, a: { position: { x: i % w, y: Math.trunc(i/w)} },
t: 1, t: 1,
f: i == 0 && this.onfinish f: i == 0 && this.onfinish
}); });
}); });
//context.dashboard.addPoints(10); //engine.dashboard.addPoints(10);
} }
}; };
resolve(this);
})
} }
} }
@@ -1,8 +1,40 @@
<template> <template>
<v-card v-if="modelValue.go">
<v-card-item>
<v-number-input density="compact" label="Width" v-model="modelValue.w"></v-number-input>
<v-number-input density="compact" label="Height" v-model="modelValue.h"></v-number-input>
<v-img :src="`/asset/thumb/${modelValue.go}.webp`" />
<div class="text-caption text-center">{{ modelValue.title }}</div>
</v-card-item>
</v-card>
<asset-selector @select="assignTexture" :type="['Texture']">
<template v-slot:activator="props">
<v-btn v-bind="props" prepend-icon="mdi-video-box" color="deep-orange-darken-4" block>Choose image object</v-btn>
</template>
</asset-selector>
</template> </template>
<script> <script>
export default {
export default {
data(){
return {
active: false
}
},
mounted(){
this.active = true;
},
props:{
modelValue: Object
},
methods:{
assignTexture(e){
this.modelValue.go = e.id;
this.modelValue.title = e.name
this.modelValue.w = 2;
this.modelValue.h = 3;
}
}
} }
</script> </script>
@@ -1,10 +1,11 @@
import { BoxGeometry, Mesh, MeshBasicMaterial, Group } from 'three'; import { BoxGeometry, Mesh, MeshBasicMaterial, Group } from 'three';
import { TextureLoader } from 'three/src/loaders/TextureLoader';
import { MotionEngine } from '../../lib/MotionEngine'; import { MotionEngine } from '../../lib/MotionEngine';
class PuzzleGame2 { class PuzzleGame2 {
constructor(context, image, w, h) { constructor(engine, data) {
const texture = new TextureLoader().load(image); return new Promise(async (resolve, reject)=>{
let w = data.w, h = data.h, wh = w*h;
const texture = await engine.loadTexture(data.$go.asset.name);
//texture.encoding = sRGBEncoding; //texture.encoding = sRGBEncoding;
const material = new MeshBasicMaterial({ const material = new MeshBasicMaterial({
map: texture map: texture
@@ -46,11 +47,11 @@ class PuzzleGame2 {
if (yl < h - 1) m.push(p.indexOf(pl + w)); if (yl < h - 1) m.push(p.indexOf(pl + w));
return m; return m;
} }
for (let i = 0; i < w * h; i++) { for (let i = 0; i < wh; i++) {
p[i] = i; p[i] = i;
} }
for (let iter = 0; iter < 73 * w * h; iter++) { for (let iter = 0; iter < 73 * wh; iter++) {
let m = getMoves(); let m = getMoves();
xch(m[Math.floor(Math.random() * m.length)], lidx); xch(m[Math.floor(Math.random() * m.length)], lidx);
} }
@@ -64,7 +65,7 @@ class PuzzleGame2 {
}); });
}; };
for (let i = 0; i < w * h; i++) { for (let i = 0; i < wh; i++) {
let x = i % w, xp = x / w; let x = i % w, xp = x / w;
let y = ~~(i / h), yp = y / h; let y = ~~(i / h), yp = y / h;
let bg = new BoxGeometry(1, 1, 1); let bg = new BoxGeometry(1, 1, 1);
@@ -120,7 +121,7 @@ class PuzzleGame2 {
}; };
this.object.children.forEach(c => { this.object.children.forEach(c => {
context.clickable.add(c, clickFn); engine.clickable.add(c, clickFn);
}); });
this.update = () => { this.update = () => {
@@ -136,10 +137,11 @@ class PuzzleGame2 {
f: i == 0 && this.onfinish f: i == 0 && this.onfinish
}); });
}); });
//context.dashboard.addPoints(10); //engine.dashboard.addPoints(10);
} }
}; };
resolve(this)
});
} }
} }
@@ -0,0 +1,40 @@
<template>
<v-card v-if="modelValue.go">
<v-card-item>
<v-number-input density="compact" label="Width" v-model="modelValue.w"></v-number-input>
<v-number-input density="compact" label="Height" v-model="modelValue.h"></v-number-input>
<v-img :src="`/asset/thumb/${modelValue.go}.webp`" />
<div class="text-caption text-center">{{ modelValue.title }}</div>
</v-card-item>
</v-card>
<asset-selector @select="assignTexture" :type="['Texture']">
<template v-slot:activator="props">
<v-btn v-bind="props" prepend-icon="mdi-video-box" color="deep-orange-darken-4" block>Choose image object</v-btn>
</template>
</asset-selector>
</template>
<script>
export default {
data(){
return {
active: false
}
},
mounted(){
this.active = true;
},
props:{
modelValue: Object
},
methods:{
assignTexture(e){
this.modelValue.go = e.id;
this.modelValue.title = e.name
this.modelValue.w = 3;
this.modelValue.h = 3;
}
}
}
</script>
+2 -1
View File
@@ -34,12 +34,13 @@ import Utils from '@/lib/Utils';
import VideoPlayer from '../InteractiveObjects/VideoPlayer.vue'; import VideoPlayer from '../InteractiveObjects/VideoPlayer.vue';
import PuzzleGame1 from '../InteractiveObjects/PuzzleGame1.vue'; import PuzzleGame1 from '../InteractiveObjects/PuzzleGame1.vue';
import PuzzleGame2 from '../InteractiveObjects/PuzzleGame2.vue';
import MazeQuizGame from '../InteractiveObjects/MazeQuizGame/MazeQuizGame.vue'; import MazeQuizGame from '../InteractiveObjects/MazeQuizGame/MazeQuizGame.vue';
import GenericObject from '../InteractiveObjects/GenericObject.vue'; import GenericObject from '../InteractiveObjects/GenericObject.vue';
export default { export default {
emits:['target', 'preview'], emits:['target', 'preview'],
components: { SvgIcon, VideoPlayer, PuzzleGame1, MazeQuizGame, GenericObject }, components: { SvgIcon, VideoPlayer, PuzzleGame1, PuzzleGame2, MazeQuizGame, GenericObject },
data(){ data(){
return { return {
active: false active: false
+11 -8
View File
@@ -16,6 +16,8 @@ import { Clickable } from './Clickable.js';
import { DashBoard } from './Dashboard.js'; import { DashBoard } from './Dashboard.js';
import { MotionEngine } from './MotionEngine.js'; import { MotionEngine } from './MotionEngine.js';
const assetPath = '/asset/default/';
class GameEngine extends THREE.EventDispatcher{ class GameEngine extends THREE.EventDispatcher{
async init(domNode, opts = {}) { async init(domNode, opts = {}) {
this.w = domNode.clientWidth || 1200, this.h = domNode.clientHeight || 800; this.w = domNode.clientWidth || 1200, this.h = domNode.clientHeight || 800;
@@ -101,6 +103,7 @@ class GameEngine extends THREE.EventDispatcher{
this.dashboard = dashboard; this.dashboard = dashboard;
this.motionQueue = new MotionEngine(); this.motionQueue = new MotionEngine();
this.assetPath = assetPath;
this.activeObjects = new THREE.Group(); this.activeObjects = new THREE.Group();
scene.add(this.activeObjects); scene.add(this.activeObjects);
@@ -158,7 +161,7 @@ class GameEngine extends THREE.EventDispatcher{
domNode.appendChild(renderer.domElement); domNode.appendChild(renderer.domElement);
let texture = await this.loadTexture('/static/textures/bck.webp'); let texture = await this.loadTexture('/static/textures/bck.webp', '');
// let bck = await this.loadTexture('/static/textures/bck.webp'); // let bck = await this.loadTexture('/static/textures/bck.webp');
// bck.premultiplyAlpha = true; // bck.premultiplyAlpha = true;
texture.mapping = THREE.EquirectangularReflectionMapping; texture.mapping = THREE.EquirectangularReflectionMapping;
@@ -383,12 +386,12 @@ class GameEngine extends THREE.EventDispatcher{
$ = THREE; $ = THREE;
async load(url, progress) { async load(url, path = assetPath, progress) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.loader.load(url, o => { this.loader.load(`${path}${url}`, o => {
o.scene.traverse(object => { o.scene.traverse(object => {
if (object.name == 'environment' || object.material){ if (object.name == 'environment' || object.material){
console.log('env recomputing') //console.log('env recomputing')
object.material.shading = THREE.SmoothShading; object.material.shading = THREE.SmoothShading;
object.geometry.computeVertexNormals(true); object.geometry.computeVertexNormals(true);
//object.material.metalness = 0; //object.material.metalness = 0;
@@ -408,9 +411,9 @@ class GameEngine extends THREE.EventDispatcher{
}) })
} }
async loadTexture(url, progress) { async loadTexture(url, path = assetPath, progress) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
new THREE.TextureLoader().load(url, texture => { new THREE.TextureLoader().load(`${path}${url}`, texture => {
//texture.encoding = THREE.sRGBEncoding; //texture.encoding = THREE.sRGBEncoding;
texture.colorSpace = THREE.SRGBColorSpace; texture.colorSpace = THREE.SRGBColorSpace;
resolve(texture) resolve(texture)
@@ -418,8 +421,8 @@ class GameEngine extends THREE.EventDispatcher{
}) })
} }
async loadPanorama(url) { async loadPanorama(url, path = assetPath) {
let t = await this.loadTexture(url); let t = await this.loadTexture(url, path);
t.mapping = THREE.EquirectangularReflectionMapping; t.mapping = THREE.EquirectangularReflectionMapping;
this.scene.background = t; this.scene.background = t;
this.scene.environment = t; this.scene.environment = t;
+3 -9
View File
@@ -102,14 +102,8 @@ export default {
}) })
for (let i of scene.data.items || []) { for (let i of scene.data.items || []) {
if (i.data.io){
if (i.data.io.go){
promises.push(this.$api.gameObject.load(i.data.io.go).then(r=>i.data.io.$go = r.data));
}
}else{
promises.push(this.$api.gameObject.load(i.data.go).then(r=>i.data.$go = r.data)); promises.push(this.$api.gameObject.load(i.data.go).then(r=>i.data.$go = r.data));
} }
}
await Promise.all(promises); await Promise.all(promises);
}, },
@@ -128,10 +122,10 @@ export default {
target.objects = target.objects || {}; target.objects = target.objects || {};
let l = target.objects; let l = target.objects;
if (this.scene.data.$environment){ if (this.scene.data.$environment){
await gameEngine.loadPanorama(`/asset/default/${this.scene.data.$environment.asset.name}`); await gameEngine.loadPanorama(this.scene.data.$environment.asset.name);
} }
if (this.scene.data.$scene){ if (this.scene.data.$scene){
let env = await gameEngine.load(`/asset/default/${this.scene.data.$scene.asset.name}`); let env = await gameEngine.load(this.scene.data.$scene.asset.name);
//console.log('ENV', env) //console.log('ENV', env)
this.setObjectAttributes(l, this.scene.data, env.scene, env, 100); this.setObjectAttributes(l, this.scene.data, env.scene, env, 100);
gameEngine.activeObjects.add(env.scene); gameEngine.activeObjects.add(env.scene);
@@ -193,7 +187,7 @@ export default {
['position', 'scale', 'rotation'].forEach(p=>{ ['position', 'scale', 'rotation'].forEach(p=>{
object[p].copy(l[data.id][p]) object[p].copy(l[data.id][p])
}) })
}else{ }else if (!data.type || data.type == 'GenericObject'){
gameEngine.autoScale(object, autoScaleFactor); gameEngine.autoScale(object, autoScaleFactor);
} }
l[data.id] = l[data.id] || {}; l[data.id] = l[data.id] || {};
+1 -1
View File
@@ -22,7 +22,7 @@ export default {
}, { }, {
value: 'object2d', value: 'object2d',
icon: 'file-image-outline', icon: 'file-image-outline',
type: 'Descriptive', type: 'Texture',
color: 'cyan-darken-3' color: 'cyan-darken-3'
}, { }, {
value: 'player3d', value: 'player3d',