From 2e955995b80d76f85687b0d42aa3066a4c41b5d0 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Fri, 28 Apr 2023 09:15:39 -0400 Subject: [PATCH 01/59] Update version. --- .../editor/plugins/phasereditor2d.ide/src/IDEPlugin.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.ide/src/IDEPlugin.ts b/source/editor/plugins/phasereditor2d.ide/src/IDEPlugin.ts index f09134110..c274ccc6c 100644 --- a/source/editor/plugins/phasereditor2d.ide/src/IDEPlugin.ts +++ b/source/editor/plugins/phasereditor2d.ide/src/IDEPlugin.ts @@ -149,15 +149,15 @@ namespace phasereditor2d.ide { const search = startScene ? `?start=${startScene}` : ""; - const url = (config.playUrl || colibri.ui.ide.FileUtils.getRoot().getExternalUrl()) - + search; - colibri.Platform.onElectron(electron => { - colibri.core.io.apiRequest("OpenBrowser", { url: config.playUrl }); + colibri.core.io.apiRequest("OpenBrowser", { url: config.playUrl, search }); }, () => { + const url = (config.playUrl || colibri.ui.ide.FileUtils.getRoot().getExternalUrl()) + + search; + controls.Controls.openUrlInNewPage(url); }); } @@ -317,7 +317,7 @@ namespace phasereditor2d.ide { /* program entry point */ - export const VER = "3.60.3"; + export const VER = "3.60.4-dev"; async function main() { From 30181c8f62c4554486b085257a30fae518783c0c Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Fri, 28 Apr 2023 10:09:31 -0400 Subject: [PATCH 02/59] Simplifies the Play command. --- .../phasereditor2d.ide/src/IDEPlugin.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.ide/src/IDEPlugin.ts b/source/editor/plugins/phasereditor2d.ide/src/IDEPlugin.ts index c274ccc6c..ae11c6438 100644 --- a/source/editor/plugins/phasereditor2d.ide/src/IDEPlugin.ts +++ b/source/editor/plugins/phasereditor2d.ide/src/IDEPlugin.ts @@ -133,6 +133,8 @@ namespace phasereditor2d.ide { openBrowser(url: string) { + console.log("Opening browser for: " + url); + colibri.Platform.onElectron(electron => { colibri.core.io.apiRequest("OpenBrowser", { url }); @@ -149,17 +151,20 @@ namespace phasereditor2d.ide { const search = startScene ? `?start=${startScene}` : ""; - colibri.Platform.onElectron(electron => { + let url: string; - colibri.core.io.apiRequest("OpenBrowser", { url: config.playUrl, search }); + if (config.playUrl) { - }, () => { + url = config.playUrl + search; - const url = (config.playUrl || colibri.ui.ide.FileUtils.getRoot().getExternalUrl()) - + search; + } else { - controls.Controls.openUrlInNewPage(url); - }); + const {protocol, host} = window.location; + + url = `${protocol}//${host}/editor/external/${search}`; + } + + this.openBrowser(url); } async requestUpdateAvailable() { From b3c36fd5e3a3a4400a2ab8fa2c39a11481735089 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Fri, 28 Apr 2023 14:33:34 -0400 Subject: [PATCH 03/59] Adds new scope LOCAL_NESTED_PREFAB. --- .../src/ui/sceneobjects/EditorSupport.ts | 4 +++- .../ui/sceneobjects/GameObjectEditorSupport.ts | 17 +++++++++++++++++ .../ui/sceneobjects/object/VariableComponent.ts | 10 ++++++++-- .../properties/GameObjectVariableSection.ts | 2 +- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts index 0d5393573..fcfbee799 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts @@ -7,6 +7,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { METHOD = "METHOD", CLASS = "CLASS", PUBLIC = "PUBLIC", + LOCAL_NESTED_PREFAB = "LOCAL_NESTED_PREFAB", NESTED_PREFAB = "NESTED_PREFAB" } @@ -97,7 +98,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { isNestedPrefabScope() { - return this._scope === ObjectScope.NESTED_PREFAB; + return this._scope === ObjectScope.NESTED_PREFAB + || this._scope === ObjectScope.LOCAL_NESTED_PREFAB; } isPublic() { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts index 1ee9fb474..9a0f092ac 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts @@ -12,6 +12,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { private _componentMap: Map>; private _unlockedProperties: Set; private _isNestedPrefabInstance: boolean; + private _isNestedPrefanInstanceBranch: boolean; // parent private _allowPickChildren: boolean; @@ -29,6 +30,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { this._serializables = []; this._componentMap = new Map(); this._isNestedPrefabInstance = false; + this._isNestedPrefanInstanceBranch = false; this._allowPickChildren = true; this._showChildrenInOutline = true; @@ -671,6 +673,21 @@ namespace phasereditor2d.scene.ui.sceneobjects { return false; } + isNestedPrefabInstanceParent() { + + for(const obj of this.getObjectChildren()) { + + const objES = obj.getEditorSupport(); + + if (objES.isNestedPrefabInstance() || objES.isNestedPrefabInstanceParent()) { + + return true; + } + } + + return false; + } + isNestedPrefabInstance() { return this._isNestedPrefabInstance; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/VariableComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/VariableComponent.ts index 7ac25f298..da39a1dcb 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/VariableComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/VariableComponent.ts @@ -30,7 +30,13 @@ namespace phasereditor2d.scene.ui.sceneobjects { local: true, getValue: obj => obj.getEditorSupport().getScope(), setValue: (obj, value) => obj.getEditorSupport().setScope(value), - values: [ObjectScope.METHOD, ObjectScope.CLASS, ObjectScope.PUBLIC, ObjectScope.NESTED_PREFAB], + values: [ + ObjectScope.METHOD, + ObjectScope.CLASS, + ObjectScope.PUBLIC, + ObjectScope.LOCAL_NESTED_PREFAB, + ObjectScope.NESTED_PREFAB + ], getValueLabel: value => value.split("_").map(v => v[0] + v.substring(1).toLowerCase()).join(" ") }; @@ -47,7 +53,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { if (this.getEditorSupport().isUseGameObjectName()) { const dom = new code.AssignPropertyCodeDOM("name", args.objectVarName); - + dom.valueLiteral(this.getEditorSupport().getLabel()); args.statements.push(dom); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/GameObjectVariableSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/GameObjectVariableSection.ts index 7b2f9ccec..52eac40f9 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/GameObjectVariableSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/GameObjectVariableSection.ts @@ -61,7 +61,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { return true; } - return scope !== ObjectScope.NESTED_PREFAB; + return scope !== ObjectScope.NESTED_PREFAB && scope !== ObjectScope.LOCAL_NESTED_PREFAB; }); } } From f20fdbc2026fbddc64728f80928b7604984b10e8 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Fri, 28 Apr 2023 14:39:38 -0400 Subject: [PATCH 04/59] Includes local_nested_prefab in all references to local_prefab. --- .../phasereditor2d.scene/src/core/json/SceneFinder.ts | 3 ++- .../src/ui/sceneobjects/EditorSupport.ts | 2 +- .../src/ui/sceneobjects/GameObjectEditorSupport.ts | 6 ++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneFinder.ts b/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneFinder.ts index ebce30ae2..f3b94c160 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneFinder.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneFinder.ts @@ -283,7 +283,8 @@ namespace phasereditor2d.scene.core.json { for (const c of objData.list) { - if (c.scope === ui.sceneobjects.ObjectScope.NESTED_PREFAB) { + if (c.scope === ui.sceneobjects.ObjectScope.NESTED_PREFAB + || c.scope === ui.sceneobjects.ObjectScope.LOCAL_NESTED_PREFAB) { prefabObjectId_ObjectData_Map.set(c.id, c); prefabId_File_Map.set(c.id, file); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts index fcfbee799..cc4e2bf05 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts @@ -8,7 +8,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { CLASS = "CLASS", PUBLIC = "PUBLIC", LOCAL_NESTED_PREFAB = "LOCAL_NESTED_PREFAB", - NESTED_PREFAB = "NESTED_PREFAB" + NESTED_PREFAB = "NESTED_PREFAB", } export abstract class EditorSupport { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts index 9a0f092ac..b5b979958 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts @@ -1259,7 +1259,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { const prefabData = prefabChildren[i]; - if (prefabData.scope === sceneobjects.ObjectScope.NESTED_PREFAB) { + if (prefabData.scope === sceneobjects.ObjectScope.NESTED_PREFAB + || prefabData.scope === sceneobjects.ObjectScope.LOCAL_NESTED_PREFAB) { sprite.getEditorSupport()._setNestedPrefabInstance(true); } @@ -1288,7 +1289,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { for (const originalChild of originalPrefabChildren) { - if (originalChild.scope !== sceneobjects.ObjectScope.NESTED_PREFAB) { + if (originalChild.scope !== sceneobjects.ObjectScope.NESTED_PREFAB + && originalChild.scope !== sceneobjects.ObjectScope.LOCAL_NESTED_PREFAB) { result.push(originalChild); From 11ad34eaa104c491e8dcf28a8e473b9e01e1efe1 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Fri, 28 Apr 2023 15:15:22 -0400 Subject: [PATCH 05/59] Hides local nested prefabs from the Outline view. --- .../SceneEditorOutlineContentProvider.ts | 26 +++++++++++++- .../sceneobjects/GameObjectEditorSupport.ts | 35 +++++++++++++------ 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineContentProvider.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineContentProvider.ts index 0bab7dcf3..5b7a04d20 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineContentProvider.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineContentProvider.ts @@ -71,7 +71,9 @@ namespace phasereditor2d.scene.ui.editor.outline { } else if (parentES.isPrefabInstance()) { - const prefabChildren = parentES.getMutableNestedPrefabChildren(); + const prefabChildren: sceneobjects.ISceneGameObject[] = []; + + this.getPublicMutableNestedPrefabChildren(parent, prefabChildren); const appendedChildren = parentES.getAppendedChildren(); @@ -125,5 +127,27 @@ namespace phasereditor2d.scene.ui.editor.outline { return []; } + + private getPublicMutableNestedPrefabChildren(parent: sceneobjects.ISceneGameObject, list: sceneobjects.ISceneGameObject[]) { + + const parentES = parent.getEditorSupport(); + + for(const child of parentES.getMutableNestedPrefabChildren()) { + + const childES = child.getEditorSupport(); + + if (childES.isMutableNestedPrefabInstance()) { + + if (childES.isLocalNestedPrefabInstance()) { + + this.getPublicMutableNestedPrefabChildren(child, list); + + } else { + + list.push(child); + } + } + } + } } } \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts index b5b979958..80b093102 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts @@ -12,7 +12,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { private _componentMap: Map>; private _unlockedProperties: Set; private _isNestedPrefabInstance: boolean; - private _isNestedPrefanInstanceBranch: boolean; + private _isLocalNestedPrefabInstance: boolean; // parent private _allowPickChildren: boolean; @@ -30,7 +30,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { this._serializables = []; this._componentMap = new Map(); this._isNestedPrefabInstance = false; - this._isNestedPrefanInstanceBranch = false; + this._isLocalNestedPrefabInstance = false; this._allowPickChildren = true; this._showChildrenInOutline = true; @@ -662,7 +662,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { const parent = this.getObjectParent(); const parentES = parent?.getEditorSupport(); - if (!parent + if (!parent || !parentES.isPrefabInstance() || this.isPrefeabInstanceAppendedChild()) { @@ -675,7 +675,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { isNestedPrefabInstanceParent() { - for(const obj of this.getObjectChildren()) { + for (const obj of this.getObjectChildren()) { const objES = obj.getEditorSupport(); @@ -693,11 +693,21 @@ namespace phasereditor2d.scene.ui.sceneobjects { return this._isNestedPrefabInstance; } - _setNestedPrefabInstance(isNestedPrefabInstace: boolean) { + private _setNestedPrefabInstance(isNestedPrefabInstace: boolean) { this._isNestedPrefabInstance = isNestedPrefabInstace; } + isLocalNestedPrefabInstance() { + + return this._isLocalNestedPrefabInstance; + } + + private _setLocalNestedPrefabInstance(isLocalNestedPrefabInstance: boolean) { + + this._isLocalNestedPrefabInstance = isLocalNestedPrefabInstance; + } + isPrefabInstance() { return this._prefabId !== undefined && this._prefabId !== null; @@ -833,7 +843,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { const result = []; - for(const obj of children) { + for (const obj of children) { const objES = obj.getEditorSupport(); @@ -1252,17 +1262,22 @@ namespace phasereditor2d.scene.ui.sceneobjects { if (sprite) { - parent.getEditorSupport().addObjectChild(sprite); + parentES.addObjectChild(sprite); + + const spriteES = sprite.getEditorSupport(); // if it is not an appended child if (i < prefabChildren.length) { const prefabData = prefabChildren[i]; + const { scope } = prefabData; - if (prefabData.scope === sceneobjects.ObjectScope.NESTED_PREFAB - || prefabData.scope === sceneobjects.ObjectScope.LOCAL_NESTED_PREFAB) { + if (scope === sceneobjects.ObjectScope.NESTED_PREFAB + || scope === sceneobjects.ObjectScope.LOCAL_NESTED_PREFAB) { - sprite.getEditorSupport()._setNestedPrefabInstance(true); + spriteES._setNestedPrefabInstance(true); + spriteES._setLocalNestedPrefabInstance( + scope === sceneobjects.ObjectScope.LOCAL_NESTED_PREFAB); } } From 7a6a1086f7aa4e7ad0183e0804090d291f199c61 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Fri, 28 Apr 2023 15:49:35 -0400 Subject: [PATCH 06/59] Doesn't generate a field for local nested prefabs. --- .../phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts | 1 + .../phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts b/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts index d54e5d20a..ff941441a 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts @@ -875,6 +875,7 @@ namespace phasereditor2d.scene.core.code { if (!objES.isMethodScope()) { createObjectMethodCall.setDeclareReturnToVar(true); + this._objectsToFieldList.push(obj); } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts index cc4e2bf05..b06bab156 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts @@ -63,7 +63,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { isMethodScope() { - return this._scope === ObjectScope.METHOD; + return this._scope === ObjectScope.METHOD + || this._scope === ObjectScope.LOCAL_NESTED_PREFAB; } getObject() { From 6fff7051a7bbbfe6d78f843e0cb52396e764d636 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 2 May 2023 00:25:01 -0400 Subject: [PATCH 07/59] Introduces new scope values: method_nested_prefab, class_nested_prefab, public_nested_prefab. --- .../phasereditor2d.scene/src/ScenePlugin.ts | 1 + .../src/core/json/SceneFinder.ts | 3 +- .../migrations/NestedPrefaScopeMigration.ts | 37 +++++++++++++++++++ .../src/core/migrations/TextAlignMigration.ts | 4 +- .../phasereditor2d.scene/src/ui/Scene.ts | 2 +- .../src/ui/sceneobjects/EditorSupport.ts | 19 +--------- .../sceneobjects/GameObjectEditorSupport.ts | 8 ++-- .../src/ui/sceneobjects/ObjectScope.ts | 35 ++++++++++++++++++ .../sceneobjects/object/VariableComponent.ts | 9 +---- .../properties/GameObjectVariableSection.ts | 2 +- 10 files changed, 85 insertions(+), 35 deletions(-) create mode 100644 source/editor/plugins/phasereditor2d.scene/src/core/migrations/NestedPrefaScopeMigration.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ObjectScope.ts diff --git a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts index bb55933dd..d9a6b59e7 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts @@ -142,6 +142,7 @@ namespace phasereditor2d.scene { reg.addExtension(new core.migrations.OriginMigration_v2_to_v3()); reg.addExtension(new core.migrations.UnlockPositionMigration_v1_to_v2()); reg.addExtension(new core.migrations.TextAlignMigration()); + reg.addExtension(new core.migrations.NestedPrefaScopeMigration()); // preload docs diff --git a/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneFinder.ts b/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneFinder.ts index f3b94c160..8a67b1311 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneFinder.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneFinder.ts @@ -283,8 +283,7 @@ namespace phasereditor2d.scene.core.json { for (const c of objData.list) { - if (c.scope === ui.sceneobjects.ObjectScope.NESTED_PREFAB - || c.scope === ui.sceneobjects.ObjectScope.LOCAL_NESTED_PREFAB) { + if (ui.sceneobjects.isNestedPrefabScope(c.scope)) { prefabObjectId_ObjectData_Map.set(c.id, c); prefabId_File_Map.set(c.id, file); diff --git a/source/editor/plugins/phasereditor2d.scene/src/core/migrations/NestedPrefaScopeMigration.ts b/source/editor/plugins/phasereditor2d.scene/src/core/migrations/NestedPrefaScopeMigration.ts new file mode 100644 index 000000000..dd3de1568 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/core/migrations/NestedPrefaScopeMigration.ts @@ -0,0 +1,37 @@ +/// +namespace phasereditor2d.scene.core.migrations { + + export class NestedPrefaScopeMigration extends ui.SceneDataMigrationExtension { + + migrate(data: json.ISceneData) { + + if (data.meta.version < 4) { + + this.migrateList(data.displayList); + } + } + + private migrateList(list?: json.IObjectData[]) { + + for (const obj of list) { + + if (obj.scope && obj.scope.toString() == "NESTED_PREFAB") { + + console.log(`Migrate nested prefab scope to PUBLIC_NESTED_PREFAB [${obj.id}]`); + + obj.scope = ui.sceneobjects.ObjectScope.PUBLIC_NESTED_PREFAB; + } + + if (obj.list) { + + this.migrateList(obj.list); + } + + if (obj.nestedPrefabs) { + + this.migrateList(obj.nestedPrefabs); + } + } + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/core/migrations/TextAlignMigration.ts b/source/editor/plugins/phasereditor2d.scene/src/core/migrations/TextAlignMigration.ts index 5565dc830..0e1a67ffa 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/core/migrations/TextAlignMigration.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/core/migrations/TextAlignMigration.ts @@ -5,8 +5,6 @@ namespace phasereditor2d.scene.core.migrations { migrate(data: json.ISceneData) { - console.log("Migrating: Text align"); - this.migrateList(data.displayList); } @@ -23,6 +21,8 @@ namespace phasereditor2d.scene.core.migrations { const alignProp = ui.sceneobjects.TextComponent.align; objData.align = alignProp.values[objData.align] ?? alignProp.defValue; + + console.log(`Migrate Text align to ${objData.align} [${objData.id}]`); } } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/Scene.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/Scene.ts index 6e983b934..2e91883c5 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/Scene.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/Scene.ts @@ -3,7 +3,7 @@ namespace phasereditor2d.scene.ui { export class Scene extends BaseScene { - static CURRENT_VERSION = 3; + static CURRENT_VERSION = 4; private _id: string; private _sceneType: core.json.SceneType; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts index b06bab156..bc3b8b50f 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts @@ -2,15 +2,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { import controls = colibri.ui.controls; - export enum ObjectScope { - - METHOD = "METHOD", - CLASS = "CLASS", - PUBLIC = "PUBLIC", - LOCAL_NESTED_PREFAB = "LOCAL_NESTED_PREFAB", - NESTED_PREFAB = "NESTED_PREFAB", - } - export abstract class EditorSupport { private _object: T; @@ -64,7 +55,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { isMethodScope() { return this._scope === ObjectScope.METHOD - || this._scope === ObjectScope.LOCAL_NESTED_PREFAB; + || this._scope === ObjectScope.METHOD_NESTED_PREFAB; } getObject() { @@ -97,16 +88,10 @@ namespace phasereditor2d.scene.ui.sceneobjects { return this._scope; } - isNestedPrefabScope() { - - return this._scope === ObjectScope.NESTED_PREFAB - || this._scope === ObjectScope.LOCAL_NESTED_PREFAB; - } - isPublic() { return this._scope === ObjectScope.PUBLIC - || this._scope === ObjectScope.NESTED_PREFAB; + || this._scope === ObjectScope.PUBLIC_NESTED_PREFAB; } setScope(scope: ObjectScope) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts index 80b093102..50c1f167e 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts @@ -1272,12 +1272,11 @@ namespace phasereditor2d.scene.ui.sceneobjects { const prefabData = prefabChildren[i]; const { scope } = prefabData; - if (scope === sceneobjects.ObjectScope.NESTED_PREFAB - || scope === sceneobjects.ObjectScope.LOCAL_NESTED_PREFAB) { + if (ui.sceneobjects.isNestedPrefabScope(scope)) { spriteES._setNestedPrefabInstance(true); spriteES._setLocalNestedPrefabInstance( - scope === sceneobjects.ObjectScope.LOCAL_NESTED_PREFAB); + scope !== sceneobjects.ObjectScope.PUBLIC_NESTED_PREFAB); } } @@ -1304,8 +1303,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { for (const originalChild of originalPrefabChildren) { - if (originalChild.scope !== sceneobjects.ObjectScope.NESTED_PREFAB - && originalChild.scope !== sceneobjects.ObjectScope.LOCAL_NESTED_PREFAB) { + if (!sceneobjects.isNestedPrefabScope(originalChild.scope)) { result.push(originalChild); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ObjectScope.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ObjectScope.ts new file mode 100644 index 000000000..b408622e1 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ObjectScope.ts @@ -0,0 +1,35 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + export enum ObjectScope { + + METHOD = "METHOD", + CLASS = "CLASS", + PUBLIC = "PUBLIC", + METHOD_NESTED_PREFAB = "METHOD_NESTED_PREFAB", + CLASS_NESTED_PREFAB = "CLASS_NESTED_PREFAB", + PUBLIC_NESTED_PREFAB = "PUBLIC_NESTED_PREFAB", + } + + export const OBJECT_SCOPES = [ + ObjectScope.METHOD, + ObjectScope.CLASS, + ObjectScope.PUBLIC, + ObjectScope.METHOD_NESTED_PREFAB, + ObjectScope.CLASS_NESTED_PREFAB, + ObjectScope.PUBLIC_NESTED_PREFAB + ]; + + export function isNestedPrefabScope(scope: ObjectScope) { + + switch (scope) { + + case ObjectScope.METHOD_NESTED_PREFAB: + case ObjectScope.CLASS_NESTED_PREFAB: + case ObjectScope.PUBLIC_NESTED_PREFAB: + + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/VariableComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/VariableComponent.ts index da39a1dcb..746a5fad3 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/VariableComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/VariableComponent.ts @@ -1,3 +1,4 @@ +/// namespace phasereditor2d.scene.ui.sceneobjects { import code = scene.core.code; @@ -30,13 +31,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { local: true, getValue: obj => obj.getEditorSupport().getScope(), setValue: (obj, value) => obj.getEditorSupport().setScope(value), - values: [ - ObjectScope.METHOD, - ObjectScope.CLASS, - ObjectScope.PUBLIC, - ObjectScope.LOCAL_NESTED_PREFAB, - ObjectScope.NESTED_PREFAB - ], + values: OBJECT_SCOPES, getValueLabel: value => value.split("_").map(v => v[0] + v.substring(1).toLowerCase()).join(" ") }; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/GameObjectVariableSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/GameObjectVariableSection.ts index 52eac40f9..b542d97f5 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/GameObjectVariableSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/GameObjectVariableSection.ts @@ -61,7 +61,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { return true; } - return scope !== ObjectScope.NESTED_PREFAB && scope !== ObjectScope.LOCAL_NESTED_PREFAB; + return !isNestedPrefabScope(scope); }); } } From 65850d57fde421949cf9cbc11445300cc42d3858 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 2 May 2023 00:35:55 -0400 Subject: [PATCH 08/59] Shows warning message if a scene file was created by a newer version of the editor. --- .../phasereditor2d.scene/src/ScenePlugin.ts | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts index d9a6b59e7..9cd976926 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts @@ -490,7 +490,7 @@ namespace phasereditor2d.scene { getPrefabColor() { - return colibri.ui.controls.Controls.getTheme().dark? "lightGreen" : "darkGreen"; + return colibri.ui.controls.Controls.getTheme().dark ? "lightGreen" : "darkGreen"; } getNestedPrefabColor() { @@ -639,8 +639,33 @@ namespace phasereditor2d.scene { dlg.close(); } + private _showIncompatibilityMessage = true; + runSceneDataMigrations(sceneData: core.json.ISceneData) { + + // check scene data min supported version + + if (this._showIncompatibilityMessage) { + + const version = sceneData.meta.version; + + if (version) { + + if (version > ui.Scene.CURRENT_VERSION) { + + alert(` + The project contains scene files created by newer versions of the editor. + You should update the editor. + `); + } + } + + this._showIncompatibilityMessage = false; + } + + // check migrations + const migrations = colibri.Platform.getExtensionRegistry() .getExtensions(ui.SceneDataMigrationExtension.POINT_ID); From b87be6deaae9ee4095463625fe13d1290bbe6440 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 2 May 2023 00:43:21 -0400 Subject: [PATCH 09/59] Rename some object scope related methods. --- .../src/core/code/SceneCodeDOMBuilder.ts | 10 +++++----- .../src/ui/sceneobjects/EditorSupport.ts | 12 ++++++------ .../src/ui/sceneobjects/ObjectScope.ts | 13 +++++++++++++ 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts b/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts index ff941441a..60bea65a2 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts @@ -282,7 +282,7 @@ namespace phasereditor2d.scene.core.code { ? objES.getPrefabName() : (explicitType ? explicitType : objES.getPhaserType()); - const isPublic = objES.isPublic(); + const isPublic = objES.isPublicScope(); const field = new FieldDeclCodeDOM(varName, type, isPublic); // Allow undefined if the object is part of a scene. @@ -490,11 +490,11 @@ namespace phasereditor2d.scene.core.code { private addCreatePlainObjectCode( obj: ui.sceneobjects.IScenePlainObject, firstStatements: CodeDOM[], lazyStatements: CodeDOM[]) { - const objSupport = obj.getEditorSupport(); + const objES = obj.getEditorSupport(); - const varname = formatToValidVarName(objSupport.getLabel()); + const varname = formatToValidVarName(objES.getLabel()); - const result = objSupport.getExtension().buildCreateObjectWithFactoryCodeDOM({ + const result = objES.getExtension().buildCreateObjectWithFactoryCodeDOM({ gameObjectFactoryExpr: this._scene.isPrefabSceneType() ? "scene" : "this", obj: obj, varname @@ -519,7 +519,7 @@ namespace phasereditor2d.scene.core.code { const objectFactoryMethodCall = result.objectFactoryMethodCall; - if (!objSupport.isMethodScope()) { + if (!objES.isMethodScope()) { objectFactoryMethodCall.setDeclareReturnToVar(true); } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts index bc3b8b50f..046e0de20 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts @@ -58,6 +58,12 @@ namespace phasereditor2d.scene.ui.sceneobjects { || this._scope === ObjectScope.METHOD_NESTED_PREFAB; } + isPublicScope() { + + return this._scope === ObjectScope.PUBLIC + || this._scope === ObjectScope.PUBLIC_NESTED_PREFAB; + } + getObject() { return this._object; @@ -88,12 +94,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { return this._scope; } - isPublic() { - - return this._scope === ObjectScope.PUBLIC - || this._scope === ObjectScope.PUBLIC_NESTED_PREFAB; - } - setScope(scope: ObjectScope) { this._scope = scope; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ObjectScope.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ObjectScope.ts index b408622e1..f56e4fd7c 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ObjectScope.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ObjectScope.ts @@ -32,4 +32,17 @@ namespace phasereditor2d.scene.ui.sceneobjects { return false; } + + export function isPublicScope(scope: ObjectScope) { + + switch (scope) { + + case ObjectScope.PUBLIC: + case ObjectScope.PUBLIC_NESTED_PREFAB: + + return true; + } + + return false; + } } \ No newline at end of file From 33f0418c0253c24abce0ceddb193e2a58575e14a Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 2 May 2023 01:39:54 -0400 Subject: [PATCH 10/59] Improves running migrations. --- .../phasereditor2d.scene/src/ScenePlugin.ts | 1 - .../src/core/json/SceneFinder.ts | 15 ++------------- .../src/ui/sceneobjects/ObjectScope.ts | 6 +++--- .../ui/sceneobjects/object/VariableComponent.ts | 2 +- .../ObjectConstructorPropertyType.ts | 2 -- .../userProperties/SceneKeyPropertyType.ts | 2 -- 6 files changed, 6 insertions(+), 22 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts index 9cd976926..3fbfcdd7f 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts @@ -643,7 +643,6 @@ namespace phasereditor2d.scene { runSceneDataMigrations(sceneData: core.json.ISceneData) { - // check scene data min supported version if (this._showIncompatibilityMessage) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneFinder.ts b/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneFinder.ts index 8a67b1311..e78acea2c 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneFinder.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneFinder.ts @@ -24,8 +24,6 @@ namespace phasereditor2d.scene.core.json { async preload(monitor: controls.IProgressMonitor) { await this._finder.preload(monitor); - - this._finder.runMigrations(); } } @@ -73,7 +71,6 @@ namespace phasereditor2d.scene.core.json { colibri.ui.ide.FileUtils.getFileStorage().addChangeListener(async (e) => { await this.handleStorageChange(e); - }); } @@ -135,14 +132,6 @@ namespace phasereditor2d.scene.core.json { monitor.step(); } - runMigrations() { - - for (const data of this._sceneFilename_Data_Map.values()) { - - ScenePlugin.getInstance().runSceneDataMigrations(data); - } - } - private async preloadComponentsFiles(monitor: controls.IProgressMonitor): Promise { const compFiles = []; @@ -214,7 +203,7 @@ namespace phasereditor2d.scene.core.json { const data = JSON.parse(content) as ISceneData; - // await ScenePlugin.getInstance().runSceneDataMigrations(data); + ScenePlugin.getInstance().runSceneDataMigrations(data); sceneFilename_Data_Map.set(file.getFullName(), data); { @@ -225,7 +214,6 @@ namespace phasereditor2d.scene.core.json { if (data.id) { - if (sceneIdSet.has(data.id)) { const mappedFile = prefabId_File_Map.get(data.id); @@ -257,6 +245,7 @@ namespace phasereditor2d.scene.core.json { sceneFiles.push(file); } catch (e) { + console.error(`SceneDataTable: parsing file ${file.getFullName()}. Error: ${(e as Error).message}`); } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ObjectScope.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ObjectScope.ts index f56e4fd7c..6668f2411 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ObjectScope.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ObjectScope.ts @@ -5,9 +5,9 @@ namespace phasereditor2d.scene.ui.sceneobjects { METHOD = "METHOD", CLASS = "CLASS", PUBLIC = "PUBLIC", - METHOD_NESTED_PREFAB = "METHOD_NESTED_PREFAB", - CLASS_NESTED_PREFAB = "CLASS_NESTED_PREFAB", - PUBLIC_NESTED_PREFAB = "PUBLIC_NESTED_PREFAB", + METHOD_NESTED_PREFAB = "NESTED_PREFAB_METHOD", + CLASS_NESTED_PREFAB = "NESTED_PREFAB_CLASS", + PUBLIC_NESTED_PREFAB = "NESTED_PREFAB_PUBLIC", } export const OBJECT_SCOPES = [ diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/VariableComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/VariableComponent.ts index 746a5fad3..7c8edf8d7 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/VariableComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/VariableComponent.ts @@ -32,7 +32,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { getValue: obj => obj.getEditorSupport().getScope(), setValue: (obj, value) => obj.getEditorSupport().setScope(value), values: OBJECT_SCOPES, - getValueLabel: value => value.split("_").map(v => v[0] + v.substring(1).toLowerCase()).join(" ") + getValueLabel: value => value.replaceAll("_", " ") // value.split("_").map(v => v[0] + v.substring(1).toLowerCase()).join(" ") }; constructor(obj: ISceneGameObject) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/ObjectConstructorPropertyType.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/ObjectConstructorPropertyType.ts index 6940accd0..d00e3d041 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/ObjectConstructorPropertyType.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/ObjectConstructorPropertyType.ts @@ -50,8 +50,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { const finder = ScenePlugin.getInstance().getSceneFinder(); - await finder.preload(controls.EMPTY_PROGRESS_MONITOR); - const file = finder.getSceneFiles() .find(f => this.valueToString(null, f) === value); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/SceneKeyPropertyType.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/SceneKeyPropertyType.ts index 5457469ee..ea73ce649 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/SceneKeyPropertyType.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/SceneKeyPropertyType.ts @@ -19,8 +19,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { const finder = ScenePlugin.getInstance().getSceneFinder(); - await finder.preload(controls.EMPTY_PROGRESS_MONITOR); - const file = finder.getSceneFiles() .find(f => this.valueToString(null, f) === value); From 009a456c5c6a274aafeb727457c2ffd7538dc502 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 2 May 2023 10:39:41 -0400 Subject: [PATCH 11/59] Fixes Outlile view 3-dots menu positioning. --- source/editor/plugins/colibri/src/ui/controls/Menu.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/editor/plugins/colibri/src/ui/controls/Menu.ts b/source/editor/plugins/colibri/src/ui/controls/Menu.ts index 343bdff4f..29bc7c699 100644 --- a/source/editor/plugins/colibri/src/ui/controls/Menu.ts +++ b/source/editor/plugins/colibri/src/ui/controls/Menu.ts @@ -345,7 +345,7 @@ namespace colibri.ui.controls { if (menuRect.width > window.innerWidth - x || openLeft) { - this._element.style.left = targetRect.right - menuRect.width + "px"; + this._element.style.left = Math.max(0, targetRect.right - menuRect.width) + "px"; } if (menuRect.height > window.innerHeight - y) { From 139a6ba7f441ea3d0c835d6fcae4d0e75135f89b Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 2 May 2023 10:43:34 -0400 Subject: [PATCH 12/59] Updates changelog. --- CHANGELOG.MD | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 05bd76d6f..c54c61c03 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -1,5 +1,10 @@ # Change Log +## v3.60.1-dev + +* Introduce the new variable scope: NESTED_PREFAB_METHOD, NESTED_PREFAB_CLASS, NESTED_PREFAB_PUBLIC. It introduce a new scene format version. +* Checks if an scene file was generated by a newer and incompatible version of the editor. + ## v3.60.3 - Apr 27, 2023 * Fixes regression bug with the Play command. From 6d05bf373263d1cb7c386a8ed015fe6272fe5ece Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 2 May 2023 11:03:59 -0400 Subject: [PATCH 13/59] Sorts prefabs in Blocks view: script nodes go after. --- .../SceneEditorBlocksContentProvider.ts | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksContentProvider.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksContentProvider.ts index f660991e9..23486a86e 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksContentProvider.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksContentProvider.ts @@ -16,8 +16,6 @@ namespace phasereditor2d.scene.ui.blocks { const grouping = pack.ui.viewers.AssetPackGrouping; - const SCRIPTS_CATEGORY = "Scripts"; - export class SceneEditorBlocksContentProvider extends pack.ui.viewers.AssetPackContentProvider { private _getPacks: () => pack.core.AssetPack[]; @@ -43,9 +41,9 @@ namespace phasereditor2d.scene.ui.blocks { getRoots(input: any) { - if (this._editor.getScene().isScriptNodePrefabScene()) { + const sceneFinder = ScenePlugin.getInstance().getSceneFinder(); - const sceneFinder = ScenePlugin.getInstance().getSceneFinder(); + if (this._editor.getScene().isScriptNodePrefabScene()) { return [ sceneobjects.ScriptNodeExtension.getInstance(), @@ -125,9 +123,19 @@ namespace phasereditor2d.scene.ui.blocks { const finder = ScenePlugin.getInstance().getSceneFinder(); - const files = (sceneType === "prefabs" ? finder.getPrefabFiles() : finder.getSceneFiles()); + let files = (sceneType === "prefabs" ? finder.getPrefabFiles() : finder.getSceneFiles()); + + files = files.filter(file => SceneMaker.acceptDropFile(file, this._editor.getInput())); + + files.sort((a, b) => { + + const aa = finder.isScriptPrefabFile(a)? 1 : 0; + const bb = finder.isScriptPrefabFile(b)? 1 : 0; + + return aa - bb; + }); - return files.filter(file => SceneMaker.acceptDropFile(file, this._editor.getInput())); + return files; } getChildren(parent: any): any[] { From e9f346766a805d474ce70f4dbdcc48c676c6d7b3 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 2 May 2023 11:05:43 -0400 Subject: [PATCH 14/59] Updates changelog. --- CHANGELOG.MD | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index c54c61c03..d9b1edcf7 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -4,6 +4,7 @@ * Introduce the new variable scope: NESTED_PREFAB_METHOD, NESTED_PREFAB_CLASS, NESTED_PREFAB_PUBLIC. It introduce a new scene format version. * Checks if an scene file was generated by a newer and incompatible version of the editor. +* Sorts prefabs in Blocks view: start with game object prefabs, end with script nodes. ## v3.60.3 - Apr 27, 2023 From 5d8e4541ca89c479f36e5dc66fbedf42f63e9e1f Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 2 May 2023 15:56:44 -0400 Subject: [PATCH 15/59] Shows in Outline view the Object List items #277 --- .../src/ui/controls/viewers/RenderCellArgs.ts | 30 +++---- .../phasereditor2d.scene/src/ScenePlugin.ts | 2 +- .../src/ui/editor/SceneEditor.ts | 15 +++- .../src/ui/editor/SelectionManager.ts | 10 ++- .../SceneEditorOutlineContentProvider.ts | 8 +- .../SceneEditorOutlineRendererProvider.ts | 5 ++ .../SceneEditorOutlineStyledLabelProvider.ts | 4 + .../src/ui/editor/undo/DeleteOperation.ts | 8 ++ .../src/ui/sceneobjects/list/ListSection.ts | 82 ------------------- .../sceneobjects/list/ListVariableSection.ts | 6 +- .../src/ui/sceneobjects/list/ObjectList.ts | 38 ++++++++- .../ui/sceneobjects/list/ObjectListItem.ts | 34 ++++++++ .../list/ObjectListItemCellRenderer.ts | 47 +++++++++++ .../list/ObjectListItemSection.ts | 63 ++++++++++++++ 14 files changed, 244 insertions(+), 108 deletions(-) delete mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ListSection.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ObjectListItem.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ObjectListItemCellRenderer.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ObjectListItemSection.ts diff --git a/source/editor/plugins/colibri/src/ui/controls/viewers/RenderCellArgs.ts b/source/editor/plugins/colibri/src/ui/controls/viewers/RenderCellArgs.ts index fbadc6e42..40c798d7e 100644 --- a/source/editor/plugins/colibri/src/ui/controls/viewers/RenderCellArgs.ts +++ b/source/editor/plugins/colibri/src/ui/controls/viewers/RenderCellArgs.ts @@ -1,20 +1,20 @@ namespace colibri.ui.controls.viewers { - export class RenderCellArgs { + export class RenderCellArgs { - constructor( - public canvasContext: CanvasRenderingContext2D, - public x: number, - public y: number, - public w: number, - public h: number, - public obj: any, - public viewer: Viewer, - public center = false) { - } + constructor( + public canvasContext: CanvasRenderingContext2D, + public x: number, + public y: number, + public w: number, + public h: number, + public obj: any, + public viewer: Viewer, + public center = false) { + } - clone() { - return new RenderCellArgs(this.canvasContext, this.x, this.y, this.w, this.h, this.obj, this.viewer, this.center); - } - } + clone() { + return new RenderCellArgs(this.canvasContext, this.x, this.y, this.w, this.h, this.obj, this.viewer, this.center); + } + } } \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts index 3fbfcdd7f..325a0cc68 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts @@ -383,7 +383,7 @@ namespace phasereditor2d.scene { page => new ui.sceneobjects.TextContentSection(page), page => new ui.sceneobjects.TextSection(page), page => new ui.sceneobjects.BitmapTextSection(page), - page => new ui.sceneobjects.ListSection(page), + page => new ui.sceneobjects.ObjectListItemSection(page), page => new ui.sceneobjects.ScenePlainObjectVariableSection(page), page => new ui.sceneobjects.TilemapSection(page), page => new ui.sceneobjects.TilesetSection(page), diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditor.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditor.ts index bd78ff7d3..3835b2465 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditor.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditor.ts @@ -383,7 +383,7 @@ namespace phasereditor2d.scene.ui.editor { if (file) { if (ScenePlugin.getInstance().getSceneFinder().isScriptPrefabFile(file)) { - + return ScenePlugin.getInstance().getIcon(ICON_BUILD); } @@ -568,14 +568,21 @@ namespace phasereditor2d.scene.ui.editor { return this.getSelection() - .filter(obj => sceneobjects.isGameObject(obj)) as any; + .filter(obj => sceneobjects.isGameObject(obj)); } getSelectedLists(): sceneobjects.ObjectList[] { return this.getSelection() - .filter(obj => obj instanceof sceneobjects.ObjectList) as any; + .filter(obj => obj instanceof sceneobjects.ObjectList); + } + + getSelectedListItems(): sceneobjects.ObjectListItem[] { + + return this.getSelection() + + .filter(obj => obj instanceof sceneobjects.ObjectListItem); } getSelectedPlainObjects(): sceneobjects.IScenePlainObject[] { @@ -901,7 +908,7 @@ namespace phasereditor2d.scene.ui.editor { } catch (e) { console.log(e); - + alert(e.message); } } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SelectionManager.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SelectionManager.ts index 0bd3e0e49..af2280d1a 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SelectionManager.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SelectionManager.ts @@ -29,11 +29,14 @@ namespace phasereditor2d.scene.ui.editor { .filter(obj => obj instanceof sceneobjects.ObjectList) .map(obj => (obj as sceneobjects.ObjectList).getId())); + list.push(...selection + .filter(obj => obj instanceof sceneobjects.ObjectListItem) + .map(obj => (obj as sceneobjects.ObjectListItem).getId())) + list.push(...selection .filter(i => i instanceof sceneobjects.UserComponentNode) .map((i: sceneobjects.UserComponentNode) => i.getId())); - list.push(...selection .filter(obj => obj instanceof sceneobjects.UserProperty) .map((p: sceneobjects.UserProperty) => `prefabProperty#${p.getName()}`)) @@ -66,6 +69,11 @@ namespace phasereditor2d.scene.ui.editor { for (const list of this._editor.getScene().getObjectLists().getLists()) { map.set(list.getId(), list); + + for(const item of list.getItems()) { + + map.set(item.getId(), item); + } } for(const prop of scene.getPrefabUserProperties().getProperties()) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineContentProvider.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineContentProvider.ts index 5b7a04d20..23d515b3b 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineContentProvider.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineContentProvider.ts @@ -110,6 +110,12 @@ namespace phasereditor2d.scene.ui.editor.outline { return parent.getLists(); + } else if (parent instanceof sceneobjects.ObjectList) { + + const scene = this._editor.getScene(); + + return parent.getItemsWithObjects(scene); + } else if (typeof parent === "string") { return this._editor.getScene().getPlainObjectsByCategory(parent); @@ -132,7 +138,7 @@ namespace phasereditor2d.scene.ui.editor.outline { const parentES = parent.getEditorSupport(); - for(const child of parentES.getMutableNestedPrefabChildren()) { + for (const child of parentES.getMutableNestedPrefabChildren()) { const childES = child.getEditorSupport(); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineRendererProvider.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineRendererProvider.ts index 17c55c2b6..74afa787f 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineRendererProvider.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineRendererProvider.ts @@ -23,6 +23,11 @@ namespace phasereditor2d.scene.ui.editor.outline { return new controls.viewers.IconImageCellRenderer(ScenePlugin.getInstance().getIcon(ICON_LIST)); + } else if (element instanceof sceneobjects.ObjectListItem) { + + return new sceneobjects.ObjectListItemCellRenderer( + this.getCellRenderer(element.getObject())); + } else if (element instanceof sceneobjects.UserComponentNode) { return new controls.viewers.IconImageCellRenderer(ScenePlugin.getInstance().getIcon(ICON_USER_COMPONENT)); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineStyledLabelProvider.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineStyledLabelProvider.ts index dc8eacc87..9670af5f2 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineStyledLabelProvider.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineStyledLabelProvider.ts @@ -37,6 +37,10 @@ namespace phasereditor2d.scene.ui.editor.outline { return obj.getLabel(); + } else if (obj instanceof sceneobjects.ObjectListItem) { + + return this.getLabel(obj.getObject()); + } else if (obj instanceof sceneobjects.UserComponentNode) { return obj.getComponentName(); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/DeleteOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/DeleteOperation.ts index e3341ea7f..eaf453c2e 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/DeleteOperation.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/DeleteOperation.ts @@ -30,6 +30,14 @@ namespace phasereditor2d.scene.ui.editor.undo { lists.removeListById(obj.getId()); } + for(const obj of editor.getSelectedListItems()) { + + for(const list of lists.getLists()) { + + list.removeItem(obj.getId()); + } + } + for(const obj of editor.getSelectedPrefabProperties()) { obj.getAllProperties().deleteProperty(obj.getName()); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ListSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ListSection.ts deleted file mode 100644 index 6505d9310..000000000 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ListSection.ts +++ /dev/null @@ -1,82 +0,0 @@ -namespace phasereditor2d.scene.ui.sceneobjects { - - import controls = colibri.ui.controls; - - export class ListSection extends editor.properties.BaseSceneSection { - - constructor(page: controls.properties.PropertyPage) { - super(page, "phasereditor2d.scene.ui.sceneobjects.ListSection", "List", true); - } - - createForm(parent: HTMLDivElement) { - - const comp = this.createGridElement(parent); - comp.style.gridTemplateColumns = "1fr"; - comp.style.gridTemplateRows = "1fr auto"; - - const viewer = new controls.viewers.TreeViewer("phasereditor2d.scene.ui.sceneobjects.ListSection"); - viewer.setCellSize(64, true); - viewer.setLabelProvider(new editor.outline.SceneEditorOutlineLabelProvider()); - viewer.setCellRendererProvider(new editor.outline.SceneEditorOutlineRendererProvider()); - viewer.setContentProvider(new controls.viewers.ArrayTreeContentProvider()); - - const filteredViewer = new colibri.ui.ide.properties - .FilteredViewerInPropertySection(this.getPage(), viewer, true); - comp.appendChild(filteredViewer.getElement()); - - this.addUpdater(() => { - - const list = this.getSelectionFirstElement(); - - const map = this.getEditor().getScene().buildObjectIdMap(); - - const input = list.getObjectIds() - .map(id => map.get(id)) - .filter(obj => obj !== undefined); - - viewer.setInput(input); - viewer.setSelection([]); - }); - - const btnRow = document.createElement("div"); - - comp.appendChild(btnRow); - - const selectBtn = this.createButton(btnRow, "Select In Scene", () => { - - this.getEditor().setSelection(viewer.getSelection()); - }); - selectBtn.style.float = "right"; - - const removeBtn = this.createButton(btnRow, "Remove From List", () => { - - this.getUndoManager().add( - new RemoveObjectsFromListOperation( - this.getEditor(), - this.getSelectionFirstElement(), - viewer.getSelection()) - ); - - }); - - removeBtn.style.float = "right"; - removeBtn.style.marginRight = "5px"; - - viewer.eventSelectionChanged.addListener(() => { - - selectBtn.disabled = removeBtn.disabled = viewer.getSelection().length === 0; - }); - } - - canEdit(obj: any, n: number): boolean { - - return obj instanceof ObjectList; - } - - canEditNumber(n: number): boolean { - - return n === 1; - } - - } -} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ListVariableSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ListVariableSection.ts index 0a68e51c1..06554cc49 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ListVariableSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ListVariableSection.ts @@ -53,13 +53,13 @@ namespace phasereditor2d.scene.ui.sceneobjects { this.createLabel(comp, "Scope", "The lexical scope of the object."); const items = [{ - name: "Method", + name: "METHOD", value: ObjectScope.METHOD }, { - name: "Class", + name: "CLASS", value: ObjectScope.CLASS }, { - name: "Public", + name: "PUBLIC", value: ObjectScope.PUBLIC }]; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ObjectList.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ObjectList.ts index 3a859f10a..43cb114ad 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ObjectList.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ObjectList.ts @@ -8,6 +8,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { private _label: string; private _scope: ObjectScope; private _objectIds: string[]; + private _items: ObjectListItem[]; constructor() { @@ -15,37 +16,72 @@ namespace phasereditor2d.scene.ui.sceneobjects { this._label = "list"; this._scope = ObjectScope.CLASS; this._objectIds = []; + this._items = []; + } + + getItemsWithObjects(scene: Scene) { + + const map = scene.buildObjectIdMap(); + + for(const item of this._items) { + + item.setObject(map.get(item.getObjectId())); + } + + return this._items.filter(item => Boolean(item.getObject())); + } + + removeItem(id: string) { + + this._items = this._items.filter(i => i.getId() !== id); + + this._objectIds = this._items.map(i => i.getObjectId()); + } + + getItems() { + + return this._items; } getObjectIds() { + return this._objectIds; } setObjectsIds(ids: string[]) { + this._objectIds = ids; + + this._items = this._objectIds.map(id => new ObjectListItem(id)); } getId() { + return this._id; } setId(id: string) { + this._id = id; } getLabel() { + return this._label; } setLabel(label: string) { + this._label = label; } getScope() { + return this._scope; } setScope(scope: ObjectScope) { + this._scope = scope; } @@ -91,8 +127,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { this._id = data.id; this._label = data.label; - this._objectIds = data.objectIds || []; this._scope = data.scope || sceneobjects.ObjectScope.CLASS; + this.setObjectsIds(data.objectIds || []); } writeJSON(data: json.IObjectListData) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ObjectListItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ObjectListItem.ts new file mode 100644 index 000000000..b9b3c2780 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ObjectListItem.ts @@ -0,0 +1,34 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + export class ObjectListItem { + + private _objectId: string; + private _obj: ISceneGameObject; + + constructor(objectId: string) { + + this._objectId = objectId; + } + + getId() { + + return `ListItem#${this._objectId}`; + } + + getObjectId() { + + return this._objectId; + } + + getObject(): ISceneGameObject { + + return this._obj; + } + + setObject(obj: ISceneGameObject) { + + this._obj = obj; + } + } + +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ObjectListItemCellRenderer.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ObjectListItemCellRenderer.ts new file mode 100644 index 000000000..ce40413bb --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ObjectListItemCellRenderer.ts @@ -0,0 +1,47 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + import controls = colibri.ui.controls; + + export class ObjectListItemCellRenderer implements controls.viewers.ICellRenderer { + + private _objRenderer: controls.viewers.ICellRenderer; + + constructor(objRenderer: controls.viewers.ICellRenderer) { + + this._objRenderer = objRenderer; + } + + private adaptArgs(args: controls.viewers.RenderCellArgs) { + + const args2 = args.clone(); + + args2.obj = (args2.obj as ObjectListItem).getObject(); + + return args2; + } + + renderCell(args: controls.viewers.RenderCellArgs): void { + + this._objRenderer.renderCell(this.adaptArgs(args)); + } + + cellHeight(args: controls.viewers.RenderCellArgs) { + + return this._objRenderer.cellHeight(this.adaptArgs(args)); + } + + preload(args: controls.viewers.PreloadCellArgs): Promise { + + const clone = args.clone(); + + clone.obj = (clone.obj as ObjectListItem).getObject(); + + return this._objRenderer.preload(clone); + } + + get layout() { + + return this._objRenderer.layout; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ObjectListItemSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ObjectListItemSection.ts new file mode 100644 index 000000000..09dd3c1b0 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ObjectListItemSection.ts @@ -0,0 +1,63 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + import controls = colibri.ui.controls; + + export class ObjectListItemSection extends editor.properties.BaseSceneSection { + + constructor(page: controls.properties.PropertyPage) { + super(page, "phasereditor2d.scene.ui.sceneobjects.ObjectListItemSection", "List Item"); + } + + createForm(parent: HTMLDivElement) { + + const comp = this.createGridElement(parent, 2); + + { + this.createLabel(comp, "Name", "The object's name."); + + const field = this.createText(comp, true); + + this.addUpdater(() => { + + const labels = this.getSelectedGameObjects() + + .map(obj => obj.getEditorSupport().getLabel()); + + field.value = this.flatValues_StringOneOrNothing(labels); + }); + } + + { + const btn = this.createButton(comp, "Select Game Object", () => { + + this.getEditor().setSelection(this.getSelectedGameObjects()); + }); + + btn.style.gridColumn = "1 / span 2"; + } + } + + private getSelectedGameObjects() { + + const map = this.getEditor().getScene().buildObjectIdMap(); + + return this.getSelection() + + .map(item => item.getObjectId()) + + .map(id => map.get(id)) + + .filter(obj => Boolean(obj)); + } + + canEdit(obj: any, n: number): boolean { + + return obj instanceof ObjectListItem; + } + + canEditNumber(n: number): boolean { + + return n > 0; + } + } +} \ No newline at end of file From 02c34fea8cb8f6067d95068b6f94285f65288ce5 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 2 May 2023 16:04:52 -0400 Subject: [PATCH 16/59] Improves validation of the Depth commands. --- .../src/ui/editor/undo/DepthOperation.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/DepthOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/DepthOperation.ts index f6c3d1173..78d59750f 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/DepthOperation.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/DepthOperation.ts @@ -14,8 +14,15 @@ namespace phasereditor2d.scene.ui.editor.undo { static allow(editor: SceneEditor, move: DepthMove) { + // sort the selection and filter off non-game-objects let sel = this.sortedSelection(editor); + // if the sorted selection contains all the selected objects + if (sel.length !== editor.getSelection().length) { + + return false; + } + for (const obj of sel) { const parent = obj.getEditorSupport().getObjectParent(); From ca5abaa1412ccaeea349b5d3006fff01116d6046 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 2 May 2023 20:38:52 -0400 Subject: [PATCH 17/59] Lists : Ability to rearrange order of list items #277 --- .../src/ui/editor/SceneEditorMenuCreator.ts | 17 ++- .../ui/editor/commands/SceneEditorCommands.ts | 37 +++++ .../src/ui/sceneobjects/list/ObjectList.ts | 7 +- .../ui/sceneobjects/list/ObjectListItem.ts | 9 +- .../list/undo/ListOrderOperation.ts | 136 ++++++++++++++++++ .../list/undo/ListsSnapshotOperation.ts | 8 +- 6 files changed, 209 insertions(+), 5 deletions(-) create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/undo/ListOrderOperation.ts diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditorMenuCreator.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditorMenuCreator.ts index ef0d6a1b9..4c4b68a56 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditorMenuCreator.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditorMenuCreator.ts @@ -41,6 +41,8 @@ namespace phasereditor2d.scene.ui.editor { menu.addMenu(this.createDepthMenu()); + menu.addMenu(this.createListMenu()); + menu.addSeparator(); menu.addMenu(this.createSnappingMenu()); @@ -168,7 +170,6 @@ namespace phasereditor2d.scene.ui.editor { return menu; } - private createDepthMenu(): controls.Menu { const menu = new controls.Menu("Depth"); @@ -183,6 +184,20 @@ namespace phasereditor2d.scene.ui.editor { return menu; } + private createListMenu(): controls.Menu { + + const menu = new controls.Menu("Object List"); + + for (const move of ["Up", "Down", "Top", "Bottom"]) { + + const id = "phasereditor2d.scene.ui.editor.commands.ListOrder" + move; + + menu.addCommand(id); + } + + return menu; + } + private createEditMenu() { const menu = new controls.Menu("Edit"); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts index 21f013803..7a65cf0f2 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts @@ -139,6 +139,8 @@ namespace phasereditor2d.scene.ui.editor.commands { this.registerDepthCommands(manager); + this.registerListCommands(manager); + this.registerTypeCommands(manager); this.registerMoveObjectCommands(manager); @@ -1998,6 +2000,41 @@ namespace phasereditor2d.scene.ui.editor.commands { } } + private static registerListCommands(manager: colibri.ui.ide.commands.CommandManager) { + + // order commands + + const moves: [undo.DepthMove, string][] = [["Up", "PageUp"], ["Down", "PageDown"], ["Top", "Home"], ["Bottom", "End"]]; + + for (const tuple of moves) { + + const move = tuple[0]; + const key = tuple[1]; + + manager.add({ + + command: { + id: "phasereditor2d.scene.ui.editor.commands.ListOrder" + move, + name: "Move " + move, + category: CAT_SCENE_EDITOR, + tooltip: "Move the object in its list to " + move + "." + }, + + handler: { + testFunc: args => isSceneScope(args) && args.activeEditor.getSelection().length > 0 + && sceneobjects.ListOrderOperation.allow(args.activeEditor as any, move), + + executeFunc: args => args.activeEditor.getUndoManager().add( + new sceneobjects.ListOrderOperation(args.activeEditor as editor.SceneEditor, move)) + }, + + keys: { + key + } + }); + } + } + static computeOriginCommandData(): Array<{ command: string, name: string, diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ObjectList.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ObjectList.ts index 43cb114ad..1860119ee 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ObjectList.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ObjectList.ts @@ -31,6 +31,11 @@ namespace phasereditor2d.scene.ui.sceneobjects { return this._items.filter(item => Boolean(item.getObject())); } + updateOrderIdsFromItems() { + + this._objectIds = this._items.map(i => i.getObjectId()); + } + removeItem(id: string) { this._items = this._items.filter(i => i.getId() !== id); @@ -52,7 +57,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { this._objectIds = ids; - this._items = this._objectIds.map(id => new ObjectListItem(id)); + this._items = this._objectIds.map(id => new ObjectListItem(this, id)); } getId() { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ObjectListItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ObjectListItem.ts index b9b3c2780..5647e68f3 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ObjectListItem.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ObjectListItem.ts @@ -2,14 +2,21 @@ namespace phasereditor2d.scene.ui.sceneobjects { export class ObjectListItem { + private _parent: ObjectList; private _objectId: string; private _obj: ISceneGameObject; - constructor(objectId: string) { + constructor(parent: ObjectList, objectId: string) { + this._parent = parent; this._objectId = objectId; } + getParent() { + + return this._parent; + } + getId() { return `ListItem#${this._objectId}`; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/undo/ListOrderOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/undo/ListOrderOperation.ts new file mode 100644 index 000000000..57989f437 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/undo/ListOrderOperation.ts @@ -0,0 +1,136 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + import DepthMove = scene.ui.editor.undo.DepthMove; + import SceneEditor = scene.ui.editor.SceneEditor; + + export class ListOrderOperation extends ListsSnapshotOperation { + + private _depthMove: DepthMove; + + constructor(editor: SceneEditor, depthMove: DepthMove) { + super(editor); + + this._depthMove = depthMove; + } + + static allow(editor: SceneEditor, move: DepthMove) { + + // sort the selection and filter off non-game-objects + let sel = this.sortedSelection(editor); + + // if the sorted selection contains all the selected objects + if (sel.length !== editor.getSelection().length) { + + return false; + } + + for (const obj of sel) { + + //const parent = obj.getEditorSupport().getObjectParent(); + const siblings = obj.getParent().getItems(); + + const index = siblings.indexOf(obj); + + const len = siblings.length; + + if (move === "Bottom" || move === "Down") { + + if (index === len - 1) { + + return false; + } + + } else { // Top || Up + + if (index === 0) { + + return false; + } + } + } + + return true; + } + + private static sortedSelection(editor: SceneEditor) { + + const sel = editor.getSelectedListItems(); + + sel.sort((a, b) => { + + const aa = a.getParent().getItems().indexOf(a); + const bb = a.getParent().getItems().indexOf(b); + + return aa - bb; + }); + + return sel; + } + + performChange(lists: ObjectLists): void { + + const editor = this.getEditor(); + + const sel = ListOrderOperation.sortedSelection(editor); + + switch (this._depthMove) { + + case "Bottom": + + for (const obj of sel) { + + const siblings = obj.getParent().getItems(); + + Phaser.Utils.Array.BringToTop(siblings, obj); + + obj.getParent().updateOrderIdsFromItems(); + } + + break; + + case "Top": + + for (let i = 0; i < sel.length; i++) { + + const obj = sel[sel.length - i - 1]; + + const siblings = obj.getParent().getItems(); + + Phaser.Utils.Array.SendToBack(siblings, obj) + + obj.getParent().updateOrderIdsFromItems(); + } + + break; + + case "Down": + + for (let i = 0; i < sel.length; i++) { + + const obj = sel[sel.length - i - 1]; + + const siblings = obj.getParent().getItems(); + + Phaser.Utils.Array.MoveUp(siblings, obj); + + obj.getParent().updateOrderIdsFromItems(); + } + + break; + + case "Up": + + for (const obj of sel) { + + const siblings = obj.getParent().getItems(); + + Phaser.Utils.Array.MoveDown(siblings, obj); + + obj.getParent().updateOrderIdsFromItems(); + } + + break; + } + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/undo/ListsSnapshotOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/undo/ListsSnapshotOperation.ts index fe9a5e594..b07ff1593 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/undo/ListsSnapshotOperation.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/undo/ListsSnapshotOperation.ts @@ -6,6 +6,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { private _before: json.IObjectListData[]; private _after: json.IObjectListData[]; + private _selection: string[]; constructor(editor: editor.SceneEditor) { super(editor); @@ -15,9 +16,12 @@ namespace phasereditor2d.scene.ui.sceneobjects { async execute() { - const lists = this._editor.getScene().getObjectLists(); + const scene = this._editor.getScene(); + + const lists = scene.getObjectLists(); this._before = lists.toJSON_lists(); + this._selection = this._editor.getSelectionManager().getSelectionIds(); this.performChange(lists); @@ -36,7 +40,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { this._editor.refreshOutline(); - this._editor.getSelectionManager().refreshSelection(); + this._editor.getSelectionManager().setSelectionByIds(this._selection); } undo(): void { From 87bf3b40f8e80394982cae19bfd788d7632317e0 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 2 May 2023 20:40:28 -0400 Subject: [PATCH 18/59] Updates changelog. --- CHANGELOG.MD | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index d9b1edcf7..72a4a55a3 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -5,6 +5,7 @@ * Introduce the new variable scope: NESTED_PREFAB_METHOD, NESTED_PREFAB_CLASS, NESTED_PREFAB_PUBLIC. It introduce a new scene format version. * Checks if an scene file was generated by a newer and incompatible version of the editor. * Sorts prefabs in Blocks view: start with game object prefabs, end with script nodes. +* Shows Object List items in the Outline view. Allows ordering the items with the Up, Down, Top, Down, commands. ## v3.60.3 - Apr 27, 2023 From fef44b7fe08963a40023236f44249eefe986513d Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 2 May 2023 22:41:35 -0400 Subject: [PATCH 19/59] Removes some private accessor methods. --- .../sceneobjects/GameObjectEditorSupport.ts | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts index 50c1f167e..41b1804e0 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts @@ -693,21 +693,11 @@ namespace phasereditor2d.scene.ui.sceneobjects { return this._isNestedPrefabInstance; } - private _setNestedPrefabInstance(isNestedPrefabInstace: boolean) { - - this._isNestedPrefabInstance = isNestedPrefabInstace; - } - isLocalNestedPrefabInstance() { return this._isLocalNestedPrefabInstance; } - private _setLocalNestedPrefabInstance(isLocalNestedPrefabInstance: boolean) { - - this._isLocalNestedPrefabInstance = isLocalNestedPrefabInstance; - } - isPrefabInstance() { return this._prefabId !== undefined && this._prefabId !== null; @@ -1274,14 +1264,18 @@ namespace phasereditor2d.scene.ui.sceneobjects { if (ui.sceneobjects.isNestedPrefabScope(scope)) { - spriteES._setNestedPrefabInstance(true); - spriteES._setLocalNestedPrefabInstance( - scope !== sceneobjects.ObjectScope.PUBLIC_NESTED_PREFAB); + spriteES._isNestedPrefabInstance = true; + spriteES._isLocalNestedPrefabInstance = + scope !== sceneobjects.ObjectScope.PUBLIC_NESTED_PREFAB; } + + } else { + // TODO: we can flag it as a regular object, that is not part of a prefab instance + // or it is appended to a prefab instance. } // updates the object with the final data - sprite.getEditorSupport().readJSON(childData); + spriteES.readJSON(childData); } i++; From 8c7c94424ce09cd06a60a72dca710dfedc2c98fa Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 2 May 2023 22:52:41 -0400 Subject: [PATCH 20/59] Fixes scene serialization of children. Removes empty `list` from object data. --- .../src/ui/sceneobjects/GameObjectEditorSupport.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts index 41b1804e0..fbb8cf6bd 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts @@ -1157,6 +1157,12 @@ namespace phasereditor2d.scene.ui.sceneobjects { return objData as json.IObjectData; }); } + + // if the container has an empty list, remove it from the file + if (containerData.list && containerData.list.length === 0) { + + delete containerData.list; + } } private static readPrefabChildren(serializer: core.json.Serializer, list: json.IObjectData[]) { From 2d57cad4121cc39e1faa7585c181c52c3867241e Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 2 May 2023 23:00:26 -0400 Subject: [PATCH 21/59] Excludes empty `components` array from data serialziation. --- .../object/UserComponentsEditorComponent.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/UserComponentsEditorComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/UserComponentsEditorComponent.ts index f4f843c1c..8bee025f5 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/UserComponentsEditorComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/UserComponentsEditorComponent.ts @@ -23,7 +23,16 @@ namespace phasereditor2d.scene.ui.sceneobjects { writeJSON(ser: core.json.Serializer) { - ser.getData()["components"] = [...this._compNames]; + const data = ser.getData() as core.json.IObjectData; + + data.components = [...this._compNames]; + + // we don't want to serialize an empty components array, + // if it is the case, we exclude it from the file + if (data.components.length === 0) { + + delete data.components; + } for (const compName of this._compNames) { @@ -39,7 +48,9 @@ namespace phasereditor2d.scene.ui.sceneobjects { readJSON(ser: core.json.Serializer) { - this._compNames = ser.getData()["components"] || []; + const data = ser.getData() as core.json.IObjectData; + + this._compNames = data.components || []; for (const compName of this._compNames) { From 9a417583071ef5263c0d29417a05aadb5ff0af78 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 2 May 2023 23:04:09 -0400 Subject: [PATCH 22/59] Removes empty `nestedPrefabs` array from scene serialization. --- .../src/ui/sceneobjects/GameObjectEditorSupport.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts index fbb8cf6bd..dc35b0ce2 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts @@ -1163,6 +1163,12 @@ namespace phasereditor2d.scene.ui.sceneobjects { delete containerData.list; } + + // if the container has an empty nestedPrefabs array, remove it from the file + if (containerData.nestedPrefabs && containerData.nestedPrefabs.length === 0) { + + delete containerData.nestedPrefabs; + } } private static readPrefabChildren(serializer: core.json.Serializer, list: json.IObjectData[]) { From ebe34d4d3cde1628faac068ee132782ddf010857 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Wed, 3 May 2023 10:01:37 -0400 Subject: [PATCH 23/59] Adds validation in scene compiler. --- .../phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts b/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts index 60bea65a2..e09bc9140 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts @@ -456,7 +456,8 @@ namespace phasereditor2d.scene.core.code { if (obj.getEditorSupport().isMutableNestedPrefabInstance()) { - this.addCreateObjectCodeOfNestedPrefab(obj, createMethodDecl, lazyStatements); + // this.addCreateObjectCodeOfNestedPrefab(obj, createMethodDecl, lazyStatements); + throw new Error("Assert: this code should not be reached."); } else { From a207b74293865ed0fa5b308491c9f9dfe080e9d6 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Wed, 3 May 2023 10:14:09 -0400 Subject: [PATCH 24/59] Adds refactoring on object scope methods. --- .../src/core/code/SceneCodeDOMBuilder.ts | 23 ++++++++-------- .../src/ui/sceneobjects/ObjectScope.ts | 26 +++++++++++++++++++ 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts b/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts index e09bc9140..72beea3a9 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts @@ -223,17 +223,16 @@ namespace phasereditor2d.scene.core.code { for (const obj of this._scene.getPlainObjects()) { - const editorSupport = obj.getEditorSupport(); - const scope = editorSupport.getScope(); + const objES = obj.getEditorSupport(); - if (scope !== ui.sceneobjects.ObjectScope.METHOD) { + if (!objES.isMethodScope()) { - const objType = editorSupport.getPhaserType(); + const objType = objES.getPhaserType(); const dom = new FieldDeclCodeDOM( - formatToValidVarName(editorSupport.getLabel()), + formatToValidVarName(objES.getLabel()), objType, - scope === ui.sceneobjects.ObjectScope.PUBLIC); + objES.isPublicScope()); dom.setAllowUndefined(!this._scene.isPrefabSceneType()); @@ -248,14 +247,14 @@ namespace phasereditor2d.scene.core.code { for (const list of this._scene.getObjectLists().getLists()) { - if (list.getScope() !== ui.sceneobjects.ObjectScope.METHOD) { + if (!ui.sceneobjects.isMethodScope(list.getScope())) { const listType = list.inferType(objMap); const dom = new FieldDeclCodeDOM( formatToValidVarName(list.getLabel()), listType, - list.getScope() === ui.sceneobjects.ObjectScope.PUBLIC); + ui.sceneobjects.isPublicScope(list.getScope())); dom.setAllowUndefined(!this._scene.isPrefabSceneType()); @@ -625,11 +624,11 @@ namespace phasereditor2d.scene.core.code { for (const obj of this._scene.getPlainObjects()) { - const editorSupport = obj.getEditorSupport(); + const objES = obj.getEditorSupport(); - if (editorSupport.getScope() !== ui.sceneobjects.ObjectScope.METHOD) { + if (!objES.isMethodScope()) { - const varname = formatToValidVarName(editorSupport.getLabel()); + const varname = formatToValidVarName(objES.getLabel()); const dom = new AssignPropertyCodeDOM(varname, "this"); @@ -641,7 +640,7 @@ namespace phasereditor2d.scene.core.code { for (const list of this._scene.getObjectLists().getLists()) { - if (list.getScope() !== ui.sceneobjects.ObjectScope.METHOD) { + if (!ui.sceneobjects.isMethodScope(list.getScope())) { const varname = formatToValidVarName(list.getLabel()); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ObjectScope.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ObjectScope.ts index 6668f2411..7a4e5f5f0 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ObjectScope.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ObjectScope.ts @@ -45,4 +45,30 @@ namespace phasereditor2d.scene.ui.sceneobjects { return false; } + + export function isMethodScope(scope: ObjectScope) { + + switch (scope) { + + case ObjectScope.METHOD: + case ObjectScope.METHOD_NESTED_PREFAB: + + return true; + } + + return false; + } + + export function isClassScope(scope: ObjectScope) { + + switch (scope) { + + case ObjectScope.CLASS: + case ObjectScope.CLASS_NESTED_PREFAB: + + return true; + } + + return false; + } } \ No newline at end of file From f13ba538ccb40bded0745203caf12aafcceb5ac3 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Wed, 3 May 2023 11:48:22 -0400 Subject: [PATCH 25/59] Adds the new LOCAL scope. --- .../src/core/code/SceneCodeDOMBuilder.ts | 39 ++++++------------- .../src/ui/sceneobjects/EditorSupport.ts | 23 ++++++++--- .../src/ui/sceneobjects/ObjectScope.ts | 24 +++++++++++- .../ScenePlainObjectVariableSection.ts | 29 ++++++++------ .../sceneobjects/list/ListVariableSection.ts | 2 + .../sceneobjects/object/VariableComponent.ts | 2 +- .../physics/ColliderEditorSupport.ts | 2 +- 7 files changed, 74 insertions(+), 47 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts b/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts index 72beea3a9..ccb9aa887 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts @@ -225,7 +225,7 @@ namespace phasereditor2d.scene.core.code { const objES = obj.getEditorSupport(); - if (!objES.isMethodScope()) { + if (objES.isClassOrPublicScope()) { const objType = objES.getPhaserType(); @@ -247,7 +247,7 @@ namespace phasereditor2d.scene.core.code { for (const list of this._scene.getObjectLists().getLists()) { - if (!ui.sceneobjects.isMethodScope(list.getScope())) { + if (ui.sceneobjects.isClassOrPublicScope(list.getScope())) { const listType = list.inferType(objMap); @@ -519,7 +519,7 @@ namespace phasereditor2d.scene.core.code { const objectFactoryMethodCall = result.objectFactoryMethodCall; - if (!objES.isMethodScope()) { + if (objES.isClassOrPublicScope() || objES.isMethodScope()) { objectFactoryMethodCall.setDeclareReturnToVar(true); } @@ -590,28 +590,6 @@ namespace phasereditor2d.scene.core.code { fields.push(dom); } } - - // for (const obj of children) { - - // const objES = obj.getEditorSupport(); - - // if (!objES.isMethodScope() && prefabObj !== obj) { - - // const varname = formatToValidVarName(objES.getLabel()); - - // const dom = new AssignPropertyCodeDOM(varname, "this"); - // dom.value(varname); - - // fields.push(dom); - // } - - // const walkingChildren = this.getWalkingChildren(obj); - - // if (walkingChildren) { - - // this.addFieldInitCode_GameObjects(fields, prefabObj, walkingChildren); - // } - // } } private addFieldInitCode(body: CodeDOM[]) { @@ -626,7 +604,7 @@ namespace phasereditor2d.scene.core.code { const objES = obj.getEditorSupport(); - if (!objES.isMethodScope()) { + if (objES.isClassOrPublicScope()) { const varname = formatToValidVarName(objES.getLabel()); @@ -640,7 +618,7 @@ namespace phasereditor2d.scene.core.code { for (const list of this._scene.getObjectLists().getLists()) { - if (!ui.sceneobjects.isMethodScope(list.getScope())) { + if (ui.sceneobjects.isClassOrPublicScope(list.getScope())) { const varname = formatToValidVarName(list.getLabel()); @@ -813,6 +791,11 @@ namespace phasereditor2d.scene.core.code { createObjectMethodCall.setDeclareReturnToVar(true); } + if (objES.isMethodScope()) { + // it is method scope... the user wants a variable! + createObjectMethodCall.setDeclareReturnToVar(true); + } + lazyStatements.push(...result.lazyStatements); createMethodDecl.getBody().push(...result.statements); @@ -872,7 +855,7 @@ namespace phasereditor2d.scene.core.code { // set var flags - if (!objES.isMethodScope()) { + if (objES.isClassOrPublicScope()) { createObjectMethodCall.setDeclareReturnToVar(true); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts index 046e0de20..b2dc162a8 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts @@ -16,7 +16,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { this._scene = scene; this._label = label; this._useGameObjectName = false; - this._scope = ObjectScope.METHOD; + this._scope = ObjectScope.LOCAL; this.setId(Phaser.Utils.String.UUID()); } @@ -52,16 +52,29 @@ namespace phasereditor2d.scene.ui.sceneobjects { return ""; } + isLocalScope() { + + return isLocalScope(this._scope); + } + isMethodScope() { - return this._scope === ObjectScope.METHOD - || this._scope === ObjectScope.METHOD_NESTED_PREFAB; + return isMethodScope(this._scope); + } + + isClassScope() { + + return isClassScope(this._scope); } isPublicScope() { - return this._scope === ObjectScope.PUBLIC - || this._scope === ObjectScope.PUBLIC_NESTED_PREFAB; + return isPublicScope(this._scope); + } + + isClassOrPublicScope() { + + return isClassOrPublicScope(this._scope); } getObject() { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ObjectScope.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ObjectScope.ts index 7a4e5f5f0..eb615d1e5 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ObjectScope.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ObjectScope.ts @@ -1,19 +1,22 @@ namespace phasereditor2d.scene.ui.sceneobjects { export enum ObjectScope { - + LOCAL = "LOCAL", METHOD = "METHOD", CLASS = "CLASS", PUBLIC = "PUBLIC", + LOCAL_NESTED_PREFAB = "NESTED_PREFAB_LOCAL", METHOD_NESTED_PREFAB = "NESTED_PREFAB_METHOD", CLASS_NESTED_PREFAB = "NESTED_PREFAB_CLASS", PUBLIC_NESTED_PREFAB = "NESTED_PREFAB_PUBLIC", } export const OBJECT_SCOPES = [ + ObjectScope.LOCAL, ObjectScope.METHOD, ObjectScope.CLASS, ObjectScope.PUBLIC, + ObjectScope.LOCAL_NESTED_PREFAB, ObjectScope.METHOD_NESTED_PREFAB, ObjectScope.CLASS_NESTED_PREFAB, ObjectScope.PUBLIC_NESTED_PREFAB @@ -23,6 +26,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { switch (scope) { + case ObjectScope.LOCAL_NESTED_PREFAB: case ObjectScope.METHOD_NESTED_PREFAB: case ObjectScope.CLASS_NESTED_PREFAB: case ObjectScope.PUBLIC_NESTED_PREFAB: @@ -33,6 +37,11 @@ namespace phasereditor2d.scene.ui.sceneobjects { return false; } + export function isClassOrPublicScope(scope: ObjectScope) { + + return isClassScope(scope) || isPublicScope(scope); + } + export function isPublicScope(scope: ObjectScope) { switch (scope) { @@ -46,6 +55,19 @@ namespace phasereditor2d.scene.ui.sceneobjects { return false; } + export function isLocalScope(scope: ObjectScope) { + + switch (scope) { + + case ObjectScope.LOCAL: + case ObjectScope.LOCAL_NESTED_PREFAB: + + return true; + } + + return false; + } + export function isMethodScope(scope: ObjectScope) { switch (scope) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ScenePlainObjectVariableSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ScenePlainObjectVariableSection.ts index 273515148..96909260d 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ScenePlainObjectVariableSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ScenePlainObjectVariableSection.ts @@ -56,16 +56,21 @@ namespace phasereditor2d.scene.ui.sceneobjects { this.createLabel(comp, "Scope", "The lexical scope of the object."); - const items = [{ - name: "Method", - value: ObjectScope.METHOD - }, { - name: "Class", - value: ObjectScope.CLASS - }, { - name: "Public", - value: ObjectScope.PUBLIC - }]; + const items = [ + { + name: "LOCAL", + value: ObjectScope.LOCAL + }, + { + name: "METHOD", + value: ObjectScope.METHOD + }, { + name: "CLASS", + value: ObjectScope.CLASS + }, { + name: "PUBLIC", + value: ObjectScope.PUBLIC + }]; const btn = this.createMenuButton(comp, "", () => items, scope => { @@ -73,7 +78,9 @@ namespace phasereditor2d.scene.ui.sceneobjects { for (const obj of objects) { - obj.getEditorSupport().setScope(scope); + const objES = obj.getEditorSupport(); + + objES.setScope(scope); } }); }); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ListVariableSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ListVariableSection.ts index 06554cc49..ba880480e 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ListVariableSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/list/ListVariableSection.ts @@ -52,6 +52,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { this.createLabel(comp, "Scope", "The lexical scope of the object."); + // I skip the LOCAL scope here because a List without a variable + // has no sense const items = [{ name: "METHOD", value: ObjectScope.METHOD diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/VariableComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/VariableComponent.ts index 7c8edf8d7..795ab3c72 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/VariableComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/VariableComponent.ts @@ -27,7 +27,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { static scope: IEnumProperty = { name: "scope", tooltip: "The variable lexical scope.", - defValue: ObjectScope.METHOD, + defValue: ObjectScope.LOCAL, local: true, getValue: obj => obj.getEditorSupport().getScope(), setValue: (obj, value) => obj.getEditorSupport().setScope(value), diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/physics/ColliderEditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/physics/ColliderEditorSupport.ts index 628fe1c0d..1dcfa153a 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/physics/ColliderEditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/physics/ColliderEditorSupport.ts @@ -8,7 +8,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { new ColliderComponent(obj) ); - this.setScope(ObjectScope.METHOD); + this.setScope(ObjectScope.LOCAL); } destroy() { From cf777c401052f50418c184ca455b8647b87c717d Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Wed, 3 May 2023 11:51:00 -0400 Subject: [PATCH 26/59] Updates changelog. --- CHANGELOG.MD | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 72a4a55a3..7e88b7b6e 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -6,6 +6,7 @@ * Checks if an scene file was generated by a newer and incompatible version of the editor. * Sorts prefabs in Blocks view: start with game object prefabs, end with script nodes. * Shows Object List items in the Outline view. Allows ordering the items with the Up, Down, Top, Down, commands. +* A new game object scope: LOCAL. The LOCAL scope is now the default scope for objects and has the same meaning of METHOD scope before. Now the METHOD scope forces the creation of a variable for the object. ## v3.60.3 - Apr 27, 2023 From f2a572110ee352ac21bd2f90657c38c974fee448 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 4 May 2023 13:55:50 -0400 Subject: [PATCH 27/59] Computes private nested prefabs automatically. --- .../phasereditor2d.scene/src/ScenePlugin.ts | 1 - .../src/core/json/IObjectData.ts | 1 + .../src/core/json/SceneFinder.ts | 2 +- .../src/core/json/SceneWriter.ts | 60 ++++++++++++++++++- .../migrations/NestedPrefaScopeMigration.ts | 37 ------------ .../SceneEditorOutlineContentProvider.ts | 2 +- .../src/ui/sceneobjects/EditorSupport.ts | 5 ++ .../sceneobjects/GameObjectEditorSupport.ts | 44 +++++++++----- .../src/ui/sceneobjects/ObjectScope.ts | 54 +++-------------- 9 files changed, 103 insertions(+), 103 deletions(-) delete mode 100644 source/editor/plugins/phasereditor2d.scene/src/core/migrations/NestedPrefaScopeMigration.ts diff --git a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts index 325a0cc68..90cccdb8b 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts @@ -142,7 +142,6 @@ namespace phasereditor2d.scene { reg.addExtension(new core.migrations.OriginMigration_v2_to_v3()); reg.addExtension(new core.migrations.UnlockPositionMigration_v1_to_v2()); reg.addExtension(new core.migrations.TextAlignMigration()); - reg.addExtension(new core.migrations.NestedPrefaScopeMigration()); // preload docs diff --git a/source/editor/plugins/phasereditor2d.scene/src/core/json/IObjectData.ts b/source/editor/plugins/phasereditor2d.scene/src/core/json/IObjectData.ts index e8bd9fd1a..cbe2dea5c 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/core/json/IObjectData.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/core/json/IObjectData.ts @@ -8,6 +8,7 @@ namespace phasereditor2d.scene.core.json { label: string; unlock?: string[]; scope?: ui.sceneobjects.ObjectScope; + private_np?: boolean; list?: IObjectData[]; nestedPrefabs?: IObjectData[]; } diff --git a/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneFinder.ts b/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneFinder.ts index e78acea2c..b5ba65213 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneFinder.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneFinder.ts @@ -272,7 +272,7 @@ namespace phasereditor2d.scene.core.json { for (const c of objData.list) { - if (ui.sceneobjects.isNestedPrefabScope(c.scope)) { + if (c.private_np || ui.sceneobjects.isNestedPrefabScope(c.scope)) { prefabObjectId_ObjectData_Map.set(c.id, c); prefabId_File_Map.set(c.id, file); diff --git a/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneWriter.ts b/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneWriter.ts index 8325fe47c..14cd95380 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneWriter.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneWriter.ts @@ -41,10 +41,32 @@ namespace phasereditor2d.scene.core.json { // display list + for (const obj of this._scene.getGameObjects()) { + + this.computeDataField_private_np(obj); + } + for (const obj of this._scene.getGameObjects()) { const objData = {} as IObjectData; - obj.getEditorSupport().writeJSON(objData); + + const objES = obj.getEditorSupport(); + + // write the `private_np` field + + const private_np = objES._private_np; + + if (private_np) { + + objData.private_np = true; + } + + // serialize all the other obj data + + objES.writeJSON(objData); + + // add the data to the list + sceneData.displayList.push(objData); } @@ -62,6 +84,42 @@ namespace phasereditor2d.scene.core.json { return sceneData; } + private computeDataField_private_np(obj: ui.sceneobjects.ISceneGameObject) { + + const objES = obj.getEditorSupport(); + + for (const child of objES.getObjectChildren()) { + + this.computeDataField_private_np(child); + } + + if (!objES.isPrefabInstancePart() + && !objES.isNestedPrefabScope() + && !objES.isScenePrefabObject()) { + + // ok, it is an object in the scene which + // I don't know if it is a private nested prefab (`private_np`) + // let's investigate with the kids + + for (const child of objES.getObjectChildren()) { + + const childES = child.getEditorSupport(); + + if ( + // the child is flagged as private_np + childES._private_np + // or the child is is a common object with a NESTED_PREFAB scope + || (childES.isNestedPrefabScope() && !childES.isPrefabInstancePart())) { + + // flag the object and stop the search + objES._private_np = true; + + break; + } + } + } + } + toString(): string { const json = this.toJSON(); diff --git a/source/editor/plugins/phasereditor2d.scene/src/core/migrations/NestedPrefaScopeMigration.ts b/source/editor/plugins/phasereditor2d.scene/src/core/migrations/NestedPrefaScopeMigration.ts deleted file mode 100644 index dd3de1568..000000000 --- a/source/editor/plugins/phasereditor2d.scene/src/core/migrations/NestedPrefaScopeMigration.ts +++ /dev/null @@ -1,37 +0,0 @@ -/// -namespace phasereditor2d.scene.core.migrations { - - export class NestedPrefaScopeMigration extends ui.SceneDataMigrationExtension { - - migrate(data: json.ISceneData) { - - if (data.meta.version < 4) { - - this.migrateList(data.displayList); - } - } - - private migrateList(list?: json.IObjectData[]) { - - for (const obj of list) { - - if (obj.scope && obj.scope.toString() == "NESTED_PREFAB") { - - console.log(`Migrate nested prefab scope to PUBLIC_NESTED_PREFAB [${obj.id}]`); - - obj.scope = ui.sceneobjects.ObjectScope.PUBLIC_NESTED_PREFAB; - } - - if (obj.list) { - - this.migrateList(obj.list); - } - - if (obj.nestedPrefabs) { - - this.migrateList(obj.nestedPrefabs); - } - } - } - } -} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineContentProvider.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineContentProvider.ts index 23d515b3b..c96c9b86e 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineContentProvider.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineContentProvider.ts @@ -144,7 +144,7 @@ namespace phasereditor2d.scene.ui.editor.outline { if (childES.isMutableNestedPrefabInstance()) { - if (childES.isLocalNestedPrefabInstance()) { + if (childES.isPrivateNestedPrefabInstance()) { this.getPublicMutableNestedPrefabChildren(child, list); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts index b2dc162a8..6d9e18016 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts @@ -72,6 +72,11 @@ namespace phasereditor2d.scene.ui.sceneobjects { return isPublicScope(this._scope); } + isNestedPrefabScope() { + + return isNestedPrefabScope(this._scope); + } + isClassOrPublicScope() { return isClassOrPublicScope(this._scope); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts index dc35b0ce2..a667ef2ef 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts @@ -12,7 +12,10 @@ namespace phasereditor2d.scene.ui.sceneobjects { private _componentMap: Map>; private _unlockedProperties: Set; private _isNestedPrefabInstance: boolean; - private _isLocalNestedPrefabInstance: boolean; + private _isPrivateNestedPrefabInstance: boolean; + private _isPrefabInstancePart: boolean; + // a temporal variable used for serialization + public _private_np: boolean; // parent private _allowPickChildren: boolean; @@ -30,7 +33,9 @@ namespace phasereditor2d.scene.ui.sceneobjects { this._serializables = []; this._componentMap = new Map(); this._isNestedPrefabInstance = false; - this._isLocalNestedPrefabInstance = false; + this._isPrivateNestedPrefabInstance = false; + this._isPrefabInstancePart = false; + this._private_np = false; this._allowPickChildren = true; this._showChildrenInOutline = true; @@ -615,6 +620,9 @@ namespace phasereditor2d.scene.ui.sceneobjects { */ isMutableNestedPrefabInstance() { + // TODO: the next line is a better implementation we should test: + // return this.isNestedPrefabInstance() && !this.isPrivateNestedPrefabInstance(); + if (this.isNestedPrefabInstance()) { const parent = this.getObjectParent(); @@ -693,9 +701,14 @@ namespace phasereditor2d.scene.ui.sceneobjects { return this._isNestedPrefabInstance; } - isLocalNestedPrefabInstance() { + isPrivateNestedPrefabInstance() { + + return this._isPrivateNestedPrefabInstance; + } + + isPrefabInstancePart() { - return this._isLocalNestedPrefabInstance; + return this._isPrefabInstancePart; } isPrefabInstance() { @@ -1025,6 +1038,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { } data.id = this.getId(); + data.private_np = this._private_np ? true : undefined; if (this._prefabId && this._unlockedProperties.size > 0) { @@ -1272,18 +1286,15 @@ namespace phasereditor2d.scene.ui.sceneobjects { if (i < prefabChildren.length) { const prefabData = prefabChildren[i]; - const { scope } = prefabData; + const { private_np, scope } = prefabData; - if (ui.sceneobjects.isNestedPrefabScope(scope)) { + if (private_np || ui.sceneobjects.isNestedPrefabScope(scope)) { spriteES._isNestedPrefabInstance = true; - spriteES._isLocalNestedPrefabInstance = - scope !== sceneobjects.ObjectScope.PUBLIC_NESTED_PREFAB; + spriteES._isPrivateNestedPrefabInstance = private_np; } - } else { - // TODO: we can flag it as a regular object, that is not part of a prefab instance - // or it is appended to a prefab instance. + this._isPrefabInstancePart = true; } // updates the object with the final data @@ -1309,11 +1320,10 @@ namespace phasereditor2d.scene.ui.sceneobjects { for (const originalChild of originalPrefabChildren) { - if (!sceneobjects.isNestedPrefabScope(originalChild.scope)) { + const isNestedPrefab = originalChild.private_np + || sceneobjects.isNestedPrefabScope(originalChild.scope); - result.push(originalChild); - - } else { + if (isNestedPrefab) { // find a local nested prefab @@ -1376,6 +1386,10 @@ namespace phasereditor2d.scene.ui.sceneobjects { result.push(nestedPrefab); } } + + } else { + + result.push(originalChild); } } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ObjectScope.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ObjectScope.ts index eb615d1e5..dfe43ef63 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ObjectScope.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ObjectScope.ts @@ -5,10 +5,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { METHOD = "METHOD", CLASS = "CLASS", PUBLIC = "PUBLIC", - LOCAL_NESTED_PREFAB = "NESTED_PREFAB_LOCAL", - METHOD_NESTED_PREFAB = "NESTED_PREFAB_METHOD", - CLASS_NESTED_PREFAB = "NESTED_PREFAB_CLASS", - PUBLIC_NESTED_PREFAB = "NESTED_PREFAB_PUBLIC", + NESTED_PREFAB = "NESTED_PREFAB", } export const OBJECT_SCOPES = [ @@ -16,25 +13,12 @@ namespace phasereditor2d.scene.ui.sceneobjects { ObjectScope.METHOD, ObjectScope.CLASS, ObjectScope.PUBLIC, - ObjectScope.LOCAL_NESTED_PREFAB, - ObjectScope.METHOD_NESTED_PREFAB, - ObjectScope.CLASS_NESTED_PREFAB, - ObjectScope.PUBLIC_NESTED_PREFAB + ObjectScope.NESTED_PREFAB ]; export function isNestedPrefabScope(scope: ObjectScope) { - switch (scope) { - - case ObjectScope.LOCAL_NESTED_PREFAB: - case ObjectScope.METHOD_NESTED_PREFAB: - case ObjectScope.CLASS_NESTED_PREFAB: - case ObjectScope.PUBLIC_NESTED_PREFAB: - - return true; - } - - return false; + return scope === ObjectScope.NESTED_PREFAB; } export function isClassOrPublicScope(scope: ObjectScope) { @@ -47,7 +31,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { switch (scope) { case ObjectScope.PUBLIC: - case ObjectScope.PUBLIC_NESTED_PREFAB: + case ObjectScope.NESTED_PREFAB: return true; } @@ -57,40 +41,16 @@ namespace phasereditor2d.scene.ui.sceneobjects { export function isLocalScope(scope: ObjectScope) { - switch (scope) { - - case ObjectScope.LOCAL: - case ObjectScope.LOCAL_NESTED_PREFAB: - - return true; - } - - return false; + return scope === ObjectScope.LOCAL; } export function isMethodScope(scope: ObjectScope) { - switch (scope) { - - case ObjectScope.METHOD: - case ObjectScope.METHOD_NESTED_PREFAB: - - return true; - } - - return false; + return scope === ObjectScope.METHOD; } export function isClassScope(scope: ObjectScope) { - switch (scope) { - - case ObjectScope.CLASS: - case ObjectScope.CLASS_NESTED_PREFAB: - - return true; - } - - return false; + return scope === ObjectScope.CLASS; } } \ No newline at end of file From 7bee7cc93a288166e0ae767093be87ca065fc90a Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 4 May 2023 14:05:57 -0400 Subject: [PATCH 28/59] Sanitizes nested prefab de-serialization. --- .../sceneobjects/GameObjectEditorSupport.ts | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts index a667ef2ef..2568d13cf 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts @@ -1351,13 +1351,31 @@ namespace phasereditor2d.scene.ui.sceneobjects { } } + let createFreshObject = true; + let newNestedPrefabs: json.IObjectData[]; + if (localNestedPrefab) { - result.push(localNestedPrefab); + if (isPublicScope(originalChild.scope)) { + // it is ok, the original child is public + // add the local nested prefab as final version of the object + result.push(localNestedPrefab); + + createFreshObject = false; + + } else { - } else { + // the original object is not public any more, + // we will create a link-object, but keeping the same nested prefabs + newNestedPrefabs = localNestedPrefab.nestedPrefabs; + } + } + + if (createFreshObject) { - // we don't have a local prefab, find one remote and create a pointer to it + // we don't have a local nested prefab, + // or the original nested prefab is not public any more + // so find one remote and create a pointer to it const remoteNestedPrefab = this.findRemoteNestedPrefab(objData.prefabId, originalChild.id); @@ -1369,6 +1387,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { id: Phaser.Utils.String.UUID(), prefabId: remoteNestedPrefab.id, label: remoteNestedPrefab.label, + nestedPrefabs: newNestedPrefabs }; result.push(nestedPrefab); @@ -1381,6 +1400,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { id: Phaser.Utils.String.UUID(), prefabId: originalChild.id, label: originalChild.label, + nestedPrefabs: newNestedPrefabs }; result.push(nestedPrefab); From 1b175a4b86e9ac619a0322daa99bee9f043c18f0 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 4 May 2023 14:39:39 -0400 Subject: [PATCH 29/59] Updates changelog. --- CHANGELOG.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 7e88b7b6e..5eca7d1fe 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -2,11 +2,11 @@ ## v3.60.1-dev -* Introduce the new variable scope: NESTED_PREFAB_METHOD, NESTED_PREFAB_CLASS, NESTED_PREFAB_PUBLIC. It introduce a new scene format version. * Checks if an scene file was generated by a newer and incompatible version of the editor. * Sorts prefabs in Blocks view: start with game object prefabs, end with script nodes. * Shows Object List items in the Outline view. Allows ordering the items with the Up, Down, Top, Down, commands. * A new game object scope: LOCAL. The LOCAL scope is now the default scope for objects and has the same meaning of METHOD scope before. Now the METHOD scope forces the creation of a variable for the object. +* Auto computes the middle-private nested prefabs. It doesn't require to declare a parent of a nested prefab as nested prefab. ## v3.60.3 - Apr 27, 2023 From 425957e3160b02a8840e8198a7b5957f59b7a27e Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 4 May 2023 16:38:44 -0400 Subject: [PATCH 30/59] Improves Outline elements tagging. --- .../SceneEditorOutlineStyledLabelProvider.ts | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineStyledLabelProvider.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineStyledLabelProvider.ts index 9670af5f2..6a4d4c346 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineStyledLabelProvider.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineStyledLabelProvider.ts @@ -110,20 +110,30 @@ namespace phasereditor2d.scene.ui.editor.outline { if (sceneobjects.isGameObject(obj)) { - const support = (obj as sceneobjects.ISceneGameObject).getEditorSupport(); + const objES = (obj as sceneobjects.ISceneGameObject).getEditorSupport(); + + if (obj instanceof sceneobjects.ScriptNode) { + + hintText += " #script"; + } - if (support.isMutableNestedPrefabInstance()) { + if (objES.isMutableNestedPrefabInstance()) { - hintText += "- nested prefab"; + hintText += " #nested_prefab_inst"; color = ScenePlugin.getInstance().getNestedPrefabColor(); - } else if (support.isPrefabInstance()) { + } else if (objES.isPrefabInstance()) { - hintText += "- prefab" + hintText += " #prefab_inst" color = ScenePlugin.getInstance().getPrefabColor(); } + + if (!objES.isNestedPrefabInstance()) { + + hintText += ` #scope_${objES.getScope().toLocaleLowerCase()}`; + } } if (hintText === "") { @@ -142,7 +152,7 @@ namespace phasereditor2d.scene.ui.editor.outline { }, { text: " " + hintText, - color: theme.viewerForeground + "90" + color: theme.viewerForeground + "45" } ]; } From f78d8f39303f64f4cd982007ed42ef2b0e3c403c Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Fri, 5 May 2023 10:11:46 -0400 Subject: [PATCH 31/59] Updates changelog. --- CHANGELOG.MD | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 5eca7d1fe..8417d5000 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -7,6 +7,7 @@ * Shows Object List items in the Outline view. Allows ordering the items with the Up, Down, Top, Down, commands. * A new game object scope: LOCAL. The LOCAL scope is now the default scope for objects and has the same meaning of METHOD scope before. Now the METHOD scope forces the creation of a variable for the object. * Auto computes the middle-private nested prefabs. It doesn't require to declare a parent of a nested prefab as nested prefab. +* Improves Outline elements tagging. It uses tahs like `#prefab_inst` `#nested_prefab_inst` `#scope_local` `#scope_nested_prefab`... So you can search for it in the Outline filter box. ## v3.60.3 - Apr 27, 2023 From 681499ba8984878c3965f9d6a25cb35e642d082a Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Fri, 5 May 2023 23:13:44 -0400 Subject: [PATCH 32/59] Advances the rectangle hit area. --- scripts/make-all-help-files.js | 5 + .../data/phaser-docs.json | 10 +- .../phasereditor2d.scene/src/ScenePlugin.ts | 2 + .../src/ui/editor/SceneEditorMenuCreator.ts | 11 ++ .../ui/editor/commands/SceneEditorCommands.ts | 95 +++++++++++++ .../sceneobjects/GameObjectEditorSupport.ts | 24 +++- .../hitArea/EnableHitAreaOperation.ts | 21 +++ .../sceneobjects/hitArea/HitAreaComponent.ts | 66 +++++++++ .../sceneobjects/hitArea/HitAreaProperty.ts | 42 ++++++ .../ui/sceneobjects/hitArea/HitAreaSection.ts | 35 +++++ .../hitArea/RectangleHitAreaComponent.ts | 128 ++++++++++++++++++ .../hitArea/RectangleHitAreaSection.ts | 30 ++++ 12 files changed, 463 insertions(+), 6 deletions(-) create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EnableHitAreaOperation.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaProperty.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaSection.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSection.ts diff --git a/scripts/make-all-help-files.js b/scripts/make-all-help-files.js index 019f6ea36..f043421a1 100755 --- a/scripts/make-all-help-files.js +++ b/scripts/make-all-help-files.js @@ -219,6 +219,11 @@ utils.makeHelpFile([ "Phaser.GameObjects.Polygon", + "Phaser.Geom.Rectangle.x", + "Phaser.Geom.Rectangle.y", + "Phaser.Geom.Rectangle.width", + "Phaser.Geom.Rectangle.height", + "Phaser.GameObjects.Layer", "Phaser.Physics.Arcade.Collider", diff --git a/source/editor/plugins/phasereditor2d.scene/data/phaser-docs.json b/source/editor/plugins/phasereditor2d.scene/data/phaser-docs.json index 17bdf2dd9..04b82b870 100644 --- a/source/editor/plugins/phasereditor2d.scene/data/phaser-docs.json +++ b/source/editor/plugins/phasereditor2d.scene/data/phaser-docs.json @@ -80,11 +80,11 @@ "Phaser.GameObjects.BitmapText.dropShadowY": "The vertical offset of the drop shadow.\n\nYou can set this directly, or use `Phaser.GameObjects.BitmapText#setDropShadow`.", "Phaser.GameObjects.BitmapText.dropShadowColor": "The color of the drop shadow.\n\nYou can set this directly, or use `Phaser.GameObjects.BitmapText#setDropShadow`.", "Phaser.GameObjects.BitmapText.dropShadowAlpha": "The alpha value of the drop shadow.\n\nYou can set this directly, or use `Phaser.GameObjects.BitmapText#setDropShadow`.", - "Phaser.Tilemaps.Tilemap": "A Tilemap is a container for Tilemap data. This isn't a display object, rather, it holds data\nabout the map and allows you to add tilesets and tilemap layers to it. A map can have one or\nmore tilemap layers, which are the display objects that actually render the tiles.\n\nThe Tilemap data can be parsed from a Tiled JSON file, a CSV file or a 2D array. Tiled is a free\nsoftware package specifically for creating tile maps, and is available from:\nhttp://www.mapeditor.org\n\nAs of Phaser 3.50.0 the Tilemap API now supports the following types of map:\n\n1) Orthogonal\n2) Isometric\n3) Hexagonal\n4) Staggered\n\nPrior to this release, only orthogonal maps were supported.\n\nAnother large change in 3.50 was the consolidation of Tilemap Layers. Previously, you created\neither a Static or Dynamic Tilemap Layer. However, as of 3.50 the features of both have been\nmerged and the API simplified, so now there is just the single `TilemapLayer` class.\n\nA Tilemap has handy methods for getting and manipulating the tiles within a layer, allowing\nyou to build or modify the tilemap data at runtime.\n\nNote that all Tilemaps use a base tile size to calculate dimensions from, but that a\nTilemapLayer may have its own unique tile size that overrides this.\n\nAs of Phaser 3.21.0, if your tilemap includes layer groups (a feature of Tiled 1.2.0+) these\nwill be traversed and the following properties will impact children:\n\n- Opacity (blended with parent) and visibility (parent overrides child)\n- Vertical and horizontal offset\n\nThe grouping hierarchy is not preserved and all layers will be flattened into a single array.\n\nGroup layers are parsed during Tilemap construction but are discarded after parsing so dynamic\nlayers will NOT continue to be affected by a parent.\n\nTo avoid duplicate layer names, a layer that is a child of a group layer will have its parent\ngroup name prepended with a '/'. For example, consider a group called 'ParentGroup' with a\nchild called 'Layer 1'. In the Tilemap object, 'Layer 1' will have the name\n'ParentGroup/Layer 1'.", + "Phaser.Tilemaps.Tilemap": "A Tilemap is a container for Tilemap data. This isn't a display object, rather, it holds data\nabout the map and allows you to add tilesets and tilemap layers to it. A map can have one or\nmore tilemap layers, which are the display objects that actually render the tiles.\n\nThe Tilemap data can be parsed from a Tiled JSON file, a CSV file or a 2D array. Tiled is a free\nsoftware package specifically for creating tile maps, and is available from:\nhttp://www.mapeditor.org\n\nAs of Phaser 3.50.0 the Tilemap API now supports the following types of map:\n\n1) Orthogonal\n2) Isometric\n3) Hexagonal\n4) Staggered\n\nPrior to this release, only orthogonal maps were supported.\n\nAnother large change in 3.50 was the consolidation of Tilemap Layers. Previously, you created\neither a Static or Dynamic Tilemap Layer. However, as of 3.50 the features of both have been\nmerged and the API simplified, so now there is just the single `TilemapLayer` class.\n\nA Tilemap has handy methods for getting and manipulating the tiles within a layer, allowing\nyou to build or modify the tilemap data at runtime.\n\nNote that all Tilemaps use a base tile size to calculate dimensions from, but that a\nTilemapLayer may have its own unique tile size that overrides this.\n\nAs of Phaser 3.21.0, if your tilemap includes layer groups (a feature of Tiled 1.2.0+) these\nwill be traversed and the following properties will impact children:\n\n- Opacity (blended with parent) and visibility (parent overrides child)\n- Vertical and horizontal offset\n\nThe grouping hierarchy is not preserved and all layers will be flattened into a single array.\n\nGroup layers are parsed during Tilemap construction but are discarded after parsing so dynamic\nlayers will NOT continue to be affected by a parent.\n\nTo avoid duplicate layer names, a layer that is a child of a group layer will have its parent\ngroup name prepended with a '/'. For example, consider a group called 'ParentGroup' with a\nchild called 'Layer 1'. In the Tilemap object, 'Layer 1' will have the name\n'ParentGroup/Layer 1'.\n\nThe Phaser Tiled Parser does **not** support the 'Collection of Images' feature for a Tileset.\nYou must ensure all of your tiles are contained in a single tileset image file (per layer)\nand have this 'embedded' in the exported Tiled JSON map data.", "Phaser.Tilemaps.Tilemap.tileWidth": "The base width of a tile in pixels. Note that individual layers may have a different tile\nwidth.", "Phaser.Tilemaps.Tilemap.tileHeight": "The base height of a tile in pixels. Note that individual layers may have a different\ntile height.", "Phaser.GameObjects.GameObjectFactory.tilemap(key)": "The key in the Phaser cache that corresponds to the loaded tilemap data.", - "Phaser.Tilemaps.Tileset": "A Tileset is a combination of an image containing the tiles and a container for data about\neach tile.", + "Phaser.Tilemaps.Tileset": "A Tileset is a combination of a single image containing the tiles and a container for data about\neach tile.", "Phaser.Tilemaps.Tileset.name": "The name of the Tileset.", "Phaser.Tilemaps.Tileset.image": "The cached image that contains the individual tiles. Use setImage to set.", "Phaser.Tilemaps.Tileset.tileWidth": "The width of each tile (in pixels). Use setTileSize to change.", @@ -119,6 +119,10 @@ "Phaser.Geom.Triangle.x3": "`x` coordinate of the third point.", "Phaser.Geom.Triangle.y3": "`y` coordinate of the third point.", "Phaser.GameObjects.Polygon": "The Polygon Shape is a Game Object that can be added to a Scene, Group or Container. You can\ntreat it like any other Game Object in your game, such as tweening it, scaling it, or enabling\nit for input or physics. It provides a quick and easy way for you to render this shape in your\ngame without using a texture, while still taking advantage of being fully batched in WebGL.\n\nThis shape supports both fill and stroke colors.\n\nThe Polygon Shape is created by providing a list of points, which are then used to create an\ninternal Polygon geometry object. The points can be set from a variety of formats:\n\n- A string containing paired values separated by a single space: `'40 0 40 20 100 20 100 80 40 80 40 100 0 50'`\n- An array of Point or Vector2 objects: `[new Phaser.Math.Vector2(x1, y1), ...]`\n- An array of objects with public x/y properties: `[obj1, obj2, ...]`\n- An array of paired numbers that represent point coordinates: `[x1,y1, x2,y2, ...]`\n- An array of arrays with two elements representing x/y coordinates: `[[x1, y1], [x2, y2], ...]`\n\nBy default the `x` and `y` coordinates of this Shape refer to the center of it. However, depending\non the coordinates of the points provided, the final shape may be rendered offset from its origin.", + "Phaser.Geom.Rectangle.x": "The X coordinate of the top left corner of the Rectangle.", + "Phaser.Geom.Rectangle.y": "The Y coordinate of the top left corner of the Rectangle.", + "Phaser.Geom.Rectangle.width": "The width of the Rectangle, i.e. the distance between its left side (defined by `x`) and its right side.", + "Phaser.Geom.Rectangle.height": "The height of the Rectangle, i.e. the distance between its top side (defined by `y`) and its bottom side.", "Phaser.GameObjects.Layer": "A Layer Game Object.\n\nA Layer is a special type of Game Object that acts as a Display List. You can add any type of Game Object\nto a Layer, just as you would to a Scene. Layers can be used to visually group together 'layers' of Game\nObjects:\n\n```javascript\nconst spaceman = this.add.sprite(150, 300, 'spaceman');\nconst bunny = this.add.sprite(400, 300, 'bunny');\nconst elephant = this.add.sprite(650, 300, 'elephant');\n\nconst layer = this.add.layer();\n\nlayer.add([ spaceman, bunny, elephant ]);\n```\n\nThe 3 sprites in the example above will now be managed by the Layer they were added to. Therefore,\nif you then set `layer.setVisible(false)` they would all vanish from the display.\n\nYou can also control the depth of the Game Objects within the Layer. For example, calling the\n`setDepth` method of a child of a Layer will allow you to adjust the depth of that child _within the\nLayer itself_, rather than the whole Scene. The Layer, too, can have its depth set as well.\n\nThe Layer class also offers many different methods for manipulating the list, such as the\nmethods `moveUp`, `moveDown`, `sendToBack`, `bringToTop` and so on. These allow you to change the\ndisplay list position of the Layers children, causing it to adjust the order in which they are\nrendered. Using `setDepth` on a child allows you to override this.\n\nLayers can have Post FX Pipelines set, which allows you to easily enable a post pipeline across\na whole range of children, which, depending on the effect, can often be far more efficient that doing so\non a per-child basis.\n\nLayers have no position or size within the Scene. This means you cannot enable a Layer for\nphysics or input, or change the position, rotation or scale of a Layer. They also have no scroll\nfactor, texture, tint, origin, crop or bounds.\n\nIf you need those kind of features then you should use a Container instead. Containers can be added\nto Layers, but Layers cannot be added to Containers.\n\nHowever, you can set the Alpha, Blend Mode, Depth, Mask and Visible state of a Layer. These settings\nwill impact all children being rendered by the Layer.", "Phaser.Physics.Arcade.Collider": "An Arcade Physics Collider will automatically check for collision, or overlaps, between two objects\nevery step. If a collision, or overlap, occurs it will invoke the given callbacks.", "Phaser.Physics.Arcade.Image": "An Arcade Physics Image is an Image with an Arcade Physics body and related components.\nThe body can be dynamic or static.\n\nThe main difference between an Arcade Image and an Arcade Sprite is that you cannot animate an Arcade Image.", @@ -129,7 +133,7 @@ "Phaser.Physics.Arcade.Body.radius": "If this Body is circular, this is the unscaled radius of the Body, as set by setCircle(), in source pixels.\nThe true radius is equal to `halfWidth`.", "Phaser.Physics.Arcade.Body.moves": "Whether the Body's position and rotation are affected by its velocity, acceleration, drag, and gravity.", "Phaser.Physics.Arcade.Body.velocity": "The Body's velocity, in pixels per second.", - "Phaser.Physics.Arcade.Body.maxVelocity": "The Body's absolute maximum velocity, in pixels per second.\nThe horizontal and vertical components are applied separately.", + "Phaser.Physics.Arcade.Body.maxVelocity": "The absolute maximum velocity of this body, in pixels per second.\nThe horizontal and vertical components are applied separately.", "Phaser.Physics.Arcade.Body.maxSpeed": "The maximum speed this Body is allowed to reach, in pixels per second.\n\nIf not negative it limits the scalar value of speed.\n\nAny negative value means no maximum is being applied (the default).", "Phaser.Physics.Arcade.Body.allowGravity": "Whether this Body's position is affected by gravity (local or world).", "Phaser.Physics.Arcade.Body.gravity": "Acceleration due to gravity (specific to this Body), in pixels per second squared.\nTotal gravity is the sum of this vector and the simulation's `gravity`.", diff --git a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts index 90cccdb8b..b7c5c05e6 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts @@ -374,6 +374,8 @@ namespace phasereditor2d.scene { page => new ui.sceneobjects.TileSpriteSection(page), page => new ui.sceneobjects.NineSliceSection(page), page => new ui.sceneobjects.ThreeSliceSection(page), + page => new ui.sceneobjects.HitAreaSection(page), + page => new ui.sceneobjects.RectangleHitAreaSection(page), page => new ui.sceneobjects.ArcadeBodySection(page), page => new ui.sceneobjects.ArcadeGeometrySection(page), page => new ui.sceneobjects.ArcadeBodyMovementSection(page), diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditorMenuCreator.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditorMenuCreator.ts index 4c4b68a56..6b88293fe 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditorMenuCreator.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditorMenuCreator.ts @@ -35,6 +35,8 @@ namespace phasereditor2d.scene.ui.editor { menu.addMenu(this.createTextureMenu()); + menu.addMenu(this.createHitAreaMenu()); + menu.addMenu(this.createArcadePhysicsMenu()); menu.addMenu(this.createParentMenu()); @@ -56,6 +58,15 @@ namespace phasereditor2d.scene.ui.editor { menu.addMenu(this.createCompilerMenu()); } + createHitAreaMenu(): controls.Menu { + + const menu = new controls.Menu("Hit Area"); + + menu.addCommand(ui.editor.commands.CMD_HIT_AREA_ENABLE); + + return menu; + } + createScriptsMenu(): controls.Menu { const menu = new controls.Menu("Scripts"); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts index 7a65cf0f2..81ecd8a89 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts @@ -59,6 +59,8 @@ namespace phasereditor2d.scene.ui.editor.commands { export const CMD_OPEN_SCRIPT_DIALOG = "phasereditor2d.scene.ui.editor.commands.OpenScriptDialog"; export const CMD_OPEN_ADD_SCRIPT_DIALOG = "phasereditor2d.scene.ui.editor.commands.OpenAddScriptDialog"; export const CMD_PREVIEW_SCENE = "phasereditor2d.scene.ui.editor.commands.PreviewScene"; + export const CMD_HIT_AREA_ENABLE = "phasereditor2d.scene.ui.editor.commands.EnableHitArea"; + export const CMD_HIT_AREA_DISABLE = "phasereditor2d.scene.ui.editor.commands.DisableHitArea"; function isSceneScope(args: colibri.ui.ide.commands.HandlerArgs) { @@ -152,6 +154,99 @@ namespace phasereditor2d.scene.ui.editor.commands { this.registerArcadeCommands(manager); this.registerScriptNodeCommands(manager); + + this.registerHitAreaCommands(manager); + } + + private static registerHitAreaCommands(manager: colibri.ui.ide.commands.CommandManager) { + + manager.add({ + command: { + id: CMD_HIT_AREA_ENABLE, + name: "Enable Hit Area", + category: CAT_SCENE_EDITOR, + tooltip: "Enable the input system of the selected objects", + }, + handler: { + testFunc: args => { + + if (!isSceneScope(args)) { + + return false; + } + + for (const obj of args.activeEditor.getSelection()) { + + const objES = sceneobjects.GameObjectEditorSupport.getEditorSupport(obj); + + if (objES && objES.isDisplayObject()) { + + if (objES.hasComponent(sceneobjects.HitAreaComponent)) { + + return false; + } + + } else { + + return false; + } + } + + return true; + }, + executeFunc: args => { + + const editor = args.activeEditor as SceneEditor; + + editor.getUndoManager().add( + new ui.sceneobjects.EnableHitAreaOperation(editor, true)); + } + } + }); + + manager.add({ + command: { + id: CMD_HIT_AREA_DISABLE, + name: "Disable Hit Area", + category: CAT_SCENE_EDITOR, + tooltip: "Enable the input system of the selected objects", + }, + handler: { + testFunc: args => { + + if (!isSceneScope(args)) { + + return false; + } + + for (const obj of args.activeEditor.getSelection()) { + + const objES = sceneobjects.GameObjectEditorSupport.getEditorSupport(obj); + + if (objES && objES.isDisplayObject()) { + + if (!objES.hasComponent(sceneobjects.HitAreaComponent)) { + + return false; + } + + } else { + + return false; + } + } + + return true; + }, + executeFunc: args => { + + const editor = args.activeEditor as SceneEditor; + + editor.getUndoManager().add( + new ui.sceneobjects.EnableHitAreaOperation(editor, false)); + } + } + }); } private static registerScriptNodeCommands(manager: colibri.ui.ide.commands.CommandManager) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts index 2568d13cf..b77c3cf4c 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts @@ -2,6 +2,12 @@ namespace phasereditor2d.scene.ui.sceneobjects { import json = core.json; + import controls = colibri.ui.controls; + + interface IUnlockListenerArg { + property: IProperty, + unlock: boolean; + } export abstract class GameObjectEditorSupport extends EditorSupport { @@ -16,6 +22,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { private _isPrefabInstancePart: boolean; // a temporal variable used for serialization public _private_np: boolean; + public unlockEvent: controls.ListenerList>; // parent private _allowPickChildren: boolean; @@ -29,6 +36,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { super(obj, extension.getTypeName().toLowerCase(), scene); this._extension = extension; + this.unlockEvent = new controls.ListenerList(); this._unlockedProperties = new Set(); this._serializables = []; this._componentMap = new Map(); @@ -52,6 +60,14 @@ namespace phasereditor2d.scene.ui.sceneobjects { this.addComponent(new PrefabUserPropertyComponent(obj)); this.addComponent(new UserComponentsEditorComponent(obj)); + if (this.isDisplayObject()) { + + this.addComponent( + new HitAreaComponent(obj), + new RectangleHitAreaComponent(obj) + ); + } + this.setInteractive(); scene.addGameObject(obj); @@ -336,6 +352,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { this._unlockedProperties.delete(property.name); } + + this.unlockEvent.fire({ property, unlock }); } setUnlockedPropertyXY(property: IPropertyXY, unlock: boolean) { @@ -1369,8 +1387,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { // we will create a link-object, but keeping the same nested prefabs newNestedPrefabs = localNestedPrefab.nestedPrefabs; } - } - + } + if (createFreshObject) { // we don't have a local nested prefab, @@ -1406,7 +1424,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { result.push(nestedPrefab); } } - + } else { result.push(originalChild); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EnableHitAreaOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EnableHitAreaOperation.ts new file mode 100644 index 000000000..0b3504fc1 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EnableHitAreaOperation.ts @@ -0,0 +1,21 @@ +/// + +namespace phasereditor2d.scene.ui.sceneobjects { + + export class EnableHitAreaOperation extends SceneGameObjectOperation { + + constructor(editor: ui.editor.SceneEditor, enable: boolean) { + super(editor, editor.getSelectedGameObjects(), enable); + } + + getValue(obj: ISceneGameObject) { + + return obj.getEditorSupport().hasComponent(HitAreaComponent); + } + + setValue(obj: ISceneGameObject, enable: boolean): void { + + HitAreaComponent.enableHitArea(obj, enable); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts new file mode 100644 index 000000000..274ad5d39 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts @@ -0,0 +1,66 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + export enum HitAreaShape { + CIRCLE = "CIRCLE", + RECTANGLE = "RECTANGLE" + } + + function getComp(obj: ISceneGameObject) { + + return obj.getEditorSupport().getComponent(HitAreaComponent) as HitAreaComponent; + } + + export class HitAreaComponent extends Component { + + static hitAreaShape: IEnumProperty = { + name: "hitArea.shape", + label: "Shape", + defValue: HitAreaShape.RECTANGLE, + getValue: obj => getComp(obj).getHitAreaShape(), + setValue: (obj, value) => getComp(obj).setHitAreaShape(value), + getValueLabel: value => value.toString(), + values: [ + HitAreaShape.RECTANGLE, + HitAreaShape.CIRCLE + ] + }; + + private _hitAreaShape: HitAreaShape; + + constructor(obj: ISceneGameObject) { + super(obj, [ + HitAreaComponent.hitAreaShape + ], false); + + this._hitAreaShape = HitAreaShape.RECTANGLE; + } + + static enableHitArea(obj: ISceneGameObject, enable: boolean) { + + const objES = obj.getEditorSupport(); + + objES.setComponentActive(HitAreaComponent, enable); + + if (enable) { + + const comp = objES.getComponent(HitAreaComponent) as HitAreaComponent; + comp.setHitAreaShape(HitAreaShape.RECTANGLE); + const rectComp = objES.getComponent(RectangleHitAreaComponent) as RectangleHitAreaComponent; + rectComp.setDefaultValues(); + } + } + + getHitAreaShape() { + + return this._hitAreaShape; + } + + setHitAreaShape(hitAreaShape: HitAreaShape) { + + this._hitAreaShape = hitAreaShape; + } + + buildSetObjectPropertiesCodeDOM(args: ISetObjectPropertiesCodeDOMArgs): void { + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaProperty.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaProperty.ts new file mode 100644 index 000000000..9275c6fdd --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaProperty.ts @@ -0,0 +1,42 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + export function HitAreaProperty( + component: Function, + name: string, + label: string, + tooltip: string, + defValue: any, + ): IProperty { + + return { + name: `hitArea.${name}`, + label, + tooltip, + defValue, + getValue: (obj:ISceneGameObject) => { + + const objES = obj.getEditorSupport(); + + const comp = objES.getComponent(component); + + if (comp) { + + return comp[name]; + } + + return undefined; + }, + setValue: (obj:ISceneGameObject, value: any) => { + + const objES = obj.getEditorSupport(); + + const comp = objES.getComponent(component); + + if (comp) { + + comp[name] = value; + } + }, + }; + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaSection.ts new file mode 100644 index 000000000..0934c2a35 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaSection.ts @@ -0,0 +1,35 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + import controls = colibri.ui.controls; + + export class HitAreaSection extends SceneGameObjectSection { + + constructor(page: controls.properties.PropertyPage) { + super(page, "phasereditor2d.scene.ui.sceneobjects.HitAreaSection", "Hit Area"); + } + + createMenu(menu: controls.Menu): void { + + menu.addCommand(ui.editor.commands.CMD_HIT_AREA_DISABLE); + } + + createForm(parent: HTMLDivElement) { + + const comp = this.createGridElement(parent, 3); + + const { hitAreaShape } = HitAreaComponent; + + this.createPropertyEnumRow(comp, hitAreaShape); + } + + canEdit(obj: any, n: number): boolean { + + return GameObjectEditorSupport.hasObjectComponent(obj, HitAreaComponent); + } + + canEditNumber(n: number): boolean { + + return n > 0; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts new file mode 100644 index 000000000..3f4c0d3eb --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts @@ -0,0 +1,128 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + export class RectangleHitAreaComponent extends Component { + + static x = HitAreaProperty(RectangleHitAreaComponent, "x", "X", "phaser:Phaser.Geom.Rectangle.x", 0); + static y = HitAreaProperty(RectangleHitAreaComponent, "y", "Y", "phaser:Phaser.Geom.Rectangle.y", 0); + static width = HitAreaProperty(RectangleHitAreaComponent, "width", "W", "phaser:Phaser.Geom.Rectangle.width", 0); + static height = HitAreaProperty(RectangleHitAreaComponent, "height", "H", "phaser:Phaser.Geom.Rectangle.height", 0); + static position: IPropertyXY = { + label: "Offset", + x: this.x, + y: this.y + }; + static size: IPropertyXY = { + label: "Size", + x: this.width, + y: this.height + }; + + public x = 0; + public y = 0; + public width = 0; + public height = 0; + + constructor(obj: ISceneGameObject) { + super(obj, [ + RectangleHitAreaComponent.x, + RectangleHitAreaComponent.y, + RectangleHitAreaComponent.width, + RectangleHitAreaComponent.height, + ]); + } + + private initUnlockListener() { + + const objES = this.getEditorSupport(); + + const unlockEvent = objES.unlockEvent; + + unlockEvent.addListener(args => { + + if (args.property === HitAreaComponent.hitAreaShape) { + + objES.setUnlockedProperty(RectangleHitAreaComponent.x, args.unlock); + objES.setUnlockedProperty(RectangleHitAreaComponent.y, args.unlock); + objES.setUnlockedProperty(RectangleHitAreaComponent.width, args.unlock); + objES.setUnlockedProperty(RectangleHitAreaComponent.height, args.unlock); + } + }); + } + + readJSON(ser: core.json.Serializer): void { + + this.initUnlockListener(); + + super.readJSON(ser); + } + + setDefaultValues() { + + console.log("here"); + + const obj = this.getObject() as Image; + const objES = this.getEditorSupport(); + + this.x = 0; + this.y = 0; + + let width = 0, height = 0; + + let [widthProp, heightProp] = objES.getSizeProperties(); + + if (widthProp && heightProp) { + + width = widthProp.getValue(obj); + height = heightProp.getValue(obj); + + } else if (obj.width && obj.height) { + + width = obj.width; + height = obj.height; + } + + this.width = width; + this.height = height; + } + + buildSetObjectPropertiesCodeDOM(args: ISetObjectPropertiesCodeDOMArgs): void { + + const obj = this.getObject(); + const objES = obj.getEditorSupport(); + const hitAreaComp = objES.getComponent(HitAreaComponent); + + if (hitAreaComp) { + + if (objES.isUnlockedProperty(HitAreaComponent.hitAreaShape)) { + + if (HitAreaComponent.hitAreaShape.getValue(obj) === HitAreaShape.RECTANGLE) { + + if (objES.isPrefabInstance()) { + + // we should disable the input, then enable it again with a new shape + const code = new core.code.MethodCallCodeDOM("removeInteractive", args.objectVarName); + args.statements.push(code); + } + + const code = new core.code.MethodCallCodeDOM("setInteractive", args.objectVarName); + + const { x, y, width, height } = RectangleHitAreaComponent; + + const geomArgs = [ + x.getValue(obj) ?? x.defValue, + y.getValue(obj) ?? y.defValue, + width.getValue(obj) ?? width.defValue, + height.getValue(obj) ?? height.defValue + ]; + + code.arg(`new Phaser.Geom.Rectangle(${geomArgs.join(", ")})`); + + code.arg("Phaser.Geom.Rectangle.Contains"); + + args.statements.push(code); + } + } + } + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSection.ts new file mode 100644 index 000000000..5eae36d8d --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSection.ts @@ -0,0 +1,30 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + import controls = colibri.ui.controls; + + export class RectangleHitAreaSection extends SceneGameObjectSection { + + constructor(page: controls.properties.PropertyPage) { + super(page, "phasereditor2d.scene.ui.sceneobjects.RectangleHitAreaSection", "Hit Area (Rectangle)"); + } + + createForm(parent: HTMLDivElement) { + + const comp = this.createGridElementWithPropertiesXY(parent); + + this.createPropertyXYRow(comp, RectangleHitAreaComponent.position, false); + this.createPropertyXYRow(comp, RectangleHitAreaComponent.size, false); + } + + canEdit(obj: any, n: number): boolean { + + return GameObjectEditorSupport.hasObjectComponent(obj, HitAreaComponent) + && GameObjectEditorSupport.hasObjectComponent(obj, RectangleHitAreaComponent); + } + + canEditNumber(n: number): boolean { + + return n > 0; + } + } +} \ No newline at end of file From 9cdcf12bf04bb78c487b2146a4e3646640d2aabe Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Fri, 5 May 2023 23:47:33 -0400 Subject: [PATCH 33/59] Allows changing the hit area shape and properties in a prefab instance. --- .../src/ui/editor/commands/SceneEditorCommands.ts | 2 +- .../src/ui/sceneobjects/hitArea/HitAreaComponent.ts | 1 + .../src/ui/sceneobjects/hitArea/HitAreaProperty.ts | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts index 81ecd8a89..df25a9939 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts @@ -179,7 +179,7 @@ namespace phasereditor2d.scene.ui.editor.commands { const objES = sceneobjects.GameObjectEditorSupport.getEditorSupport(obj); - if (objES && objES.isDisplayObject()) { + if (objES && objES.isDisplayObject() && !objES.isPrefabInstance()) { if (objES.hasComponent(sceneobjects.HitAreaComponent)) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts index 274ad5d39..9c1e6b209 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts @@ -13,6 +13,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { export class HitAreaComponent extends Component { static hitAreaShape: IEnumProperty = { + local: true, name: "hitArea.shape", label: "Shape", defValue: HitAreaShape.RECTANGLE, diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaProperty.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaProperty.ts index 9275c6fdd..eaa03674a 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaProperty.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaProperty.ts @@ -9,6 +9,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { ): IProperty { return { + local: true, name: `hitArea.${name}`, label, tooltip, From 9477896427ccb1e1992fb6ad5142ce21f50be273 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Sat, 6 May 2023 12:55:32 -0400 Subject: [PATCH 34/59] Implements first version of the rectangle tool. --- .../phasereditor2d.scene/src/ScenePlugin.ts | 3 +- .../ui/editor/commands/SceneEditorCommands.ts | 18 ++ .../hitArea/BaseHitAreaSizeToolItem.ts | 192 +++++++++++++++ .../hitArea/RectangleHitAreaComponent.ts | 9 + .../hitArea/RectangleHitAreaSection.ts | 4 +- .../hitArea/ResizeHitAreaOperation.ts | 31 +++ .../sceneobjects/hitArea/ResizeHitAreaTool.ts | 226 ++++++++++++++++++ .../hitArea/ResizeHitAreaToolItem.ts | 60 +++++ 8 files changed, 541 insertions(+), 2 deletions(-) create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaSizeToolItem.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaOperation.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaTool.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaToolItem.ts diff --git a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts index b7c5c05e6..b5bcc36a4 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts @@ -406,11 +406,12 @@ namespace phasereditor2d.scene { new ui.sceneobjects.ScaleTool(), new ui.sceneobjects.OriginTool(), new ui.sceneobjects.SizeTool(), + new ui.sceneobjects.ResizeHitAreaTool(), new ui.sceneobjects.ArcadeBodyTool(), new ui.sceneobjects.SliceTool(), new ui.sceneobjects.PolygonTool(), new ui.sceneobjects.SelectionRegionTool(), - new ui.sceneobjects.PanTool(), + new ui.sceneobjects.PanTool() )); // files view sections diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts index df25a9939..332a7aa40 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts @@ -61,6 +61,7 @@ namespace phasereditor2d.scene.ui.editor.commands { export const CMD_PREVIEW_SCENE = "phasereditor2d.scene.ui.editor.commands.PreviewScene"; export const CMD_HIT_AREA_ENABLE = "phasereditor2d.scene.ui.editor.commands.EnableHitArea"; export const CMD_HIT_AREA_DISABLE = "phasereditor2d.scene.ui.editor.commands.DisableHitArea"; + export const CMD_RESIZE_HIT_AREA = "phasereditor2d.scene.ui.editor.commands.ResizeHitArea"; function isSceneScope(args: colibri.ui.ide.commands.HandlerArgs) { @@ -1995,6 +1996,23 @@ namespace phasereditor2d.scene.ui.editor.commands { .getToolsManager().swapTool(ui.sceneobjects.SliceTool.ID) } }); + + manager.add({ + command: { + id: CMD_RESIZE_HIT_AREA, + name: "Resize Hit Area Tool", + tooltip: "Resize the hit area of the selected objects.", + category: CAT_SCENE_EDITOR + }, + handler: { + testFunc: isSceneScope, + executeFunc: args => (args.activeEditor as SceneEditor) + .getToolsManager().swapTool(ui.sceneobjects.ResizeHitAreaTool.ID) + }, + keys: { + key: "KeyI" + } + }); } private static registerVisibilityCommands(manager: colibri.ui.ide.commands.CommandManager) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaSizeToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaSizeToolItem.ts new file mode 100644 index 000000000..79105a47a --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaSizeToolItem.ts @@ -0,0 +1,192 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + export abstract class BaseHitAreaSizeToolItem + extends editor.tools.SceneToolItem implements editor.tools.ISceneToolItemXY { + + private _x: IScaleAxis; + private _y: IScaleAxis; + private _dragging: boolean; + + constructor(x: IScaleAxis, y: IScaleAxis) { + super(); + + this._x = x; + this._y = y; + } + + protected abstract computeSize(obj: ISceneGameObject): { width: number, height: number }; + + protected abstract getHitAreaOffset(obj: ISceneGameObject): { x: number, y: number }; + + protected abstract getDataKey(): string; + + protected abstract getHitAreaSectionId(): string; + + protected abstract onDragValues(obj: ISceneGameObject, changeX: boolean, changeY: boolean, width: number, height: number): void; + + protected abstract createStopDragOperation(args: editor.tools.ISceneToolDragEventArgs): colibri.ui.ide.undo.Operation; + + getPoint(args: editor.tools.ISceneToolContextArgs): { x: number; y: number; } { + + return this.getAvgScreenPointOfObjects(args, + + (sprite: sceneobjects.Image) => this._x, + + (sprite: sceneobjects.Image) => this._y, + ); + } + + protected getScreenPointOfObject(args: ui.editor.tools.ISceneToolContextArgs, sprite: Sprite, fx: number, fy: number, removeRotation = false) { + + const worldPoint = new Phaser.Geom.Point(0, 0); + + const { width, height } = this.computeSize(sprite); + + let { displayOriginX, displayOriginY } = sprite.getEditorSupport().computeDisplayOrigin(); + + if (sprite instanceof Container) { + + displayOriginX = 0; + displayOriginY = 0; + } + + const offset = this.getHitAreaOffset(sprite); + const x = offset.x - displayOriginX + fx * width; + const y = offset.y - displayOriginY + fy * height; + + const tx = sprite.getWorldTransformMatrix(); + + if (removeRotation) { + + tx.rotate(-tx.rotation); + } + + tx.transformPoint(x, y, worldPoint); + + return args.camera.getScreenPoint(worldPoint.x, worldPoint.y); + } + + render(args: editor.tools.ISceneToolRenderArgs) { + + const point = this.getPoint(args); + + const ctx = args.canvasContext; + + ctx.save(); + + ctx.translate(point.x, point.y); + + const angle = this.globalAngle(args.objects[0] as any); + ctx.rotate(Phaser.Math.DegToRad(angle)); + + this.drawRect(ctx, args.canEdit ? + ResizeHitAreaTool.TOOL_COLOR : editor.tools.SceneTool.COLOR_CANNOT_EDIT); + + ctx.restore(); + } + + containsPoint(args: editor.tools.ISceneToolDragEventArgs): boolean { + + const point = this.getPoint(args); + + return Phaser.Math.Distance.Between(args.x, args.y, point.x, point.y) < 20; + } + + onStartDrag(args: editor.tools.ISceneToolDragEventArgs): void { + + if (!this.containsPoint(args)) { + return; + } + + this._dragging = true; + + const point = this.getPoint(args); + + for (const obj of args.objects) { + + const sprite = obj as unknown as Image; + + const worldTx = new Phaser.GameObjects.Components.TransformMatrix(); + + const initLocalPos = new Phaser.Math.Vector2(); + + sprite.getWorldTransformMatrix(worldTx); + + worldTx.applyInverse(point.x, point.y, initLocalPos); + + const { width, height } = this.computeSize(sprite); + + sprite.setData(this.getDataKey(), { + initWidth: width, + initHeight: height, + initLocalPos: initLocalPos, + initWorldTx: worldTx + }); + } + } + + protected getInitialSize(obj: any): { x: number, y: number } { + + const data = obj.getData(this.getDataKey()); + + return { x: data.initWidth, y: data.initHeight }; + } + + onDrag(args: editor.tools.ISceneToolDragEventArgs): void { + + if (!this._dragging) { + + return; + } + + const camera = args.camera; + + for (const obj of args.objects) { + + const sprite = obj as Sprite; + const data = sprite.data.get(this.getDataKey()); + const initLocalPos: Phaser.Math.Vector2 = data.initLocalPos; + const worldTx: Phaser.GameObjects.Components.TransformMatrix = data.initWorldTx; + + const localPos = new Phaser.Math.Vector2(); + + worldTx.applyInverse(args.x, args.y, localPos); + + const flipX = sprite.flipX ? -1 : 1; + const flipY = sprite.flipY ? -1 : 1; + + let originX = 0; + let originY = 0; + + const dx = (localPos.x - initLocalPos.x) * flipX / camera.zoom; + const dy = (localPos.y - initLocalPos.y) * flipY / camera.zoom; + + const dw = dx / (1 - (originX === 1 ? 0 : originX)); + const dh = dy / (1 - (originY === 1 ? 0 : originY)); + + const { x: width, y: height } = args.editor.getScene().snapPoint( + data.initWidth + dw, + data.initHeight + dh + ); + + const changeAll = this._x === 1 && this._y === 1; + const changeX = this._x === 1 && this._y === 0.5 || changeAll; + const changeY = this._x === 0.5 && this._y === 1 || changeAll; + + this.onDragValues(sprite, changeX, changeY, width, height); + + args.editor.updateInspectorViewSection(this.getHitAreaSectionId()); + } + } + + onStopDrag(args: editor.tools.ISceneToolDragEventArgs): void { + + if (this._dragging) { + + args.editor.getUndoManager().add(this.createStopDragOperation(args)); + + this._dragging = false; + } + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts index 3f4c0d3eb..75b210f7b 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts @@ -31,6 +31,15 @@ namespace phasereditor2d.scene.ui.sceneobjects { ]); } + static getRectangleComponent(obj: ISceneGameObject) { + + const objES = obj.getEditorSupport(); + + const comp = objES.getComponent(RectangleHitAreaComponent) as RectangleHitAreaComponent; + + return comp; + } + private initUnlockListener() { const objES = this.getEditorSupport(); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSection.ts index 5eae36d8d..f8e5d98ec 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSection.ts @@ -4,8 +4,10 @@ namespace phasereditor2d.scene.ui.sceneobjects { export class RectangleHitAreaSection extends SceneGameObjectSection { + static ID = "phasereditor2d.scene.ui.sceneobjects.RectangleHitAreaSection"; + constructor(page: controls.properties.PropertyPage) { - super(page, "phasereditor2d.scene.ui.sceneobjects.RectangleHitAreaSection", "Hit Area (Rectangle)"); + super(page, RectangleHitAreaSection.ID, "Hit Area (Rectangle)"); } createForm(parent: HTMLDivElement) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaOperation.ts new file mode 100644 index 000000000..252e2a442 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaOperation.ts @@ -0,0 +1,31 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + export class ResizeHitAreaOperation extends editor.tools.SceneToolOperation<{ x: number, y: number }> { + + constructor( + toolArgs: editor.tools.ISceneToolContextArgs, + private getInitialSize: (obj: ISceneGameObject) => { x: number, y: number }) { + + super(toolArgs); + } + + getInitialValue(obj: ISceneGameObject): { x: number; y: number; } { + + return this.getInitialSize(obj); + } + + getFinalValue(obj: ISceneGameObject): { x: number; y: number; } { + + return { + x: RectangleHitAreaComponent.width.getValue(obj), + y: RectangleHitAreaComponent.height.getValue(obj) + }; + } + + setValue(obj: ISceneGameObject, value: { x: number; y: number; }) { + + RectangleHitAreaComponent.width.setValue(obj, value.x); + RectangleHitAreaComponent.height.setValue(obj, value.y); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaTool.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaTool.ts new file mode 100644 index 000000000..8f386d0dc --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaTool.ts @@ -0,0 +1,226 @@ +/// +namespace phasereditor2d.scene.ui.sceneobjects { + + export class ResizeHitAreaTool extends BaseObjectTool { + + static ID = "phasereditor2d.scene.ui.sceneobjects.ResizeHitAreaTool"; + static TOOL_COLOR = "orange"; + + constructor() { + super({ + id: ResizeHitAreaTool.ID, + command: editor.commands.CMD_RESIZE_HIT_AREA, + }, + RectangleHitAreaComponent.x, + RectangleHitAreaComponent.y, + RectangleHitAreaComponent.width, + RectangleHitAreaComponent.height + ); + + this.addItems( + new ResizeHitAreaToolItem(1, 0.5), + new ResizeHitAreaToolItem(1, 1), + new ResizeHitAreaToolItem(0.5, 1), + // new ArcadeBodyOffsetToolItem(0, 0), + // new ArcadeBodyOffsetToolItem(0.5, 0), + // new ArcadeBodyOffsetToolItem(0, 0.5), + // new ArcadeBodyCircleOffsetToolItem(), + // new ArcadeBodyRadiusToolItem() + ); + } + + protected getProperties(obj?: any): IProperty[] { + + if (GameObjectEditorSupport.hasObjectComponent(obj, RectangleHitAreaComponent)) { + + return [ + RectangleHitAreaComponent.x, + RectangleHitAreaComponent.x, + RectangleHitAreaComponent.width, + RectangleHitAreaComponent.height + ]; + } + + return []; + } + + async onActivated(args: editor.tools.ISceneToolContextArgs) { + + super.onActivated(args); + + for (const obj of args.objects) { + + const objES = obj.getEditorSupport(); + + if (!objES.hasComponent(HitAreaComponent)) { + + return; + } + } + + const sections = [RectangleHitAreaSection.ID]; + + const props: Set> = new Set(); + + for (const obj of args.objects) { + + const objProps = this.getProperties(obj); + + for (const prop of objProps) { + + props.add(prop); + } + } + + await this.confirmUnlockProperty(args, [...props], "hit area", ...sections); + } + + render(args: editor.tools.ISceneToolRenderArgs): void { + + for (const obj of args.objects) { + + this.renderObj(args, obj as Sprite); + } + + super.render(args); + } + + private renderObj(args: editor.tools.ISceneToolRenderArgs, obj: Sprite) { + + const ctx = args.canvasContext; + + ctx.save(); + + const objES = obj.getEditorSupport(); + + const comp = objES.getComponent(HitAreaComponent) as HitAreaComponent; + + const shape = comp.getHitAreaShape(); + + if (shape === HitAreaShape.CIRCLE) { + + this.renderCircle(obj, args, ctx); + + } else { + + this.renderRect(obj, args, ctx); + } + + ctx.restore(); + } + + private renderRect(obj: Sprite, args: editor.tools.ISceneToolRenderArgs, ctx: CanvasRenderingContext2D) { + + const origin = obj.getEditorSupport().computeDisplayOrigin(); + + if (obj instanceof Container) { + + origin.displayOriginX = 0; + origin.displayOriginY = 0; + } + + const comp = RectangleHitAreaComponent.getRectangleComponent(obj); + + const { x, y, width, height } = comp; + + let x1 = x - origin.displayOriginX; + let y1 = y - origin.displayOriginY; + let x2 = x1 + width; + let y2 = y1 + height; + + const tx = obj.getWorldTransformMatrix(); + + const points = [ + [x1, y1], + [x2, y1], + [x2, y2], + [x1, y2], + [x1, y1] + ].map(([x, y]) => { + + return tx.transformPoint(x, y); + }).map(p => { + + return args.camera.getScreenPoint(p.x, p.y); + }); + + ctx.save(); + + ctx.strokeStyle = "black"; + ctx.lineWidth = 3; + this.drawPath(ctx, points); + + ctx.strokeStyle = ResizeHitAreaTool.TOOL_COLOR; + ctx.lineWidth = 1; + this.drawPath(ctx, points); + + ctx.restore(); + } + + private drawPath(ctx: CanvasRenderingContext2D, points: Phaser.Math.Vector2[]) { + + ctx.beginPath(); + + ctx.moveTo(points[0].x, points[0].y); + + for (const p of points) { + + ctx.lineTo(p.x, p.y); + } + + ctx.stroke(); + ctx.closePath(); + } + + private renderCircle(obj: Sprite, args: editor.tools.ISceneToolRenderArgs, ctx: CanvasRenderingContext2D) { + + // const body = ArcadeComponent.getBody(obj); + + // const p = new Phaser.Math.Vector2(); + + // const origin = obj.getEditorSupport().computeDisplayOrigin(); + + // if (obj instanceof Container) { + + // origin.displayOriginX = 0; + // origin.displayOriginY = 0; + // } + + // const bodyRadius = ArcadeComponent.radius.getValue(obj); + // let x1 = body.offset.x - origin.displayOriginX; + // let y1 = body.offset.y - origin.displayOriginY; + // let x2 = x1 + bodyRadius * 2; + // let y2 = y1 + bodyRadius * 2; + + // const tx = obj.getWorldTransformMatrix(); + // // removes rotation + // tx.rotate(-tx.rotation); + // tx.transformPoint(x1, y1, p); + // x1 = p.x; + // y1 = p.y; + + // tx.transformPoint(x2, y2, p); + // x2 = p.x; + // y2 = p.y; + + // const p1 = args.camera.getScreenPoint(x1, y1); + // const p2 = args.camera.getScreenPoint(x2, y2); + + // const r = (p2.x - p1.x) / 2; + // const x = p1.x + r; + // const y = p1.y + r; + + // ctx.strokeStyle = "black"; + // ctx.lineWidth = 3; + // ctx.beginPath(); + // ctx.ellipse(x, y, r, r, 0, 0, 360); + // ctx.stroke(); + + // ctx.strokeStyle = ArcadeBodyTool.BODY_TOOL_COLOR; + // ctx.lineWidth = 1; + // ctx.beginPath(); + // ctx.ellipse(x, y, r, r, 0, 0, 360); + // ctx.stroke(); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaToolItem.ts new file mode 100644 index 000000000..282640b07 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaToolItem.ts @@ -0,0 +1,60 @@ +/// + +namespace phasereditor2d.scene.ui.sceneobjects { + + export class ResizeHitAreaToolItem extends BaseHitAreaSizeToolItem { + + private getHitAreaComp(obj: ISceneGameObject) { + + const objEs = obj.getEditorSupport(); + + const comp = objEs.getComponent(RectangleHitAreaComponent) as RectangleHitAreaComponent; + + return comp; + } + + protected computeSize(obj: ISceneGameObject): { width: number; height: number; } { + + const { width, height } = this.getHitAreaComp(obj); + + return { width, height }; + } + + protected getHitAreaOffset(obj: ISceneGameObject): { x: number; y: number; } { + + const { x, y } = this.getHitAreaComp(obj); + + return { x, y }; + } + + protected getDataKey(): string { + + return "ResizeHitAreaToolItem"; + } + + protected getHitAreaSectionId(): string { + + return RectangleHitAreaSection.ID; + } + + protected onDragValues(obj: ISceneGameObject, changeX: boolean, changeY: boolean, width: number, height: number) { + + const comp = this.getHitAreaComp(obj); + + if (changeX) { + + comp.width = width; + } + + if (changeY) { + + comp.height = height; + } + } + + protected createStopDragOperation(args: editor.tools.ISceneToolDragEventArgs): colibri.ui.ide.undo.Operation { + + return new ResizeHitAreaOperation(args, obj => this.getInitialSize(obj)); + } + } +} \ No newline at end of file From e97fcf4f7f8376a8a9e6cf6b22d4a5632a0a88be Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Sat, 6 May 2023 13:39:56 -0400 Subject: [PATCH 35/59] Implements the offset of the resize hit area tool. --- .../hitArea/BaseHitAreaOffsetToolItem.ts | 222 ++++++++++++++++++ .../sceneobjects/hitArea/HitAreaComponent.ts | 5 + .../hitArea/RectangleHitAreaOffsetToolItem.ts | 34 +++ .../hitArea/RectangleOffsetOperation.ts | 31 +++ .../sceneobjects/hitArea/ResizeHitAreaTool.ts | 6 +- 5 files changed, 295 insertions(+), 3 deletions(-) create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaOffsetToolItem.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaOffsetToolItem.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleOffsetOperation.ts diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaOffsetToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaOffsetToolItem.ts new file mode 100644 index 000000000..a3e932149 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaOffsetToolItem.ts @@ -0,0 +1,222 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + export abstract class BaseHitAreaOffsetToolItem + extends editor.tools.SceneToolItem implements editor.tools.ISceneToolItemXY { + + private _x: IScaleAxis; + private _y: IScaleAxis; + private _dragging: boolean; + + constructor(x: IScaleAxis, y: IScaleAxis) { + super(); + + this._x = x; + this._y = y; + } + + getPoint(args: editor.tools.ISceneToolContextArgs): { x: number; y: number; } { + + return this.getAvgScreenPointOfObjects(args, + + (sprite: sceneobjects.Image) => this._x, + + (sprite: sceneobjects.Image) => this._y, + ); + } + + protected getScreenPointOfObject(args: ui.editor.tools.ISceneToolContextArgs, sprite: Sprite, fx: number, fy: number, removeRotation = false) { + + const worldPoint = new Phaser.Geom.Point(0, 0); + + let { displayOriginX, displayOriginY } = sprite.getEditorSupport().computeDisplayOrigin(); + + if (sprite instanceof Container) { + + displayOriginX = 0; + displayOriginY = 0; + } + + const offset = this.getOffsetProperties(sprite); + const size = this.getSizeProperties(sprite); + + let x = offset.x.getValue(sprite) as number; + let y = offset.y.getValue(sprite) as number; + let width = size.width.getValue(sprite) as number; + let height = size.height.getValue(sprite) as number; + + x = x - displayOriginX + fx * width; + y = y - displayOriginY + fy * height; + + const tx = sprite.getWorldTransformMatrix(); + + if (removeRotation) { + + tx.rotate(-tx.rotation); + } + + tx.transformPoint(x, y, worldPoint); + + return args.camera.getScreenPoint(worldPoint.x, worldPoint.y); + } + + protected computeSize(obj: ISceneGameObject) { + + const size = this.getSizeProperties(obj); + + return { + width: size.width.getValue(obj) as number, + height: size.height.getValue(obj) as number + } + } + + private computeOffset(obj: ISceneGameObject) { + + const offset = this.getOffsetProperties(obj); + + return { + x: offset.x.getValue(obj), + y: offset.y.getValue(obj) + } + }; + + protected abstract getOffsetProperties(obj: ISceneGameObject): { + x: IProperty, + y: IProperty + }; + + protected abstract getSizeProperties(obj: ISceneGameObject): { + width: IProperty, + height: IProperty + }; + + render(args: editor.tools.ISceneToolRenderArgs) { + + const point = this.getPoint(args); + + const ctx = args.canvasContext; + + ctx.save(); + + ctx.translate(point.x, point.y); + + const angle = this.globalAngle(args.objects[0] as any); + ctx.rotate(Phaser.Math.DegToRad(angle)); + + this.drawRect(ctx, args.canEdit ? + ResizeHitAreaTool.TOOL_COLOR : editor.tools.SceneTool.COLOR_CANNOT_EDIT); + + ctx.restore(); + } + + containsPoint(args: editor.tools.ISceneToolDragEventArgs): boolean { + + const point = this.getPoint(args); + + return Phaser.Math.Distance.Between(args.x, args.y, point.x, point.y) < 20; + } + + onStartDrag(args: editor.tools.ISceneToolDragEventArgs): void { + + if (!this.containsPoint(args)) { + + return; + } + + this._dragging = true; + + const point = this.getPoint(args); + + for (const obj of args.objects) { + + const sprite = obj as unknown as Image; + + const worldTx = new Phaser.GameObjects.Components.TransformMatrix(); + + const initLocalPos = new Phaser.Math.Vector2(); + + sprite.getWorldTransformMatrix(worldTx); + + worldTx.applyInverse(point.x, point.y, initLocalPos); + + const offset = this.computeOffset(sprite); + + sprite.setData(this.getKeyData(), { + initLocalPos: initLocalPos, + initLocalOffset: offset, + initWorldTx: worldTx + }); + } + } + + protected getInitialValue(obj: ISceneGameObject) { + + const { initLocalOffset } = obj.getData(this.getKeyData()); + + return initLocalOffset; + } + + protected abstract getKeyData(): string; + + onDrag(args: editor.tools.ISceneToolDragEventArgs): void { + + if (!this._dragging) { + return; + } + + const camera = args.camera; + + for (const obj of args.objects) { + + const sprite = obj as Sprite; + const data = sprite.data.get(this.getKeyData()); + const initLocalPos: Phaser.Math.Vector2 = data.initLocalPos; + const worldTx: Phaser.GameObjects.Components.TransformMatrix = data.initWorldTx; + + const localPos = new Phaser.Math.Vector2(); + + worldTx.applyInverse(args.x, args.y, localPos); + + const flipX = sprite.flipX ? -1 : 1; + const flipY = sprite.flipY ? -1 : 1; + + const dx = (localPos.x - initLocalPos.x) * flipX / camera.zoom; + const dy = (localPos.y - initLocalPos.y) * flipY / camera.zoom; + + const x = data.initLocalOffset.x + dx; + const y = data.initLocalOffset.y + dy; + + const changeAll = this._x === 0 && this._y === 0 || this._x === 0.5 && this._y === 0.5; + const changeX = this._x === 0 && this._y === 0.5 || changeAll; + const changeY = this._x === 0.5 && this._y === 0 || changeAll; + + const offset = this.getOffsetProperties(obj); + + if (changeX) { + + offset.x.setValue(sprite, Math.floor(x)); + } + + if (changeY) { + + offset.y.setValue(sprite, Math.floor(y)); + } + + args.editor.updateInspectorViewSection(this.getOffsetSectionId()); + } + } + + protected abstract getOffsetSectionId(): string; + + onStopDrag(args: editor.tools.ISceneToolDragEventArgs): void { + + if (this._dragging) { + + args.editor.getUndoManager().add(this.createStopDragOperation(args)); + + this._dragging = false; + } + } + + protected abstract createStopDragOperation(args: editor.tools.ISceneToolDragEventArgs): colibri.ui.ide.undo.Operation; + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts index 9c1e6b209..191fb73f4 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts @@ -36,6 +36,11 @@ namespace phasereditor2d.scene.ui.sceneobjects { this._hitAreaShape = HitAreaShape.RECTANGLE; } + static getShape(obj: ISceneGameObject): HitAreaShape { + + return this.hitAreaShape.getValue(obj) + } + static enableHitArea(obj: ISceneGameObject, enable: boolean) { const objES = obj.getEditorSupport(); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaOffsetToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaOffsetToolItem.ts new file mode 100644 index 000000000..b5f4c728b --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaOffsetToolItem.ts @@ -0,0 +1,34 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + export class RectangleHitAreaOffsetToolItem extends BaseHitAreaOffsetToolItem { + + protected getOffsetProperties(obj: ISceneGameObject): { x: IProperty; y: IProperty; } { + + const { x, y } = RectangleHitAreaComponent; + + return { x, y }; + } + + protected getSizeProperties(obj: ISceneGameObject): { width: IProperty; height: IProperty; } { + + const { width, height } = RectangleHitAreaComponent; + + return { width, height }; + } + + protected getKeyData(): string { + + return "RectangleHitAreaOffsetToolItem"; + } + + protected getOffsetSectionId(): string { + + return RectangleHitAreaSection.ID; + } + + protected createStopDragOperation(args: editor.tools.ISceneToolDragEventArgs): colibri.ui.ide.undo.Operation { + + return new RectangleOffsetOperation(args, obj => this.getInitialValue(obj)) + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleOffsetOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleOffsetOperation.ts new file mode 100644 index 000000000..bed82602e --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleOffsetOperation.ts @@ -0,0 +1,31 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + export class RectangleOffsetOperation extends editor.tools.SceneToolOperation<{ x: number, y: number }> { + + constructor( + toolArgs: editor.tools.ISceneToolContextArgs, + private getInitialOffset: (obj: ISceneGameObject) => { x: number, y: number }) { + + super(toolArgs); + } + + getInitialValue(obj: ISceneGameObject): { x: number; y: number; } { + + return this.getInitialOffset(obj); + } + + getFinalValue(obj: ISceneGameObject): { x: number; y: number; } { + + return { + x: RectangleHitAreaComponent.x.getValue(obj), + y: RectangleHitAreaComponent.y.getValue(obj) + }; + } + + setValue(obj: ISceneGameObject, value: { x: number; y: number; }) { + + RectangleHitAreaComponent.x.setValue(obj, value.x); + RectangleHitAreaComponent.y.setValue(obj, value.y); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaTool.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaTool.ts index 8f386d0dc..8ea63c336 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaTool.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaTool.ts @@ -21,9 +21,9 @@ namespace phasereditor2d.scene.ui.sceneobjects { new ResizeHitAreaToolItem(1, 0.5), new ResizeHitAreaToolItem(1, 1), new ResizeHitAreaToolItem(0.5, 1), - // new ArcadeBodyOffsetToolItem(0, 0), - // new ArcadeBodyOffsetToolItem(0.5, 0), - // new ArcadeBodyOffsetToolItem(0, 0.5), + new RectangleHitAreaOffsetToolItem(0, 0), + new RectangleHitAreaOffsetToolItem(0.5, 0), + new RectangleHitAreaOffsetToolItem(0, 0.5), // new ArcadeBodyCircleOffsetToolItem(), // new ArcadeBodyRadiusToolItem() ); From 79ec8aa3adcf98ee836af9211f8563fbd144a141 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Wed, 10 May 2023 10:01:16 -0400 Subject: [PATCH 36/59] Fixes hit area rendering bug. --- .../hitArea/BaseHitAreaOffsetToolItem.ts | 5 ++++ .../hitArea/BaseHitAreaSizeToolItem.ts | 5 ++++ .../sceneobjects/hitArea/ResizeHitAreaTool.ts | 24 ++++++++++++++++--- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaOffsetToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaOffsetToolItem.ts index a3e932149..189133ccf 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaOffsetToolItem.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaOffsetToolItem.ts @@ -14,6 +14,11 @@ namespace phasereditor2d.scene.ui.sceneobjects { this._y = y; } + isValidFor(objects: ISceneGameObject[]): boolean { + + return ResizeHitAreaTool.isValidFor(...objects); + } + getPoint(args: editor.tools.ISceneToolContextArgs): { x: number; y: number; } { return this.getAvgScreenPointOfObjects(args, diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaSizeToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaSizeToolItem.ts index 79105a47a..bd3b46350 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaSizeToolItem.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaSizeToolItem.ts @@ -26,6 +26,11 @@ namespace phasereditor2d.scene.ui.sceneobjects { protected abstract createStopDragOperation(args: editor.tools.ISceneToolDragEventArgs): colibri.ui.ide.undo.Operation; + isValidFor(objects: ISceneGameObject[]): boolean { + + return ResizeHitAreaTool.isValidFor(...objects); + } + getPoint(args: editor.tools.ISceneToolContextArgs): { x: number; y: number; } { return this.getAvgScreenPointOfObjects(args, diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaTool.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaTool.ts index 8ea63c336..625838263 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaTool.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaTool.ts @@ -79,12 +79,30 @@ namespace phasereditor2d.scene.ui.sceneobjects { for (const obj of args.objects) { - this.renderObj(args, obj as Sprite); + if (ResizeHitAreaTool.isValidFor(obj)) { + + this.renderObj(args, obj as Sprite); + } } super.render(args); } + static isValidFor(...objects: ISceneGameObject[]): boolean { + + for (const obj of objects) { + + const objES = obj.getEditorSupport(); + + if (!objES.hasComponent(HitAreaComponent)) { + + return false + } + } + + return true; + } + private renderObj(args: editor.tools.ISceneToolRenderArgs, obj: Sprite) { const ctx = args.canvasContext; @@ -129,7 +147,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { let y2 = y1 + height; const tx = obj.getWorldTransformMatrix(); - + const points = [ [x1, y1], [x2, y1], @@ -145,7 +163,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { }); ctx.save(); - + ctx.strokeStyle = "black"; ctx.lineWidth = 3; this.drawPath(ctx, points); From f7172b498d5d31cfee4b6b1dbac3b8897dc5b8ec Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Wed, 10 May 2023 10:51:08 -0400 Subject: [PATCH 37/59] Improves when enabling the hit area in a container. --- .../hitArea/RectangleHitAreaComponent.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts index 75b210f7b..3b7013c87 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts @@ -36,7 +36,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { const objES = obj.getEditorSupport(); const comp = objES.getComponent(RectangleHitAreaComponent) as RectangleHitAreaComponent; - + return comp; } @@ -49,7 +49,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { unlockEvent.addListener(args => { if (args.property === HitAreaComponent.hitAreaShape) { - + objES.setUnlockedProperty(RectangleHitAreaComponent.x, args.unlock); objES.setUnlockedProperty(RectangleHitAreaComponent.y, args.unlock); objES.setUnlockedProperty(RectangleHitAreaComponent.width, args.unlock); @@ -67,8 +67,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { setDefaultValues() { - console.log("here"); - const obj = this.getObject() as Image; const objES = this.getEditorSupport(); @@ -84,6 +82,15 @@ namespace phasereditor2d.scene.ui.sceneobjects { width = widthProp.getValue(obj); height = heightProp.getValue(obj); + } else if (obj instanceof Container) { + + const c = obj as Container; + + const b = c.getBounds(); + + width = b.width; + height = b.height; + } else if (obj.width && obj.height) { width = obj.width; From d53d1e5808efa1d82a6d80bf23b700fafa1758c4 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Wed, 10 May 2023 16:10:34 -0400 Subject: [PATCH 38/59] Improves hit area serialization. --- .../src/ui/editor/SceneEditorMenuCreator.ts | 11 --- .../ui/editor/commands/SceneEditorCommands.ts | 95 ------------------- .../hitArea/EnableHitAreaOperation.ts | 21 ---- .../sceneobjects/hitArea/HitAreaComponent.ts | 46 +++++---- .../sceneobjects/hitArea/HitAreaProperty.ts | 1 - .../ui/sceneobjects/hitArea/HitAreaSection.ts | 21 ++-- .../hitArea/RectangleHitAreaComponent.ts | 12 +-- .../hitArea/RectangleHitAreaSection.ts | 16 +++- .../sceneobjects/hitArea/ResizeHitAreaTool.ts | 11 +-- 9 files changed, 64 insertions(+), 170 deletions(-) delete mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EnableHitAreaOperation.ts diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditorMenuCreator.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditorMenuCreator.ts index 6b88293fe..4c4b68a56 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditorMenuCreator.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditorMenuCreator.ts @@ -35,8 +35,6 @@ namespace phasereditor2d.scene.ui.editor { menu.addMenu(this.createTextureMenu()); - menu.addMenu(this.createHitAreaMenu()); - menu.addMenu(this.createArcadePhysicsMenu()); menu.addMenu(this.createParentMenu()); @@ -58,15 +56,6 @@ namespace phasereditor2d.scene.ui.editor { menu.addMenu(this.createCompilerMenu()); } - createHitAreaMenu(): controls.Menu { - - const menu = new controls.Menu("Hit Area"); - - menu.addCommand(ui.editor.commands.CMD_HIT_AREA_ENABLE); - - return menu; - } - createScriptsMenu(): controls.Menu { const menu = new controls.Menu("Scripts"); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts index 332a7aa40..5f6680721 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts @@ -59,8 +59,6 @@ namespace phasereditor2d.scene.ui.editor.commands { export const CMD_OPEN_SCRIPT_DIALOG = "phasereditor2d.scene.ui.editor.commands.OpenScriptDialog"; export const CMD_OPEN_ADD_SCRIPT_DIALOG = "phasereditor2d.scene.ui.editor.commands.OpenAddScriptDialog"; export const CMD_PREVIEW_SCENE = "phasereditor2d.scene.ui.editor.commands.PreviewScene"; - export const CMD_HIT_AREA_ENABLE = "phasereditor2d.scene.ui.editor.commands.EnableHitArea"; - export const CMD_HIT_AREA_DISABLE = "phasereditor2d.scene.ui.editor.commands.DisableHitArea"; export const CMD_RESIZE_HIT_AREA = "phasereditor2d.scene.ui.editor.commands.ResizeHitArea"; function isSceneScope(args: colibri.ui.ide.commands.HandlerArgs) { @@ -155,99 +153,6 @@ namespace phasereditor2d.scene.ui.editor.commands { this.registerArcadeCommands(manager); this.registerScriptNodeCommands(manager); - - this.registerHitAreaCommands(manager); - } - - private static registerHitAreaCommands(manager: colibri.ui.ide.commands.CommandManager) { - - manager.add({ - command: { - id: CMD_HIT_AREA_ENABLE, - name: "Enable Hit Area", - category: CAT_SCENE_EDITOR, - tooltip: "Enable the input system of the selected objects", - }, - handler: { - testFunc: args => { - - if (!isSceneScope(args)) { - - return false; - } - - for (const obj of args.activeEditor.getSelection()) { - - const objES = sceneobjects.GameObjectEditorSupport.getEditorSupport(obj); - - if (objES && objES.isDisplayObject() && !objES.isPrefabInstance()) { - - if (objES.hasComponent(sceneobjects.HitAreaComponent)) { - - return false; - } - - } else { - - return false; - } - } - - return true; - }, - executeFunc: args => { - - const editor = args.activeEditor as SceneEditor; - - editor.getUndoManager().add( - new ui.sceneobjects.EnableHitAreaOperation(editor, true)); - } - } - }); - - manager.add({ - command: { - id: CMD_HIT_AREA_DISABLE, - name: "Disable Hit Area", - category: CAT_SCENE_EDITOR, - tooltip: "Enable the input system of the selected objects", - }, - handler: { - testFunc: args => { - - if (!isSceneScope(args)) { - - return false; - } - - for (const obj of args.activeEditor.getSelection()) { - - const objES = sceneobjects.GameObjectEditorSupport.getEditorSupport(obj); - - if (objES && objES.isDisplayObject()) { - - if (!objES.hasComponent(sceneobjects.HitAreaComponent)) { - - return false; - } - - } else { - - return false; - } - } - - return true; - }, - executeFunc: args => { - - const editor = args.activeEditor as SceneEditor; - - editor.getUndoManager().add( - new ui.sceneobjects.EnableHitAreaOperation(editor, false)); - } - } - }); } private static registerScriptNodeCommands(manager: colibri.ui.ide.commands.CommandManager) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EnableHitAreaOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EnableHitAreaOperation.ts deleted file mode 100644 index 0b3504fc1..000000000 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EnableHitAreaOperation.ts +++ /dev/null @@ -1,21 +0,0 @@ -/// - -namespace phasereditor2d.scene.ui.sceneobjects { - - export class EnableHitAreaOperation extends SceneGameObjectOperation { - - constructor(editor: ui.editor.SceneEditor, enable: boolean) { - super(editor, editor.getSelectedGameObjects(), enable); - } - - getValue(obj: ISceneGameObject) { - - return obj.getEditorSupport().hasComponent(HitAreaComponent); - } - - setValue(obj: ISceneGameObject, enable: boolean): void { - - HitAreaComponent.enableHitArea(obj, enable); - } - } -} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts index 191fb73f4..cd129c297 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts @@ -1,6 +1,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { export enum HitAreaShape { + NONE = "NONE", CIRCLE = "CIRCLE", RECTANGLE = "RECTANGLE" } @@ -13,14 +14,14 @@ namespace phasereditor2d.scene.ui.sceneobjects { export class HitAreaComponent extends Component { static hitAreaShape: IEnumProperty = { - local: true, name: "hitArea.shape", label: "Shape", - defValue: HitAreaShape.RECTANGLE, + defValue: HitAreaShape.NONE, getValue: obj => getComp(obj).getHitAreaShape(), setValue: (obj, value) => getComp(obj).setHitAreaShape(value), getValueLabel: value => value.toString(), values: [ + HitAreaShape.NONE, HitAreaShape.RECTANGLE, HitAreaShape.CIRCLE ] @@ -31,33 +32,33 @@ namespace phasereditor2d.scene.ui.sceneobjects { constructor(obj: ISceneGameObject) { super(obj, [ HitAreaComponent.hitAreaShape - ], false); + ]); - this._hitAreaShape = HitAreaShape.RECTANGLE; + this._hitAreaShape = HitAreaShape.NONE; } - static getShape(obj: ISceneGameObject): HitAreaShape { + static hasHitAreaShape(obj: ISceneGameObject, shape: HitAreaShape) { - return this.hitAreaShape.getValue(obj) + if (this.hasHitArea(obj)) { + + return this.getShape(obj) === shape; + } + + return false; } - static enableHitArea(obj: ISceneGameObject, enable: boolean) { + static hasHitArea(obj: ISceneGameObject) { - const objES = obj.getEditorSupport(); - - objES.setComponentActive(HitAreaComponent, enable); + return GameObjectEditorSupport.hasObjectComponent(obj, HitAreaComponent); + } - if (enable) { + static getShape(obj: ISceneGameObject): HitAreaShape { - const comp = objES.getComponent(HitAreaComponent) as HitAreaComponent; - comp.setHitAreaShape(HitAreaShape.RECTANGLE); - const rectComp = objES.getComponent(RectangleHitAreaComponent) as RectangleHitAreaComponent; - rectComp.setDefaultValues(); - } + return this.hitAreaShape.getValue(obj) } getHitAreaShape() { - + return this._hitAreaShape; } @@ -67,6 +68,17 @@ namespace phasereditor2d.scene.ui.sceneobjects { } buildSetObjectPropertiesCodeDOM(args: ISetObjectPropertiesCodeDOMArgs): void { + + const objES = this.getEditorSupport(); + + if (objES.isPrefabInstance()) { + + if (objES.isUnlockedProperty(HitAreaComponent.hitAreaShape)) { + + const code = new core.code.MethodCallCodeDOM("removeInteractive", args.objectVarName); + args.statements.push(code); + } + } } } } \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaProperty.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaProperty.ts index eaa03674a..9275c6fdd 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaProperty.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaProperty.ts @@ -9,7 +9,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { ): IProperty { return { - local: true, name: `hitArea.${name}`, label, tooltip, diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaSection.ts index 0934c2a35..1258922b4 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaSection.ts @@ -8,18 +8,27 @@ namespace phasereditor2d.scene.ui.sceneobjects { super(page, "phasereditor2d.scene.ui.sceneobjects.HitAreaSection", "Hit Area"); } - createMenu(menu: controls.Menu): void { - - menu.addCommand(ui.editor.commands.CMD_HIT_AREA_DISABLE); - } - createForm(parent: HTMLDivElement) { const comp = this.createGridElement(parent, 3); const { hitAreaShape } = HitAreaComponent; - this.createPropertyEnumRow(comp, hitAreaShape); + const prop = {...hitAreaShape}; + + prop.setValue = (obj, value) => { + + hitAreaShape.setValue(obj, value); + + if (value === HitAreaShape.RECTANGLE) { + + const comp = RectangleHitAreaComponent.getRectangleComponent(obj); + + comp.setDefaultValues(); + } + } + + this.createPropertyEnumRow(comp, prop); } canEdit(obj: any, n: number): boolean { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts index 3b7013c87..22e41f491 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts @@ -48,7 +48,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { unlockEvent.addListener(args => { - if (args.property === HitAreaComponent.hitAreaShape) { + if (args.property.name === HitAreaComponent.hitAreaShape.name) { objES.setUnlockedProperty(RectangleHitAreaComponent.x, args.unlock); objES.setUnlockedProperty(RectangleHitAreaComponent.y, args.unlock); @@ -105,21 +105,13 @@ namespace phasereditor2d.scene.ui.sceneobjects { const obj = this.getObject(); const objES = obj.getEditorSupport(); - const hitAreaComp = objES.getComponent(HitAreaComponent); - if (hitAreaComp) { + if (objES.getComponent(HitAreaComponent)) { if (objES.isUnlockedProperty(HitAreaComponent.hitAreaShape)) { if (HitAreaComponent.hitAreaShape.getValue(obj) === HitAreaShape.RECTANGLE) { - if (objES.isPrefabInstance()) { - - // we should disable the input, then enable it again with a new shape - const code = new core.code.MethodCallCodeDOM("removeInteractive", args.objectVarName); - args.statements.push(code); - } - const code = new core.code.MethodCallCodeDOM("setInteractive", args.objectVarName); const { x, y, width, height } = RectangleHitAreaComponent; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSection.ts index f8e5d98ec..cfe68aa46 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSection.ts @@ -10,18 +10,30 @@ namespace phasereditor2d.scene.ui.sceneobjects { super(page, RectangleHitAreaSection.ID, "Hit Area (Rectangle)"); } + createMenu(menu: controls.Menu) { + + this.createToolMenuItem(menu, ResizeHitAreaTool.ID); + + super.createMenu(menu); + } + createForm(parent: HTMLDivElement) { const comp = this.createGridElementWithPropertiesXY(parent); this.createPropertyXYRow(comp, RectangleHitAreaComponent.position, false); this.createPropertyXYRow(comp, RectangleHitAreaComponent.size, false); + + this.createButton(comp, "Resize Tool", () => { + + this.getEditor().getToolsManager().activateTool(ResizeHitAreaTool.ID); + + }).style.gridColumn = " span 6"; } canEdit(obj: any, n: number): boolean { - return GameObjectEditorSupport.hasObjectComponent(obj, HitAreaComponent) - && GameObjectEditorSupport.hasObjectComponent(obj, RectangleHitAreaComponent); + return HitAreaComponent.hasHitAreaShape(obj, HitAreaShape.RECTANGLE); } canEditNumber(n: number): boolean { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaTool.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaTool.ts index 625838263..96f32a001 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaTool.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaTool.ts @@ -31,7 +31,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { protected getProperties(obj?: any): IProperty[] { - if (GameObjectEditorSupport.hasObjectComponent(obj, RectangleHitAreaComponent)) { + if (HitAreaComponent.hasHitArea(obj)) { return [ RectangleHitAreaComponent.x, @@ -50,9 +50,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { for (const obj of args.objects) { - const objES = obj.getEditorSupport(); - - if (!objES.hasComponent(HitAreaComponent)) { + if (!HitAreaComponent.hasHitArea(obj)) { return; } @@ -92,9 +90,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { for (const obj of objects) { - const objES = obj.getEditorSupport(); - - if (!objES.hasComponent(HitAreaComponent)) { + if (!HitAreaComponent.hasHitArea(obj) + || HitAreaComponent.getShape(obj) === HitAreaShape.NONE) { return false } From 2916132de55e00055789183eb5f8d9859821107d Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Wed, 10 May 2023 16:34:16 -0400 Subject: [PATCH 39/59] Removes Resize Tool button. --- .../src/ui/sceneobjects/hitArea/RectangleHitAreaSection.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSection.ts index cfe68aa46..3084ec14c 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSection.ts @@ -23,12 +23,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { this.createPropertyXYRow(comp, RectangleHitAreaComponent.position, false); this.createPropertyXYRow(comp, RectangleHitAreaComponent.size, false); - - this.createButton(comp, "Resize Tool", () => { - - this.getEditor().getToolsManager().activateTool(ResizeHitAreaTool.ID); - - }).style.gridColumn = " span 6"; } canEdit(obj: any, n: number): boolean { From 3166b6bb2df18afc16a17d37fb34f8f2fde050c8 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 11 May 2023 09:29:29 -0400 Subject: [PATCH 40/59] Refactoring the hit area component. --- scripts/make-all-help-files.js | 9 +- .../data/phaser-docs.json | 6 +- .../hitArea/BaseHitAreaComponent.ts | 95 +++++++++++++++++++ .../hitArea/EllipseHitAreaComponent.ts | 0 .../sceneobjects/hitArea/HitAreaComponent.ts | 4 +- .../hitArea/RectangleHitAreaComponent.ts | 91 ++---------------- .../sceneobjects/hitArea/ResizeHitAreaTool.ts | 6 +- 7 files changed, 120 insertions(+), 91 deletions(-) create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaComponent.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaComponent.ts diff --git a/scripts/make-all-help-files.js b/scripts/make-all-help-files.js index f043421a1..13b3ad8b0 100755 --- a/scripts/make-all-help-files.js +++ b/scripts/make-all-help-files.js @@ -209,6 +209,8 @@ utils.makeHelpFile([ "Phaser.GameObjects.Ellipse", "Phaser.GameObjects.Ellipse.smoothness", + "Phaser.GameObjects.Polygon", + "Phaser.GameObjects.Triangle", "Phaser.Geom.Triangle.x1", "Phaser.Geom.Triangle.y1", @@ -217,12 +219,15 @@ utils.makeHelpFile([ "Phaser.Geom.Triangle.x3", "Phaser.Geom.Triangle.y3", - "Phaser.GameObjects.Polygon", - "Phaser.Geom.Rectangle.x", "Phaser.Geom.Rectangle.y", "Phaser.Geom.Rectangle.width", "Phaser.Geom.Rectangle.height", + + "Phaser.Geom.Ellipse.x", + "Phaser.Geom.Ellipse.y", + "Phaser.Geom.Ellipse.width", + "Phaser.Geom.Ellipse.height", "Phaser.GameObjects.Layer", diff --git a/source/editor/plugins/phasereditor2d.scene/data/phaser-docs.json b/source/editor/plugins/phasereditor2d.scene/data/phaser-docs.json index 04b82b870..1a7ab6859 100644 --- a/source/editor/plugins/phasereditor2d.scene/data/phaser-docs.json +++ b/source/editor/plugins/phasereditor2d.scene/data/phaser-docs.json @@ -111,6 +111,7 @@ "Phaser.GameObjects.Rectangle": "The Rectangle Shape is a Game Object that can be added to a Scene, Group or Container. You can\ntreat it like any other Game Object in your game, such as tweening it, scaling it, or enabling\nit for input or physics. It provides a quick and easy way for you to render this shape in your\ngame without using a texture, while still taking advantage of being fully batched in WebGL.\n\nThis shape supports both fill and stroke colors.\n\nYou can change the size of the rectangle by changing the `width` and `height` properties.", "Phaser.GameObjects.Ellipse": "The Ellipse Shape is a Game Object that can be added to a Scene, Group or Container. You can\ntreat it like any other Game Object in your game, such as tweening it, scaling it, or enabling\nit for input or physics. It provides a quick and easy way for you to render this shape in your\ngame without using a texture, while still taking advantage of being fully batched in WebGL.\n\nThis shape supports both fill and stroke colors.\n\nWhen it renders it displays an ellipse shape. You can control the width and height of the ellipse.\nIf the width and height match it will render as a circle. If the width is less than the height,\nit will look more like an egg shape.\n\nThe Ellipse shape also has a `smoothness` property and corresponding `setSmoothness` method.\nThis allows you to control how smooth the shape renders in WebGL, by controlling the number of iterations\nthat take place during construction. Increase and decrease the default value for smoother, or more\njagged, shapes.", "Phaser.GameObjects.Ellipse.smoothness": "The smoothness of the ellipse. The number of points used when rendering it.\nIncrease this value for a smoother ellipse, at the cost of more polygons being rendered.", + "Phaser.GameObjects.Polygon": "The Polygon Shape is a Game Object that can be added to a Scene, Group or Container. You can\ntreat it like any other Game Object in your game, such as tweening it, scaling it, or enabling\nit for input or physics. It provides a quick and easy way for you to render this shape in your\ngame without using a texture, while still taking advantage of being fully batched in WebGL.\n\nThis shape supports both fill and stroke colors.\n\nThe Polygon Shape is created by providing a list of points, which are then used to create an\ninternal Polygon geometry object. The points can be set from a variety of formats:\n\n- A string containing paired values separated by a single space: `'40 0 40 20 100 20 100 80 40 80 40 100 0 50'`\n- An array of Point or Vector2 objects: `[new Phaser.Math.Vector2(x1, y1), ...]`\n- An array of objects with public x/y properties: `[obj1, obj2, ...]`\n- An array of paired numbers that represent point coordinates: `[x1,y1, x2,y2, ...]`\n- An array of arrays with two elements representing x/y coordinates: `[[x1, y1], [x2, y2], ...]`\n\nBy default the `x` and `y` coordinates of this Shape refer to the center of it. However, depending\non the coordinates of the points provided, the final shape may be rendered offset from its origin.", "Phaser.GameObjects.Triangle": "The Triangle Shape is a Game Object that can be added to a Scene, Group or Container. You can\ntreat it like any other Game Object in your game, such as tweening it, scaling it, or enabling\nit for input or physics. It provides a quick and easy way for you to render this shape in your\ngame without using a texture, while still taking advantage of being fully batched in WebGL.\n\nThis shape supports both fill and stroke colors.\n\nThe Triangle consists of 3 lines, joining up to form a triangular shape. You can control the\nposition of each point of these lines. The triangle is always closed and cannot have an open\nface. If you require that, consider using a Polygon instead.", "Phaser.Geom.Triangle.x1": "`x` coordinate of the first point.", "Phaser.Geom.Triangle.y1": "`y` coordinate of the first point.", @@ -118,11 +119,14 @@ "Phaser.Geom.Triangle.y2": "`y` coordinate of the second point.", "Phaser.Geom.Triangle.x3": "`x` coordinate of the third point.", "Phaser.Geom.Triangle.y3": "`y` coordinate of the third point.", - "Phaser.GameObjects.Polygon": "The Polygon Shape is a Game Object that can be added to a Scene, Group or Container. You can\ntreat it like any other Game Object in your game, such as tweening it, scaling it, or enabling\nit for input or physics. It provides a quick and easy way for you to render this shape in your\ngame without using a texture, while still taking advantage of being fully batched in WebGL.\n\nThis shape supports both fill and stroke colors.\n\nThe Polygon Shape is created by providing a list of points, which are then used to create an\ninternal Polygon geometry object. The points can be set from a variety of formats:\n\n- A string containing paired values separated by a single space: `'40 0 40 20 100 20 100 80 40 80 40 100 0 50'`\n- An array of Point or Vector2 objects: `[new Phaser.Math.Vector2(x1, y1), ...]`\n- An array of objects with public x/y properties: `[obj1, obj2, ...]`\n- An array of paired numbers that represent point coordinates: `[x1,y1, x2,y2, ...]`\n- An array of arrays with two elements representing x/y coordinates: `[[x1, y1], [x2, y2], ...]`\n\nBy default the `x` and `y` coordinates of this Shape refer to the center of it. However, depending\non the coordinates of the points provided, the final shape may be rendered offset from its origin.", "Phaser.Geom.Rectangle.x": "The X coordinate of the top left corner of the Rectangle.", "Phaser.Geom.Rectangle.y": "The Y coordinate of the top left corner of the Rectangle.", "Phaser.Geom.Rectangle.width": "The width of the Rectangle, i.e. the distance between its left side (defined by `x`) and its right side.", "Phaser.Geom.Rectangle.height": "The height of the Rectangle, i.e. the distance between its top side (defined by `y`) and its bottom side.", + "Phaser.Geom.Ellipse.x": "The x position of the center of the ellipse.", + "Phaser.Geom.Ellipse.y": "The y position of the center of the ellipse.", + "Phaser.Geom.Ellipse.width": "The width of the ellipse.", + "Phaser.Geom.Ellipse.height": "The height of the ellipse.", "Phaser.GameObjects.Layer": "A Layer Game Object.\n\nA Layer is a special type of Game Object that acts as a Display List. You can add any type of Game Object\nto a Layer, just as you would to a Scene. Layers can be used to visually group together 'layers' of Game\nObjects:\n\n```javascript\nconst spaceman = this.add.sprite(150, 300, 'spaceman');\nconst bunny = this.add.sprite(400, 300, 'bunny');\nconst elephant = this.add.sprite(650, 300, 'elephant');\n\nconst layer = this.add.layer();\n\nlayer.add([ spaceman, bunny, elephant ]);\n```\n\nThe 3 sprites in the example above will now be managed by the Layer they were added to. Therefore,\nif you then set `layer.setVisible(false)` they would all vanish from the display.\n\nYou can also control the depth of the Game Objects within the Layer. For example, calling the\n`setDepth` method of a child of a Layer will allow you to adjust the depth of that child _within the\nLayer itself_, rather than the whole Scene. The Layer, too, can have its depth set as well.\n\nThe Layer class also offers many different methods for manipulating the list, such as the\nmethods `moveUp`, `moveDown`, `sendToBack`, `bringToTop` and so on. These allow you to change the\ndisplay list position of the Layers children, causing it to adjust the order in which they are\nrendered. Using `setDepth` on a child allows you to override this.\n\nLayers can have Post FX Pipelines set, which allows you to easily enable a post pipeline across\na whole range of children, which, depending on the effect, can often be far more efficient that doing so\non a per-child basis.\n\nLayers have no position or size within the Scene. This means you cannot enable a Layer for\nphysics or input, or change the position, rotation or scale of a Layer. They also have no scroll\nfactor, texture, tint, origin, crop or bounds.\n\nIf you need those kind of features then you should use a Container instead. Containers can be added\nto Layers, but Layers cannot be added to Containers.\n\nHowever, you can set the Alpha, Blend Mode, Depth, Mask and Visible state of a Layer. These settings\nwill impact all children being rendered by the Layer.", "Phaser.Physics.Arcade.Collider": "An Arcade Physics Collider will automatically check for collision, or overlaps, between two objects\nevery step. If a collision, or overlap, occurs it will invoke the given callbacks.", "Phaser.Physics.Arcade.Image": "An Arcade Physics Image is an Image with an Arcade Physics body and related components.\nThe body can be dynamic or static.\n\nThe main difference between an Arcade Image and an Arcade Sprite is that you cannot animate an Arcade Image.", diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaComponent.ts new file mode 100644 index 000000000..8e175f9f9 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaComponent.ts @@ -0,0 +1,95 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + export abstract class BaseHitAreaComponent extends Component { + + private _shape: HitAreaShape; + + constructor(obj: ISceneGameObject, shape: HitAreaShape, properties: IProperty[]) { + super(obj, properties); + + this._shape = shape; + } + + private initUnlockListener() { + + const objES = this.getEditorSupport(); + + const unlockEvent = objES.unlockEvent; + + unlockEvent.addListener(args => { + + if (args.property.name === HitAreaComponent.hitAreaShape.name) { + + for (const prop of this.getProperties()) { + + objES.setUnlockedProperty(prop, args.unlock); + } + } + }); + } + + readJSON(ser: core.json.Serializer): void { + + this.initUnlockListener(); + + super.readJSON(ser); + } + + protected abstract _setDefaultValues(width: number, height: number): void; + + setDefaultValues() { + + const obj = this.getObject() as Image; + const objES = this.getEditorSupport(); + + let width = 0, height = 0; + + let [widthProp, heightProp] = objES.getSizeProperties(); + + if (widthProp && heightProp) { + + width = widthProp.getValue(obj); + height = heightProp.getValue(obj); + + } else if (obj instanceof Container) { + + const c = obj as Container; + + const b = c.getBounds(); + + width = b.width; + height = b.height; + + } else if (obj.width && obj.height) { + + width = obj.width; + height = obj.height; + } + + this._setDefaultValues(width, height); + } + + buildSetObjectPropertiesCodeDOM(args: ISetObjectPropertiesCodeDOMArgs): void { + + const obj = this.getObject(); + const objES = obj.getEditorSupport(); + + if (objES.getComponent(HitAreaComponent)) { + + if (objES.isUnlockedProperty(HitAreaComponent.hitAreaShape)) { + + if (HitAreaComponent.hitAreaShape.getValue(obj) === this._shape) { + + const code = new core.code.MethodCallCodeDOM("setInteractive", args.objectVarName); + + this.buildSetInteractiveCodeCOM(obj, code); + + args.statements.push(code); + } + } + } + } + + protected abstract buildSetInteractiveCodeCOM(obj: ISceneGameObject, code: core.code.MethodCallCodeDOM): void; + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaComponent.ts new file mode 100644 index 000000000..e69de29bb diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts index cd129c297..db2b6a7e3 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts @@ -2,7 +2,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { export enum HitAreaShape { NONE = "NONE", - CIRCLE = "CIRCLE", + ELLIPSE = "ELLIPSE", RECTANGLE = "RECTANGLE" } @@ -23,7 +23,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { values: [ HitAreaShape.NONE, HitAreaShape.RECTANGLE, - HitAreaShape.CIRCLE + HitAreaShape.ELLIPSE ] }; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts index 22e41f491..cc2b7d60c 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts @@ -1,6 +1,7 @@ +/// namespace phasereditor2d.scene.ui.sceneobjects { - export class RectangleHitAreaComponent extends Component { + export class RectangleHitAreaComponent extends BaseHitAreaComponent { static x = HitAreaProperty(RectangleHitAreaComponent, "x", "X", "phaser:Phaser.Geom.Rectangle.x", 0); static y = HitAreaProperty(RectangleHitAreaComponent, "y", "Y", "phaser:Phaser.Geom.Rectangle.y", 0); @@ -23,7 +24,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { public height = 0; constructor(obj: ISceneGameObject) { - super(obj, [ + super(obj, HitAreaShape.RECTANGLE, [ RectangleHitAreaComponent.x, RectangleHitAreaComponent.y, RectangleHitAreaComponent.width, @@ -40,97 +41,21 @@ namespace phasereditor2d.scene.ui.sceneobjects { return comp; } - private initUnlockListener() { - - const objES = this.getEditorSupport(); - - const unlockEvent = objES.unlockEvent; - - unlockEvent.addListener(args => { - - if (args.property.name === HitAreaComponent.hitAreaShape.name) { - - objES.setUnlockedProperty(RectangleHitAreaComponent.x, args.unlock); - objES.setUnlockedProperty(RectangleHitAreaComponent.y, args.unlock); - objES.setUnlockedProperty(RectangleHitAreaComponent.width, args.unlock); - objES.setUnlockedProperty(RectangleHitAreaComponent.height, args.unlock); - } - }); - } - - readJSON(ser: core.json.Serializer): void { - - this.initUnlockListener(); - - super.readJSON(ser); - } - - setDefaultValues() { - - const obj = this.getObject() as Image; - const objES = this.getEditorSupport(); + protected _setDefaultValues(width: number, height: number): void { this.x = 0; this.y = 0; - - let width = 0, height = 0; - - let [widthProp, heightProp] = objES.getSizeProperties(); - - if (widthProp && heightProp) { - - width = widthProp.getValue(obj); - height = heightProp.getValue(obj); - - } else if (obj instanceof Container) { - - const c = obj as Container; - - const b = c.getBounds(); - - width = b.width; - height = b.height; - - } else if (obj.width && obj.height) { - - width = obj.width; - height = obj.height; - } - this.width = width; this.height = height; } - buildSetObjectPropertiesCodeDOM(args: ISetObjectPropertiesCodeDOMArgs): void { - - const obj = this.getObject(); - const objES = obj.getEditorSupport(); - - if (objES.getComponent(HitAreaComponent)) { - - if (objES.isUnlockedProperty(HitAreaComponent.hitAreaShape)) { - - if (HitAreaComponent.hitAreaShape.getValue(obj) === HitAreaShape.RECTANGLE) { - - const code = new core.code.MethodCallCodeDOM("setInteractive", args.objectVarName); - - const { x, y, width, height } = RectangleHitAreaComponent; - - const geomArgs = [ - x.getValue(obj) ?? x.defValue, - y.getValue(obj) ?? y.defValue, - width.getValue(obj) ?? width.defValue, - height.getValue(obj) ?? height.defValue - ]; + protected override buildSetInteractiveCodeCOM(obj: ISceneGameObject, code: core.code.MethodCallCodeDOM): void { - code.arg(`new Phaser.Geom.Rectangle(${geomArgs.join(", ")})`); + const { x, y, width, height } = this; - code.arg("Phaser.Geom.Rectangle.Contains"); + code.arg(`new Phaser.Geom.Rectangle(${x}, ${y}, ${width}, ${height})`); - args.statements.push(code); - } - } - } + code.arg("Phaser.Geom.Rectangle.Contains"); } } } \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaTool.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaTool.ts index 96f32a001..739f1b1b6 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaTool.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaTool.ts @@ -112,9 +112,9 @@ namespace phasereditor2d.scene.ui.sceneobjects { const shape = comp.getHitAreaShape(); - if (shape === HitAreaShape.CIRCLE) { + if (shape === HitAreaShape.ELLIPSE) { - this.renderCircle(obj, args, ctx); + this.renderEllipse(obj, args, ctx); } else { @@ -187,7 +187,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { ctx.closePath(); } - private renderCircle(obj: Sprite, args: editor.tools.ISceneToolRenderArgs, ctx: CanvasRenderingContext2D) { + private renderEllipse(obj: Sprite, args: editor.tools.ISceneToolRenderArgs, ctx: CanvasRenderingContext2D) { // const body = ArcadeComponent.getBody(obj); From 0277f85e41bf4c669c7a6e9fef463cb05ab368bb Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 11 May 2023 12:24:19 -0400 Subject: [PATCH 41/59] Refactors the hit area tooling. Renders the ellipse input area. --- .../phasereditor2d.scene/src/ScenePlugin.ts | 3 +- .../ui/editor/commands/SceneEditorCommands.ts | 8 +- .../sceneobjects/GameObjectEditorSupport.ts | 3 +- .../hitArea/BaseHitAreaOffsetToolItem.ts | 18 ++- .../hitArea/BaseHitAreaSizeToolItem.ts | 18 ++- .../hitArea/BaseHitAreaToolItem.ts | 29 +++++ ...esizeHitAreaTool.ts => EditHitAreaTool.ts} | 106 ++++++++++-------- .../hitArea/EllipseHitAreaComponent.ts | 62 ++++++++++ .../hitArea/EllipseHitAreaSection.ts | 40 +++++++ .../sceneobjects/hitArea/HitAreaComponent.ts | 18 +++ .../ui/sceneobjects/hitArea/HitAreaSection.ts | 7 +- .../hitArea/RectangleHitAreaOffsetToolItem.ts | 6 +- .../hitArea/RectangleHitAreaSection.ts | 2 +- ...on.ts => RectangleHitAreaSizeOperation.ts} | 2 +- ...tem.ts => RectangleHitAreaSizeToolItem.ts} | 10 +- .../sceneobjects/object/tools/IAxisFactor.ts | 4 + .../object/tools/ScaleToolItem.ts | 8 +- .../sceneobjects/object/tools/SizeToolItem.ts | 6 +- .../physics/ArcadeBodyOffsetToolItem.ts | 2 +- .../physics/BaseArcadeBodyOffsetToolItem.ts | 6 +- .../physics/BaseArcadeBodySizeToolItem.ts | 6 +- 21 files changed, 263 insertions(+), 101 deletions(-) create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaToolItem.ts rename source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/{ResizeHitAreaTool.ts => EditHitAreaTool.ts} (65%) create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaSection.ts rename source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/{ResizeHitAreaOperation.ts => RectangleHitAreaSizeOperation.ts} (88%) rename source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/{ResizeHitAreaToolItem.ts => RectangleHitAreaSizeToolItem.ts} (80%) create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/tools/IAxisFactor.ts diff --git a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts index b5bcc36a4..09874d535 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts @@ -376,6 +376,7 @@ namespace phasereditor2d.scene { page => new ui.sceneobjects.ThreeSliceSection(page), page => new ui.sceneobjects.HitAreaSection(page), page => new ui.sceneobjects.RectangleHitAreaSection(page), + page => new ui.sceneobjects.EllipseHitAreaSection(page), page => new ui.sceneobjects.ArcadeBodySection(page), page => new ui.sceneobjects.ArcadeGeometrySection(page), page => new ui.sceneobjects.ArcadeBodyMovementSection(page), @@ -406,7 +407,7 @@ namespace phasereditor2d.scene { new ui.sceneobjects.ScaleTool(), new ui.sceneobjects.OriginTool(), new ui.sceneobjects.SizeTool(), - new ui.sceneobjects.ResizeHitAreaTool(), + new ui.sceneobjects.EditHitAreaTool(), new ui.sceneobjects.ArcadeBodyTool(), new ui.sceneobjects.SliceTool(), new ui.sceneobjects.PolygonTool(), diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts index 5f6680721..eb27e94de 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts @@ -59,7 +59,7 @@ namespace phasereditor2d.scene.ui.editor.commands { export const CMD_OPEN_SCRIPT_DIALOG = "phasereditor2d.scene.ui.editor.commands.OpenScriptDialog"; export const CMD_OPEN_ADD_SCRIPT_DIALOG = "phasereditor2d.scene.ui.editor.commands.OpenAddScriptDialog"; export const CMD_PREVIEW_SCENE = "phasereditor2d.scene.ui.editor.commands.PreviewScene"; - export const CMD_RESIZE_HIT_AREA = "phasereditor2d.scene.ui.editor.commands.ResizeHitArea"; + export const CMD_EDIT_HIT_AREA = "phasereditor2d.scene.ui.editor.commands.ResizeHitArea"; function isSceneScope(args: colibri.ui.ide.commands.HandlerArgs) { @@ -1904,15 +1904,15 @@ namespace phasereditor2d.scene.ui.editor.commands { manager.add({ command: { - id: CMD_RESIZE_HIT_AREA, - name: "Resize Hit Area Tool", + id: CMD_EDIT_HIT_AREA, + name: "Hit Area Tool", tooltip: "Resize the hit area of the selected objects.", category: CAT_SCENE_EDITOR }, handler: { testFunc: isSceneScope, executeFunc: args => (args.activeEditor as SceneEditor) - .getToolsManager().swapTool(ui.sceneobjects.ResizeHitAreaTool.ID) + .getToolsManager().swapTool(ui.sceneobjects.EditHitAreaTool.ID) }, keys: { key: "KeyI" diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts index b77c3cf4c..0ad331be3 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts @@ -64,7 +64,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { this.addComponent( new HitAreaComponent(obj), - new RectangleHitAreaComponent(obj) + new RectangleHitAreaComponent(obj), + new EllipseHitAreaComponent(obj) ); } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaOffsetToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaOffsetToolItem.ts index 189133ccf..fb67dd738 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaOffsetToolItem.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaOffsetToolItem.ts @@ -1,24 +1,20 @@ +/// namespace phasereditor2d.scene.ui.sceneobjects { export abstract class BaseHitAreaOffsetToolItem - extends editor.tools.SceneToolItem implements editor.tools.ISceneToolItemXY { + extends BaseHitAreaToolItem implements editor.tools.ISceneToolItemXY { - private _x: IScaleAxis; - private _y: IScaleAxis; + private _x: IAxisFactor; + private _y: IAxisFactor; private _dragging: boolean; - constructor(x: IScaleAxis, y: IScaleAxis) { - super(); + constructor(shape: HitAreaShape, x: IAxisFactor, y: IAxisFactor) { + super(shape); this._x = x; this._y = y; } - isValidFor(objects: ISceneGameObject[]): boolean { - - return ResizeHitAreaTool.isValidFor(...objects); - } - getPoint(args: editor.tools.ISceneToolContextArgs): { x: number; y: number; } { return this.getAvgScreenPointOfObjects(args, @@ -108,7 +104,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { ctx.rotate(Phaser.Math.DegToRad(angle)); this.drawRect(ctx, args.canEdit ? - ResizeHitAreaTool.TOOL_COLOR : editor.tools.SceneTool.COLOR_CANNOT_EDIT); + EditHitAreaTool.TOOL_COLOR : editor.tools.SceneTool.COLOR_CANNOT_EDIT); ctx.restore(); } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaSizeToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaSizeToolItem.ts index bd3b46350..5200eb914 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaSizeToolItem.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaSizeToolItem.ts @@ -1,14 +1,15 @@ +/// namespace phasereditor2d.scene.ui.sceneobjects { export abstract class BaseHitAreaSizeToolItem - extends editor.tools.SceneToolItem implements editor.tools.ISceneToolItemXY { + extends BaseHitAreaToolItem implements editor.tools.ISceneToolItemXY { - private _x: IScaleAxis; - private _y: IScaleAxis; + private _x: IAxisFactor; + private _y: IAxisFactor; private _dragging: boolean; - constructor(x: IScaleAxis, y: IScaleAxis) { - super(); + constructor(shape: HitAreaShape, x: IAxisFactor, y: IAxisFactor) { + super(shape); this._x = x; this._y = y; @@ -26,11 +27,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { protected abstract createStopDragOperation(args: editor.tools.ISceneToolDragEventArgs): colibri.ui.ide.undo.Operation; - isValidFor(objects: ISceneGameObject[]): boolean { - - return ResizeHitAreaTool.isValidFor(...objects); - } - getPoint(args: editor.tools.ISceneToolContextArgs): { x: number; y: number; } { return this.getAvgScreenPointOfObjects(args, @@ -85,7 +81,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { ctx.rotate(Phaser.Math.DegToRad(angle)); this.drawRect(ctx, args.canEdit ? - ResizeHitAreaTool.TOOL_COLOR : editor.tools.SceneTool.COLOR_CANNOT_EDIT); + EditHitAreaTool.TOOL_COLOR : editor.tools.SceneTool.COLOR_CANNOT_EDIT); ctx.restore(); } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaToolItem.ts new file mode 100644 index 000000000..cd81ce1de --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaToolItem.ts @@ -0,0 +1,29 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + export abstract class BaseHitAreaToolItem extends editor.tools.SceneToolItem { + + private _shape: HitAreaShape; + + constructor(shape: HitAreaShape) { + super(); + + this._shape = shape; + } + + isValidFor(objects: ISceneGameObject[]): boolean { + + if (EditHitAreaTool.isValidFor(...objects)) { + + for (const obj of objects) { + + if (HitAreaComponent.getShape(obj) === this._shape) { + + return true; + } + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaTool.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts similarity index 65% rename from source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaTool.ts rename to source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts index 739f1b1b6..927189894 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaTool.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts @@ -1,15 +1,15 @@ /// namespace phasereditor2d.scene.ui.sceneobjects { - export class ResizeHitAreaTool extends BaseObjectTool { + export class EditHitAreaTool extends BaseObjectTool { - static ID = "phasereditor2d.scene.ui.sceneobjects.ResizeHitAreaTool"; + static ID = "phasereditor2d.scene.ui.sceneobjects.EditHitAreaTool"; static TOOL_COLOR = "orange"; constructor() { super({ - id: ResizeHitAreaTool.ID, - command: editor.commands.CMD_RESIZE_HIT_AREA, + id: EditHitAreaTool.ID, + command: editor.commands.CMD_EDIT_HIT_AREA, }, RectangleHitAreaComponent.x, RectangleHitAreaComponent.y, @@ -18,14 +18,12 @@ namespace phasereditor2d.scene.ui.sceneobjects { ); this.addItems( - new ResizeHitAreaToolItem(1, 0.5), - new ResizeHitAreaToolItem(1, 1), - new ResizeHitAreaToolItem(0.5, 1), + new RectangleHitAreaSizeToolItem(1, 0.5), + new RectangleHitAreaSizeToolItem(1, 1), + new RectangleHitAreaSizeToolItem(0.5, 1), new RectangleHitAreaOffsetToolItem(0, 0), new RectangleHitAreaOffsetToolItem(0.5, 0), new RectangleHitAreaOffsetToolItem(0, 0.5), - // new ArcadeBodyCircleOffsetToolItem(), - // new ArcadeBodyRadiusToolItem() ); } @@ -77,7 +75,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { for (const obj of args.objects) { - if (ResizeHitAreaTool.isValidFor(obj)) { + if (EditHitAreaTool.isValidFor(obj)) { this.renderObj(args, obj as Sprite); } @@ -165,7 +163,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { ctx.lineWidth = 3; this.drawPath(ctx, points); - ctx.strokeStyle = ResizeHitAreaTool.TOOL_COLOR; + ctx.strokeStyle = EditHitAreaTool.TOOL_COLOR; ctx.lineWidth = 1; this.drawPath(ctx, points); @@ -189,53 +187,67 @@ namespace phasereditor2d.scene.ui.sceneobjects { private renderEllipse(obj: Sprite, args: editor.tools.ISceneToolRenderArgs, ctx: CanvasRenderingContext2D) { - // const body = ArcadeComponent.getBody(obj); + const origin = obj.getEditorSupport().computeDisplayOrigin(); + + if (obj instanceof Container) { + + origin.displayOriginX = 0; + origin.displayOriginY = 0; + } + + const comp = EllipseHitAreaComponent.getEllipseComponent(obj); + + const { x, y, width, height } = comp; + + let x1 = x - origin.displayOriginX; + let y1 = y - origin.displayOriginY; + let x2 = x1 + width; + let y2 = y1 + height; + const tx = obj.getWorldTransformMatrix(); + + const points = [ + [x1, y1], + [x2, y1], + [x2, y2], + ].map(([x, y]) => { - // const p = new Phaser.Math.Vector2(); + return tx.transformPoint(x, y); + }).map(p => { - // const origin = obj.getEditorSupport().computeDisplayOrigin(); + return args.camera.getScreenPoint(p.x, p.y); + }); - // if (obj instanceof Container) { + const [p1, p2, p3] = points; - // origin.displayOriginX = 0; - // origin.displayOriginY = 0; - // } + const screenWidth = Phaser.Math.Distance.BetweenPoints(p1, p2); + const screenHeight = Phaser.Math.Distance.BetweenPoints(p2, p3); + const angle = ui.editor.tools.SceneToolItem.getGlobalAngle(obj); - // const bodyRadius = ArcadeComponent.radius.getValue(obj); - // let x1 = body.offset.x - origin.displayOriginX; - // let y1 = body.offset.y - origin.displayOriginY; - // let x2 = x1 + bodyRadius * 2; - // let y2 = y1 + bodyRadius * 2; + ctx.save(); - // const tx = obj.getWorldTransformMatrix(); - // // removes rotation - // tx.rotate(-tx.rotation); - // tx.transformPoint(x1, y1, p); - // x1 = p.x; - // y1 = p.y; + ctx.beginPath(); + ctx.strokeStyle = "black"; + ctx.lineWidth = 3; + this.drawEllipse(ctx, p1.x, p1.y, screenWidth, screenHeight, angle); + ctx.stroke(); + ctx.closePath(); - // tx.transformPoint(x2, y2, p); - // x2 = p.x; - // y2 = p.y; + ctx.beginPath(); + ctx.strokeStyle = EditHitAreaTool.TOOL_COLOR; + ctx.lineWidth = 1; + this.drawEllipse(ctx, p1.x, p1.y, screenWidth, screenHeight, angle); + ctx.stroke(); + ctx.closePath(); - // const p1 = args.camera.getScreenPoint(x1, y1); - // const p2 = args.camera.getScreenPoint(x2, y2); + ctx.restore(); + } - // const r = (p2.x - p1.x) / 2; - // const x = p1.x + r; - // const y = p1.y + r; + private drawEllipse(ctx: CanvasRenderingContext2D, x:number, y:number, w: number, h: number, angle: number) { - // ctx.strokeStyle = "black"; - // ctx.lineWidth = 3; - // ctx.beginPath(); - // ctx.ellipse(x, y, r, r, 0, 0, 360); - // ctx.stroke(); + const rx = w / 2; + const ry = h / 2; - // ctx.strokeStyle = ArcadeBodyTool.BODY_TOOL_COLOR; - // ctx.lineWidth = 1; - // ctx.beginPath(); - // ctx.ellipse(x, y, r, r, 0, 0, 360); - // ctx.stroke(); + ctx.ellipse(x, y, rx, ry, Phaser.Math.DegToRad(angle), 0, Math.PI * 2); } } } \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaComponent.ts index e69de29bb..1c4bbeaf0 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaComponent.ts @@ -0,0 +1,62 @@ +/// +/// +namespace phasereditor2d.scene.ui.sceneobjects { + + export class EllipseHitAreaComponent extends BaseHitAreaComponent { + + static x = HitAreaProperty(EllipseHitAreaComponent, "x", "X", "phaser:Phaser.Geom.Ellipse.x", 0); + static y = HitAreaProperty(EllipseHitAreaComponent, "y", "Y", "phaser:Phaser.Geom.Ellipse.y", 0); + static width = HitAreaProperty(EllipseHitAreaComponent, "width", "W", "phaser:Phaser.Geom.Ellipse.width", 0); + static height = HitAreaProperty(EllipseHitAreaComponent, "height", "H", "phaser:Phaser.Geom.Ellipse.height", 0); + static position: IPropertyXY = { + label: "Offset", + x: this.x, + y: this.y + }; + static size: IPropertyXY = { + label: "Size", + x: this.width, + y: this.height + }; + + public x = 0; + public y = 0; + public width = 0; + public height = 0; + + constructor(obj: ISceneGameObject) { + super(obj, HitAreaShape.ELLIPSE, [ + EllipseHitAreaComponent.x, + EllipseHitAreaComponent.y, + EllipseHitAreaComponent.width, + EllipseHitAreaComponent.height, + ]); + } + + static getEllipseComponent(obj: ISceneGameObject) { + + const objES = obj.getEditorSupport(); + + const comp = objES.getComponent(EllipseHitAreaComponent) as EllipseHitAreaComponent; + + return comp; + } + + protected _setDefaultValues(width: number, height: number): void { + + this.x = 0; + this.y = 0; + this.width = width; + this.height = height; + } + + protected override buildSetInteractiveCodeCOM(obj: ISceneGameObject, code: core.code.MethodCallCodeDOM): void { + + const { x, y, width, height } = this; + + code.arg(`new Phaser.Geom.Ellipse(${x}, ${y}, ${width}, ${height})`); + + code.arg("Phaser.Geom.Ellipse.Contains"); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaSection.ts new file mode 100644 index 000000000..0fc2e2642 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaSection.ts @@ -0,0 +1,40 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + import controls = colibri.ui.controls; + + export class EllipseHitAreaSection extends SceneGameObjectSection { + + static ID = "phasereditor2d.scene.ui.sceneobjects.EllipseHitAreaSection"; + + constructor(page: controls.properties.PropertyPage) { + super(page, EllipseHitAreaSection.ID, "Hit Area (Ellipse)"); + } + + createMenu(menu: controls.Menu) { + + this.createToolMenuItem(menu, EditHitAreaTool.ID); + + super.createMenu(menu); + } + + createForm(parent: HTMLDivElement) { + + const comp = this.createGridElementWithPropertiesXY(parent); + + this.createPropertyXYRow(comp, EllipseHitAreaComponent.position, false); + this.createPropertyXYRow(comp, EllipseHitAreaComponent.size, false); + } + + canEdit(obj: any, n: number): boolean { + + console.log("here"); + + return HitAreaComponent.hasHitAreaShape(obj, HitAreaShape.ELLIPSE); + } + + canEditNumber(n: number): boolean { + + return n > 0; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts index db2b6a7e3..fbcadfb06 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts @@ -37,6 +37,24 @@ namespace phasereditor2d.scene.ui.sceneobjects { this._hitAreaShape = HitAreaShape.NONE; } + static getShapeComponent(obj: ISceneGameObject) { + + const shape = this.getShape(obj); + + switch(shape) { + + case HitAreaShape.RECTANGLE: + + return RectangleHitAreaComponent.getRectangleComponent(obj); + + case HitAreaShape.ELLIPSE: + + return EllipseHitAreaComponent.getEllipseComponent(obj); + } + + return undefined; + } + static hasHitAreaShape(obj: ISceneGameObject, shape: HitAreaShape) { if (this.hasHitArea(obj)) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaSection.ts index 1258922b4..c4b833311 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaSection.ts @@ -20,12 +20,9 @@ namespace phasereditor2d.scene.ui.sceneobjects { hitAreaShape.setValue(obj, value); - if (value === HitAreaShape.RECTANGLE) { + const comp = HitAreaComponent.getShapeComponent(obj) - const comp = RectangleHitAreaComponent.getRectangleComponent(obj); - - comp.setDefaultValues(); - } + comp.setDefaultValues(); } this.createPropertyEnumRow(comp, prop); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaOffsetToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaOffsetToolItem.ts index b5f4c728b..0b9477ea0 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaOffsetToolItem.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaOffsetToolItem.ts @@ -2,6 +2,10 @@ namespace phasereditor2d.scene.ui.sceneobjects { export class RectangleHitAreaOffsetToolItem extends BaseHitAreaOffsetToolItem { + constructor(x: IAxisFactor, y: IAxisFactor) { + super(HitAreaShape.RECTANGLE, x, y); + } + protected getOffsetProperties(obj: ISceneGameObject): { x: IProperty; y: IProperty; } { const { x, y } = RectangleHitAreaComponent; @@ -27,7 +31,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { } protected createStopDragOperation(args: editor.tools.ISceneToolDragEventArgs): colibri.ui.ide.undo.Operation { - + return new RectangleOffsetOperation(args, obj => this.getInitialValue(obj)) } } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSection.ts index 3084ec14c..211660d20 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSection.ts @@ -12,7 +12,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { createMenu(menu: controls.Menu) { - this.createToolMenuItem(menu, ResizeHitAreaTool.ID); + this.createToolMenuItem(menu, EditHitAreaTool.ID); super.createMenu(menu); } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSizeOperation.ts similarity index 88% rename from source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaOperation.ts rename to source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSizeOperation.ts index 252e2a442..1d7b845fd 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaOperation.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSizeOperation.ts @@ -1,6 +1,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { - export class ResizeHitAreaOperation extends editor.tools.SceneToolOperation<{ x: number, y: number }> { + export class RectangleHitAreaSizeOperation extends editor.tools.SceneToolOperation<{ x: number, y: number }> { constructor( toolArgs: editor.tools.ISceneToolContextArgs, diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSizeToolItem.ts similarity index 80% rename from source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaToolItem.ts rename to source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSizeToolItem.ts index 282640b07..b8e17c98e 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/ResizeHitAreaToolItem.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSizeToolItem.ts @@ -2,7 +2,11 @@ namespace phasereditor2d.scene.ui.sceneobjects { - export class ResizeHitAreaToolItem extends BaseHitAreaSizeToolItem { + export class RectangleHitAreaSizeToolItem extends BaseHitAreaSizeToolItem { + + constructor(x: IAxisFactor, y: IAxisFactor) { + super(HitAreaShape.RECTANGLE, x, y); + } private getHitAreaComp(obj: ISceneGameObject) { @@ -29,7 +33,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { protected getDataKey(): string { - return "ResizeHitAreaToolItem"; + return "RectangleHitAreaSizeToolItem"; } protected getHitAreaSectionId(): string { @@ -54,7 +58,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { protected createStopDragOperation(args: editor.tools.ISceneToolDragEventArgs): colibri.ui.ide.undo.Operation { - return new ResizeHitAreaOperation(args, obj => this.getInitialSize(obj)); + return new RectangleHitAreaSizeOperation(args, obj => this.getInitialSize(obj)); } } } \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/tools/IAxisFactor.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/tools/IAxisFactor.ts new file mode 100644 index 000000000..31c698f63 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/tools/IAxisFactor.ts @@ -0,0 +1,4 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + export declare type IAxisFactor = 0 | 0.5 | 1; +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/tools/ScaleToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/tools/ScaleToolItem.ts index 6238071e3..0982eaf7a 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/tools/ScaleToolItem.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/tools/ScaleToolItem.ts @@ -1,15 +1,13 @@ namespace phasereditor2d.scene.ui.sceneobjects { - export declare type IScaleAxis = 0 | 0.5 | 1; - export class ScaleToolItem extends editor.tools.SceneToolItem implements editor.tools.ISceneToolItemXY { - private _x: IScaleAxis; - private _y: IScaleAxis; + private _x: IAxisFactor; + private _y: IAxisFactor; private _dragging: boolean; - constructor(x: IScaleAxis, y: IScaleAxis) { + constructor(x: IAxisFactor, y: IAxisFactor) { super(); this._x = x; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/tools/SizeToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/tools/SizeToolItem.ts index 44a63924d..90657015b 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/tools/SizeToolItem.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/tools/SizeToolItem.ts @@ -3,11 +3,11 @@ namespace phasereditor2d.scene.ui.sceneobjects { export class SizeToolItem extends editor.tools.SceneToolItem implements editor.tools.ISceneToolItemXY { - private _x: IScaleAxis; - private _y: IScaleAxis; + private _x: IAxisFactor; + private _y: IAxisFactor; private _dragging: boolean; - constructor(x: IScaleAxis, y: IScaleAxis) { + constructor(x: IAxisFactor, y: IAxisFactor) { super(); this._x = x; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/physics/ArcadeBodyOffsetToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/physics/ArcadeBodyOffsetToolItem.ts index bc94a0c33..5f891a704 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/physics/ArcadeBodyOffsetToolItem.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/physics/ArcadeBodyOffsetToolItem.ts @@ -5,7 +5,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { extends BaseArcadeBodyOffsetToolItem implements editor.tools.ISceneToolItemXY { - constructor(x: IScaleAxis, y: IScaleAxis) { + constructor(x: IAxisFactor, y: IAxisFactor) { super(x, y); } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/physics/BaseArcadeBodyOffsetToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/physics/BaseArcadeBodyOffsetToolItem.ts index 561cf4928..e9fcdce6d 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/physics/BaseArcadeBodyOffsetToolItem.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/physics/BaseArcadeBodyOffsetToolItem.ts @@ -3,11 +3,11 @@ namespace phasereditor2d.scene.ui.sceneobjects { export class BaseArcadeBodyOffsetToolItem extends editor.tools.SceneToolItem implements editor.tools.ISceneToolItemXY { - private _x: IScaleAxis; - private _y: IScaleAxis; + private _x: IAxisFactor; + private _y: IAxisFactor; private _dragging: boolean; - constructor(x: IScaleAxis, y: IScaleAxis) { + constructor(x: IAxisFactor, y: IAxisFactor) { super(); this._x = x; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/physics/BaseArcadeBodySizeToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/physics/BaseArcadeBodySizeToolItem.ts index 40cbf7bd2..0d8c1fdd6 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/physics/BaseArcadeBodySizeToolItem.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/physics/BaseArcadeBodySizeToolItem.ts @@ -3,11 +3,11 @@ namespace phasereditor2d.scene.ui.sceneobjects { export abstract class BaseArcadeBodySizeToolItem extends editor.tools.SceneToolItem implements editor.tools.ISceneToolItemXY { - private _x: IScaleAxis; - private _y: IScaleAxis; + private _x: IAxisFactor; + private _y: IAxisFactor; private _dragging: boolean; - constructor(x: IScaleAxis, y: IScaleAxis) { + constructor(x: IAxisFactor, y: IAxisFactor) { super(); this._x = x; From 47367a818e60c21c74b700e07fb60ace26ac02e0 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 11 May 2023 14:18:28 -0400 Subject: [PATCH 42/59] Adds ellipse hit area tools. --- .../hitArea/BaseHitAreaOffsetToolItem.ts | 11 +-- .../hitArea/BaseHitAreaSizeToolItem.ts | 20 +++--- .../sceneobjects/hitArea/EditHitAreaTool.ts | 7 ++ .../hitArea/EllipseHitAreaOffsetToolItem.ts | 48 +++++++++++++ .../hitArea/EllipseHitAreaSizeOperation.ts | 31 +++++++++ .../hitArea/EllipseHitAreaSizeToolItem.ts | 69 +++++++++++++++++++ .../hitArea/EllipseOffsetOperation.ts | 31 +++++++++ .../hitArea/RectangleHitAreaSizeToolItem.ts | 5 ++ .../physics/BaseArcadeBodySizeToolItem.ts | 7 +- 9 files changed, 203 insertions(+), 26 deletions(-) create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaOffsetToolItem.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaSizeOperation.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaSizeToolItem.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseOffsetOperation.ts diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaOffsetToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaOffsetToolItem.ts index fb67dd738..96d011091 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaOffsetToolItem.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaOffsetToolItem.ts @@ -4,8 +4,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { export abstract class BaseHitAreaOffsetToolItem extends BaseHitAreaToolItem implements editor.tools.ISceneToolItemXY { - private _x: IAxisFactor; - private _y: IAxisFactor; + protected _x: IAxisFactor; + protected _y: IAxisFactor; private _dragging: boolean; constructor(shape: HitAreaShape, x: IAxisFactor, y: IAxisFactor) { @@ -25,7 +25,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { ); } - protected getScreenPointOfObject(args: ui.editor.tools.ISceneToolContextArgs, sprite: Sprite, fx: number, fy: number, removeRotation = false) { + protected getScreenPointOfObject(args: ui.editor.tools.ISceneToolContextArgs, sprite: Sprite, fx: number, fy: number) { const worldPoint = new Phaser.Geom.Point(0, 0); @@ -50,11 +50,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { const tx = sprite.getWorldTransformMatrix(); - if (removeRotation) { - - tx.rotate(-tx.rotation); - } - tx.transformPoint(x, y, worldPoint); return args.camera.getScreenPoint(worldPoint.x, worldPoint.y); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaSizeToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaSizeToolItem.ts index 5200eb914..3de3c207d 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaSizeToolItem.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaSizeToolItem.ts @@ -4,8 +4,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { export abstract class BaseHitAreaSizeToolItem extends BaseHitAreaToolItem implements editor.tools.ISceneToolItemXY { - private _x: IAxisFactor; - private _y: IAxisFactor; + protected _x: IAxisFactor; + protected _y: IAxisFactor; private _dragging: boolean; constructor(shape: HitAreaShape, x: IAxisFactor, y: IAxisFactor) { @@ -31,13 +31,13 @@ namespace phasereditor2d.scene.ui.sceneobjects { return this.getAvgScreenPointOfObjects(args, - (sprite: sceneobjects.Image) => this._x, + (sprite: sceneobjects.Image) => this._x - this.getToolOrigin(sprite).originX, - (sprite: sceneobjects.Image) => this._y, + (sprite: sceneobjects.Image) => this._y - this.getToolOrigin(sprite).originY, ); } - protected getScreenPointOfObject(args: ui.editor.tools.ISceneToolContextArgs, sprite: Sprite, fx: number, fy: number, removeRotation = false) { + protected getScreenPointOfObject(args: ui.editor.tools.ISceneToolContextArgs, sprite: Sprite, fx: number, fy: number) { const worldPoint = new Phaser.Geom.Point(0, 0); @@ -57,11 +57,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { const tx = sprite.getWorldTransformMatrix(); - if (removeRotation) { - - tx.rotate(-tx.rotation); - } - tx.transformPoint(x, y, worldPoint); return args.camera.getScreenPoint(worldPoint.x, worldPoint.y); @@ -133,6 +128,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { return { x: data.initWidth, y: data.initHeight }; } + protected abstract getToolOrigin(obj: ISceneGameObject): { originX: number, originY: number }; + onDrag(args: editor.tools.ISceneToolDragEventArgs): void { if (!this._dragging) { @@ -156,8 +153,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { const flipX = sprite.flipX ? -1 : 1; const flipY = sprite.flipY ? -1 : 1; - let originX = 0; - let originY = 0; + const { originX, originY } = this.getToolOrigin(obj); const dx = (localPos.x - initLocalPos.x) * flipX / camera.zoom; const dy = (localPos.y - initLocalPos.y) * flipY / camera.zoom; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts index 927189894..610245d42 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts @@ -24,6 +24,13 @@ namespace phasereditor2d.scene.ui.sceneobjects { new RectangleHitAreaOffsetToolItem(0, 0), new RectangleHitAreaOffsetToolItem(0.5, 0), new RectangleHitAreaOffsetToolItem(0, 0.5), + + new EllipseHitAreaSizeToolItem(1, 0.5), + new EllipseHitAreaSizeToolItem(1, 1), + new EllipseHitAreaSizeToolItem(0.5, 1), + new EllipseHitAreaOffsetToolItem(0, 0), + new EllipseHitAreaOffsetToolItem(0.5, 0), + new EllipseHitAreaOffsetToolItem(0, 0.5), ); } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaOffsetToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaOffsetToolItem.ts new file mode 100644 index 000000000..38cb8d02e --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaOffsetToolItem.ts @@ -0,0 +1,48 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + export class EllipseHitAreaOffsetToolItem extends BaseHitAreaOffsetToolItem { + + constructor(x: IAxisFactor, y: IAxisFactor) { + super(HitAreaShape.ELLIPSE, x, y); + } + + override getPoint(args: editor.tools.ISceneToolContextArgs): { x: number; y: number; } { + + return this.getAvgScreenPointOfObjects(args, + + (sprite: sceneobjects.Image) => this._x - sprite.getEditorSupport().computeOrigin().originX, + + (sprite: sceneobjects.Image) => this._y - sprite.getEditorSupport().computeOrigin().originY, + ); + } + + protected getOffsetProperties(obj: ISceneGameObject): { x: IProperty; y: IProperty; } { + + const { x, y } = EllipseHitAreaComponent; + + return { x, y }; + } + + protected getSizeProperties(obj: ISceneGameObject): { width: IProperty; height: IProperty; } { + + const { width, height } = EllipseHitAreaComponent; + + return { width, height }; + } + + protected getKeyData(): string { + + return "EllipseHitAreaOffsetToolItem"; + } + + protected getOffsetSectionId(): string { + + return EllipseHitAreaSection.ID; + } + + protected createStopDragOperation(args: editor.tools.ISceneToolDragEventArgs): colibri.ui.ide.undo.Operation { + + return new EllipseOffsetOperation(args, obj => this.getInitialValue(obj)); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaSizeOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaSizeOperation.ts new file mode 100644 index 000000000..d3f35ca3d --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaSizeOperation.ts @@ -0,0 +1,31 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + export class EllipseHitAreaSizeOperation extends editor.tools.SceneToolOperation<{ x: number, y: number }> { + + constructor( + toolArgs: editor.tools.ISceneToolContextArgs, + private getInitialSize: (obj: ISceneGameObject) => { x: number, y: number }) { + + super(toolArgs); + } + + getInitialValue(obj: ISceneGameObject): { x: number; y: number; } { + + return this.getInitialSize(obj); + } + + getFinalValue(obj: ISceneGameObject): { x: number; y: number; } { + + return { + x: EllipseHitAreaComponent.width.getValue(obj), + y: EllipseHitAreaComponent.height.getValue(obj) + }; + } + + setValue(obj: ISceneGameObject, value: { x: number; y: number; }) { + + EllipseHitAreaComponent.width.setValue(obj, value.x); + EllipseHitAreaComponent.height.setValue(obj, value.y); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaSizeToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaSizeToolItem.ts new file mode 100644 index 000000000..cea520b10 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaSizeToolItem.ts @@ -0,0 +1,69 @@ +/// + +namespace phasereditor2d.scene.ui.sceneobjects { + + export class EllipseHitAreaSizeToolItem extends BaseHitAreaSizeToolItem { + + constructor(x: IAxisFactor, y: IAxisFactor) { + super(HitAreaShape.ELLIPSE, x, y); + } + + protected getToolOrigin(obj: ISceneGameObject): { originX: number; originY: number; } { + + return obj.getEditorSupport().computeOrigin(); + } + + private getHitAreaComp(obj: ISceneGameObject) { + + const objEs = obj.getEditorSupport(); + + const comp = objEs.getComponent(EllipseHitAreaComponent) as EllipseHitAreaComponent; + + return comp; + } + + protected computeSize(obj: ISceneGameObject): { width: number; height: number; } { + + const { width, height } = this.getHitAreaComp(obj); + + return { width, height }; + } + + protected getHitAreaOffset(obj: ISceneGameObject): { x: number; y: number; } { + + const { x, y } = this.getHitAreaComp(obj); + + return { x, y }; + } + + protected getDataKey(): string { + + return "EllipseHitAreaSizeToolItem"; + } + + protected getHitAreaSectionId(): string { + + return EllipseHitAreaSection.ID; + } + + protected onDragValues(obj: ISceneGameObject, changeX: boolean, changeY: boolean, width: number, height: number) { + + const comp = this.getHitAreaComp(obj); + + if (changeX) { + + comp.width = width; + } + + if (changeY) { + + comp.height = height; + } + } + + protected createStopDragOperation(args: editor.tools.ISceneToolDragEventArgs): colibri.ui.ide.undo.Operation { + + return new EllipseHitAreaSizeOperation(args, obj => this.getInitialSize(obj)); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseOffsetOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseOffsetOperation.ts new file mode 100644 index 000000000..fab55ed46 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseOffsetOperation.ts @@ -0,0 +1,31 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + export class EllipseOffsetOperation extends editor.tools.SceneToolOperation<{ x: number, y: number }> { + + constructor( + toolArgs: editor.tools.ISceneToolContextArgs, + private getInitialOffset: (obj: ISceneGameObject) => { x: number, y: number }) { + + super(toolArgs); + } + + getInitialValue(obj: ISceneGameObject): { x: number; y: number; } { + + return this.getInitialOffset(obj); + } + + getFinalValue(obj: ISceneGameObject): { x: number; y: number; } { + + return { + x: EllipseHitAreaComponent.x.getValue(obj), + y: EllipseHitAreaComponent.y.getValue(obj) + }; + } + + setValue(obj: ISceneGameObject, value: { x: number; y: number; }) { + + EllipseHitAreaComponent.x.setValue(obj, value.x); + EllipseHitAreaComponent.y.setValue(obj, value.y); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSizeToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSizeToolItem.ts index b8e17c98e..0db8afa7c 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSizeToolItem.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaSizeToolItem.ts @@ -8,6 +8,11 @@ namespace phasereditor2d.scene.ui.sceneobjects { super(HitAreaShape.RECTANGLE, x, y); } + protected getToolOrigin(): { originX: number; originY: number; } { + + return { originX: 0, originY: 0 }; + } + private getHitAreaComp(obj: ISceneGameObject) { const objEs = obj.getEditorSupport(); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/physics/BaseArcadeBodySizeToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/physics/BaseArcadeBodySizeToolItem.ts index 0d8c1fdd6..99cc61769 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/physics/BaseArcadeBodySizeToolItem.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/physics/BaseArcadeBodySizeToolItem.ts @@ -27,7 +27,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { ); } - protected getScreenPointOfObject(args: ui.editor.tools.ISceneToolContextArgs, sprite: Sprite, fx: number, fy: number, removeRotation = false) { + protected getScreenPointOfObject(args: ui.editor.tools.ISceneToolContextArgs, sprite: Sprite, fx: number, fy: number) { const worldPoint = new Phaser.Geom.Point(0, 0); @@ -47,11 +47,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { const tx = sprite.getWorldTransformMatrix(); - if (removeRotation) { - - tx.rotate(-tx.rotation); - } - tx.transformPoint(x, y, worldPoint); return args.camera.getScreenPoint(worldPoint.x, worldPoint.y); From 818e0a67213d828043333aeb94c71cb37b0db3b4 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 11 May 2023 14:22:38 -0400 Subject: [PATCH 43/59] Refactoring. --- .../hitArea/BaseHitAreaOffsetToolItem.ts | 6 ++++-- .../hitArea/EllipseHitAreaOffsetToolItem.ts | 13 ++++--------- .../hitArea/RectangleHitAreaOffsetToolItem.ts | 5 +++++ 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaOffsetToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaOffsetToolItem.ts index 96d011091..507ec8b17 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaOffsetToolItem.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaOffsetToolItem.ts @@ -15,13 +15,15 @@ namespace phasereditor2d.scene.ui.sceneobjects { this._y = y; } + protected abstract getToolOrigin(obj: ISceneGameObject): { originX: number, originY: number }; + getPoint(args: editor.tools.ISceneToolContextArgs): { x: number; y: number; } { return this.getAvgScreenPointOfObjects(args, - (sprite: sceneobjects.Image) => this._x, + (sprite: sceneobjects.Image) => this._x - this.getToolOrigin(sprite).originX, - (sprite: sceneobjects.Image) => this._y, + (sprite: sceneobjects.Image) => this._y - this.getToolOrigin(sprite).originY, ); } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaOffsetToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaOffsetToolItem.ts index 38cb8d02e..4faf947a9 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaOffsetToolItem.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaOffsetToolItem.ts @@ -1,20 +1,15 @@ namespace phasereditor2d.scene.ui.sceneobjects { export class EllipseHitAreaOffsetToolItem extends BaseHitAreaOffsetToolItem { - + constructor(x: IAxisFactor, y: IAxisFactor) { super(HitAreaShape.ELLIPSE, x, y); } - override getPoint(args: editor.tools.ISceneToolContextArgs): { x: number; y: number; } { + protected getToolOrigin(obj: ISceneGameObject): { originX: number; originY: number; } { - return this.getAvgScreenPointOfObjects(args, - - (sprite: sceneobjects.Image) => this._x - sprite.getEditorSupport().computeOrigin().originX, - - (sprite: sceneobjects.Image) => this._y - sprite.getEditorSupport().computeOrigin().originY, - ); - } + return obj.getEditorSupport().computeOrigin(); + } protected getOffsetProperties(obj: ISceneGameObject): { x: IProperty; y: IProperty; } { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaOffsetToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaOffsetToolItem.ts index 0b9477ea0..bfc2c535e 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaOffsetToolItem.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaOffsetToolItem.ts @@ -6,6 +6,11 @@ namespace phasereditor2d.scene.ui.sceneobjects { super(HitAreaShape.RECTANGLE, x, y); } + protected getToolOrigin(obj: ISceneGameObject): { originX: number; originY: number; } { + + return { originX: 0, originY: 0 }; + } + protected getOffsetProperties(obj: ISceneGameObject): { x: IProperty; y: IProperty; } { const { x, y } = RectangleHitAreaComponent; From 4f75139ee2f13ae22e39c55d1b6a8438825c0a82 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 11 May 2023 16:10:11 -0400 Subject: [PATCH 44/59] Improves ellipse input rendering. --- scripts/make-all-help-files.js | 4 ++ .../data/phaser-docs.json | 3 ++ .../sceneobjects/GameObjectEditorSupport.ts | 1 + .../hitArea/CircleHitAreaComponent.ts | 54 +++++++++++++++++++ .../sceneobjects/hitArea/EditHitAreaTool.ts | 36 +++++++++---- .../hitArea/EllipseHitAreaComponent.ts | 2 + .../hitArea/EllipseHitAreaOffsetToolItem.ts | 8 +-- .../hitArea/EllipseHitAreaSizeToolItem.ts | 4 +- .../sceneobjects/hitArea/HitAreaComponent.ts | 9 +++- 9 files changed, 104 insertions(+), 17 deletions(-) create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaComponent.ts diff --git a/scripts/make-all-help-files.js b/scripts/make-all-help-files.js index 13b3ad8b0..3aeb4c9d7 100755 --- a/scripts/make-all-help-files.js +++ b/scripts/make-all-help-files.js @@ -229,6 +229,10 @@ utils.makeHelpFile([ "Phaser.Geom.Ellipse.width", "Phaser.Geom.Ellipse.height", + "Phaser.Geom.Circle.x", + "Phaser.Geom.Circle.y", + "Phaser.Geom.Circle.radius", + "Phaser.GameObjects.Layer", "Phaser.Physics.Arcade.Collider", diff --git a/source/editor/plugins/phasereditor2d.scene/data/phaser-docs.json b/source/editor/plugins/phasereditor2d.scene/data/phaser-docs.json index 1a7ab6859..ddbe28db6 100644 --- a/source/editor/plugins/phasereditor2d.scene/data/phaser-docs.json +++ b/source/editor/plugins/phasereditor2d.scene/data/phaser-docs.json @@ -127,6 +127,9 @@ "Phaser.Geom.Ellipse.y": "The y position of the center of the ellipse.", "Phaser.Geom.Ellipse.width": "The width of the ellipse.", "Phaser.Geom.Ellipse.height": "The height of the ellipse.", + "Phaser.Geom.Circle.x": "The x position of the center of the circle.", + "Phaser.Geom.Circle.y": "The y position of the center of the circle.", + "Phaser.Geom.Circle.radius": "The radius of the Circle.", "Phaser.GameObjects.Layer": "A Layer Game Object.\n\nA Layer is a special type of Game Object that acts as a Display List. You can add any type of Game Object\nto a Layer, just as you would to a Scene. Layers can be used to visually group together 'layers' of Game\nObjects:\n\n```javascript\nconst spaceman = this.add.sprite(150, 300, 'spaceman');\nconst bunny = this.add.sprite(400, 300, 'bunny');\nconst elephant = this.add.sprite(650, 300, 'elephant');\n\nconst layer = this.add.layer();\n\nlayer.add([ spaceman, bunny, elephant ]);\n```\n\nThe 3 sprites in the example above will now be managed by the Layer they were added to. Therefore,\nif you then set `layer.setVisible(false)` they would all vanish from the display.\n\nYou can also control the depth of the Game Objects within the Layer. For example, calling the\n`setDepth` method of a child of a Layer will allow you to adjust the depth of that child _within the\nLayer itself_, rather than the whole Scene. The Layer, too, can have its depth set as well.\n\nThe Layer class also offers many different methods for manipulating the list, such as the\nmethods `moveUp`, `moveDown`, `sendToBack`, `bringToTop` and so on. These allow you to change the\ndisplay list position of the Layers children, causing it to adjust the order in which they are\nrendered. Using `setDepth` on a child allows you to override this.\n\nLayers can have Post FX Pipelines set, which allows you to easily enable a post pipeline across\na whole range of children, which, depending on the effect, can often be far more efficient that doing so\non a per-child basis.\n\nLayers have no position or size within the Scene. This means you cannot enable a Layer for\nphysics or input, or change the position, rotation or scale of a Layer. They also have no scroll\nfactor, texture, tint, origin, crop or bounds.\n\nIf you need those kind of features then you should use a Container instead. Containers can be added\nto Layers, but Layers cannot be added to Containers.\n\nHowever, you can set the Alpha, Blend Mode, Depth, Mask and Visible state of a Layer. These settings\nwill impact all children being rendered by the Layer.", "Phaser.Physics.Arcade.Collider": "An Arcade Physics Collider will automatically check for collision, or overlaps, between two objects\nevery step. If a collision, or overlap, occurs it will invoke the given callbacks.", "Phaser.Physics.Arcade.Image": "An Arcade Physics Image is an Image with an Arcade Physics body and related components.\nThe body can be dynamic or static.\n\nThe main difference between an Arcade Image and an Arcade Sprite is that you cannot animate an Arcade Image.", diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts index 0ad331be3..76b6fc895 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts @@ -65,6 +65,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { this.addComponent( new HitAreaComponent(obj), new RectangleHitAreaComponent(obj), + new CircleHitAreaComponent(obj), new EllipseHitAreaComponent(obj) ); } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaComponent.ts new file mode 100644 index 000000000..f6183d05b --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaComponent.ts @@ -0,0 +1,54 @@ +/// +/// +namespace phasereditor2d.scene.ui.sceneobjects { + + export class CircleHitAreaComponent extends BaseHitAreaComponent { + + static x = HitAreaProperty(CircleHitAreaComponent, "x", "X", "phaser:Phaser.Geom.Circle.x", 0); + static y = HitAreaProperty(CircleHitAreaComponent, "y", "Y", "phaser:Phaser.Geom.Circle.y", 0); + static radius = HitAreaProperty(CircleHitAreaComponent, "radius", "Radius", "phaser:Phaser.Geom.Circle.radius", 0); + + static position: IPropertyXY = { + label: "Offset", + x: this.x, + y: this.y + }; + + public x = 0; + public y = 0; + public radius = 0; + + constructor(obj: ISceneGameObject) { + super(obj, HitAreaShape.CIRCLE, [ + CircleHitAreaComponent.x, + CircleHitAreaComponent.y, + CircleHitAreaComponent.radius, + ]); + } + + static getCircleComponent(obj: ISceneGameObject) { + + const objES = obj.getEditorSupport(); + + const comp = objES.getComponent(CircleHitAreaComponent) as CircleHitAreaComponent; + + return comp; + } + + protected _setDefaultValues(width: number, height: number): void { + + this.x = 0; + this.y = 0; + this.radius = Math.min(width, height); + } + + protected override buildSetInteractiveCodeCOM(obj: ISceneGameObject, code: core.code.MethodCallCodeDOM): void { + + const { x, y, radius } = this; + + code.arg(`new Phaser.Geom.Circle(${x}, ${y}, ${radius}`); + + code.arg("Phaser.Geom.Circle.Contains"); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts index 610245d42..a3d0ec988 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts @@ -24,7 +24,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { new RectangleHitAreaOffsetToolItem(0, 0), new RectangleHitAreaOffsetToolItem(0.5, 0), new RectangleHitAreaOffsetToolItem(0, 0.5), - + new EllipseHitAreaSizeToolItem(1, 0.5), new EllipseHitAreaSizeToolItem(1, 1), new EllipseHitAreaSizeToolItem(0.5, 1), @@ -212,17 +212,14 @@ namespace phasereditor2d.scene.ui.sceneobjects { let y2 = y1 + height; const tx = obj.getWorldTransformMatrix(); - const points = [ + let points = [ [x1, y1], [x2, y1], [x2, y2], - ].map(([x, y]) => { - - return tx.transformPoint(x, y); - }).map(p => { + ] + .map(([x, y]) => tx.transformPoint(x, y)) - return args.camera.getScreenPoint(p.x, p.y); - }); + .map(p => args.camera.getScreenPoint(p.x, p.y)); const [p1, p2, p3] = points; @@ -246,10 +243,31 @@ namespace phasereditor2d.scene.ui.sceneobjects { ctx.stroke(); ctx.closePath(); + // dashed rect + + x1 = x - origin.displayOriginX - width / 2; + y1 = y - origin.displayOriginY - height / 2; + x2 = x1 + width; + y2 = y1 + height; + + points = [ + [x1, y1], + [x2, y1], + [x2, y2], + [x1, y2], + [x1, y1] + ] + .map(([x, y]) => tx.transformPoint(x, y)) + + .map(p => args.camera.getScreenPoint(p.x, p.y)); + + ctx.setLineDash([1, 1]); + this.drawPath(ctx, points); + ctx.restore(); } - private drawEllipse(ctx: CanvasRenderingContext2D, x:number, y:number, w: number, h: number, angle: number) { + private drawEllipse(ctx: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, angle: number) { const rx = w / 2; const ry = h / 2; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaComponent.ts index 1c4bbeaf0..a406d54cf 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaComponent.ts @@ -8,11 +8,13 @@ namespace phasereditor2d.scene.ui.sceneobjects { static y = HitAreaProperty(EllipseHitAreaComponent, "y", "Y", "phaser:Phaser.Geom.Ellipse.y", 0); static width = HitAreaProperty(EllipseHitAreaComponent, "width", "W", "phaser:Phaser.Geom.Ellipse.width", 0); static height = HitAreaProperty(EllipseHitAreaComponent, "height", "H", "phaser:Phaser.Geom.Ellipse.height", 0); + static position: IPropertyXY = { label: "Offset", x: this.x, y: this.y }; + static size: IPropertyXY = { label: "Size", x: this.width, diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaOffsetToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaOffsetToolItem.ts index 4faf947a9..c43b11763 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaOffsetToolItem.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaOffsetToolItem.ts @@ -1,15 +1,15 @@ namespace phasereditor2d.scene.ui.sceneobjects { export class EllipseHitAreaOffsetToolItem extends BaseHitAreaOffsetToolItem { - + constructor(x: IAxisFactor, y: IAxisFactor) { super(HitAreaShape.ELLIPSE, x, y); } protected getToolOrigin(obj: ISceneGameObject): { originX: number; originY: number; } { - - return obj.getEditorSupport().computeOrigin(); - } + + return { originX: 0.5, originY: 0.5 }; + } protected getOffsetProperties(obj: ISceneGameObject): { x: IProperty; y: IProperty; } { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaSizeToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaSizeToolItem.ts index cea520b10..3574a28db 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaSizeToolItem.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaSizeToolItem.ts @@ -9,8 +9,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { } protected getToolOrigin(obj: ISceneGameObject): { originX: number; originY: number; } { - - return obj.getEditorSupport().computeOrigin(); + + return { originX: 0.5, originY: 0.5 }; } private getHitAreaComp(obj: ISceneGameObject) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts index fbcadfb06..24c4b73a3 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts @@ -2,8 +2,9 @@ namespace phasereditor2d.scene.ui.sceneobjects { export enum HitAreaShape { NONE = "NONE", - ELLIPSE = "ELLIPSE", - RECTANGLE = "RECTANGLE" + RECTANGLE = "RECTANGLE", + CIRCLE = "CIRCLE", + ELLIPSE = "ELLIPSE" } function getComp(obj: ISceneGameObject) { @@ -46,6 +47,10 @@ namespace phasereditor2d.scene.ui.sceneobjects { case HitAreaShape.RECTANGLE: return RectangleHitAreaComponent.getRectangleComponent(obj); + + case HitAreaShape.CIRCLE: + + return CircleHitAreaComponent.getCircleComponent(obj); case HitAreaShape.ELLIPSE: From 617333c1b05649028c306dc9ff0a4549b1b9e10b Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 11 May 2023 17:59:26 -0400 Subject: [PATCH 45/59] Adds the circle hit area tool. --- .../phasereditor2d.scene/src/ScenePlugin.ts | 1 + .../hitArea/CircleHitAreaComponent.ts | 2 +- .../hitArea/CircleHitAreaOffsetToolItem.ts | 47 ++++++++++ .../hitArea/CircleHitAreaSection.ts | 40 ++++++++ .../hitArea/CircleHitAreaSizeOperation.ts | 30 ++++++ .../hitArea/CircleHitAreaSizeToolItem.ts | 60 ++++++++++++ .../hitArea/CircleOffsetOperation.ts | 31 +++++++ .../sceneobjects/hitArea/EditHitAreaTool.ts | 92 +++++++++++++------ .../hitArea/EllipseHitAreaSection.ts | 2 - .../sceneobjects/hitArea/HitAreaComponent.ts | 1 + 10 files changed, 274 insertions(+), 32 deletions(-) create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaOffsetToolItem.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaSection.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaSizeOperation.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaSizeToolItem.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleOffsetOperation.ts diff --git a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts index 09874d535..ffb1911fa 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts @@ -376,6 +376,7 @@ namespace phasereditor2d.scene { page => new ui.sceneobjects.ThreeSliceSection(page), page => new ui.sceneobjects.HitAreaSection(page), page => new ui.sceneobjects.RectangleHitAreaSection(page), + page => new ui.sceneobjects.CircleHitAreaSection(page), page => new ui.sceneobjects.EllipseHitAreaSection(page), page => new ui.sceneobjects.ArcadeBodySection(page), page => new ui.sceneobjects.ArcadeGeometrySection(page), diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaComponent.ts index f6183d05b..66d33efa5 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaComponent.ts @@ -46,7 +46,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { const { x, y, radius } = this; - code.arg(`new Phaser.Geom.Circle(${x}, ${y}, ${radius}`); + code.arg(`new Phaser.Geom.Circle(${x}, ${y}, ${radius})`); code.arg("Phaser.Geom.Circle.Contains"); } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaOffsetToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaOffsetToolItem.ts new file mode 100644 index 000000000..d2de5b424 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaOffsetToolItem.ts @@ -0,0 +1,47 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + export class CircleHitAreaOffsetToolItem extends BaseHitAreaOffsetToolItem { + + constructor() { + super(HitAreaShape.CIRCLE, 0.5, 0.5); + } + + protected getToolOrigin(obj: ISceneGameObject): { originX: number; originY: number; } { + + return { originX: 0.5, originY: 0.5 }; + } + + protected getOffsetProperties(obj: ISceneGameObject): { x: IProperty; y: IProperty; } { + + const { x, y } = CircleHitAreaComponent; + + return { x, y }; + } + + protected getSizeProperties(obj: ISceneGameObject): { width: IProperty; height: IProperty; } { + + const { radius } = CircleHitAreaComponent; + + const prop = {...radius}; + + prop.getValue = obj => radius.getValue(obj) * 2; + + return { width: prop, height: prop }; + } + + protected getKeyData(): string { + + return "CircleHitAreaOffsetToolItem"; + } + + protected getOffsetSectionId(): string { + + return CircleHitAreaSection.ID; + } + + protected createStopDragOperation(args: editor.tools.ISceneToolDragEventArgs): colibri.ui.ide.undo.Operation { + + return new CircleOffsetOperation(args, obj => this.getInitialValue(obj)); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaSection.ts new file mode 100644 index 000000000..528f9b455 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaSection.ts @@ -0,0 +1,40 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + import controls = colibri.ui.controls; + + export class CircleHitAreaSection extends SceneGameObjectSection { + + static ID = "phasereditor2d.scene.ui.sceneobjects.CircleHitAreaSection"; + + constructor(page: controls.properties.PropertyPage) { + super(page, CircleHitAreaSection.ID, "Hit Area (Circle)"); + } + + createMenu(menu: controls.Menu) { + + this.createToolMenuItem(menu, EditHitAreaTool.ID); + + super.createMenu(menu); + } + + createForm(parent: HTMLDivElement) { + + const comp = this.createGridElementWithPropertiesXY(parent); + + this.createPropertyXYRow(comp, CircleHitAreaComponent.position, false); + + this.createPropertyFloatRow(comp, CircleHitAreaComponent.radius, false) + .style.gridColumn = "span 4"; + } + + canEdit(obj: any, n: number): boolean { + + return HitAreaComponent.hasHitAreaShape(obj, HitAreaShape.CIRCLE); + } + + canEditNumber(n: number): boolean { + + return n > 0; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaSizeOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaSizeOperation.ts new file mode 100644 index 000000000..bcb1ddbe8 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaSizeOperation.ts @@ -0,0 +1,30 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + export class CircleHitAreaSizeOperation extends editor.tools.SceneToolOperation<{ x: number, y: number }> { + + constructor( + toolArgs: editor.tools.ISceneToolContextArgs, + private getInitialSize: (obj: ISceneGameObject) => { x: number, y: number }) { + + super(toolArgs); + } + + getInitialValue(obj: ISceneGameObject): { x: number; y: number; } { + + return this.getInitialSize(obj); + } + + getFinalValue(obj: ISceneGameObject): { x: number; y: number; } { + + return { + x: CircleHitAreaComponent.radius.getValue(obj), + y: CircleHitAreaComponent.radius.getValue(obj) + }; + } + + setValue(obj: ISceneGameObject, value: { x: number; y: number; }) { + + CircleHitAreaComponent.radius.setValue(obj, value.x / 2); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaSizeToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaSizeToolItem.ts new file mode 100644 index 000000000..05f20b973 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaSizeToolItem.ts @@ -0,0 +1,60 @@ +/// + +namespace phasereditor2d.scene.ui.sceneobjects { + + export class CircleHitAreaSizeToolItem extends BaseHitAreaSizeToolItem { + + constructor() { + super(HitAreaShape.CIRCLE, 1, 0.5); + } + + protected getToolOrigin(obj: ISceneGameObject): { originX: number; originY: number; } { + + return { originX: 0.5, originY: 0.5 }; + } + + protected computeSize(obj: ISceneGameObject): { width: number; height: number; } { + + const { radius } = CircleHitAreaComponent.getCircleComponent(obj); + + return { width: radius * 2, height: radius * 2 }; + } + + protected getHitAreaOffset(obj: ISceneGameObject): { x: number; y: number; } { + + const { x, y } = CircleHitAreaComponent.getCircleComponent(obj); + + return { x, y }; + } + + protected getDataKey(): string { + + return "CircleHitAreaSizeToolItem"; + } + + protected getHitAreaSectionId(): string { + + return CircleHitAreaSection.ID; + } + + protected onDragValues(obj: ISceneGameObject, changeX: boolean, changeY: boolean, width: number, height: number) { + + const comp = CircleHitAreaComponent.getCircleComponent(obj); + + if (changeX) { + + comp.radius = width / 2; + } + + if (changeY) { + + comp.radius = height / 2; + } + } + + protected createStopDragOperation(args: editor.tools.ISceneToolDragEventArgs): colibri.ui.ide.undo.Operation { + + return new CircleHitAreaSizeOperation(args, obj => this.getInitialSize(obj)); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleOffsetOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleOffsetOperation.ts new file mode 100644 index 000000000..2c78968b2 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleOffsetOperation.ts @@ -0,0 +1,31 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + export class CircleOffsetOperation extends editor.tools.SceneToolOperation<{ x: number, y: number }> { + + constructor( + toolArgs: editor.tools.ISceneToolContextArgs, + private getInitialOffset: (obj: ISceneGameObject) => { x: number, y: number }) { + + super(toolArgs); + } + + getInitialValue(obj: ISceneGameObject): { x: number; y: number; } { + + return this.getInitialOffset(obj); + } + + getFinalValue(obj: ISceneGameObject): { x: number; y: number; } { + + return { + x: CircleHitAreaComponent.x.getValue(obj), + y: CircleHitAreaComponent.y.getValue(obj) + }; + } + + setValue(obj: ISceneGameObject, value: { x: number; y: number; }) { + + CircleHitAreaComponent.x.setValue(obj, value.x); + CircleHitAreaComponent.y.setValue(obj, value.y); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts index a3d0ec988..8575ebd80 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts @@ -14,7 +14,14 @@ namespace phasereditor2d.scene.ui.sceneobjects { RectangleHitAreaComponent.x, RectangleHitAreaComponent.y, RectangleHitAreaComponent.width, - RectangleHitAreaComponent.height + RectangleHitAreaComponent.height, + EllipseHitAreaComponent.x, + EllipseHitAreaComponent.y, + EllipseHitAreaComponent.width, + EllipseHitAreaComponent.height, + CircleHitAreaComponent.x, + CircleHitAreaComponent.y, + CircleHitAreaComponent.radius ); this.addItems( @@ -31,6 +38,9 @@ namespace phasereditor2d.scene.ui.sceneobjects { new EllipseHitAreaOffsetToolItem(0, 0), new EllipseHitAreaOffsetToolItem(0.5, 0), new EllipseHitAreaOffsetToolItem(0, 0.5), + + new CircleHitAreaSizeToolItem(), + new CircleHitAreaOffsetToolItem() ); } @@ -121,6 +131,10 @@ namespace phasereditor2d.scene.ui.sceneobjects { this.renderEllipse(obj, args, ctx); + } else if (shape === HitAreaShape.CIRCLE) { + + this.renderCircle(obj, args, ctx); + } else { this.renderRect(obj, args, ctx); @@ -194,6 +208,35 @@ namespace phasereditor2d.scene.ui.sceneobjects { private renderEllipse(obj: Sprite, args: editor.tools.ISceneToolRenderArgs, ctx: CanvasRenderingContext2D) { + const comp = EllipseHitAreaComponent.getEllipseComponent(obj); + + const { x, y, width, height } = comp; + + this.renderEllipseHelper(args, ctx, obj, x, y, width, height, true); + } + + private drawEllipse(ctx: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, angle: number) { + + const rx = w / 2; + const ry = h / 2; + + ctx.ellipse(x, y, rx, ry, Phaser.Math.DegToRad(angle), 0, Math.PI * 2); + } + + private renderCircle(obj: Sprite, args: editor.tools.ISceneToolRenderArgs, ctx: CanvasRenderingContext2D) { + + const comp = CircleHitAreaComponent.getCircleComponent(obj); + + const { x, y, radius } = comp; + + const width = radius * 2; + const height = width; + + this.renderEllipseHelper(args, ctx, obj, x, y, width, height, false); + } + + private renderEllipseHelper(args: editor.tools.ISceneToolRenderArgs, ctx: CanvasRenderingContext2D, obj: Sprite, x: number, y: number, width: number, height: number, dashedRect: boolean) { + const origin = obj.getEditorSupport().computeDisplayOrigin(); if (obj instanceof Container) { @@ -202,10 +245,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { origin.displayOriginY = 0; } - const comp = EllipseHitAreaComponent.getEllipseComponent(obj); - - const { x, y, width, height } = comp; - let x1 = x - origin.displayOriginX; let y1 = y - origin.displayOriginY; let x2 = x1 + width; @@ -243,36 +282,31 @@ namespace phasereditor2d.scene.ui.sceneobjects { ctx.stroke(); ctx.closePath(); - // dashed rect + if (dashedRect) { - x1 = x - origin.displayOriginX - width / 2; - y1 = y - origin.displayOriginY - height / 2; - x2 = x1 + width; - y2 = y1 + height; + // dashed rect - points = [ - [x1, y1], - [x2, y1], - [x2, y2], - [x1, y2], - [x1, y1] - ] - .map(([x, y]) => tx.transformPoint(x, y)) + x1 = x - origin.displayOriginX - width / 2; + y1 = y - origin.displayOriginY - height / 2; + x2 = x1 + width; + y2 = y1 + height; - .map(p => args.camera.getScreenPoint(p.x, p.y)); - - ctx.setLineDash([1, 1]); - this.drawPath(ctx, points); + points = [ + [x1, y1], + [x2, y1], + [x2, y2], + [x1, y2], + [x1, y1] + ] + .map(([x, y]) => tx.transformPoint(x, y)) - ctx.restore(); - } + .map(p => args.camera.getScreenPoint(p.x, p.y)); - private drawEllipse(ctx: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, angle: number) { + ctx.setLineDash([1, 1]); + this.drawPath(ctx, points); - const rx = w / 2; - const ry = h / 2; - - ctx.ellipse(x, y, rx, ry, Phaser.Math.DegToRad(angle), 0, Math.PI * 2); + ctx.restore(); + } } } } \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaSection.ts index 0fc2e2642..1f39f4a1d 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaSection.ts @@ -27,8 +27,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { canEdit(obj: any, n: number): boolean { - console.log("here"); - return HitAreaComponent.hasHitAreaShape(obj, HitAreaShape.ELLIPSE); } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts index 24c4b73a3..b853bd4fb 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts @@ -24,6 +24,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { values: [ HitAreaShape.NONE, HitAreaShape.RECTANGLE, + HitAreaShape.CIRCLE, HitAreaShape.ELLIPSE ] }; From c8229ba6dd57ee5bb5f96ce199e51876d01dbcf7 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Fri, 12 May 2023 10:14:00 -0400 Subject: [PATCH 46/59] Fixes setting default values to the hit area. --- .../ui/sceneobjects/hitArea/CircleHitAreaComponent.ts | 6 +++--- .../ui/sceneobjects/hitArea/EllipseHitAreaComponent.ts | 4 ++-- .../src/ui/sceneobjects/hitArea/HitAreaSection.ts | 9 ++++++--- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaComponent.ts index 66d33efa5..89f1348a7 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaComponent.ts @@ -37,9 +37,9 @@ namespace phasereditor2d.scene.ui.sceneobjects { protected _setDefaultValues(width: number, height: number): void { - this.x = 0; - this.y = 0; - this.radius = Math.min(width, height); + this.x = width / 2; + this.y = height / 2; + this.radius = Math.min(width, height) / 2; } protected override buildSetInteractiveCodeCOM(obj: ISceneGameObject, code: core.code.MethodCallCodeDOM): void { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaComponent.ts index a406d54cf..ffd3fbf84 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaComponent.ts @@ -46,8 +46,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { protected _setDefaultValues(width: number, height: number): void { - this.x = 0; - this.y = 0; + this.x = width / 2; + this.y = height / 2; this.width = width; this.height = height; } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaSection.ts index c4b833311..e324bd1f2 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaSection.ts @@ -14,15 +14,18 @@ namespace phasereditor2d.scene.ui.sceneobjects { const { hitAreaShape } = HitAreaComponent; - const prop = {...hitAreaShape}; + const prop = { ...hitAreaShape }; prop.setValue = (obj, value) => { hitAreaShape.setValue(obj, value); - const comp = HitAreaComponent.getShapeComponent(obj) + if (value !== HitAreaShape.NONE) { - comp.setDefaultValues(); + const comp = HitAreaComponent.getShapeComponent(obj); + + comp.setDefaultValues(); + } } this.createPropertyEnumRow(comp, prop); From 3bbabdb58ed814f2dc56d7a333105a8ed60a2dbe Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Fri, 12 May 2023 12:05:52 -0400 Subject: [PATCH 47/59] Some advances on the polygon hit area. --- .../phasereditor2d.scene/src/ScenePlugin.ts | 1 + .../sceneobjects/GameObjectEditorSupport.ts | 3 +- .../sceneobjects/hitArea/EditHitAreaTool.ts | 47 ++- .../sceneobjects/hitArea/HitAreaComponent.ts | 20 +- .../hitArea/PolygonHitAreaComponent.ts | 57 +++ .../hitArea/PolygonHitAreaSection.ts | 37 ++ .../hitArea/PolygonHitAreaToolItem.ts | 398 ++++++++++++++++++ 7 files changed, 554 insertions(+), 9 deletions(-) create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaComponent.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaSection.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaToolItem.ts diff --git a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts index ffb1911fa..7dd6c6a5e 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts @@ -378,6 +378,7 @@ namespace phasereditor2d.scene { page => new ui.sceneobjects.RectangleHitAreaSection(page), page => new ui.sceneobjects.CircleHitAreaSection(page), page => new ui.sceneobjects.EllipseHitAreaSection(page), + page => new ui.sceneobjects.PolygonHitAreaSection(page), page => new ui.sceneobjects.ArcadeBodySection(page), page => new ui.sceneobjects.ArcadeGeometrySection(page), page => new ui.sceneobjects.ArcadeBodyMovementSection(page), diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts index 76b6fc895..05874997d 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts @@ -66,7 +66,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { new HitAreaComponent(obj), new RectangleHitAreaComponent(obj), new CircleHitAreaComponent(obj), - new EllipseHitAreaComponent(obj) + new EllipseHitAreaComponent(obj), + new PolygonHitAreaComponent(obj) ); } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts index 8575ebd80..a4d53690e 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts @@ -40,7 +40,9 @@ namespace phasereditor2d.scene.ui.sceneobjects { new EllipseHitAreaOffsetToolItem(0, 0.5), new CircleHitAreaSizeToolItem(), - new CircleHitAreaOffsetToolItem() + new CircleHitAreaOffsetToolItem(), + + new PolygonHitAreaToolItem() ); } @@ -135,6 +137,10 @@ namespace phasereditor2d.scene.ui.sceneobjects { this.renderCircle(obj, args, ctx); + } else if (shape === HitAreaShape.POLYGON) { + + this.renderPolygon(obj, args, ctx); + } else { this.renderRect(obj, args, ctx); @@ -143,6 +149,45 @@ namespace phasereditor2d.scene.ui.sceneobjects { ctx.restore(); } + private renderPolygon(obj: Sprite, args: editor.tools.ISceneToolRenderArgs, ctx: CanvasRenderingContext2D) { + + const origin = obj.getEditorSupport().computeDisplayOrigin(); + + if (obj instanceof Container) { + + origin.displayOriginX = 0; + origin.displayOriginY = 0; + } + + const comp = PolygonHitAreaComponent.getPolygonComponent(obj); + + const tx = obj.getWorldTransformMatrix(); + + const points: Array = comp.vectors + .map(p => new Phaser.Math.Vector2( + p.x - origin.displayOriginX, + p.y - origin.displayOriginY)) + + .map(p => tx.transformPoint(p.x, p.y)) + + .map(p => args.camera.getScreenPoint(p.x, p.y)); + + // close the path + points.push(points[0]); + + ctx.save(); + + ctx.strokeStyle = "black"; + ctx.lineWidth = 3; + this.drawPath(ctx, points); + + ctx.strokeStyle = EditHitAreaTool.TOOL_COLOR; + ctx.lineWidth = 1; + this.drawPath(ctx, points); + + ctx.restore(); + } + private renderRect(obj: Sprite, args: editor.tools.ISceneToolRenderArgs, ctx: CanvasRenderingContext2D) { const origin = obj.getEditorSupport().computeDisplayOrigin(); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts index b853bd4fb..2e61029e2 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts @@ -4,7 +4,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { NONE = "NONE", RECTANGLE = "RECTANGLE", CIRCLE = "CIRCLE", - ELLIPSE = "ELLIPSE" + ELLIPSE = "ELLIPSE", + POLYGON = "POLYGON" } function getComp(obj: ISceneGameObject) { @@ -25,7 +26,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { HitAreaShape.NONE, HitAreaShape.RECTANGLE, HitAreaShape.CIRCLE, - HitAreaShape.ELLIPSE + HitAreaShape.ELLIPSE, + HitAreaShape.POLYGON ] }; @@ -43,19 +45,23 @@ namespace phasereditor2d.scene.ui.sceneobjects { const shape = this.getShape(obj); - switch(shape) { + switch (shape) { case HitAreaShape.RECTANGLE: return RectangleHitAreaComponent.getRectangleComponent(obj); - + case HitAreaShape.CIRCLE: - return CircleHitAreaComponent.getCircleComponent(obj); + return CircleHitAreaComponent.getCircleComponent(obj); + + case HitAreaShape.ELLIPSE: + + return EllipseHitAreaComponent.getEllipseComponent(obj); - case HitAreaShape.ELLIPSE: + case HitAreaShape.POLYGON: - return EllipseHitAreaComponent.getEllipseComponent(obj); + return PolygonHitAreaComponent.getPolygonComponent(obj); } return undefined; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaComponent.ts new file mode 100644 index 000000000..3695e40b6 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaComponent.ts @@ -0,0 +1,57 @@ +/// +/// +namespace phasereditor2d.scene.ui.sceneobjects { + + export class PolygonHitAreaComponent extends BaseHitAreaComponent { + + static points = HitAreaProperty(PolygonHitAreaComponent, "points", "Points", "The polygon's points, in a string format `X1 Y1 Y2 X2...`", ""); + + public points: string; + + constructor(obj: ISceneGameObject) { + super(obj, HitAreaShape.POLYGON, [ + PolygonHitAreaComponent.points + ]); + + this.points = ""; + } + + get vectors() { + + const vectors: Phaser.Math.Vector2[] = []; + + const chunks = this.points.split(" ").map(s => s.trim()).filter(s => s.length > 0); + + for (let i = 0; i < chunks.length - 1; i += 2) { + + const x = Number.parseFloat(chunks[i]); + const y = Number.parseFloat(chunks[i + 1]); + + vectors.push(new Phaser.Math.Vector2(x, y)) + } + + return vectors; + } + + static getPolygonComponent(obj: ISceneGameObject) { + + const objES = obj.getEditorSupport(); + + const comp = objES.getComponent(PolygonHitAreaComponent) as PolygonHitAreaComponent; + + return comp; + } + + protected _setDefaultValues(w: number, h: number): void { + + this.points = `0 ${h * 0.25} ${w/2} 0 ${w} ${h * 0.25} ${w} ${h} 0 ${h}`; + } + + protected override buildSetInteractiveCodeCOM(obj: ISceneGameObject, code: core.code.MethodCallCodeDOM): void { + + code.arg(`new Phaser.Geom.Polygon("${this.points}")`); + + code.arg("Phaser.Geom.Polygon.Contains"); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaSection.ts new file mode 100644 index 000000000..5e93459f7 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaSection.ts @@ -0,0 +1,37 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + import controls = colibri.ui.controls; + + export class PolygonHitAreaSection extends SceneGameObjectSection { + + static ID = "phasereditor2d.scene.ui.sceneobjects.PolygonHitAreaSection"; + + constructor(page: controls.properties.PropertyPage) { + super(page, PolygonHitAreaSection.ID, "Hit Area (Polygon)"); + } + + createMenu(menu: controls.Menu) { + + this.createToolMenuItem(menu, EditHitAreaTool.ID); + + super.createMenu(menu); + } + + createForm(parent: HTMLDivElement) { + + const comp = this.createGridElement(parent, 3); + + this.createPropertyStringDialogRow(comp, PolygonHitAreaComponent.points, false); + } + + canEdit(obj: any, n: number): boolean { + + return HitAreaComponent.hasHitAreaShape(obj, HitAreaShape.POLYGON); + } + + canEditNumber(n: number): boolean { + + return n > 0; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaToolItem.ts new file mode 100644 index 000000000..d42b15e19 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaToolItem.ts @@ -0,0 +1,398 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + export class PolygonHitAreaToolItem + extends editor.tools.SceneToolItem implements editor.tools.ISceneToolItemXY { + private _dragging: boolean; + private _draggingIndex: number; + private _newPoint: Phaser.Math.Vector2; + private _newPointIndex: number; + private _highlightPointIndex = -1; + + constructor() { + super(); + } + + handleDoubleClick(args: editor.tools.ISceneToolContextArgs): boolean { + + if (this._highlightPointIndex >= 0) { + + const op = new editor.undo.SceneSnapshotOperation(args.editor, async () => { + + this.handleDeleteCommand(args); + }); + + args.editor.getUndoManager().add(op); + + return true; + } + + return false; + } + + handleDeleteCommand(args: editor.tools.ISceneToolContextArgs): boolean { + + if (this._highlightPointIndex >= 0) { + + const comp = PolygonHitAreaComponent.getPolygonComponent(args.objects[0]); + + const points = comp.vectors; + + if (points.length <= 3) { + + return true; + } + + const newPoints = []; + + for (let i = 0; i < points.length; i++) { + + if (i !== this._highlightPointIndex) { + + newPoints.push(points[i]); + } + } + + comp.points = newPoints.map(p => `${p.x} ${p.y}`).join(" "); + + return true; + } + + return false; + } + + getPoint(args: editor.tools.ISceneToolContextArgs): { x: number; y: number; } { + + return { x: 0, y: 0 }; + } + + render(args: editor.tools.ISceneToolRenderArgs) { + + if (args.objects.length !== 1) { + + return; + } + + let nearPoint: Phaser.Geom.Point; + let nearPointIndex: number; + + const comp = PolygonHitAreaComponent.getPolygonComponent(args.objects[0]); + + const sprite = comp.getObject() as Sprite; + + const points = this.getPolygonScreenPoints(comp); + + const ctx = args.canvasContext; + + const cursor = args.editor.getMouseManager().getMousePosition(); + + // find highlihting point + + let highlightPoint: Phaser.Math.Vector2; + let highlightPointIndex = -1; + + for (let i = 0; i < points.length; i++) { + + const point = points[i]; + + if (this.isCursorOnPoint(cursor.x, cursor.y, point)) { + + highlightPoint = point; + highlightPointIndex = i; + + break; + } + } + + + if (!highlightPoint) { + + // paint near line + + let nearLine: Phaser.Geom.Line; + let nearLineDistance = Number.MAX_VALUE; + + const line = new Phaser.Geom.Line(); + const tempPoint = new Phaser.Geom.Point(); + + for (let i = 0; i < points.length; i++) { + + const p1 = points[i]; + const p2 = points[(i + 1) % points.length]; + + line.setTo(p1.x, p1.y, p2.x, p2.y); + + Phaser.Geom.Line.GetNearestPoint(line, new Phaser.Geom.Point(cursor.x, cursor.y), tempPoint); + + const d = Phaser.Math.Distance.BetweenPoints(cursor, tempPoint); + + if (d < 10 && d < nearLineDistance) { + + const lineLength = Phaser.Geom.Line.Length(line); + const length1 = Phaser.Math.Distance.BetweenPoints(p1, tempPoint); + const length2 = Phaser.Math.Distance.BetweenPoints(p2, tempPoint); + + // check the point is inside the segment + if (length1 <= lineLength && length2 <= lineLength) { + + nearLineDistance = d; + nearPointIndex = i; + + if (nearLine) { + + nearLine.setTo(line.x1, line.y1, line.x2, line.y2); + nearPoint.setTo(tempPoint.x, tempPoint.y); + + } else { + + nearLine = new Phaser.Geom.Line(line.x1, line.y1, line.x2, line.y2); + nearPoint = new Phaser.Geom.Point(tempPoint.x, tempPoint.y); + } + } + } + } + + if (nearLine) { + + const color = args.canEdit ? "#fff" : editor.tools.SceneTool.COLOR_CANNOT_EDIT; + + // draw near line + + ctx.save(); + + ctx.translate(nearLine.x1, nearLine.y1); + + const angle = this.globalAngle(sprite); + + ctx.rotate(Phaser.Math.DegToRad(angle)); + + ctx.beginPath(); + ctx.moveTo(0, 0); + ctx.lineTo(nearLine.x2 - nearLine.x1, nearLine.y2 - nearLine.y1); + ctx.strokeStyle = color; + ctx.lineWidth = 3; + ctx.stroke(); + + ctx.restore(); + + // draw near point + + ctx.save(); + + ctx.translate(nearPoint.x, nearPoint.y); + ctx.rotate(Phaser.Math.DegToRad(this.globalAngle(sprite))); + + this.drawRect(ctx, args.canEdit ? "#faa" : editor.tools.SceneTool.COLOR_CANNOT_EDIT); + + ctx.restore(); + } + } + + // paint highlight point + + for (const point of points) { + + ctx.save(); + + ctx.translate(point.x, point.y); + + const angle = this.globalAngle(sprite); + + ctx.rotate(Phaser.Math.DegToRad(angle)); + + const color = point === highlightPoint ? "#f00" : EditHitAreaTool.TOOL_COLOR; + + this.drawRect(ctx, args.canEdit ? color : editor.tools.SceneTool.COLOR_CANNOT_EDIT); + + ctx.restore(); + } + + this._newPoint = nearPoint ? this.getPolygonLocalPoint(sprite, nearPoint) : undefined; + this._newPointIndex = nearPointIndex; + this._highlightPointIndex = highlightPointIndex; + } + + private getPolygonScreenPoints(comp: PolygonHitAreaComponent) { + + const points: Phaser.Math.Vector2[] = []; + + const worldPoint = new Phaser.Math.Vector2(0, 0); + + const obj = comp.getObject() as Sprite; + + let { displayOriginX, displayOriginY } = obj.getEditorSupport().computeDisplayOrigin(); + + displayOriginX = obj.displayWidth * 0.5; + displayOriginY = obj.displayHeight * 0.5; + + const tx = obj.getWorldTransformMatrix(); + + for (const point of comp.vectors) { + + tx.transformPoint( + point.x - displayOriginX, + point.y - displayOriginY, + worldPoint); + + const screenPoint = obj.scene.cameras.main.getScreenPoint(worldPoint.x, worldPoint.y); + + points.push(screenPoint); + } + + return points; + } + + getPolygonLocalPoint(polygon: Sprite, point: { x: number, y: number }) { + + const camera = polygon.scene.cameras.main; + point = camera.getWorldPoint2(point.x, point.y); + + const localPoint = polygon.getWorldTransformMatrix().applyInverse(point.x, point.y); + localPoint.x += polygon.displayOriginX; + localPoint.y += polygon.displayOriginY; + + return localPoint; + } + + containsPoint(args: editor.tools.ISceneToolDragEventArgs): boolean { + + if (this._newPoint) { + + return true; + } + + const points = this.getPolygonScreenPoints(PolygonHitAreaComponent.getPolygonComponent(args.objects[0])); + + for (const point of points) { + + if (this.isCursorOnPoint(args.x, args.y, point)) { + + return true; + } + } + + return false; + } + + private isCursorOnPoint(cursorX: number, cursorY: number, point: { x: number, y: number }) { + + return cursorX >= point.x - 5 && cursorX <= point.x + 5 + && cursorY >= point.y - 5 && cursorY <= point.y + 5; + } + + private _startDragTime = 0; + + onStartDrag(args: editor.tools.ISceneToolDragEventArgs): void { + + this._startDragTime = Date.now(); + + const comp = PolygonHitAreaComponent.getPolygonComponent(args.objects[0]); + + if (this._newPoint) { + + const points = comp.vectors; + + let newPoints: { x: number, y: number }[] = []; + + for (let i = 0; i < points.length; i++) { + + const point = points[i]; + + newPoints.push(point); + + if (this._newPointIndex === i) { + + newPoints.push(this._newPoint); + } + } + + comp.points = newPoints.map(p => `${p.x} ${p.y}`).join(" "); + } + + const cursor = args.editor.getMouseManager().getMousePosition(); + + const points = this.getPolygonScreenPoints(comp); + + for (let i = 0; i < points.length; i++) { + + const point = points[i]; + + if (this.isCursorOnPoint(cursor.x, cursor.y, point)) { + + comp.getObject().setData("PolygonHitAreaToolItem", { + initPoints: comp.points + }); + + this._draggingIndex = i; + this._dragging = true; + + break; + } + } + } + + static getInitialPoints(obj: ISceneGameObject) { + + return obj.getData("PolygonHitAreaToolItem").initPoints; + } + + isValidFor(objects: ISceneGameObject[]): boolean { + + const obj = objects[0]; + + if (obj) { + + if (HitAreaComponent.hasHitArea(obj) && HitAreaComponent.hasHitAreaShape(obj, HitAreaShape.POLYGON)) { + + return true; + } + } + + return false; + } + + onDrag(args: editor.tools.ISceneToolDragEventArgs): void { + + if (!this._dragging) { + + return; + } + + const comp = PolygonHitAreaComponent.getPolygonComponent(args.objects[0]); + + const points = comp.vectors; + + const point = points[this._draggingIndex]; + + const newPoint = args.editor.getScene().getCamera().getWorldPoint(args.x, args.y); + + const sprite = comp.getObject() as Sprite; + + sprite.getWorldTransformMatrix().applyInverse(newPoint.x, newPoint.y, newPoint); + + const {displayOriginX, displayOriginY} = sprite.getEditorSupport().computeDisplayOrigin(); + + point.x = newPoint.x + displayOriginX; + point.y = newPoint.y + displayOriginY; + + comp.points = points.map(p => `${p.x} ${p.y}`).join(" "); + + args.editor.updateInspectorViewSection(PolygonSection.SECTION_ID); + } + + onStopDrag(args: editor.tools.ISceneToolDragEventArgs): void { + + this._newPoint = undefined; + + if (this._dragging) { + + if (Date.now() - this._startDragTime > 300) { + + args.editor.getUndoManager().add(new PolygonOperation(args)); + + } + + this._dragging = false; + } + } + } +} \ No newline at end of file From 2c1cae84e5c09de85733fca5950f34e62c97fb17 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Mon, 15 May 2023 21:26:49 -0400 Subject: [PATCH 48/59] Adds polygon input shape. --- .../sceneobjects/hitArea/EditHitAreaTool.ts | 32 +++++++++++++++++-- .../hitArea/PolygonHitAreaToolItem.ts | 7 ++-- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts index a4d53690e..5bee58703 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts @@ -5,6 +5,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { static ID = "phasereditor2d.scene.ui.sceneobjects.EditHitAreaTool"; static TOOL_COLOR = "orange"; + private _polygonToolItem: PolygonHitAreaToolItem; constructor() { super({ @@ -42,10 +43,35 @@ namespace phasereditor2d.scene.ui.sceneobjects { new CircleHitAreaSizeToolItem(), new CircleHitAreaOffsetToolItem(), - new PolygonHitAreaToolItem() + this._polygonToolItem = new PolygonHitAreaToolItem() ); } + handleDoubleClick(args: editor.tools.ISceneToolContextArgs): boolean { + + if (this._polygonToolItem.isValidFor(args.objects)) { + + return this._polygonToolItem.handleDoubleClick(args); + } + + return super.handleDoubleClick(args); + } + + handleDeleteCommand(args: editor.tools.ISceneToolContextArgs): boolean { + + if (this._polygonToolItem.isValidFor(args.objects)) { + + return this._polygonToolItem.handleDeleteCommand(args); + } + + return super.handleDeleteCommand(args); + } + + requiresRepaintOnMouseMove() { + + return true; + } + protected getProperties(obj?: any): IProperty[] { if (HitAreaComponent.hasHitArea(obj)) { @@ -165,8 +191,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { const points: Array = comp.vectors .map(p => new Phaser.Math.Vector2( - p.x - origin.displayOriginX, - p.y - origin.displayOriginY)) + p.x - origin.displayOriginX, + p.y - origin.displayOriginY)) .map(p => tx.transformPoint(p.x, p.y)) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaToolItem.ts index d42b15e19..45336587a 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaToolItem.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaToolItem.ts @@ -219,10 +219,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { const obj = comp.getObject() as Sprite; - let { displayOriginX, displayOriginY } = obj.getEditorSupport().computeDisplayOrigin(); - - displayOriginX = obj.displayWidth * 0.5; - displayOriginY = obj.displayHeight * 0.5; + const { displayOriginX, displayOriginY } = obj.getEditorSupport().computeDisplayOrigin(); const tx = obj.getWorldTransformMatrix(); @@ -376,7 +373,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { comp.points = points.map(p => `${p.x} ${p.y}`).join(" "); - args.editor.updateInspectorViewSection(PolygonSection.SECTION_ID); + args.editor.updateInspectorViewSection(PolygonHitAreaSection.ID); } onStopDrag(args: editor.tools.ISceneToolDragEventArgs): void { From cc97994563106d1716eb05966303427d1de78eeb Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Mon, 15 May 2023 21:33:46 -0400 Subject: [PATCH 49/59] Implements missing polygon hit area undo operation. --- CHANGELOG.MD | 1 + .../hitArea/PolygonHitAreaToolItem.ts | 2 +- .../object/tools/PolygonHitAreaOperation.ts | 24 +++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/tools/PolygonHitAreaOperation.ts diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 8417d5000..aaaafe17d 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -8,6 +8,7 @@ * A new game object scope: LOCAL. The LOCAL scope is now the default scope for objects and has the same meaning of METHOD scope before. Now the METHOD scope forces the creation of a variable for the object. * Auto computes the middle-private nested prefabs. It doesn't require to declare a parent of a nested prefab as nested prefab. * Improves Outline elements tagging. It uses tahs like `#prefab_inst` `#nested_prefab_inst` `#scope_local` `#scope_nested_prefab`... So you can search for it in the Outline filter box. +* Allows enabling input in objects. ## v3.60.3 - Apr 27, 2023 diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaToolItem.ts index 45336587a..6b55224ce 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaToolItem.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaToolItem.ts @@ -384,7 +384,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { if (Date.now() - this._startDragTime > 300) { - args.editor.getUndoManager().add(new PolygonOperation(args)); + args.editor.getUndoManager().add(new PolygonHitAreaOperation(args)); } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/tools/PolygonHitAreaOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/tools/PolygonHitAreaOperation.ts new file mode 100644 index 000000000..6b7fc3008 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/tools/PolygonHitAreaOperation.ts @@ -0,0 +1,24 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + export class PolygonHitAreaOperation extends editor.tools.SceneToolOperation { + + getInitialValue(obj: any) { + + return PolygonHitAreaToolItem.getInitialPoints(obj); + } + + getFinalValue(obj: any) { + + const comp = PolygonHitAreaComponent.getPolygonComponent(obj); + + return comp.points; + } + + setValue(obj: any, value: string) { + + const comp = PolygonHitAreaComponent.getPolygonComponent(obj); + + comp.points = value; + } + } +} \ No newline at end of file From c8aba01fc767a84c41324fcb1297996af1832e76 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Mon, 15 May 2023 22:08:55 -0400 Subject: [PATCH 50/59] Adds the Name parameter to the Variable section of a nested prefab instance #282 --- CHANGELOG.MD | 1 + .../src/core/code/SceneCodeDOMBuilder.ts | 10 +++---- .../NestedPrefabObjectVariableSection.ts | 30 +++++++++++++++++-- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index aaaafe17d..534c421fb 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -9,6 +9,7 @@ * Auto computes the middle-private nested prefabs. It doesn't require to declare a parent of a nested prefab as nested prefab. * Improves Outline elements tagging. It uses tahs like `#prefab_inst` `#nested_prefab_inst` `#scope_local` `#scope_nested_prefab`... So you can search for it in the Outline filter box. * Allows enabling input in objects. +* Adds the Name parameter to the Variable section of a nested prefab instance. ## v3.60.3 - Apr 27, 2023 diff --git a/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts b/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts index ccb9aa887..e24e970e7 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts @@ -639,7 +639,7 @@ namespace phasereditor2d.scene.core.code { private addCreateObjectCodeOfNestedPrefab(obj: ISceneGameObject, createMethodDecl: MethodDeclCodeDOM, lazyStatements: CodeDOM[]) { - const varname = this.getPrefabInstanceVarName(obj); + const varname = SceneCodeDOMBuilder.getPrefabInstanceVarName(obj); const result = this.buildSetObjectProperties({ obj, @@ -687,7 +687,7 @@ namespace phasereditor2d.scene.core.code { && objParent === this._scene.getPrefabObject(); parentVarName = parentIsPrefabObject ? "this" - : this.getPrefabInstanceVarName(objParent); + : SceneCodeDOMBuilder.getPrefabInstanceVarName(objParent); } // the script nodes require using the varname of the parents @@ -881,7 +881,7 @@ namespace phasereditor2d.scene.core.code { .join(" & "); } - private getPrefabInstanceVarName(obj: ISceneGameObject): string { + static getPrefabInstanceVarName(obj: ISceneGameObject): string { const objES = obj.getEditorSupport(); @@ -904,7 +904,7 @@ namespace phasereditor2d.scene.core.code { return varName; } - private findPrefabInstanceWhereTheGivenObjectIsDefined(obj: ui.sceneobjects.ISceneGameObject): ui.sceneobjects.ISceneGameObject { + private static findPrefabInstanceWhereTheGivenObjectIsDefined(obj: ui.sceneobjects.ISceneGameObject): ui.sceneobjects.ISceneGameObject { const objES = obj.getEditorSupport(); @@ -919,7 +919,7 @@ namespace phasereditor2d.scene.core.code { return this.findPrefabInstanceOfFile(parent, objPrefabFile); } - private findPrefabInstanceOfFile(obj: ISceneGameObject, targetPrefaFile: io.FilePath): ISceneGameObject { + private static findPrefabInstanceOfFile(obj: ISceneGameObject, targetPrefaFile: io.FilePath): ISceneGameObject { const finder = ScenePlugin.getInstance().getSceneFinder(); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/NestedPrefabObjectVariableSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/NestedPrefabObjectVariableSection.ts index c99fa52c8..2abbdc15d 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/NestedPrefabObjectVariableSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/NestedPrefabObjectVariableSection.ts @@ -19,8 +19,34 @@ namespace phasereditor2d.scene.ui.sceneobjects { const comp = this.createGridElement(parent, 2); - const { btn } = GameObjectVariableSection.createTypeEditor(this, comp); - btn.disabled = true; + { + // Name + + this.createLabel(comp, "Name", "The name of the variable associated to this object. This name is used by the compiler."); + + const text = this.createText(comp, true); + + this.addUpdater(() => { + + text.value = this.flatValues_StringOneOrNothing( + this.getSelection() + .map(obj => this.getVarName(obj))); + }) + } + + { + // Type + + const { btn } = GameObjectVariableSection.createTypeEditor(this, comp); + btn.disabled = true; + } + } + + private getVarName(obj: ISceneGameObject) { + + const varName = core.code.SceneCodeDOMBuilder.getPrefabInstanceVarName(obj); + + return varName; } canEdit(obj: any, n: number): boolean { From 89a9cd93e1c658015bedfaac024435831a83f24b Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Mon, 15 May 2023 22:43:29 -0400 Subject: [PATCH 51/59] #282 Use full nested prefab path in user object variable properties. --- CHANGELOG.MD | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 534c421fb..65b4fca90 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -10,6 +10,7 @@ * Improves Outline elements tagging. It uses tahs like `#prefab_inst` `#nested_prefab_inst` `#scope_local` `#scope_nested_prefab`... So you can search for it in the Outline filter box. * Allows enabling input in objects. * Adds the Name parameter to the Variable section of a nested prefab instance. +* #282 Use full nested prefab path in user object variable properties. ## v3.60.3 - Apr 27, 2023 From 9b8e6eb37995328dc4ef24b0b6729d50b7b010ca Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Mon, 15 May 2023 22:43:36 -0400 Subject: [PATCH 52/59] #282 Use full nested prefab path in user object variable properties. --- .../userProperties/ObjectVarPropertyType.ts | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/ObjectVarPropertyType.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/ObjectVarPropertyType.ts index 398158fc1..5c0c3023c 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/ObjectVarPropertyType.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/ObjectVarPropertyType.ts @@ -55,11 +55,16 @@ namespace phasereditor2d.scene.ui.sceneobjects { protected valueToString(viewer: colibri.ui.controls.viewers.TreeViewer, value: any): string { - const support = EditorSupport.getEditorSupport(value); + const objES = EditorSupport.getEditorSupport(value); - if (support) { + if (objES) { - return support.getLabel(); + if (objES instanceof GameObjectEditorSupport && objES.isNestedPrefabInstance()) { + + return core.code.SceneCodeDOMBuilder.getPrefabInstanceVarName(value); + } + + return objES.getLabel(); } return viewer.getLabelProvider().getLabel(value); @@ -76,14 +81,31 @@ namespace phasereditor2d.scene.ui.sceneobjects { const foundElement = [undefined]; - scene.visitAll(obj => { + scene.visitAllAskChildren(obj => { if (!foundElement[0]) { - if (obj.getEditorSupport().getLabel() === value) { + const objES = obj.getEditorSupport(); + + if (objES.isNestedPrefabInstance()) { + + const objVarName = core.code.SceneCodeDOMBuilder.getPrefabInstanceVarName(obj); + + if (objVarName === value) { + + foundElement[0] = obj; + + return false; + } + + } else if (core.code.formatToValidVarName(objES.getLabel()) === value) { foundElement[0] = obj; + + return false; } + + return true; } }); From 75fcf12dbb046e882fe0bc2bc2b7631f2212e7e1 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Mon, 15 May 2023 22:44:09 -0400 Subject: [PATCH 53/59] Updates changelog. --- CHANGELOG.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 65b4fca90..f7b06f404 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -10,7 +10,7 @@ * Improves Outline elements tagging. It uses tahs like `#prefab_inst` `#nested_prefab_inst` `#scope_local` `#scope_nested_prefab`... So you can search for it in the Outline filter box. * Allows enabling input in objects. * Adds the Name parameter to the Variable section of a nested prefab instance. -* #282 Use full nested prefab path in user object variable properties. +* [#282](https://github.com/PhaserEditor2D/PhaserEditor2D-v3/issues/282) Use full nested prefab path in user object variable properties. ## v3.60.3 - Apr 27, 2023 From 0bcd510f2fcdc08d54e3bd1d637dc8090ee2f7c3 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 16 May 2023 10:13:25 -0400 Subject: [PATCH 54/59] Exclude script node prefabs from the Blocks view. --- .../SceneEditorBlocksContentProvider.ts | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksContentProvider.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksContentProvider.ts index 23486a86e..0b19af699 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksContentProvider.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksContentProvider.ts @@ -41,14 +41,11 @@ namespace phasereditor2d.scene.ui.blocks { getRoots(input: any) { - const sceneFinder = ScenePlugin.getInstance().getSceneFinder(); - if (this._editor.getScene().isScriptNodePrefabScene()) { return [ sceneobjects.ScriptNodeExtension.getInstance(), - ...this.getSceneFiles("prefabs") - .filter(f => sceneFinder.isScriptPrefabFile(f)) + ...this.getPrefabFiles("scripts") ]; } @@ -66,7 +63,7 @@ namespace phasereditor2d.scene.ui.blocks { if (groupingType === grouping.GROUP_ASSETS_BY_LOCATION) { const files = colibri.ui.ide.FileUtils.distinct( - this.getSceneFiles("prefabs").map(f => f.getParent())); + this.getPrefabFiles("prefabs").map(f => f.getParent())); return files; } @@ -111,7 +108,7 @@ namespace phasereditor2d.scene.ui.blocks { return [ BUILTIN_SECTION, ...colibri.ui.ide.FileUtils.distinct([ - ...this.getSceneFiles().map(f => f.getParent()), + ...this.getPrefabFiles().map(f => f.getParent()), ...packFolders]) ] } @@ -119,21 +116,26 @@ namespace phasereditor2d.scene.ui.blocks { return []; } - private getSceneFiles(sceneType: "prefabs" | "all" = "all") { + private getPrefabFiles(sceneType: "prefabs" | "scripts" = "prefabs") { const finder = ScenePlugin.getInstance().getSceneFinder(); - let files = (sceneType === "prefabs" ? finder.getPrefabFiles() : finder.getSceneFiles()); + let files: io.FilePath[] = []; - files = files.filter(file => SceneMaker.acceptDropFile(file, this._editor.getInput())); + switch (sceneType) { + + case "prefabs": - files.sort((a, b) => { + files = finder.getPrefabFiles().filter(f => !finder.isScriptPrefabFile(f)); + break; - const aa = finder.isScriptPrefabFile(a)? 1 : 0; - const bb = finder.isScriptPrefabFile(b)? 1 : 0; + case "scripts": - return aa - bb; - }); + files = finder.getPrefabFiles().filter(f => finder.isScriptPrefabFile(f)); + break; + } + + files = files.filter(file => SceneMaker.acceptDropFile(file, this._editor.getInput())); return files; } @@ -144,7 +146,7 @@ namespace phasereditor2d.scene.ui.blocks { const finder = ScenePlugin.getInstance().getSceneFinder(); - return this.getSceneFiles().filter(file => finder.getScenePhaserType(file) === parent.getPhaserType()); + return this.getPrefabFiles().filter(file => finder.getScenePhaserType(file) === parent.getPhaserType()); } if (parent instanceof pack.core.AssetPack) { @@ -190,7 +192,7 @@ namespace phasereditor2d.scene.ui.blocks { case PREFAB_SECTION: - const files = this.getSceneFiles(); + const files = this.getPrefabFiles(); return files; } @@ -205,10 +207,10 @@ namespace phasereditor2d.scene.ui.blocks { if (tabSection === TAB_SECTION_PREFABS) { - return this.getSceneFiles("prefabs").filter(f => f.getParent() === parent); + return this.getPrefabFiles("prefabs").filter(f => f.getParent() === parent); } - const scenes = this.getSceneFiles().filter(f => f.getParent() === parent); + const scenes = this.getPrefabFiles().filter(f => f.getParent() === parent); const items = this.getPackItems().filter(item => grouping.getItemFolder(item) === parent); return [...scenes, ...items]; From 906970e0c6716181d803c0ff537cfbbae72face8 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 16 May 2023 10:13:56 -0400 Subject: [PATCH 55/59] Updates changelog. --- CHANGELOG.MD | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index f7b06f404..8d6c0b90f 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -11,6 +11,7 @@ * Allows enabling input in objects. * Adds the Name parameter to the Variable section of a nested prefab instance. * [#282](https://github.com/PhaserEditor2D/PhaserEditor2D-v3/issues/282) Use full nested prefab path in user object variable properties. +* Excludes script node prefabs from the Blocks view when editing a non-script node scene. ## v3.60.3 - Apr 27, 2023 From 32025662ffb96dbc2275692dd69282785b8e07d9 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 16 May 2023 10:18:42 -0400 Subject: [PATCH 56/59] Update version. --- source/editor/plugins/phasereditor2d.ide/src/IDEPlugin.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/editor/plugins/phasereditor2d.ide/src/IDEPlugin.ts b/source/editor/plugins/phasereditor2d.ide/src/IDEPlugin.ts index ae11c6438..d2ba894aa 100644 --- a/source/editor/plugins/phasereditor2d.ide/src/IDEPlugin.ts +++ b/source/editor/plugins/phasereditor2d.ide/src/IDEPlugin.ts @@ -322,7 +322,7 @@ namespace phasereditor2d.ide { /* program entry point */ - export const VER = "3.60.4-dev"; + export const VER = "3.61.0"; async function main() { From b7b026eae740ed52085199ebd7413dff2bf9b4be Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 16 May 2023 23:04:40 -0400 Subject: [PATCH 57/59] Updates chagelog. --- CHANGELOG.MD | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 8d6c0b90f..196a78186 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -2,8 +2,7 @@ ## v3.60.1-dev -* Checks if an scene file was generated by a newer and incompatible version of the editor. -* Sorts prefabs in Blocks view: start with game object prefabs, end with script nodes. +* Checks if a scene file was generated by a newer and incompatible version of the editor. * Shows Object List items in the Outline view. Allows ordering the items with the Up, Down, Top, Down, commands. * A new game object scope: LOCAL. The LOCAL scope is now the default scope for objects and has the same meaning of METHOD scope before. Now the METHOD scope forces the creation of a variable for the object. * Auto computes the middle-private nested prefabs. It doesn't require to declare a parent of a nested prefab as nested prefab. From dc2768b5ce493ee6203090017dc4b355795d85b2 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Wed, 17 May 2023 00:03:09 -0400 Subject: [PATCH 58/59] Adds support to pixel perfect hit area. --- .vscode/settings.json | 2 +- scripts/make-all-help-files.js | 2 + .../data/phaser-docs.json | 1 + .../phasereditor2d.scene/src/ScenePlugin.ts | 1 + .../src/ui/sceneobjects/Component.ts | 1 - .../sceneobjects/GameObjectEditorSupport.ts | 3 +- .../hitArea/BaseHitAreaComponent.ts | 4 +- .../hitArea/CircleHitAreaComponent.ts | 5 +- .../sceneobjects/hitArea/EditHitAreaTool.ts | 15 ++++-- .../hitArea/EllipseHitAreaComponent.ts | 9 ++-- .../sceneobjects/hitArea/HitAreaComponent.ts | 10 +++- .../ui/sceneobjects/hitArea/HitAreaSection.ts | 16 ++++++- .../hitArea/PixelPerfectHitAreaComponent.ts | 47 +++++++++++++++++++ .../hitArea/PixelPerfectHitAreaSection.ts | 37 +++++++++++++++ .../hitArea/PolygonHitAreaComponent.ts | 5 +- .../hitArea/RectangleHitAreaComponent.ts | 5 +- .../properties/SceneGameObjectSection.ts | 4 +- 17 files changed, 147 insertions(+), 20 deletions(-) create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PixelPerfectHitAreaComponent.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PixelPerfectHitAreaSection.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 305652651..ff45a1acc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "workbench.colorTheme": "Solarized Light" + "workbench.colorTheme": "Solarized Dark" } \ No newline at end of file diff --git a/scripts/make-all-help-files.js b/scripts/make-all-help-files.js index 3aeb4c9d7..2192f1fb3 100755 --- a/scripts/make-all-help-files.js +++ b/scripts/make-all-help-files.js @@ -233,6 +233,8 @@ utils.makeHelpFile([ "Phaser.Geom.Circle.y", "Phaser.Geom.Circle.radius", + "Phaser.Input.InputPlugin.makePixelPerfect(alphaTolerance)", + "Phaser.GameObjects.Layer", "Phaser.Physics.Arcade.Collider", diff --git a/source/editor/plugins/phasereditor2d.scene/data/phaser-docs.json b/source/editor/plugins/phasereditor2d.scene/data/phaser-docs.json index ddbe28db6..ca6d2b0d3 100644 --- a/source/editor/plugins/phasereditor2d.scene/data/phaser-docs.json +++ b/source/editor/plugins/phasereditor2d.scene/data/phaser-docs.json @@ -130,6 +130,7 @@ "Phaser.Geom.Circle.x": "The x position of the center of the circle.", "Phaser.Geom.Circle.y": "The y position of the center of the circle.", "Phaser.Geom.Circle.radius": "The radius of the Circle.", + "Phaser.Input.InputPlugin.makePixelPerfect(alphaTolerance)": "The alpha level that the pixel should be above to be included as a successful interaction.", "Phaser.GameObjects.Layer": "A Layer Game Object.\n\nA Layer is a special type of Game Object that acts as a Display List. You can add any type of Game Object\nto a Layer, just as you would to a Scene. Layers can be used to visually group together 'layers' of Game\nObjects:\n\n```javascript\nconst spaceman = this.add.sprite(150, 300, 'spaceman');\nconst bunny = this.add.sprite(400, 300, 'bunny');\nconst elephant = this.add.sprite(650, 300, 'elephant');\n\nconst layer = this.add.layer();\n\nlayer.add([ spaceman, bunny, elephant ]);\n```\n\nThe 3 sprites in the example above will now be managed by the Layer they were added to. Therefore,\nif you then set `layer.setVisible(false)` they would all vanish from the display.\n\nYou can also control the depth of the Game Objects within the Layer. For example, calling the\n`setDepth` method of a child of a Layer will allow you to adjust the depth of that child _within the\nLayer itself_, rather than the whole Scene. The Layer, too, can have its depth set as well.\n\nThe Layer class also offers many different methods for manipulating the list, such as the\nmethods `moveUp`, `moveDown`, `sendToBack`, `bringToTop` and so on. These allow you to change the\ndisplay list position of the Layers children, causing it to adjust the order in which they are\nrendered. Using `setDepth` on a child allows you to override this.\n\nLayers can have Post FX Pipelines set, which allows you to easily enable a post pipeline across\na whole range of children, which, depending on the effect, can often be far more efficient that doing so\non a per-child basis.\n\nLayers have no position or size within the Scene. This means you cannot enable a Layer for\nphysics or input, or change the position, rotation or scale of a Layer. They also have no scroll\nfactor, texture, tint, origin, crop or bounds.\n\nIf you need those kind of features then you should use a Container instead. Containers can be added\nto Layers, but Layers cannot be added to Containers.\n\nHowever, you can set the Alpha, Blend Mode, Depth, Mask and Visible state of a Layer. These settings\nwill impact all children being rendered by the Layer.", "Phaser.Physics.Arcade.Collider": "An Arcade Physics Collider will automatically check for collision, or overlaps, between two objects\nevery step. If a collision, or overlap, occurs it will invoke the given callbacks.", "Phaser.Physics.Arcade.Image": "An Arcade Physics Image is an Image with an Arcade Physics body and related components.\nThe body can be dynamic or static.\n\nThe main difference between an Arcade Image and an Arcade Sprite is that you cannot animate an Arcade Image.", diff --git a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts index 7dd6c6a5e..7f42def3e 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts @@ -379,6 +379,7 @@ namespace phasereditor2d.scene { page => new ui.sceneobjects.CircleHitAreaSection(page), page => new ui.sceneobjects.EllipseHitAreaSection(page), page => new ui.sceneobjects.PolygonHitAreaSection(page), + page => new ui.sceneobjects.PixelPerfectHitAreaSection(page), page => new ui.sceneobjects.ArcadeBodySection(page), page => new ui.sceneobjects.ArcadeGeometrySection(page), page => new ui.sceneobjects.ArcadeBodyMovementSection(page), diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/Component.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/Component.ts index b84e67bc4..0a7db2c38 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/Component.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/Component.ts @@ -22,7 +22,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { export abstract class Component implements core.json.ISerializable { - private _obj: T; private _properties: Set>; private _active: boolean; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts index 05874997d..ec9fc74ff 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/GameObjectEditorSupport.ts @@ -67,7 +67,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { new RectangleHitAreaComponent(obj), new CircleHitAreaComponent(obj), new EllipseHitAreaComponent(obj), - new PolygonHitAreaComponent(obj) + new PolygonHitAreaComponent(obj), + new PixelPerfectHitAreaComponent(obj) ); } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaComponent.ts index 8e175f9f9..368d775d2 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaComponent.ts @@ -82,7 +82,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { const code = new core.code.MethodCallCodeDOM("setInteractive", args.objectVarName); - this.buildSetInteractiveCodeCOM(obj, code); + this.buildSetInteractiveCodeCOM(args, obj, code); args.statements.push(code); } @@ -90,6 +90,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { } } - protected abstract buildSetInteractiveCodeCOM(obj: ISceneGameObject, code: core.code.MethodCallCodeDOM): void; + protected abstract buildSetInteractiveCodeCOM(args: ISetObjectPropertiesCodeDOMArgs, obj: ISceneGameObject, code: core.code.MethodCallCodeDOM): void; } } \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaComponent.ts index 89f1348a7..1121d070f 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaComponent.ts @@ -42,7 +42,10 @@ namespace phasereditor2d.scene.ui.sceneobjects { this.radius = Math.min(width, height) / 2; } - protected override buildSetInteractiveCodeCOM(obj: ISceneGameObject, code: core.code.MethodCallCodeDOM): void { + protected override buildSetInteractiveCodeCOM( + args: ISetObjectPropertiesCodeDOMArgs, + obj: ISceneGameObject, + code: core.code.MethodCallCodeDOM): void { const { x, y, radius } = this; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts index 5bee58703..83eab5abb 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EditHitAreaTool.ts @@ -145,16 +145,23 @@ namespace phasereditor2d.scene.ui.sceneobjects { private renderObj(args: editor.tools.ISceneToolRenderArgs, obj: Sprite) { - const ctx = args.canvasContext; - - ctx.save(); - const objES = obj.getEditorSupport(); const comp = objES.getComponent(HitAreaComponent) as HitAreaComponent; const shape = comp.getHitAreaShape(); + if (shape === HitAreaShape.PIXEL_PERFECT) { + + // pixel perfect doesn't have any tool item + + return; + } + + const ctx = args.canvasContext; + + ctx.save(); + if (shape === HitAreaShape.ELLIPSE) { this.renderEllipse(obj, args, ctx); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaComponent.ts index ffd3fbf84..e8d5d0b08 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaComponent.ts @@ -8,13 +8,13 @@ namespace phasereditor2d.scene.ui.sceneobjects { static y = HitAreaProperty(EllipseHitAreaComponent, "y", "Y", "phaser:Phaser.Geom.Ellipse.y", 0); static width = HitAreaProperty(EllipseHitAreaComponent, "width", "W", "phaser:Phaser.Geom.Ellipse.width", 0); static height = HitAreaProperty(EllipseHitAreaComponent, "height", "H", "phaser:Phaser.Geom.Ellipse.height", 0); - + static position: IPropertyXY = { label: "Offset", x: this.x, y: this.y }; - + static size: IPropertyXY = { label: "Size", x: this.width, @@ -52,7 +52,10 @@ namespace phasereditor2d.scene.ui.sceneobjects { this.height = height; } - protected override buildSetInteractiveCodeCOM(obj: ISceneGameObject, code: core.code.MethodCallCodeDOM): void { + protected override buildSetInteractiveCodeCOM( + args: ISetObjectPropertiesCodeDOMArgs, + obj: ISceneGameObject, + code: core.code.MethodCallCodeDOM): void { const { x, y, width, height } = this; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts index 2e61029e2..9c7c2337b 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaComponent.ts @@ -5,7 +5,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { RECTANGLE = "RECTANGLE", CIRCLE = "CIRCLE", ELLIPSE = "ELLIPSE", - POLYGON = "POLYGON" + POLYGON = "POLYGON", + PIXEL_PERFECT = "PIXEL_PERFECT" } function getComp(obj: ISceneGameObject) { @@ -27,7 +28,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { HitAreaShape.RECTANGLE, HitAreaShape.CIRCLE, HitAreaShape.ELLIPSE, - HitAreaShape.POLYGON + HitAreaShape.POLYGON, + HitAreaShape.PIXEL_PERFECT ] }; @@ -62,6 +64,10 @@ namespace phasereditor2d.scene.ui.sceneobjects { case HitAreaShape.POLYGON: return PolygonHitAreaComponent.getPolygonComponent(obj); + + case HitAreaShape.PIXEL_PERFECT: + + return PixelPerfectHitAreaComponent.getPixelPerfectComponent(obj) } return undefined; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaSection.ts index e324bd1f2..e783014d3 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/HitAreaSection.ts @@ -28,7 +28,21 @@ namespace phasereditor2d.scene.ui.sceneobjects { } } - this.createPropertyEnumRow(comp, prop); + this.createPropertyEnumRow(comp, prop, undefined, value => { + + if (value === HitAreaShape.PIXEL_PERFECT) { + + for(const obj of this.getSelection()) { + + if (!(obj instanceof Sprite) && !(obj instanceof Image)) { + + return false; + } + } + } + + return true; + }); } canEdit(obj: any, n: number): boolean { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PixelPerfectHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PixelPerfectHitAreaComponent.ts new file mode 100644 index 000000000..1cbe18fb6 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PixelPerfectHitAreaComponent.ts @@ -0,0 +1,47 @@ +/// +/// +namespace phasereditor2d.scene.ui.sceneobjects { + + export class PixelPerfectHitAreaComponent extends BaseHitAreaComponent { + + static alphaTolerance = HitAreaProperty(PixelPerfectHitAreaComponent, "alphaTolerance", "Alpha Tolerance", "phaser:Phaser.Input.InputPlugin.makePixelPerfect(alphaTolerance)", 1); + + public alphaTolerance = 1; + + constructor(obj: ISceneGameObject) { + super(obj, HitAreaShape.PIXEL_PERFECT, [ + PixelPerfectHitAreaComponent.alphaTolerance + ]); + } + + static getPixelPerfectComponent(obj: ISceneGameObject) { + + const objES = obj.getEditorSupport(); + + const comp = objES.getComponent(PixelPerfectHitAreaComponent) as PixelPerfectHitAreaComponent; + + return comp; + } + + protected _setDefaultValues(width: number, height: number): void { + // nothing + } + + protected buildSetInteractiveCodeCOM( + args: ISetObjectPropertiesCodeDOMArgs, + obj: ISceneGameObject, + code: core.code.MethodCallCodeDOM): void { + + const objES = this.getEditorSupport(); + + const scene = objES.getScene(); + + const sceneVar = scene.isPrefabSceneType() ? + "this.scene" : "this"; + + const alpha = this.alphaTolerance === 1 ? "" : this.alphaTolerance; + + code.arg(`${sceneVar}.input.makePixelPerfect(${alpha})`); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PixelPerfectHitAreaSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PixelPerfectHitAreaSection.ts new file mode 100644 index 000000000..356fba033 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PixelPerfectHitAreaSection.ts @@ -0,0 +1,37 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + import controls = colibri.ui.controls; + + export class PixelPerfectHitAreaSection extends SceneGameObjectSection { + + static ID = "phasereditor2d.scene.ui.sceneobjects.PixelPerfectHitAreaSection"; + + constructor(page: controls.properties.PropertyPage) { + super(page, PixelPerfectHitAreaSection.ID, "Hit Area (Pixel Perfect)"); + } + + createMenu(menu: controls.Menu) { + + this.createToolMenuItem(menu, EditHitAreaTool.ID); + + super.createMenu(menu); + } + + createForm(parent: HTMLDivElement) { + + const comp = this.createGridElement(parent, 3); + + this.createPropertyFloatRow(comp, PixelPerfectHitAreaComponent.alphaTolerance, false); + } + + canEdit(obj: any, n: number): boolean { + + return HitAreaComponent.hasHitAreaShape(obj, HitAreaShape.PIXEL_PERFECT); + } + + canEditNumber(n: number): boolean { + + return n > 0; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaComponent.ts index 3695e40b6..6b79d76f1 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaComponent.ts @@ -47,7 +47,10 @@ namespace phasereditor2d.scene.ui.sceneobjects { this.points = `0 ${h * 0.25} ${w/2} 0 ${w} ${h * 0.25} ${w} ${h} 0 ${h}`; } - protected override buildSetInteractiveCodeCOM(obj: ISceneGameObject, code: core.code.MethodCallCodeDOM): void { + protected override buildSetInteractiveCodeCOM( + args: ISetObjectPropertiesCodeDOMArgs, + obj: ISceneGameObject, + code: core.code.MethodCallCodeDOM): void { code.arg(`new Phaser.Geom.Polygon("${this.points}")`); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts index cc2b7d60c..f10ddb978 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts @@ -49,7 +49,10 @@ namespace phasereditor2d.scene.ui.sceneobjects { this.height = height; } - protected override buildSetInteractiveCodeCOM(obj: ISceneGameObject, code: core.code.MethodCallCodeDOM): void { + protected override buildSetInteractiveCodeCOM( + args: ISetObjectPropertiesCodeDOMArgs, + obj: ISceneGameObject, + code: core.code.MethodCallCodeDOM): void { const { x, y, width, height } = this; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/SceneGameObjectSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/SceneGameObjectSection.ts index 8dab6e833..6ed9555ea 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/SceneGameObjectSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/SceneGameObjectSection.ts @@ -204,7 +204,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { return text; } - createPropertyEnumRow(parent: HTMLElement, prop: IEnumProperty, lockIcon: boolean = true) { + createPropertyEnumRow(parent: HTMLElement, prop: IEnumProperty, lockIcon: boolean = true, filter?: (v: any) => boolean) { if (lockIcon) { @@ -214,7 +214,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { const label = this.createLabel(parent, prop.label, PhaserHelp(prop.tooltip)); label.style.gridColumn = "2"; - const btn = this.createEnumField(parent, prop); + const btn = this.createEnumField(parent, prop, undefined, filter); return btn; } From 7dcf8705633dd9100ce5a04cb6ed25b25d04c877 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 18 May 2023 12:01:26 -0400 Subject: [PATCH 59/59] Updates changelog. --- CHANGELOG.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 196a78186..b3b0b9f04 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -1,6 +1,6 @@ # Change Log -## v3.60.1-dev +## v3.61.0 - May 18, 2023 * Checks if a scene file was generated by a newer and incompatible version of the editor. * Shows Object List items in the Outline view. Allows ordering the items with the Up, Down, Top, Down, commands.