objects locking system
This commit is contained in:
@@ -22,7 +22,7 @@
|
|||||||
<v-list nav>
|
<v-list nav>
|
||||||
<v-list-item prepend-icon="mdi-database" to="/game-objects/list" :title="l.gameObjects"></v-list-item>
|
<v-list-item prepend-icon="mdi-database" to="/game-objects/list" :title="l.gameObjects"></v-list-item>
|
||||||
<v-list-item prepend-icon="mdi-receipt-text-edit-outline" to="/scenarios/list" :title="l.gameScenarios"></v-list-item>
|
<v-list-item prepend-icon="mdi-receipt-text-edit-outline" to="/scenarios/list" :title="l.gameScenarios"></v-list-item>
|
||||||
<v-list-item prepend-icon="mdi-cogs" :title="l.gameRules"></v-list-item>
|
<!-- <v-list-item prepend-icon="mdi-cogs" :title="l.gameRules"></v-list-item> -->
|
||||||
<v-divider></v-divider>
|
<v-divider></v-divider>
|
||||||
<v-list-item prepend-icon="mdi-controller" :title="l.games" to="/games/list"></v-list-item>
|
<v-list-item prepend-icon="mdi-controller" :title="l.games" to="/games/list"></v-list-item>
|
||||||
<v-list-item prepend-icon="mdi-cog-play" :title="l.play" to="/play/list"></v-list-item>
|
<v-list-item prepend-icon="mdi-cog-play" :title="l.play" to="/play/list"></v-list-item>
|
||||||
|
|||||||
@@ -9,20 +9,6 @@ class GenericObject extends EventDispatcher{
|
|||||||
this.object = centerOrigin(this.source.scene)
|
this.object = centerOrigin(this.source.scene)
|
||||||
|
|
||||||
if (!data.exclude){
|
if (!data.exclude){
|
||||||
const bckGeometry = new SphereGeometry(getBoundingBoxMaxLength(this.object.userData.bbox)/2,8,8)
|
|
||||||
const bckMesh = new Mesh(bckGeometry, new MeshStandardMaterial({
|
|
||||||
transparent: true,
|
|
||||||
opacity:1, color: 0xaaaaaa,
|
|
||||||
alphaMap: await engine.loadTexture('locked.webp', '/static/textures/'),
|
|
||||||
}))
|
|
||||||
this.object.add(bckMesh)
|
|
||||||
engine.motionQueue.add(
|
|
||||||
{ o: bckMesh, a:{
|
|
||||||
material: { opacity: k=>0.11 + 0.52*Math.sin(k*Math.PI)},
|
|
||||||
rotation: { y: k=>k*Math.PI*2},
|
|
||||||
scale: (k,s)=>s.setScalar(1+0.11*Math.sin(2*k*Math.PI))
|
|
||||||
}, r: true, t: 3 },
|
|
||||||
)
|
|
||||||
engine.clickable.add(this.object, async e=>{
|
engine.clickable.add(this.object, async e=>{
|
||||||
this.object._active = !this.object._active;
|
this.object._active = !this.object._active;
|
||||||
if (engine.dashboard){
|
if (engine.dashboard){
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Group, EventDispatcher } from "three";
|
import { Group, EventDispatcher, MeshStandardMaterial, Mesh, SphereGeometry } from "three";
|
||||||
|
|
||||||
import { GenericObject } from "./GenenricObject";
|
import { GenericObject } from "./GenenricObject";
|
||||||
import { TextObject } from "./TextObject";
|
import { TextObject } from "./TextObject";
|
||||||
@@ -13,7 +13,8 @@ import { PuzzleGame4 } from "./PuzzleGame4";
|
|||||||
// import { Game6 } from "./games/Game6";
|
// import { Game6 } from "./games/Game6";
|
||||||
import { MazeQuizGame } from "./MazeQuizGame/MazeQuizGame";
|
import { MazeQuizGame } from "./MazeQuizGame/MazeQuizGame";
|
||||||
import { Particles } from "./Particles";
|
import { Particles } from "./Particles";
|
||||||
import { assignMaterial, assignParams } from "@/lib/MeshUtils";
|
import { assignMaterial, assignParams, wrapInGroup, getBoundingBoxMaxLength } from "@/lib/MeshUtils";
|
||||||
|
import { GameEngine } from "@/lib/GameEngine";
|
||||||
|
|
||||||
const InteractiveObjectsImports = {
|
const InteractiveObjectsImports = {
|
||||||
GenericObject, CharacterObject, TextObject, ImageObject, VideoPlayer, Particles,
|
GenericObject, CharacterObject, TextObject, ImageObject, VideoPlayer, Particles,
|
||||||
@@ -72,12 +73,52 @@ class InteractiveObject extends EventDispatcher{
|
|||||||
this.io.addEventListener?.('finish', this.dispatchEvent.bind(this))
|
this.io.addEventListener?.('finish', this.dispatchEvent.bind(this))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (obj.shouldBeLocked){
|
||||||
|
this.object = wrapInGroup(this.object)
|
||||||
|
this.locker = new Locker(gameEngine, this.object);
|
||||||
|
this.locker.lock();
|
||||||
|
}
|
||||||
assignParams(this.object, obj);
|
assignParams(this.object, obj);
|
||||||
resolve(this);
|
resolve(this);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Locker{
|
||||||
|
static materialLocked = new MeshStandardMaterial({
|
||||||
|
transparent: true, opacity:1, color: 0xaaaaaa
|
||||||
|
})
|
||||||
|
constructor(engine, group){
|
||||||
|
const bckGeometry = new SphereGeometry(getBoundingBoxMaxLength(group.userData.bbox)/2,8,8)
|
||||||
|
const bckMesh = new Mesh(bckGeometry, Locker.materialLocked);
|
||||||
|
bckMesh.visible = false;
|
||||||
|
group.add(bckMesh)
|
||||||
|
function animate(){
|
||||||
|
engine.motionQueue.add(
|
||||||
|
{ o: bckMesh, a:{
|
||||||
|
material: { opacity: k=>0.11 + 0.52*Math.sin(k*Math.PI)},
|
||||||
|
rotation: { y: k=>k*Math.PI*2},
|
||||||
|
scale: (k,s)=>s.setScalar(1+0.11*Math.sin(2*k*Math.PI))
|
||||||
|
}, r: true, t: 3 },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
this.object = bckMesh;
|
||||||
|
this.lock = function(){
|
||||||
|
bckMesh.visible = true;
|
||||||
|
//bckMesh.material.needsUpdate = true;
|
||||||
|
engine.motionQueue.clear(bckMesh);
|
||||||
|
group.__locked = true;
|
||||||
|
animate();
|
||||||
|
}
|
||||||
|
this.unlock = function(){
|
||||||
|
bckMesh.visible = false;
|
||||||
|
engine.motionQueue.clear(bckMesh);
|
||||||
|
group.__locked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GameEngine.loadTexture('locked.webp', '/static/textures/', null, [Locker.materialLocked, 'alphaMap'])
|
||||||
|
|
||||||
const InteractiveObjectTypes = [
|
const InteractiveObjectTypes = [
|
||||||
{
|
{
|
||||||
id: 'CharacterObject', name: 'Character'
|
id: 'CharacterObject', name: 'Character'
|
||||||
|
|||||||
@@ -47,7 +47,8 @@ export default {
|
|||||||
mounted(){
|
mounted(){
|
||||||
this.modelValue.questions ??= [];
|
this.modelValue.questions ??= [];
|
||||||
this.modelValue.questionPoints ??= 10;
|
this.modelValue.questionPoints ??= 10;
|
||||||
this.modelValue.questionPenalty ??= 0
|
this.modelValue.questionPenalty ??= 0;
|
||||||
|
this.modelValue.exclude = true;
|
||||||
},
|
},
|
||||||
methods:{
|
methods:{
|
||||||
addQuestion(){
|
addQuestion(){
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class VideoPlayer extends EventDispatcher {
|
|||||||
//material.opacity = 0.5;
|
//material.opacity = 0.5;
|
||||||
if (data.playInHud){
|
if (data.playInHud){
|
||||||
engine.dashboard?.detach(plane, {
|
engine.dashboard?.detach(plane, {
|
||||||
skipTransition: !data.skipTransition
|
skipTransition: data.skipTransition
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -24,7 +24,13 @@ class Clickable {
|
|||||||
this.update = function (mouse, camera, event) {
|
this.update = function (mouse, camera, event) {
|
||||||
raycaster.setFromCamera(mouse, camera);
|
raycaster.setFromCamera(mouse, camera);
|
||||||
let forExecute = [];
|
let forExecute = [];
|
||||||
objects.forEach(o => {
|
objects.filter(o=>{
|
||||||
|
do {
|
||||||
|
if (o.__locked) return false;
|
||||||
|
o = o.parent;
|
||||||
|
} while (o);
|
||||||
|
return true;
|
||||||
|
}).forEach(o => {
|
||||||
o.getWorldPosition(v);
|
o.getWorldPosition(v);
|
||||||
if (camera.position.distanceTo(v) <= o._clickable.distance && o.visible) {
|
if (camera.position.distanceTo(v) <= o._clickable.distance && o.visible) {
|
||||||
const intersects = raycaster.intersectObject(o);
|
const intersects = raycaster.intersectObject(o);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
|
|
||||||
import { Text } from "troika-three-text";
|
import { Text } from "troika-three-text";
|
||||||
class DashBoard {
|
class DashBoard {
|
||||||
|
#points = 0;
|
||||||
constructor(engine) {
|
constructor(engine) {
|
||||||
let svg = p=>`<?xml version="1.0" encoding="UTF-8"?>
|
let svg = p=>`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
|
||||||
@@ -25,7 +26,6 @@ class DashBoard {
|
|||||||
let updating = false;
|
let updating = false;
|
||||||
let params = {}
|
let params = {}
|
||||||
let occupied = false;
|
let occupied = false;
|
||||||
let points = 0;
|
|
||||||
|
|
||||||
img.addEventListener('load', function () {
|
img.addEventListener('load', function () {
|
||||||
ctx.drawImage(img, 0, 0, engine.w, engine.h);
|
ctx.drawImage(img, 0, 0, engine.w, engine.h);
|
||||||
@@ -152,13 +152,13 @@ class DashBoard {
|
|||||||
this.levelProgress = levelProgress;
|
this.levelProgress = levelProgress;
|
||||||
|
|
||||||
this.addPoints = function(p){
|
this.addPoints = function(p){
|
||||||
points += p;
|
this.#points += p;
|
||||||
engine.motionQueue.add({
|
engine.motionQueue.add({
|
||||||
o: pointsText,
|
o: pointsText,
|
||||||
a: {rotation:{y: Math.PI}, scale:{x:1.5, y:1.5, z:1.5}},
|
a: {rotation:{y: Math.PI}, scale:{x:1.5, y:1.5, z:1.5}},
|
||||||
t: 0.25,
|
t: 0.25,
|
||||||
f:()=>{
|
f:()=>{
|
||||||
pointsText.text = points;
|
pointsText.text = this.#points;
|
||||||
pointsText.sync();
|
pointsText.sync();
|
||||||
engine.motionQueue.add({
|
engine.motionQueue.add({
|
||||||
o: pointsText,
|
o: pointsText,
|
||||||
@@ -242,13 +242,11 @@ class DashBoard {
|
|||||||
object._hud.parent?.attach(object);
|
object._hud.parent?.attach(object);
|
||||||
hud.rotation.y = 0;
|
hud.rotation.y = 0;
|
||||||
hud.visible = false;
|
hud.visible = false;
|
||||||
if (!opts.skipTransition){
|
|
||||||
engine.motionQueue.add({
|
engine.motionQueue.add({
|
||||||
o: object,
|
o: object,
|
||||||
a: object._hud.placement,
|
a: object._hud.placement,
|
||||||
t: 1
|
t: opts.skipTransition ? 0 : 1
|
||||||
});
|
});
|
||||||
}
|
|
||||||
delete object._hud;
|
delete object._hud;
|
||||||
occupied = false;
|
occupied = false;
|
||||||
hudAnimation = null;
|
hudAnimation = null;
|
||||||
@@ -268,6 +266,10 @@ class DashBoard {
|
|||||||
set active(v){
|
set active(v){
|
||||||
this.group.visible = v;
|
this.group.visible = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get points(){
|
||||||
|
return this.#points;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProgressBar{
|
class ProgressBar{
|
||||||
|
|||||||
+19
-12
@@ -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';
|
||||||
|
|
||||||
|
THREE.Cache.enabled = true
|
||||||
|
|
||||||
const assetPath = '/asset/default/';
|
const assetPath = '/asset/default/';
|
||||||
|
|
||||||
class GameEngine extends THREE.EventDispatcher{
|
class GameEngine extends THREE.EventDispatcher{
|
||||||
@@ -161,7 +163,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 GameEngine.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;
|
||||||
@@ -415,18 +417,8 @@ class GameEngine extends THREE.EventDispatcher{
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadTexture(url, path = assetPath, progress) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
new THREE.TextureLoader().load(`${path}${url}`, texture => {
|
|
||||||
//texture.encoding = THREE.sRGBEncoding;
|
|
||||||
texture.colorSpace = THREE.SRGBColorSpace;
|
|
||||||
resolve(texture)
|
|
||||||
}, progress, reject)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadPanorama(url, path = assetPath) {
|
async loadPanorama(url, path = assetPath) {
|
||||||
let t = await this.loadTexture(url, path);
|
let t = await GameEngine.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;
|
||||||
@@ -567,6 +559,21 @@ class GameEngine extends THREE.EventDispatcher{
|
|||||||
this.clickable.removeAll();
|
this.clickable.removeAll();
|
||||||
this.motionQueue.clearAll();
|
this.motionQueue.clearAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static textureLoader = new THREE.TextureLoader();
|
||||||
|
static async loadTexture(url, path = assetPath, progress, assignTo) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
GameEngine.textureLoader.load(`${path}${url}`, texture => {
|
||||||
|
texture.colorSpace = THREE.SRGBColorSpace;
|
||||||
|
if (assignTo){
|
||||||
|
assignTo[0][assignTo[1]] = texture;
|
||||||
|
}
|
||||||
|
resolve(texture)
|
||||||
|
}, progress, reject)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
loadTexture = GameEngine.loadTexture
|
||||||
}
|
}
|
||||||
|
|
||||||
export { GameEngine }
|
export { GameEngine }
|
||||||
+14
-6
@@ -55,16 +55,24 @@ function autoScale(object, mk = 1) {
|
|||||||
object.scale.multiplyScalar(mk / k);
|
object.scale.multiplyScalar(mk / k);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function wrapInGroup(object){
|
||||||
|
if (object.isWrapper) return object;
|
||||||
|
let group = new Group();
|
||||||
|
group.userData.bbox = getBoundingBox(object);
|
||||||
|
group.add(object);
|
||||||
|
group.userData.object = object;
|
||||||
|
group.isWrapper = true;
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
function centerOrigin(object){
|
function centerOrigin(object){
|
||||||
let result = new Group();
|
let group = wrapInGroup(object);
|
||||||
result.userData.bbox = getBoundingBox(object);
|
let position = getBoundingBoxCenterPoint(group.userData.bbox, object.position).negate();
|
||||||
let position = getBoundingBoxCenterPoint(result.userData.bbox, object.position).negate();
|
|
||||||
object.position.copy(position)
|
object.position.copy(position)
|
||||||
result.add(object);
|
return group;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
assignParams, assignMaterial, autoScale, centerOrigin,
|
assignParams, assignMaterial, autoScale, centerOrigin, wrapInGroup,
|
||||||
getBoundingBox, getBoundingBoxSize, getBoundingBoxMaxLength, getBoundingBoxCenterPoint
|
getBoundingBox, getBoundingBoxSize, getBoundingBoxMaxLength, getBoundingBoxCenterPoint
|
||||||
}
|
}
|
||||||
@@ -146,7 +146,13 @@ export default {
|
|||||||
if (this.scene.data.items){
|
if (this.scene.data.items){
|
||||||
let loaded = 0;
|
let loaded = 0;
|
||||||
for (let i of this.scene.data.items) {
|
for (let i of this.scene.data.items) {
|
||||||
|
if (this.env == 'GamePlaying'){
|
||||||
|
if (i.data.activationTriggers?.length || i.data.activationScore){
|
||||||
|
i.data.shouldBeLocked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
let io = await new InteractiveObject(gameEngine, i.data)
|
let io = await new InteractiveObject(gameEngine, i.data)
|
||||||
|
i.__io = io;
|
||||||
this.setObjectAttributes(l, i.data, io.object, io.source, 1);
|
this.setObjectAttributes(l, i.data, io.object, io.source, 1);
|
||||||
gameEngine.activeObjects.add(io.object);
|
gameEngine.activeObjects.add(io.object);
|
||||||
if (this.env == 'GamePlaying'){
|
if (this.env == 'GamePlaying'){
|
||||||
@@ -167,8 +173,20 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
io.addEventListener('finish', ()=>{
|
io.addEventListener('finish', ()=>{
|
||||||
|
if (!i.data.pointsGiven){
|
||||||
gameEngine.dashboard?.addPoints(i.data.points)
|
gameEngine.dashboard?.addPoints(i.data.points)
|
||||||
i.data.points = 0;
|
}
|
||||||
|
i.data.pointsGiven = true;
|
||||||
|
this.scene.data.items.forEach(di=>{
|
||||||
|
if (di.data.activationTriggers?.includes(i.data.id)){
|
||||||
|
let idx = di.data.activationTriggers.indexOf(i.data.id)
|
||||||
|
di.data.activationTriggers.splice(idx, 1);
|
||||||
|
}
|
||||||
|
if (!di.data.activationTriggers?.length && di.__io.object.__locked &&
|
||||||
|
gameEngine.dashboard.points > di.data.activationScore){
|
||||||
|
di.__io.locker.unlock();
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
loaded += 1/this.scene.data.items.length
|
loaded += 1/this.scene.data.items.length
|
||||||
@@ -189,12 +207,6 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
gameEngine.dashboard?.loading(1)
|
gameEngine.dashboard?.loading(1)
|
||||||
// let camera = new gameEngine.$.PerspectiveCamera();
|
|
||||||
// let cameraHelper = new gameEngine.$.CameraHelper(camera);
|
|
||||||
// gameEngine.activeObjects.add(cameraHelper);
|
|
||||||
// gameEngine.activeObjects.add(camera);
|
|
||||||
// this.setObjectAttributes(l, { id: 'camera', 'title': 'Main camera' }, { scene: camera })
|
|
||||||
// cameraHelper.update();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
targetPointerDown(){
|
targetPointerDown(){
|
||||||
|
|||||||
Reference in New Issue
Block a user