classic puzzle v1
This commit is contained in:
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -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'
|
||||
},{
|
||||
|
||||
@@ -43,12 +43,12 @@
|
||||
|
||||
<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';
|
||||
@@ -56,7 +56,10 @@ import OffsetLine from './OffsetLine.vue';
|
||||
|
||||
export default {
|
||||
emits:['target', 'preview'],
|
||||
components: { SvgIcon, OffsetLine, GenericObject, CharacterObject, VideoPlayer, PuzzleGame1, PuzzleGame2, MazeQuizGame, Particles, },
|
||||
components: {
|
||||
SvgIcon, OffsetLine, GenericObject, CharacterObject, VideoPlayer,
|
||||
PuzzleGame1, PuzzleGame2, MazeQuizGame, Particles, ClassicPuzzle
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
active: false
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { Raycaster, Vector3 } from "three"
|
||||
|
||||
class Draggable{
|
||||
constructor(defaultDistance){
|
||||
const objects = [];
|
||||
const raycaster = new Raycaster();
|
||||
let v = new Vector3;
|
||||
let dragging = null;
|
||||
this.add = function(object, dragZone, fn, distance){
|
||||
objects.push(object);
|
||||
object._draggable = {fn, dragZone, distance: distance || defaultDistance}
|
||||
}
|
||||
|
||||
this.remove = function(object){
|
||||
delete object._draggable;
|
||||
objects.splice(objects.indexOf(object), 1);
|
||||
}
|
||||
|
||||
this.update = function(pointer, camera, action){
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
if (action == 'start'){
|
||||
let forExecute = [];
|
||||
objects.forEach(o=>{
|
||||
o.getWorldPosition(v);
|
||||
if (camera.position.distanceTo(v) <= o._draggable.distance && o.visible){
|
||||
const intersects = raycaster.intersectObject(o);
|
||||
if (intersects[0]) forExecute.push({o, i:intersects[0]})
|
||||
}
|
||||
});
|
||||
|
||||
if (forExecute[0]) {
|
||||
let s = forExecute.sort((a,b)=>a.i.distance-b.i.distance)[0];
|
||||
s.o._draggable.fn.start && s.o._draggable.fn.start(s.i);
|
||||
dragging = s;
|
||||
dragging.zone = raycaster.intersectObject(s.o._draggable.dragZone)[0];
|
||||
}
|
||||
}else if (action == 'end' && dragging){
|
||||
dragging.o._draggable.fn.end && dragging.o._draggable.fn.end(dragging);
|
||||
dragging = null;
|
||||
}else if(action == 'drag' && dragging){
|
||||
const intersect = raycaster.intersectObject(dragging.o._draggable.dragZone)[0];
|
||||
if (intersect?.uv && dragging.zone?.uv){
|
||||
dragging.o.position.x += -4*(dragging.zone.uv.x - intersect.uv.x);
|
||||
dragging.o.position.y += 4*(dragging.zone.uv.y - intersect.uv.y);
|
||||
dragging.o._draggable.fn.drag && dragging.o._draggable.fn.drag(dragging);
|
||||
dragging.zone = intersect;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { Draggable }
|
||||
@@ -15,6 +15,7 @@ import { Physics } from './Physics.js';
|
||||
import { Clickable } from './Clickable.js';
|
||||
import { DashBoard } from './Dashboard.js';
|
||||
import { MotionEngine } from './MotionEngine.js';
|
||||
import { Draggable } from './Draggable.js';
|
||||
|
||||
THREE.Cache.enabled = true
|
||||
|
||||
@@ -209,6 +210,7 @@ class GameEngine extends THREE.EventDispatcher{
|
||||
}
|
||||
|
||||
this.clickable = new Clickable(20);
|
||||
this.draggable = new Draggable(20);
|
||||
}
|
||||
|
||||
initXrControllers() {
|
||||
@@ -485,6 +487,11 @@ class GameEngine extends THREE.EventDispatcher{
|
||||
this.hero?.characterControls?.idleReset();
|
||||
}
|
||||
|
||||
onPointer(mouseEvent, domElement, type){
|
||||
let mouse = this.getMouseVector(mouseEvent, domElement);
|
||||
this.draggable?.update(mouse, this.camera, type);
|
||||
}
|
||||
|
||||
setCamera(camera) {
|
||||
//camera.updateProjectionMatrix();
|
||||
this.camera = camera;
|
||||
|
||||
@@ -123,6 +123,8 @@ export default {
|
||||
await this.expandScenarioData(scene);
|
||||
gameEngine.dashboard?.loading(0.1);
|
||||
|
||||
gameEngine.orbitControls.enableRotate = this.env == 'GameDesigner'
|
||||
|
||||
//this is needed cause when mounted canvas has different size
|
||||
this.resize();
|
||||
target.objects = target.objects || {};
|
||||
@@ -212,6 +214,7 @@ export default {
|
||||
gameEngine.activeObjects.add(intro.object);
|
||||
intro.video.addEventListener('pause',()=>{
|
||||
intro.object.removeFromParent();
|
||||
gameEngine.clickable.remove(intro.object); //TODO!!!!
|
||||
gameEngine.activeObjects.visible = true;
|
||||
});
|
||||
intro.video.play();
|
||||
@@ -244,6 +247,10 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
targetPointer(e, t){
|
||||
gameEngine.onPointer(e, this.$refs.target, t);
|
||||
},
|
||||
|
||||
setObjectAttributes(l, data, object, source, autoScaleFactor = 1){
|
||||
if (l[data.id]){
|
||||
['position', 'scale', 'rotation'].forEach(p=>{
|
||||
|
||||
Reference in New Issue
Block a user