classic puzzle v1

This commit is contained in:
2025-11-14 18:41:57 +02:00
parent 8eea84b697
commit 3d86e2574a
10 changed files with 2754 additions and 5 deletions
+5 -1
View File
@@ -20,7 +20,11 @@
<v-btn icon="mdi-walk" @click="control"></v-btn>
</v-navigation-drawer>
<div class="container my-3 position-relative game-designer-canvas">
<div ref="target" @click="targetClick" @pointerdown="targetPointerDown"></div>
<div ref="target" @click="targetClick"
@mousedown="targetPointer($event, 'start')"
@mousemove="targetPointer($event, 'drag')"
@mouseup="targetPointer($event, 'end')"
@pointerdown="targetPointerDown"></div>
</div>
<!-- <v-toolbar density="compact">
<v-slide-group show-arrows>
@@ -0,0 +1,75 @@
import { Color, Group, EventDispatcher, DoubleSide } from "three"
import { centerOrigin } from "@/lib/MeshUtils";
class ClassicPuzzle extends EventDispatcher {
emits = ['finish']
constructor(engine, data, gltfName, objPrefix='Plane'){
super();
const container = new Group();
const that = this;
return new Promise(async (resolve, reject)=>{
let gltf = await engine.load('puzzle-5x4/puzzle-5x4.gltf', '/static/meshes/');
let dragZone = gltf.scene.getObjectByName('DragZone');
dragZone.material.side = DoubleSide;
let eventsFn= {
start(){},
drag(){},
end(e){
if (Math.abs(e.o.position.x)<.1 && Math.abs(e.o.position.y)<.1){
e.o.position.set(0,0,0);
engine.draggable.remove(e.o);
e.o.material = doneMaterial;
done++;
if (done == pCount){
doneMaterial.emissiveIntensity = .5;
engine.motionQueue.add({
o: doneMaterial,
t:1.5,
d:1,
a:{ emissiveIntensity:0 },
f:()=>{
that.dispatchEvent({type:'finish'})
}
});
}
}
}
}
let pCount = 0;
dragZone.visible = false;
gltf.scene.children.forEach((o, i)=>{
if (o.name.startsWith(objPrefix)){
let pp = o.clone();
container.add(pp);
pp.position.set(2*Math.random()-1, 2*Math.random() - 1, 0.01*(i+1));
engine.draggable.add(pp, dragZone, eventsFn);
pCount++;
}
});
let defaultMaterial = container.children[0].material;
defaultMaterial.emissiveIntensity=.05
let doneMaterial = defaultMaterial.clone();
doneMaterial.emissive = new Color(10,114,10);
defaultMaterial.emissive = new Color(114,10,10);
engine.motionQueue.add(
{
o: defaultMaterial,
r: true,
t:1.5,
d:1,
rd:true,
a:{emissiveIntensity:function(k){return .05*Math.sin(k*3.14);}}
});
let done0 = container.children[Math.floor(Math.random() * container.children.length)];
let done = 1;
done0.material = doneMaterial;
done0.position.set(0,0,0);
engine.draggable.remove(done0)
container.add(dragZone);
this.object = centerOrigin(container);
resolve(this);
})
}
}
export { ClassicPuzzle }
@@ -0,0 +1,38 @@
<template>
<div v-if="modelValue.go">
<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>
</div>
<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>
@@ -9,6 +9,7 @@ import { PuzzleGame1 } from "./PuzzleGame1";
import { PuzzleGame2 } from "./PuzzleGame2";
// import { Game3 } from "./games/Game3";
import { PuzzleGame4 } from "./PuzzleGame4";
import { ClassicPuzzle } from "./ClassicPuzzle";
// import { Game5 } from "./games/Game5";
// import { Game6 } from "./games/Game6";
import { MazeQuizGame } from "./MazeQuizGame/MazeQuizGame";
@@ -18,7 +19,7 @@ import { GameEngine } from "@/lib/GameEngine";
const InteractiveObjectsImports = {
GenericObject, CharacterObject, TextObject, ImageObject, VideoPlayer, Particles,
PuzzleGame1, PuzzleGame2, PuzzleGame4, MazeQuizGame
PuzzleGame1, PuzzleGame2, PuzzleGame4, MazeQuizGame, ClassicPuzzle
};
class InteractiveObject extends EventDispatcher{
@@ -66,6 +67,7 @@ class InteractiveObject extends EventDispatcher{
case 'PuzzleGame1':
case 'PuzzleGame2':
case 'MazeQuizGame':
case 'ClassicPuzzle':
case 'Particles':
this.io = await new InteractiveObjectsImports[obj.type](gameEngine, obj);
this.source = this.io.source || this.io;
@@ -131,6 +133,8 @@ const InteractiveObjectTypes = [
id: 'PuzzleGame2', name: 'Puzzle Game 2'
},{
id: 'MazeQuizGame', name: 'Maze Quiz Game'
},{
id: 'ClassicPuzzle', name: 'Classic Puzzle Game'
},{
id: 'VideoPlayer', name: 'Video Player'
},{
+6 -3
View File
@@ -43,20 +43,23 @@
<script>
import SvgIcon from './SvgIcon.vue';
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 ClassicPuzzle from '../InteractiveObjects/ClassicPuzzle.vue';
import Particles from '../InteractiveObjects/Particles.vue';
import GenericObject from '../InteractiveObjects/GenericObject.vue';
import CharacterObject from '../InteractiveObjects/CharacterObject.vue';
import OffsetLine from './OffsetLine.vue';
export default {
emits:['target', 'preview'],
components: { SvgIcon, OffsetLine, GenericObject, CharacterObject, VideoPlayer, PuzzleGame1, PuzzleGame2, MazeQuizGame, Particles, },
emits:['target', 'preview'],
components: {
SvgIcon, OffsetLine, GenericObject, CharacterObject, VideoPlayer,
PuzzleGame1, PuzzleGame2, MazeQuizGame, Particles, ClassicPuzzle
},
data(){
return {
active: false