game scenarios
This commit is contained in:
@@ -0,0 +1,12 @@
|
|||||||
|
<template>
|
||||||
|
<line :x1="x" :y1="y" :x2="x" :y2="y" class="annotation point"></line>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props:{
|
||||||
|
x: Number,
|
||||||
|
y: Number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<template>
|
||||||
|
<teleport to=".scene-designer" v-if="active">
|
||||||
|
<g @mousedown="$emit('target', {target:vd, attrs:['x1', 'y1'], delta: true})" :class="{gameObject: true, selected}">
|
||||||
|
<line :x1="vd.x1" :y1="vd.y1" :x2="parent.vd.x1" :y2="parent.vd.y1"></line>
|
||||||
|
<svg-icon :src="`/asset/thumb/${modelValue.id}.webp`" :x="vd.x1" :y="vd.y1" :size="37"></svg-icon>
|
||||||
|
</g>
|
||||||
|
</teleport>
|
||||||
|
<v-list density="compact" nav v-if="selected">
|
||||||
|
<v-list-item prepend-icon="mdi-panorama-outline" :title="$l.addScene" value="scene"></v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import SvgIcon from './SvgIcon.vue';
|
||||||
|
import Utils from '@/lib/utils';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
emits:['target'],
|
||||||
|
components: { SvgIcon },
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
active: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted(){
|
||||||
|
this.active = true;
|
||||||
|
},
|
||||||
|
props:{
|
||||||
|
modelValue: Object,
|
||||||
|
vd: Object,
|
||||||
|
selected: Boolean,
|
||||||
|
cid:String,
|
||||||
|
visible: Boolean,
|
||||||
|
parent: Object
|
||||||
|
},
|
||||||
|
steps: [['x1', 'y1']],
|
||||||
|
icon: 'mdi-vector-line',
|
||||||
|
name: 'svg-game-object',
|
||||||
|
modifiers: ['x1', 'y1'],
|
||||||
|
methods:{
|
||||||
|
intersect(v){
|
||||||
|
return Utils.intersectLineRect(this.vd, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<template>
|
||||||
|
<teleport to=".scene-designer" v-if="active">
|
||||||
|
<g @mousedown="$emit('target', {target:vd, attrs:['x1', 'y1'], delta: true})" :class="{scene: true, selected}">
|
||||||
|
<svg-icon :src="`/asset/thumb/${modelValue.environment}.webp`" :x="vd.x1" :y="vd.y1" :size="65"></svg-icon>
|
||||||
|
</g>
|
||||||
|
</teleport>
|
||||||
|
<v-card title="Scene" v-if="selected">
|
||||||
|
<v-form class="pa-4">
|
||||||
|
<v-text-field density="compact" :label="$l.name" v-model="modelValue.name"></v-text-field>
|
||||||
|
</v-form>
|
||||||
|
<v-btn prepend-icon="mdi-panorama-outline" ></v-btn>
|
||||||
|
</v-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import SvgIcon from './SvgIcon.vue';
|
||||||
|
import Utils from '@/lib/utils';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
emits:['target'],
|
||||||
|
components: { SvgIcon },
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
active: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted(){
|
||||||
|
this.active = true;
|
||||||
|
},
|
||||||
|
props:{
|
||||||
|
modelValue: Object,
|
||||||
|
vd: Object,
|
||||||
|
selected: Boolean,
|
||||||
|
cid:String,
|
||||||
|
visible: Boolean,
|
||||||
|
parent: Object
|
||||||
|
},
|
||||||
|
steps: [['x1', 'y1']],
|
||||||
|
icon: 'mdi-vector-line',
|
||||||
|
name: 'svg-scene',
|
||||||
|
modifiers: ['x1', 'y1'],
|
||||||
|
methods:{
|
||||||
|
intersect(v){
|
||||||
|
return Utils.intersectLineRect(this.vd, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,40 +1,93 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container my-3">
|
<div class="container my-3">
|
||||||
<v-btn-toggle variant="tonal" density="compact" class="mx-auto" v-model="listMode" color="blue">
|
<v-btn-toggle variant="tonal" density="compact" class="mx-auto" v-model="mode" color="blue">
|
||||||
<v-btn class="text-none" value="scene"><v-icon>mdi-panorama-outline</v-icon><span>{{ $l.addScene }}</span></v-btn>
|
<v-btn size="small" class="text-none" value="default" prepend-icon="mdi-cursor-default-click">Pointer</v-btn>
|
||||||
<v-btn class="text-none" value="object"><v-icon>mdi-bird</v-icon><span>{{ $l.addScene }}</span></v-btn>
|
<v-btn size="small" class="text-none" value="select" prepend-icon="mdi-select-multiple">Select</v-btn>
|
||||||
<v-btn class="text-none" value="task"><v-icon>mdi-checkbox-marked-circle-plus-outline</v-icon><span>{{ $l.addScene }}</span></v-btn>
|
<v-btn size="small" class="text-none" value="move" prepend-icon="mdi-cursor-move">Move</v-btn>
|
||||||
|
<v-btn size="small" class="text-none" value="pan" prepend-icon="mdi-hand-back-right-outline">Pan</v-btn>
|
||||||
|
|
||||||
|
<v-btn size="small" class="text-none" value="scene" prepend-icon="mdi-panorama-outline">{{ $l.addScene }}</v-btn>
|
||||||
|
<v-btn size="small" class="text-none" value="object" prepend-icon="mdi-bird">{{ $l.addScene }}</v-btn>
|
||||||
|
<v-btn size="small" class="text-none" value="task" prepend-icon="mdi-checkbox-marked-circle-plus-outline">{{ $l.addScene }}</v-btn>
|
||||||
</v-btn-toggle>
|
</v-btn-toggle>
|
||||||
<div @wheel="onWheel" @mousedown="onMouseDown" @mouseup="onMouseUp" @mousemove="onDrag"
|
<div @wheel="onWheel" @mousedown="onMouseDown" @mouseup="onMouseUp" @mousemove="onDrag"
|
||||||
:class="`svg-container ${mode}`" ref="svgContainer">
|
:class="`svg-container ${mode}`" ref="svgContainer">
|
||||||
<svg>
|
<svg class="scene-designer" @resize="resize" :width="viewBox.w" :height="viewBox.h" :viewBox="`${vb.x} ${vb.y} ${vb.w} ${vb.h}`" x="0" y="0"
|
||||||
|
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<SvgRectangle v-model="selector" class="selector"></SvgRectangle>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<v-navigation-drawer location="right">
|
<v-navigation-drawer location="right">
|
||||||
<svg-scene></svg-scene>
|
<template v-for="(item, i) in flatItems" :key="i">
|
||||||
|
<component :is="components[item.__type]" :ref="'svg-'+item.id"
|
||||||
|
:vd="item.vd" v-model="item.data" @target="setTarget($event, item)"
|
||||||
|
:visible="item.visible" :cid="item.id"
|
||||||
|
:parent="item.__parent" :selected="selectedItem.includes(item)">
|
||||||
|
</component>
|
||||||
|
</template>
|
||||||
</v-navigation-drawer>
|
</v-navigation-drawer>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import SvgScene from './SvgScene.vue';
|
import GameObject from './GameObject.vue';
|
||||||
|
import Scene from './Scene.vue';
|
||||||
|
import SvgRectangle from './SvgRectangle.vue';
|
||||||
|
import Utils from '@/lib/utils';
|
||||||
|
|
||||||
const components = {
|
const components = {
|
||||||
SvgScene
|
Scene, GameObject
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { SvgScene },
|
|
||||||
props:{
|
props:{
|
||||||
modelValue: Object
|
modelValue: Object
|
||||||
},
|
},
|
||||||
data(){
|
data(){
|
||||||
return {
|
return {
|
||||||
listMode: 'select'
|
mode: 'default',
|
||||||
|
selectedItem: [],
|
||||||
|
viewBox: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
w: 1000,
|
||||||
|
h: 800
|
||||||
|
},
|
||||||
|
scale: 1,
|
||||||
|
offset:{
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
},
|
||||||
|
selector:{
|
||||||
|
x1:0, x2:0, y1:0, y2:0
|
||||||
|
},
|
||||||
|
modeStep: 0,
|
||||||
|
mousedown: false,
|
||||||
|
target: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
mounted(){
|
||||||
|
window.addEventListener('resize', this.resize);
|
||||||
|
this.resize();
|
||||||
|
},
|
||||||
|
unmounted(){
|
||||||
|
window,removeEventListener('resize', this.resize);
|
||||||
|
},
|
||||||
computed:{
|
computed:{
|
||||||
object:()=>this.modelValue,
|
vb(){
|
||||||
|
return {
|
||||||
|
x: (1-this.scale)*100 + this.offset.x,
|
||||||
|
y: (1-this.scale)*100 + this.offset.y,
|
||||||
|
w: this.viewBox.w * this.scale,
|
||||||
|
h: this.viewBox.h * this.scale
|
||||||
|
}
|
||||||
|
},
|
||||||
|
object(){
|
||||||
|
return this.modelValue;
|
||||||
|
},
|
||||||
|
items(){
|
||||||
|
return this.object.scenes;
|
||||||
|
},
|
||||||
zoom:{
|
zoom:{
|
||||||
get(){
|
get(){
|
||||||
return 1 / this.scale;
|
return 1 / this.scale;
|
||||||
@@ -43,12 +96,22 @@ export default {
|
|||||||
this.rescale(1 / v);
|
this.rescale(1 / v);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mode(){
|
|
||||||
return this.listMode[0];
|
|
||||||
},
|
|
||||||
components(){
|
components(){
|
||||||
return components;
|
return components;
|
||||||
},
|
},
|
||||||
|
flatItems(){
|
||||||
|
let fi = [];
|
||||||
|
this.items.forEach(i=>{
|
||||||
|
i.__type = 'Scene';
|
||||||
|
fi.push(i);
|
||||||
|
i.data?.gameObjects?.forEach(go=>{
|
||||||
|
fi.push(go);
|
||||||
|
go.__parent = i;
|
||||||
|
go.__type = 'GameObject';
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return fi.reverse();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods:{
|
methods:{
|
||||||
rescale(scale, e){
|
rescale(scale, e){
|
||||||
@@ -111,6 +174,7 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
onWheel(e){
|
onWheel(e){
|
||||||
|
e.preventDefault();
|
||||||
this.rescale(null, e);
|
this.rescale(null, e);
|
||||||
},
|
},
|
||||||
onDrag(e){
|
onDrag(e){
|
||||||
@@ -121,11 +185,11 @@ export default {
|
|||||||
y: Utils.round(e.movementY*this.scale, 0)
|
y: Utils.round(e.movementY*this.scale, 0)
|
||||||
}
|
}
|
||||||
this.selectedItem.forEach(i=>{
|
this.selectedItem.forEach(i=>{
|
||||||
let mf = components[i.name].modifiers;
|
let mf = components[i.__type].modifiers;
|
||||||
mf.filter(m=>m.match(/^x[0-9]+$/)).forEach(x=>i.data[x]+= p.x);
|
mf.filter(m=>m.match(/^x[0-9]+$/)).forEach(x=>i.vd[x]+= p.x);
|
||||||
mf.filter(m=>m.match(/^y[0-9]+$/)).forEach(y=>i.data[y]+= p.y);
|
mf.filter(m=>m.match(/^y[0-9]+$/)).forEach(y=>i.vd[y]+= p.y);
|
||||||
})
|
})
|
||||||
}else if (this.target) {
|
}else if (this.target && this.mode != 'pan') {
|
||||||
this.retarget(e)
|
this.retarget(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,7 +228,7 @@ export default {
|
|||||||
let id, nid = 1;
|
let id, nid = 1;
|
||||||
do {
|
do {
|
||||||
id = `${this.components[this.mode].name}-${nid++}`
|
id = `${this.components[this.mode].name}-${nid++}`
|
||||||
}while (this.items.find(i=>i.id == id))
|
}while (this.flatItems.find(i=>i.id == id))
|
||||||
this.items.push({
|
this.items.push({
|
||||||
name: this.mode,
|
name: this.mode,
|
||||||
data: this.target.target,
|
data: this.target.target,
|
||||||
@@ -211,52 +275,28 @@ export default {
|
|||||||
this.target = t;
|
this.target = t;
|
||||||
this.selectedItem = [item]
|
this.selectedItem = [item]
|
||||||
},
|
},
|
||||||
async save(){
|
|
||||||
let imageData = await Utils.blobToBase64(await (await fetch(this.imageTarget)).blob());
|
|
||||||
//console.log(imageData)
|
|
||||||
let jsonData = {
|
|
||||||
items: this.items,
|
|
||||||
viewBox: this.viewBox,
|
|
||||||
offset: this.offset,
|
|
||||||
zoom: this.zoom,
|
|
||||||
rotation: this.imgRotation,
|
|
||||||
page: this.page,
|
|
||||||
data: imageData
|
|
||||||
};
|
|
||||||
const link = document.createElement('a')
|
|
||||||
const blob = new Blob([JSON.stringify(jsonData)], {type: 'application/json'});
|
|
||||||
link.href = URL.createObjectURL(blob)
|
|
||||||
link.download = "document.json"
|
|
||||||
link.click();
|
|
||||||
},
|
|
||||||
select(){
|
select(){
|
||||||
let r = Utils.adjustMinMax(this.selector);
|
let r = Utils.adjustMinMax(this.selector);
|
||||||
this.selectedItem = this.items.filter(i=>this.$refs['svg-'+i.id][0].intersect(r));
|
this.selectedItem = this.flatItems.filter(i=>this.$refs['svg-'+i.id][0].intersect(r));
|
||||||
},
|
},
|
||||||
|
resize(){
|
||||||
async processImage(){
|
let r = this.$refs.svgContainer;
|
||||||
this.processingImage = true;
|
this.viewBox.w = r.clientWidth;
|
||||||
await this.$nextTick();
|
this.viewBox.h = r.clientHeight;
|
||||||
let processor = new ImageProcessor(this.$refs.imageCanvas, this.img)
|
//this.zoom = Math.min(r.clientWidth / this.viewBox.w, r.clientHeight / this.viewBox.h);
|
||||||
let lines = processor.identifyLines(), iw = this.img.naturalWidth;
|
|
||||||
lines.forEach((l, i)=>{
|
|
||||||
this.items.push({
|
|
||||||
name: 'SvgHorizontalLine', id: `aline-${i}`, visible: true, title: `ALine-${i}`,
|
|
||||||
data: { x1:iw*.1, y1: l, x2: iw*.9}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
this.processingImage = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--svg-scale: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.svg-container{
|
.svg-container{
|
||||||
svg{
|
svg{
|
||||||
width: 100%;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
image{
|
image{
|
||||||
clip-path: circle(50% at 50% 50%);
|
clip-path: circle(50% at 50% 50%);
|
||||||
}
|
}
|
||||||
@@ -265,5 +305,26 @@ export default {
|
|||||||
fill:rgba(255,255,255,.5);
|
fill:rgba(255,255,255,.5);
|
||||||
stroke-width: 2px;
|
stroke-width: 2px;
|
||||||
}
|
}
|
||||||
|
g{
|
||||||
|
&.selected circle{
|
||||||
|
fill: rgba(var(--v-theme-secondary), .9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
line, path{
|
||||||
|
stroke: #19c;
|
||||||
|
stroke-width: calc( 2px * var(--svg-scale) );
|
||||||
|
}
|
||||||
|
g.selector {
|
||||||
|
line {
|
||||||
|
stroke-dasharray: 0 calc(8 * var(--svg-scale)) 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: 100vw;
|
||||||
|
max-height: 95vh;
|
||||||
|
&.pan {
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
<template>
|
|
||||||
<circle :cx="65 + x" :cy="65 + y" :r="70"></circle>
|
|
||||||
<image :href="src" :x="x" :y="y" height="130" width="130" preserveAspectRatio="xMidYMid slice"></image>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props:['src', 'x', 'y'],
|
|
||||||
data(){
|
|
||||||
return {
|
|
||||||
target: null,
|
|
||||||
img: null,
|
|
||||||
size:{}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created(){
|
|
||||||
// this.img = new Image();
|
|
||||||
// this.img.onload = ()=>{
|
|
||||||
// this.size = {
|
|
||||||
// w: this.img.naturalWidth,
|
|
||||||
// h: this.img.naturalHeight,
|
|
||||||
// a: this.img.naturalWidth / this.img.naturalHeight
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// this.img.src= this.target;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<template>
|
||||||
|
<circle :cx="x" :cy="y" :r="size+5"></circle>
|
||||||
|
<image :href="src" :x="x-size" :y="y-size" :height="size*2" :width="size*2" preserveAspectRatio="xMidYMid slice"></image>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props:['src', 'x', 'y', 'size'],
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created(){
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<template>
|
||||||
|
<g :class="{ selected }">
|
||||||
|
<line :x1="modelValue.x1" :y1="modelValue.y1" :x2="modelValue.x2" :y2="modelValue.y1" @mousedown="$emit('target', { target:modelValue, attrs:[, 'y1'] })"></line>
|
||||||
|
<line :x1="modelValue.x2" :y1="modelValue.y1" :x2="modelValue.x2" :y2="modelValue.y2" @mousedown="$emit('target', { target:modelValue, attrs:['x2'] })"></line>
|
||||||
|
<line :x1="modelValue.x2" :y1="modelValue.y2" :x2="modelValue.x1" :y2="modelValue.y2" @mousedown="$emit('target', { target:modelValue, attrs:[, 'y2'] })"></line>
|
||||||
|
<line :x1="modelValue.x1" :y1="modelValue.y2" :x2="modelValue.x1" :y2="modelValue.y1" @mousedown="$emit('target', { target:modelValue, attrs:['x1'] })"></line>
|
||||||
|
<AnnotationPoint :x="modelValue.x1" :y="modelValue.y1" class="movable" @mousedown="$emit('target', {target:modelValue, attrs:['x1', 'y1']})"></AnnotationPoint>
|
||||||
|
<AnnotationPoint :x="modelValue.x2" :y="modelValue.y2" class="movable" @mousedown="$emit('target', {target:modelValue, attrs:['x2', 'y2']})"></AnnotationPoint>
|
||||||
|
<AnnotationPoint :x="modelValue.x1" :y="modelValue.y2" class="movable" @mousedown="$emit('target', {target:modelValue, attrs:['x1', 'y2']})"></AnnotationPoint>
|
||||||
|
<AnnotationPoint :x="modelValue.x2" :y="modelValue.y1" class="movable" @mousedown="$emit('target', {target:modelValue, attrs:['x2', 'y1']})"></AnnotationPoint>
|
||||||
|
</g>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import AnnotationPoint from './AnnotationPoint.vue';
|
||||||
|
import Utils from '@/lib/utils';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { AnnotationPoint },
|
||||||
|
props:{
|
||||||
|
modelValue: Object,
|
||||||
|
selected: Boolean
|
||||||
|
},
|
||||||
|
steps: [['x1', 'y1'], ['x2', 'y2']],
|
||||||
|
icon: 'mdi-vector-rectangle',
|
||||||
|
name: 'rectangle',
|
||||||
|
modifiers: ['x1', 'y1', 'x2', 'y2'],
|
||||||
|
methods:{
|
||||||
|
intersect(v){
|
||||||
|
let r = Utils.adjustMinMax(this.modelValue);
|
||||||
|
return Utils.intersectRectRect(r, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
<template>
|
|
||||||
<teleport to="svg" defer>
|
|
||||||
<g>
|
|
||||||
<svg-avatar src="/asset/thumb/6.webp" :x="50" :y="50"></svg-avatar>
|
|
||||||
</g>
|
|
||||||
</teleport>
|
|
||||||
<v-list density="compact" nav>
|
|
||||||
<v-list-item prepend-icon="mdi-panorama-outline" :title="$l.addScene" value="scene"></v-list-item>
|
|
||||||
</v-list>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import SvgAvatar from './SvgAvatar.vue';
|
|
||||||
export default {
|
|
||||||
components: { SvgAvatar },
|
|
||||||
data(){
|
|
||||||
return {
|
|
||||||
active: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted(){
|
|
||||||
this.active = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
const epsilon = 0.1;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
blobToBase64: blob => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(blob);
|
||||||
|
return new Promise(resolve => {
|
||||||
|
reader.onloadend = () => {
|
||||||
|
resolve(reader.result);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
adjustMinMax(r){
|
||||||
|
return {
|
||||||
|
x1: Math.min(r.x1, r.x2), x2: Math.max(r.x1, r.x2),
|
||||||
|
y1: Math.min(r.y1, r.y2), y2: Math.max(r.y1, r.y2)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
intersectPointRect(p, r){
|
||||||
|
//r = this.adjustMinMax(r);
|
||||||
|
return p[0] >= r.x1 && p[0] <= r.x2 && p[1] >= r.y1 && p[1] <= r.y2;
|
||||||
|
},
|
||||||
|
|
||||||
|
intersectPointLine(p, l){
|
||||||
|
//l = this.adjustMinMax(l);
|
||||||
|
let dx = l.x2 - l.x1, dy = l.y2 - l.y1;
|
||||||
|
let c = dy / dx;
|
||||||
|
return this.intersectPointRect(p, l) && c * p[0] - p[1] <= epsilon
|
||||||
|
},
|
||||||
|
|
||||||
|
intersectLineRect(l, r){
|
||||||
|
return this.intersectPointRect([l.x1, l.y1], r) ||
|
||||||
|
this.intersectPointRect([l.x2, l.y2], r);
|
||||||
|
},
|
||||||
|
|
||||||
|
intersectRectRect(r1, r2){
|
||||||
|
return this.intersectPointRect([r1.x1, r1.y1], r2) ||
|
||||||
|
this.intersectPointRect([r1.x1, r1.y2], r2) ||
|
||||||
|
this.intersectPointRect([r1.x2, r1.y1], r2) ||
|
||||||
|
this.intersectPointRect([r1.x2, r1.y2], r2);
|
||||||
|
},
|
||||||
|
|
||||||
|
round(n, p = 2){
|
||||||
|
let pp = Math.pow(10, p);
|
||||||
|
return Math.round(n*pp)/pp;
|
||||||
|
},
|
||||||
|
|
||||||
|
deg2rad(deg){
|
||||||
|
return deg * (Math.PI / 180);
|
||||||
|
},
|
||||||
|
|
||||||
|
rad2deg(rad){
|
||||||
|
return rad * 180 / Math.PI;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { GameEngine } from '@/gameEngine';
|
import { GameEngine } from '@/lib/gameEngine.js';
|
||||||
let gameEngine = null;
|
let gameEngine = null;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -48,7 +48,7 @@ export default {
|
|||||||
valid: false,
|
valid: false,
|
||||||
rules: {
|
rules: {
|
||||||
required: v => v ? true : this.$l.fieldRequired,
|
required: v => v ? true : this.$l.fieldRequired,
|
||||||
requiredFile: v => (v?.length || this.id != 'add') ? true : this.$l.fieldRequired
|
requiredFile: v => (v?.size || this.id != 'add') ? true : this.$l.fieldRequired
|
||||||
},
|
},
|
||||||
loading: false,
|
loading: false,
|
||||||
animations: []
|
animations: []
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-card :title="id == 'add' ? $l.createScenario : $l.editScenario" class="container my-3">
|
<v-card :title="id == 'add' ? $l.createScenario : $l.editScenario" class="container my-3">
|
||||||
<v-form class="pa-4" v-model="valid">
|
<!-- <v-form class="pa-4" v-model="valid">
|
||||||
<v-text-field :label="$l.name" v-model="object.name" :rules="[rules.required]"></v-text-field>
|
<v-text-field :label="$l.name" v-model="object.name" :rules="[rules.required]"></v-text-field>
|
||||||
</v-form>
|
</v-form> -->
|
||||||
<!-- <v-card-actions>
|
<v-card-actions>
|
||||||
<v-btn @click="saveAndPreview" :loading="loading" prepend-icon="mdi-content-save" color="primary"
|
<v-btn @click="save" :loading="loading" prepend-icon="mdi-content-save" color="primary">
|
||||||
:disabled="!valid">
|
{{ $l.save }}
|
||||||
{{ $l.saveAndPreview }}
|
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn @click="publish" prepend-icon="mdi-publish" color="success" v-if="false && object.id">{{ $l.publish
|
</v-card-actions>
|
||||||
}}</v-btn>
|
|
||||||
</v-card-actions> -->
|
|
||||||
</v-card>
|
</v-card>
|
||||||
<SceneDesigner v-model="object"></SceneDesigner>
|
<SceneDesigner v-model="object"></SceneDesigner>
|
||||||
|
|
||||||
@@ -25,7 +22,22 @@ import SceneDesigner from '@/components/SceneDesigner/SceneDesigner.vue';
|
|||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
object: {},
|
object: {
|
||||||
|
scenes: [
|
||||||
|
{
|
||||||
|
id: 'test',
|
||||||
|
vd: { x1: 220, y1: 220 },
|
||||||
|
data: {
|
||||||
|
environment: 3, intro: 2,
|
||||||
|
gameObjects: [
|
||||||
|
{ vd: { x1: 350, y1:350 }, data:{ id: 7, }},
|
||||||
|
{ vd: { x1: 200, y1:400 }, data:{ id: 8, }},
|
||||||
|
{ vd: { x1: 70, y1:350 }, data:{ id: 9, }}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
valid: false,
|
valid: false,
|
||||||
rules: {
|
rules: {
|
||||||
required: v => v ? true : this.$l.fieldRequired,
|
required: v => v ? true : this.$l.fieldRequired,
|
||||||
@@ -34,10 +46,30 @@ export default {
|
|||||||
loading: false,
|
loading: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async mounted(){
|
||||||
|
if (this.id && this.id != 'add') {
|
||||||
|
this.object = (await this.$api.scenario.load(this.id)).data;
|
||||||
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
id() {
|
id() {
|
||||||
return this.$route.params?.id;
|
return this.$route.params?.id;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
methods:{
|
||||||
|
async save(params) {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
let result = await this.$api.scenario.save(this.object);
|
||||||
|
Object.assign(this.object, result.data.object);
|
||||||
|
if (this.id == 'add') {
|
||||||
|
this.$router.replace({ params: { id: this.object.id } });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
this.loading = false
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
+16
-1
@@ -1,7 +1,8 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
const $ax = axios.create({
|
const $ax = axios.create({
|
||||||
baseURL: '/api/'
|
baseURL: '/api/',
|
||||||
|
//transformRequest: data=>
|
||||||
})
|
})
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -20,6 +21,20 @@ export default {
|
|||||||
async remove(id){
|
async remove(id){
|
||||||
return await $ax.delete(`/game-object/${id}`)
|
return await $ax.delete(`/game-object/${id}`)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
scenario:{
|
||||||
|
async save(data){
|
||||||
|
return await $ax.put('/scenario', data);
|
||||||
|
},
|
||||||
|
async load(id){
|
||||||
|
return await $ax.get(`/scenario/${id}`);
|
||||||
|
},
|
||||||
|
async search(query){
|
||||||
|
return await $ax.post('/scenario', query);
|
||||||
|
},
|
||||||
|
async remove(id){
|
||||||
|
return await $ax.delete(`/scenario/${id}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user