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) {
gameEngine.activeObjects.clear();
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}`);
// t.mapping = gameEngine.$.EquirectangularReflectionMapping;
// gameEngine.scene.background = t;
// gameEngine.scene.environment = t;
// gameEngine.scene.add(gameEngine.camera);
} 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);
this.loadedAsset = gltf;
this.animations = gltf.animations.map(a => ({
@@ -11,7 +11,7 @@ import { VideoPlayer } from "./VideoPlayer";
import { MazeQuizGame } from "./MazeQuizGame/MazeQuizGame";
import { assignMaterial, assignParams } from "@/lib/MeshUtils";
const games = {PuzzleGame1, PuzzleGame2, PuzzleGame4};
const games = {PuzzleGame1, PuzzleGame2, PuzzleGame4, MazeQuizGame};
class InteractiveObject {
constructor(obj, gameEngine, params) {
@@ -35,7 +35,7 @@ class InteractiveObject {
this.object = this.source.object;
break;
case 'Gltf':
let gltf = await gameEngine.load(obj.value);
let gltf = await gameEngine.load(obj.value, '');
let gltfObj = gltf.scene;
gltfObj.traverse(function (object) {
object.frustumCulled = false;
@@ -58,11 +58,9 @@ class InteractiveObject {
this.source = gltf;
break;
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;
break;
case 'PuzzleGame1':
case 'PuzzleGame2':
case 'PuzzleGame3':
case 'PuzzleGame4':
case 'PuzzleGame5':
@@ -72,13 +70,14 @@ class InteractiveObject {
this.object.game = game;
break;
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.object = vp.object;
break;
case 'PuzzleGame1':
case 'PuzzleGame2':
case 'MazeQuizGame':
this.source = new MazeQuizGame(gameEngine, obj);
await this.source.load();
this.source = await new games[obj.type](gameEngine, obj);
this.object = this.source.object;
break;
}
@@ -91,9 +90,11 @@ class InteractiveObject {
const InteractiveObjectTypes = [
{
id: 'PuzzleGame1', name: 'Puzzle Game 1'
}, {
},{
id: 'PuzzleGame2', name: 'Puzzle Game 2'
},{
id: 'MazeQuizGame', name: 'Maze Quiz Game'
}, {
},{
id: 'VideoPlayer', name: 'Video Player'
}
];
@@ -144,7 +144,7 @@ class MazeObject {
};
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 => {
o[e] = mazeAsset.scene.getObjectByName(e);
//o[e].frustumCulled = false;
@@ -18,47 +18,46 @@ const tl = 4;
class MazeQuizGame {
constructor(engine, data) {
let questions = data.shuffle ? Utils.shuffleArray(data.questions) : data.questions;
let def = this.generate(questions);
this.mazeObject = new MazeObject(engine, def)
engine.addEventListener('collision', async e=>{
let ud1 = engine.physics.world.getCollider(e.handle1)?.parent()?.userData,
ud2 = engine.physics.world.getCollider(e.handle2)?.parent()?.userData;
let ud = {...ud1, ...ud2}
if (ud?.finish){
if (e.started){
engine.dashboard.updateProgress(1)
engine.hero.animationsMap._idle = engine.hero.animationsMap.idle
if ( engine.hero.animationsMap?.win){
engine.hero.animationsMap.idle = engine.hero.animationsMap.win
return new Promise(async (resolve, reject)=>{
let questions = data.shuffle ? Utils.shuffleArray(data.questions) : data.questions;
let def = this.generate(questions);
this.mazeObject = new MazeObject(engine, def)
engine.addEventListener('collision', async e=>{
let ud1 = engine.physics.world.getCollider(e.handle1)?.parent()?.userData,
ud2 = engine.physics.world.getCollider(e.handle2)?.parent()?.userData;
let ud = {...ud1, ...ud2}
if (ud?.finish){
if (e.started){
engine.dashboard.updateProgress(1)
engine.hero.animationsMap._idle = engine.hero.animationsMap.idle
if ( engine.hero.animationsMap?.win){
engine.hero.animationsMap.idle = engine.hero.animationsMap.win
}
await Utils.wait(1000);
engine.hero.characterControls.cameraDelta = Math.PI;
engine.hero.characterControls.direction += Math.PI;
//engine.hero.model.rotation.y += Math.PI;
await Utils.wait(10000);
this.onfinish?.()
}else{
engine.hero.animationsMap.idle = engine.hero.animationsMap._idle
engine.hero.characterControls.cameraDelta = 0
}
await Utils.wait(1000);
engine.hero.characterControls.cameraDelta = Math.PI;
engine.hero.characterControls.direction += Math.PI;
//engine.hero.model.rotation.y += Math.PI;
await Utils.wait(10000);
this.onfinish?.()
}else{
engine.hero.animationsMap.idle = engine.hero.animationsMap._idle
engine.hero.characterControls.cameraDelta = 0
}
}
if (ud.qid !== undefined && e.started){
engine.dashboard.update({
hint: ud.question.q
})
engine.dashboard.updateProgress(ud.qid / questions.length)
}
//console.log(e, ud, engine.hero?.animationsMap);
if (ud.qid !== undefined && e.started){
engine.dashboard.update({
hint: ud.question.q
})
engine.dashboard.updateProgress(ud.qid / questions.length)
}
//console.log(e, ud, engine.hero?.animationsMap);
})
await this.mazeObject.load();
this.object = this.mazeObject.object;
resolve(this)
})
}
async load(){
await this.mazeObject.load();
this.object = this.mazeObject.object;
return this;
}
generate(questions, qid = 0, len){
let question = questions[qid]
if (!question) return {
@@ -1,95 +1,99 @@
import { BoxGeometry, Mesh, MeshBasicMaterial, Group } from 'three';
import { TextureLoader } from 'three/src/loaders/TextureLoader';
import { MotionEngine } from '../../lib/MotionEngine';
class PuzzleGame1 {
constructor(context, image, w, h) {
this.object = new Group();
const aq = new MotionEngine();
const pr = [[0, -1], [0, 1], [1, 0], [-1, 0], [0, 0], [0, 2]];
let d = 1.2;
constructor(engine, data) {
return new Promise(async (resolve, reject)=>{
let w = data.w, h = data.h, wh = w*h;
this.object = new Group();
const aq = new MotionEngine();
const pr = [[0, -1], [0, 1], [1, 0], [-1, 0], [0, 0], [0, 2]];
let d = 1.2;
let bm = new BoxGeometry(1, 1, 1);
let uv = bm.getAttribute('uv');
let bm = new BoxGeometry(1, 1, 1);
let uv = bm.getAttribute('uv');
for (let i = 0; i < 6; i++) {
let s = [(i % w) / w, (i % h) / h];
//top left
uv.array[8 * i] = s[0];
uv.array[8 * i + 1] = s[1] + 1 / h;
//top right
uv.array[8 * i + 2] = s[0] + 1 / w;
uv.array[8 * i + 3] = s[1] + 1 / h;
//bottom left
uv.array[8 * i + 4] = s[0];
uv.array[8 * i + 5] = s[1];
//bottom right
uv.array[8 * i + 6] = s[0] + 1 / w;
uv.array[8 * i + 7] = s[1];
}
let material = new MeshBasicMaterial({
map: new TextureLoader().load(image)
});
//material.map.encoding = sRGBEncoding;
for (let i = 0; i < 6; i++) {
let b = bm.clone();
let mesh = new Mesh(b, material);
mesh.position.set((i % w) * d, (i % h) * d, 0);
let ri;
do {
ri = Math.floor(Math.random() * 6);
} while (ri == this.object.children.length);
mesh.rotation.set(pr[ri][0] * Math.PI / 2, pr[ri][1] * Math.PI / 2, 0);
mesh._ri = ri;
this.object.add(mesh);
}
this.object.children[0].onBeforeRender = () => {
this.update();
};
var check = () => {
if (!this.object.children.length) return false;
let i = 0;
for (let c of this.object.children) {
if (Math.abs(c.rotation.x - pr[i][0] * Math.PI / 2) > 0.0001 || Math.abs(c.rotation.y - pr[i][1] * Math.PI / 2) > 0.0001) return false;
i++;
for (let i = 0; i < wh; i++) {
let s = [(i % w) / w, Math.trunc(i / w) / h];
//top left
uv.array[8 * i] = s[0];
uv.array[8 * i + 1] = s[1] + 1 / h;
//top right
uv.array[8 * i + 2] = s[0] + 1 / w;
uv.array[8 * i + 3] = s[1] + 1 / h;
//bottom left
uv.array[8 * i + 4] = s[0];
uv.array[8 * i + 5] = s[1];
//bottom right
uv.array[8 * i + 6] = s[0] + 1 / w;
uv.array[8 * i + 7] = s[1];
}
return true;
};
let clickFn = (i) => {
if (!this.done && !aq.isActive(i.object)) {
i.object._ri = (i.object._ri + 1) % 6;
aq.add({
o: i.object,
a: { rotation: { x: pr[i.object._ri][0] * Math.PI / 2, y: pr[i.object._ri][1] * Math.PI / 2 } },
t: .5
});
let material = new MeshBasicMaterial({
map: await engine.loadTexture(data.$go.asset.name)
});
//material.map.encoding = sRGBEncoding;
for (let i = 0; i < wh; i++) {
let b = bm.clone();
let mesh = new Mesh(b, material);
mesh.position.set((i % w) * d, (Math.trunc(i / w)) * d, 0);
let ri;
do {
ri = Math.floor(Math.random() * 6);
} while (ri == this.object.children.length);
mesh.rotation.set(pr[ri][0] * Math.PI / 2, pr[ri][1] * Math.PI / 2, 0);
mesh._ri = ri;
this.object.add(mesh);
}
};
this.object.children.forEach(c => {
context.clickable.add(c, clickFn);
});
this.object.children[0].onBeforeRender = () => {
this.update();
};
this.update = () => {
aq.update();
if (aq.isIdle() && !this.done && check()) {
this.done = true;
this.object.children.forEach((c, i) => {
var check = () => {
if (!this.object.children.length) return false;
let i = 0;
for (let c of this.object.children) {
if (Math.abs(c.rotation.x - pr[i][0] * Math.PI / 2) > 0.0001 || Math.abs(c.rotation.y - pr[i][1] * Math.PI / 2) > 0.0001) return false;
i++;
}
return true;
};
let clickFn = (i) => {
if (!this.done && !aq.isActive(i.object)) {
i.object._ri = (i.object._ri + 1) % 6;
aq.add({
o: c,
a: { position: { x: i % w, y: i % h } },
t: 1,
f: i == 0 && this.onfinish
o: i.object,
a: { rotation: { x: pr[i.object._ri][0] * Math.PI / 2, y: pr[i.object._ri][1] * Math.PI / 2 } },
t: .5
});
});
//context.dashboard.addPoints(10);
}
};
}
};
this.object.children.forEach(c => {
engine.clickable.add(c, clickFn);
});
this.update = () => {
aq.update();
if (aq.isIdle() && !this.done && check()) {
this.done = true;
this.object.children.forEach((c, i) => {
aq.add({
o: c,
a: { position: { x: i % w, y: Math.trunc(i/w)} },
t: 1,
f: i == 0 && this.onfinish
});
});
//engine.dashboard.addPoints(10);
}
};
resolve(this);
})
}
}
@@ -1,8 +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 = 2;
this.modelValue.h = 3;
}
}
}
</script>
+131 -129
View File
@@ -1,145 +1,147 @@
import { BoxGeometry, Mesh, MeshBasicMaterial, Group } from 'three';
import { TextureLoader } from 'three/src/loaders/TextureLoader';
import { MotionEngine } from '../../lib/MotionEngine';
class PuzzleGame2 {
constructor(context, image, w, h) {
const texture = new TextureLoader().load(image);
//texture.encoding = sRGBEncoding;
const material = new MeshBasicMaterial({
map: texture
});
const aq = new MotionEngine();
const m2 = new MeshBasicMaterial({
map: texture,
transparent: true,
opacity: 0.37
});
let last, lidx = w - 1;
this.object = new Group();
let d = 1.2, p = [];
function check() {
if (p.length) {
for (let i = 0; i < p.length; i++) {
if (p[i] != i) return false;
}
return true;
}
return false;
}
function xch(x, y) {
let temp = p[x];
p[x] = p[y];
p[y] = temp;
}
this.shuffle = function () {
function getMoves() {
let m = [], pl = p[lidx];
let xl = pl % w, yl = ~~(pl / h);
if (xl > 0) m.push(p.indexOf(pl - 1));
if (xl < w - 1) m.push(p.indexOf(pl + 1));
if (yl > 0) m.push(p.indexOf(pl - w));
if (yl < h - 1) m.push(p.indexOf(pl + w));
return m;
}
for (let i = 0; i < w * h; i++) {
p[i] = i;
}
for (let iter = 0; iter < 73 * w * h; iter++) {
let m = getMoves();
xch(m[Math.floor(Math.random() * m.length)], lidx);
}
// while (p.length<9){
// let n = Math.floor(Math.random()*9);
// if (p.indexOf(n) == -1) p.push(n);
// }
p.forEach((e, i) => {
let x = e % w, y = ~~(e / h);
this.object.children[i].position.set(x * d, y * d, 0);
constructor(engine, data) {
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;
const material = new MeshBasicMaterial({
map: texture
});
};
const aq = new MotionEngine();
const m2 = new MeshBasicMaterial({
map: texture,
transparent: true,
opacity: 0.37
});
let last, lidx = w - 1;
for (let i = 0; i < w * h; i++) {
let x = i % w, xp = x / w;
let y = ~~(i / h), yp = y / h;
let bg = new BoxGeometry(1, 1, 1);
let uv = bg.getAttribute('uv');
for (let k = 0; k < 6; k++) {
//top left
uv.array[8 * k] = xp;
uv.array[8 * k + 1] = yp + 1 / h;
//top right
uv.array[8 * k + 2] = xp + 1 / w;
uv.array[8 * k + 3] = yp + 1 / h;
//bottom left
uv.array[8 * k + 4] = xp;
uv.array[8 * k + 5] = yp;
//bottom right
uv.array[8 * k + 6] = xp + 1 / w;
uv.array[8 * k + 7] = yp;
}
let mesh = new Mesh(bg, i != lidx ? material : m2);
mesh.position.set(x * d, y * d, 0);
this.object.add(mesh);
}
last = this.object.children[lidx];
this.object = new Group();
let d = 1.2, p = [];
this.object.children[0].onBeforeRender = () => {
this.update();
};
this.shuffle();
let clickFn = (i) => {
if (!this.done && !aq.isActive(i.object)) {
let idx = this.object.children.indexOf(i.object);
if (idx == lidx) return; //we ignore the empty cell
let xc = p[idx] % w, yc = ~~(p[idx] / h);
let xl = p[lidx] % w, yl = ~~(p[lidx] / h);
if (Math.abs(xc - xl) + Math.abs(yc - yl) == 1) {
aq.add({
o: i.object,
a: { position: { x: (xl - xc) * d, y: (yl - yc) * d } },
t: .3,
m: 'offset'
});
aq.add({
o: last,
a: { position: { x: (xc - xl) * d, y: (yc - yl) * d } },
t: .3,
m: 'offset'
});
xch(idx, lidx);
function check() {
if (p.length) {
for (let i = 0; i < p.length; i++) {
if (p[i] != i) return false;
}
return true;
}
return false;
}
};
this.object.children.forEach(c => {
context.clickable.add(c, clickFn);
});
function xch(x, y) {
let temp = p[x];
p[x] = p[y];
p[y] = temp;
}
this.update = () => {
aq.update();
if (aq.isIdle() && !this.done && check()) {
this.done = true;
this.object.children.forEach((c, i) => {
last.material = material;
aq.add({
o: c,
a: { position: { x: i % w, y: ~~(i / h) } },
t: 1,
f: i == 0 && this.onfinish
});
this.shuffle = function () {
function getMoves() {
let m = [], pl = p[lidx];
let xl = pl % w, yl = ~~(pl / h);
if (xl > 0) m.push(p.indexOf(pl - 1));
if (xl < w - 1) m.push(p.indexOf(pl + 1));
if (yl > 0) m.push(p.indexOf(pl - w));
if (yl < h - 1) m.push(p.indexOf(pl + w));
return m;
}
for (let i = 0; i < wh; i++) {
p[i] = i;
}
for (let iter = 0; iter < 73 * wh; iter++) {
let m = getMoves();
xch(m[Math.floor(Math.random() * m.length)], lidx);
}
// while (p.length<9){
// let n = Math.floor(Math.random()*9);
// if (p.indexOf(n) == -1) p.push(n);
// }
p.forEach((e, i) => {
let x = e % w, y = ~~(e / h);
this.object.children[i].position.set(x * d, y * d, 0);
});
//context.dashboard.addPoints(10);
}
};
};
for (let i = 0; i < wh; i++) {
let x = i % w, xp = x / w;
let y = ~~(i / h), yp = y / h;
let bg = new BoxGeometry(1, 1, 1);
let uv = bg.getAttribute('uv');
for (let k = 0; k < 6; k++) {
//top left
uv.array[8 * k] = xp;
uv.array[8 * k + 1] = yp + 1 / h;
//top right
uv.array[8 * k + 2] = xp + 1 / w;
uv.array[8 * k + 3] = yp + 1 / h;
//bottom left
uv.array[8 * k + 4] = xp;
uv.array[8 * k + 5] = yp;
//bottom right
uv.array[8 * k + 6] = xp + 1 / w;
uv.array[8 * k + 7] = yp;
}
let mesh = new Mesh(bg, i != lidx ? material : m2);
mesh.position.set(x * d, y * d, 0);
this.object.add(mesh);
}
last = this.object.children[lidx];
this.object.children[0].onBeforeRender = () => {
this.update();
};
this.shuffle();
let clickFn = (i) => {
if (!this.done && !aq.isActive(i.object)) {
let idx = this.object.children.indexOf(i.object);
if (idx == lidx) return; //we ignore the empty cell
let xc = p[idx] % w, yc = ~~(p[idx] / h);
let xl = p[lidx] % w, yl = ~~(p[lidx] / h);
if (Math.abs(xc - xl) + Math.abs(yc - yl) == 1) {
aq.add({
o: i.object,
a: { position: { x: (xl - xc) * d, y: (yl - yc) * d } },
t: .3,
m: 'offset'
});
aq.add({
o: last,
a: { position: { x: (xc - xl) * d, y: (yc - yl) * d } },
t: .3,
m: 'offset'
});
xch(idx, lidx);
}
}
};
this.object.children.forEach(c => {
engine.clickable.add(c, clickFn);
});
this.update = () => {
aq.update();
if (aq.isIdle() && !this.done && check()) {
this.done = true;
this.object.children.forEach((c, i) => {
last.material = material;
aq.add({
o: c,
a: { position: { x: i % w, y: ~~(i / h) } },
t: 1,
f: i == 0 && this.onfinish
});
});
//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 PuzzleGame1 from '../InteractiveObjects/PuzzleGame1.vue';
import PuzzleGame2 from '../InteractiveObjects/PuzzleGame2.vue';
import MazeQuizGame from '../InteractiveObjects/MazeQuizGame/MazeQuizGame.vue';
import GenericObject from '../InteractiveObjects/GenericObject.vue';
export default {
emits:['target', 'preview'],
components: { SvgIcon, VideoPlayer, PuzzleGame1, MazeQuizGame, GenericObject },
components: { SvgIcon, VideoPlayer, PuzzleGame1, PuzzleGame2, MazeQuizGame, GenericObject },
data(){
return {
active: false