ctx menu
This commit is contained in:
parent
9ea5b6910b
commit
2f68699913
|
@ -79,12 +79,18 @@ const rerenderSelection = () => {
|
|||
};
|
||||
|
||||
register('initialized', () => createSceneMap());
|
||||
register('sceneJoin', () => createSceneMap());
|
||||
register('sceneLeave', () => createSceneMap());
|
||||
register('selected', (event) => updateSelectionMap(event));
|
||||
register('deselected', (event) => updateSelectionMap(event));
|
||||
register('change', (event) => {
|
||||
if (['name', 'visible', 'parent'].includes(event.property)) createSceneMap();
|
||||
rerenderSelection();
|
||||
});
|
||||
register('reset', () => {
|
||||
createSceneMap();
|
||||
rerenderSelection();
|
||||
});
|
||||
|
||||
onMounted(() => createSceneMap());
|
||||
</script>
|
||||
|
|
|
@ -1,9 +1,36 @@
|
|||
<template>
|
||||
<div class="toolbar"></div>
|
||||
<div class="toolbar">
|
||||
<Menu
|
||||
id="file"
|
||||
:items="fileMenu"
|
||||
:open-sibling="currentlyOpen"
|
||||
@toggle="(state, reason) => toggleEvent('file', state, reason)"
|
||||
>File</Menu
|
||||
>
|
||||
<Menu
|
||||
id="edit"
|
||||
:items="editMenu"
|
||||
:open-sibling="currentlyOpen"
|
||||
@toggle="(state, reason) => toggleEvent('edit', state, reason)"
|
||||
>Edit</Menu
|
||||
>
|
||||
<Menu
|
||||
id="add"
|
||||
:items="addMenu"
|
||||
:open-sibling="currentlyOpen"
|
||||
@toggle="(state, reason) => toggleEvent('add', state, reason)"
|
||||
>Add</Menu
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
import { Editor } from '../editor';
|
||||
import Menu from './menu/Menu.vue';
|
||||
import { instancableGameObjects } from '@freeblox/engine';
|
||||
|
||||
const currentlyOpen = ref<string | undefined>(undefined);
|
||||
|
||||
const props = defineProps<{
|
||||
editor: Editor;
|
||||
|
@ -12,6 +39,71 @@ const props = defineProps<{
|
|||
const emit = defineEmits<{
|
||||
(e: 'update'): void;
|
||||
}>();
|
||||
|
||||
const fileMenu = [
|
||||
{
|
||||
id: 'new',
|
||||
label: 'New',
|
||||
onClick: () => props.editor.events.emit('reset'),
|
||||
},
|
||||
{
|
||||
id: 'export',
|
||||
label: 'Export',
|
||||
onClick: () => {},
|
||||
},
|
||||
];
|
||||
|
||||
const editMenu = computed(() => [
|
||||
{
|
||||
id: 'undo',
|
||||
label: 'Undo',
|
||||
shortcut: 'CTRL+Z',
|
||||
onClick: () => props.editor.events.emit('undo'),
|
||||
},
|
||||
{
|
||||
id: 'redo',
|
||||
label: 'Redo',
|
||||
shortcut: 'CTRL+Y',
|
||||
onClick: () => props.editor.events.emit('redo'),
|
||||
},
|
||||
{
|
||||
id: 'cut',
|
||||
label: 'Cut',
|
||||
shortcut: 'CTRL+X',
|
||||
onClick: () => props.editor.events.emit('cut'),
|
||||
},
|
||||
{
|
||||
id: 'copy',
|
||||
label: 'Copy',
|
||||
shortcut: 'CTRL+C',
|
||||
onClick: () => props.editor.events.emit('copy'),
|
||||
},
|
||||
{
|
||||
id: 'paste',
|
||||
label: 'Paste',
|
||||
shortcut: 'CTRL+V',
|
||||
onClick: () => props.editor.events.emit('paste', undefined),
|
||||
},
|
||||
{
|
||||
id: 'delete',
|
||||
label: 'Delete',
|
||||
shortcut: 'Delete',
|
||||
onClick: () => props.editor.events.emit('delete'),
|
||||
},
|
||||
]);
|
||||
|
||||
const addMenu = computed(() =>
|
||||
Object.keys(instancableGameObjects).map((item) => ({
|
||||
id: item,
|
||||
label: item,
|
||||
onClick: () => props.editor.events.emit('instance', { type: item }),
|
||||
}))
|
||||
);
|
||||
|
||||
const toggleEvent = (id: string, state: boolean, reason: 'user' | 'leave') => {
|
||||
if (state) currentlyOpen.value = id;
|
||||
else if (reason === 'user') currentlyOpen.value = undefined;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="form-field-check">
|
||||
<label class="form-field-check-label" :for="id">{{ name }}</label>
|
||||
<label class="form-field-check-label" :for="id">{{ label }}</label>
|
||||
<div class="form-field-check-wrap">
|
||||
<input
|
||||
class="form-field-check-input"
|
||||
|
@ -18,6 +18,7 @@ import { computed, ref, watch } from 'vue';
|
|||
|
||||
const props = defineProps<{
|
||||
name: string;
|
||||
label: string;
|
||||
value: boolean;
|
||||
}>();
|
||||
|
||||
|
@ -41,11 +42,11 @@ watch(modelValue, (value, oldValue) => {
|
|||
|
||||
&-wrap {
|
||||
padding: 8px;
|
||||
margin: 2px 0;
|
||||
}
|
||||
|
||||
&-input {
|
||||
border: 0;
|
||||
background-color: #efefef;
|
||||
}
|
||||
|
||||
&-label {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="form-field-color">
|
||||
<label class="form-field-color-label" :for="id">{{ name }}</label>
|
||||
<label class="form-field-color-label" :for="id">{{ label }}</label>
|
||||
<div class="form-field-color-input">
|
||||
<div class="form-field-color-wrapper">
|
||||
<input
|
||||
|
@ -34,6 +34,7 @@ const secretInput = ref();
|
|||
|
||||
const props = defineProps<{
|
||||
name: string;
|
||||
label: string;
|
||||
value: Color;
|
||||
}>();
|
||||
|
||||
|
@ -66,6 +67,7 @@ const changed = ($event: Event) => {
|
|||
&-input {
|
||||
position: relative;
|
||||
display: flex;
|
||||
margin: 2px 0;
|
||||
}
|
||||
|
||||
&-wrapper {
|
||||
|
@ -77,6 +79,7 @@ const changed = ($event: Event) => {
|
|||
&-button {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
font-family: monospace;
|
||||
align-items: center;
|
||||
appearance: none;
|
||||
padding: 4px;
|
||||
|
@ -84,6 +87,8 @@ const changed = ($event: Event) => {
|
|||
border: 0;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
background: transparent;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
&-preview {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="form-field">
|
||||
<label class="form-field-label" :for="id">{{ name }}</label>
|
||||
<label class="form-field-label" :for="id">{{ label }}</label>
|
||||
<input
|
||||
class="form-field-input"
|
||||
type="text"
|
||||
|
@ -17,6 +17,7 @@ import { computed, ref, watch } from 'vue';
|
|||
|
||||
const props = defineProps<{
|
||||
name: string;
|
||||
label: string;
|
||||
value: unknown;
|
||||
type?: 'string' | 'number';
|
||||
}>();
|
||||
|
@ -46,9 +47,12 @@ const changed = () => {
|
|||
|
||||
&-input {
|
||||
border: 0;
|
||||
background-color: #efefef;
|
||||
background-color: #ffffff;
|
||||
padding: 8px;
|
||||
width: 100%;
|
||||
margin: 2px 0;
|
||||
font-size: 0.75rem;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
&-label {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<template>
|
||||
<div class="form-field-vec">
|
||||
<label class="form-field-vec-label" :for="id + '-x'">{{ name }}</label>
|
||||
<label class="form-field-vec-label" :for="id + '-x'">{{ label }}</label>
|
||||
<div class="form-field-vec-pieces">
|
||||
<div class="form-field-vec-piece">
|
||||
<label class="form-field-vec-label-sub" :for="id + '-x'">{{
|
||||
names[0]
|
||||
}}</label>
|
||||
<label class="form-field-vec-label-sub" :for="id + '-x'"
|
||||
>{{ names[0] }}:</label
|
||||
>
|
||||
<input
|
||||
class="form-field-vec-input"
|
||||
type="text"
|
||||
|
@ -16,9 +16,9 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="form-field-vec-piece">
|
||||
<label class="form-field-vec-label-sub" :for="id + '-y'">{{
|
||||
names[1]
|
||||
}}</label>
|
||||
<label class="form-field-vec-label-sub" :for="id + '-y'"
|
||||
>{{ names[1] }}:</label
|
||||
>
|
||||
<input
|
||||
class="form-field-vec-input"
|
||||
type="text"
|
||||
|
@ -29,9 +29,9 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="form-field-vec-piece">
|
||||
<label class="form-field-vec-label-sub" :for="id + '-z'">{{
|
||||
names[2]
|
||||
}}</label>
|
||||
<label class="form-field-vec-label-sub" :for="id + '-z'"
|
||||
>{{ names[2] }}:</label
|
||||
>
|
||||
<input
|
||||
class="form-field-vec-input"
|
||||
type="text"
|
||||
|
@ -51,6 +51,7 @@ import { computed, ref } from 'vue';
|
|||
|
||||
const props = defineProps<{
|
||||
name: string;
|
||||
label: string;
|
||||
value: Vector3 | Color | Euler;
|
||||
type?: 'vector' | 'color' | 'euler';
|
||||
}>();
|
||||
|
@ -132,12 +133,16 @@ const changed = () => {
|
|||
padding: 8px 4px;
|
||||
width: 16px;
|
||||
user-select: none;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
&-pieces {
|
||||
display: grid;
|
||||
position: relative;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
background-color: #ffffff;
|
||||
margin: 2px 0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
&-piece {
|
||||
|
@ -150,8 +155,9 @@ const changed = () => {
|
|||
display: block;
|
||||
width: 32px;
|
||||
border: 0;
|
||||
background-color: #efefef;
|
||||
background-color: #ffffff;
|
||||
text-align: center;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
<template>
|
||||
<div :class="menuClass">
|
||||
<button
|
||||
type="button"
|
||||
class="menu-button"
|
||||
@click="toggle()"
|
||||
ref="buttonRef"
|
||||
@mouseenter="mouseEnter"
|
||||
>
|
||||
<slot></slot>
|
||||
</button>
|
||||
<div class="menu-dropdown" v-if="isOpen">
|
||||
<div class="menu-dropdown-inner">
|
||||
<template v-for="item of items">
|
||||
<button type="button" class="menu-button" @click="item.onClick">
|
||||
<template v-if="item.component">
|
||||
<component :is="item.component" v-bind="item"></component>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="menu-button-inner">{{ item.label }}</span>
|
||||
<span class="menu-button-shortcut" v-if="item.shortcut">{{
|
||||
item.shortcut
|
||||
}}</span>
|
||||
</template>
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, watch } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
type ToggleTrigger = 'user' | 'leave';
|
||||
|
||||
const buttonRef = ref();
|
||||
const isOpen = ref(false);
|
||||
|
||||
export interface MenuItem {
|
||||
id: string;
|
||||
label?: string;
|
||||
shortcut?: string;
|
||||
onClick?: () => void;
|
||||
component?: any;
|
||||
children?: MenuItem[];
|
||||
}
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
id: string;
|
||||
items?: MenuItem[];
|
||||
onClick?: () => void;
|
||||
position?: 'bottom' | 'right';
|
||||
trigger?: 'click' | 'hover';
|
||||
openSibling?: string;
|
||||
}>(),
|
||||
{
|
||||
position: 'bottom',
|
||||
trigger: 'click',
|
||||
items: () => [],
|
||||
}
|
||||
);
|
||||
const emit = defineEmits<{
|
||||
(e: 'toggle', v: boolean, t: ToggleTrigger): void;
|
||||
}>();
|
||||
|
||||
const toggle = (trigger: ToggleTrigger = 'user') => {
|
||||
isOpen.value = !isOpen.value;
|
||||
emit('toggle', isOpen.value, trigger);
|
||||
};
|
||||
|
||||
const menuClass = computed(() => [
|
||||
'menu-wrapper',
|
||||
isOpen.value ? 'menu-wrapper--open' : 'menu-wrapper--closed',
|
||||
`menu-wrapper--${props.position}`,
|
||||
]);
|
||||
|
||||
const mouseEnter = () => {
|
||||
if (
|
||||
isOpen.value ||
|
||||
((!props.openSibling || props.openSibling === props.id) &&
|
||||
props.trigger !== 'hover')
|
||||
)
|
||||
return;
|
||||
toggle();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const handler = (ev: MouseEvent) => {
|
||||
if ((ev.target as HTMLElement).isEqualNode(buttonRef.value)) return;
|
||||
if (isOpen.value) toggle();
|
||||
};
|
||||
|
||||
window.addEventListener('click', handler);
|
||||
return () => window.removeEventListener('click', handler);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.openSibling,
|
||||
(value) => {
|
||||
if (value === props.id || !isOpen.value) return;
|
||||
toggle('leave');
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.menu {
|
||||
&-wrapper {
|
||||
position: relative;
|
||||
|
||||
&--bottom > .menu-dropdown {
|
||||
top: 100%;
|
||||
}
|
||||
|
||||
&--right > .menu-dropdown {
|
||||
top: 0;
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
&--open > .menu-button {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
|
||||
& > .menu-button {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&-dropdown {
|
||||
background-color: #e6e6e6;
|
||||
position: absolute;
|
||||
z-index: 1001;
|
||||
|
||||
&-inner {
|
||||
min-width: 180px;
|
||||
|
||||
& > .menu-button {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-button {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
cursor: pointer;
|
||||
appearance: none;
|
||||
font-size: 1rem;
|
||||
padding: 8px 16px;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
&:hover {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
&-shortcut {
|
||||
font-size: 0.875rem;
|
||||
color: #6b6b6b;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -4,6 +4,7 @@
|
|||
<component
|
||||
:is="form.component"
|
||||
:name="form.name"
|
||||
:label="form.label"
|
||||
:value="form.value"
|
||||
:type="form.type"
|
||||
@update="(value: string) => propertyChanged(form.name, value)"
|
||||
|
@ -24,6 +25,7 @@ import ColorPicker from '../form/ColorPicker.vue';
|
|||
|
||||
interface FormItem {
|
||||
name: string;
|
||||
label: string;
|
||||
value: unknown;
|
||||
type?: unknown;
|
||||
component: Component;
|
||||
|
@ -42,6 +44,16 @@ const propertyChanged = (property: string, value: string) => {
|
|||
emit('update', object, property, value);
|
||||
};
|
||||
|
||||
function toCapitalizedWords(name: string) {
|
||||
const words = name.match(/[A-Za-z][a-z]*/g) || [];
|
||||
|
||||
return words.map(capitalize).join(' ');
|
||||
}
|
||||
|
||||
function capitalize(word: string) {
|
||||
return word.charAt(0).toUpperCase() + word.substring(1);
|
||||
}
|
||||
|
||||
const formFields = computed(() => {
|
||||
// TODO: multi-edit
|
||||
const object = props.selection[0];
|
||||
|
@ -54,6 +66,7 @@ const formFields = computed(() => {
|
|||
if (property.type === String || property.type === Number) {
|
||||
fields.push({
|
||||
name: property.name,
|
||||
label: toCapitalizedWords(property.name),
|
||||
value: (object as unknown as Record<string, unknown>)[property.name],
|
||||
type: property.type === String ? 'string' : 'number',
|
||||
component: Field,
|
||||
|
@ -63,6 +76,7 @@ const formFields = computed(() => {
|
|||
if (property.type === Vector3 || property.type === Euler) {
|
||||
fields.push({
|
||||
name: property.name,
|
||||
label: toCapitalizedWords(property.name),
|
||||
value: (object as unknown as Record<string, unknown>)[property.name],
|
||||
type: property.type === Vector3 ? 'vector' : 'euler',
|
||||
component: Vector3Field,
|
||||
|
@ -72,6 +86,7 @@ const formFields = computed(() => {
|
|||
if (property.type === Color) {
|
||||
fields.push({
|
||||
name: property.name,
|
||||
label: toCapitalizedWords(property.name),
|
||||
value: (object as unknown as Record<string, unknown>)[property.name],
|
||||
component: ColorPicker,
|
||||
});
|
||||
|
@ -80,6 +95,7 @@ const formFields = computed(() => {
|
|||
if (property.type === Boolean) {
|
||||
fields.push({
|
||||
name: property.name,
|
||||
label: toCapitalizedWords(property.name),
|
||||
value: (object as unknown as Record<string, unknown>)[property.name],
|
||||
component: Checkbox,
|
||||
});
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
background-color: #f7f7f7;
|
||||
|
||||
&-title {
|
||||
background-color: #e5e5e5;
|
||||
|
|
|
@ -53,7 +53,7 @@ const filtered = (items: Object3D[]) =>
|
|||
flex-direction: column;
|
||||
|
||||
&-button {
|
||||
background-color: #efefef;
|
||||
background-color: transparent;
|
||||
user-select: none;
|
||||
appearance: none;
|
||||
padding: 8px;
|
||||
|
@ -62,7 +62,7 @@ const filtered = (items: Object3D[]) =>
|
|||
cursor: pointer;
|
||||
|
||||
&.selected {
|
||||
background-color: #ffffff;
|
||||
background-color: #bcefff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -179,6 +179,11 @@ export class HistoryComponent extends EngineComponent {
|
|||
const undo = this.undo.bind(this);
|
||||
const redo = this.redo.bind(this);
|
||||
|
||||
const resetEvent = () => {
|
||||
this.history.length = 0;
|
||||
this.restory.length = 0;
|
||||
};
|
||||
|
||||
this.events.addListener('change', changeEvent);
|
||||
this.events.addListener('transformStart', transformStart);
|
||||
this.events.addListener('transformEnd', transformEnd);
|
||||
|
@ -186,6 +191,7 @@ export class HistoryComponent extends EngineComponent {
|
|||
this.events.addListener('remove', removeEvent);
|
||||
this.events.addListener('undo', undo);
|
||||
this.events.addListener('redo', redo);
|
||||
this.events.addListener('resetHistory', resetEvent);
|
||||
|
||||
return () => {
|
||||
this.events.removeEventListener('change', changeEvent);
|
||||
|
@ -195,6 +201,7 @@ export class HistoryComponent extends EngineComponent {
|
|||
this.events.removeEventListener('remove', removeEvent);
|
||||
this.events.removeEventListener('undo', undo);
|
||||
this.events.removeEventListener('redo', redo);
|
||||
this.events.removeEventListener('resetHistory', resetEvent);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
Wedge,
|
||||
WedgeCorner,
|
||||
WedgeInnerCorner,
|
||||
World,
|
||||
} from '@freeblox/engine';
|
||||
import {
|
||||
AxesHelper,
|
||||
|
@ -31,7 +32,6 @@ import {
|
|||
} from '../types/events';
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
||||
import { TransformControls } from 'three/examples/jsm/controls/TransformControls.js';
|
||||
import { World } from '@freeblox/engine/dist/gameobjects/world.object';
|
||||
|
||||
/**
|
||||
* This component does most of the work related to editing the level.
|
||||
|
@ -349,6 +349,25 @@ export class WorkspaceComponent extends EngineComponent {
|
|||
if (this.cutOperation) this.cutOperation = false;
|
||||
};
|
||||
|
||||
const resetEvent = () => {
|
||||
const oldSelection = [...this.selection];
|
||||
this.cutOperation = false;
|
||||
this.transforming = false;
|
||||
this.selection.length = 0;
|
||||
this.clipboard.length = 0;
|
||||
this.transformPosition = undefined;
|
||||
this.transformRotation = undefined;
|
||||
this.transformScale = undefined;
|
||||
this.transforming = false;
|
||||
oldSelection.forEach((selection) =>
|
||||
this.events.emit('deselected', {
|
||||
object: selection,
|
||||
selection: [],
|
||||
})
|
||||
);
|
||||
this.events.emit('resetHistory');
|
||||
};
|
||||
|
||||
this.events.addListener('mouseDown', mouseDownEventHandler);
|
||||
this.events.addListener('mouseMove', mouseMoveEventHandler);
|
||||
this.events.addListener('mouseUp', mouseUpEventHandler);
|
||||
|
@ -364,6 +383,7 @@ export class WorkspaceComponent extends EngineComponent {
|
|||
this.events.addListener('paste', pasteEvent);
|
||||
this.events.addListener('delete', deleteEvent);
|
||||
this.events.addListener('undo', undoEvent);
|
||||
this.events.addListener('reset', resetEvent);
|
||||
|
||||
return () => {
|
||||
this.events.removeEventListener('mouseDown', mouseDownEventHandler);
|
||||
|
@ -384,6 +404,7 @@ export class WorkspaceComponent extends EngineComponent {
|
|||
this.events.removeEventListener('paste', pasteEvent);
|
||||
this.events.removeEventListener('delete', deleteEvent);
|
||||
this.events.removeEventListener('undo', undoEvent);
|
||||
this.events.removeEventListener('reset', resetEvent);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ export type Events = {
|
|||
copy: () => void;
|
||||
delete: () => void;
|
||||
paste: (event: Object3D | undefined) => void;
|
||||
resetHistory: () => void;
|
||||
};
|
||||
|
||||
export type EditorEvents = Events & EngineEvents;
|
||||
|
|
|
@ -92,6 +92,13 @@ export class AssetManagerFactory {
|
|||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Free all assets from memory
|
||||
*/
|
||||
freeAll() {
|
||||
this.assets.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load texture
|
||||
* @param path Path
|
||||
|
|
|
@ -15,6 +15,7 @@ import { World } from '../gameobjects/world.object';
|
|||
import { instancableGameObjects } from '../gameobjects';
|
||||
import { Object3D } from 'three';
|
||||
import { GameObject } from '../types/game-object';
|
||||
import { environmentDefaults } from '../defaults/environment';
|
||||
|
||||
/**
|
||||
* Game level management component
|
||||
|
@ -113,16 +114,25 @@ export class LevelComponent extends EngineComponent {
|
|||
const instanceEvent = (event: InstanceEvent) =>
|
||||
this.createObject(event.type, event.parent);
|
||||
|
||||
const resetEvent = () => {
|
||||
this.world.clear();
|
||||
assetManager.freeAll();
|
||||
this.events.emit('setEnvironment', environmentDefaults);
|
||||
Object.assign(this.environment, environmentDefaults);
|
||||
};
|
||||
|
||||
this.events.addListener('change', changeEvent);
|
||||
this.events.addListener('remove', removeEvent);
|
||||
this.events.addListener('reparent', reparentEvent);
|
||||
this.events.addListener('instance', instanceEvent);
|
||||
this.events.addListener('reset', resetEvent);
|
||||
|
||||
return () => {
|
||||
this.events.removeEventListener('change', changeEvent);
|
||||
this.events.removeEventListener('remove', removeEvent);
|
||||
this.events.removeEventListener('reparent', reparentEvent);
|
||||
this.events.removeEventListener('instance', instanceEvent);
|
||||
this.events.removeEventListener('reset', resetEvent);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
import { Color, Vector3 } from 'three';
|
||||
|
||||
export const environmentDefaults = {
|
||||
sunColor: new Color(0xffffff),
|
||||
sunPosition: new Vector3(1, 1, 1),
|
||||
sunStrength: 1,
|
||||
ambientColor: new Color(0x8a8a8a),
|
||||
ambientStrength: 1,
|
||||
clearColor: new Color(0x00aaff),
|
||||
};
|
|
@ -5,6 +5,7 @@ import {
|
|||
SerializedObject,
|
||||
} from '../types/game-object';
|
||||
import { Property } from '../types/property';
|
||||
import { environmentDefaults } from '../defaults/environment';
|
||||
|
||||
export const environmentEditorProperties: EditorProperties = {
|
||||
sunColor: new Property('sunColor', Color, true, []),
|
||||
|
@ -20,12 +21,12 @@ export class Environment extends GameObject {
|
|||
public name = Environment.name;
|
||||
public virtual = true;
|
||||
|
||||
sunColor = new Color(0xffffff);
|
||||
sunPosition = new Vector3(1, 1, 1);
|
||||
sunStrength = 1;
|
||||
ambientColor = new Color(0x8a8a8a);
|
||||
ambientStrength = 1;
|
||||
clearColor = new Color(0x00aaff);
|
||||
sunColor = environmentDefaults.sunColor.clone();
|
||||
sunPosition = environmentDefaults.sunPosition.clone();
|
||||
sunStrength = environmentDefaults.sunStrength;
|
||||
ambientColor = environmentDefaults.ambientColor.clone();
|
||||
ambientStrength = environmentDefaults.ambientStrength;
|
||||
clearColor = environmentDefaults.clearColor.clone();
|
||||
|
||||
constructor() {
|
||||
super(environmentEditorProperties);
|
||||
|
|
|
@ -17,4 +17,5 @@ export const instancableGameObjects: Record<string, Instancable<GameObject>> = {
|
|||
};
|
||||
|
||||
export * from './environment.object';
|
||||
export * from './world.object';
|
||||
export { Cylinder, Brick, Sphere, Wedge, WedgeCorner, WedgeInnerCorner };
|
||||
|
|
|
@ -4,3 +4,4 @@ export * from './types';
|
|||
export * from './components';
|
||||
export * from './gameobjects';
|
||||
export * from './assets';
|
||||
export * from './defaults/environment';
|
||||
|
|
|
@ -79,4 +79,5 @@ export type EngineEvents = {
|
|||
sceneJoin: (event: Object3D) => void;
|
||||
sceneLeave: (event: Object3D) => void;
|
||||
initialized: () => void;
|
||||
reset: () => void;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue