new interactive object - single question

This commit is contained in:
2026-02-02 17:57:21 +02:00
parent 19ee1583bb
commit 136daa36ac
4 changed files with 92 additions and 2 deletions
@@ -13,6 +13,7 @@ import { PuzzleGame2 } from "./PuzzleGame2";
import { PuzzleGame4 } from "./PuzzleGame4";
import { ClassicPuzzle } from "./ClassicPuzzle";
import { PairMatchingGame } from "./PairMatchingGame";
import { SingleQuestion } from "./SingleQuestion";
// import { Game5 } from "./games/Game5";
// import { Game6 } from "./games/Game6";
import { MazeQuizGame } from "./MazeQuizGame/MazeQuizGame";
@@ -23,7 +24,7 @@ import { GameEngine } from "@/lib/GameEngine";
const InteractiveObjectsImports = {
GenericObject, CharacterObject, TextObject, ImageObject, GltfObject, VideoPlayer, Particles, SceneSwitcher,
PuzzleGame1, PuzzleGame2, PuzzleGame4, MazeQuizGame, ClassicPuzzle, PairMatchingGame
PuzzleGame1, PuzzleGame2, PuzzleGame4, MazeQuizGame, ClassicPuzzle, PairMatchingGame, SingleQuestion
};
class InteractiveObject extends EventManager{
@@ -52,6 +53,7 @@ class InteractiveObject extends EventManager{
case 'MazeQuizGame':
case 'ClassicPuzzle':
case 'PairMatchingGame':
case 'SingleQuestion':
case 'Particles':
case 'SceneSwitcher':
this.io = await new InteractiveObjectsImports[obj.type](engine, obj);
@@ -165,6 +167,8 @@ const InteractiveObjectTypes = [
id: 'ClassicPuzzle', name: 'Classic Puzzle Game'
},{
id: 'PairMatchingGame', name: 'Pair Matching Game'
},{
id: 'SingleQuestion', name: 'Single Question'
},{
id: 'VideoPlayer', name: 'Video Player'
},{
@@ -0,0 +1,45 @@
import { Group, Color } from 'three';
import { centerOrigin } from '@/lib/MeshUtils';
import { EventManager } from '@/lib/EventManager';
import { TextObject } from './TextObject';
import Utils from '@/lib/Utils';
class SingleQuestion extends EventManager {
emits = ['finish']
constructor(engine, data) {
super();
return new Promise(async (resolve, reject)=>{
const container = new Group();
const question = await new TextObject(engine, { text: data.q, fontSize: 0.125 });
const answers = new Group();
let ca = data.a[0];
let ans = Utils.shuffleArray(data.a.filter(e=>!!e));
for (let i=0; i<ans.length; i++){
let a = ans[i];
let qa = await new TextObject(engine, { text: `${i+1}). ${a}`, fontSize: 0.1 });
qa.object.position.y = -0.15 * (i + 1);
answers.add(qa.object);
qa.object._answer = a;
engine.clickable.add(qa.object, (i) => {
//if (!container.visible) return;
if (qa.object._answer == ca) {
this.dispatchEvent({type:'finish'})
qa.object.outlineColor = new Color(0x55ff55);
answers.children.forEach(c => engine.clickable.remove(c));
} else {
qa.object.outlineColor = new Color(0xff5555);
setTimeout(() => {
qa.object.outlineColor = new Color(0xffffff);
}, 1000);
}
});
}
container.add(question.object);
container.add(answers);
this.object = centerOrigin(container);
resolve(this);
})
}
}
export { SingleQuestion }
@@ -0,0 +1,40 @@
<template>
<div v-if="modelValue.go">
<v-text-field hide-details density="compact" v-model="modelValue.q" class="pb-2" label="Question" icon-color="blue" prepend-icon="mdi-chat-question-outline"></v-text-field>
<v-text-field hide-details density="compact" v-model="modelValue.a[0]" class="pb-2" label="Correct answer" icon-color="success" prepend-icon="mdi-check"></v-text-field>
<v-text-field v-for="i in 4" :key="i" hide-details density="compact" v-model="modelValue.a[i]"
v-show="modelValue.a[i-1]" class="pb-2" :label="`Wrong answer #${i}`" icon-color="error"
prepend-icon="mdi-close"></v-text-field>
<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-cube-outline" color="deep-orange-darken-4" block>Choose 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.a = [];
}
}
}
</script>
+2 -1
View File
@@ -52,6 +52,7 @@ import PuzzleGame2 from '../InteractiveObjects/PuzzleGame2.vue';
import MazeQuizGame from '../InteractiveObjects/MazeQuizGame/MazeQuizGame.vue';
import ClassicPuzzle from '../InteractiveObjects/ClassicPuzzle.vue';
import PairMatchingGame from '../InteractiveObjects/PairMatchingGame.vue';
import SingleQuestion from '../InteractiveObjects/SingleQuestion.vue';
import Particles from '../InteractiveObjects/Particles.vue';
import GenericObject from '../InteractiveObjects/GenericObject.vue';
import CharacterObject from '../InteractiveObjects/CharacterObject.vue';
@@ -62,7 +63,7 @@ import { InteractiveObjectTypes } from '../InteractiveObjects/InteractiveObject'
const components = {
SvgIcon, OffsetLine, GenericObject, CharacterObject, VideoPlayer, SceneSwitcher,
PuzzleGame1, PuzzleGame2, MazeQuizGame, Particles, ClassicPuzzle, PairMatchingGame
PuzzleGame1, PuzzleGame2, MazeQuizGame, Particles, ClassicPuzzle, PairMatchingGame, SingleQuestion
};
export default {