diff --git a/backend/app/Db.js b/backend/app/Db.js
index 27587dd..672c22a 100644
--- a/backend/app/Db.js
+++ b/backend/app/Db.js
@@ -26,7 +26,7 @@ class Db {
try {
dbo = db.db(app.config.db.name);
this.instance = dbo;
- for (let c of ['users', 'user_sessions', 'history', 'log', 'assets', 'scenarios']){
+ for (let c of ['users', 'user_sessions', 'history', 'log', 'assets', 'scenarios', 'games']){
try {
await dbo.createCollection(c);
}catch(err){}
diff --git a/backend/app/bl/GamesManager.js b/backend/app/bl/GamesManager.js
index c072f40..69402d6 100644
--- a/backend/app/bl/GamesManager.js
+++ b/backend/app/bl/GamesManager.js
@@ -1,9 +1,10 @@
+const collection = 'games';
/**
* Games manager class
*/
class GamesManager{
- name = 'games';
+ name = 'game';
/**
* Class initializer, инициализация на плъгин
@@ -18,7 +19,9 @@ class GamesManager{
* @param {Game} data the game description, дефиниция на играта
*/
this.create = async function(ctx, data){
-
+ data.id = (await db.getLastId(collection)) + 1;
+ await db.create(collection, data);
+ return data;
}
/**
@@ -27,7 +30,8 @@ class GamesManager{
* @returns {Game} the game, игрова дефиниция
*/
this.read = async function(id){
-
+ id = parseInt(id);
+ return await db.get(collection, {id});
}
/**
@@ -36,7 +40,11 @@ class GamesManager{
* @param {Game} data the game description, игрова дефиниция
*/
this.update = async function(ctx, data){
-
+ data.id = parseInt(data.id);
+ let object = await this.read(data.id);
+ data = Object.assign(object, data);
+ await db.update(collection, {id: data.id}, data);
+ return data;
}
/**
@@ -44,7 +52,8 @@ class GamesManager{
* @param {Number} id game definition ID, идентификатор на игровата дефиниция
*/
this.remove = async function(id){
-
+ id = parseInt(id);
+ await db.remove(collection, {id});
}
/**
@@ -53,7 +62,10 @@ class GamesManager{
* @returns {Game[]} Array of games, масив от игрови дефиниции
*/
this.list = async function(query){
-
+ return await db.list(collection, {
+ query,
+ project: { name:1, id:1, sceneThumb: '$scenes.data.environment'}
+ });
}
}
diff --git a/backend/app/bl/RulesManager.js b/backend/app/bl/RulesManager.js
index 8d15b2b..4b4170d 100644
--- a/backend/app/bl/RulesManager.js
+++ b/backend/app/bl/RulesManager.js
@@ -3,7 +3,7 @@
* Rules manager class, контролен клас за управление на игрови правила
*/
class RulesManager{
- name = 'rules';
+ name = 'rule';
/**
* Class initializer, инициализация на плъгин
diff --git a/backend/controllers/api/GamesController.js b/backend/controllers/api/GamesController.js
index 6f8fea7..c37ab04 100644
--- a/backend/controllers/api/GamesController.js
+++ b/backend/controllers/api/GamesController.js
@@ -14,7 +14,7 @@ class GamesController{
*/
init(app){
const router = express.Router();
- const { games } = app;
+ const { game } = app;
/**
* API: PUT /api/game/ Create or update game, създаване/обновяване на игрова дефиниция
@@ -22,7 +22,14 @@ class GamesController{
* @memberof GamesController
*/
router.put('/', async (req, res)=>{
-
+ try{
+ let data = req.body;
+ let object = await game[data.id? 'update' : 'create'](req, data)
+ res.json({status: 'OK', object});
+ }catch(err){
+ console.error(err);
+ res.status(500).json({status: 'ERR', err});
+ }
});
/**
@@ -32,6 +39,8 @@ class GamesController{
* @memberof GamesController
*/
router.post('/', async (req, res)=>{
+ let result = await game.list(req.body);
+ res.json(result);
})
/**
@@ -42,6 +51,8 @@ class GamesController{
* @memberof GamesController
*/
router.get('/:id', async (req, res)=>{
+ let object = await game.read(parseInt(req.params.id));
+ res.json(object);
})
/**
@@ -51,6 +62,8 @@ class GamesController{
* @memberof GamesController
*/
router.delete('/:id', async (req, res)=>{
+ await scenario.remove(req.params.id);
+ res.json({status: 'OK'});
})
app.webServer.xapp.use(this.route, router);
diff --git a/backend/main.js b/backend/main.js
index 35fbd67..ad5d182 100644
--- a/backend/main.js
+++ b/backend/main.js
@@ -9,14 +9,19 @@ console.debug = function(){
import App from './app/App.js';
const modules = [
- {name:'Config', path:'app/Config.js'},
- {name:'Db', path:'app/Db.js'},
- {name:'GameObjectsManager', path:'app/bl/GameObjectsManager.js'},
- {name:'ScenariosManager', path:'app/bl/ScenariosManager.js'},
- {name:'WebServer', path:'app/WebServer.js'},
+ {name: 'Config', path:'app/Config.js'},
+ {name: 'Db', path:'app/Db.js'},
+
+ {name: 'GameObjectsManager', path:'app/bl/GameObjectsManager.js'},
+ {name: 'ScenariosManager', path:'app/bl/ScenariosManager.js'},
+ {name: 'GamesManager', path:'app/bl/GamesManager.js'},
+
+ {name: 'WebServer', path:'app/WebServer.js'},
+
{name: 'AssetController', path: 'controllers/AssetController.js'},
- {name:'GameObjectsController', path:'controllers/api/GameObjectsController.js'},
- {name:'ScenariosController', path:'controllers/api/ScenariosController.js'},
+ {name: 'GameObjectsController', path:'controllers/api/GameObjectsController.js'},
+ {name: 'ScenariosController', path:'controllers/api/ScenariosController.js'},
+ {name: 'GamesController', path:'controllers/api/GamesController.js'},
]
process.on('uncaughtException', err => {
diff --git a/src/components/AssetsManagement/AssetPreview.vue b/src/components/AssetsManagement/AssetPreview.vue
index 45882fb..c6cbac6 100644
--- a/src/components/AssetsManagement/AssetPreview.vue
+++ b/src/components/AssetsManagement/AssetPreview.vue
@@ -59,22 +59,25 @@ export default{
if (this.forRendering) {
gameEngine.scene.clear();
if (this.obj.type == 'panorama2d') {
- 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);
+ await gameEngine.loadPanorama(`/asset/default/${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}`);
- console.debug('GLTF', gltf);
+ //console.debug('GLTF', gltf);
this.loadedAsset = gltf;
this.animations = gltf.animations.map(a => ({
name: a.name, id: a.uuid
}));
+ gameEngine.autoScale(gltf.scene);
let bb = new gameEngine.$.Box3().setFromObject(gltf.scene);
gltf.scene.traverse(function (o) {
o.frustumCulled = false;
});
+ //console.log(bb)
gameEngine.camera.position.set(bb.max.x, bb.max.y, bb.max.z);
gameEngine.controls.target.set((bb.max.x + bb.min.x) / 2, (bb.max.y + bb.min.y) / 2, (bb.max.z + bb.min.z) / 2)
gameEngine.controls.update();
diff --git a/src/components/GameDesigner/GameDesigner.vue b/src/components/GameDesigner/GameDesigner.vue
new file mode 100644
index 0000000..2dc62e8
--- /dev/null
+++ b/src/components/GameDesigner/GameDesigner.vue
@@ -0,0 +1,111 @@
+
+
+
+ Move
+ Rotate
+ Scale
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/lib/gameEngine.js b/src/lib/gameEngine.js
index 4281665..e32726d 100644
--- a/src/lib/gameEngine.js
+++ b/src/lib/gameEngine.js
@@ -10,6 +10,7 @@ class GameEngine {
const width = 1200, height = 800;
const camera = new THREE.PerspectiveCamera(70, width / height, 0.01, 10000);
+ this.raycaster = new THREE.Raycaster();
camera.position.set(1, 0, 1);
const scene = new THREE.Scene();
@@ -56,6 +57,10 @@ class GameEngine {
this.camera = camera;
this.controls = controls;
this.mixer = mixer;
+
+ this.activeObjects = new THREE.Group();
+ scene.add(this.activeObjects);
+
domNode.appendChild(renderer.domElement);
let texture = await this.loadTexture('/static/textures/bck.webp');
@@ -92,6 +97,13 @@ class GameEngine {
})
}
+ async loadPanorama(url){
+ let t = await this.loadTexture(url);
+ t.mapping = THREE.EquirectangularReflectionMapping;
+ this.scene.background = t;
+ this.scene.environment = t;
+ }
+
async captureScreenshot(type = 'image/webp', quality = 80){
return new Promise((resolve, reject)=>{
this.renderer.domElement.toBlob(resolve, type, quality)
@@ -107,6 +119,46 @@ class GameEngine {
stop(){
this.renderer.setAnimationLoop(null);
}
+
+ getMouseVector(mouseEvent, domElement){
+ //console.log(mouseEvent, domElement)
+ const mouse = new THREE.Vector2();
+ if (this.renderType == 'VR'){
+ let x;
+ if (mouseEvent.offsetX > window.innerWidth / 2){
+ x = (mouseEvent.offsetX - window.innerWidth / 2) * 2
+ }else {
+ x = mouseEvent.offsetX * 2;
+ }
+ mouse.x = ( x / window.innerWidth ) * 2 - 1;
+ mouse.y = - ( mouseEvent.offsetY / window.innerHeight ) * 2 + 1;
+ }else{
+ mouse.x = ( mouseEvent.offsetX / domElement.clientWidth ) * 2 - 1;
+ mouse.y = - ( mouseEvent.offsetY / domElement.clientHeight ) * 2 + 1;
+ }
+ return mouse;
+ }
+
+ intersect(mouseEvent, domElement, objects, recursive = false, returnInputObjects = true){
+ let mouse = this.getMouseVector(mouseEvent, domElement);
+ this.raycaster.setFromCamera(mouse, this.camera);
+ console.log(objects)
+ let intersects = this.raycaster.intersectObjects(objects, recursive);
+ if (returnInputObjects && recursive){
+ intersects.forEach(o=>{
+ while (o.object && !objects.includes(o.object)) {
+ o.object = o.object.parent;
+ }
+ })
+ }
+ return intersects;
+ }
+
+ autoScale(object, mk = 1){
+ let bb = new THREE.Box3().setFromObject(object);
+ let k = Math.max(bb.max.x - bb.min.x, bb.max.y - bb.min.y, bb.max.z - bb.min.z);
+ object.scale.multiplyScalar(mk/k);
+ }
}
export {GameEngine}
\ No newline at end of file
diff --git a/src/pages/games/[[id]].vue b/src/pages/games/[[id]].vue
index 8b00dca..a478312 100644
--- a/src/pages/games/[[id]].vue
+++ b/src/pages/games/[[id]].vue
@@ -1,9 +1,84 @@
-
+
+
+
+ {{ id == 'add' ? l.createGame : l.editGame }}
+
+
+ {{ l.gameDesigner }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ l.save }}
+
\ No newline at end of file
diff --git a/src/pages/games/list.vue b/src/pages/games/list.vue
new file mode 100644
index 0000000..56d6c33
--- /dev/null
+++ b/src/pages/games/list.vue
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+ {{ v.name }}
+
+
+
+
+
+
+
+
+
+ {{ l.no }}
+ {{ l.yes }}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/plugins/api.js b/src/plugins/api.js
index d6cc480..ce3fd6b 100644
--- a/src/plugins/api.js
+++ b/src/plugins/api.js
@@ -46,6 +46,20 @@ export default {
async remove(id){
return await $ax.delete(`/scenario/${id}`)
}
+ },
+ game:{
+ async save(data){
+ return await $ax.put('/game', data);
+ },
+ async load(id){
+ return await $ax.get(`/game/${id}`);
+ },
+ async search(query){
+ return await $ax.post('/game', query);
+ },
+ async remove(id){
+ return await $ax.delete(`/game/${id}`)
+ }
}
}
}
diff --git a/src/plugins/lang.js b/src/plugins/lang.js
index fc6f8f4..21fce42 100644
--- a/src/plugins/lang.js
+++ b/src/plugins/lang.js
@@ -3,6 +3,8 @@ const lang = {
_code: 'en',
createGameObject: 'Add game object',
editGameObject: 'Edit game object',
+ createGame: 'Add game',
+ editGame: 'Edit game',
name: 'Name',
description: 'Description',
fieldRequired: 'Field is required',
@@ -23,11 +25,13 @@ const lang = {
gameObjects: 'Objects',
gameScenarios: 'Scenarios',
gameRules: 'Rules',
+ gameDesigner: 'Game studio',
games: 'Games',
darkMode: 'Dark mode',
confirmDeletionOf: 'Confirm deletion of',
yes: 'Yes',
no: 'No',
+ scenario: 'Scenario',
createScenario: 'Create scenario',
editScenario: 'Edit scenario',
editScenes: 'Edit scenes',
@@ -38,6 +42,8 @@ const lang = {
_code: 'bg',
createGameObject: 'Добавяне на игрови обект',
editGameObject: 'Редактиране на игрови обект',
+ createGame: 'Добавяне на игра',
+ editGame: 'Редактиране на игра',
name: 'Име',
description: 'Описание',
fieldRequired: 'Полето е задължително',
@@ -58,11 +64,13 @@ const lang = {
gameObjects: 'Обекти',
gameScenarios: 'Сценарии',
gameRules: 'Правила',
+ gameDesigner: 'Студио',
games: 'Игри',
darkMode: 'Тъмен режим',
confirmDeletionOf: 'Потвърдете изтриването на',
yes: 'Да',
no: 'Не',
+ scenario: 'Сценарий',
createScenario: 'Създаване на сценарий',
editScenario: 'Редактиране на сценарий',
editScenes: 'Редактиране на сцени',