From 4c83d5c2f5b5965189d618b761ea67755bb88881 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Mon, 2 Oct 2023 10:33:37 -0400 Subject: [PATCH 01/58] Fixes single-layer prefab thumbnail generator. --- CHANGELOG.MD | 4 ++++ .../phasereditor2d.scene/src/ui/SceneThumbnailImage.ts | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index d02aa1bd6..5dbd9d2c5 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -1,5 +1,9 @@ # Change Log +## dev + +* Fixes making thumbnails of single-layer prefabs. + ## v3.63.0 - Sep 30, 2023 * Opens the file given in the `openfile` URL search parameter. Like in `?openfile=some/file`. diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/SceneThumbnailImage.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/SceneThumbnailImage.ts index 754c0b188..f957eda2a 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/SceneThumbnailImage.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/SceneThumbnailImage.ts @@ -54,7 +54,7 @@ namespace phasereditor2d.scene.ui { if (singleObject) { - if (singleObject instanceof sceneobjects.Container) { + if (singleObject instanceof sceneobjects.Container || singleObject instanceof sceneobjects.Layer) { const container = singleObject as sceneobjects.Container; From 177be0936f1220529c20ae164d25d4ab5deb755a Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Mon, 9 Oct 2023 14:47:59 -0400 Subject: [PATCH 02/58] Improves importing files from the Files view. --- .../src/ui/DefaultAssetPackExtension.ts | 13 +- .../src/ui/editor/ImportFileSection.ts | 2 + .../ui/properties/AddFileToPackFileSection.ts | 169 ++++++++++-------- 3 files changed, 106 insertions(+), 78 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/DefaultAssetPackExtension.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/DefaultAssetPackExtension.ts index bada3b0e4..1f2db6b3a 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/DefaultAssetPackExtension.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/DefaultAssetPackExtension.ts @@ -467,6 +467,12 @@ namespace phasereditor2d.pack.ui { new importers.UnityAtlasImporter(), + new importers.SpineImporter(core.contentTypes.CONTENT_TYPE_SPINE_JSON, core.SPINE_JSON_TYPE), + + new importers.SpineImporter(core.contentTypes.CONTENT_TYPE_SPINE_BINARY, core.SPINE_BINARY_TYPE), + + new importers.SpineAtlasImporter(), + new importers.BitmapFontImporter(), new importers.SingleFileImporter(webContentTypes.core.CONTENT_TYPE_IMAGE, core.IMAGE_TYPE), @@ -480,6 +486,7 @@ namespace phasereditor2d.pack.ui { scale: 0 } }), + new importers.SpritesheetImporter(), new importers.SingleFileImporter(core.contentTypes.CONTENT_TYPE_ANIMATIONS, core.ANIMATION_TYPE), @@ -491,12 +498,6 @@ namespace phasereditor2d.pack.ui { new importers.SingleFileImporter(core.contentTypes.CONTENT_TYPE_TILEMAP_TILED_JSON, core.TILEMAP_TILED_JSON_TYPE), - new importers.SpineImporter(core.contentTypes.CONTENT_TYPE_SPINE_JSON, core.SPINE_JSON_TYPE), - - new importers.SpineImporter(core.contentTypes.CONTENT_TYPE_SPINE_BINARY, core.SPINE_BINARY_TYPE), - - new importers.SpineAtlasImporter(), - new importers.SingleFileImporter(webContentTypes.core.CONTENT_TYPE_JAVASCRIPT, core.PLUGIN_TYPE, false, { start: false, mapping: "" diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/editor/ImportFileSection.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/editor/ImportFileSection.ts index 6fcd3d574..319b3c6c2 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/editor/ImportFileSection.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/editor/ImportFileSection.ts @@ -16,6 +16,7 @@ namespace phasereditor2d.pack.ui.editor { this.addUpdater(() => { while (comp.children.length > 0) { + comp.children.item(0).remove(); } @@ -58,6 +59,7 @@ namespace phasereditor2d.pack.ui.editor { } canEditNumber(n: number): boolean { + return n > 0; } } diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AddFileToPackFileSection.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AddFileToPackFileSection.ts index c14c74a00..3ff35c285 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AddFileToPackFileSection.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AddFileToPackFileSection.ts @@ -29,116 +29,127 @@ namespace phasereditor2d.pack.ui.properties { this.addUpdater(async () => { + comp.innerHTML = ""; + const finder = new core.PackFinder(); await finder.preload(); - const packItems = await this.getPackItems(finder); + await this.buildImportButtons(finder, comp); - comp.innerHTML = ""; + await this.buildOpenButtons(finder, comp); + }); + } - const used = new Set(); + private async buildOpenButtons(finder: core.PackFinder, comp: HTMLDivElement) { - for (const item of packItems) { + const packItems = await this.getPackItems(finder); - const btn = document.createElement("button"); + const used = new Set(); - const key = item.getKey(); - const packPath = item.getPack().getFile().getProjectRelativeName(); - const hash = `${key}@${packPath}`; + for (const item of packItems) { - if (used.has(hash)) { + const btn = document.createElement("button"); - continue; - } + const key = item.getKey(); + const packName = item.getPack().getFile().getName(); + const packPath = item.getPack().getFile().getProjectRelativeName(); + const hash = `${key}@${packPath}`; - used.add(hash); + if (used.has(hash)) { - btn.innerHTML = - `${key} at ${packPath}`; + continue; + } - btn.addEventListener("click", async (e) => { + used.add(hash); - const editor = colibri.Platform.getWorkbench() - .openEditor(item.getPack().getFile()) as editor.AssetPackEditor; + btn.innerHTML = + `Open ${key} at ${packName}`; - editor.revealKey(item.getKey()); - }); + btn.addEventListener("click", async (e) => { - comp.appendChild(btn); - } + const editor = colibri.Platform.getWorkbench() + .openEditor(item.getPack().getFile()) as editor.AssetPackEditor; + + editor.revealKey(item.getKey()); + }); - if (packItems.length === 0) { + comp.appendChild(btn); + } + } - const importList = this.buildImportList(); + private async buildImportButtons(finder: core.PackFinder, comp: HTMLDivElement) { - for (const importData of importList) { + const importersData = await this.buildImportersData(finder); - const btn = document.createElement("button"); + for (const importerData of importersData) { - btn.innerText = `Import as ${importData.importer.getType()} (${importData.files.length})`; + const btn = document.createElement("button"); - btn.addEventListener("click", async (e) => { + const importDesc = importerData.files.length === 1 ? + importerData.files[0].getName() : importerData.files.length.toString(); - const packs = finder.getPacks(); + btn.innerText = `Import as ${importerData.importer.getType()} (${importDesc})`; - const menu = new controls.Menu(); + btn.addEventListener("click", async (e) => { - for (const pack of packs) { + const packs = finder.getPacks(); - const validFiles = importData.files - .filter(file => { + const menu = new controls.Menu(); - const publicRoot = colibri.ui.ide.FileUtils.getPublicRoot(pack.getFile().getParent()); + for (const pack of packs) { - return file.getFullName().startsWith(publicRoot.getFullName()) - }); + const validFiles = importerData.files + .filter(file => { - menu.add(new controls.Action({ - text: "Add To " + pack.getFile().getProjectRelativeName(), - enabled: validFiles.length > 0, - callback: () => { + const publicRoot = colibri.ui.ide.FileUtils.getPublicRoot(pack.getFile().getParent()); - this.importWithImporter(importData, pack); - } - })); + return file.getFullName().startsWith(publicRoot.getFullName()); + }); + + menu.add(new controls.Action({ + text: "Add To " + pack.getFile().getProjectRelativeName(), + enabled: validFiles.length > 0, + callback: () => { + + this.importWithImporter(importerData, pack); } + })); + } - menu.add(new controls.Action({ - text: "Add To New Pack File", - callback: () => { + menu.add(new controls.Action({ + text: "Add To New Pack File", + callback: () => { - const ext = new pack.ui.dialogs.NewAssetPackFileWizardExtension(); + const ext = new pack.ui.dialogs.NewAssetPackFileWizardExtension(); - const dlg = ext.createDialog({ - initialFileLocation: this.getSelectionFirstElement().getParent() - }); + const dlg = ext.createDialog({ + initialFileLocation: this.getSelectionFirstElement().getParent() + }); - dlg.setTitle("New " + ext.getDialogName()); + dlg.setTitle("New " + ext.getDialogName()); - const callback = dlg.getFileCreatedCallback(); + const callback = dlg.getFileCreatedCallback(); - dlg.setFileCreatedCallback(async (file) => { + dlg.setFileCreatedCallback(async (file) => { - await callback(file); + await callback(file); - const content = colibri.ui.ide.FileUtils.getFileString(file); + const content = colibri.ui.ide.FileUtils.getFileString(file); - const pack = new core.AssetPack(file, content); + const pack = new core.AssetPack(file, content); - this.importWithImporter(importData, pack); + this.importWithImporter(importerData, pack); - }); - } - })); + }); + } + })); - menu.createWithEvent(e); - }); + menu.createWithEvent(e); + }); - comp.appendChild(btn); - } - } - }); + comp.appendChild(btn); + } } private async importWithImporter(importData: editor.IImportData, pack: core.AssetPack) { @@ -158,13 +169,25 @@ namespace phasereditor2d.pack.ui.properties { blocks.BlocksPlugin.getInstance().refreshBlocksView(); } - private buildImportList() { + private async buildImportersData(finder: core.PackFinder) { const importList: editor.IImportData[] = []; + const selection: io.FilePath[] = []; + + for (const file of this.getSelection()) { + + const items = await finder.findPackItemsFor(file); + + if (items.length === 0) { + + selection.push(file); + } + } + for (const importer of importers.Importers.getAll()) { - const files = this.getSelection().filter(file => importer.acceptFile(file)); + const files = selection.filter(file => importer.acceptFile(file)); if (files.length > 0) { @@ -193,14 +216,16 @@ namespace phasereditor2d.pack.ui.properties { } } - if (n > 0) { + return n > 0; - const list = this.buildImportList(); + // if (n > 0) { - return list.length > 0; - } + // const list = this.buildImportList(); + + // return list.length > 0; + // } - return false; + // return false; } } } \ No newline at end of file From 498bde9a81e7870bcf6d3b416fe7a9dcdd482d63 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Mon, 9 Oct 2023 14:48:59 -0400 Subject: [PATCH 03/58] Updates changelog. --- CHANGELOG.MD | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 5dbd9d2c5..535183267 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -3,6 +3,7 @@ ## dev * Fixes making thumbnails of single-layer prefabs. +* Allows merging Import and Open buttons in the Asset Pack Entry section. ## v3.63.0 - Sep 30, 2023 From 968c4d3fd4516751b59ae1372b2ced2bb4d0df9e Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Mon, 9 Oct 2023 15:25:25 -0400 Subject: [PATCH 04/58] #251 Fixes sprite positioning in Animations Editor. --- .../src/ui/editors/AnimationsScene.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/AnimationsScene.ts b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/AnimationsScene.ts index 64a0db46a..3ae9d8bca 100644 --- a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/AnimationsScene.ts +++ b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/AnimationsScene.ts @@ -207,8 +207,8 @@ namespace phasereditor2d.animations.ui.editors { sprite.setScale(scale, scale); - const marginX = size - sprite.width * scale; - const marginY = size - sprite.height * scale; + const marginX = size / 2 - sprite.width * scale / 2; + const marginY = size / 2 - sprite.height * scale / 2; sprite.setData("cell", { x, y, size }); From 99879686c6d38dfc26eb2eddf863a8a49e04c73c Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Mon, 9 Oct 2023 15:25:59 -0400 Subject: [PATCH 05/58] Updates changelog. --- CHANGELOG.MD | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 535183267..25354eee8 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -4,6 +4,7 @@ * Fixes making thumbnails of single-layer prefabs. * Allows merging Import and Open buttons in the Asset Pack Entry section. +* [#251](https://github.com/PhaserEditor2D/PhaserEditor2D-v3/issues/251) Fixes sprite positioning in Animations Editor. ## v3.63.0 - Sep 30, 2023 From 76d4d1cad7c06e31573e168df39a9e5d968bee79 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 10 Oct 2023 00:42:53 -0400 Subject: [PATCH 06/58] #209 Improves scene border painting. --- .../phasereditor2d.scene/src/ui/editor/OverlayLayer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/OverlayLayer.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/OverlayLayer.ts index 45ade5977..b9778f0ec 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/OverlayLayer.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/OverlayLayer.ts @@ -293,11 +293,11 @@ namespace phasereditor2d.scene.ui.editor { const b = camera.getScreenPoint(borderX + borderWidth, borderY + borderHeight); ctx.save(); ctx.strokeStyle = theme.dark ? "#0a0a0a" : "#404040"; - ctx.strokeRect(a.x + 2, a.y + 2, b.x - a.x, b.y - a.y); + ctx.strokeRect(a.x - 2, a.y - 2, b.x - a.x + 4, b.y - a.y + 4); ctx.restore(); ctx.lineWidth = 1; - ctx.strokeRect(a.x, a.y, b.x - a.x, b.y - a.y); + ctx.strokeRect(a.x - 3, a.y - 3, b.x - a.x + 6, b.y - a.y + 6); ctx.restore(); } } From a131b77ab62d3577095b1bebe070420ad8951c16 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 10 Oct 2023 00:43:25 -0400 Subject: [PATCH 07/58] Updates changelog. --- CHANGELOG.MD | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 25354eee8..5145dd93b 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -5,6 +5,7 @@ * Fixes making thumbnails of single-layer prefabs. * Allows merging Import and Open buttons in the Asset Pack Entry section. * [#251](https://github.com/PhaserEditor2D/PhaserEditor2D-v3/issues/251) Fixes sprite positioning in Animations Editor. +* [#209](https://github.com/PhaserEditor2D/PhaserEditor2D-v3/issues/209) Improves scene border painting. ## v3.63.0 - Sep 30, 2023 From 82429cc93e856195d7e6a3f54801153992e98e79 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Wed, 11 Oct 2023 22:42:48 -0400 Subject: [PATCH 08/58] Starts with the sprite animations blocks. --- .../controls/viewers/IconGridCellRenderer.ts | 2 ++ .../src/core/AnimationConfigInPackItem.ts | 9 +++++- .../core/AnimationFrameConfigInPackItem.ts | 11 +++++++ .../src/core/AnimationsAssetPackItem.ts | 18 ++++++++++- .../src/core/AssetPackItem.ts | 14 ++++++-- .../src/core/PackFinder.ts | 17 ++++++++-- .../src/ui/DefaultAssetPackExtension.ts | 4 ++- .../src/ui/editor/AssetPackEditor.ts | 11 +++++++ .../editor/AssetPackEditorContentProvider.ts | 2 ++ .../AssetPackEditorOutlineContentProvider.ts | 7 +++- .../src/ui/importers/Importer.ts | 12 +++++++ .../ui/viewers/AnimationConfigCellRenderer.ts | 29 +++++++++++------ .../ui/viewers/AnimationsItemCellRenderer.ts | 18 +++++++++++ .../viewers/AssetPackCellRendererProvider.ts | 5 +++ .../src/ui/SceneThumbnailImage.ts | 5 ++- .../SceneEditorBlocksCellRendererProvider.ts | 4 +++ .../SceneEditorBlocksContentProvider.ts | 6 ++++ .../ui/blocks/SceneEditorBlocksProvider.ts | 14 ++++++++ .../SceneEditorBlocksTreeRendererProvider.ts | 6 ++++ .../ui/sceneobjects/ImageLoaderExtension.ts | 16 +++++++++- .../ui/sceneobjects/sprite/SpriteExtension.ts | 32 +++++++++++++++++-- .../AbstractAssetKeyPropertyType.ts | 2 +- .../userProperties/EventPropertyType.ts | 9 ++---- 23 files changed, 223 insertions(+), 30 deletions(-) create mode 100644 source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AnimationsItemCellRenderer.ts diff --git a/source/editor/plugins/colibri/src/ui/controls/viewers/IconGridCellRenderer.ts b/source/editor/plugins/colibri/src/ui/controls/viewers/IconGridCellRenderer.ts index 4b90337d3..1a512167d 100644 --- a/source/editor/plugins/colibri/src/ui/controls/viewers/IconGridCellRenderer.ts +++ b/source/editor/plugins/colibri/src/ui/controls/viewers/IconGridCellRenderer.ts @@ -41,10 +41,12 @@ namespace colibri.ui.controls.viewers { } cellHeight(args: RenderCellArgs): number { + return args.viewer.getCellSize(); } preload(args: PreloadCellArgs): Promise { + return this._icon.preload(); } } diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/AnimationConfigInPackItem.ts b/source/editor/plugins/phasereditor2d.pack/src/core/AnimationConfigInPackItem.ts index f233fdb8f..3176078bc 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/core/AnimationConfigInPackItem.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/core/AnimationConfigInPackItem.ts @@ -2,14 +2,21 @@ namespace phasereditor2d.pack.core { export class AnimationConfigInPackItem { + private _parent: AnimationsAssetPackItem; private _key: string; private _frames: AnimationFrameConfigInPackItem[]; - constructor() { + constructor(parent: AnimationsAssetPackItem) { + this._parent = parent; this._frames = []; } + getParent() { + + return this._parent; + } + getKey() { return this._key; diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/AnimationFrameConfigInPackItem.ts b/source/editor/plugins/phasereditor2d.pack/src/core/AnimationFrameConfigInPackItem.ts index 5e38081a3..f1b08b114 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/core/AnimationFrameConfigInPackItem.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/core/AnimationFrameConfigInPackItem.ts @@ -4,6 +4,17 @@ namespace phasereditor2d.pack.core { private _textureKey: string; private _frameKey: string | number; + private _textureFrame: ImageAssetPackItem | AssetPackImageFrame; + + setTextureFrame(textureFrame: ImageAssetPackItem | AssetPackImageFrame) { + + this._textureFrame = textureFrame; + } + + getTextureFrame() { + + return this._textureFrame; + } getTextureKey() { diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/AnimationsAssetPackItem.ts b/source/editor/plugins/phasereditor2d.pack/src/core/AnimationsAssetPackItem.ts index cf0ac5234..db930288c 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/core/AnimationsAssetPackItem.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/core/AnimationsAssetPackItem.ts @@ -43,7 +43,7 @@ namespace phasereditor2d.pack.core { for (const animData of data.anims) { - const animConfig = new AnimationConfigInPackItem(); + const animConfig = new AnimationConfigInPackItem(this); animConfig.setKey(animData.key); @@ -68,5 +68,21 @@ namespace phasereditor2d.pack.core { return controls.PreloadResult.RESOURCES_LOADED; } + + async build(finder: PackFinder) { + + for (const anim of this._animations) { + + for (const frameConfig of anim.getFrames()) { + + const textureKey = frameConfig.getTextureKey(); + const frameKey = frameConfig.getFrameKey(); + + const textureFrame = finder.getAssetPackItemOrFrame(textureKey, frameKey); + + frameConfig.setTextureFrame(textureFrame); + } + } + } } } \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/AssetPackItem.ts b/source/editor/plugins/phasereditor2d.pack/src/core/AssetPackItem.ts index ad3bc7cf1..321a7e207 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/core/AssetPackItem.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/core/AssetPackItem.ts @@ -99,6 +99,16 @@ namespace phasereditor2d.pack.core { return controls.Controls.resolveNothingLoaded(); } + /** + * For building connections with other assets. + * It is the case of the frames of the sprite animations. + * + * @param finder + */ + async build(finder: pack.core.PackFinder) { + // empty + } + resetCache() { // empty } @@ -111,12 +121,12 @@ namespace phasereditor2d.pack.core { computeHash() { const files = new Set(); - + this.computeUsedFiles(files); const builder = new ide.core.MultiHashBuilder(); - for(const file of files) { + for (const file of files) { builder.addPartialFileToken(file); } diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/PackFinder.ts b/source/editor/plugins/phasereditor2d.pack/src/core/PackFinder.ts index 471d63248..5f25cd958 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/core/PackFinder.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/core/PackFinder.ts @@ -25,6 +25,7 @@ namespace phasereditor2d.pack.core { for (const item of items) { const result2 = await item.preload(); + result = Math.max(result, result2); if (monitor) { @@ -33,6 +34,11 @@ namespace phasereditor2d.pack.core { } } + for (const item of items) { + + await item.build(this); + } + return Promise.resolve(result); } @@ -85,11 +91,12 @@ namespace phasereditor2d.pack.core { return null; } - getAssetPackItemOrFrame(key: string, frame: any) { + getAssetPackItemOrFrame(key: string, frame: any): ImageAssetPackItem | AssetPackImageFrame { const item = this.findAssetPackItem(key); if (!item) { + return null; } @@ -104,7 +111,12 @@ namespace phasereditor2d.pack.core { return imageFrame; } - return item; + if (item instanceof ImageAssetPackItem) { + + return item; + } + + return null; } getAssetPackItemImage(key: string, frame: any): AssetPackImageFrame { @@ -118,7 +130,6 @@ namespace phasereditor2d.pack.core { } else if (asset instanceof AssetPackImageFrame) { return asset; - } return null; diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/DefaultAssetPackExtension.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/DefaultAssetPackExtension.ts index 1f2db6b3a..6533743dc 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/DefaultAssetPackExtension.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/DefaultAssetPackExtension.ts @@ -422,7 +422,9 @@ namespace phasereditor2d.pack.ui { } else if (element instanceof core.AnimationConfigInPackItem) { - return DefaultAssetPackExtension.getIconRenderer(resources.getIcon(resources.ICON_ANIMATIONS), layout); + // return DefaultAssetPackExtension.getIconRenderer(resources.getIcon(resources.ICON_ANIMATIONS), layout); + + return new viewers.AnimationConfigCellRenderer(); } return undefined; diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/editor/AssetPackEditor.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/editor/AssetPackEditor.ts index e900cd313..30b33775b 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/editor/AssetPackEditor.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/editor/AssetPackEditor.ts @@ -136,8 +136,19 @@ namespace phasereditor2d.pack.ui.editor { const content = await ide.FileUtils.preloadAndGetFileString(file); + const finder = new pack.core.PackFinder(); + + await finder.preload(); + this._pack = new core.AssetPack(file, content); + for(const item of this._pack.getItems()) { + + await item.preload(); + + await item.build(finder); + } + this.getViewer().repaint(); await this.updateBlocks(); diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/editor/AssetPackEditorContentProvider.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/editor/AssetPackEditorContentProvider.ts index 931dbfbfd..127ec2a2c 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/editor/AssetPackEditorContentProvider.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/editor/AssetPackEditorContentProvider.ts @@ -13,6 +13,7 @@ namespace phasereditor2d.pack.ui.editor { } getPack() { + return this._editor.getPack(); } @@ -29,6 +30,7 @@ namespace phasereditor2d.pack.ui.editor { getChildren(parent: any): any[] { if (typeof (parent) === "string") { + const type = parent; if (this.getPack()) { diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/editor/AssetPackEditorOutlineContentProvider.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/editor/AssetPackEditorOutlineContentProvider.ts index 679f76ec4..e5e4ff54a 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/editor/AssetPackEditorOutlineContentProvider.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/editor/AssetPackEditorOutlineContentProvider.ts @@ -24,12 +24,17 @@ namespace phasereditor2d.pack.ui.editor { } getChildren(parent: any): any[] { - + if (parent instanceof core.SpineAssetPackItem) { return parent.getGuessSkinItems(); } + if (parent instanceof core.AnimationsAssetPackItem) { + + return parent.getAnimations(); + } + return super.getChildren(parent); } } diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/importers/Importer.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/importers/Importer.ts index 9620e6e46..aa0906f82 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/importers/Importer.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/importers/Importer.ts @@ -72,8 +72,14 @@ namespace phasereditor2d.pack.ui.importers { pack.addItem(item); + await item.preload(); + const finder = new core.PackFinder(); + await finder.preload(); + + await item.build(finder); + return item; } @@ -99,6 +105,12 @@ namespace phasereditor2d.pack.ui.importers { await item.preload(); + const finder = new core.PackFinder(); + + await finder.preload(); + + await item.build(finder); + return item; } } diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AnimationConfigCellRenderer.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AnimationConfigCellRenderer.ts index 840d27439..1bd71b77a 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AnimationConfigCellRenderer.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AnimationConfigCellRenderer.ts @@ -4,12 +4,12 @@ namespace phasereditor2d.pack.ui.viewers { export class AnimationConfigCellRenderer implements controls.viewers.ICellRenderer { - private _finder: pack.core.PackFinder; - public layout: "square" | "full-width" = "full-width"; + public layout: "square" | "full-width"; + private static _finder: pack.core.PackFinder; - constructor(finder: pack.core.PackFinder) { + constructor(layout: "square" | "full-width" = "full-width") { - this._finder = finder; + this.layout = layout; } getAnimationConfig(args: controls.viewers.RenderCellArgs | controls.viewers.PreloadCellArgs): pack.core.AnimationConfigInPackItem { @@ -38,7 +38,7 @@ namespace phasereditor2d.pack.ui.viewers { ctx.save(); - if (cellSize <= controls.ROW_HEIGHT) { + if (cellSize <= controls.ROW_HEIGHT || this.layout === "square") { const img = this.getImage(frames[0]); @@ -70,9 +70,18 @@ namespace phasereditor2d.pack.ui.viewers { private getImage(frame: pack.core.AnimationFrameConfigInPackItem) { - const image = this._finder.getAssetPackItemImage(frame.getTextureKey(), frame.getFrameKey()); + const texture = frame.getTextureFrame(); - return image; + if (texture instanceof pack.core.ImageAssetPackItem) { + + return texture.getFrames()[0]; + + } else if (texture instanceof pack.core.AssetPackImageFrame) { + + return texture; + } + + return null; } cellHeight(args: controls.viewers.RenderCellArgs): number { @@ -88,11 +97,11 @@ namespace phasereditor2d.pack.ui.viewers { for (const frame of anim.getFrames()) { - const obj = this._finder.getAssetPackItemOrFrame(frame.getTextureKey(), frame.getFrameKey()); + const asset = frame.getTextureFrame(); - if (obj) { + if (asset) { - const objResult = await obj.preload(); + const objResult = await asset.preload(); result = Math.max(result, objResult); } diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AnimationsItemCellRenderer.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AnimationsItemCellRenderer.ts new file mode 100644 index 000000000..3edc0751b --- /dev/null +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AnimationsItemCellRenderer.ts @@ -0,0 +1,18 @@ +namespace phasereditor2d.pack.ui.viewers { + + import controls = colibri.ui.controls; + + export class AnimationsItemCellRenderer extends controls.viewers.IconImageCellRenderer { + + constructor() { + super(resources.getIcon(resources.ICON_ANIMATIONS)); + } + + async preload(args: controls.viewers.PreloadCellArgs): Promise { + + super.preload(args); + + return (args.obj as pack.core.AnimationsAssetPackItem).preload(); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AssetPackCellRendererProvider.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AssetPackCellRendererProvider.ts index 1e3a4bf6d..a4725355a 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AssetPackCellRendererProvider.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AssetPackCellRendererProvider.ts @@ -11,6 +11,7 @@ namespace phasereditor2d.pack.ui.viewers { constructor(layout: "grid" | "tree") { + this._layout = layout; this._fileRendererProvider = new files.ui.viewers.FileCellRendererProvider(layout); @@ -41,6 +42,10 @@ namespace phasereditor2d.pack.ui.viewers { return this._fileRendererProvider.getCellRenderer(element); + } else if (element instanceof pack.core.AnimationsAssetPackItem) { + + return new viewers.AnimationsItemCellRenderer(); + } else { const extensions = AssetPackPlugin.getInstance().getExtensions(); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/SceneThumbnailImage.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/SceneThumbnailImage.ts index f957eda2a..14f31709c 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/SceneThumbnailImage.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/SceneThumbnailImage.ts @@ -80,7 +80,10 @@ namespace phasereditor2d.scene.ui { const cx = s.borderX + s.borderWidth / 2 + dx; const cy = s.borderY + s.borderHeight / 2 + dy; - sprite.setPosition(cx, cy); + if (sprite.setPosition) { + + sprite.setPosition(cx, cy); + } } } else { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksCellRendererProvider.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksCellRendererProvider.ts index aa7dabd62..06b7fdfa0 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksCellRendererProvider.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksCellRendererProvider.ts @@ -42,6 +42,10 @@ namespace phasereditor2d.scene.ui.blocks { } else if (element instanceof viewers.PhaserTypeSymbol) { return new controls.viewers.IconImageCellRenderer(colibri.ColibriPlugin.getInstance().getIcon(colibri.ICON_FOLDER)); + + } else if (element instanceof pack.core.AnimationConfigInPackItem) { + + return new pack.ui.viewers.AnimationConfigCellRenderer("square"); } return super.getCellRenderer(element); 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 97e796c44..146d7cc73 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksContentProvider.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksContentProvider.ts @@ -11,6 +11,7 @@ namespace phasereditor2d.scene.ui.blocks { pack.core.MULTI_ATLAS_TYPE, pack.core.UNITY_ATLAS_TYPE, pack.core.SPRITESHEET_TYPE, + pack.core.ANIMATION_TYPE, pack.core.BITMAP_FONT_TYPE, pack.core.SPINE_JSON_TYPE, pack.core.SPINE_BINARY_TYPE, @@ -156,6 +157,11 @@ namespace phasereditor2d.scene.ui.blocks { return parent.getItems().filter(i => SCENE_EDITOR_BLOCKS_PACK_ITEM_TYPES.has(i.getType())); } + if (parent instanceof pack.core.AnimationsAssetPackItem) { + + return parent.getAnimations(); + } + if (typeof (parent) === "string") { if (SCENE_OBJECT_CATEGORY_SET.has(parent)) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksProvider.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksProvider.ts index 407e8d0a1..a558f6078 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksProvider.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksProvider.ts @@ -128,6 +128,20 @@ namespace phasereditor2d.scene.ui.blocks { } } + } else if (obj instanceof pack.core.AnimationConfigInPackItem) { + + const item = this.getFreshItem(obj.getParent()) as pack.core.AnimationsAssetPackItem; + + if (item) { + + const found = item.getAnimations().find(a => a.getKey() === obj.getKey()); + + if (found) { + + set.add(found); + } + } + } else { set.add(obj); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksTreeRendererProvider.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksTreeRendererProvider.ts index 3c9f49f01..dd981420f 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksTreeRendererProvider.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksTreeRendererProvider.ts @@ -12,6 +12,7 @@ namespace phasereditor2d.scene.ui.blocks { pack.core.SVG_TYPE, pack.core.ATLAS_TYPE, pack.core.SPRITESHEET_TYPE, + pack.core.ANIMATION_TYPE, pack.core.BITMAP_FONT_TYPE, pack.core.SPINE_JSON_TYPE, pack.core.SPINE_BINARY_TYPE @@ -69,6 +70,11 @@ namespace phasereditor2d.scene.ui.blocks { return true; } + if (obj instanceof pack.core.AnimationConfigInPackItem) { + + return true; + } + return super.isShadowAsChild(obj); } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ImageLoaderExtension.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ImageLoaderExtension.ts index b74ce90e4..e86fd32aa 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ImageLoaderExtension.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ImageLoaderExtension.ts @@ -29,11 +29,25 @@ namespace phasereditor2d.scene.ui.sceneobjects { acceptAsset(asset: any): boolean { return asset instanceof pack.core.ImageFrameContainerAssetPackItem - || asset instanceof pack.core.AssetPackImageFrame; + || asset instanceof pack.core.AssetPackImageFrame + || asset instanceof pack.core.AnimationConfigInPackItem; } async updateLoader(scene: BaseScene, asset: any) { + if (asset instanceof pack.core.AnimationConfigInPackItem) { + + for(const animFrame of asset.getFrames()) { + + const textureFrame = animFrame.getTextureFrame(); + + if (textureFrame) { + + await this.updateLoader(scene, textureFrame); + } + } + } + let imageFrameContainerPackItem: pack.core.ImageFrameContainerAssetPackItem = null; if (asset instanceof pack.core.ImageFrameContainerAssetPackItem) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteExtension.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteExtension.ts index b7c690ab5..e7f6f0712 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteExtension.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteExtension.ts @@ -17,12 +17,40 @@ namespace phasereditor2d.scene.ui.sceneobjects { }); } - getCodeDOMBuilder(): GameObjectCodeDOMBuilder { + override getCodeDOMBuilder(): GameObjectCodeDOMBuilder { return new BaseImageCodeDOMBuilder("sprite"); } - protected newObject(scene: Scene, x: number, y: number, key?: string, frame?: string | number): ISceneGameObject { + override acceptsDropData(data: any): boolean { + + if (data instanceof pack.core.AnimationConfigInPackItem) { + + return data.getFrames().length > 0; + } + + return super.acceptsDropData(data); + } + + override createSceneObjectWithAsset(args: ICreateWithAssetArgs): ISceneGameObject { + + const animConfig = args.asset as pack.core.AnimationConfigInPackItem; + + const frames = animConfig.getFrames(); + + const frame = frames[0]; + + const args2: ICreateWithAssetArgs = { + ...args, + asset: frame.getTextureFrame() + } + + const sprite = super.createSceneObjectWithAsset(args2); + + return sprite; + } + + protected override newObject(scene: Scene, x: number, y: number, key?: string, frame?: string | number): ISceneGameObject { return new Sprite(scene, x, y, key || null, frame); } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AbstractAssetKeyPropertyType.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AbstractAssetKeyPropertyType.ts index 26257d98f..00008fc78 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AbstractAssetKeyPropertyType.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AbstractAssetKeyPropertyType.ts @@ -94,7 +94,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { if (element instanceof pack.core.AnimationConfigInPackItem) { - return new pack.ui.viewers.AnimationConfigCellRenderer(this._finder); + return new pack.ui.viewers.AnimationConfigCellRenderer(); } return super.getCellRenderer(element); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/EventPropertyType.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/EventPropertyType.ts index 36c9debec..f8e971aa1 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/EventPropertyType.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/EventPropertyType.ts @@ -64,11 +64,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { protected async createViewer() { - const finder = new pack.core.PackFinder(); - await finder.preload(); - const viewer = new controls.viewers.TreeViewer("phasereditor2d.scene.editor.EventPropertyType.Dialog"); - viewer.setCellRendererProvider(new EventCellRendererProvider(finder)); + viewer.setCellRendererProvider(new EventCellRendererProvider()); viewer.setLabelProvider(new EventLabelProvider()); viewer.setStyledLabelProvider(new EventPropertyStyleLabelProvider()); viewer.setContentProvider(new controls.viewers.ArrayTreeContentProvider()); @@ -257,7 +254,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { class EventCellRendererProvider implements controls.viewers.ICellRendererProvider { - constructor(private _finder: pack.core.PackFinder) { + constructor() { } @@ -265,7 +262,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { if (element instanceof AnimationEventItem) { - return new AnimationEventCellRenderer(this._finder); + return new AnimationEventCellRenderer(); } else if (element instanceof SkeletonEventItem) { From b8926b094ee1adfeebfdf5a4ecf4c37fdfef94cf Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Wed, 11 Oct 2023 23:15:42 -0400 Subject: [PATCH 09/58] Shows Animations Preview section. --- .../properties/BaseManyImagePreviewSection.ts | 7 +++- .../src/ui/DefaultAssetPackExtension.ts | 2 + .../ui/properties/AnimationsPreviewSection.ts | 38 +++++++++++++++++++ .../AssetPackPreviewPropertyProvider.ts | 1 + .../ui/properties/ManyImagePreviewSection.ts | 1 - 5 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 source/editor/plugins/phasereditor2d.pack/src/ui/properties/AnimationsPreviewSection.ts diff --git a/source/editor/plugins/colibri/src/ui/ide/properties/BaseManyImagePreviewSection.ts b/source/editor/plugins/colibri/src/ui/ide/properties/BaseManyImagePreviewSection.ts index 7bf19a05e..0baf2a422 100644 --- a/source/editor/plugins/colibri/src/ui/ide/properties/BaseManyImagePreviewSection.ts +++ b/source/editor/plugins/colibri/src/ui/ide/properties/BaseManyImagePreviewSection.ts @@ -11,7 +11,7 @@ namespace colibri.ui.ide.properties { const viewer = new controls.viewers.TreeViewer("colibri.ui.ide.properties.ManyImagePreviewFormArea"); viewer.setContentProvider(new controls.viewers.ArrayTreeContentProvider()); - viewer.setTreeRenderer(new controls.viewers.GridTreeViewerRenderer(viewer, false, true).setPaintItemShadow(true)); + viewer.setTreeRenderer(this.createTreeRenderer(viewer)); this.prepareViewer(viewer); @@ -35,6 +35,11 @@ namespace colibri.ui.ide.properties { }); } + protected createTreeRenderer(viewer: controls.viewers.TreeViewer): controls.viewers.TreeViewerRenderer { + + return new controls.viewers.GridTreeViewerRenderer(viewer, false, true).setPaintItemShadow(true); + } + protected abstract getViewerInput(): Promise; protected abstract prepareViewer(viewer: controls.viewers.TreeViewer); diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/DefaultAssetPackExtension.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/DefaultAssetPackExtension.ts index 6533743dc..9347f9b51 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/DefaultAssetPackExtension.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/DefaultAssetPackExtension.ts @@ -320,6 +320,8 @@ namespace phasereditor2d.pack.ui { new ui.properties.ManyImagePreviewSection(page), + new ui.properties.AnimationsPreviewSection(page), + ...exts.flatMap(ext => ext.getSections(page)) ]; } diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AnimationsPreviewSection.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AnimationsPreviewSection.ts new file mode 100644 index 000000000..66f916f36 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AnimationsPreviewSection.ts @@ -0,0 +1,38 @@ +namespace phasereditor2d.pack.ui.properties { + + import controls = colibri.ui.controls; + + export class AnimationsPreviewSection extends colibri.ui.ide.properties.BaseManyImagePreviewSection { + + constructor(page: controls.properties.PropertyPage) { + super(page, "phasereditor2d.pack.ui.properties.AnimationsPreviewSection", "Animations Preview", true); + } + + protected override async getViewerInput() { + + const frames = this.getSelection().flatMap(obj => { + + return obj.getAnimations(); + }); + + return frames; + } + + protected override prepareViewer(viewer: controls.viewers.TreeViewer) { + + viewer.setLabelProvider(new viewers.AssetPackLabelProvider()); + viewer.setCellRendererProvider(new controls.viewers.EmptyCellRendererProvider( + e => new viewers.AnimationConfigCellRenderer("square"))); + } + + override canEdit(obj: any, n: number): boolean { + + return obj instanceof core.AnimationsAssetPackItem; + } + + override canEditNumber(n: number): boolean { + + return n > 0; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AssetPackPreviewPropertyProvider.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AssetPackPreviewPropertyProvider.ts index 27dc015ea..0b5328706 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AssetPackPreviewPropertyProvider.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AssetPackPreviewPropertyProvider.ts @@ -11,6 +11,7 @@ namespace phasereditor2d.pack.ui.properties { sections.push(new pack.ui.properties.AtlasFrameInfoSection(page)); sections.push(new pack.ui.properties.ImagePreviewSection(page)); sections.push(new pack.ui.properties.ManyImagePreviewSection(page)); + sections.push(new pack.ui.properties.AnimationsPreviewSection(page)); sections.push(new pack.ui.properties.BitmapFontPreviewSection(page)); sections.push(new pack.ui.properties.ManyBitmapFontPreviewSection(page)); sections.push(new pack.ui.properties.TilemapTiledSection(page)); diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/properties/ManyImagePreviewSection.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/properties/ManyImagePreviewSection.ts index af252ce8d..c2f010ae2 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/properties/ManyImagePreviewSection.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/properties/ManyImagePreviewSection.ts @@ -1,7 +1,6 @@ namespace phasereditor2d.pack.ui.properties { import controls = colibri.ui.controls; - import ide = colibri.ui.ide; export class ManyImagePreviewSection extends colibri.ui.ide.properties.BaseManyImagePreviewSection { From 87a2e71e9f0d705f10d5f1c3710f421cea71b3b7 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 12 Oct 2023 01:15:44 -0400 Subject: [PATCH 10/58] Shows the Animation section for Sprite objects. --- .../phasereditor2d.scene/src/ScenePlugin.ts | 1 + .../object/properties/SceneObjectSection.ts | 3 - .../sceneobjects/sprite/AnimationSection.ts | 104 ++++++++++++++++++ .../src/ui/sceneobjects/sprite/Sprite.ts | 9 ++ .../ui/sceneobjects/sprite/SpriteComponent.ts | 40 +++++++ .../sprite/SpriteEditorSupport.ts | 2 + .../ui/sceneobjects/sprite/SpriteExtension.ts | 5 +- .../AnimationKeyPropertyType.ts | 6 + 8 files changed, 166 insertions(+), 4 deletions(-) create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationSection.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteComponent.ts diff --git a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts index a4ab6930e..3b4ed3989 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts @@ -289,6 +289,7 @@ namespace phasereditor2d.scene { page => new ui.sceneobjects.ArcadeGeometrySection(page), page => new ui.sceneobjects.ArcadeBodyMovementSection(page), page => new ui.sceneobjects.ArcadeBodyCollisionSection(page), + page => new ui.sceneobjects.AnimationSection(page), page => new ui.sceneobjects.TextContentSection(page), page => new ui.sceneobjects.TextSection(page), page => new ui.sceneobjects.BitmapTextSection(page), diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/SceneObjectSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/SceneObjectSection.ts index ef11fe665..a225ffad4 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/SceneObjectSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/SceneObjectSection.ts @@ -105,7 +105,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { text.readOnly = forceReadOnly || checkUnlock && !this.isUnlocked(property); - if (readOnlyOnMultiple) { text.readOnly = text.readOnly || readOnlyOnMultiple && this.getSelection().length > 1; @@ -389,8 +388,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { this.addUpdater(() => { - console.log("update button man!"); - result.updateDialogButtonIcon(); }); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationSection.ts new file mode 100644 index 000000000..5cc1ed7dc --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationSection.ts @@ -0,0 +1,104 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + import controls = colibri.ui.controls; + + export class AnimationSection extends SceneGameObjectSection { + + constructor(page: controls.properties.PropertyPage) { + super(page, "phasereditor2d.scene.ui.sceneobjects.AnimationSection", "Animation"); + } + + protected getSectionHelpPath() { + + return "TODO"; + } + + createForm(parent: HTMLDivElement) { + + const comp = this.createGridElement(parent, 4); + comp.style.gridTemplateColumns = "auto auto 1fr auto"; + + { + const btn = this.createPropertyEnumRow(comp, SpriteComponent.playMethod); + btn.style.gridColumn = "3 / span 2"; + } + + { + // play animation + + this.createPropertyStringRow(comp, SpriteComponent.playAnimation); + + const btnUI = this.createButtonDialog({ + dialogTittle: "Select Animation Key", + createDialogViewer: async (revealValue: string) => { + + const viewer = new controls.viewers.TreeViewer("phasereditor2d.scene.ui.sceneobjects.AnimationSection." + this.getId()); + + viewer.setCellRendererProvider(new controls.viewers.EmptyCellRendererProvider(e => new pack.ui.viewers.AnimationConfigCellRenderer())); + viewer.setLabelProvider(new pack.ui.viewers.AssetPackLabelProvider()); + viewer.setTreeRenderer(new controls.viewers.TreeViewerRenderer(viewer)); + viewer.setContentProvider(new controls.viewers.ArrayTreeContentProvider()); + + const finder = new pack.core.PackFinder(); + await finder.preload(); + + const animations = finder + .getAssets(i => i instanceof pack.core.AnimationsAssetPackItem) + .flatMap((i: pack.core.AnimationsAssetPackItem) => i.getAnimations()); + + viewer.setInput(animations); + + viewer.revealAndSelect(animations.find(a => a.getKey() === revealValue)); + + return viewer; + }, + getValue: () => { + + return this.getSelection()[0].playAnimation || ""; + }, + onValueSelected: (value: string) => { + + this.getEditor().getUndoManager().add( + new SimpleOperation(this.getEditor(), this.getSelection(), SpriteComponent.playAnimation, value)); + }, + dialogElementToString: (viewer: controls.viewers.TreeViewer, value: pack.core.AnimationConfigInPackItem): string => { + + return value.getKey(); + }, + updateIconCallback: async (iconControl, value) => { + + const finder = new pack.core.PackFinder(); + + await finder.preload(); + + const image = AnimationKeyPropertyType.getAnimationIcon(finder, value); + + iconControl.setIcon(image); + }, + }); + + comp.appendChild(btnUI.buttonElement); + + this.addUpdater(() => { + + btnUI.buttonElement.disabled = this.getSelection() + .filter(sprite => !sprite.getEditorSupport() + .isUnlockedProperty(SpriteComponent.playAnimation)) + .length > 0; + + btnUI.updateDialogButtonIcon(); + }); + } + } + + canEdit(obj: any, n: number): boolean { + + return obj instanceof Sprite; + } + + canEditNumber(n: number): boolean { + + return n > 0; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/Sprite.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/Sprite.ts index 10a504f77..123706283 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/Sprite.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/Sprite.ts @@ -1,9 +1,18 @@ namespace phasereditor2d.scene.ui.sceneobjects { + export enum PlayMethod { + NONE = 0, + PLAY = 1, + PLAY_REVERSE = 2 + } + export class Sprite extends Phaser.GameObjects.Image implements ISceneGameObject { private _editorSupport: SpriteEditorSupport; + public playMethod: PlayMethod = PlayMethod.NONE; + public playAnimation = ""; + constructor( scene: Scene, x: number, y: number, texture: string, frame?: string | number) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteComponent.ts new file mode 100644 index 000000000..750d1cbf7 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteComponent.ts @@ -0,0 +1,40 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + export class SpriteComponent extends Component { + + static playMethod: IEnumProperty = { + name: "playMethod", + label: "Action", + defValue: PlayMethod.NONE, + getValue: obj => obj.playMethod, + setValue: (obj: Sprite, val: PlayMethod) => { obj.playMethod = val; }, + getValueLabel: val => { + switch (val) { + + case PlayMethod.NONE: + return "NONE"; + + case PlayMethod.PLAY: + return "PLAY"; + + case PlayMethod.PLAY_REVERSE: + return "PLAY_REVERSE"; + } + }, + values: [PlayMethod.NONE, PlayMethod.PLAY, PlayMethod.PLAY_REVERSE], + }; + + static playAnimation = SimpleProperty("playAnimation", "", "Play Animation", "The animation to play."); + + constructor(obj: Sprite) { + super(obj, [ + SpriteComponent.playMethod, + SpriteComponent.playAnimation + ]) + } + + buildSetObjectPropertiesCodeDOM(args: ISetObjectPropertiesCodeDOMArgs): void { + // TODO + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteEditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteEditorSupport.ts index 56311c348..b456c0219 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteEditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteEditorSupport.ts @@ -6,6 +6,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { constructor(obj: Sprite, scene: Scene) { super(SpriteExtension.getInstance(), obj, scene); + + this.addComponent(new SpriteComponent(obj)); } } } \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteExtension.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteExtension.ts index e7f6f0712..2edb84629 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteExtension.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteExtension.ts @@ -45,7 +45,10 @@ namespace phasereditor2d.scene.ui.sceneobjects { asset: frame.getTextureFrame() } - const sprite = super.createSceneObjectWithAsset(args2); + const sprite = super.createSceneObjectWithAsset(args2) as Sprite; + + sprite.playMethod = PlayMethod.PLAY; + sprite.playAnimation = animConfig.getKey(); return sprite; } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AnimationKeyPropertyType.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AnimationKeyPropertyType.ts index 12bd4d0ed..fc4805eb9 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AnimationKeyPropertyType.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AnimationKeyPropertyType.ts @@ -14,8 +14,14 @@ namespace phasereditor2d.scene.ui.sceneobjects { }); } + protected getIcon(finder: pack.core.PackFinder, value: string): controls.IImage { + return AnimationKeyPropertyType.getAnimationIcon(finder, value); + } + + static getAnimationIcon(finder: pack.core.PackFinder, value: string): controls.IImage { + const animation = finder.getPacks() .flatMap(pack => pack.getItems()) From c31ff83387262029e50613838fa045a386bd3309 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 12 Oct 2023 01:37:27 -0400 Subject: [PATCH 11/58] Improves border painting. --- .../plugins/phasereditor2d.scene/src/ui/editor/OverlayLayer.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/OverlayLayer.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/OverlayLayer.ts index b9778f0ec..9e1a76629 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/OverlayLayer.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/OverlayLayer.ts @@ -297,7 +297,8 @@ namespace phasereditor2d.scene.ui.editor { ctx.restore(); ctx.lineWidth = 1; - ctx.strokeRect(a.x - 3, a.y - 3, b.x - a.x + 6, b.y - a.y + 6); + ctx.strokeStyle = theme.dark ? "#a0a0a0" : "#f0f0f0"; + ctx.strokeRect(a.x - 1, a.y - 1, b.x - a.x + 2, b.y - a.y + 2); ctx.restore(); } } From 40d14bff0d2baadd898abdce0183e96f6b48fb7f Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 12 Oct 2023 02:10:14 -0400 Subject: [PATCH 12/58] Generates sprite animation code. --- .../ui/sceneobjects/sprite/SpriteComponent.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteComponent.ts index 750d1cbf7..5c28c3dab 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteComponent.ts @@ -34,7 +34,23 @@ namespace phasereditor2d.scene.ui.sceneobjects { } buildSetObjectPropertiesCodeDOM(args: ISetObjectPropertiesCodeDOMArgs): void { - // TODO + + this.buildSetObjectPropertyCodeDOM([SpriteComponent.playMethod], args2 => { + + const sprite = args.obj as Sprite; + + const method = sprite.playMethod; + + if (method === PlayMethod.PLAY || method === PlayMethod.PLAY_REVERSE) { + + const name = method === PlayMethod.PLAY? "play" : "playReverse"; + + const call = new core.code.MethodCallCodeDOM(name, args.objectVarName); + call.argLiteral(sprite.playAnimation); + + args.statements.push(call); + } + }); } } } \ No newline at end of file From 1b11890e08508d3aaa27ce67d1e4fbfc8f440056 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 12 Oct 2023 09:36:05 -0400 Subject: [PATCH 13/58] Fixes Asset Pack editor outline rendering. --- .../src/ui/controls/viewers/TreeViewerRenderer.ts | 2 +- .../src/ui/editor/AssetPackEditor.ts | 10 ++++++---- .../src/ui/editor/properties/BlocksSection.ts | 2 +- .../src/ui/viewers/AnimationConfigCellRenderer.ts | 2 +- .../phasereditor2d.scene/src/ui/editor/SceneEditor.ts | 2 ++ 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/source/editor/plugins/colibri/src/ui/controls/viewers/TreeViewerRenderer.ts b/source/editor/plugins/colibri/src/ui/controls/viewers/TreeViewerRenderer.ts index b9fb12acd..ac0563c7a 100644 --- a/source/editor/plugins/colibri/src/ui/controls/viewers/TreeViewerRenderer.ts +++ b/source/editor/plugins/colibri/src/ui/controls/viewers/TreeViewerRenderer.ts @@ -170,7 +170,7 @@ namespace colibri.ui.controls.viewers { y += 15; - } else if (renderer.layout === "full-width") { + } else if (renderer.layout === "full-width" && args.h > ROW_HEIGHT * 2) { args2 = new RenderCellArgs( args.canvasContext, args.x, args.y, args.w, args.h - 20, args.obj, args.viewer); diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/editor/AssetPackEditor.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/editor/AssetPackEditor.ts index 30b33775b..f770b8103 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/editor/AssetPackEditor.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/editor/AssetPackEditor.ts @@ -151,7 +151,9 @@ namespace phasereditor2d.pack.ui.editor { this.getViewer().repaint(); - await this.updateBlocks(); + await this.refreshBlocks(); + + this._outlineProvider.repaint(); if (this._revealKey) { @@ -212,7 +214,7 @@ namespace phasereditor2d.pack.ui.editor { await this.resetPackCache(); - await this.updateBlocks(); + await this.refreshBlocks(); } private async resetPackCache() { @@ -408,7 +410,7 @@ namespace phasereditor2d.pack.ui.editor { this.setDirty(true); - await this.updateBlocks(); + await this.refreshBlocks(); this._viewer.setSelection(items); @@ -419,7 +421,7 @@ namespace phasereditor2d.pack.ui.editor { this.getUndoManager().add(new undo.AssetPackEditorOperation(this, before, after)); } - async updateBlocks() { + async refreshBlocks() { if (!this._pack) { return; diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/editor/properties/BlocksSection.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/editor/properties/BlocksSection.ts index 5b21aa2ad..48baa1a29 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/editor/properties/BlocksSection.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/editor/properties/BlocksSection.ts @@ -18,7 +18,7 @@ namespace phasereditor2d.pack.ui.editor.properties { this.getSelectionFirstElement().setShowAllFilesInBlocks(check.checked); const editor = colibri.Platform.getWorkbench().getActiveEditor() as AssetPackEditor; - editor.updateBlocks(); + editor.refreshBlocks(); editor.setDirty(true); }); diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AnimationConfigCellRenderer.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AnimationConfigCellRenderer.ts index 1bd71b77a..ff9f1bc9b 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AnimationConfigCellRenderer.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AnimationConfigCellRenderer.ts @@ -38,7 +38,7 @@ namespace phasereditor2d.pack.ui.viewers { ctx.save(); - if (cellSize <= controls.ROW_HEIGHT || this.layout === "square") { + if (cellSize <= controls.ROW_HEIGHT * 2 || this.layout === "square") { const img = this.getImage(frames[0]); 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 7337633fc..1f98600e1 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditor.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditor.ts @@ -866,10 +866,12 @@ namespace phasereditor2d.scene.ui.editor { } getOutlineProvider() { + return this._outlineProvider; } refreshOutline() { + this._outlineProvider.repaint(); } From 582baf870cf9657060bca1bf97cd775d496e4abc Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 12 Oct 2023 10:13:55 -0400 Subject: [PATCH 14/58] Adds Animation Preview section. --- .../src/core/AnimationConfigInPackItem.ts | 22 +++++++++++++++ .../core/AnimationFrameConfigInPackItem.ts | 17 +++++++++++ .../src/ui/DefaultAssetPackExtension.ts | 2 ++ .../ui/properties/AnimationPreviewSection.ts | 26 +++++++++++++++++ .../ui/viewers/AnimationConfigCellRenderer.ts | 28 ++++--------------- .../AnimationKeyPropertyType.ts | 12 +------- 6 files changed, 74 insertions(+), 33 deletions(-) create mode 100644 source/editor/plugins/phasereditor2d.pack/src/ui/properties/AnimationPreviewSection.ts diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/AnimationConfigInPackItem.ts b/source/editor/plugins/phasereditor2d.pack/src/core/AnimationConfigInPackItem.ts index 3176078bc..6e4a22bc6 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/core/AnimationConfigInPackItem.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/core/AnimationConfigInPackItem.ts @@ -31,5 +31,27 @@ namespace phasereditor2d.pack.core { return this._frames; } + + getPreviewFrame() { + + if (this._frames.length > 0) { + + return this._frames[Math.floor(frames.length / 2)]; + } + + return null; + } + + getPreviewImageAsset() { + + const frame = this.getPreviewFrame(); + + if (frame) { + + return frame.getImageAsset(); + } + + return null; + } } } \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/AnimationFrameConfigInPackItem.ts b/source/editor/plugins/phasereditor2d.pack/src/core/AnimationFrameConfigInPackItem.ts index f1b08b114..c8cad091b 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/core/AnimationFrameConfigInPackItem.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/core/AnimationFrameConfigInPackItem.ts @@ -11,6 +11,23 @@ namespace phasereditor2d.pack.core { this._textureFrame = textureFrame; } + getImageAsset() { + + if (this._textureFrame) { + + if (this._textureFrame instanceof pack.core.ImageAssetPackItem) { + + return this._textureFrame.getFrames()[0]; + + } else if (this._textureFrame instanceof pack.core.AssetPackImageFrame) { + + return this._textureFrame; + } + } + + return null; + } + getTextureFrame() { return this._textureFrame; diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/DefaultAssetPackExtension.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/DefaultAssetPackExtension.ts index 9347f9b51..b4a0c67f0 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/DefaultAssetPackExtension.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/DefaultAssetPackExtension.ts @@ -322,6 +322,8 @@ namespace phasereditor2d.pack.ui { new ui.properties.AnimationsPreviewSection(page), + new ui.properties.AnimationPreviewSection(page), + ...exts.flatMap(ext => ext.getSections(page)) ]; } diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AnimationPreviewSection.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AnimationPreviewSection.ts new file mode 100644 index 000000000..9022e7710 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AnimationPreviewSection.ts @@ -0,0 +1,26 @@ +namespace phasereditor2d.pack.ui.properties { + + import controls = colibri.ui.controls; + + export class AnimationPreviewSection + extends colibri.ui.ide.properties.BaseImagePreviewSection { + + constructor(page: controls.properties.PropertyPage) { + super(page, "phasereditor2d.pack.ui.properties.AnimationPreviewSection", "Animation Preview", true); + } + + protected getSelectedImage(): controls.IImage { + + const anim = this.getSelection()[0]; + + const img = anim.getPreviewImageAsset(); + + return img; + } + + canEdit(obj: any): boolean { + + return obj instanceof core.AnimationConfigInPackItem; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AnimationConfigCellRenderer.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AnimationConfigCellRenderer.ts index ff9f1bc9b..8f78662bf 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AnimationConfigCellRenderer.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AnimationConfigCellRenderer.ts @@ -30,17 +30,13 @@ namespace phasereditor2d.pack.ui.viewers { const cellSize = args.viewer.getCellSize(); - const len = frames.length; - - const indexes = [0, Math.floor(len / 2), len - 1]; - const ctx = args.canvasContext; ctx.save(); if (cellSize <= controls.ROW_HEIGHT * 2 || this.layout === "square") { - const img = this.getImage(frames[0]); + const img = anim.getPreviewImageAsset(); if (img) { @@ -49,12 +45,16 @@ namespace phasereditor2d.pack.ui.viewers { } else { + const len = frames.length; + + const indexes = [0, Math.floor(len / 2), len - 1]; + // tslint:disable-next-line:prefer-for-of for (let i = 0; i < indexes.length; i++) { const frame = frames[indexes[i]]; - const img = this.getImage(frame); + const img = frame.getImageAsset(); if (img) { @@ -68,22 +68,6 @@ namespace phasereditor2d.pack.ui.viewers { ctx.restore(); } - private getImage(frame: pack.core.AnimationFrameConfigInPackItem) { - - const texture = frame.getTextureFrame(); - - if (texture instanceof pack.core.ImageAssetPackItem) { - - return texture.getFrames()[0]; - - } else if (texture instanceof pack.core.AssetPackImageFrame) { - - return texture; - } - - return null; - } - cellHeight(args: controls.viewers.RenderCellArgs): number { return args.viewer.getCellSize(); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AnimationKeyPropertyType.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AnimationKeyPropertyType.ts index fc4805eb9..e004d1619 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AnimationKeyPropertyType.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AnimationKeyPropertyType.ts @@ -34,17 +34,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { if (animation) { - const frames = animation.getFrames(); - - if (frames.length > 0) { - - const frame = frames[Math.floor(frames.length / 2)]; - - if (frame) { - - return finder.getAssetPackItemImage(frame.getTextureKey(), frame.getFrameKey()); - } - } + return animation.getPreviewImageAsset(); } return null; From e41a5cda5cb228aa7bc1cf2a299443099562f227 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 12 Oct 2023 10:16:53 -0400 Subject: [PATCH 15/58] Updates changelog. --- CHANGELOG.MD | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 5145dd93b..786f66b5a 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -6,6 +6,11 @@ * Allows merging Import and Open buttons in the Asset Pack Entry section. * [#251](https://github.com/PhaserEditor2D/PhaserEditor2D-v3/issues/251) Fixes sprite positioning in Animations Editor. * [#209](https://github.com/PhaserEditor2D/PhaserEditor2D-v3/issues/209) Improves scene border painting. +* Improves animations preview UI: + - New Animation preview section. + - New Animations Preview section. +* Adds new Animation section to Sprite game objects. It is about to auto-start a sprite animation. +* Shows Sprite Animation blocks in the Scene Editor's Blocks view. ## v3.63.0 - Sep 30, 2023 From e1c5cdd6a90174568d378223fd60598c1686bd06 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 12 Oct 2023 10:25:52 -0400 Subject: [PATCH 16/58] Fixes animation editor's outline renderer. --- .../src/ui/editors/EditorAnimationCellRenderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/EditorAnimationCellRenderer.ts b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/EditorAnimationCellRenderer.ts index 3942e6db2..600acd09f 100644 --- a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/EditorAnimationCellRenderer.ts +++ b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/EditorAnimationCellRenderer.ts @@ -37,7 +37,7 @@ namespace phasereditor2d.animations.ui.editors { ctx.save(); - if (cellSize <= controls.ROW_HEIGHT) { + if (cellSize <= controls.ROW_HEIGHT * 2) { const img = this.getImage(frames[0]); From 3765428d4af2877ea5d74ef88eb75d7d2b40ccff Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 12 Oct 2023 11:19:10 -0400 Subject: [PATCH 17/58] Adds Animation Info section. --- .../src/AnimationsPlugin.ts | 25 ++++++- .../src/ui/editors/AnimationsEditor.ts | 30 +++++++- .../properties/AnimationInfoSection.ts | 73 +++++++++++++++++++ .../src/core/AnimationsAssetPackItem.ts | 7 +- .../src/core/PackFinder.ts | 13 +++- .../AssetPackPreviewPropertyProvider.ts | 1 + 6 files changed, 145 insertions(+), 4 deletions(-) create mode 100644 source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationInfoSection.ts diff --git a/source/editor/plugins/phasereditor2d.animations/src/AnimationsPlugin.ts b/source/editor/plugins/phasereditor2d.animations/src/AnimationsPlugin.ts index d758e2495..e5331dee0 100644 --- a/source/editor/plugins/phasereditor2d.animations/src/AnimationsPlugin.ts +++ b/source/editor/plugins/phasereditor2d.animations/src/AnimationsPlugin.ts @@ -19,6 +19,23 @@ namespace phasereditor2d.animations { super("phasereditor2d.animations"); } + async openAnimationInEditor(anim: pack.core.AnimationConfigInPackItem) { + + const animationsItem = anim.getParent(); + + const file = animationsItem.getAnimationsFile(); + + if (file) { + + const editor = colibri.Platform.getWorkbench().openEditor(file); + + if (editor instanceof ui.editors.AnimationsEditor) { + + editor.selectAnimationByKey(anim.getKey()); + } + } + } + getPhaserDocs() { if (!this._docs) { @@ -26,7 +43,7 @@ namespace phasereditor2d.animations { this._docs = new phasereditor2d.ide.core.PhaserDocs( resources.ResourcesPlugin.getInstance(), "phasereditor2d.animations/docs/phaser-docs.json"); } - + return this._docs; } @@ -48,6 +65,12 @@ namespace phasereditor2d.animations { reg.addExtension( new colibri.ui.ide.commands.CommandExtension(manager => this.registerCommands(manager)) ); + + // asset pack preview extension + + reg.addExtension(new pack.ui.AssetPackPreviewPropertyProviderExtension( + page => new ui.editors.properties.AnimationInfoSection(page), + )); } private registerCommands(manager: colibri.ui.ide.commands.CommandManager) { diff --git a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/AnimationsEditor.ts b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/AnimationsEditor.ts index b0f969036..fa0789e82 100644 --- a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/AnimationsEditor.ts +++ b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/AnimationsEditor.ts @@ -21,6 +21,8 @@ namespace phasereditor2d.animations.ui.editors { private _currentDependenciesHash: string; private _menuCreator: AnimationsEditorMenuCreator; private _model: AnimationsModel; + private _editorReady = false; + private _selectAnimationKeyOnBoot: string; static getFactory() { @@ -502,7 +504,21 @@ namespace phasereditor2d.animations.ui.editors { this.refreshOutline(); - this.setSelection([]); + let selection: Phaser.Animations.Animation[] = []; + + if (this._selectAnimationKeyOnBoot) { + + const anims = this.getAnimations(); + + if (anims) { + + selection = anims.filter(a => a.key === this._selectAnimationKeyOnBoot); + } + } + + this.setSelection(selection); + + this._editorReady = true; } private async readScene() { @@ -744,6 +760,18 @@ namespace phasereditor2d.animations.ui.editors { return list; } + selectAnimationByKey(animationKey: string) { + + if (this._editorReady) { + + this.setSelection(this.getAnimations().filter(a => a.key === animationKey)); + + } else { + + this._selectAnimationKeyOnBoot = animationKey; + } + } + getAnimations() { return this._scene.anims["anims"].getArray(); diff --git a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationInfoSection.ts b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationInfoSection.ts new file mode 100644 index 000000000..751cfc232 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationInfoSection.ts @@ -0,0 +1,73 @@ +namespace phasereditor2d.animations.ui.editors.properties { + + import controls = colibri.ui.controls; + + export class AnimationInfoSection + extends controls.properties.PropertySection { + + constructor(page: controls.properties.PropertyPage) { + super(page, "phasereditor2d.animations.ui.editors.properties", "Animation Info"); + } + + createForm(parent: HTMLDivElement): void { + + const comp = this.createGridElement(parent, 2); + + { + // Animation Key + this.createLabel(comp, "Animation Key"); + + const btn = this.createButton(comp, "", () => { + + const anim = this.getSelectionFirstElement(); + + AnimationsPlugin.getInstance().openAnimationInEditor(anim); + }); + + this.addUpdater(() => { + + const anim = this.getSelectionFirstElement(); + + btn.textContent = anim.getKey(); + }); + } + + + { + // Animations File + this.createLabel(comp, "Animations File"); + + const btn = this.createButton(comp, "", () => { + + const file = this.getSelectionFirstElement().getParent().getAnimationsFile(); + + if (file) { + + colibri.Platform.getWorkbench().openEditor(file); + } + }); + + this.addUpdater(() => { + + const anim = this.getSelectionFirstElement(); + + const file = anim.getParent().getAnimationsFile(); + + btn.textContent = file ? + file.getName() + " - " + file.getParent().getProjectRelativeName() + : ""; + }); + } + } + + canEdit(obj: any, n: number): boolean { + + return obj instanceof pack.core.AnimationConfigInPackItem; + } + + canEditNumber(n: number): boolean { + + return n === 1; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/AnimationsAssetPackItem.ts b/source/editor/plugins/phasereditor2d.pack/src/core/AnimationsAssetPackItem.ts index db930288c..9700d89b7 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/core/AnimationsAssetPackItem.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/core/AnimationsAssetPackItem.ts @@ -22,6 +22,11 @@ namespace phasereditor2d.pack.core { return this._animations || []; } + getAnimationsFile() { + + return this.getFileFromAssetUrl(this.getUrl()); + } + async preload() { if (this._animations) { @@ -33,7 +38,7 @@ namespace phasereditor2d.pack.core { try { - const file = this.getFileFromAssetUrl(this.getUrl()); + const file = this.getAnimationsFile(); if (file) { diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/PackFinder.ts b/source/editor/plugins/phasereditor2d.pack/src/core/PackFinder.ts index 5f25cd958..c543c2ee7 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/core/PackFinder.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/core/PackFinder.ts @@ -25,7 +25,7 @@ namespace phasereditor2d.pack.core { for (const item of items) { const result2 = await item.preload(); - + result = Math.max(result, result2); if (monitor) { @@ -55,6 +55,17 @@ namespace phasereditor2d.pack.core { .filter(i => !filter || filter(i)); } + findAnimationByKey(key: string) { + + return this.getAssets() + + .filter(i => i instanceof AnimationsAssetPackItem) + + .flatMap((i: AnimationsAssetPackItem) => i.getAnimations()) + + .find(a => a.getKey() === key); + } + findAssetPackItem(key: string) { if (!key) { diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AssetPackPreviewPropertyProvider.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AssetPackPreviewPropertyProvider.ts index 0b5328706..7478e07c9 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AssetPackPreviewPropertyProvider.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AssetPackPreviewPropertyProvider.ts @@ -12,6 +12,7 @@ namespace phasereditor2d.pack.ui.properties { sections.push(new pack.ui.properties.ImagePreviewSection(page)); sections.push(new pack.ui.properties.ManyImagePreviewSection(page)); sections.push(new pack.ui.properties.AnimationsPreviewSection(page)); + sections.push(new pack.ui.properties.AnimationPreviewSection(page)); sections.push(new pack.ui.properties.BitmapFontPreviewSection(page)); sections.push(new pack.ui.properties.ManyBitmapFontPreviewSection(page)); sections.push(new pack.ui.properties.TilemapTiledSection(page)); From 2aa58acbe1d704c376de2af18052109e85b4fbc5 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 12 Oct 2023 11:41:49 -0400 Subject: [PATCH 18/58] Sort preview sections. --- .../src/ui/controls/properties/PropertyPage.ts | 1 + .../src/ui/controls/properties/PropertySection.ts | 1 + .../ui/controls/properties/PropertySectionProvider.ts | 11 +++++++++++ .../src/ui/editors/properties/AnimationInfoSection.ts | 2 +- .../src/ui/views/FilePropertySectionProvider.ts | 8 +------- .../properties/AssetPackEditorPropertyProvider.ts | 2 ++ .../ui/properties/AssetPackPreviewPropertyProvider.ts | 2 ++ 7 files changed, 19 insertions(+), 8 deletions(-) diff --git a/source/editor/plugins/colibri/src/ui/controls/properties/PropertyPage.ts b/source/editor/plugins/colibri/src/ui/controls/properties/PropertyPage.ts index 774448858..fd18797d8 100644 --- a/source/editor/plugins/colibri/src/ui/controls/properties/PropertyPage.ts +++ b/source/editor/plugins/colibri/src/ui/controls/properties/PropertyPage.ts @@ -192,6 +192,7 @@ namespace colibri.ui.controls.properties { const sortedPanes = this._sectionPanes .map(p => p) .sort((a, b) => + sectionIdList.indexOf(a.getSection().getId()) - sectionIdList.indexOf(b.getSection().getId()) ); diff --git a/source/editor/plugins/colibri/src/ui/controls/properties/PropertySection.ts b/source/editor/plugins/colibri/src/ui/controls/properties/PropertySection.ts index 40187eb37..3afefc04d 100644 --- a/source/editor/plugins/colibri/src/ui/controls/properties/PropertySection.ts +++ b/source/editor/plugins/colibri/src/ui/controls/properties/PropertySection.ts @@ -81,6 +81,7 @@ namespace colibri.ui.controls.properties { } isFillSpace() { + return this._fillSpace; } diff --git a/source/editor/plugins/colibri/src/ui/controls/properties/PropertySectionProvider.ts b/source/editor/plugins/colibri/src/ui/controls/properties/PropertySectionProvider.ts index b34d6f6ad..c9415382b 100644 --- a/source/editor/plugins/colibri/src/ui/controls/properties/PropertySectionProvider.ts +++ b/source/editor/plugins/colibri/src/ui/controls/properties/PropertySectionProvider.ts @@ -11,6 +11,17 @@ namespace colibri.ui.controls.properties { abstract addSections(page: PropertyPage, sections: Array>): void; + sortSections(sections: controls.properties.PropertySection[]) { + + sections.sort((a, b) => { + + const aa = a.isFillSpace() ? 1 : 0; + const bb = b.isFillSpace() ? 1 : 0; + + return aa - bb; + }); + } + getEmptySelectionObject() { return null; diff --git a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationInfoSection.ts b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationInfoSection.ts index 751cfc232..c494e1ba1 100644 --- a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationInfoSection.ts +++ b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationInfoSection.ts @@ -6,7 +6,7 @@ namespace phasereditor2d.animations.ui.editors.properties { extends controls.properties.PropertySection { constructor(page: controls.properties.PropertyPage) { - super(page, "phasereditor2d.animations.ui.editors.properties", "Animation Info"); + super(page, "phasereditor2d.animations.ui.editors.properties", "Animation Info", false); } createForm(parent: HTMLDivElement): void { diff --git a/source/editor/plugins/phasereditor2d.files/src/ui/views/FilePropertySectionProvider.ts b/source/editor/plugins/phasereditor2d.files/src/ui/views/FilePropertySectionProvider.ts index 99bfd2fac..41faae380 100644 --- a/source/editor/plugins/phasereditor2d.files/src/ui/views/FilePropertySectionProvider.ts +++ b/source/editor/plugins/phasereditor2d.files/src/ui/views/FilePropertySectionProvider.ts @@ -23,13 +23,7 @@ namespace phasereditor2d.files.ui.views { } } - sections.sort((a, b) => { - - const aa = a.isFillSpace() ? 1 : 0; - const bb = b.isFillSpace() ? 1 : 0; - - return aa - bb; - }); + this.sortSections(sections); } protected acceptSection(section: controls.properties.PropertySection) { diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/editor/properties/AssetPackEditorPropertyProvider.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/editor/properties/AssetPackEditorPropertyProvider.ts index 437d10304..014ce89fd 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/editor/properties/AssetPackEditorPropertyProvider.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/editor/properties/AssetPackEditorPropertyProvider.ts @@ -15,6 +15,8 @@ namespace phasereditor2d.pack.ui.editor.properties { .flatMap(ext => ext.createEditorPropertySections(page)); sections.push(...list); + + this.sortSections(sections); } getEmptySelectionObject() { diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AssetPackPreviewPropertyProvider.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AssetPackPreviewPropertyProvider.ts index 7478e07c9..5283460e3 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AssetPackPreviewPropertyProvider.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AssetPackPreviewPropertyProvider.ts @@ -27,6 +27,8 @@ namespace phasereditor2d.pack.ui.properties { sections.push(...ext.getSections(page)); } + + this.sortSections(sections); } } } \ No newline at end of file From 3e253d97087df71ab9329895ebefe0685c3647ed Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 12 Oct 2023 12:17:19 -0400 Subject: [PATCH 19/58] Adds the Open Animation button to the Sprite Animation section. --- .../src/AnimationsPlugin.ts | 5 ++++ .../phasereditor2d.scene/src/ScenePlugin.ts | 6 ++++- ...onSection.ts => SpriteAnimationSection.ts} | 24 +++++++++++++++++-- 3 files changed, 32 insertions(+), 3 deletions(-) rename source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/{AnimationSection.ts => SpriteAnimationSection.ts} (82%) diff --git a/source/editor/plugins/phasereditor2d.animations/src/AnimationsPlugin.ts b/source/editor/plugins/phasereditor2d.animations/src/AnimationsPlugin.ts index e5331dee0..5a8920225 100644 --- a/source/editor/plugins/phasereditor2d.animations/src/AnimationsPlugin.ts +++ b/source/editor/plugins/phasereditor2d.animations/src/AnimationsPlugin.ts @@ -71,6 +71,11 @@ namespace phasereditor2d.animations { reg.addExtension(new pack.ui.AssetPackPreviewPropertyProviderExtension( page => new ui.editors.properties.AnimationInfoSection(page), )); + + scene.ScenePlugin.getInstance().openAnimationInEditor = anim => { + + return this.openAnimationInEditor(anim); + }; } private registerCommands(manager: colibri.ui.ide.commands.CommandManager) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts index 3b4ed3989..58b85b709 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts @@ -289,7 +289,7 @@ namespace phasereditor2d.scene { page => new ui.sceneobjects.ArcadeGeometrySection(page), page => new ui.sceneobjects.ArcadeBodyMovementSection(page), page => new ui.sceneobjects.ArcadeBodyCollisionSection(page), - page => new ui.sceneobjects.AnimationSection(page), + page => new ui.sceneobjects.SpriteAnimationSection(page), page => new ui.sceneobjects.TextContentSection(page), page => new ui.sceneobjects.TextSection(page), page => new ui.sceneobjects.BitmapTextSection(page), @@ -353,6 +353,10 @@ namespace phasereditor2d.scene { )); } + async openAnimationInEditor(anim: pack.core.AnimationConfigInPackItem) { + // nothing, it is injected in the AnimationsPlugin. + } + getTools() { return colibri.Platform.getExtensions (ui.editor.tools.SceneToolExtension.POINT_ID) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts similarity index 82% rename from source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationSection.ts rename to source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts index 5cc1ed7dc..0475d68d1 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts @@ -2,10 +2,10 @@ namespace phasereditor2d.scene.ui.sceneobjects { import controls = colibri.ui.controls; - export class AnimationSection extends SceneGameObjectSection { + export class SpriteAnimationSection extends SceneGameObjectSection { constructor(page: controls.properties.PropertyPage) { - super(page, "phasereditor2d.scene.ui.sceneobjects.AnimationSection", "Animation"); + super(page, "phasereditor2d.scene.ui.sceneobjects.SpriteAnimationSection", "Animation"); } protected getSectionHelpPath() { @@ -89,6 +89,26 @@ namespace phasereditor2d.scene.ui.sceneobjects { btnUI.updateDialogButtonIcon(); }); } + { + // open animations editor + const btn = this.createButton(comp, "Open Animation", async () => { + + const sprite = this.getSelectionFirstElement(); + + const finder = new pack.core.PackFinder(); + + await finder.preload(); + + const anim = finder.findAnimationByKey(sprite.playAnimation); + + if (anim) { + + ScenePlugin.getInstance().openAnimationInEditor(anim); + } + }); + + btn.style.gridColumn = "1 / span 4"; + } } canEdit(obj: any, n: number): boolean { From 155d2a568631bb82922ff8c4694814350830c93b Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 12 Oct 2023 14:20:15 -0400 Subject: [PATCH 20/58] Adds Aseprite content type & icon. --- design/aseprite/aseprite.png | Bin 0 -> 778 bytes .../phasereditor2d.resources@1x.tps | 2 + .../phasereditor2d.resources@2x.tps | 2 + .../x1/dark/aseprite.png | Bin 0 -> 1108 bytes .../x1/light/aseprite.png | Bin 0 -> 1108 bytes .../x2/dark/aseprite@2x.png | Bin 0 -> 1654 bytes .../x2/light/aseprite@2x.png | Bin 0 -> 1654 bytes .../src/AssetPackPlugin.ts | 10 + .../src/core/AsepriteAssetPackItem.ts | 30 ++ .../phasereditor2d.pack/src/core/AssetPack.ts | 1 + .../AsepriteContentTypeResolver.ts | 44 +++ .../src/ui/DefaultAssetPackExtension.ts | 4 + .../icons/atlas@1x.json | 242 ++++++++-------- .../icons/atlas@1x.png | Bin 15052 -> 15589 bytes .../icons/atlas@2x.json | 266 ++++++++++-------- .../icons/atlas@2x.png | Bin 32363 -> 33095 bytes .../src/ResourcesPlugin.ts | 1 + 17 files changed, 364 insertions(+), 238 deletions(-) create mode 100644 design/aseprite/aseprite.png create mode 100644 design/texture-packer/phasereditor2d.resources/x1/dark/aseprite.png create mode 100644 design/texture-packer/phasereditor2d.resources/x1/light/aseprite.png create mode 100644 design/texture-packer/phasereditor2d.resources/x2/dark/aseprite@2x.png create mode 100644 design/texture-packer/phasereditor2d.resources/x2/light/aseprite@2x.png create mode 100644 source/editor/plugins/phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts create mode 100644 source/editor/plugins/phasereditor2d.pack/src/core/contentTypes/AsepriteContentTypeResolver.ts diff --git a/design/aseprite/aseprite.png b/design/aseprite/aseprite.png new file mode 100644 index 0000000000000000000000000000000000000000..dc4a83e698ac1485627f31ab68ac4480b6baf6d0 GIT binary patch literal 778 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7xzrV7ln(;uumf=k1;SezyZ896stV zTWP#8^3~}NQa-V#9JH6L={Z`G(%X{JUiR|J^%JKRJ~H*{T;f|B+V&&v(TqPYE%$Ri z6)F~$w=?jRaP`03efryBtFM*z@^b5b)$zLeta@ejS+msT$eVA=u7B)~`+lkZFv9~L zhI1g|UFh{glka~Dk23RhaS?g^af9`xs9T>WSjWePbNVstILGmJ+=l*|s z{Q6e&rS<3h&rj-qzhqTW`-!eH zUqw~?|K{xCq9fiNzmmCP?*8cK%hOMOU8LhKC^&!r{k3uOb>3DgbN4gr7~5NyNEAK4 z$jOk-P{4+p_&(=5>zj~wKi|dfmrK0=efnb-m=h3=A)j^o_kaEk@87S#|NZY%3r1K# zfkTS~!mKX-!10^gpZr?>a<}h(mZ*0h?@kehggL^LcSrx*AMlPpo@I<4j*E1Zm6CQw zNy{GctV($)_8sn(=-*e|%$ItyPUu0}$gNP63J^DbCl7HKj1Wqe$S+5fn s{@ZKbx5Ap0>FIMBCaHK%YWXC+dToZ=k(PzVz%FVdQ&MBb@0M5Hhp#T5? literal 0 HcmV?d00001 diff --git a/design/texture-packer/phasereditor2d.resources/phasereditor2d.resources@1x.tps b/design/texture-packer/phasereditor2d.resources/phasereditor2d.resources@1x.tps index e13437b97..463a7a4ec 100644 --- a/design/texture-packer/phasereditor2d.resources/phasereditor2d.resources@1x.tps +++ b/design/texture-packer/phasereditor2d.resources/phasereditor2d.resources@1x.tps @@ -183,6 +183,7 @@ x1/dark/align-top.png x1/dark/angle.png x1/dark/animations.png + x1/dark/aseprite.png x1/dark/asset-pack.png x1/dark/bitmapfont-type.png x1/dark/blocks.png @@ -246,6 +247,7 @@ x1/light/align-top.png x1/light/angle.png x1/light/animations.png + x1/light/aseprite.png x1/light/asset-pack.png x1/light/bitmapfont-type.png x1/light/blocks.png diff --git a/design/texture-packer/phasereditor2d.resources/phasereditor2d.resources@2x.tps b/design/texture-packer/phasereditor2d.resources/phasereditor2d.resources@2x.tps index 1c40a3809..49e59b1c9 100644 --- a/design/texture-packer/phasereditor2d.resources/phasereditor2d.resources@2x.tps +++ b/design/texture-packer/phasereditor2d.resources/phasereditor2d.resources@2x.tps @@ -183,6 +183,7 @@ x2/dark/align-top@2x.png x2/dark/angle@2x.png x2/dark/animations@2x.png + x2/dark/aseprite@2x.png x2/dark/asset-pack@2x.png x2/dark/bitmapfont-type@2x.png x2/dark/blocks@2x.png @@ -246,6 +247,7 @@ x2/light/align-top@2x.png x2/light/angle@2x.png x2/light/animations@2x.png + x2/light/aseprite@2x.png x2/light/asset-pack@2x.png x2/light/bitmapfont-type@2x.png x2/light/blocks@2x.png diff --git a/design/texture-packer/phasereditor2d.resources/x1/dark/aseprite.png b/design/texture-packer/phasereditor2d.resources/x1/dark/aseprite.png new file mode 100644 index 0000000000000000000000000000000000000000..e91ae3f5b951257f47db2d5c03d191147facd0c7 GIT binary patch literal 1108 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|T2doC(|mmy zw18|523AHP24;{FAY@>aVqgWc85q16rQz%#Mh&PMCI*J~Oa>OHnkXO*0v4nJa0`PlBg3pY5xV%QuQiw3qZOUY$~jP%-qzHM1_jnoV;SI3R@+x3M(KRB&@Hb09I0xZL1XF8=&Bv zUzDm~re~mMpk&9TprBw=l#*r@P?Wt5Z@Sn2DRmzV368|&p4rRy77T3YHG z80i}s=>k>g7FXt#Bv$C=6)QswftllyTAW;zSx}OhpQivaH!&%{w8U0P31kr*K-^i9 znTD__uNdkrpa=CqGWv#k2KsQbfm&@qqE`MznW;dVLFU^T+JIG}h(YbK(Fa+MHfYh=t(PVDD@ZM}c?q+`s2UOm;n?8YlMg!Q)34 zll|hph(FPr)G8|P$Wbw)`N2_kyBiAkY);0xaB$4~#4n;!)%8R`QDwpA!r1NA<-69d zPp?i-aNU@&tozri->Y`*nr*x(OnYtrulDl3^XKn!Scp&SIV2NtR7c(T+N*r`X7|F{ zk9o@{f2-VnTlBrz_G|uT{&#ZL$IhPKxutTR^7cciJhKudqD({ny!@-~9$&WKZcjz! zPq|4_uixJO-8`RJWEsF!huEDYEr>l%*MGW;j*Kzf|U*PiZ@@&BaAH=1& z^9d``*)+JMn4f(}n16ma{W(zU}&+9w-v!k+jr|_MP zJhJ;Q-Fg_hNd3pvz{o3oYzU@7JSR>?t+u5g|o16FM zDF5EFH|_kK)t6h#+hgwaboN!psrbEL^1X7m{M9UJ?<1v8KS<3ymnUhOb5X2iX+RtdvBKOEKD%=y4 OwLD$@T-G@yGywqmRD*y3 literal 0 HcmV?d00001 diff --git a/design/texture-packer/phasereditor2d.resources/x1/light/aseprite.png b/design/texture-packer/phasereditor2d.resources/x1/light/aseprite.png new file mode 100644 index 0000000000000000000000000000000000000000..e91ae3f5b951257f47db2d5c03d191147facd0c7 GIT binary patch literal 1108 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|T2doC(|mmy zw18|523AHP24;{FAY@>aVqgWc85q16rQz%#Mh&PMCI*J~Oa>OHnkXO*0v4nJa0`PlBg3pY5xV%QuQiw3qZOUY$~jP%-qzHM1_jnoV;SI3R@+x3M(KRB&@Hb09I0xZL1XF8=&Bv zUzDm~re~mMpk&9TprBw=l#*r@P?Wt5Z@Sn2DRmzV368|&p4rRy77T3YHG z80i}s=>k>g7FXt#Bv$C=6)QswftllyTAW;zSx}OhpQivaH!&%{w8U0P31kr*K-^i9 znTD__uNdkrpa=CqGWv#k2KsQbfm&@qqE`MznW;dVLFU^T+JIG}h(YbK(Fa+MHfYh=t(PVDD@ZM}c?q+`s2UOm;n?8YlMg!Q)34 zll|hph(FPr)G8|P$Wbw)`N2_kyBiAkY);0xaB$4~#4n;!)%8R`QDwpA!r1NA<-69d zPp?i-aNU@&tozri->Y`*nr*x(OnYtrulDl3^XKn!Scp&SIV2NtR7c(T+N*r`X7|F{ zk9o@{f2-VnTlBrz_G|uT{&#ZL$IhPKxutTR^7cciJhKudqD({ny!@-~9$&WKZcjz! zPq|4_uixJO-8`RJWEsF!huEDYEr>l%*MGW;j*Kzf|U*PiZ@@&BaAH=1& z^9d``*)+JMn4f(}n16ma{W(zU}&+9w-v!k+jr|_MP zJhJ;Q-Fg_hNd3pvz{o3oYzU@7JSR>?t+u5g|o16FM zDF5EFH|_kK)t6h#+hgwaboN!psrbEL^1X7m{M9UJ?<1v8KS<3ymnUhOb5X2iX+RtdvBKOEKD%=y4 OwLD$@T-G@yGywqmRD*y3 literal 0 HcmV?d00001 diff --git a/design/texture-packer/phasereditor2d.resources/x2/dark/aseprite@2x.png b/design/texture-packer/phasereditor2d.resources/x2/dark/aseprite@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..10ef885650e642e48d17a8be579e7317a44d693a GIT binary patch literal 1654 zcmY*ZdpJ~S7(X*E8I{$PH58@^C7EF?>pH}!K^Wt%DQ1|N=8?JQZn~%x={|@e&>15`}=<9_x|4R{r)(oiT=CSY8z?;0I=4Z zfG2?&sR}Jk@Xb$2nE?}oPulGP)c0?i05`{J;ofvVKVUnEwEztW98gn9zz2XB0vbyg z0DK@DS1<{(ZCL{afJ`Q!zO0D^vuY8*0IQd?nh5gEQ3PF4BSmW8u&ONT=66$If^i5D zd;nOzUKJ3as00DJMKFWI1>t^soMPDVXi6-bN<$~bb5vD;OOg`^<7om4GAaHbi|>@= zids@QfmlUjP{<{TAkGyP?ngv=vUxP5J=z*=jlyXokw_O_EZvEO-?dy0?p#p^1Oko| z29ubWh)%Rcvv~}RjiaL@#u|&kVy!@h6+f9Jpd?wb_*=f3{Og0K@nd*Qj)2K#AyvK< zD*KSY6@^kYT3KJuDPYpSbzuq(AJn0Z?M!w#X1G>m^84n${%OrvZVYU zx7^1CqiX)2!+f1|i3LZ+X}e%nt_`PcuwStX0MyOB@jHXVVclUe*2pg1GBXPjoYw1M zir2}Sc?)}QESa11REwgTWNCV3i)!+qUPot%ZSL!;R+(te=*|#D4eOr6j+yHBoHivk z)tM~3VKL<7LwbWd%xeGCQlblNpMB&e5GJwK2N{f>Ytma=l+EcWyr#aYIsy!iSO~Q_ zLqlX|G9`~48_Bhn(O(`b6qG7O$#hO^#~(tbgTdV8zVs2wK!57SGZ6q>kKB#eeh!Mj zh9=)l-xI~?>@hCf94jH`LfwepYvujGHQ#FyV8(@4P!A9$T-|WPlcR5%-_B1--`_PJy0k0s(FUZ{C%+pm zXMCKbnxkG8H_Wzljpt2fnuvw?d(4FF*A4;fw2>ww(}HT!1iL+?7q&UOdQI%bGY(a{ zIBBa=igo@`pbX}u^fyGN85TsI^%{Dz6`h`r237@^d0Q2-24KcYhtR|6!idiSLPOsc zgmZaNkd$^Y>+d!R`{)9mQ}|Gf64KLHg^UT(f@E-?t$!`)tlo#cz;FIM4TOc-AYWf8 zi=(^UXG27%09*GgD*JYY(6EiT_Q1=<3Rx*M?_9)mr(GsyCVOr|N`}z6-4CfBiU;or z%gW8|MpT5jzD0K9#7BJKEsY~@Ov8@oaQ5l94=s{PNsCy^SByLQaCb=ZZS&|rH>2rW zZW{XP#GU6SLMVp#2QT^&j?YPqnH|eMv(+&jue+m+ahzh{fdXmkWII}N@AzijA4PO zarj&%j4nNC@UvBAhKTKcwLkDq3Eg&+wt zjVb-Xu)GdeXSSbN!2LaIH)G`QQKz>RnjE{5);MwIqwA^F+Z+#+*!R!KcUXFyOtm}3 zy`Law2c+C6ynxDS4Zn($>!Jt4QgwpGK^=;cpr`LxpI<(Q2uIX@s%^=&9G1)5caPLq zuaWqWDP^x^CI+R$w5Qv0hMtrvL+cD42AdfDr5L?tNkMllHnbmxJV<`IH9zIBvFhjR M?d6ZJ_lQ3B5BQ#zQUCw| literal 0 HcmV?d00001 diff --git a/design/texture-packer/phasereditor2d.resources/x2/light/aseprite@2x.png b/design/texture-packer/phasereditor2d.resources/x2/light/aseprite@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..10ef885650e642e48d17a8be579e7317a44d693a GIT binary patch literal 1654 zcmY*ZdpJ~S7(X*E8I{$PH58@^C7EF?>pH}!K^Wt%DQ1|N=8?JQZn~%x={|@e&>15`}=<9_x|4R{r)(oiT=CSY8z?;0I=4Z zfG2?&sR}Jk@Xb$2nE?}oPulGP)c0?i05`{J;ofvVKVUnEwEztW98gn9zz2XB0vbyg z0DK@DS1<{(ZCL{afJ`Q!zO0D^vuY8*0IQd?nh5gEQ3PF4BSmW8u&ONT=66$If^i5D zd;nOzUKJ3as00DJMKFWI1>t^soMPDVXi6-bN<$~bb5vD;OOg`^<7om4GAaHbi|>@= zids@QfmlUjP{<{TAkGyP?ngv=vUxP5J=z*=jlyXokw_O_EZvEO-?dy0?p#p^1Oko| z29ubWh)%Rcvv~}RjiaL@#u|&kVy!@h6+f9Jpd?wb_*=f3{Og0K@nd*Qj)2K#AyvK< zD*KSY6@^kYT3KJuDPYpSbzuq(AJn0Z?M!w#X1G>m^84n${%OrvZVYU zx7^1CqiX)2!+f1|i3LZ+X}e%nt_`PcuwStX0MyOB@jHXVVclUe*2pg1GBXPjoYw1M zir2}Sc?)}QESa11REwgTWNCV3i)!+qUPot%ZSL!;R+(te=*|#D4eOr6j+yHBoHivk z)tM~3VKL<7LwbWd%xeGCQlblNpMB&e5GJwK2N{f>Ytma=l+EcWyr#aYIsy!iSO~Q_ zLqlX|G9`~48_Bhn(O(`b6qG7O$#hO^#~(tbgTdV8zVs2wK!57SGZ6q>kKB#eeh!Mj zh9=)l-xI~?>@hCf94jH`LfwepYvujGHQ#FyV8(@4P!A9$T-|WPlcR5%-_B1--`_PJy0k0s(FUZ{C%+pm zXMCKbnxkG8H_Wzljpt2fnuvw?d(4FF*A4;fw2>ww(}HT!1iL+?7q&UOdQI%bGY(a{ zIBBa=igo@`pbX}u^fyGN85TsI^%{Dz6`h`r237@^d0Q2-24KcYhtR|6!idiSLPOsc zgmZaNkd$^Y>+d!R`{)9mQ}|Gf64KLHg^UT(f@E-?t$!`)tlo#cz;FIM4TOc-AYWf8 zi=(^UXG27%09*GgD*JYY(6EiT_Q1=<3Rx*M?_9)mr(GsyCVOr|N`}z6-4CfBiU;or z%gW8|MpT5jzD0K9#7BJKEsY~@Ov8@oaQ5l94=s{PNsCy^SByLQaCb=ZZS&|rH>2rW zZW{XP#GU6SLMVp#2QT^&j?YPqnH|eMv(+&jue+m+ahzh{fdXmkWII}N@AzijA4PO zarj&%j4nNC@UvBAhKTKcwLkDq3Eg&+wt zjVb-Xu)GdeXSSbN!2LaIH)G`QQKz>RnjE{5);MwIqwA^F+Z+#+*!R!KcUXFyOtm}3 zy`Law2c+C6ynxDS4Zn($>!Jt4QgwpGK^=;cpr`LxpI<(Q2uIX@s%^=&9G1)5caPLq zuaWqWDP^x^CI+R$w5Qv0hMtrvL+cD42AdfDr5L?tNkMllHnbmxJV<`IH9zIBvFhjR M?d6ZJ_lQ3B5BQ#zQUCw| literal 0 HcmV?d00001 diff --git a/source/editor/plugins/phasereditor2d.pack/src/AssetPackPlugin.ts b/source/editor/plugins/phasereditor2d.pack/src/AssetPackPlugin.ts index 85ef122e7..dcfa76ab4 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/AssetPackPlugin.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/AssetPackPlugin.ts @@ -148,6 +148,12 @@ namespace phasereditor2d.pack { 5 )); + reg.addExtension( + new colibri.core.ContentTypeExtension( + [new pack.core.contentTypes.AsepriteContentTypeResolver()], + 4 + )); + reg.addExtension( new colibri.core.ContentTypeExtension( [new pack.core.contentTypes.BitmapFontContentTypeResolver()], @@ -194,6 +200,10 @@ namespace phasereditor2d.pack { iconName: resources.ICON_ANIMATIONS, contentType: core.contentTypes.CONTENT_TYPE_ANIMATIONS }, + { + iconName: resources.ICON_ASEPRITE, + contentType: core.contentTypes.CONTENT_TYPE_ASEPRITE + }, { iconName: resources.ICON_TILEMAP, contentType: core.contentTypes.CONTENT_TYPE_TILEMAP_TILED_JSON diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts b/source/editor/plugins/phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts new file mode 100644 index 000000000..79da0278f --- /dev/null +++ b/source/editor/plugins/phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts @@ -0,0 +1,30 @@ +/// + +namespace phasereditor2d.pack.core { + + import io = colibri.core.io; + + export class AsepriteAssetPackItem extends AssetPackItem { + + constructor(pack: AssetPack, data: any) { + super(pack, data); + } + + getAtlasURL() { + + return this.getData().atlasURL; + } + + getTextureURL() { + + return this.getData().textureURL; + } + + computeUsedFiles(files: Set) { + + super.computeUsedFiles(files); + + this.addFilesFromDataKey(files, "atlasURL", "textureURL"); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/AssetPack.ts b/source/editor/plugins/phasereditor2d.pack/src/core/AssetPack.ts index befc68d7f..c510ae80f 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/core/AssetPack.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/core/AssetPack.ts @@ -14,6 +14,7 @@ namespace phasereditor2d.pack.core { export const SPINE_ATLAS_TYPE = "spineAtlas"; export const SPRITESHEET_TYPE = "spritesheet"; export const ANIMATION_TYPE = "animation"; + export const ASEPRITE_TYPE = "asprite"; export const AUDIO_TYPE = "audio"; export const AUDIO_SPRITE_TYPE = "audioSprite"; export const BINARY_TYPE = "binary"; diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/contentTypes/AsepriteContentTypeResolver.ts b/source/editor/plugins/phasereditor2d.pack/src/core/contentTypes/AsepriteContentTypeResolver.ts new file mode 100644 index 000000000..0980a70d0 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.pack/src/core/contentTypes/AsepriteContentTypeResolver.ts @@ -0,0 +1,44 @@ +namespace phasereditor2d.pack.core.contentTypes { + + import io = colibri.core.io; + import ide = colibri.ui.ide; + + export const CONTENT_TYPE_ASEPRITE = "Aseprite"; + + export class AsepriteContentTypeResolver implements colibri.core.IContentTypeResolver { + + getId(): string { + return "phasereditor2d.pack.core.AsepriteContentTypeResolver"; + } + + async computeContentType(file: io.FilePath): Promise { + + if (file.getExtension() === "json") { + + const content = await ide.FileUtils.preloadAndGetFileString(file); + + try { + + const data = JSON.parse(content); + + if (data.meta) { + + const app = data.meta.app || ""; + + if (app.indexOf("www.aseprite.org") >= 0) { + + return CONTENT_TYPE_ASEPRITE; + } + } + + } catch (e) { + // nothing + } + } + + return colibri.core.CONTENT_TYPE_ANY; + } + + } + +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/DefaultAssetPackExtension.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/DefaultAssetPackExtension.ts index b4a0c67f0..0997fcce7 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/DefaultAssetPackExtension.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/DefaultAssetPackExtension.ts @@ -11,6 +11,7 @@ namespace phasereditor2d.pack.ui { core.MULTI_ATLAS_TYPE, core.SPRITESHEET_TYPE, core.ANIMATION_TYPE, + core.ASEPRITE_TYPE, core.BITMAP_FONT_TYPE, core.TILEMAP_CSV_TYPE, core.TILEMAP_IMPACT_TYPE, @@ -100,6 +101,9 @@ namespace phasereditor2d.pack.ui { case core.ANIMATION_TYPE: return new core.AnimationsAssetPackItem(pack, data); + case core.ASEPRITE_TYPE: + return new core.AsepriteAssetPackItem(pack, data); + case core.BITMAP_FONT_TYPE: return new core.BitmapFontAssetPackItem(pack, data); diff --git a/source/editor/plugins/phasereditor2d.resources/icons/atlas@1x.json b/source/editor/plugins/phasereditor2d.resources/icons/atlas@1x.json index 89423aa36..7c6d791fb 100644 --- a/source/editor/plugins/phasereditor2d.resources/icons/atlas@1x.json +++ b/source/editor/plugins/phasereditor2d.resources/icons/atlas@1x.json @@ -80,7 +80,7 @@ "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, "sourceSize": {"w":16,"h":16} }, -"dark/asset-pack.png": +"dark/aseprite.png": { "frame": {"x":1,"y":73,"w":16,"h":16}, "rotated": false, @@ -88,6 +88,14 @@ "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, "sourceSize": {"w":16,"h":16} }, +"dark/asset-pack.png": +{ + "frame": {"x":19,"y":55,"w":16,"h":16}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, + "sourceSize": {"w":16,"h":16} +}, "dark/bitmapfont-type.png": { "frame": {"x":37,"y":289,"w":15,"h":16}, @@ -98,7 +106,7 @@ }, "dark/blocks.png": { - "frame": {"x":19,"y":55,"w":16,"h":16}, + "frame": {"x":37,"y":37,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -106,7 +114,7 @@ }, "dark/border-bottom.png": { - "frame": {"x":37,"y":37,"w":16,"h":16}, + "frame": {"x":55,"y":19,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -114,7 +122,7 @@ }, "dark/border-center.png": { - "frame": {"x":55,"y":19,"w":16,"h":16}, + "frame": {"x":73,"y":1,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -122,7 +130,7 @@ }, "dark/border-left.png": { - "frame": {"x":73,"y":1,"w":16,"h":16}, + "frame": {"x":1,"y":91,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -130,7 +138,7 @@ }, "dark/border-middle.png": { - "frame": {"x":1,"y":91,"w":16,"h":16}, + "frame": {"x":19,"y":73,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -138,7 +146,7 @@ }, "dark/border-right.png": { - "frame": {"x":19,"y":73,"w":16,"h":16}, + "frame": {"x":37,"y":55,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -146,7 +154,7 @@ }, "dark/border-top.png": { - "frame": {"x":37,"y":55,"w":16,"h":16}, + "frame": {"x":55,"y":37,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -154,7 +162,7 @@ }, "dark/build.png": { - "frame": {"x":55,"y":37,"w":16,"h":16}, + "frame": {"x":73,"y":19,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -162,7 +170,7 @@ }, "dark/collider.png": { - "frame": {"x":73,"y":19,"w":16,"h":16}, + "frame": {"x":91,"y":1,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -170,7 +178,7 @@ }, "dark/column.png": { - "frame": {"x":91,"y":1,"w":16,"h":16}, + "frame": {"x":109,"y":1,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -186,7 +194,7 @@ }, "dark/file-font.png": { - "frame": {"x":54,"y":289,"w":13,"h":16}, + "frame": {"x":91,"y":253,"w":13,"h":16}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":13,"h":16}, @@ -194,7 +202,7 @@ }, "dark/file-image.png": { - "frame": {"x":109,"y":1,"w":16,"h":16}, + "frame": {"x":1,"y":109,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -202,7 +210,7 @@ }, "dark/file-movie.png": { - "frame": {"x":1,"y":109,"w":16,"h":16}, + "frame": {"x":19,"y":91,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -210,7 +218,7 @@ }, "dark/file-new.png": { - "frame": {"x":19,"y":91,"w":16,"h":16}, + "frame": {"x":37,"y":73,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -218,7 +226,7 @@ }, "dark/file-script.png": { - "frame": {"x":37,"y":73,"w":16,"h":16}, + "frame": {"x":55,"y":55,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -226,7 +234,7 @@ }, "dark/file-sound.png": { - "frame": {"x":55,"y":55,"w":16,"h":16}, + "frame": {"x":73,"y":37,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -234,7 +242,7 @@ }, "dark/file-text.png": { - "frame": {"x":73,"y":37,"w":16,"h":16}, + "frame": {"x":91,"y":19,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -242,7 +250,7 @@ }, "dark/grid.png": { - "frame": {"x":91,"y":19,"w":16,"h":16}, + "frame": {"x":109,"y":19,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -250,7 +258,7 @@ }, "dark/group.png": { - "frame": {"x":109,"y":19,"w":16,"h":16}, + "frame": {"x":1,"y":127,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -258,7 +266,7 @@ }, "dark/image-type.png": { - "frame": {"x":109,"y":268,"w":14,"h":14}, + "frame": {"x":91,"y":271,"w":14,"h":14}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":14,"h":14}, @@ -266,7 +274,7 @@ }, "dark/inspector.png": { - "frame": {"x":1,"y":127,"w":16,"h":16}, + "frame": {"x":19,"y":109,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -274,7 +282,7 @@ }, "dark/keyboard-key.png": { - "frame": {"x":19,"y":109,"w":16,"h":16}, + "frame": {"x":37,"y":91,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -282,7 +290,7 @@ }, "dark/layer.png": { - "frame": {"x":37,"y":91,"w":16,"h":16}, + "frame": {"x":55,"y":73,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -290,7 +298,7 @@ }, "dark/list.png": { - "frame": {"x":109,"y":217,"w":16,"h":15}, + "frame": {"x":55,"y":271,"w":16,"h":15}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":15}, @@ -298,7 +306,7 @@ }, "dark/locked.png": { - "frame": {"x":112,"y":284,"w":11,"h":13}, + "frame": {"x":107,"y":287,"w":11,"h":13}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":1,"w":11,"h":13}, @@ -306,7 +314,7 @@ }, "dark/origin-bottomcenter.png": { - "frame": {"x":55,"y":73,"w":16,"h":16}, + "frame": {"x":73,"y":55,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -314,7 +322,7 @@ }, "dark/origin-bottomleft.png": { - "frame": {"x":73,"y":55,"w":16,"h":16}, + "frame": {"x":91,"y":37,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -322,7 +330,7 @@ }, "dark/origin-bottomright.png": { - "frame": {"x":91,"y":37,"w":16,"h":16}, + "frame": {"x":109,"y":37,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -330,7 +338,7 @@ }, "dark/origin-middlecenter.png": { - "frame": {"x":109,"y":37,"w":16,"h":16}, + "frame": {"x":1,"y":145,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -338,7 +346,7 @@ }, "dark/origin-middleleft.png": { - "frame": {"x":1,"y":145,"w":16,"h":16}, + "frame": {"x":19,"y":127,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -346,7 +354,7 @@ }, "dark/origin-middleright.png": { - "frame": {"x":19,"y":127,"w":16,"h":16}, + "frame": {"x":37,"y":109,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -354,7 +362,7 @@ }, "dark/origin-topcenter.png": { - "frame": {"x":37,"y":109,"w":16,"h":16}, + "frame": {"x":55,"y":91,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -362,7 +370,7 @@ }, "dark/origin-topleft.png": { - "frame": {"x":55,"y":91,"w":16,"h":16}, + "frame": {"x":73,"y":73,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -370,7 +378,7 @@ }, "dark/origin-topright.png": { - "frame": {"x":73,"y":73,"w":16,"h":16}, + "frame": {"x":91,"y":55,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -378,7 +386,7 @@ }, "dark/origin.png": { - "frame": {"x":91,"y":55,"w":16,"h":16}, + "frame": {"x":109,"y":55,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -386,7 +394,7 @@ }, "dark/outline.png": { - "frame": {"x":109,"y":55,"w":16,"h":16}, + "frame": {"x":1,"y":163,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -394,7 +402,7 @@ }, "dark/play.png": { - "frame": {"x":84,"y":284,"w":12,"h":14}, + "frame": {"x":107,"y":271,"w":12,"h":14}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":1,"w":12,"h":14}, @@ -402,7 +410,7 @@ }, "dark/project.png": { - "frame": {"x":109,"y":234,"w":16,"h":15}, + "frame": {"x":73,"y":253,"w":16,"h":15}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":1,"w":16,"h":15}, @@ -418,7 +426,7 @@ }, "dark/scale.png": { - "frame": {"x":1,"y":163,"w":16,"h":16}, + "frame": {"x":19,"y":145,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -426,7 +434,7 @@ }, "dark/select-region.png": { - "frame": {"x":19,"y":145,"w":16,"h":16}, + "frame": {"x":37,"y":127,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -434,7 +442,7 @@ }, "dark/spine.png": { - "frame": {"x":37,"y":127,"w":16,"h":16}, + "frame": {"x":55,"y":109,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -442,7 +450,7 @@ }, "dark/sprite-type.png": { - "frame": {"x":55,"y":109,"w":16,"h":16}, + "frame": {"x":73,"y":91,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -450,7 +458,7 @@ }, "dark/text-type.png": { - "frame": {"x":73,"y":91,"w":16,"h":16}, + "frame": {"x":91,"y":73,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -458,7 +466,7 @@ }, "dark/tilemap-layer.png": { - "frame": {"x":91,"y":73,"w":16,"h":16}, + "frame": {"x":109,"y":73,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -466,7 +474,7 @@ }, "dark/tilemap.png": { - "frame": {"x":109,"y":73,"w":16,"h":16}, + "frame": {"x":1,"y":181,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -474,7 +482,7 @@ }, "dark/tilesprite.png": { - "frame": {"x":91,"y":252,"w":16,"h":14}, + "frame": {"x":55,"y":288,"w":16,"h":14}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":1,"w":16,"h":14}, @@ -482,7 +490,7 @@ }, "dark/translate.png": { - "frame": {"x":1,"y":181,"w":16,"h":16}, + "frame": {"x":19,"y":163,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -490,7 +498,7 @@ }, "dark/unlocked.png": { - "frame": {"x":73,"y":269,"w":16,"h":13}, + "frame": {"x":54,"y":304,"w":16,"h":13}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":1,"w":16,"h":13}, @@ -498,7 +506,7 @@ }, "dark/user-component.png": { - "frame": {"x":19,"y":163,"w":16,"h":16}, + "frame": {"x":37,"y":145,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -506,7 +514,7 @@ }, "light/3slice.png": { - "frame": {"x":37,"y":145,"w":16,"h":16}, + "frame": {"x":55,"y":127,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -514,7 +522,7 @@ }, "light/9slice.png": { - "frame": {"x":55,"y":127,"w":16,"h":16}, + "frame": {"x":73,"y":109,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -522,7 +530,7 @@ }, "light/align-bottom.png": { - "frame": {"x":73,"y":109,"w":16,"h":16}, + "frame": {"x":91,"y":91,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -530,7 +538,7 @@ }, "light/align-center.png": { - "frame": {"x":91,"y":91,"w":16,"h":16}, + "frame": {"x":109,"y":91,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -538,7 +546,7 @@ }, "light/align-left.png": { - "frame": {"x":109,"y":91,"w":16,"h":16}, + "frame": {"x":1,"y":199,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -546,7 +554,7 @@ }, "light/align-middle.png": { - "frame": {"x":1,"y":199,"w":16,"h":16}, + "frame": {"x":19,"y":181,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -554,7 +562,7 @@ }, "light/align-right.png": { - "frame": {"x":19,"y":181,"w":16,"h":16}, + "frame": {"x":37,"y":163,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -562,7 +570,7 @@ }, "light/align-top.png": { - "frame": {"x":37,"y":163,"w":16,"h":16}, + "frame": {"x":55,"y":145,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -570,7 +578,7 @@ }, "light/angle.png": { - "frame": {"x":55,"y":145,"w":16,"h":16}, + "frame": {"x":73,"y":127,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -578,7 +586,15 @@ }, "light/animations.png": { - "frame": {"x":73,"y":127,"w":16,"h":16}, + "frame": {"x":91,"y":109,"w":16,"h":16}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, + "sourceSize": {"w":16,"h":16} +}, +"light/aseprite.png": +{ + "frame": {"x":1,"y":73,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -586,7 +602,7 @@ }, "light/asset-pack.png": { - "frame": {"x":91,"y":109,"w":16,"h":16}, + "frame": {"x":109,"y":109,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -594,7 +610,7 @@ }, "light/bitmapfont-type.png": { - "frame": {"x":55,"y":271,"w":15,"h":16}, + "frame": {"x":91,"y":235,"w":15,"h":16}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":15,"h":16}, @@ -602,7 +618,7 @@ }, "light/blocks.png": { - "frame": {"x":109,"y":109,"w":16,"h":16}, + "frame": {"x":1,"y":217,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -610,7 +626,7 @@ }, "light/border-bottom.png": { - "frame": {"x":1,"y":217,"w":16,"h":16}, + "frame": {"x":19,"y":199,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -618,7 +634,7 @@ }, "light/border-center.png": { - "frame": {"x":19,"y":199,"w":16,"h":16}, + "frame": {"x":37,"y":181,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -626,7 +642,7 @@ }, "light/border-left.png": { - "frame": {"x":37,"y":181,"w":16,"h":16}, + "frame": {"x":55,"y":163,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -634,7 +650,7 @@ }, "light/border-middle.png": { - "frame": {"x":55,"y":163,"w":16,"h":16}, + "frame": {"x":73,"y":145,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -642,7 +658,7 @@ }, "light/border-right.png": { - "frame": {"x":73,"y":145,"w":16,"h":16}, + "frame": {"x":91,"y":127,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -658,7 +674,7 @@ }, "light/border-top.png": { - "frame": {"x":91,"y":127,"w":16,"h":16}, + "frame": {"x":109,"y":127,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -666,7 +682,7 @@ }, "light/build.png": { - "frame": {"x":109,"y":127,"w":16,"h":16}, + "frame": {"x":1,"y":235,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -674,7 +690,7 @@ }, "light/collider.png": { - "frame": {"x":1,"y":235,"w":16,"h":16}, + "frame": {"x":19,"y":217,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -682,7 +698,7 @@ }, "light/column.png": { - "frame": {"x":19,"y":217,"w":16,"h":16}, + "frame": {"x":37,"y":199,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -698,7 +714,7 @@ }, "light/file-font.png": { - "frame": {"x":69,"y":289,"w":13,"h":16}, + "frame": {"x":106,"y":253,"w":13,"h":16}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":0,"w":13,"h":16}, @@ -706,7 +722,7 @@ }, "light/file-image.png": { - "frame": {"x":37,"y":199,"w":16,"h":16}, + "frame": {"x":55,"y":181,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -714,7 +730,7 @@ }, "light/file-movie.png": { - "frame": {"x":55,"y":181,"w":16,"h":16}, + "frame": {"x":73,"y":163,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -722,7 +738,7 @@ }, "light/file-new.png": { - "frame": {"x":73,"y":163,"w":16,"h":16}, + "frame": {"x":91,"y":145,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -730,7 +746,7 @@ }, "light/file-script.png": { - "frame": {"x":91,"y":145,"w":16,"h":16}, + "frame": {"x":109,"y":145,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -738,7 +754,7 @@ }, "light/file-sound.png": { - "frame": {"x":109,"y":145,"w":16,"h":16}, + "frame": {"x":1,"y":253,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -746,7 +762,7 @@ }, "light/file-text.png": { - "frame": {"x":1,"y":253,"w":16,"h":16}, + "frame": {"x":19,"y":235,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -754,7 +770,7 @@ }, "light/grid.png": { - "frame": {"x":19,"y":235,"w":16,"h":16}, + "frame": {"x":37,"y":217,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -762,7 +778,7 @@ }, "light/group.png": { - "frame": {"x":37,"y":217,"w":16,"h":16}, + "frame": {"x":55,"y":199,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -770,7 +786,7 @@ }, "light/image-type.png": { - "frame": {"x":84,"y":300,"w":14,"h":14}, + "frame": {"x":91,"y":287,"w":14,"h":14}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":14,"h":14}, @@ -778,7 +794,7 @@ }, "light/inspector.png": { - "frame": {"x":55,"y":199,"w":16,"h":16}, + "frame": {"x":73,"y":181,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -786,7 +802,7 @@ }, "light/keyboard-key.png": { - "frame": {"x":73,"y":181,"w":16,"h":16}, + "frame": {"x":91,"y":163,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -794,7 +810,7 @@ }, "light/layer.png": { - "frame": {"x":91,"y":163,"w":16,"h":16}, + "frame": {"x":109,"y":163,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -802,7 +818,7 @@ }, "light/list.png": { - "frame": {"x":91,"y":235,"w":16,"h":15}, + "frame": {"x":73,"y":270,"w":16,"h":15}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":15}, @@ -810,7 +826,7 @@ }, "light/locked.png": { - "frame": {"x":100,"y":300,"w":11,"h":13}, + "frame": {"x":104,"y":303,"w":11,"h":13}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":3,"y":1,"w":11,"h":13}, @@ -818,7 +834,7 @@ }, "light/origin-bottomcenter.png": { - "frame": {"x":109,"y":163,"w":16,"h":16}, + "frame": {"x":1,"y":271,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -826,7 +842,7 @@ }, "light/origin-bottomleft.png": { - "frame": {"x":1,"y":271,"w":16,"h":16}, + "frame": {"x":19,"y":253,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -834,7 +850,7 @@ }, "light/origin-bottomright.png": { - "frame": {"x":19,"y":253,"w":16,"h":16}, + "frame": {"x":37,"y":235,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -842,7 +858,7 @@ }, "light/origin-middlecenter.png": { - "frame": {"x":37,"y":235,"w":16,"h":16}, + "frame": {"x":55,"y":217,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -850,7 +866,7 @@ }, "light/origin-middleleft.png": { - "frame": {"x":55,"y":217,"w":16,"h":16}, + "frame": {"x":73,"y":199,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -858,7 +874,7 @@ }, "light/origin-middleright.png": { - "frame": {"x":73,"y":199,"w":16,"h":16}, + "frame": {"x":91,"y":181,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -866,7 +882,7 @@ }, "light/origin-topcenter.png": { - "frame": {"x":91,"y":181,"w":16,"h":16}, + "frame": {"x":109,"y":181,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -874,7 +890,7 @@ }, "light/origin-topleft.png": { - "frame": {"x":109,"y":181,"w":16,"h":16}, + "frame": {"x":1,"y":289,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -882,7 +898,7 @@ }, "light/origin-topright.png": { - "frame": {"x":1,"y":289,"w":16,"h":16}, + "frame": {"x":19,"y":271,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -890,7 +906,7 @@ }, "light/origin.png": { - "frame": {"x":19,"y":271,"w":16,"h":16}, + "frame": {"x":37,"y":253,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -898,7 +914,7 @@ }, "light/outline.png": { - "frame": {"x":37,"y":253,"w":16,"h":16}, + "frame": {"x":55,"y":235,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -906,7 +922,7 @@ }, "light/play.png": { - "frame": {"x":98,"y":284,"w":12,"h":14}, + "frame": {"x":90,"y":303,"w":12,"h":14}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":1,"w":12,"h":14}, @@ -914,7 +930,7 @@ }, "light/project.png": { - "frame": {"x":109,"y":251,"w":16,"h":15}, + "frame": {"x":108,"y":235,"w":16,"h":15}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":1,"w":16,"h":15}, @@ -930,7 +946,7 @@ }, "light/scale.png": { - "frame": {"x":55,"y":235,"w":16,"h":16}, + "frame": {"x":73,"y":217,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -938,7 +954,7 @@ }, "light/select-region.png": { - "frame": {"x":73,"y":217,"w":16,"h":16}, + "frame": {"x":91,"y":199,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -946,7 +962,7 @@ }, "light/spine.png": { - "frame": {"x":91,"y":199,"w":16,"h":16}, + "frame": {"x":109,"y":199,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -954,7 +970,7 @@ }, "light/sprite-type.png": { - "frame": {"x":109,"y":199,"w":16,"h":16}, + "frame": {"x":19,"y":289,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -962,7 +978,7 @@ }, "light/text-type.png": { - "frame": {"x":19,"y":289,"w":16,"h":16}, + "frame": {"x":37,"y":271,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -970,7 +986,7 @@ }, "light/tilemap-layer.png": { - "frame": {"x":37,"y":271,"w":16,"h":16}, + "frame": {"x":55,"y":253,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -978,7 +994,7 @@ }, "light/tilemap.png": { - "frame": {"x":55,"y":253,"w":16,"h":16}, + "frame": {"x":73,"y":235,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -986,7 +1002,7 @@ }, "light/tilesprite.png": { - "frame": {"x":73,"y":253,"w":16,"h":14}, + "frame": {"x":73,"y":287,"w":16,"h":14}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":1,"w":16,"h":14}, @@ -994,7 +1010,7 @@ }, "light/translate.png": { - "frame": {"x":73,"y":235,"w":16,"h":16}, + "frame": {"x":91,"y":217,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -1002,7 +1018,7 @@ }, "light/unlocked.png": { - "frame": {"x":91,"y":268,"w":16,"h":13}, + "frame": {"x":72,"y":304,"w":16,"h":13}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":1,"w":16,"h":13}, @@ -1010,7 +1026,7 @@ }, "light/user-component.png": { - "frame": {"x":91,"y":217,"w":16,"h":16}, + "frame": {"x":109,"y":217,"w":16,"h":16}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, @@ -1021,8 +1037,8 @@ "version": "1.0", "image": "atlas@1x.png", "format": "RGBA8888", - "size": {"w":126,"h":315}, + "size": {"w":126,"h":318}, "scale": "1", - "smartupdate": "$TexturePacker:SmartUpdate:1d7560dab3bc8230de7b90d61fae75cd:0a0072a306be10c3e37a3d7445f0513b:a6f74b55a90107bc4ae6d23dde1c2d9e$" + "smartupdate": "$TexturePacker:SmartUpdate:8f7e6fa8c50e6e4a1a10036e7c58450f:5241bfccae5d16a07ca49ffa984ca94e:a6f74b55a90107bc4ae6d23dde1c2d9e$" } } diff --git a/source/editor/plugins/phasereditor2d.resources/icons/atlas@1x.png b/source/editor/plugins/phasereditor2d.resources/icons/atlas@1x.png index 9485ae8bad65416ca996a9b21650183137a7c073..e34775b8a0135c35dd3fa80b9ef4367306087bd4 100644 GIT binary patch literal 15589 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 PcgujHtQx3X+Bo=seij)Z literal 15052 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 diff --git a/source/editor/plugins/phasereditor2d.resources/icons/atlas@2x.json b/source/editor/plugins/phasereditor2d.resources/icons/atlas@2x.json index d0b4ee4a3..49205b0aa 100644 --- a/source/editor/plugins/phasereditor2d.resources/icons/atlas@2x.json +++ b/source/editor/plugins/phasereditor2d.resources/icons/atlas@2x.json @@ -2,7 +2,7 @@ "dark/3slice@2x.png": { - "frame": {"x":137,"y":228,"w":32,"h":26}, + "frame": {"x":170,"y":228,"w":32,"h":26}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":3,"w":32,"h":26}, @@ -18,7 +18,7 @@ }, "dark/align-bottom@2x.png": { - "frame": {"x":457,"y":159,"w":28,"h":30}, + "frame": {"x":427,"y":191,"w":28,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":1,"w":28,"h":30}, @@ -26,7 +26,7 @@ }, "dark/align-center@2x.png": { - "frame": {"x":235,"y":33,"w":32,"h":28}, + "frame": {"x":235,"y":1,"w":32,"h":28}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":2,"w":32,"h":28}, @@ -34,7 +34,7 @@ }, "dark/align-left@2x.png": { - "frame": {"x":199,"y":97,"w":32,"h":29}, + "frame": {"x":201,"y":1,"w":32,"h":29}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":1,"w":32,"h":29}, @@ -42,7 +42,7 @@ }, "dark/align-middle@2x.png": { - "frame": {"x":35,"y":205,"w":32,"h":31}, + "frame": {"x":68,"y":171,"w":32,"h":31}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":31}, @@ -50,7 +50,7 @@ }, "dark/align-right@2x.png": { - "frame": {"x":102,"y":135,"w":32,"h":30}, + "frame": {"x":102,"y":168,"w":32,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":30}, @@ -58,7 +58,7 @@ }, "dark/align-top@2x.png": { - "frame": {"x":68,"y":137,"w":32,"h":31}, + "frame": {"x":68,"y":204,"w":32,"h":31}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":31}, @@ -66,7 +66,7 @@ }, "dark/angle@2x.png": { - "frame": {"x":135,"y":34,"w":31,"h":30}, + "frame": {"x":136,"y":34,"w":31,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":1,"w":31,"h":30}, @@ -74,15 +74,23 @@ }, "dark/animations@2x.png": { - "frame": {"x":69,"y":1,"w":31,"h":31}, + "frame": {"x":69,"y":67,"w":31,"h":31}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":31,"h":31}, "sourceSize": {"w":32,"h":32} }, +"dark/aseprite@2x.png": +{ + "frame": {"x":1,"y":35,"w":32,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, + "sourceSize": {"w":32,"h":32} +}, "dark/asset-pack@2x.png": { - "frame": {"x":233,"y":1,"w":30,"h":30}, + "frame": {"x":202,"y":32,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -90,7 +98,7 @@ }, "dark/bitmapfont-type@2x.png": { - "frame": {"x":426,"y":159,"w":29,"h":30}, + "frame": {"x":457,"y":95,"w":29,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":1,"w":29,"h":30}, @@ -98,7 +106,7 @@ }, "dark/blocks@2x.png": { - "frame": {"x":201,"y":64,"w":30,"h":30}, + "frame": {"x":201,"y":96,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -106,7 +114,7 @@ }, "dark/border-bottom@2x.png": { - "frame": {"x":202,"y":128,"w":30,"h":30}, + "frame": {"x":232,"y":128,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -114,7 +122,7 @@ }, "dark/border-center@2x.png": { - "frame": {"x":202,"y":160,"w":30,"h":30}, + "frame": {"x":203,"y":64,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -122,7 +130,7 @@ }, "dark/border-left@2x.png": { - "frame": {"x":167,"y":1,"w":31,"h":30}, + "frame": {"x":168,"y":1,"w":31,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":1,"w":31,"h":30}, @@ -130,7 +138,7 @@ }, "dark/border-middle@2x.png": { - "frame": {"x":203,"y":192,"w":30,"h":30}, + "frame": {"x":234,"y":32,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -138,7 +146,7 @@ }, "dark/border-right@2x.png": { - "frame": {"x":35,"y":137,"w":31,"h":32}, + "frame": {"x":35,"y":171,"w":31,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":31,"h":32}, @@ -146,7 +154,7 @@ }, "dark/border-top@2x.png": { - "frame": {"x":205,"y":224,"w":30,"h":30}, + "frame": {"x":233,"y":96,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -154,7 +162,7 @@ }, "dark/build@2x.png": { - "frame": {"x":233,"y":64,"w":30,"h":30}, + "frame": {"x":235,"y":64,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -162,7 +170,7 @@ }, "dark/collider@2x.png": { - "frame": {"x":136,"y":132,"w":31,"h":30}, + "frame": {"x":136,"y":165,"w":31,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":31,"h":30}, @@ -170,7 +178,7 @@ }, "dark/column@2x.png": { - "frame": {"x":489,"y":1,"w":10,"h":30}, + "frame": {"x":495,"y":1,"w":10,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":11,"y":1,"w":10,"h":30}, @@ -178,7 +186,7 @@ }, "dark/dot@2x.png": { - "frame": {"x":65,"y":238,"w":10,"h":10}, + "frame": {"x":65,"y":239,"w":10,"h":10}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":11,"y":11,"w":10,"h":10}, @@ -186,7 +194,7 @@ }, "dark/file-font@2x.png": { - "frame": {"x":458,"y":191,"w":24,"h":30}, + "frame": {"x":457,"y":191,"w":24,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":1,"w":24,"h":30}, @@ -194,7 +202,7 @@ }, "dark/file-image@2x.png": { - "frame": {"x":265,"y":1,"w":30,"h":30}, + "frame": {"x":266,"y":31,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -202,7 +210,7 @@ }, "dark/file-movie@2x.png": { - "frame": {"x":233,"y":96,"w":30,"h":30}, + "frame": {"x":235,"y":160,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -210,7 +218,7 @@ }, "dark/file-new@2x.png": { - "frame": {"x":234,"y":128,"w":30,"h":30}, + "frame": {"x":264,"y":128,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -218,7 +226,7 @@ }, "dark/file-script@2x.png": { - "frame": {"x":166,"y":99,"w":31,"h":30}, + "frame": {"x":166,"y":132,"w":31,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":31,"h":30}, @@ -226,7 +234,7 @@ }, "dark/file-sound@2x.png": { - "frame": {"x":234,"y":160,"w":30,"h":30}, + "frame": {"x":265,"y":96,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -234,7 +242,7 @@ }, "dark/file-text@2x.png": { - "frame": {"x":102,"y":167,"w":32,"h":30}, + "frame": {"x":102,"y":200,"w":32,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":1,"w":32,"h":30}, @@ -242,7 +250,7 @@ }, "dark/grid@2x.png": { - "frame": {"x":235,"y":192,"w":30,"h":30}, + "frame": {"x":267,"y":63,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -250,7 +258,7 @@ }, "dark/group@2x.png": { - "frame": {"x":237,"y":224,"w":30,"h":30}, + "frame": {"x":298,"y":31,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -258,7 +266,7 @@ }, "dark/image-type@2x.png": { - "frame": {"x":457,"y":85,"w":24,"h":24}, + "frame": {"x":460,"y":223,"w":24,"h":24}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":4,"w":24,"h":24}, @@ -266,7 +274,7 @@ }, "dark/inspector@2x.png": { - "frame": {"x":134,"y":99,"w":30,"h":31}, + "frame": {"x":134,"y":132,"w":30,"h":31}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":31}, @@ -274,7 +282,7 @@ }, "dark/keyboard-key@2x.png": { - "frame": {"x":1,"y":35,"w":32,"h":32}, + "frame": {"x":1,"y":69,"w":32,"h":32}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, @@ -282,7 +290,7 @@ }, "dark/layer@2x.png": { - "frame": {"x":265,"y":63,"w":30,"h":30}, + "frame": {"x":235,"y":192,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -290,7 +298,7 @@ }, "dark/list@2x.png": { - "frame": {"x":303,"y":33,"w":30,"h":28}, + "frame": {"x":303,"y":1,"w":30,"h":28}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":28}, @@ -298,7 +306,7 @@ }, "dark/locked@2x.png": { - "frame": {"x":485,"y":219,"w":20,"h":24}, + "frame": {"x":488,"y":91,"w":20,"h":24}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":7,"y":3,"w":20,"h":24}, @@ -306,7 +314,7 @@ }, "dark/origin-bottomcenter@2x.png": { - "frame": {"x":297,"y":1,"w":30,"h":30}, + "frame": {"x":238,"y":224,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -314,7 +322,7 @@ }, "dark/origin-bottomleft@2x.png": { - "frame": {"x":265,"y":95,"w":30,"h":30}, + "frame": {"x":267,"y":160,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -322,7 +330,7 @@ }, "dark/origin-bottomright@2x.png": { - "frame": {"x":266,"y":127,"w":30,"h":30}, + "frame": {"x":296,"y":128,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -330,7 +338,7 @@ }, "dark/origin-middlecenter@2x.png": { - "frame": {"x":266,"y":159,"w":30,"h":30}, + "frame": {"x":297,"y":95,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -338,7 +346,7 @@ }, "dark/origin-middleleft@2x.png": { - "frame": {"x":267,"y":191,"w":30,"h":30}, + "frame": {"x":299,"y":63,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -346,7 +354,7 @@ }, "dark/origin-middleright@2x.png": { - "frame": {"x":269,"y":223,"w":30,"h":30}, + "frame": {"x":330,"y":31,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -354,7 +362,7 @@ }, "dark/origin-topcenter@2x.png": { - "frame": {"x":297,"y":63,"w":30,"h":30}, + "frame": {"x":267,"y":192,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -362,7 +370,7 @@ }, "dark/origin-topleft@2x.png": { - "frame": {"x":329,"y":1,"w":30,"h":30}, + "frame": {"x":270,"y":224,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -370,7 +378,7 @@ }, "dark/origin-topright@2x.png": { - "frame": {"x":297,"y":95,"w":30,"h":30}, + "frame": {"x":299,"y":160,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -378,7 +386,7 @@ }, "dark/origin@2x.png": { - "frame": {"x":69,"y":34,"w":31,"h":31}, + "frame": {"x":69,"y":100,"w":31,"h":31}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":31,"h":31}, @@ -386,7 +394,7 @@ }, "dark/outline@2x.png": { - "frame": {"x":298,"y":127,"w":30,"h":30}, + "frame": {"x":328,"y":127,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -394,7 +402,7 @@ }, "dark/play@2x.png": { - "frame": {"x":484,"y":191,"w":22,"h":26}, + "frame": {"x":485,"y":159,"w":22,"h":26}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":3,"w":22,"h":26}, @@ -402,7 +410,7 @@ }, "dark/project@2x.png": { - "frame": {"x":335,"y":33,"w":30,"h":28}, + "frame": {"x":335,"y":1,"w":30,"h":28}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":3,"w":30,"h":28}, @@ -418,7 +426,7 @@ }, "dark/scale@2x.png": { - "frame": {"x":136,"y":164,"w":31,"h":30}, + "frame": {"x":168,"y":99,"w":31,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":1,"w":31,"h":30}, @@ -426,7 +434,7 @@ }, "dark/select-region@2x.png": { - "frame": {"x":137,"y":196,"w":31,"h":30}, + "frame": {"x":136,"y":197,"w":31,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":31,"h":30}, @@ -434,7 +442,7 @@ }, "dark/spine@2x.png": { - "frame": {"x":1,"y":69,"w":32,"h":32}, + "frame": {"x":1,"y":103,"w":32,"h":32}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, @@ -442,7 +450,7 @@ }, "dark/sprite-type@2x.png": { - "frame": {"x":69,"y":67,"w":31,"h":31}, + "frame": {"x":69,"y":133,"w":31,"h":31}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":31,"h":31}, @@ -450,7 +458,7 @@ }, "dark/text-type@2x.png": { - "frame": {"x":298,"y":159,"w":30,"h":30}, + "frame": {"x":329,"y":95,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -458,7 +466,7 @@ }, "dark/tilemap-layer@2x.png": { - "frame": {"x":1,"y":103,"w":32,"h":32}, + "frame": {"x":1,"y":137,"w":32,"h":32}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, @@ -466,7 +474,7 @@ }, "dark/tilemap@2x.png": { - "frame": {"x":1,"y":137,"w":32,"h":32}, + "frame": {"x":1,"y":171,"w":32,"h":32}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, @@ -474,7 +482,7 @@ }, "dark/tilesprite@2x.png": { - "frame": {"x":171,"y":227,"w":32,"h":24}, + "frame": {"x":136,"y":229,"w":32,"h":24}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":4,"w":32,"h":24}, @@ -482,7 +490,7 @@ }, "dark/translate@2x.png": { - "frame": {"x":299,"y":191,"w":30,"h":30}, + "frame": {"x":331,"y":63,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -490,7 +498,7 @@ }, "dark/unlocked@2x.png": { - "frame": {"x":457,"y":59,"w":30,"h":24}, + "frame": {"x":431,"y":1,"w":30,"h":24}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":3,"w":30,"h":24}, @@ -498,7 +506,7 @@ }, "dark/user-component@2x.png": { - "frame": {"x":102,"y":67,"w":30,"h":32}, + "frame": {"x":102,"y":100,"w":30,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":30,"h":32}, @@ -506,7 +514,7 @@ }, "light/3slice@2x.png": { - "frame": {"x":1,"y":171,"w":32,"h":32}, + "frame": {"x":1,"y":205,"w":32,"h":32}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, @@ -514,7 +522,7 @@ }, "light/9slice@2x.png": { - "frame": {"x":1,"y":205,"w":32,"h":32}, + "frame": {"x":35,"y":1,"w":32,"h":32}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, @@ -522,7 +530,7 @@ }, "light/align-bottom@2x.png": { - "frame": {"x":429,"y":223,"w":28,"h":30}, + "frame": {"x":430,"y":223,"w":28,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":1,"w":28,"h":30}, @@ -530,7 +538,7 @@ }, "light/align-center@2x.png": { - "frame": {"x":269,"y":33,"w":32,"h":28}, + "frame": {"x":269,"y":1,"w":32,"h":28}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":2,"w":32,"h":28}, @@ -538,7 +546,7 @@ }, "light/align-left@2x.png": { - "frame": {"x":201,"y":33,"w":32,"h":29}, + "frame": {"x":169,"y":65,"w":32,"h":29}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":1,"w":32,"h":29}, @@ -546,7 +554,7 @@ }, "light/align-middle@2x.png": { - "frame": {"x":68,"y":170,"w":32,"h":31}, + "frame": {"x":69,"y":1,"w":32,"h":31}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":31}, @@ -554,7 +562,7 @@ }, "light/align-right@2x.png": { - "frame": {"x":103,"y":199,"w":32,"h":30}, + "frame": {"x":134,"y":100,"w":32,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":30}, @@ -562,7 +570,7 @@ }, "light/align-top@2x.png": { - "frame": {"x":69,"y":203,"w":32,"h":31}, + "frame": {"x":69,"y":34,"w":32,"h":31}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":31}, @@ -570,7 +578,7 @@ }, "light/angle@2x.png": { - "frame": {"x":169,"y":131,"w":31,"h":30}, + "frame": {"x":169,"y":164,"w":31,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":1,"w":31,"h":30}, @@ -578,15 +586,23 @@ }, "light/animations@2x.png": { - "frame": {"x":69,"y":100,"w":31,"h":31}, + "frame": {"x":102,"y":67,"w":31,"h":31}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":31,"h":31}, "sourceSize": {"w":32,"h":32} }, +"light/aseprite@2x.png": +{ + "frame": {"x":1,"y":35,"w":32,"h":32}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, + "sourceSize": {"w":32,"h":32} +}, "light/asset-pack@2x.png": { - "frame": {"x":301,"y":223,"w":30,"h":30}, + "frame": {"x":362,"y":31,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -594,7 +610,7 @@ }, "light/bitmapfont-type@2x.png": { - "frame": {"x":427,"y":191,"w":29,"h":30}, + "frame": {"x":459,"y":59,"w":29,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":2,"y":1,"w":29,"h":30}, @@ -602,7 +618,7 @@ }, "light/blocks@2x.png": { - "frame": {"x":329,"y":63,"w":30,"h":30}, + "frame": {"x":299,"y":192,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -610,7 +626,7 @@ }, "light/border-bottom@2x.png": { - "frame": {"x":361,"y":1,"w":30,"h":30}, + "frame": {"x":302,"y":224,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -618,7 +634,7 @@ }, "light/border-center@2x.png": { - "frame": {"x":329,"y":95,"w":30,"h":30}, + "frame": {"x":331,"y":159,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -626,7 +642,7 @@ }, "light/border-left@2x.png": { - "frame": {"x":169,"y":163,"w":31,"h":30}, + "frame": {"x":199,"y":131,"w":31,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":1,"w":31,"h":30}, @@ -634,7 +650,7 @@ }, "light/border-middle@2x.png": { - "frame": {"x":330,"y":127,"w":30,"h":30}, + "frame": {"x":360,"y":127,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -642,7 +658,7 @@ }, "light/border-right@2x.png": { - "frame": {"x":35,"y":171,"w":31,"h":32}, + "frame": {"x":35,"y":205,"w":31,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":31,"h":32}, @@ -658,7 +674,7 @@ }, "light/border-top@2x.png": { - "frame": {"x":330,"y":159,"w":30,"h":30}, + "frame": {"x":361,"y":95,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -666,7 +682,7 @@ }, "light/build@2x.png": { - "frame": {"x":331,"y":191,"w":30,"h":30}, + "frame": {"x":363,"y":63,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -674,7 +690,7 @@ }, "light/collider@2x.png": { - "frame": {"x":170,"y":195,"w":31,"h":30}, + "frame": {"x":169,"y":196,"w":31,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":31,"h":30}, @@ -682,7 +698,7 @@ }, "light/column@2x.png": { - "frame": {"x":487,"y":139,"w":10,"h":30}, + "frame": {"x":490,"y":33,"w":10,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":11,"y":1,"w":10,"h":30}, @@ -690,7 +706,7 @@ }, "light/dot@2x.png": { - "frame": {"x":77,"y":236,"w":10,"h":10}, + "frame": {"x":77,"y":237,"w":10,"h":10}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":11,"y":11,"w":10,"h":10}, @@ -698,7 +714,7 @@ }, "light/file-font@2x.png": { - "frame": {"x":459,"y":223,"w":24,"h":30}, + "frame": {"x":459,"y":159,"w":24,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":1,"w":24,"h":30}, @@ -706,7 +722,7 @@ }, "light/file-image@2x.png": { - "frame": {"x":333,"y":223,"w":30,"h":30}, + "frame": {"x":394,"y":31,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -714,7 +730,7 @@ }, "light/file-movie@2x.png": { - "frame": {"x":361,"y":63,"w":30,"h":30}, + "frame": {"x":331,"y":191,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -722,7 +738,7 @@ }, "light/file-new@2x.png": { - "frame": {"x":393,"y":1,"w":30,"h":30}, + "frame": {"x":334,"y":223,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -730,7 +746,7 @@ }, "light/file-script@2x.png": { - "frame": {"x":168,"y":33,"w":31,"h":30}, + "frame": {"x":202,"y":163,"w":31,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":31,"h":30}, @@ -738,7 +754,7 @@ }, "light/file-sound@2x.png": { - "frame": {"x":361,"y":95,"w":30,"h":30}, + "frame": {"x":363,"y":159,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -746,7 +762,7 @@ }, "light/file-text@2x.png": { - "frame": {"x":134,"y":67,"w":32,"h":30}, + "frame": {"x":135,"y":67,"w":32,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":1,"w":32,"h":30}, @@ -754,7 +770,7 @@ }, "light/grid@2x.png": { - "frame": {"x":362,"y":127,"w":30,"h":30}, + "frame": {"x":392,"y":127,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -762,7 +778,7 @@ }, "light/group@2x.png": { - "frame": {"x":362,"y":159,"w":30,"h":30}, + "frame": {"x":393,"y":95,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -770,7 +786,7 @@ }, "light/image-type@2x.png": { - "frame": {"x":480,"y":113,"w":24,"h":24}, + "frame": {"x":483,"y":191,"w":24,"h":24}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":4,"y":4,"w":24,"h":24}, @@ -778,7 +794,7 @@ }, "light/inspector@2x.png": { - "frame": {"x":135,"y":1,"w":30,"h":31}, + "frame": {"x":136,"y":1,"w":30,"h":31}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":31}, @@ -786,7 +802,7 @@ }, "light/keyboard-key@2x.png": { - "frame": {"x":35,"y":1,"w":32,"h":32}, + "frame": {"x":35,"y":35,"w":32,"h":32}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, @@ -794,7 +810,7 @@ }, "light/layer@2x.png": { - "frame": {"x":363,"y":191,"w":30,"h":30}, + "frame": {"x":395,"y":63,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -802,7 +818,7 @@ }, "light/list@2x.png": { - "frame": {"x":367,"y":33,"w":30,"h":28}, + "frame": {"x":367,"y":1,"w":30,"h":28}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":28}, @@ -810,7 +826,7 @@ }, "light/locked@2x.png": { - "frame": {"x":458,"y":111,"w":20,"h":24}, + "frame": {"x":488,"y":117,"w":20,"h":24}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":7,"y":3,"w":20,"h":24}, @@ -818,7 +834,7 @@ }, "light/origin-bottomcenter@2x.png": { - "frame": {"x":365,"y":223,"w":30,"h":30}, + "frame": {"x":426,"y":31,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -826,7 +842,7 @@ }, "light/origin-bottomleft@2x.png": { - "frame": {"x":393,"y":63,"w":30,"h":30}, + "frame": {"x":363,"y":191,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -834,7 +850,7 @@ }, "light/origin-bottomright@2x.png": { - "frame": {"x":425,"y":1,"w":30,"h":30}, + "frame": {"x":366,"y":223,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -842,7 +858,7 @@ }, "light/origin-middlecenter@2x.png": { - "frame": {"x":393,"y":95,"w":30,"h":30}, + "frame": {"x":395,"y":159,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -850,7 +866,7 @@ }, "light/origin-middleleft@2x.png": { - "frame": {"x":394,"y":127,"w":30,"h":30}, + "frame": {"x":424,"y":127,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -858,7 +874,7 @@ }, "light/origin-middleright@2x.png": { - "frame": {"x":394,"y":159,"w":30,"h":30}, + "frame": {"x":425,"y":95,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -866,7 +882,7 @@ }, "light/origin-topcenter@2x.png": { - "frame": {"x":395,"y":191,"w":30,"h":30}, + "frame": {"x":427,"y":63,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -874,7 +890,7 @@ }, "light/origin-topleft@2x.png": { - "frame": {"x":397,"y":223,"w":30,"h":30}, + "frame": {"x":458,"y":27,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -882,7 +898,7 @@ }, "light/origin-topright@2x.png": { - "frame": {"x":425,"y":63,"w":30,"h":30}, + "frame": {"x":395,"y":191,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -890,7 +906,7 @@ }, "light/origin@2x.png": { - "frame": {"x":102,"y":1,"w":31,"h":31}, + "frame": {"x":103,"y":1,"w":31,"h":31}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":31,"h":31}, @@ -898,7 +914,7 @@ }, "light/outline@2x.png": { - "frame": {"x":457,"y":1,"w":30,"h":30}, + "frame": {"x":398,"y":223,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -906,7 +922,7 @@ }, "light/play@2x.png": { - "frame": {"x":483,"y":85,"w":22,"h":26}, + "frame": {"x":486,"y":217,"w":22,"h":26}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":5,"y":3,"w":22,"h":26}, @@ -914,7 +930,7 @@ }, "light/project@2x.png": { - "frame": {"x":399,"y":33,"w":30,"h":28}, + "frame": {"x":399,"y":1,"w":30,"h":28}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":3,"w":30,"h":28}, @@ -930,7 +946,7 @@ }, "light/scale@2x.png": { - "frame": {"x":200,"y":1,"w":31,"h":30}, + "frame": {"x":202,"y":195,"w":31,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":1,"w":31,"h":30}, @@ -938,7 +954,7 @@ }, "light/select-region@2x.png": { - "frame": {"x":168,"y":65,"w":31,"h":30}, + "frame": {"x":169,"y":33,"w":31,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":31,"h":30}, @@ -946,7 +962,7 @@ }, "light/spine@2x.png": { - "frame": {"x":35,"y":35,"w":32,"h":32}, + "frame": {"x":35,"y":69,"w":32,"h":32}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, @@ -954,7 +970,7 @@ }, "light/sprite-type@2x.png": { - "frame": {"x":102,"y":34,"w":31,"h":31}, + "frame": {"x":103,"y":34,"w":31,"h":31}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":31,"h":31}, @@ -962,7 +978,7 @@ }, "light/text-type@2x.png": { - "frame": {"x":425,"y":95,"w":30,"h":30}, + "frame": {"x":427,"y":159,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -970,7 +986,7 @@ }, "light/tilemap-layer@2x.png": { - "frame": {"x":35,"y":69,"w":32,"h":32}, + "frame": {"x":35,"y":103,"w":32,"h":32}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, @@ -978,7 +994,7 @@ }, "light/tilemap@2x.png": { - "frame": {"x":35,"y":103,"w":32,"h":32}, + "frame": {"x":35,"y":137,"w":32,"h":32}, "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":32,"h":32}, @@ -986,7 +1002,7 @@ }, "light/tilesprite@2x.png": { - "frame": {"x":431,"y":33,"w":32,"h":24}, + "frame": {"x":204,"y":227,"w":32,"h":24}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":0,"y":4,"w":32,"h":24}, @@ -994,7 +1010,7 @@ }, "light/translate@2x.png": { - "frame": {"x":426,"y":127,"w":30,"h":30}, + "frame": {"x":456,"y":127,"w":30,"h":30}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":1,"w":30,"h":30}, @@ -1002,7 +1018,7 @@ }, "light/unlocked@2x.png": { - "frame": {"x":465,"y":33,"w":30,"h":24}, + "frame": {"x":463,"y":1,"w":30,"h":24}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":3,"w":30,"h":24}, @@ -1010,7 +1026,7 @@ }, "light/user-component@2x.png": { - "frame": {"x":102,"y":101,"w":30,"h":32}, + "frame": {"x":102,"y":134,"w":30,"h":32}, "rotated": false, "trimmed": true, "spriteSourceSize": {"x":1,"y":0,"w":30,"h":32}, @@ -1021,8 +1037,8 @@ "version": "1.0", "image": "atlas@2x.png", "format": "RGBA8888", - "size": {"w":507,"h":255}, + "size": {"w":509,"h":255}, "scale": "1", - "smartupdate": "$TexturePacker:SmartUpdate:5e67f5a1c8c9329ba77181c49fe873a7:f3bdf3a9c6869b70fb67e0931571668d:9eb1a82ac620c259737931966d4495d0$" + "smartupdate": "$TexturePacker:SmartUpdate:83f3fa35535afce5b5ea39c0ff8c8dd2:e9b9cbf5df6beb15642e03aafee2eb0b:9eb1a82ac620c259737931966d4495d0$" } } diff --git a/source/editor/plugins/phasereditor2d.resources/icons/atlas@2x.png b/source/editor/plugins/phasereditor2d.resources/icons/atlas@2x.png index 17d3c63598d57c77ce540c33b3517d05c753e028..1ddbcc94571abe1641abd589b9213dd828277c7d 100644 GIT binary patch literal 33095 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±81`N7ydjfLr73`m;|QAP{& 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 E1L)b*9RL6T diff --git a/source/editor/plugins/phasereditor2d.resources/src/ResourcesPlugin.ts b/source/editor/plugins/phasereditor2d.resources/src/ResourcesPlugin.ts index f25863e4b..1d879cb5a 100644 --- a/source/editor/plugins/phasereditor2d.resources/src/ResourcesPlugin.ts +++ b/source/editor/plugins/phasereditor2d.resources/src/ResourcesPlugin.ts @@ -9,6 +9,7 @@ namespace phasereditor2d.resources { // phasereditor2d.blocks export const ICON_ASSET_PACK = "asset-pack"; export const ICON_ANIMATIONS = "animations"; + export const ICON_ASEPRITE = "aseprite"; export const ICON_TILEMAP = "tilemap"; export const ICON_TILEMAP_LAYER = "tilemap-layer"; export const ICON_SPINE = "spine"; From 2d9413691d68d348403aea2c9ddc4a1e21545564 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 12 Oct 2023 15:05:08 -0400 Subject: [PATCH 21/58] Adds Aseprite asset type support. --- scripts/make-all-help-files.js | 3 ++ .../src/ui/controls/properties/FormBuilder.ts | 13 +++++-- .../src/core/AsepriteAssetPackItem.ts | 16 ++------- .../phasereditor2d.pack/src/core/AssetPack.ts | 2 +- .../src/core/BaseAtlasAssetPackItem.ts | 2 +- .../src/ui/DefaultAssetPackExtension.ts | 5 +++ .../ui/editor/properties/AsepriteSection.ts | 34 +++++++++++++++++++ .../src/ui/editor/properties/AtlasSection.ts | 2 ++ .../src/ui/editor/properties/BaseSection.ts | 4 ++- .../src/ui/importers/AsepriteImporter.ts | 11 ++++++ .../src/ui/importers/Importers.ts | 1 + .../ui/viewers/AsepriteItemCellRenderer.ts | 18 ++++++++++ .../viewers/AssetPackCellRendererProvider.ts | 4 +++ .../phasereditor2d.pack/docs/phaser-docs.json | 2 ++ 14 files changed, 99 insertions(+), 18 deletions(-) create mode 100644 source/editor/plugins/phasereditor2d.pack/src/ui/editor/properties/AsepriteSection.ts create mode 100644 source/editor/plugins/phasereditor2d.pack/src/ui/importers/AsepriteImporter.ts create mode 100644 source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AsepriteItemCellRenderer.ts diff --git a/scripts/make-all-help-files.js b/scripts/make-all-help-files.js index 7bfc38a6d..8d68865f0 100755 --- a/scripts/make-all-help-files.js +++ b/scripts/make-all-help-files.js @@ -8,6 +8,9 @@ utils.makeHelpFile([ "Phaser.Loader.LoaderPlugin.atlas(textureURL)", "Phaser.Types.Loader.FileTypes.AtlasJSONFileConfig.normalMap", + "Phaser.Loader.LoaderPlugin.aseprite(atlasURL)", + "Phaser.Loader.LoaderPlugin.aseprite(textureURL)", + "Phaser.Loader.LoaderPlugin.atlasXML(atlasURL)", "Phaser.Loader.LoaderPlugin.atlasXML(textureURL)", "Phaser.Types.Loader.FileTypes.AtlasXMLFileConfig.normalMap", diff --git a/source/editor/plugins/colibri/src/ui/controls/properties/FormBuilder.ts b/source/editor/plugins/colibri/src/ui/controls/properties/FormBuilder.ts index 3056cfc65..017fcf0de 100644 --- a/source/editor/plugins/colibri/src/ui/controls/properties/FormBuilder.ts +++ b/source/editor/plugins/colibri/src/ui/controls/properties/FormBuilder.ts @@ -44,11 +44,20 @@ namespace colibri.ui.controls.properties { return label; } - createButton(parent: HTMLElement, text: string, callback: (e?: MouseEvent) => void) { + createButton(parent: HTMLElement, textOrIcon: string | IImage, callback: (e?: MouseEvent) => void) { const btn = document.createElement("button"); - btn.innerText = text; + if (typeof textOrIcon === "string") { + + btn.innerText = textOrIcon; + + } else { + + const iconControl = new controls.IconControl(textOrIcon); + + btn.appendChild(iconControl.getCanvas()); + } btn.addEventListener("click", e => callback(e)); diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts b/source/editor/plugins/phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts index 79da0278f..2f6eaa3d2 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts @@ -1,30 +1,20 @@ -/// +/// namespace phasereditor2d.pack.core { import io = colibri.core.io; - + export class AsepriteAssetPackItem extends AssetPackItem { constructor(pack: AssetPack, data: any) { super(pack, data); } - getAtlasURL() { - - return this.getData().atlasURL; - } - - getTextureURL() { - - return this.getData().textureURL; - } - computeUsedFiles(files: Set) { super.computeUsedFiles(files); - this.addFilesFromDataKey(files, "atlasURL", "textureURL"); + this.addFilesFromDataKey(files, "atlasURL", "textureURL", "normalMap"); } } } \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/AssetPack.ts b/source/editor/plugins/phasereditor2d.pack/src/core/AssetPack.ts index c510ae80f..073adc7b4 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/core/AssetPack.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/core/AssetPack.ts @@ -14,7 +14,7 @@ namespace phasereditor2d.pack.core { export const SPINE_ATLAS_TYPE = "spineAtlas"; export const SPRITESHEET_TYPE = "spritesheet"; export const ANIMATION_TYPE = "animation"; - export const ASEPRITE_TYPE = "asprite"; + export const ASEPRITE_TYPE = "aseprite"; export const AUDIO_TYPE = "audio"; export const AUDIO_SPRITE_TYPE = "audioSprite"; export const BINARY_TYPE = "binary"; diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/BaseAtlasAssetPackItem.ts b/source/editor/plugins/phasereditor2d.pack/src/core/BaseAtlasAssetPackItem.ts index 8ffb152f6..d2fee1223 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/core/BaseAtlasAssetPackItem.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/core/BaseAtlasAssetPackItem.ts @@ -10,7 +10,7 @@ namespace phasereditor2d.pack.core { super.computeUsedFiles(files); - this.addFilesFromDataKey(files, "atlasURL", "textureURL"); + this.addFilesFromDataKey(files, "atlasURL", "textureURL", "normalMap"); } } } \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/DefaultAssetPackExtension.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/DefaultAssetPackExtension.ts index 0997fcce7..58ab83d6c 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/DefaultAssetPackExtension.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/DefaultAssetPackExtension.ts @@ -46,6 +46,7 @@ namespace phasereditor2d.pack.ui { multiatlas: "Multiatlas", spritesheet: "Spritesheet", animation: "Animation", + aseprite: "Aseprite", bitmapFont: "Bitmap Font", tilemapCSV: "Tilemap CSV", tilemapImpact: "Tilemap Impact", @@ -211,6 +212,8 @@ namespace phasereditor2d.pack.ui { core.contentTypes.CONTENT_TYPE_ANIMATIONS, core.ANIMATION_TYPE), + new editor.properties.AsepriteSection(page), + new editor.properties.BitmapFontSection(page), new editor.properties.TilemapCSVSection(page), @@ -485,6 +488,8 @@ namespace phasereditor2d.pack.ui { new importers.BitmapFontImporter(), + new importers.AsepriteImporter(), + new importers.SingleFileImporter(webContentTypes.core.CONTENT_TYPE_IMAGE, core.IMAGE_TYPE), new importers.SingleFileImporter(webContentTypes.core.CONTENT_TYPE_SVG, core.IMAGE_TYPE), diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/editor/properties/AsepriteSection.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/editor/properties/AsepriteSection.ts new file mode 100644 index 000000000..6154484e6 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/editor/properties/AsepriteSection.ts @@ -0,0 +1,34 @@ +/// + +namespace phasereditor2d.pack.ui.editor.properties { + + import controls = colibri.ui.controls; + + export class AsepriteSection extends BaseSection { + + constructor(page: controls.properties.PropertyPage) { + super(page, "phasereditor2d.pack.ui.editor.properties.AsepriteSection", "Aseprite", core.ASEPRITE_TYPE); + } + + canEdit(obj: any, n: number) { + + return super.canEdit(obj, n) && obj instanceof core.AsepriteAssetPackItem; + } + + createForm(parent: HTMLDivElement) { + + const comp = this.createGridElement(parent, 3); + + comp.style.gridTemplateColumns = "auto 1fr auto"; + + this.createFileField(comp, "Atlas URL", "atlasURL", core.contentTypes.CONTENT_TYPE_ASEPRITE, + "Phaser.Loader.LoaderPlugin.aseprite(atlasURL)"); + + this.createFileField(comp, "Texture URL", "textureURL", webContentTypes.core.CONTENT_TYPE_IMAGE, + "Phaser.Loader.LoaderPlugin.aseprite(textureURL)"); + + this.createFileField(comp, "Normal Map", "normalMap", webContentTypes.core.CONTENT_TYPE_IMAGE, + "Phaser.Types.Loader.FileTypes.AtlasJSONFileConfig.normalMap"); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/editor/properties/AtlasSection.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/editor/properties/AtlasSection.ts index 58941c5d0..ac16680a9 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/editor/properties/AtlasSection.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/editor/properties/AtlasSection.ts @@ -11,10 +11,12 @@ namespace phasereditor2d.pack.ui.editor.properties { } canEdit(obj: any, n: number) { + return super.canEdit(obj, n) && obj instanceof core.AtlasAssetPackItem; } createForm(parent: HTMLDivElement) { + const comp = this.createGridElement(parent, 3); comp.style.gridTemplateColumns = "auto 1fr auto"; diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/editor/properties/BaseSection.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/editor/properties/BaseSection.ts index 2d8f4c086..ea62c07af 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/editor/properties/BaseSection.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/editor/properties/BaseSection.ts @@ -152,7 +152,9 @@ namespace phasereditor2d.pack.ui.editor.properties { text.value = val === undefined ? "" : val; }); - this.createButton(comp, "Browse", () => { + const icon = colibri.ColibriPlugin.getInstance().getIcon(colibri.ICON_FOLDER); + + this.createButton(comp, icon, () => { this.browseFile_onlyContentType("Select File", contentType, (files) => { diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/importers/AsepriteImporter.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/importers/AsepriteImporter.ts new file mode 100644 index 000000000..fb77da641 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/importers/AsepriteImporter.ts @@ -0,0 +1,11 @@ +/// + +namespace phasereditor2d.pack.ui.importers { + + export class AsepriteImporter extends BaseAtlasImporter { + + constructor() { + super(core.contentTypes.CONTENT_TYPE_ASEPRITE, core.ASEPRITE_TYPE); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/importers/Importers.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/importers/Importers.ts index b678ad245..831c7d6e5 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/importers/Importers.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/importers/Importers.ts @@ -27,6 +27,7 @@ namespace phasereditor2d.pack.ui.importers { } static getImporter(type: string) { + return this.getAll().find(i => i.getType() === type); } } diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AsepriteItemCellRenderer.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AsepriteItemCellRenderer.ts new file mode 100644 index 000000000..e1d59f670 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AsepriteItemCellRenderer.ts @@ -0,0 +1,18 @@ +namespace phasereditor2d.pack.ui.viewers { + + import controls = colibri.ui.controls; + + export class AsepriteItemCellRenderer extends controls.viewers.IconImageCellRenderer { + + constructor() { + super(resources.getIcon(resources.ICON_ASEPRITE)); + } + + async preload(args: controls.viewers.PreloadCellArgs): Promise { + + super.preload(args); + + return (args.obj as pack.core.AsepriteAssetPackItem).preload(); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AssetPackCellRendererProvider.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AssetPackCellRendererProvider.ts index a4725355a..9fdafc962 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AssetPackCellRendererProvider.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AssetPackCellRendererProvider.ts @@ -46,6 +46,10 @@ namespace phasereditor2d.pack.ui.viewers { return new viewers.AnimationsItemCellRenderer(); + } else if (element instanceof pack.core.AsepriteAssetPackItem) { + + return new viewers.AsepriteItemCellRenderer(); + } else { const extensions = AssetPackPlugin.getInstance().getExtensions(); diff --git a/source/editor/plugins/phasereditor2d.resources/_res/phasereditor2d.pack/docs/phaser-docs.json b/source/editor/plugins/phasereditor2d.resources/_res/phasereditor2d.pack/docs/phaser-docs.json index f8daf0daa..9f18a15f2 100644 --- a/source/editor/plugins/phasereditor2d.resources/_res/phasereditor2d.pack/docs/phaser-docs.json +++ b/source/editor/plugins/phasereditor2d.resources/_res/phasereditor2d.pack/docs/phaser-docs.json @@ -2,6 +2,8 @@ "Phaser.Loader.LoaderPlugin.atlas(atlasURL)": "The absolute or relative URL to load the texture atlas json data file from. If undefined or `null` it will be set to `.json`, i.e. if `key` was \"alien\" then the URL will be \"alien.json\". Or, a well formed JSON object.", "Phaser.Loader.LoaderPlugin.atlas(textureURL)": "The absolute or relative URL to load the texture image file from. If undefined or `null` it will be set to `.png`, i.e. if `key` was \"alien\" then the URL will be \"alien.png\".", "Phaser.Types.Loader.FileTypes.AtlasJSONFileConfig.normalMap": "The filename of an associated normal map. It uses the same path and url to load as the texture image.", + "Phaser.Loader.LoaderPlugin.aseprite(atlasURL)": "The absolute or relative URL to load the texture atlas json data file from. If undefined or `null` it will be set to `.json`, i.e. if `key` was \"alien\" then the URL will be \"alien.json\". Or, a well formed JSON object.", + "Phaser.Loader.LoaderPlugin.aseprite(textureURL)": "The absolute or relative URL to load the texture image file from. If undefined or `null` it will be set to `.png`, i.e. if `key` was \"alien\" then the URL will be \"alien.png\".", "Phaser.Loader.LoaderPlugin.atlasXML(atlasURL)": "The absolute or relative URL to load the texture atlas xml data file from. If undefined or `null` it will be set to `.xml`, i.e. if `key` was \"alien\" then the URL will be \"alien.xml\".", "Phaser.Loader.LoaderPlugin.atlasXML(textureURL)": "The absolute or relative URL to load the texture image file from. If undefined or `null` it will be set to `.png`, i.e. if `key` was \"alien\" then the URL will be \"alien.png\".", "Phaser.Types.Loader.FileTypes.AtlasXMLFileConfig.normalMap": "The filename of an associated normal map. It uses the same path and url to load as the texture image.", From ef1f42ccafc7e112e161facc771caf5485dfb43b Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Fri, 13 Oct 2023 13:56:22 -0400 Subject: [PATCH 22/58] Adds Aseprite support. --- .../properties/AnimationInfoSection.ts | 1 - .../src/core/AnimationConfigInPackItem.ts | 4 +- .../src/core/AnimationsAssetPackItem.ts | 88 +++++-------------- .../src/core/AsepriteAssetPackItem.ts | 84 +++++++++++++++++- .../src/core/BaseAnimationsAssetPackItem.ts | 62 +++++++++++++ .../core/ImageFrameContainerAssetPackItem.ts | 4 +- .../src/core/PackFinder.ts | 34 ++----- .../src/core/parsers/AssetPackCache.ts | 7 +- .../AssetPackEditorOutlineContentProvider.ts | 2 +- .../ui/properties/AnimationsPreviewSection.ts | 4 +- .../ui/viewers/AnimationsItemCellRenderer.ts | 2 +- .../SceneEditorBlocksContentProvider.ts | 3 +- .../ui/blocks/SceneEditorBlocksProvider.ts | 2 +- .../SceneEditorBlocksTreeRendererProvider.ts | 1 + .../ui/sceneobjects/ImageLoaderExtension.ts | 20 +++-- .../sprite/SpriteAnimationSection.ts | 4 +- .../ui/sceneobjects/sprite/SpriteExtension.ts | 2 + .../AbstractAssetKeyPropertyType.ts | 2 +- .../AnimationKeyPropertyType.ts | 8 +- .../userProperties/EventPropertyType.ts | 4 +- 20 files changed, 206 insertions(+), 132 deletions(-) create mode 100644 source/editor/plugins/phasereditor2d.pack/src/core/BaseAnimationsAssetPackItem.ts diff --git a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationInfoSection.ts b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationInfoSection.ts index c494e1ba1..0aa3752f2 100644 --- a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationInfoSection.ts +++ b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationInfoSection.ts @@ -32,7 +32,6 @@ namespace phasereditor2d.animations.ui.editors.properties { }); } - { // Animations File this.createLabel(comp, "Animations File"); diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/AnimationConfigInPackItem.ts b/source/editor/plugins/phasereditor2d.pack/src/core/AnimationConfigInPackItem.ts index 6e4a22bc6..5df536677 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/core/AnimationConfigInPackItem.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/core/AnimationConfigInPackItem.ts @@ -2,11 +2,11 @@ namespace phasereditor2d.pack.core { export class AnimationConfigInPackItem { - private _parent: AnimationsAssetPackItem; + private _parent: BaseAnimationsAssetPackItem; private _key: string; private _frames: AnimationFrameConfigInPackItem[]; - constructor(parent: AnimationsAssetPackItem) { + constructor(parent: BaseAnimationsAssetPackItem) { this._parent = parent; this._frames = []; diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/AnimationsAssetPackItem.ts b/source/editor/plugins/phasereditor2d.pack/src/core/AnimationsAssetPackItem.ts index 9700d89b7..a7ac0a8a2 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/core/AnimationsAssetPackItem.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/core/AnimationsAssetPackItem.ts @@ -1,91 +1,43 @@ -/// +/// namespace phasereditor2d.pack.core { - import controls = colibri.ui.controls; + export class AnimationsAssetPackItem extends BaseAnimationsAssetPackItem { - export class AnimationsAssetPackItem extends AssetPackItem { + override getAnimationsFile() { - private _animations: AnimationConfigInPackItem[]; - - constructor(pack: AssetPack, data: any) { - super(pack, data); - } - - getUrl() { - - return this.getData()["url"]; - } - - getAnimations() { - - return this._animations || []; + const url = this.getData()["url"]; + + return this.getFileFromAssetUrl(url); } - getAnimationsFile() { - - return this.getFileFromAssetUrl(this.getUrl()); - } - - async preload() { - - if (this._animations) { - - return controls.PreloadResult.NOTHING_LOADED; - } - - this._animations = []; - - try { + protected override async parseAnimations(animations: AnimationConfigInPackItem[]): Promise { - const file = this.getAnimationsFile(); + const file = this.getAnimationsFile(); - if (file) { + if (file) { - const content = await colibri.ui.ide.FileUtils.preloadAndGetFileString(file); + const content = await colibri.ui.ide.FileUtils.preloadAndGetFileString(file); - const data = JSON.parse(content) as Phaser.Types.Animations.JSONAnimations; + const data = JSON.parse(content) as Phaser.Types.Animations.JSONAnimations; - for (const animData of data.anims) { + for (const animData of data.anims) { - const animConfig = new AnimationConfigInPackItem(this); + const animConfig = new AnimationConfigInPackItem(this); - animConfig.setKey(animData.key); + animConfig.setKey(animData.key); - for (const frameData of animData.frames) { + for (const frameData of animData.frames) { - const frameConfig = new AnimationFrameConfigInPackItem(); + const frameConfig = new AnimationFrameConfigInPackItem(); - frameConfig.setTextureKey(frameData.key); - frameConfig.setFrameKey(frameData.frame); + frameConfig.setTextureKey(frameData.key); + frameConfig.setFrameKey(frameData.frame); - animConfig.getFrames().push(frameConfig); - } - - this._animations.push(animConfig); + animConfig.getFrames().push(frameConfig); } - } - - } catch (e) { - - console.error(e); - } - - return controls.PreloadResult.RESOURCES_LOADED; - } - - async build(finder: PackFinder) { - - for (const anim of this._animations) { - - for (const frameConfig of anim.getFrames()) { - - const textureKey = frameConfig.getTextureKey(); - const frameKey = frameConfig.getFrameKey(); - - const textureFrame = finder.getAssetPackItemOrFrame(textureKey, frameKey); - frameConfig.setTextureFrame(textureFrame); + animations.push(animConfig); } } } diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts b/source/editor/plugins/phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts index 2f6eaa3d2..150ba7ff0 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts @@ -2,12 +2,94 @@ namespace phasereditor2d.pack.core { + import controls = colibri.ui.controls; import io = colibri.core.io; - export class AsepriteAssetPackItem extends AssetPackItem { + interface IAsepriteData { + meta: { + frameTags: { + name: string, + from: number, + to: number, + direction: "forward" | "backward" + }[] + } + } + + export class AsepriteAssetPackItem extends BaseAnimationsAssetPackItem { + + private _atlasItem: AtlasAssetPackItem; constructor(pack: AssetPack, data: any) { super(pack, data); + + this._atlasItem = new AtlasAssetPackItem(this.getPack(), this.getData()); + } + + override getAnimationsFile() { + + const url = this.getData()["atlasURL"]; + + return this.getFileFromAssetUrl(url); + } + + preloadImages(): Promise { + + return this._atlasItem.preloadImages(); + } + + async preload(): Promise { + + await this._atlasItem.preload(); + + return super.preload(); + } + + findFrame(frameName: string | number) { + + return this._atlasItem.findFrame(frameName); + } + + protected async parseAnimations(animations: AnimationConfigInPackItem[]): Promise { + + const atlasURL = this.getData().atlasURL; + + const file = this.getFileFromAssetUrl(atlasURL); + + if (file) { + + const content = await colibri.ui.ide.FileUtils.preloadAndGetFileString(file); + + const data = JSON.parse(content) as IAsepriteData; + + for (const animData of data.meta.frameTags) { + + const animConfig = new AnimationConfigInPackItem(this); + + animConfig.setKey(animData.name); + + for(let i = animData.from; i<= animData.to; i++) { + + const frameKey = i.toString(); + + const frameConfig = new AnimationFrameConfigInPackItem(); + + frameConfig.setTextureKey(this.getKey()); + frameConfig.setFrameKey(frameKey); + + animConfig.getFrames().push(frameConfig); + } + + animations.push(animConfig); + } + } + } + + addToPhaserCache(game: Phaser.Game, cache: parsers.AssetPackCache): void { + + const parser = new parsers.AtlasParser(this._atlasItem); + + parser.addToPhaserCache(game, cache); } computeUsedFiles(files: Set) { diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/BaseAnimationsAssetPackItem.ts b/source/editor/plugins/phasereditor2d.pack/src/core/BaseAnimationsAssetPackItem.ts new file mode 100644 index 000000000..faf035c6c --- /dev/null +++ b/source/editor/plugins/phasereditor2d.pack/src/core/BaseAnimationsAssetPackItem.ts @@ -0,0 +1,62 @@ +/// + +namespace phasereditor2d.pack.core { + + import controls = colibri.ui.controls; + import io = colibri.core.io; + + export abstract class BaseAnimationsAssetPackItem extends AssetPackItem { + + private _animations: AnimationConfigInPackItem[]; + + constructor(pack: AssetPack, data: any) { + super(pack, data); + } + + abstract getAnimationsFile(): io.FilePath; + + getAnimations() { + + return this._animations || []; + } + + async preload(): Promise { + + if (this._animations) { + + return controls.PreloadResult.NOTHING_LOADED; + } + + this._animations = []; + + try { + + await this.parseAnimations(this._animations); + + } catch (e) { + + console.error(e); + } + + return controls.PreloadResult.RESOURCES_LOADED; + } + + protected abstract parseAnimations(animations: AnimationConfigInPackItem[]): Promise; + + async build(finder: PackFinder) { + + for (const anim of this._animations) { + + for (const frameConfig of anim.getFrames()) { + + const textureKey = frameConfig.getTextureKey(); + const frameKey = frameConfig.getFrameKey(); + + const textureFrame = finder.getAssetPackItemOrFrame(textureKey, frameKey); + + frameConfig.setTextureFrame(textureFrame); + } + } + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/ImageFrameContainerAssetPackItem.ts b/source/editor/plugins/phasereditor2d.pack/src/core/ImageFrameContainerAssetPackItem.ts index e0acedeb8..86079fed3 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/core/ImageFrameContainerAssetPackItem.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/core/ImageFrameContainerAssetPackItem.ts @@ -181,8 +181,8 @@ namespace phasereditor2d.pack.core { protected abstract createParser(): parsers.ImageFrameParser; - findFrame(frameName: any) { - + findFrame(frameName: string | number) { + return this.getFrames().find(f => f.getName() === frameName); } diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/PackFinder.ts b/source/editor/plugins/phasereditor2d.pack/src/core/PackFinder.ts index c543c2ee7..2d9f9d6d6 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/core/PackFinder.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/core/PackFinder.ts @@ -58,10 +58,10 @@ namespace phasereditor2d.pack.core { findAnimationByKey(key: string) { return this.getAssets() - - .filter(i => i instanceof AnimationsAssetPackItem) - .flatMap((i: AnimationsAssetPackItem) => i.getAnimations()) + .filter(i => i instanceof BaseAnimationsAssetPackItem) + + .flatMap((i: BaseAnimationsAssetPackItem) => i.getAnimations()) .find(a => a.getKey() === key); } @@ -77,31 +77,6 @@ namespace phasereditor2d.pack.core { .find(item => item.getKey() === key); } - findPackItemOrFrameWithKey(key: string) { - - for (const pack of this._packs) { - - for (const item of pack.getItems()) { - - if (item.getKey() === key) { - return item; - } - - if (item instanceof ImageFrameContainerAssetPackItem) { - - for (const frame of item.getFrames()) { - - if (frame.getName() === key) { - return frame; - } - } - } - } - } - - return null; - } - getAssetPackItemOrFrame(key: string, frame: any): ImageAssetPackItem | AssetPackImageFrame { const item = this.findAssetPackItem(key); @@ -115,7 +90,8 @@ namespace phasereditor2d.pack.core { return item; - } else if (item instanceof ImageFrameContainerAssetPackItem) { + } else if (item instanceof ImageFrameContainerAssetPackItem + || item instanceof AsepriteAssetPackItem) { const imageFrame = item.findFrame(frame); diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/parsers/AssetPackCache.ts b/source/editor/plugins/phasereditor2d.pack/src/core/parsers/AssetPackCache.ts index 1539e38ee..1dc947085 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/core/parsers/AssetPackCache.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/core/parsers/AssetPackCache.ts @@ -29,11 +29,6 @@ namespace phasereditor2d.pack.core.parsers { this._assets.add(asset); } - getAssets() { - - return this._assets; - } - findAsset(key: string) { for(const asset of this._assets) { @@ -80,7 +75,7 @@ namespace phasereditor2d.pack.core.parsers { const files = new Set(); - for (const asset of this.getAssets()) { + for (const asset of this._assets) { files.add(asset.getPack().getFile()); diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/editor/AssetPackEditorOutlineContentProvider.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/editor/AssetPackEditorOutlineContentProvider.ts index e5e4ff54a..7cc1c05e2 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/editor/AssetPackEditorOutlineContentProvider.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/editor/AssetPackEditorOutlineContentProvider.ts @@ -30,7 +30,7 @@ namespace phasereditor2d.pack.ui.editor { return parent.getGuessSkinItems(); } - if (parent instanceof core.AnimationsAssetPackItem) { + if (parent instanceof core.BaseAnimationsAssetPackItem) { return parent.getAnimations(); } diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AnimationsPreviewSection.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AnimationsPreviewSection.ts index 66f916f36..e49ca4949 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AnimationsPreviewSection.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AnimationsPreviewSection.ts @@ -2,7 +2,7 @@ namespace phasereditor2d.pack.ui.properties { import controls = colibri.ui.controls; - export class AnimationsPreviewSection extends colibri.ui.ide.properties.BaseManyImagePreviewSection { + export class AnimationsPreviewSection extends colibri.ui.ide.properties.BaseManyImagePreviewSection { constructor(page: controls.properties.PropertyPage) { super(page, "phasereditor2d.pack.ui.properties.AnimationsPreviewSection", "Animations Preview", true); @@ -27,7 +27,7 @@ namespace phasereditor2d.pack.ui.properties { override canEdit(obj: any, n: number): boolean { - return obj instanceof core.AnimationsAssetPackItem; + return obj instanceof core.BaseAnimationsAssetPackItem; } override canEditNumber(n: number): boolean { diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AnimationsItemCellRenderer.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AnimationsItemCellRenderer.ts index 3edc0751b..00984872b 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AnimationsItemCellRenderer.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/viewers/AnimationsItemCellRenderer.ts @@ -12,7 +12,7 @@ namespace phasereditor2d.pack.ui.viewers { super.preload(args); - return (args.obj as pack.core.AnimationsAssetPackItem).preload(); + return (args.obj as pack.core.BaseAnimationsAssetPackItem).preload(); } } } \ No newline at end of file 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 146d7cc73..d87c5c580 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksContentProvider.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksContentProvider.ts @@ -12,6 +12,7 @@ namespace phasereditor2d.scene.ui.blocks { pack.core.UNITY_ATLAS_TYPE, pack.core.SPRITESHEET_TYPE, pack.core.ANIMATION_TYPE, + pack.core.ASEPRITE_TYPE, pack.core.BITMAP_FONT_TYPE, pack.core.SPINE_JSON_TYPE, pack.core.SPINE_BINARY_TYPE, @@ -157,7 +158,7 @@ namespace phasereditor2d.scene.ui.blocks { return parent.getItems().filter(i => SCENE_EDITOR_BLOCKS_PACK_ITEM_TYPES.has(i.getType())); } - if (parent instanceof pack.core.AnimationsAssetPackItem) { + if (parent instanceof pack.core.BaseAnimationsAssetPackItem) { return parent.getAnimations(); } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksProvider.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksProvider.ts index a558f6078..d74f7da5d 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksProvider.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksProvider.ts @@ -130,7 +130,7 @@ namespace phasereditor2d.scene.ui.blocks { } else if (obj instanceof pack.core.AnimationConfigInPackItem) { - const item = this.getFreshItem(obj.getParent()) as pack.core.AnimationsAssetPackItem; + const item = this.getFreshItem(obj.getParent()) as pack.core.BaseAnimationsAssetPackItem; if (item) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksTreeRendererProvider.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksTreeRendererProvider.ts index dd981420f..1663484fd 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksTreeRendererProvider.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksTreeRendererProvider.ts @@ -13,6 +13,7 @@ namespace phasereditor2d.scene.ui.blocks { pack.core.ATLAS_TYPE, pack.core.SPRITESHEET_TYPE, pack.core.ANIMATION_TYPE, + pack.core.ASEPRITE_TYPE, pack.core.BITMAP_FONT_TYPE, pack.core.SPINE_JSON_TYPE, pack.core.SPINE_BINARY_TYPE diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ImageLoaderExtension.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ImageLoaderExtension.ts index e86fd32aa..2caa5b412 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ImageLoaderExtension.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ImageLoaderExtension.ts @@ -29,12 +29,15 @@ namespace phasereditor2d.scene.ui.sceneobjects { acceptAsset(asset: any): boolean { return asset instanceof pack.core.ImageFrameContainerAssetPackItem + || asset instanceof pack.core.AsepriteAssetPackItem || asset instanceof pack.core.AssetPackImageFrame || asset instanceof pack.core.AnimationConfigInPackItem; } async updateLoader(scene: BaseScene, asset: any) { + console.log("here"); + if (asset instanceof pack.core.AnimationConfigInPackItem) { for(const animFrame of asset.getFrames()) { @@ -48,24 +51,25 @@ namespace phasereditor2d.scene.ui.sceneobjects { } } - let imageFrameContainerPackItem: pack.core.ImageFrameContainerAssetPackItem = null; + let framesContainer: pack.core.ImageFrameContainerAssetPackItem | pack.core.AsepriteAssetPackItem = null; - if (asset instanceof pack.core.ImageFrameContainerAssetPackItem) { + if (asset instanceof pack.core.ImageFrameContainerAssetPackItem + || asset instanceof pack.core.AsepriteAssetPackItem) { - imageFrameContainerPackItem = asset; + framesContainer = asset; } else if (asset instanceof pack.core.AssetPackImageFrame) { - imageFrameContainerPackItem = (asset.getPackItem() as pack.core.ImageFrameContainerAssetPackItem); + framesContainer = (asset.getPackItem() as pack.core.ImageFrameContainerAssetPackItem); } - if (imageFrameContainerPackItem !== null) { + if (framesContainer !== null) { - await imageFrameContainerPackItem.preload(); + await framesContainer.preload(); - await imageFrameContainerPackItem.preloadImages(); + await framesContainer.preloadImages(); - imageFrameContainerPackItem.addToPhaserCache(scene.game, scene.getPackCache()); + framesContainer.addToPhaserCache(scene.game, scene.getPackCache()); } } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts index 0475d68d1..1efd88bc4 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts @@ -43,8 +43,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { await finder.preload(); const animations = finder - .getAssets(i => i instanceof pack.core.AnimationsAssetPackItem) - .flatMap((i: pack.core.AnimationsAssetPackItem) => i.getAnimations()); + .getAssets(i => i instanceof pack.core.BaseAnimationsAssetPackItem) + .flatMap((i: pack.core.BaseAnimationsAssetPackItem) => i.getAnimations()); viewer.setInput(animations); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteExtension.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteExtension.ts index 2edb84629..fa3c0be63 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteExtension.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteExtension.ts @@ -47,6 +47,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { const sprite = super.createSceneObjectWithAsset(args2) as Sprite; + sprite.getEditorSupport().setLabel(animConfig.getKey()); + sprite.playMethod = PlayMethod.PLAY; sprite.playAnimation = animConfig.getKey(); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AbstractAssetKeyPropertyType.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AbstractAssetKeyPropertyType.ts index 00008fc78..23b9c4c79 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AbstractAssetKeyPropertyType.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AbstractAssetKeyPropertyType.ts @@ -125,7 +125,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { return parent.getFrames(); } - if (parent instanceof pack.core.AnimationsAssetPackItem) { + if (parent instanceof pack.core.BaseAnimationsAssetPackItem) { return parent.getAnimations(); } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AnimationKeyPropertyType.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AnimationKeyPropertyType.ts index e004d1619..c7446ae93 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AnimationKeyPropertyType.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AnimationKeyPropertyType.ts @@ -26,9 +26,9 @@ namespace phasereditor2d.scene.ui.sceneobjects { .flatMap(pack => pack.getItems()) - .filter(item => item instanceof pack.core.AnimationsAssetPackItem) + .filter(item => item instanceof pack.core.BaseAnimationsAssetPackItem) - .flatMap((item: pack.core.AnimationsAssetPackItem) => item.getAnimations()) + .flatMap((item: pack.core.BaseAnimationsAssetPackItem) => item.getAnimations()) .find(anim => anim.getKey() === value); @@ -60,9 +60,9 @@ namespace phasereditor2d.scene.ui.sceneobjects { .flatMap(pack => pack.getItems()) - .filter(item => item instanceof pack.core.AnimationsAssetPackItem) + .filter(item => item instanceof pack.core.BaseAnimationsAssetPackItem) - .flatMap((item: pack.core.AnimationsAssetPackItem) => item.getAnimations()); + .flatMap((item: pack.core.BaseAnimationsAssetPackItem) => item.getAnimations()); } getChildren(parent: any): any[] { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/EventPropertyType.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/EventPropertyType.ts index f8e971aa1..9a16002f3 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/EventPropertyType.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/EventPropertyType.ts @@ -100,8 +100,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { await packFinder.preload(); const animEvents = packFinder - .getAssets(i => i instanceof pack.core.AnimationsAssetPackItem) - .map(i => i as pack.core.AnimationsAssetPackItem) + .getAssets(i => i instanceof pack.core.BaseAnimationsAssetPackItem) + .map(i => i as pack.core.BaseAnimationsAssetPackItem) .flatMap(i => i.getAnimations()) .map(anim => new AnimationEventItem(`animationcomplete-${anim.getKey()}`, anim)); From 6744079b872a12b375c2a368170d34106f6723aa Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Fri, 13 Oct 2023 23:45:51 -0400 Subject: [PATCH 23/58] Shows Animation Configuration section. --- scripts/make-all-help-files.js | 15 +++- .../docs/phaser-docs.json | 4 +- .../phasereditor2d.scene/docs/phaser.json | 12 ++- .../plugins/phasereditor2d.resources/res.json | 18 +++- .../phasereditor2d.scene/src/ScenePlugin.ts | 1 + .../properties/SceneGameObjectSection.ts | 2 +- .../src/ui/sceneobjects/sprite/Sprite.ts | 17 +++- .../sprite/SpriteAnimationConfigSection.ts | 55 ++++++++++++ .../sprite/SpriteAnimationSection.ts | 17 ++-- .../ui/sceneobjects/sprite/SpriteComponent.ts | 87 +++++++++++++++---- .../ui/sceneobjects/sprite/SpriteExtension.ts | 4 +- 11 files changed, 196 insertions(+), 36 deletions(-) create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationConfigSection.ts diff --git a/scripts/make-all-help-files.js b/scripts/make-all-help-files.js index 8d68865f0..6bbfbb287 100755 --- a/scripts/make-all-help-files.js +++ b/scripts/make-all-help-files.js @@ -288,7 +288,18 @@ utils.makeHelpFile([ "spine.SkinsAndAnimationBoundsProvider(timeStep)", "spine.AnimationState.timeScale", "spine.AnimationStateData.defaultMix", - "spine.AnimationStateData.setMixWith" + "spine.AnimationStateData.setMixWith", + + "Phaser.Types.Animations.PlayAnimationConfig.frameRate", + "Phaser.Types.Animations.PlayAnimationConfig.delay", + "Phaser.Types.Animations.PlayAnimationConfig.repeat", + "Phaser.Types.Animations.PlayAnimationConfig.repeatDelay", + "Phaser.Types.Animations.PlayAnimationConfig.yoyo", + "Phaser.Types.Animations.PlayAnimationConfig.showOnStart", + "Phaser.Types.Animations.PlayAnimationConfig.hideOnComplete", + "Phaser.Types.Animations.PlayAnimationConfig.showBeforeDelay", + "Phaser.Types.Animations.PlayAnimationConfig.startFrame", + "Phaser.Types.Animations.PlayAnimationConfig.timeScale", ], "../source/editor/plugins/phasereditor2d.resources/_res/phasereditor2d.scene/docs/phaser.json"); @@ -303,7 +314,9 @@ utils.makeHelpFile([ "Phaser.Types.Animations.Animation.yoyo", "Phaser.Types.Animations.Animation.showOnStart", "Phaser.Types.Animations.Animation.hideOnComplete", + "Phaser.Types.Animations.Animation.showBeforeDelay", "Phaser.Types.Animations.Animation.skipMissedFrames", + "Phaser.Types.Animations.PlayAnimationConfig.startFrame", ], "../source/editor/plugins/phasereditor2d.resources/_res/phasereditor2d.animations/docs/phaser-docs.json"); diff --git a/source/editor/plugins/phasereditor2d.resources/_res/phasereditor2d.animations/docs/phaser-docs.json b/source/editor/plugins/phasereditor2d.resources/_res/phasereditor2d.animations/docs/phaser-docs.json index 71c70a048..a803a7d63 100644 --- a/source/editor/plugins/phasereditor2d.resources/_res/phasereditor2d.animations/docs/phaser-docs.json +++ b/source/editor/plugins/phasereditor2d.resources/_res/phasereditor2d.animations/docs/phaser-docs.json @@ -7,5 +7,7 @@ "Phaser.Types.Animations.Animation.yoyo": "Should the animation yoyo? (reverse back down to the start) before repeating?", "Phaser.Types.Animations.Animation.showOnStart": "Should sprite.visible = true when the animation starts to play? This happens _after_ any delay, if set.", "Phaser.Types.Animations.Animation.hideOnComplete": "Should sprite.visible = false when the animation finishes?", - "Phaser.Types.Animations.Animation.skipMissedFrames": "Skip frames if the time lags, or always advanced anyway?" + "Phaser.Types.Animations.Animation.showBeforeDelay": "If this animation has a delay, should it show the first frame immediately (true), or only after the delay (false)", + "Phaser.Types.Animations.Animation.skipMissedFrames": "Skip frames if the time lags, or always advanced anyway?", + "Phaser.Types.Animations.PlayAnimationConfig.startFrame": "The frame of the animation to start playback from." } \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.resources/_res/phasereditor2d.scene/docs/phaser.json b/source/editor/plugins/phasereditor2d.resources/_res/phasereditor2d.scene/docs/phaser.json index 81444c4bc..c99ab5cfc 100644 --- a/source/editor/plugins/phasereditor2d.resources/_res/phasereditor2d.scene/docs/phaser.json +++ b/source/editor/plugins/phasereditor2d.resources/_res/phasereditor2d.scene/docs/phaser.json @@ -177,5 +177,15 @@ "spine.SkinsAndAnimationBoundsProvider(timeStep)": "The time step to use for calculating the bounds. A smaller time step means more precision, but slower calculation.", "spine.AnimationState.timeScale": "Multiplier for the delta time when the animation state is updated, causing time for all animations and mixes to play slower\nor faster. Defaults to 1.\n\nSee TrackEntry {@link TrackEntry#timeScale} for affecting a single animation.", "spine.AnimationStateData.defaultMix": "The mix duration to use when no mix duration has been defined between two animations.", - "spine.AnimationStateData.setMixWith": "Sets the mix duration when changing from the specified animation to the other.\n\nSee {@link TrackEntry#mixDuration}." + "spine.AnimationStateData.setMixWith": "Sets the mix duration when changing from the specified animation to the other.\n\nSee {@link TrackEntry#mixDuration}.", + "Phaser.Types.Animations.PlayAnimationConfig.frameRate": "The frame rate of playback in frames per second (default 24 if duration is null)", + "Phaser.Types.Animations.PlayAnimationConfig.delay": "Delay before starting playback. Value given in milliseconds.", + "Phaser.Types.Animations.PlayAnimationConfig.repeat": "Number of times to repeat the animation (-1 for infinity)", + "Phaser.Types.Animations.PlayAnimationConfig.repeatDelay": "Delay before the animation repeats. Value given in milliseconds.", + "Phaser.Types.Animations.PlayAnimationConfig.yoyo": "Should the animation yoyo? (reverse back down to the start) before repeating?", + "Phaser.Types.Animations.PlayAnimationConfig.showOnStart": "Should sprite.visible = true when the animation starts to play?", + "Phaser.Types.Animations.PlayAnimationConfig.hideOnComplete": "Should sprite.visible = false when the animation finishes?", + "Phaser.Types.Animations.PlayAnimationConfig.showBeforeDelay": "If this animation has a delay, should it show the first frame immediately (true), or only after the delay (false)", + "Phaser.Types.Animations.PlayAnimationConfig.startFrame": "The frame of the animation to start playback from.", + "Phaser.Types.Animations.PlayAnimationConfig.timeScale": "The time scale to be applied to playback of this animation." } \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.resources/res.json b/source/editor/plugins/phasereditor2d.resources/res.json index 5117320ca..f0071b57b 100644 --- a/source/editor/plugins/phasereditor2d.resources/res.json +++ b/source/editor/plugins/phasereditor2d.resources/res.json @@ -8,12 +8,16 @@ "Phaser.Types.Animations.Animation.yoyo": "Should the animation yoyo? (reverse back down to the start) before repeating?", "Phaser.Types.Animations.Animation.showOnStart": "Should sprite.visible = true when the animation starts to play? This happens _after_ any delay, if set.", "Phaser.Types.Animations.Animation.hideOnComplete": "Should sprite.visible = false when the animation finishes?", - "Phaser.Types.Animations.Animation.skipMissedFrames": "Skip frames if the time lags, or always advanced anyway?" + "Phaser.Types.Animations.Animation.showBeforeDelay": "If this animation has a delay, should it show the first frame immediately (true), or only after the delay (false)", + "Phaser.Types.Animations.Animation.skipMissedFrames": "Skip frames if the time lags, or always advanced anyway?", + "Phaser.Types.Animations.PlayAnimationConfig.startFrame": "The frame of the animation to start playback from." }, "phasereditor2d.pack/docs/phaser-docs.json": { "Phaser.Loader.LoaderPlugin.atlas(atlasURL)": "The absolute or relative URL to load the texture atlas json data file from. If undefined or `null` it will be set to `.json`, i.e. if `key` was \"alien\" then the URL will be \"alien.json\". Or, a well formed JSON object.", "Phaser.Loader.LoaderPlugin.atlas(textureURL)": "The absolute or relative URL to load the texture image file from. If undefined or `null` it will be set to `.png`, i.e. if `key` was \"alien\" then the URL will be \"alien.png\".", "Phaser.Types.Loader.FileTypes.AtlasJSONFileConfig.normalMap": "The filename of an associated normal map. It uses the same path and url to load as the texture image.", + "Phaser.Loader.LoaderPlugin.aseprite(atlasURL)": "The absolute or relative URL to load the texture atlas json data file from. If undefined or `null` it will be set to `.json`, i.e. if `key` was \"alien\" then the URL will be \"alien.json\". Or, a well formed JSON object.", + "Phaser.Loader.LoaderPlugin.aseprite(textureURL)": "The absolute or relative URL to load the texture image file from. If undefined or `null` it will be set to `.png`, i.e. if `key` was \"alien\" then the URL will be \"alien.png\".", "Phaser.Loader.LoaderPlugin.atlasXML(atlasURL)": "The absolute or relative URL to load the texture atlas xml data file from. If undefined or `null` it will be set to `.xml`, i.e. if `key` was \"alien\" then the URL will be \"alien.xml\".", "Phaser.Loader.LoaderPlugin.atlasXML(textureURL)": "The absolute or relative URL to load the texture image file from. If undefined or `null` it will be set to `.png`, i.e. if `key` was \"alien\" then the URL will be \"alien.png\".", "Phaser.Types.Loader.FileTypes.AtlasXMLFileConfig.normalMap": "The filename of an associated normal map. It uses the same path and url to load as the texture image.", @@ -500,6 +504,16 @@ "spine.SkinsAndAnimationBoundsProvider(timeStep)": "The time step to use for calculating the bounds. A smaller time step means more precision, but slower calculation.", "spine.AnimationState.timeScale": "Multiplier for the delta time when the animation state is updated, causing time for all animations and mixes to play slower\nor faster. Defaults to 1.\n\nSee TrackEntry {@link TrackEntry#timeScale} for affecting a single animation.", "spine.AnimationStateData.defaultMix": "The mix duration to use when no mix duration has been defined between two animations.", - "spine.AnimationStateData.setMixWith": "Sets the mix duration when changing from the specified animation to the other.\n\nSee {@link TrackEntry#mixDuration}." + "spine.AnimationStateData.setMixWith": "Sets the mix duration when changing from the specified animation to the other.\n\nSee {@link TrackEntry#mixDuration}.", + "Phaser.Types.Animations.PlayAnimationConfig.frameRate": "The frame rate of playback in frames per second (default 24 if duration is null)", + "Phaser.Types.Animations.PlayAnimationConfig.delay": "Delay before starting playback. Value given in milliseconds.", + "Phaser.Types.Animations.PlayAnimationConfig.repeat": "Number of times to repeat the animation (-1 for infinity)", + "Phaser.Types.Animations.PlayAnimationConfig.repeatDelay": "Delay before the animation repeats. Value given in milliseconds.", + "Phaser.Types.Animations.PlayAnimationConfig.yoyo": "Should the animation yoyo? (reverse back down to the start) before repeating?", + "Phaser.Types.Animations.PlayAnimationConfig.showOnStart": "Should sprite.visible = true when the animation starts to play?", + "Phaser.Types.Animations.PlayAnimationConfig.hideOnComplete": "Should sprite.visible = false when the animation finishes?", + "Phaser.Types.Animations.PlayAnimationConfig.showBeforeDelay": "If this animation has a delay, should it show the first frame immediately (true), or only after the delay (false)", + "Phaser.Types.Animations.PlayAnimationConfig.startFrame": "The frame of the animation to start playback from.", + "Phaser.Types.Animations.PlayAnimationConfig.timeScale": "The time scale to be applied to playback of this animation." } } \ 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 58b85b709..44e228979 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts @@ -290,6 +290,7 @@ namespace phasereditor2d.scene { page => new ui.sceneobjects.ArcadeBodyMovementSection(page), page => new ui.sceneobjects.ArcadeBodyCollisionSection(page), page => new ui.sceneobjects.SpriteAnimationSection(page), + page => new ui.sceneobjects.SpriteAnimationConfigSection(page), page => new ui.sceneobjects.TextContentSection(page), page => new ui.sceneobjects.TextSection(page), page => new ui.sceneobjects.BitmapTextSection(page), 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 05bdbcede..f44282ec9 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 @@ -99,7 +99,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { this.createLock(parent, prop); this.createLabel(parent, prop.label, PhaserHelp(prop.tooltip)) - .style.gridColumn = "2/ span 2"; + .style.gridColumn = "2 / span 2"; this.createFloatField(parent, prop) .style.gridColumn = fullWidth ? "4 / span 3" : "4"; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/Sprite.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/Sprite.ts index 123706283..5156df3c8 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/Sprite.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/Sprite.ts @@ -1,6 +1,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { - export enum PlayMethod { + export enum AnimationPlayMethod { NONE = 0, PLAY = 1, PLAY_REVERSE = 2 @@ -10,8 +10,19 @@ namespace phasereditor2d.scene.ui.sceneobjects { private _editorSupport: SpriteEditorSupport; - public playMethod: PlayMethod = PlayMethod.NONE; - public playAnimation = ""; + public animationPlayMethod: AnimationPlayMethod = AnimationPlayMethod.NONE; + public animationKey = ""; + public animationCustomConfig = false; + public animationFrameRate = 24; + public animationDelay = 0; + public animationRepeat = 0; + public animationRepeatDelay = 0; + public animationYoyo = false; + public animationShowBeforeDelay = false; + public animationShowOnStart = false; + public animationHideOnComplete = false; + public animationStartFrame = 0; + public animationTimeScale = 1; constructor( scene: Scene, x: number, y: number, texture: string, frame?: string | number) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationConfigSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationConfigSection.ts new file mode 100644 index 000000000..eaf2516ba --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationConfigSection.ts @@ -0,0 +1,55 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + import controls = colibri.ui.controls; + + export class SpriteAnimationConfigSection extends SceneGameObjectSection { + + constructor(page: controls.properties.PropertyPage) { + super(page, "phasereditor2d.scene.ui.sceneobjects.SpriteAnimationConfigSection", "Animation Configuration"); + } + + protected getSectionHelpPath() { + + return "TODO"; + } + + createForm(parent: HTMLDivElement) { + + const comp = this.createGridElement(parent, 3); + + this.createNumberPropertyRow(comp, SpriteComponent.animationFrameRate); + + this.createNumberPropertyRow(comp, SpriteComponent.animationDelay); + + this.createNumberPropertyRow(comp, SpriteComponent.animationRepeat); + + this.createNumberPropertyRow(comp, SpriteComponent.animationRepeatDelay); + + this.createPropertyBoolean(comp, SpriteComponent.animationYoyo) + .labelElement.style.gridColumn = "2 / span 2"; + + this.createPropertyBoolean(comp, SpriteComponent.animationShowBeforeDelay) + .labelElement.style.gridColumn = "2 / span 2"; + + this.createPropertyBoolean(comp, SpriteComponent.animationShowOnStart) + .labelElement.style.gridColumn = "2 / span 2"; + + this.createPropertyBoolean(comp, SpriteComponent.animationHideOnComplete) + .labelElement.style.gridColumn = "2 / span 2" + + this.createNumberPropertyRow(comp, SpriteComponent.animationStartFrame); + + this.createNumberPropertyRow(comp, SpriteComponent.animationTimeScale); + } + + canEdit(obj: any, n: number): boolean { + + return obj instanceof Sprite && obj.animationCustomConfig; + } + + canEditNumber(n: number): boolean { + + return n > 0; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts index 1efd88bc4..4f8052c67 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts @@ -19,14 +19,14 @@ namespace phasereditor2d.scene.ui.sceneobjects { comp.style.gridTemplateColumns = "auto auto 1fr auto"; { - const btn = this.createPropertyEnumRow(comp, SpriteComponent.playMethod); + const btn = this.createPropertyEnumRow(comp, SpriteComponent.animationPlayMethod); btn.style.gridColumn = "3 / span 2"; } { // play animation - this.createPropertyStringRow(comp, SpriteComponent.playAnimation); + this.createPropertyStringRow(comp, SpriteComponent.animationKey); const btnUI = this.createButtonDialog({ dialogTittle: "Select Animation Key", @@ -54,12 +54,12 @@ namespace phasereditor2d.scene.ui.sceneobjects { }, getValue: () => { - return this.getSelection()[0].playAnimation || ""; + return this.getSelection()[0].animationKey || ""; }, onValueSelected: (value: string) => { this.getEditor().getUndoManager().add( - new SimpleOperation(this.getEditor(), this.getSelection(), SpriteComponent.playAnimation, value)); + new SimpleOperation(this.getEditor(), this.getSelection(), SpriteComponent.animationKey, value)); }, dialogElementToString: (viewer: controls.viewers.TreeViewer, value: pack.core.AnimationConfigInPackItem): string => { @@ -83,12 +83,17 @@ namespace phasereditor2d.scene.ui.sceneobjects { btnUI.buttonElement.disabled = this.getSelection() .filter(sprite => !sprite.getEditorSupport() - .isUnlockedProperty(SpriteComponent.playAnimation)) + .isUnlockedProperty(SpriteComponent.animationKey)) .length > 0; btnUI.updateDialogButtonIcon(); }); } + { + // enable config + + this.createPropertyBoolean(comp, SpriteComponent.animationCustomConfig); + } { // open animations editor const btn = this.createButton(comp, "Open Animation", async () => { @@ -99,7 +104,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { await finder.preload(); - const anim = finder.findAnimationByKey(sprite.playAnimation); + const anim = finder.findAnimationByKey(sprite.animationKey); if (anim) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteComponent.ts index 5c28c3dab..5dec677cc 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteComponent.ts @@ -2,51 +2,100 @@ namespace phasereditor2d.scene.ui.sceneobjects { export class SpriteComponent extends Component { - static playMethod: IEnumProperty = { - name: "playMethod", + static animationPlayMethod: IEnumProperty = { + name: "animationPlayMethod", label: "Action", - defValue: PlayMethod.NONE, - getValue: obj => obj.playMethod, - setValue: (obj: Sprite, val: PlayMethod) => { obj.playMethod = val; }, + defValue: AnimationPlayMethod.NONE, + getValue: obj => obj.animationPlayMethod, + setValue: (obj: Sprite, val: AnimationPlayMethod) => { obj.animationPlayMethod = val; }, getValueLabel: val => { switch (val) { - case PlayMethod.NONE: + case AnimationPlayMethod.NONE: return "NONE"; - case PlayMethod.PLAY: + case AnimationPlayMethod.PLAY: return "PLAY"; - case PlayMethod.PLAY_REVERSE: + case AnimationPlayMethod.PLAY_REVERSE: return "PLAY_REVERSE"; } }, - values: [PlayMethod.NONE, PlayMethod.PLAY, PlayMethod.PLAY_REVERSE], + values: [AnimationPlayMethod.NONE, AnimationPlayMethod.PLAY, AnimationPlayMethod.PLAY_REVERSE], }; - static playAnimation = SimpleProperty("playAnimation", "", "Play Animation", "The animation to play."); + static animationKey = SimpleProperty({ name: "animationKey", codeName: "key" }, "", "Animation Key", "The animation key to auto-play."); + static animationCustomConfig = SimpleProperty("animationCustomConfig", false, "Animation Config", "Set a new configuration?"); + static animationFrameRate = SimpleProperty({ name: "animationFrameRate", codeName: "frameRate" }, 24, "Frame Rate", "phaser:Phaser.Types.Animations.PlayAnimationConfig.frameRate"); + static animationDelay = SimpleProperty({ name: "animationDelay", codeName: "delay" }, 0, "Delay", "phaser:Phaser.Types.Animations.PlayAnimationConfig.delay"); + static animationRepeat = SimpleProperty({ name: "animationRepeat", codeName: "repeat" }, 0, "Repeat", "phaser:Phaser.Types.Animations.PlayAnimationConfig.repeat"); + static animationRepeatDelay = SimpleProperty({ name: "animationRepeatDelay", codeName: "repeatDelay" }, 0, "Repeat Delay", "phaser:Phaser.Types.Animations.PlayAnimationConfig.repeatDelay"); + static animationYoyo = SimpleProperty({ name: "animationYoyo", codeName: "yoyo" }, false, "Yoyo", "phaser:Phaser.Types.Animations.PlayAnimationConfig.yoyo"); + static animationShowBeforeDelay = SimpleProperty({ name: "animationShowBeforeDelay", codeName: "showBeforeDelay" }, false, "Show Before Delay", "phaser:Phaser.Types.Animations.PlayAnimationConfig.showBeforeDelay"); + static animationShowOnStart = SimpleProperty({ name: "animationShowOnStart", codeName: "showOnStart" }, false, "Show Before Start", "phaser:Phaser.Types.Animations.PlayAnimationConfig.showBeforeStart"); + static animationHideOnComplete = SimpleProperty({ name: "animationHideOnComplete", codeName: "hideOnComplete" }, false, "Hide On Complete", "phaser:Phaser.Types.Animations.PlayAnimationConfig.hideOnComplete"); + static animationStartFrame = SimpleProperty({ name: "animationStartFrame", codeName: "startFrame" }, 0, "Start Frame", "phaser:Phaser.Types.Animations.PlayAnimationConfig.startFrame"); + static animationTimeScale = SimpleProperty({ name: "animationTimeScale", codeName: "timeScale" }, 1, "Time Scale", "phaser:Phaser.Types.Animations.PlayAnimationConfig.timeScale"); constructor(obj: Sprite) { super(obj, [ - SpriteComponent.playMethod, - SpriteComponent.playAnimation - ]) + SpriteComponent.animationPlayMethod, + SpriteComponent.animationKey, + SpriteComponent.animationCustomConfig, + SpriteComponent.animationFrameRate, + SpriteComponent.animationDelay, + SpriteComponent.animationRepeat, + SpriteComponent.animationRepeatDelay, + SpriteComponent.animationYoyo, + SpriteComponent.animationShowBeforeDelay, + SpriteComponent.animationShowOnStart, + SpriteComponent.animationHideOnComplete, + SpriteComponent.animationStartFrame, + SpriteComponent.animationTimeScale, + ]); } buildSetObjectPropertiesCodeDOM(args: ISetObjectPropertiesCodeDOMArgs): void { - this.buildSetObjectPropertyCodeDOM([SpriteComponent.playMethod], args2 => { - + this.buildSetObjectPropertyCodeDOM([SpriteComponent.animationPlayMethod], args2 => { + const sprite = args.obj as Sprite; - const method = sprite.playMethod; + const method = sprite.animationPlayMethod; - if (method === PlayMethod.PLAY || method === PlayMethod.PLAY_REVERSE) { + if (method === AnimationPlayMethod.PLAY || method === AnimationPlayMethod.PLAY_REVERSE) { - const name = method === PlayMethod.PLAY? "play" : "playReverse"; + const name = method === AnimationPlayMethod.PLAY ? "play" : "playReverse"; const call = new core.code.MethodCallCodeDOM(name, args.objectVarName); - call.argLiteral(sprite.playAnimation); + + if (sprite.animationCustomConfig) { + + const config = {}; + + for (const prop of [ + SpriteComponent.animationKey, + SpriteComponent.animationFrameRate, + SpriteComponent.animationDelay, + SpriteComponent.animationRepeat, + SpriteComponent.animationRepeatDelay, + SpriteComponent.animationYoyo, + SpriteComponent.animationShowBeforeDelay, + SpriteComponent.animationShowOnStart, + SpriteComponent.animationHideOnComplete, + SpriteComponent.animationStartFrame, + SpriteComponent.animationTimeScale + ]) { + + colibri.core.json.write(config, prop.codeName, prop.getValue(sprite), prop.defValue); + } + + call.arg(JSON.stringify(config)); + + } else { + + call.argLiteral(sprite.animationKey); + } args.statements.push(call); } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteExtension.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteExtension.ts index fa3c0be63..aefb022df 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteExtension.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteExtension.ts @@ -49,8 +49,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { sprite.getEditorSupport().setLabel(animConfig.getKey()); - sprite.playMethod = PlayMethod.PLAY; - sprite.playAnimation = animConfig.getKey(); + sprite.animationPlayMethod = AnimationPlayMethod.PLAY; + sprite.animationKey = animConfig.getKey(); return sprite; } From 78bd76d20dae850726c6720d204bad4dba61c7cc Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Sat, 14 Oct 2023 00:04:11 -0400 Subject: [PATCH 24/58] Shows Aseprite animation blocks under the Animations section. --- .../src/ui/blocks/SceneEditorBlocksContentProvider.ts | 4 ++++ .../src/ui/blocks/SceneEditorBlocksTreeRendererProvider.ts | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) 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 d87c5c580..0f3b19a7c 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksContentProvider.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksContentProvider.ts @@ -195,6 +195,10 @@ namespace phasereditor2d.scene.ui.blocks { return this.getPackItems() .filter(item => item instanceof pack.core.BitmapFontAssetPackItem); + case pack.core.ANIMATION_TYPE: + return this.getPackItems() + .filter(item => item instanceof pack.core.BaseAnimationsAssetPackItem); + case BUILTIN_SECTION: return SCENE_OBJECT_CATEGORIES; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksTreeRendererProvider.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksTreeRendererProvider.ts index 1663484fd..dd981420f 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksTreeRendererProvider.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksTreeRendererProvider.ts @@ -13,7 +13,6 @@ namespace phasereditor2d.scene.ui.blocks { pack.core.ATLAS_TYPE, pack.core.SPRITESHEET_TYPE, pack.core.ANIMATION_TYPE, - pack.core.ASEPRITE_TYPE, pack.core.BITMAP_FONT_TYPE, pack.core.SPINE_JSON_TYPE, pack.core.SPINE_BINARY_TYPE From 8ca672da2214d99f3f36e653ec298073b73a2aad Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Sat, 14 Oct 2023 23:30:54 -0400 Subject: [PATCH 25/58] Allows selecting Aseprite frames from the texture dialogs. --- .../src/core/AsepriteAssetPackItem.ts | 5 ++++ .../SceneEditorBlocksCellRendererProvider.ts | 1 - .../texture/TextureSelectionDialog.ts | 28 +++++++++++++++++-- .../TextureConfigPropertyType.ts | 6 ++-- 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts b/source/editor/plugins/phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts index 150ba7ff0..19a08dbfb 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts @@ -50,6 +50,11 @@ namespace phasereditor2d.pack.core { return this._atlasItem.findFrame(frameName); } + getFrames() { + + return this._atlasItem.getFrames(); + } + protected async parseAnimations(animations: AnimationConfigInPackItem[]): Promise { const atlasURL = this.getData().atlasURL; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksCellRendererProvider.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksCellRendererProvider.ts index 06b7fdfa0..f13f46206 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksCellRendererProvider.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/blocks/SceneEditorBlocksCellRendererProvider.ts @@ -7,7 +7,6 @@ namespace phasereditor2d.scene.ui.blocks { constructor() { super("grid"); - } getCellRenderer(element: any) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/texture/TextureSelectionDialog.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/texture/TextureSelectionDialog.ts index db3415e6b..803c9cc10 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/texture/TextureSelectionDialog.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/texture/TextureSelectionDialog.ts @@ -8,7 +8,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { pack.core.IMAGE_TYPE, pack.core.SVG_TYPE, pack.core.ATLAS_TYPE, - pack.core.SPRITESHEET_TYPE + pack.core.SPRITESHEET_TYPE, + pack.core.ASEPRITE_TYPE ]; export class TextureSelectionDialog extends controls.dialogs.ViewerDialog { @@ -73,7 +74,19 @@ namespace phasereditor2d.scene.ui.sceneobjects { viewer.setLabelProvider(new pack.ui.viewers.AssetPackLabelProvider()); viewer.setTreeRenderer(new pack.ui.viewers.AssetPackTreeViewerRenderer(viewer, false)); - viewer.setCellRendererProvider(new pack.ui.viewers.AssetPackCellRendererProvider("grid")); + + viewer.setCellRendererProvider(new class extends pack.ui.viewers.AssetPackCellRendererProvider { + + getCellRenderer(element: any): controls.viewers.ICellRenderer { + + if (element instanceof pack.core.AnimationConfigInPackItem) { + + return new pack.ui.viewers.AnimationConfigCellRenderer("square"); + } + + return super.getCellRenderer(element); + } + }("grid")); viewer.setContentProvider(new (class extends ui.blocks.SceneEditorBlocksContentProvider { @@ -81,6 +94,17 @@ namespace phasereditor2d.scene.ui.sceneobjects { return input; } + + getChildren(parent: any): any[] { + + if (parent instanceof pack.core.AsepriteAssetPackItem) { + + return parent.getFrames(); + } + + return super.getChildren(parent); + } + })(this._editor, () => this._editor.getPackFinder().getPacks())); viewer.setCellSize(64, true); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/TextureConfigPropertyType.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/TextureConfigPropertyType.ts index 613aee117..fb5e3fa8c 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/TextureConfigPropertyType.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/TextureConfigPropertyType.ts @@ -92,7 +92,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { const finder = new pack.core.PackFinder(); await finder.preload(); - + const viewer = await super.createViewer(); viewer.setContentProvider(new TextureContentProvider(finder)); viewer.setTreeRenderer(new pack.ui.viewers.AssetPackTreeViewerRenderer(viewer, false)); @@ -114,6 +114,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { pack.core.IMAGE_TYPE, pack.core.SVG_TYPE, pack.core.ATLAS_TYPE, + pack.core.ASEPRITE_TYPE, pack.core.SPRITESHEET_TYPE]; } @@ -132,7 +133,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { return this.getItems(parent); } - if (parent instanceof pack.core.ImageFrameContainerAssetPackItem) { + if (parent instanceof pack.core.ImageFrameContainerAssetPackItem + || parent instanceof pack.core.AsepriteAssetPackItem) { if (!(parent instanceof pack.core.ImageAssetPackItem)) { From 9649dcd1cef854938230f2b0d77d3067efc5a246 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Sun, 15 Oct 2023 00:29:25 -0400 Subject: [PATCH 26/58] Fixes IAsepriteData. --- .../phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts b/source/editor/plugins/phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts index 19a08dbfb..2521882ff 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts @@ -10,8 +10,7 @@ namespace phasereditor2d.pack.core { frameTags: { name: string, from: number, - to: number, - direction: "forward" | "backward" + to: number }[] } } From fdaafe416bebb2de91b04d565d27bd2c62bd2cda Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Sun, 15 Oct 2023 02:04:56 -0400 Subject: [PATCH 27/58] A small improvement of the content type editor factory. --- .../colibri/src/ui/ide/ContentTypeEditorFactory.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/editor/plugins/colibri/src/ui/ide/ContentTypeEditorFactory.ts b/source/editor/plugins/colibri/src/ui/ide/ContentTypeEditorFactory.ts index 5228eaaba..89de76b14 100644 --- a/source/editor/plugins/colibri/src/ui/ide/ContentTypeEditorFactory.ts +++ b/source/editor/plugins/colibri/src/ui/ide/ContentTypeEditorFactory.ts @@ -6,14 +6,14 @@ namespace colibri.ui.ide { export class ContentTypeEditorFactory extends EditorFactory { private _name: string; - private _contentType: string; + private _contentTypeSet: Set; private _newEditor?: (factory?: ContentTypeEditorFactory) => EditorPart; - constructor(name: string, contentType: string, newEditor: (factory?: ContentTypeEditorFactory) => EditorPart) { + constructor(name: string, contentType: string | string[], newEditor: (factory?: ContentTypeEditorFactory) => EditorPart) { super(); this._name = name; - this._contentType = contentType; + this._contentTypeSet = new Set(Array.isArray(contentType) ? contentType : [contentType]); this._newEditor = newEditor; } @@ -29,7 +29,7 @@ namespace colibri.ui.ide { const contentType = colibri.Platform.getWorkbench() .getContentTypeRegistry().getCachedContentType(input); - return this._contentType === contentType; + return this._contentTypeSet.has(contentType); } return false; From fb3a7243f7dbfa589c66c41745337071b98d4e48 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Sun, 15 Oct 2023 02:14:15 -0400 Subject: [PATCH 28/58] Fixes importing aseprite files from the asset pack editor. --- .../phasereditor2d.pack/src/ui/editor/AssetPackEditor.ts | 4 ++++ .../plugins/phasereditor2d.pack/src/ui/importers/Importer.ts | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/editor/AssetPackEditor.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/editor/AssetPackEditor.ts index f770b8103..15fef9a24 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/editor/AssetPackEditor.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/editor/AssetPackEditor.ts @@ -401,9 +401,13 @@ namespace phasereditor2d.pack.ui.editor { const items = await importData.importer.autoImport(this._pack, importData.files); + const finder = new pack.core.PackFinder(this._pack); + for (const item of items) { await item.preload(); + + await item.build(finder); } this._viewer.repaint(); diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/importers/Importer.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/importers/Importer.ts index aa0906f82..cdf6fbd41 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/importers/Importer.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/importers/Importer.ts @@ -72,7 +72,6 @@ namespace phasereditor2d.pack.ui.importers { pack.addItem(item); - await item.preload(); const finder = new core.PackFinder(); From 07db6ff2c010aa9d0c3e3ad19996073dc61fd78e Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Sun, 15 Oct 2023 03:58:42 -0400 Subject: [PATCH 29/58] Adds the Preview Animation dialog. --- .../properties/AnimationInfoSection.ts | 21 +++ .../src/core/AsepriteAssetPackItem.ts | 12 ++ .../sprite/AnimationPreviewDialog.ts | 70 +++++++++ .../sprite/AnimationPreviewManager.ts | 144 ++++++++++++++++++ 4 files changed, 247 insertions(+) create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewDialog.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts diff --git a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationInfoSection.ts b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationInfoSection.ts index 0aa3752f2..485f7796a 100644 --- a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationInfoSection.ts +++ b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationInfoSection.ts @@ -57,6 +57,27 @@ namespace phasereditor2d.animations.ui.editors.properties { : ""; }); } + + { + // preview button + + this.createButton(comp, "Preview Animation", async () => { + + const elem = this.getSelectionFirstElement(); + + + const animAsset = elem.getParent(); + const animationKey = elem.getKey(); + + await animAsset.preload(); + + const dlg = new scene.ui.sceneobjects.AnimationPreviewDialog( + animAsset, animationKey); + + dlg.create(); + + }).style.gridColumn = "span 2"; + } } canEdit(obj: any, n: number): boolean { diff --git a/source/editor/plugins/phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts b/source/editor/plugins/phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts index 2521882ff..99584e128 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/core/AsepriteAssetPackItem.ts @@ -32,6 +32,18 @@ namespace phasereditor2d.pack.core { return this.getFileFromAssetUrl(url); } + getAtlasFile() { + + return this.getAnimationsFile(); + } + + getTextureFile() { + + const url = this.getData()["textureURL"]; + + return this.getFileFromAssetUrl(url); + } + preloadImages(): Promise { return this._atlasItem.preloadImages(); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewDialog.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewDialog.ts new file mode 100644 index 000000000..4207376a0 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewDialog.ts @@ -0,0 +1,70 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + import controls = colibri.ui.controls; + + export class AnimationPreviewDialog extends controls.dialogs.Dialog { + private _previewManager: AnimationPreviewManager; + private _animationAsset: pack.core.BaseAnimationsAssetPackItem; + private _animationKey: string; + + constructor(animationAsset: pack.core.BaseAnimationsAssetPackItem, animationKey: string) { + super(); + + const size = Math.min(window.innerWidth * 0.5, window.innerHeight * 0.5); + + this.setSize(size, size); + + this._animationAsset = animationAsset; + this._animationKey = animationKey; + } + + protected createDialogArea(): void { + + const clientArea = document.createElement("div") + + clientArea.classList.add("DialogClientArea"); + + const style = clientArea.style; + + style.width = "100%"; + style.height = "100%"; + + this.getElement().appendChild(clientArea); + + this._previewManager = new AnimationPreviewManager(clientArea); + } + + createUI() { + + const finder = new pack.core.PackFinder(); + + finder.preload().then(() => { + + setTimeout(() => { + + this._previewManager.createGame({ + animationAsset: this._animationAsset, + animationKey: this._animationKey, + finder + }); + }, 10); + }); + } + + create(hideParentDialog?: boolean): void { + + super.create(hideParentDialog); + + this.createUI(); + + this.setTitle("Animation Preview"); + + this.addButton("Close", () => this.close()); + + this.eventDialogClose.addListener(() => { + + this._previewManager.dispose(); + }) + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts new file mode 100644 index 000000000..3d1aae6b4 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts @@ -0,0 +1,144 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + export class AnimationPreviewManager { + + private _parent: HTMLElement; + private _game: Phaser.Game; + + constructor(parent: HTMLElement) { + + this._parent = parent; + } + + dispose() { + + if (this._game) { + + this._game.destroy(true); + } + } + + createGame(data: IAnimationPreviewSceneData) { + + const { width, height } = this._parent.getBoundingClientRect(); + + this._game = new Phaser.Game({ + width, height, + parent: this._parent, + transparent: true, + pixelArt: true, + fps: { + target: 30, + }, + scale: { + mode: Phaser.Scale.ScaleModes.NONE, + resizeInterval: 10 + } + }); + + this._game.canvas.classList.add("SpinePreviewCanvas"); + + this._game.scene.add("PreviewScene", PreviewScene, true, data); + + setTimeout(() => this._game.scale.refresh(), 10); + } + } + + export interface IAnimationPreviewSceneData { + animationAsset: pack.core.BaseAnimationsAssetPackItem; + animationKey: string; + finder: pack.core.PackFinder + } + + class PreviewScene extends Phaser.Scene { + + private _data: IAnimationPreviewSceneData; + + init(data: IAnimationPreviewSceneData) { + + this._data = data; + } + + preload() { + + const asset = this._data.animationAsset; + + if (asset instanceof pack.core.AnimationsAssetPackItem) { + + const cache = new pack.core.parsers.AssetPackCache(); + + for(const item of this._data.finder.getAssets()) { + + item.addToPhaserCache(this.game, cache); + } + + this.load.animation(asset.getKey(), asset.getAnimationsFile().getExternalUrl()); + + } else { + + const asset2 = asset as pack.core.AsepriteAssetPackItem; + + const textureURL = asset2.getTextureFile().getExternalUrl(); + const atlasURL = asset2.getAnimationsFile().getExternalUrl(); + + console.log("load", textureURL, atlasURL); + + this.load.aseprite(asset2.getKey(), textureURL, atlasURL); + } + } + + create() { + + this.anims.createFromAseprite(this._data.animationAsset.getKey()); + + const obj = this.add.sprite(400, 400, null); + + obj.play({ + key: this._data.animationKey, + repeat: -1 + }); + + obj.on("drag", (pointer: any, dragX: number, dragY: number) => { + + obj.setPosition(dragX, dragY); + }); + + const w = 100000; + + obj.setInteractive({ + draggable: true, + hitArea: new Phaser.Geom.Rectangle(-w, -w, w * 2, w * 2), + hitAreaCallback: Phaser.Geom.Rectangle.Contains + }); + + const camera = this.cameras.main; + + const gameWidth = camera.width; + const gameHeight = camera.height; + + const fx = gameWidth / obj.width; + const fy = gameHeight / obj.height; + + const z = Math.min(fx, fy); + + obj.setOrigin(0.5, 0.5); + obj.setPosition(this.game.scale.width / 2, this.game.scale.height / 2); + + camera.zoom = z; + + this.input.on("wheel", (pointer: any, over: any, deltaX: number, deltaY: number, deltaZ: number) => { + + const scrollWidth = Math.abs(deltaY) * 2; + + const screenWidth = camera.width; + + const zoomDelta = scrollWidth / (screenWidth + scrollWidth); + + const zoomFactor = (deltaY > 0 ? 1 - zoomDelta : 1 + zoomDelta); + + camera.zoom *= zoomFactor; + camera.zoom = Math.min(100, Math.max(0.2, camera.zoom)); + }); + } + } +} \ No newline at end of file From 2d0c85aa5cc1bc222b88002200edbd106fd7c611 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Sun, 15 Oct 2023 04:20:01 -0400 Subject: [PATCH 30/58] More about animation preview. --- .../properties/AnimationInfoSection.ts | 3 -- .../ui/sceneobjects/ImageLoaderExtension.ts | 2 - .../sprite/AnimationPreviewDialog.ts | 5 --- .../sprite/AnimationPreviewManager.ts | 2 - .../sprite/SpriteAnimationSection.ts | 37 +++++++++++++++++-- 5 files changed, 33 insertions(+), 16 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationInfoSection.ts b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationInfoSection.ts index 485f7796a..f2d920db5 100644 --- a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationInfoSection.ts +++ b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationInfoSection.ts @@ -65,12 +65,9 @@ namespace phasereditor2d.animations.ui.editors.properties { const elem = this.getSelectionFirstElement(); - const animAsset = elem.getParent(); const animationKey = elem.getKey(); - await animAsset.preload(); - const dlg = new scene.ui.sceneobjects.AnimationPreviewDialog( animAsset, animationKey); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ImageLoaderExtension.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ImageLoaderExtension.ts index 2caa5b412..f7e717e54 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ImageLoaderExtension.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/ImageLoaderExtension.ts @@ -36,8 +36,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { async updateLoader(scene: BaseScene, asset: any) { - console.log("here"); - if (asset instanceof pack.core.AnimationConfigInPackItem) { for(const animFrame of asset.getFrames()) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewDialog.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewDialog.ts index 4207376a0..63c6b933d 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewDialog.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewDialog.ts @@ -24,11 +24,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { clientArea.classList.add("DialogClientArea"); - const style = clientArea.style; - - style.width = "100%"; - style.height = "100%"; - this.getElement().appendChild(clientArea); this._previewManager = new AnimationPreviewManager(clientArea); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts index 3d1aae6b4..fd26cfdca 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts @@ -36,8 +36,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { } }); - this._game.canvas.classList.add("SpinePreviewCanvas"); - this._game.scene.add("PreviewScene", PreviewScene, true, data); setTimeout(() => this._game.scale.refresh(), 10); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts index 4f8052c67..70a2e34ae 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts @@ -94,9 +94,38 @@ namespace phasereditor2d.scene.ui.sceneobjects { this.createPropertyBoolean(comp, SpriteComponent.animationCustomConfig); } + { + // preview animations editor + const btn = this.createButton(comp, "Preview Animation", async () => { + + console.log("here"); + + const sprite = this.getSelectionFirstElement(); + + const finder = new pack.core.PackFinder(); + + await finder.preload(); + + const anim = finder.findAnimationByKey(sprite.animationKey); + + if (anim) { + + const dlg = new AnimationPreviewDialog(anim.getParent(), anim.getKey()); + + dlg.create(); + + } else { + + alert("Animation not found."); + } + }); + + btn.style.gridColumn = "1 / span 4"; + } + { // open animations editor - const btn = this.createButton(comp, "Open Animation", async () => { + const btn = this.createButton(comp, "Open Animation File", async () => { const sprite = this.getSelectionFirstElement(); @@ -104,12 +133,12 @@ namespace phasereditor2d.scene.ui.sceneobjects { await finder.preload(); - const anim = finder.findAnimationByKey(sprite.animationKey); + const anim = finder.findAnimationByKey(sprite.animationKey); - if (anim) { + if (anim) { ScenePlugin.getInstance().openAnimationInEditor(anim); - } + } }); btn.style.gridColumn = "1 / span 4"; From 211b4a5eeb53440fc1b2e5d6fff4ae744fa2f80a Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Sun, 15 Oct 2023 16:24:08 -0400 Subject: [PATCH 31/58] Fixes animation preview dialog layout. --- .../src/ui/sceneobjects/sprite/AnimationPreviewManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts index fd26cfdca..65e0a0da4 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts @@ -23,7 +23,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { const { width, height } = this._parent.getBoundingClientRect(); this._game = new Phaser.Game({ - width, height, + width, height: height - 5, parent: this._parent, transparent: true, pixelArt: true, From 990d7413e49d949ef99b2564082101da12efc749 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Sun, 15 Oct 2023 16:47:31 -0400 Subject: [PATCH 32/58] Preview animation dialog allows repeating the animation. Allows getting the config from the the sprite. --- .../properties/AnimationInfoSection.ts | 4 +- .../sprite/AnimationPreviewDialog.ts | 18 ++++++-- .../sprite/AnimationPreviewManager.ts | 30 ++++++++++---- .../sprite/SpriteAnimationSection.ts | 15 +++++-- .../ui/sceneobjects/sprite/SpriteComponent.ts | 41 +++++++++++-------- 5 files changed, 73 insertions(+), 35 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationInfoSection.ts b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationInfoSection.ts index f2d920db5..44ff4d1c5 100644 --- a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationInfoSection.ts +++ b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationInfoSection.ts @@ -69,7 +69,9 @@ namespace phasereditor2d.animations.ui.editors.properties { const animationKey = elem.getKey(); const dlg = new scene.ui.sceneobjects.AnimationPreviewDialog( - animAsset, animationKey); + animAsset, { + key: animationKey + }); dlg.create(); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewDialog.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewDialog.ts index 63c6b933d..ffc675c41 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewDialog.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewDialog.ts @@ -5,9 +5,9 @@ namespace phasereditor2d.scene.ui.sceneobjects { export class AnimationPreviewDialog extends controls.dialogs.Dialog { private _previewManager: AnimationPreviewManager; private _animationAsset: pack.core.BaseAnimationsAssetPackItem; - private _animationKey: string; + private _config: Phaser.Types.Animations.PlayAnimationConfig; - constructor(animationAsset: pack.core.BaseAnimationsAssetPackItem, animationKey: string) { + constructor(animationAsset: pack.core.BaseAnimationsAssetPackItem, config: Phaser.Types.Animations.PlayAnimationConfig) { super(); const size = Math.min(window.innerWidth * 0.5, window.innerHeight * 0.5); @@ -15,7 +15,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { this.setSize(size, size); this._animationAsset = animationAsset; - this._animationKey = animationKey; + this._config = config; } protected createDialogArea(): void { @@ -39,7 +39,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { this._previewManager.createGame({ animationAsset: this._animationAsset, - animationKey: this._animationKey, + config: this._config, finder }); }, 10); @@ -56,6 +56,16 @@ namespace phasereditor2d.scene.ui.sceneobjects { this.addButton("Close", () => this.close()); + this.addButton("Play", () => { + + this._previewManager.play(false); + }); + + this.addButton("Play Repeat", () => { + + this._previewManager.play(true); + }); + this.eventDialogClose.addListener(() => { this._previewManager.dispose(); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts index 65e0a0da4..af9b6f75b 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts @@ -18,6 +18,11 @@ namespace phasereditor2d.scene.ui.sceneobjects { } } + play(forceRepeat: boolean) { + + this._game.events.emit("play", forceRepeat); + } + createGame(data: IAnimationPreviewSceneData) { const { width, height } = this._parent.getBoundingClientRect(); @@ -44,7 +49,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { export interface IAnimationPreviewSceneData { animationAsset: pack.core.BaseAnimationsAssetPackItem; - animationKey: string; + config: Phaser.Types.Animations.PlayAnimationConfig, finder: pack.core.PackFinder } @@ -64,8 +69,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { if (asset instanceof pack.core.AnimationsAssetPackItem) { const cache = new pack.core.parsers.AssetPackCache(); - - for(const item of this._data.finder.getAssets()) { + + for (const item of this._data.finder.getAssets()) { item.addToPhaserCache(this.game, cache); } @@ -79,8 +84,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { const textureURL = asset2.getTextureFile().getExternalUrl(); const atlasURL = asset2.getAnimationsFile().getExternalUrl(); - console.log("load", textureURL, atlasURL); - this.load.aseprite(asset2.getKey(), textureURL, atlasURL); } } @@ -91,10 +94,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { const obj = this.add.sprite(400, 400, null); - obj.play({ - key: this._data.animationKey, - repeat: -1 - }); + obj.play(this._data.config); obj.on("drag", (pointer: any, dragX: number, dragY: number) => { @@ -137,6 +137,18 @@ namespace phasereditor2d.scene.ui.sceneobjects { camera.zoom *= zoomFactor; camera.zoom = Math.min(100, Math.max(0.2, camera.zoom)); }); + + this.game.events.on("play", (forceRepeat: boolean) => { + + if (forceRepeat) { + + obj.play({ ...this._data.config, repeat: -1 }); + + } else { + + obj.play(this._data.config); + } + }); } } } \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts index 70a2e34ae..b4a781779 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts @@ -98,8 +98,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { // preview animations editor const btn = this.createButton(comp, "Preview Animation", async () => { - console.log("here"); - const sprite = this.getSelectionFirstElement(); const finder = new pack.core.PackFinder(); @@ -110,7 +108,18 @@ namespace phasereditor2d.scene.ui.sceneobjects { if (anim) { - const dlg = new AnimationPreviewDialog(anim.getParent(), anim.getKey()); + console.log("here"); + + const config: Phaser.Types.Animations.PlayAnimationConfig = { + key: anim.getKey() + } + + if (sprite.animationCustomConfig) { + + SpriteComponent.buildPlayConfig(sprite, config); + } + + const dlg = new AnimationPreviewDialog(anim.getParent(), config); dlg.create(); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteComponent.ts index 5dec677cc..525678f8d 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteComponent.ts @@ -71,24 +71,9 @@ namespace phasereditor2d.scene.ui.sceneobjects { if (sprite.animationCustomConfig) { - const config = {}; - - for (const prop of [ - SpriteComponent.animationKey, - SpriteComponent.animationFrameRate, - SpriteComponent.animationDelay, - SpriteComponent.animationRepeat, - SpriteComponent.animationRepeatDelay, - SpriteComponent.animationYoyo, - SpriteComponent.animationShowBeforeDelay, - SpriteComponent.animationShowOnStart, - SpriteComponent.animationHideOnComplete, - SpriteComponent.animationStartFrame, - SpriteComponent.animationTimeScale - ]) { - - colibri.core.json.write(config, prop.codeName, prop.getValue(sprite), prop.defValue); - } + const config: Phaser.Types.Animations.PlayAnimationConfig = {} as any; + + SpriteComponent.buildPlayConfig(sprite, config); call.arg(JSON.stringify(config)); @@ -101,5 +86,25 @@ namespace phasereditor2d.scene.ui.sceneobjects { } }); } + + public static buildPlayConfig(sprite: Sprite, config: Phaser.Types.Animations.PlayAnimationConfig) { + + for (const prop of [ + SpriteComponent.animationKey, + SpriteComponent.animationFrameRate, + SpriteComponent.animationDelay, + SpriteComponent.animationRepeat, + SpriteComponent.animationRepeatDelay, + SpriteComponent.animationYoyo, + SpriteComponent.animationShowBeforeDelay, + SpriteComponent.animationShowOnStart, + SpriteComponent.animationHideOnComplete, + SpriteComponent.animationStartFrame, + SpriteComponent.animationTimeScale + ]) { + + colibri.core.json.write(config, prop.codeName, prop.getValue(sprite), prop.defValue); + } + } } } \ No newline at end of file From 8802324ce9977aeaed1eb1a362d76b740ac6f4af Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Sun, 15 Oct 2023 16:58:23 -0400 Subject: [PATCH 33/58] Allows preview animation from the animations preview section. --- .../src/ui/properties/AnimationsPreviewSection.ts | 10 ++++++++++ .../phasereditor2d.scene/src/ScenePlugin.ts | 14 ++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AnimationsPreviewSection.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AnimationsPreviewSection.ts index e49ca4949..2ce794a4e 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AnimationsPreviewSection.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/properties/AnimationsPreviewSection.ts @@ -23,6 +23,16 @@ namespace phasereditor2d.pack.ui.properties { viewer.setLabelProvider(new viewers.AssetPackLabelProvider()); viewer.setCellRendererProvider(new controls.viewers.EmptyCellRendererProvider( e => new viewers.AnimationConfigCellRenderer("square"))); + + viewer.eventOpenItem.addListener((elem: pack.core.AnimationConfigInPackItem) => { + + AnimationsPreviewSection.openPreviewDialog(elem); + }); + } + + static openPreviewDialog(elem: core.AnimationConfigInPackItem) { + + alert("Preview dialog not found."); } override canEdit(obj: any, n: number): boolean { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts index 44e228979..be2e1db43 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts @@ -113,6 +113,8 @@ namespace phasereditor2d.scene { this._sceneFinder = new core.json.SceneFinder(); + this.registerAnimationsPreviewDialogInAssetPack(); + // migrations reg.addExtension(new core.migrations.OriginMigration_v2_to_v3()); @@ -354,6 +356,18 @@ namespace phasereditor2d.scene { )); } + private registerAnimationsPreviewDialogInAssetPack() { + + pack.ui.properties.AnimationsPreviewSection.openPreviewDialog = elem => { + + const dlg = new ui.sceneobjects.AnimationPreviewDialog(elem.getParent(), { + key: elem.getKey() + }); + + dlg.create(); + }; + } + async openAnimationInEditor(anim: pack.core.AnimationConfigInPackItem) { // nothing, it is injected in the AnimationsPlugin. } From 70f8501a6ddb262f0db91bd15a9261a53b40e4ae Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Sun, 15 Oct 2023 18:39:54 -0400 Subject: [PATCH 34/58] Use canvas manager for the animation preview dialog. --- .../phasereditor2d.scene/src/ScenePlugin.ts | 8 ++++ .../src/ui/CanvasManager.ts | 41 +++++++++++++++++++ .../src/ui/ThumbnailCache.ts | 2 +- .../sprite/AnimationPreviewManager.ts | 8 +++- .../sprite/SpriteAnimationSection.ts | 2 - 5 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/CanvasManager.ts diff --git a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts index be2e1db43..f99202a9a 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts @@ -43,6 +43,7 @@ namespace phasereditor2d.scene { private _docs: phasereditor2d.ide.core.PhaserDocs; private _eventsDocs: phasereditor2d.ide.core.PhaserDocs; private _spineThumbnailCache: ui.SpineThumbnailCache; + private _canvasManager: ui.CanvasManager; static getInstance() { @@ -54,6 +55,11 @@ namespace phasereditor2d.scene { super("phasereditor2d.scene"); } + getCanvasManager() { + + return this._canvasManager; + } + async starting() { const type = window.localStorage.getItem("phasereditor2d.scene.RENDER_TYPE"); @@ -67,6 +73,8 @@ namespace phasereditor2d.scene { this.setDefaultRenderPixelArt(pixelArt); console.log("ScenePlugin: default pixelArt: " + pixelArt); + + this._canvasManager = new ui.CanvasManager(); } setDefaultRenderType(type?: "canvas" | "webgl") { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/CanvasManager.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/CanvasManager.ts new file mode 100644 index 000000000..90595a5d3 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/CanvasManager.ts @@ -0,0 +1,41 @@ +namespace phasereditor2d.scene.ui { + + export class CanvasManager { + + private _freeCanvases: HTMLCanvasElement[]; + private _count: number; + + constructor() { + + this._freeCanvases = []; + this._count = 0; + } + + takeCanvas() { + + if (this._freeCanvases.length === 0) { + + this._count++; + + console.log("CanvasManager: create new canvas. Count new: " + this._count); + + const canvas = document.createElement("canvas"); + + return canvas; + + } else { + + console.log("CanvasManager: reuse canvas. Total available: " + (this._freeCanvases.length - 1)); + + return this._freeCanvases.pop(); + } + } + + releaseCanvas(canvas: HTMLCanvasElement) { + + console.log("CanvasManager: release canvas. Total available: " + (this._freeCanvases.length + 1)); + + this._freeCanvases.push(canvas); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/ThumbnailCache.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/ThumbnailCache.ts index 19679b710..c76006a4a 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/ThumbnailCache.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/ThumbnailCache.ts @@ -11,7 +11,7 @@ namespace phasereditor2d.scene.ui { async clearCache() { - await this._database.clear(); + await this._database?.clear(); } constructor(dbName: string) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts index af9b6f75b..bef139259 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts @@ -14,7 +14,9 @@ namespace phasereditor2d.scene.ui.sceneobjects { if (this._game) { - this._game.destroy(true); + ScenePlugin.getInstance().getCanvasManager().releaseCanvas(this._game.canvas); + + this._game.destroy(false); } } @@ -27,9 +29,13 @@ namespace phasereditor2d.scene.ui.sceneobjects { const { width, height } = this._parent.getBoundingClientRect(); + const canvas = ScenePlugin.getInstance().getCanvasManager().takeCanvas(); + this._game = new Phaser.Game({ width, height: height - 5, parent: this._parent, + canvas, + type: ScenePlugin.DEFAULT_CANVAS_CONTEXT, transparent: true, pixelArt: true, fps: { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts index b4a781779..ac46af49f 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts @@ -108,8 +108,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { if (anim) { - console.log("here"); - const config: Phaser.Types.Animations.PlayAnimationConfig = { key: anim.getKey() } From 8786e6c6200ae02d5ca6e85a1a8eacf4d8025675 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Sun, 15 Oct 2023 18:55:23 -0400 Subject: [PATCH 35/58] Uses the canvas manager in the spine preview dialog. --- .../ui/sceneobjects/spine/SpinePreviewManager.ts | 16 +++++++++++++++- .../sprite/AnimationPreviewManager.ts | 10 +++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/spine/SpinePreviewManager.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/spine/SpinePreviewManager.ts index 6911f224d..b20d07a1d 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/spine/SpinePreviewManager.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/spine/SpinePreviewManager.ts @@ -14,6 +14,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { if (this._game) { + ScenePlugin.getInstance().getCanvasManager().releaseCanvas(this._game.canvas); + this._game.destroy(true); } } @@ -52,8 +54,14 @@ namespace phasereditor2d.scene.ui.sceneobjects { const { width, height } = this._parent.getBoundingClientRect(); + const canvas = ScenePlugin.getInstance().getCanvasManager().takeCanvas(); + + canvas.style.visibility = "hidden"; + this._game = new Phaser.Game({ + type: ScenePlugin.DEFAULT_CANVAS_CONTEXT, width, height, + canvas, parent: this._parent, transparent: true, fps: { @@ -75,7 +83,13 @@ namespace phasereditor2d.scene.ui.sceneobjects { this._game.scene.add("PreviewScene", PreviewScene, true, data); this._game.scene.add("EventScene", EventScene); - setTimeout(() => this._game.scale.refresh(), 10); + setTimeout(() => { + + canvas.style.visibility = "visible"; + + this._game.scale.refresh(); + + }, 10); } } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts index bef139259..df5d6fb5c 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts @@ -47,9 +47,17 @@ namespace phasereditor2d.scene.ui.sceneobjects { } }); + canvas.style.visibility = "hidden"; + this._game.scene.add("PreviewScene", PreviewScene, true, data); - setTimeout(() => this._game.scale.refresh(), 10); + setTimeout(() => { + + canvas.style.visibility = "visible"; + + this._game.scale.refresh(); + + }, 10); } } From a884f095afdc57413cfeb1b3245b32f5cdf166dc Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Sun, 15 Oct 2023 18:56:33 -0400 Subject: [PATCH 36/58] Updates changelog. --- CHANGELOG.MD | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 786f66b5a..b44ce13ee 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -11,6 +11,7 @@ - New Animations Preview section. * Adds new Animation section to Sprite game objects. It is about to auto-start a sprite animation. * Shows Sprite Animation blocks in the Scene Editor's Blocks view. +* Optimizes pooling of WEBGL contexts. ## v3.63.0 - Sep 30, 2023 From 41b688e8653f63056e36a12d85df7a2e02bd780a Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Sun, 15 Oct 2023 19:46:29 -0400 Subject: [PATCH 37/58] Improves canvas manager. --- .../phasereditor2d.scene/src/ui/CanvasManager.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/CanvasManager.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/CanvasManager.ts index 90595a5d3..ec21fa5d2 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/CanvasManager.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/CanvasManager.ts @@ -33,9 +33,16 @@ namespace phasereditor2d.scene.ui { releaseCanvas(canvas: HTMLCanvasElement) { - console.log("CanvasManager: release canvas. Total available: " + (this._freeCanvases.length + 1)); + if (this._freeCanvases.indexOf(canvas) < 0) { - this._freeCanvases.push(canvas); + console.log("CanvasManager: release canvas. Total available: " + (this._freeCanvases.length + 1)); + + this._freeCanvases.push(canvas); + + } else { + + console.log("CanvasManager: Hey, releasing a released canvas?"); + } } } } \ No newline at end of file From c02a669227c8adc6cd18997274af1b58fc786661 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Sun, 15 Oct 2023 20:02:46 -0400 Subject: [PATCH 38/58] Uses canvas manager in scene editor and animations editor. --- .../src/ui/editors/AnimationsEditor.ts | 6 ++++-- .../src/ui/editor/SceneEditor.ts | 13 ++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/AnimationsEditor.ts b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/AnimationsEditor.ts index fa0789e82..2058afc83 100644 --- a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/AnimationsEditor.ts +++ b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/AnimationsEditor.ts @@ -394,9 +394,9 @@ namespace phasereditor2d.animations.ui.editors { this._overlayLayer = new AnimationsOverlayLayer(this); container.appendChild(this._overlayLayer.getCanvas()); - const pool = Phaser.Display.Canvas.CanvasPool; + this._gameCanvas = scene.ScenePlugin.getInstance().getCanvasManager().takeCanvas(); + this._gameCanvas.style.visibility = "hidden"; - this._gameCanvas = pool.create2D(this.getElement(), 100, 100); this._gameCanvas.style.position = "absolute"; this._gameCanvas.tabIndex = 1; container.appendChild(this._gameCanvas); @@ -495,6 +495,8 @@ namespace phasereditor2d.animations.ui.editors { this._gameBooted = true; + this._gameCanvas.style.visibility = "visible"; + if (!this._sceneRead) { await this.readScene(); 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 1f98600e1..536184b73 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditor.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditor.ts @@ -275,13 +275,8 @@ namespace phasereditor2d.scene.ui.editor { this.getElement().appendChild(container); - const pool = Phaser.Display.Canvas.CanvasPool; - - this._gameCanvas = ScenePlugin.DEFAULT_EDITOR_CANVAS_CONTEXT === Phaser.CANVAS - - ? pool.create2D(this.getElement(), 100, 100) - - : pool.createWebGL(this.getElement(), 100, 100); + this._gameCanvas = ScenePlugin.getInstance().getCanvasManager().takeCanvas(); + this._gameCanvas.style.visibility = "hidden"; this._gameCanvas.classList.add("GameCanvas"); @@ -724,6 +719,8 @@ namespace phasereditor2d.scene.ui.editor { if (this._scene) { this._scene.destroyGame(); + + ScenePlugin.getInstance().getCanvasManager().releaseCanvas( this._game.canvas); } this._cellRendererCache.clear(); @@ -879,6 +876,8 @@ namespace phasereditor2d.scene.ui.editor { this._gameBooted = true; + this._gameCanvas.style.visibility = "visible"; + if (!this._sceneRead) { await this.readScene(); From 0c89d730739eed97ca4c0038db75b3c1994e604a Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Sun, 15 Oct 2023 21:50:01 -0400 Subject: [PATCH 39/58] Removes method. --- .../phasereditor2d.scene/src/ui/editor/SceneEditor.ts | 5 ----- 1 file changed, 5 deletions(-) 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 536184b73..2fa2ce0f7 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditor.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditor.ts @@ -635,11 +635,6 @@ namespace phasereditor2d.scene.ui.editor { return this._overlayLayer; } - getGameCanvas() { - - return this._gameCanvas; - } - getScene() { return this._scene; From d14d87b6d28ed21344671831538b69ffed652574 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Sun, 15 Oct 2023 21:56:56 -0400 Subject: [PATCH 40/58] Adds missing field `showBeforeDelay` to the Animations Editor. --- .../phasereditor2d.animations/src/ui/editors/AnimationsModel.ts | 2 ++ .../src/ui/editors/properties/AnimationSection.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/AnimationsModel.ts b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/AnimationsModel.ts index b4deb8140..f5537eedd 100644 --- a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/AnimationsModel.ts +++ b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/AnimationsModel.ts @@ -73,6 +73,8 @@ namespace phasereditor2d.animations.ui.editors { if (!a.yoyo) delete a.yoyo; + if (!a.showBeforeDelay) delete a.showBeforeDelay; + if (!a.showOnStart) delete a.showOnStart; if (!a.hideOnComplete) delete a.hideOnComplete; diff --git a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationSection.ts b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationSection.ts index 5ba86a9fc..90d6abcc0 100644 --- a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationSection.ts +++ b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/properties/AnimationSection.ts @@ -69,6 +69,8 @@ namespace phasereditor2d.animations.ui.editors.properties { this.createBooleanProperty(comp, "yoyo", "Yoyo"); + this.createBooleanProperty(comp, "showBeforeDelay", "Show Before Delay"); + this.createBooleanProperty(comp, "showOnStart", "Show On Start"); this.createBooleanProperty(comp, "hideOnComplete", "Hide On Complete"); From 81833bd1522024bb3cf399bab4718563d1f8f667 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Sun, 15 Oct 2023 22:28:18 -0400 Subject: [PATCH 41/58] Better handing of reusable canvases. --- CHANGELOG.MD | 1 + .../src/ui/editors/AnimationsEditor.ts | 14 +++++++++++--- .../sceneobjects/spine/SpinePreviewManager.ts | 18 +++++++++++++++--- .../sprite/AnimationPreviewManager.ts | 18 +++++++++++++++--- 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index b44ce13ee..b35d955ff 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -10,6 +10,7 @@ - New Animation preview section. - New Animations Preview section. * Adds new Animation section to Sprite game objects. It is about to auto-start a sprite animation. +* Adds `showBeforeDelay` to the Animations Editor. * Shows Sprite Animation blocks in the Scene Editor's Blocks view. * Optimizes pooling of WEBGL contexts. diff --git a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/AnimationsEditor.ts b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/AnimationsEditor.ts index 2058afc83..e87201734 100644 --- a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/AnimationsEditor.ts +++ b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/AnimationsEditor.ts @@ -410,7 +410,11 @@ namespace phasereditor2d.animations.ui.editors { private registerDropListeners() { - this._gameCanvas.addEventListener("dragover", e => { + // canvas can be reused, don't use it for events + + const eventElement = this._gameCanvas.parentElement; + + eventElement.addEventListener("dragover", e => { const dataArray = controls.Controls.getApplicationDragData(); @@ -426,7 +430,7 @@ namespace phasereditor2d.animations.ui.editors { } }); - this._gameCanvas.addEventListener("drop", e => { + eventElement.addEventListener("drop", e => { e.preventDefault(); @@ -442,7 +446,11 @@ namespace phasereditor2d.animations.ui.editors { this._menuCreator = new AnimationsEditorMenuCreator(this); - this._gameCanvas.addEventListener("contextmenu", e => this.onMenu(e)); + // canvas can be reused, don't use it for events + + const eventElement = this._gameCanvas.parentElement; + + eventElement.addEventListener("contextmenu", e => this.onMenu(e)); } private onMenu(e: MouseEvent) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/spine/SpinePreviewManager.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/spine/SpinePreviewManager.ts index b20d07a1d..f24936264 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/spine/SpinePreviewManager.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/spine/SpinePreviewManager.ts @@ -104,6 +104,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { class PreviewScene extends Phaser.Scene { private _data: IPreviewSceneData; + private _wheelListener: (e: WheelEvent) => void; init(data: IPreviewSceneData) { @@ -188,7 +189,9 @@ namespace phasereditor2d.scene.ui.sceneobjects { obj.animationState.addListener(eventScene); - this.input.on("wheel", (pointer: any, over: any, deltaX: number, deltaY: number, deltaZ: number) => { + this._wheelListener = (e: WheelEvent) => { + + const deltaY = e.deltaY; const scrollWidth = Math.abs(deltaY) * 2; @@ -199,8 +202,10 @@ namespace phasereditor2d.scene.ui.sceneobjects { const zoomFactor = (deltaY > 0 ? 1 - zoomDelta : 1 + zoomDelta); camera.zoom *= zoomFactor; - camera.zoom = Math.min(4, Math.max(0.2, camera.zoom)); - }); + camera.zoom = Math.min(100, Math.max(0.2, camera.zoom)); + }; + + this.game.canvas.addEventListener("wheel", this._wheelListener); this.game.events.on("updateAnimation", (track: number, animationName: string, loop: boolean) => { @@ -241,6 +246,13 @@ namespace phasereditor2d.scene.ui.sceneobjects { obj.animationStateData.setMix(from, to, duration); } }); + + this.game.events.once(Phaser.Core.Events.DESTROY, () => this.removeListeners()); + } + + private removeListeners() { + + this.game.canvas.removeEventListener("wheel", this._wheelListener); } } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts index df5d6fb5c..7ef079e8e 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts @@ -56,7 +56,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { canvas.style.visibility = "visible"; this._game.scale.refresh(); - + }, 10); } } @@ -70,6 +70,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { class PreviewScene extends Phaser.Scene { private _data: IAnimationPreviewSceneData; + private _wheelListener: (e: WheelEvent) => void; init(data: IAnimationPreviewSceneData) { @@ -138,7 +139,9 @@ namespace phasereditor2d.scene.ui.sceneobjects { camera.zoom = z; - this.input.on("wheel", (pointer: any, over: any, deltaX: number, deltaY: number, deltaZ: number) => { + this._wheelListener = (e: WheelEvent) => { + + const deltaY = e.deltaY; const scrollWidth = Math.abs(deltaY) * 2; @@ -150,7 +153,9 @@ namespace phasereditor2d.scene.ui.sceneobjects { camera.zoom *= zoomFactor; camera.zoom = Math.min(100, Math.max(0.2, camera.zoom)); - }); + }; + + this.game.canvas.addEventListener("wheel", this._wheelListener); this.game.events.on("play", (forceRepeat: boolean) => { @@ -163,6 +168,13 @@ namespace phasereditor2d.scene.ui.sceneobjects { obj.play(this._data.config); } }); + + this.game.events.once(Phaser.Core.Events.DESTROY, () => this.removeListeners()); + } + + private removeListeners() { + + this.game.canvas.removeEventListener("wheel", this._wheelListener); } } } \ No newline at end of file From ea51befa8d28f19b2234da7de78493f20e03d082 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 17 Oct 2023 20:51:49 -0400 Subject: [PATCH 42/58] Starts with the aseprite code snippets. --- .../src/ui/dialogs/AssetSelectionDialog.ts | 28 ++++--- .../phasereditor2d.scene/src/ScenePlugin.ts | 22 ++++- .../src/core/json/ISceneData.ts | 1 + .../src/core/json/SceneWriter.ts | 9 +++ .../phasereditor2d.scene/src/ui/Scene.ts | 13 +++ .../phasereditor2d.scene/src/ui/SceneMaker.ts | 5 ++ .../src/ui/codesnippets/CodeSnippet.ts | 44 ++++++++++ .../ui/codesnippets/CodeSnippetExtension.ts | 32 ++++++++ .../src/ui/codesnippets/CodeSnippets.ts | 52 ++++++++++++ .../aseprite/CodeSnippetsSnapshotOperation.ts | 79 ++++++++++++++++++ .../aseprite/CreateFromAsepriteCodeSnippet.ts | 43 ++++++++++ .../CreateFromAsepriteCodeSnippetExtension.ts | 46 +++++++++++ .../CreateFromAsepriteCodeSnippetSection.ts | 81 +++++++++++++++++++ .../src/ui/editor/SceneEditorMenuCreator.ts | 38 ++++++++- .../src/ui/editor/SelectionManager.ts | 19 +++-- .../SceneEditorOutlineContentProvider.ts | 11 +++ .../SceneEditorOutlineRendererProvider.ts | 8 ++ .../SceneEditorOutlineStyledLabelProvider.ts | 8 ++ 18 files changed, 518 insertions(+), 21 deletions(-) create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippet.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippetExtension.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippets.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/aseprite/CodeSnippetsSnapshotOperation.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/aseprite/CreateFromAsepriteCodeSnippet.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/aseprite/CreateFromAsepriteCodeSnippetExtension.ts create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/aseprite/CreateFromAsepriteCodeSnippetSection.ts diff --git a/source/editor/plugins/phasereditor2d.pack/src/ui/dialogs/AssetSelectionDialog.ts b/source/editor/plugins/phasereditor2d.pack/src/ui/dialogs/AssetSelectionDialog.ts index dba95d3c9..bdf8ed9b5 100644 --- a/source/editor/plugins/phasereditor2d.pack/src/ui/dialogs/AssetSelectionDialog.ts +++ b/source/editor/plugins/phasereditor2d.pack/src/ui/dialogs/AssetSelectionDialog.ts @@ -7,12 +7,15 @@ namespace phasereditor2d.pack.ui.dialogs { private _selectionCallback: (selection: any[]) => void; private _cancelCallback: () => void; private _viewerLayout: "tree" | "grid"; + private _selectOnlyOne: boolean; - constructor(layout: "tree" | "grid" = "grid") { + constructor(layout: "tree" | "grid" = "grid", selectOnlyOne = true) { super(new controls.viewers.TreeViewer("phasereditor2d.pack.ui.dialogs.AssetSelectionDialog"), true); this._viewerLayout = layout; + this._selectOnlyOne = selectOnlyOne; + const size = this.getSize(); this.setSize(size.width, size.height * 1.5); @@ -28,7 +31,7 @@ namespace phasereditor2d.pack.ui.dialogs { this._cancelCallback = callback; } - async getResultPromise(): Promise { + async getResultPromise(): Promise { const promise = new Promise((resolve, reject) => { @@ -39,7 +42,7 @@ namespace phasereditor2d.pack.ui.dialogs { this.setCancelCallback(() => { - reject(); + resolve(undefined); }) }); @@ -50,7 +53,7 @@ namespace phasereditor2d.pack.ui.dialogs { const sel = await this.getResultPromise(); - return sel[0]; + return sel ?? sel[0]; } create(hideParentDialog = true) { @@ -61,7 +64,7 @@ namespace phasereditor2d.pack.ui.dialogs { if (this._viewerLayout === "tree") { - viewer.setTreeRenderer(new controls.viewers.GridTreeViewerRenderer(viewer)); + viewer.setTreeRenderer(new controls.viewers.TreeViewerRenderer(viewer)); } else { @@ -79,15 +82,18 @@ namespace phasereditor2d.pack.ui.dialogs { this.setTitle("Select Asset"); - this.enableButtonOnlyWhenOneElementIsSelected( + const openBtn = this.addOpenButton("Select", sel => { - this.addOpenButton("Select", sel => { + if (this._selectionCallback) { - if (this._selectionCallback) { + this._selectionCallback(sel); + } + }); - this._selectionCallback(sel); - } - })); + if (this._selectOnlyOne) { + + this.enableButtonOnlyWhenOneElementIsSelected(openBtn); + } this.addButton("Cancel", () => { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts index f99202a9a..d3e53a1fa 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts @@ -255,6 +255,10 @@ namespace phasereditor2d.scene { ui.sceneobjects.TilemapExtension.getInstance() ); + reg.addExtension( + new ui.codesnippets.CreateFromAsepriteCodeSnippetExtension() + ); + // align extensions reg.addExtension(...ui.editor.layout.DefaultLayoutExtensions.ALL); @@ -319,7 +323,8 @@ namespace phasereditor2d.scene { page => new ui.sceneobjects.TextureSection(page), page => new ui.sceneobjects.SpineSection(page), page => new ui.sceneobjects.SpineBoundsProviderSection(page), - page => new ui.sceneobjects.SpineAnimationSection(page) + page => new ui.sceneobjects.SpineAnimationSection(page), + page => new ui.codesnippets.CreateFromAsepriteCodeSnippetSection(page) )); // scene tools @@ -365,9 +370,9 @@ namespace phasereditor2d.scene { } private registerAnimationsPreviewDialogInAssetPack() { - + pack.ui.properties.AnimationsPreviewSection.openPreviewDialog = elem => { - + const dlg = new ui.sceneobjects.AnimationPreviewDialog(elem.getParent(), { key: elem.getKey() }); @@ -466,6 +471,17 @@ namespace phasereditor2d.scene { return !file.isFolder() && colibri.Platform.getWorkbench().getContentTypeRegistry().getCachedContentType(file) === core.CONTENT_TYPE_SCENE; } + getCodeSnippetExtensions() { + + return colibri.Platform + .getExtensions(ui.codesnippets.CodeSnippetExtension.POINT_ID); + } + + getCodeSnippetExtensionByType(type: string) { + + return this.getCodeSnippetExtensions().find(e => e.getType() === type); + } + getPlainObjectExtensions() { return colibri.Platform diff --git a/source/editor/plugins/phasereditor2d.scene/src/core/json/ISceneData.ts b/source/editor/plugins/phasereditor2d.scene/src/core/json/ISceneData.ts index 9e8316326..ff393756c 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/core/json/ISceneData.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/core/json/ISceneData.ts @@ -15,6 +15,7 @@ namespace phasereditor2d.scene.core.json { plainObjects?: IScenePlainObjectData[]; displayList: IObjectData[]; prefabProperties?: any[]; + codeSnippets?: ui.codesnippets.ICodeSnippetData[]; meta: { app: string, url: string, 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 14cd95380..6c4e87fc1 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneWriter.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneWriter.ts @@ -81,6 +81,15 @@ namespace phasereditor2d.scene.core.json { sceneData.prefabProperties = prefabProperties; } + // code snippets + + const codeSnippets = this._scene.getCodeSnippets(); + + if (codeSnippets.getSnippets().length > 0) { + + sceneData.codeSnippets = codeSnippets.toJSON(); + } + return sceneData; } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/Scene.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/Scene.ts index 46a65da88..7b58a8869 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/Scene.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/Scene.ts @@ -12,6 +12,7 @@ namespace phasereditor2d.scene.ui { private _prefabProperties: sceneobjects.PrefabUserProperties; private _objectLists: sceneobjects.ObjectLists; private _plainObjects: sceneobjects.IScenePlainObject[]; + private _codeSnippets: codesnippets.CodeSnippets; private _version: number; constructor(editor?: editor.SceneEditor) { @@ -27,6 +28,8 @@ namespace phasereditor2d.scene.ui { this._plainObjects = []; + this._codeSnippets = new codesnippets.CodeSnippets(); + this._prefabProperties = new sceneobjects.PrefabUserProperties(); this._version = Scene.CURRENT_VERSION; @@ -146,6 +149,16 @@ namespace phasereditor2d.scene.ui { this.children.addAt(obj, index, skipCallback); } + getCodeSnippets() { + + return this._codeSnippets; + } + + addCodeSnippet(codeSnippet: codesnippets.CodeSnippet) { + + this._codeSnippets.add(codeSnippet); + } + addPlainObject(obj: sceneobjects.IScenePlainObject) { this._plainObjects.push(obj); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/SceneMaker.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/SceneMaker.ts index 68e307866..59bbd3f2a 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/SceneMaker.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/SceneMaker.ts @@ -377,6 +377,11 @@ namespace phasereditor2d.scene.ui { this._editorScene.getPrefabUserProperties().readJSON(sceneData.prefabProperties); } + if (sceneData.codeSnippets) { + + this._editorScene.getCodeSnippets().readJSON(sceneData.codeSnippets); + } + this._editorScene.setSceneType(sceneData.sceneType || core.json.SceneType.SCENE); // removes this condition, it is used temporal for compatibility diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippet.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippet.ts new file mode 100644 index 000000000..c79734dc9 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippet.ts @@ -0,0 +1,44 @@ +namespace phasereditor2d.scene.ui.codesnippets { + + export interface ICodeSnippetData { + type: string; + id: string; + } + + export abstract class CodeSnippet { + + private _type: string; + private _id: string; + + constructor(type: string) { + + this._type = type; + this._id = Phaser.Utils.String.UUID(); + } + + getId() { + + return this._id; + } + + getType() { + + return this._type; + } + + abstract buildCodeDOM(): core.code.CodeDOM[]; + + abstract getDisplayName(): string; + + writeJSON(data: ICodeSnippetData): void { + + data.type = this._type; + data.id = this._id; + } + + readJSON(data: ICodeSnippetData) { + + this._id = data.id; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippetExtension.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippetExtension.ts new file mode 100644 index 000000000..b4bb8ef5c --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippetExtension.ts @@ -0,0 +1,32 @@ + +namespace phasereditor2d.scene.ui.codesnippets { + + export abstract class CodeSnippetExtension extends colibri.Extension { + + static POINT_ID = "phasereditor2d.scene.ui.codesnippets.CodeSnippetExtension"; + + private _name: string; + private _type: string; + + constructor(type: string, name: string) { + super(CodeSnippetExtension.POINT_ID); + + this._type = type; + this._name = name; + } + + getType() { + + return this._type; + } + + getName() { + + return this._name; + } + + abstract createAndConfigureCodeSnippets(): Promise; + + abstract createEmptyCodeSnippet(): CodeSnippet; + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippets.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippets.ts new file mode 100644 index 000000000..328728be1 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippets.ts @@ -0,0 +1,52 @@ +namespace phasereditor2d.scene.ui.codesnippets { + + export class CodeSnippets { + + private _list: CodeSnippet[] = []; + + add(snippet: CodeSnippet) { + + this._list.push(snippet); + } + + getSnippets() { + + return this._list; + } + + readJSON(codeSnippets: ICodeSnippetData[]) { + + this._list = []; + + for (const snippetData of codeSnippets) { + + const ext = ScenePlugin.getInstance().getCodeSnippetExtensionByType(snippetData.type); + + if (ext) { + + const snippet = ext.createEmptyCodeSnippet(); + + snippet.readJSON(snippetData); + + this.add(snippet); + } + } + } + + toJSON(): ICodeSnippetData[] { + + const result: ICodeSnippetData[] = []; + + for (const snippet of this._list) { + + const data: ICodeSnippetData = {} as any; + + snippet.writeJSON(data); + + result.push(data); + } + + return result; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/aseprite/CodeSnippetsSnapshotOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/aseprite/CodeSnippetsSnapshotOperation.ts new file mode 100644 index 000000000..7884487cb --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/aseprite/CodeSnippetsSnapshotOperation.ts @@ -0,0 +1,79 @@ +/// +namespace phasereditor2d.scene.ui.codesnippets { + + import json = core.json; + + export interface ISceneSnapshot { + + selection: string[]; + displayList: json.IObjectData[]; + lists: json.IObjectListData[]; + plainObjects: json.IScenePlainObjectData[], + prefabUserProperties: any[]; + } + + export class CodeSnippetsSnapshotOperation extends editor.undo.SceneEditorOperation { + + private _before: ICodeSnippetData[]; + private _after: ICodeSnippetData[]; + private _operation: () => Promise; + + constructor(editor: editor.SceneEditor, operation?: () => Promise) { + super(editor); + + this._operation = operation; + } + + async execute() { + + this._before = this.takeSnapshot(); + + await this.performModification(); + + this._after = this.takeSnapshot(); + + this._editor.setDirty(true); + this._editor.refreshOutline(); + this._editor.repaint(); + this._editor.dispatchSelectionChanged(); + } + + protected async performModification(): Promise { + + if (this._operation) { + + await this._operation(); + } + } + + private takeSnapshot(): ICodeSnippetData[] { + + const scene = this.getScene(); + + return scene.getCodeSnippets().toJSON(); + } + + protected loadSnapshot(snapshot: ICodeSnippetData[]) { + + const editor = this.getEditor(); + const scene = this.getScene(); + + scene.getCodeSnippets().readJSON(snapshot); + + editor.setDirty(true); + editor.repaint(); + editor.refreshOutline(); + editor.getSelectionManager().setSelectionByIds(snapshot.map(s => s.id)); + } + + undo(): void { + + this.loadSnapshot(this._before); + } + + redo(): void { + + this.loadSnapshot(this._after); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/aseprite/CreateFromAsepriteCodeSnippet.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/aseprite/CreateFromAsepriteCodeSnippet.ts new file mode 100644 index 000000000..a4a4019e1 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/aseprite/CreateFromAsepriteCodeSnippet.ts @@ -0,0 +1,43 @@ +namespace phasereditor2d.scene.ui.codesnippets { + + export interface ICreateFromAsepriteCodeSnippetData extends ICodeSnippetData { + key: string; + } + + export class CreateFromAsepriteCodeSnippet extends CodeSnippet { + + public assetKey: string; + + constructor() { + super(CreateFromAsepriteCodeSnippetExtension.TYPE); + } + + buildCodeDOM(): core.code.CodeDOM[] { + + const dom = new core.code.MethodCallCodeDOM("createFromAseprite", "this.anims"); + + dom.argLiteral(this.assetKey); + + return [dom]; + } + + getDisplayName(): string { + + return `${this.assetKey} - anims.createFromAseprite`; + } + + writeJSON(data: ICreateFromAsepriteCodeSnippetData): void { + + super.writeJSON(data); + + data.key = this.assetKey; + } + + readJSON(data: ICreateFromAsepriteCodeSnippetData): void { + + super.readJSON(data); + + this.assetKey = data.key; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/aseprite/CreateFromAsepriteCodeSnippetExtension.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/aseprite/CreateFromAsepriteCodeSnippetExtension.ts new file mode 100644 index 000000000..8a15eaaa0 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/aseprite/CreateFromAsepriteCodeSnippetExtension.ts @@ -0,0 +1,46 @@ +namespace phasereditor2d.scene.ui.codesnippets { + + export class CreateFromAsepriteCodeSnippetExtension extends CodeSnippetExtension { + + static TYPE = "animsCreateFromAseprite"; + + constructor() { + super(CreateFromAsepriteCodeSnippetExtension.TYPE, "Create Animations From Aseprite"); + } + + createEmptyCodeSnippet(): CodeSnippet { + + return new CreateFromAsepriteCodeSnippet(); + } + + async createAndConfigureCodeSnippets(): Promise { + + const finder = new pack.core.PackFinder(); + + await finder.preload(); + + const input = finder.getAssets() + .filter(i => i instanceof pack.core.AsepriteAssetPackItem); + + const dlg = new pack.ui.dialogs.AssetSelectionDialog("tree", false); + + dlg.create(); + + dlg.setTitle("Select Aseprite File Key"); + + dlg.getViewer().setInput(input); + + const result = await dlg.getResultPromise() as pack.core.AnimationConfigInPackItem[]; + + const snippets = (result || []).map(a => { + + const snippet = new CreateFromAsepriteCodeSnippet(); + snippet.assetKey = a.getKey(); + + return snippet; + }); + + return snippets; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/aseprite/CreateFromAsepriteCodeSnippetSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/aseprite/CreateFromAsepriteCodeSnippetSection.ts new file mode 100644 index 000000000..4bc439531 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/aseprite/CreateFromAsepriteCodeSnippetSection.ts @@ -0,0 +1,81 @@ +/// +namespace phasereditor2d.scene.ui.codesnippets { + + import controls = colibri.ui.controls; + + export class CreateFromAsepriteCodeSnippetSection extends ui.editor.properties.BaseSceneSection { + + constructor(page: controls.properties.PropertyPage) { + super(page, "phasereditor2d.scene.ui.codesnippets.CreateFromAsepriteCodeSnippetSection", "Create From Aseprite") + } + + createForm(parent: HTMLDivElement): void { + + const comp = this.createGridElement(parent, 3); + comp.style.gridTemplateColumns = "auto 1fr auto"; + + this.createLabel(comp, "Aseprite File Key", "The Aseprite animations file key."); + + { + const text = this.createText(comp, false); + + this.addUpdater(() => { + + text.value = this.getSelectionFirstElement().assetKey; + }); + } + + const btnUI = this.createButtonDialog({ + dialogTittle: "Select Animation File Key", + createDialogViewer: async (revealValue: string) => { + + const viewer = new controls.viewers.TreeViewer("phasereditor2d.scene.ui.sceneobjects.CreateFromAsepriteCodeSnippetSection." + this.getId()); + + viewer.setCellRendererProvider(new controls.viewers.EmptyCellRendererProvider(e => new pack.ui.viewers.AnimationsItemCellRenderer())); + viewer.setLabelProvider(new pack.ui.viewers.AssetPackLabelProvider()); + viewer.setTreeRenderer(new controls.viewers.TreeViewerRenderer(viewer)); + viewer.setContentProvider(new controls.viewers.ArrayTreeContentProvider()); + + const finder = new pack.core.PackFinder(); + await finder.preload(); + + const assetItems = finder + .getAssets(i => i instanceof pack.core.AsepriteAssetPackItem); + + viewer.setInput(assetItems); + + viewer.revealAndSelect(assetItems.find(a => a.getKey() === revealValue)); + + return viewer; + }, + getValue: () => { + + return this.getSelectionFirstElement().assetKey || ""; + }, + onValueSelected: (value: string) => { + + this.getEditor().getUndoManager().add(new CodeSnippetsSnapshotOperation(this.getEditor(), async () => { + + this.getSelectionFirstElement().assetKey = value; + })); + }, + dialogElementToString: (viewer: controls.viewers.TreeViewer, value: pack.core.AsepriteAssetPackItem): string => { + + return value.getKey(); + } + }); + + comp.appendChild(btnUI.buttonElement); + } + + canEdit(obj: any, n: number): boolean { + + return obj instanceof codesnippets.CreateFromAsepriteCodeSnippet; + } + + canEditNumber(n: number): boolean { + + return n === 1; + } + } +} \ No newline at end of file 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 ff4167fe9..ed2d013fe 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditorMenuCreator.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditorMenuCreator.ts @@ -23,6 +23,8 @@ namespace phasereditor2d.scene.ui.editor { menu.addMenu(this.createAddObjectMenu()) + menu.addMenu(this.createAddCodeSnippetMenu()) + menu.addSeparator(); menu.addMenu(this.createPrefabMenu()); @@ -52,7 +54,39 @@ namespace phasereditor2d.scene.ui.editor { menu.addMenu(this.createCompilerMenu()); } - createScriptingMenu(): controls.Menu { + private createAddCodeSnippetMenu(): controls.Menu { + + const menu = new controls.Menu("Code Snippets"); + + for (const ext of ScenePlugin.getInstance().getCodeSnippetExtensions()) { + + menu.addAction({ + text: "Add " + ext.getName(), + icon: resources.getIcon(resources.ICON_BUILD), + callback: async () => { + + const snippets = await ext.createAndConfigureCodeSnippets(); + + if (snippets) { + + this._editor.getUndoManager().add(new codesnippets.CodeSnippetsSnapshotOperation(this._editor, async () => { + + for (const snippet of snippets) { + + this._editor.getScene().addCodeSnippet(snippet); + } + + this._editor.setSelection(snippets); + })); + } + } + }); + } + + return menu; + } + + private createScriptingMenu(): controls.Menu { const menu = new controls.Menu("Scripting"); @@ -68,7 +102,7 @@ namespace phasereditor2d.scene.ui.editor { } createArcadePhysicsMenu(): controls.Menu { - + const menu = new controls.Menu("Arcade Physics"); menu.addCommand(editor.commands.CMD_ARCADE_ENABLE_BODY, { 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 af2280d1a..c8262e968 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SelectionManager.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SelectionManager.ts @@ -23,7 +23,7 @@ namespace phasereditor2d.scene.ui.editor { .map(obj => obj.getEditorSupport().getId())); list.push(...selectedPlainObjects - .map(obj => obj.getEditorSupport().getId())) + .map(obj => obj.getEditorSupport().getId())); list.push(...selection .filter(obj => obj instanceof sceneobjects.ObjectList) @@ -31,7 +31,7 @@ namespace phasereditor2d.scene.ui.editor { list.push(...selection .filter(obj => obj instanceof sceneobjects.ObjectListItem) - .map(obj => (obj as sceneobjects.ObjectListItem).getId())) + .map(obj => (obj as sceneobjects.ObjectListItem).getId())); list.push(...selection .filter(i => i instanceof sceneobjects.UserComponentNode) @@ -39,7 +39,11 @@ namespace phasereditor2d.scene.ui.editor { list.push(...selection .filter(obj => obj instanceof sceneobjects.UserProperty) - .map((p: sceneobjects.UserProperty) => `prefabProperty#${p.getName()}`)) + .map((p: sceneobjects.UserProperty) => `prefabProperty#${p.getName()}`)); + + list.push(...selection + .filter(obj => obj instanceof codesnippets.CodeSnippet) + .map((s: codesnippets.CodeSnippet) => s.getId())); return list; } @@ -70,17 +74,22 @@ namespace phasereditor2d.scene.ui.editor { map.set(list.getId(), list); - for(const item of list.getItems()) { + for (const item of list.getItems()) { map.set(item.getId(), item); } } - for(const prop of scene.getPrefabUserProperties().getProperties()) { + for (const prop of scene.getPrefabUserProperties().getProperties()) { map.set(`prefabProperty#${prop.getName()}`, prop); } + for (const snippet of scene.getCodeSnippets().getSnippets()) { + + map.set(snippet.getId(), snippet); + } + const sel = selectionIds .map(id => map.get(id)) .filter(obj => obj !== undefined); 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 a00a8a16b..50f59ebf9 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 @@ -44,6 +44,12 @@ namespace phasereditor2d.scene.ui.editor.outline { })); } + if (!scene.isPrefabSceneType() + && scene.getCodeSnippets().getSnippets().length > 0) { + + roots.push(scene.getCodeSnippets()); + } + if (scene.isPrefabSceneType()) { roots.push(scene.getPrefabUserProperties()); @@ -54,6 +60,11 @@ namespace phasereditor2d.scene.ui.editor.outline { getChildren(parent: any): any[] { + if (parent instanceof codesnippets.CodeSnippets) { + + return parent.getSnippets(); + } + if (parent instanceof sceneobjects.PrefabUserProperties) { return parent.getProperties(); 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 8577a69c8..7a284c860 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 @@ -35,6 +35,14 @@ namespace phasereditor2d.scene.ui.editor.outline { } else if (element instanceof sceneobjects.UserProperty) { return new controls.viewers.IconImageCellRenderer(resources.getIcon(resources.ICON_USER_PROPERTY)); + + } else if (element instanceof codesnippets.CodeSnippets) { + + return new controls.viewers.IconImageCellRenderer(colibri.ColibriPlugin.getInstance().getIcon(colibri.ICON_FOLDER)); + + } else if (element instanceof codesnippets.CodeSnippet) { + + return new controls.viewers.IconImageCellRenderer(resources.getIcon(resources.ICON_BUILD)); } const extensions = ScenePlugin.getInstance().getSceneEditorOutlineExtensions(); 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 6a4d4c346..b07872de4 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 @@ -52,6 +52,14 @@ namespace phasereditor2d.scene.ui.editor.outline { } else if (obj instanceof sceneobjects.UserProperty) { return obj.getLabel(); + + } else if (obj instanceof codesnippets.CodeSnippets) { + + return "Code Snippets"; + + } else if (obj instanceof codesnippets.CodeSnippet) { + + return obj.getDisplayName(); } const extensions = ScenePlugin.getInstance().getSceneEditorOutlineExtensions(); From 2685161f21bedaf564d812691ca12e812f62660f Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 17 Oct 2023 20:59:57 -0400 Subject: [PATCH 43/58] Allows deleting code snippets. --- .../src/ui/codesnippets/CodeSnippets.ts | 7 +++++++ .../phasereditor2d.scene/src/ui/editor/SceneEditor.ts | 10 ++++++++-- .../src/ui/editor/undo/DeleteOperation.ts | 4 ++++ .../src/ui/editor/undo/SceneSnapshotOperation.ts | 7 ++++++- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippets.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippets.ts index 328728be1..ebc5c29cf 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippets.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippets.ts @@ -9,6 +9,13 @@ namespace phasereditor2d.scene.ui.codesnippets { this._list.push(snippet); } + removeByIds(ids: string[]) { + + const removeSet = new Set(ids); + + this._list = this._list.filter(s => !removeSet.has(s.getId())); + } + getSnippets() { return this._list; 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 2fa2ce0f7..d50012887 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditor.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditor.ts @@ -590,6 +590,12 @@ namespace phasereditor2d.scene.ui.editor { return this.getSelection().filter(obj => sceneobjects.ScenePlainObjectEditorSupport.hasEditorSupport(obj)); } + getSelectedCodeSnippets(): codesnippets.CodeSnippet[] { + + return this.getSelection() + .filter(obj => obj instanceof codesnippets.CodeSnippet); + } + getSelectedUserComponentNodes(): sceneobjects.UserComponentNode[] { return this.getSelection().filter(obj => obj instanceof sceneobjects.UserComponentNode); @@ -715,7 +721,7 @@ namespace phasereditor2d.scene.ui.editor { this._scene.destroyGame(); - ScenePlugin.getInstance().getCanvasManager().releaseCanvas( this._game.canvas); + ScenePlugin.getInstance().getCanvasManager().releaseCanvas(this._game.canvas); } this._cellRendererCache.clear(); @@ -858,7 +864,7 @@ namespace phasereditor2d.scene.ui.editor { } getOutlineProvider() { - + return this._outlineProvider; } 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 8103fc6c0..a6c03739e 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 @@ -45,6 +45,10 @@ namespace phasereditor2d.scene.ui.editor.undo { scene.removePlainObjects(editor.getSelectedPlainObjects()); + const codeSnippetIds = editor.getSelectedCodeSnippets().map(s => s.getId()); + + editor.getScene().getCodeSnippets().removeByIds(codeSnippetIds); + const nodes = editor.getSelectedUserComponentNodes(); for (const node of nodes) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/SceneSnapshotOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/SceneSnapshotOperation.ts index 724a3db36..11192633f 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/SceneSnapshotOperation.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/SceneSnapshotOperation.ts @@ -9,6 +9,7 @@ namespace phasereditor2d.scene.ui.editor.undo { lists: json.IObjectListData[]; plainObjects: json.IScenePlainObjectData[], prefabUserProperties: any[]; + codeSnippets: codesnippets.ICodeSnippetData[]; } export class SceneSnapshotOperation extends SceneEditorOperation { @@ -81,7 +82,9 @@ namespace phasereditor2d.scene.ui.editor.undo { prefabUserProperties: scene.getPrefabUserProperties().toJSON(), - selection: this.getEditor().getSelectionManager().getSelectionIds() + codeSnippets: scene.getCodeSnippets().toJSON(), + + selection: this.getEditor().getSelectionManager().getSelectionIds(), }; } @@ -102,6 +105,8 @@ namespace phasereditor2d.scene.ui.editor.undo { scene.getObjectLists().readJSON_lists(snapshot.lists); + scene.getCodeSnippets().readJSON(snapshot.codeSnippets); + scene.getPrefabUserProperties().readJSON(snapshot.prefabUserProperties); editor.setDirty(true); From 6d8b2a38706b743b73f947722035c851f2e07754 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 17 Oct 2023 21:27:22 -0400 Subject: [PATCH 44/58] Implements the code snippets order commands. --- .../codesnippets/CodeSnippetOrderOperation.ts | 127 ++++++++++++++++++ .../CodeSnippetsSnapshotOperation.ts | 26 ++-- .../src/ui/editor/SelectionManager.ts | 8 +- .../ui/editor/commands/SceneEditorCommands.ts | 27 ++++ 4 files changed, 171 insertions(+), 17 deletions(-) create mode 100644 source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippetOrderOperation.ts rename source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/{aseprite => }/CodeSnippetsSnapshotOperation.ts (69%) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippetOrderOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippetOrderOperation.ts new file mode 100644 index 000000000..75af7fb3d --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippetOrderOperation.ts @@ -0,0 +1,127 @@ +/// +namespace phasereditor2d.scene.ui.codesnippets { + + import SceneEditor = editor.SceneEditor; + import DepthMove = editor.undo.DepthMove; + + export class CodeSnippetOrderOperation extends CodeSnippetsSnapshotOperation { + + private _depthMove: editor.undo.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; + } + + const siblings = editor.getScene().getCodeSnippets().getSnippets(); + + for (const obj of sel) { + + const index = siblings.indexOf(obj); + + let bottomIndex = 0; + const len = siblings.length; + + if (move === "Bottom" || move === "Down") { + + if (index === len - 1) { + + return false; + } + + } else { // Top || Up + + if (index === bottomIndex) { + + return false; + } + } + } + + return true; + } + + protected async performModification() { + + const editor = this.getEditor(); + + const sel = CodeSnippetOrderOperation.sortedSelection(editor); + + const siblings = editor.getScene().getCodeSnippets().getSnippets(); + + switch (this._depthMove) { + + case "Bottom": + + for (const obj of sel) { + + Phaser.Utils.Array.BringToTop(siblings, obj); + } + + break; + + case "Top": + + for (let i = 0; i < sel.length; i++) { + + const obj = sel[sel.length - i - 1]; + + Phaser.Utils.Array.SendToBack(siblings, obj); + } + + break; + + case "Down": + + for (let i = 0; i < sel.length; i++) { + + const obj = sel[sel.length - i - 1]; + + Phaser.Utils.Array.MoveUp(siblings, obj); + } + + break; + + case "Up": + + for (const obj of sel) { + + Phaser.Utils.Array.MoveDown(siblings, obj); + } + + break; + } + + this.getEditor().repaint(); + } + + private static sortedSelection(editor: SceneEditor) { + + const sel = editor.getSelectedCodeSnippets(); + + const siblings = editor.getScene().getCodeSnippets().getSnippets(); + + sel.sort((a, b) => { + + const aa = siblings.indexOf(a); + const bb = siblings.indexOf(b); + + return aa - bb; + }); + + return sel; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/aseprite/CodeSnippetsSnapshotOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippetsSnapshotOperation.ts similarity index 69% rename from source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/aseprite/CodeSnippetsSnapshotOperation.ts rename to source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippetsSnapshotOperation.ts index 7884487cb..445d90472 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/aseprite/CodeSnippetsSnapshotOperation.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippetsSnapshotOperation.ts @@ -1,21 +1,18 @@ -/// +/// namespace phasereditor2d.scene.ui.codesnippets { import json = core.json; - export interface ISceneSnapshot { + export interface ICodeSnippetSnapshot { selection: string[]; - displayList: json.IObjectData[]; - lists: json.IObjectListData[]; - plainObjects: json.IScenePlainObjectData[], - prefabUserProperties: any[]; + codeSnippets: ICodeSnippetData[]; } export class CodeSnippetsSnapshotOperation extends editor.undo.SceneEditorOperation { - private _before: ICodeSnippetData[]; - private _after: ICodeSnippetData[]; + private _before: ICodeSnippetSnapshot; + private _after: ICodeSnippetSnapshot; private _operation: () => Promise; constructor(editor: editor.SceneEditor, operation?: () => Promise) { @@ -46,24 +43,27 @@ namespace phasereditor2d.scene.ui.codesnippets { } } - private takeSnapshot(): ICodeSnippetData[] { + private takeSnapshot(): ICodeSnippetSnapshot { const scene = this.getScene(); - return scene.getCodeSnippets().toJSON(); + return { + selection: this.getEditor().getSelectedCodeSnippets().map(s => s.getId()), + codeSnippets: scene.getCodeSnippets().toJSON() + }; } - protected loadSnapshot(snapshot: ICodeSnippetData[]) { + protected loadSnapshot(snapshot: ICodeSnippetSnapshot) { const editor = this.getEditor(); const scene = this.getScene(); - scene.getCodeSnippets().readJSON(snapshot); + scene.getCodeSnippets().readJSON(snapshot.codeSnippets); editor.setDirty(true); editor.repaint(); editor.refreshOutline(); - editor.getSelectionManager().setSelectionByIds(snapshot.map(s => s.id)); + editor.getSelectionManager().setSelectionByIds(snapshot.selection); } undo(): void { 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 c8262e968..b6838e4da 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SelectionManager.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SelectionManager.ts @@ -18,6 +18,7 @@ namespace phasereditor2d.scene.ui.editor { const selection = this._editor.getSelection(); const selectedObjects = this._editor.getSelectedGameObjects(); const selectedPlainObjects = this._editor.getSelectedPlainObjects(); + const selectedCodeSnippets = this._editor.getSelectedCodeSnippets(); list.push(...selectedObjects .map(obj => obj.getEditorSupport().getId())); @@ -25,6 +26,9 @@ namespace phasereditor2d.scene.ui.editor { list.push(...selectedPlainObjects .map(obj => obj.getEditorSupport().getId())); + list.push(...selectedCodeSnippets + .map((s: codesnippets.CodeSnippet) => s.getId())); + list.push(...selection .filter(obj => obj instanceof sceneobjects.ObjectList) .map(obj => (obj as sceneobjects.ObjectList).getId())); @@ -41,10 +45,6 @@ namespace phasereditor2d.scene.ui.editor { .filter(obj => obj instanceof sceneobjects.UserProperty) .map((p: sceneobjects.UserProperty) => `prefabProperty#${p.getName()}`)); - list.push(...selection - .filter(obj => obj instanceof codesnippets.CodeSnippet) - .map((s: codesnippets.CodeSnippet) => s.getId())); - return list; } 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 c519f7791..255af7b4e 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 @@ -196,6 +196,33 @@ namespace phasereditor2d.scene.ui.editor.commands { this.registerPropertiesCommands(manager); this.registerSpineCommands(manager); + + this.registerCodeSnippetOrderCommands(manager); + } + + static registerCodeSnippetOrderCommands(manager: colibri.ui.ide.commands.CommandManager) { + + const moves: [undo.DepthMove, string][] = [ + ["Up", CMD_SORT_OBJ_UP], + ["Down", CMD_SORT_OBJ_DOWN], + ["Top", CMD_SORT_OBJ_TOP], + ["Bottom", CMD_SORT_OBJ_BOTTOM] + ]; + + for (const tuple of moves) { + + const move = tuple[0]; + const cmd = tuple[1]; + + manager.addHandlerHelper(cmd, + // testFunc + args => isSceneScope(args) && args.activeEditor.getSelection().length > 0 + && codesnippets.CodeSnippetOrderOperation.allow(args.activeEditor as any, move), + // execFunc + args => args.activeEditor.getUndoManager().add( + new codesnippets.CodeSnippetOrderOperation(args.activeEditor as editor.SceneEditor, move) + )); + } } static registerPlainObjectOrderCommands(manager: colibri.ui.ide.commands.CommandManager) { From c3b0541355cd189652ec41141fef65fce3249867 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 17 Oct 2023 21:41:12 -0400 Subject: [PATCH 45/58] Completes anims create from aseprite code snippets. --- .../src/core/code/SceneCodeDOMBuilder.ts | 23 +++++++++++++++++++ .../ui/codesnippets/CodeSnippetExtension.ts | 2 ++ .../CreateFromAsepriteCodeSnippetExtension.ts | 5 ++++ .../src/ui/editor/SceneEditorMenuCreator.ts | 1 + 4 files changed, 31 insertions(+) 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 b7b490292..82b274716 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts @@ -382,6 +382,8 @@ namespace phasereditor2d.scene.core.code { varname: "this" }); + this.buildCodeSnippets(result.statements); + lazyStatements.push(...result.lazyStatements); body.push(...result.statements); @@ -420,6 +422,24 @@ namespace phasereditor2d.scene.core.code { return ctrDecl; } + buildCodeSnippets(statements: CodeDOM[]) { + + const snippets = this._scene.getCodeSnippets().getSnippets(); + + if (snippets.length > 0) { + + statements.push(new code.RawCodeDOM("")); + statements.push(new code.RawCodeDOM("// snippets")); + + for (const codeSnippet of snippets) { + + const code = codeSnippet.buildCodeDOM(); + + statements.push(...code); + } + } + } + private buildPrefabTypeScriptDefinitionsCodeDOM(prefabObj: ISceneGameObject, objBuilder: ui.sceneobjects.GameObjectCodeDOMBuilder) { for (const comp of prefabObj.getEditorSupport().getActiveComponents()) { @@ -479,8 +499,11 @@ namespace phasereditor2d.scene.core.code { } const body = createMethodDecl.getBody(); + const lazyStatements: CodeDOM[] = []; + this.buildCodeSnippets(body); + this.addCreateAllPlainObjectCode(body, lazyStatements); for (const obj of this._scene.getGameObjects()) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippetExtension.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippetExtension.ts index b4bb8ef5c..81751cf4e 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippetExtension.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/CodeSnippetExtension.ts @@ -25,6 +25,8 @@ namespace phasereditor2d.scene.ui.codesnippets { return this._name; } + abstract isEnabledFor(_editor: editor.SceneEditor): boolean; + abstract createAndConfigureCodeSnippets(): Promise; abstract createEmptyCodeSnippet(): CodeSnippet; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/aseprite/CreateFromAsepriteCodeSnippetExtension.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/aseprite/CreateFromAsepriteCodeSnippetExtension.ts index 8a15eaaa0..69ada74f2 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/aseprite/CreateFromAsepriteCodeSnippetExtension.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/codesnippets/aseprite/CreateFromAsepriteCodeSnippetExtension.ts @@ -8,6 +8,11 @@ namespace phasereditor2d.scene.ui.codesnippets { super(CreateFromAsepriteCodeSnippetExtension.TYPE, "Create Animations From Aseprite"); } + isEnabledFor(_editor: editor.SceneEditor): boolean { + + return !_editor.getScene().isPrefabSceneType(); + } + createEmptyCodeSnippet(): CodeSnippet { return new CreateFromAsepriteCodeSnippet(); 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 ed2d013fe..192d007f3 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditorMenuCreator.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditorMenuCreator.ts @@ -63,6 +63,7 @@ namespace phasereditor2d.scene.ui.editor { menu.addAction({ text: "Add " + ext.getName(), icon: resources.getIcon(resources.ICON_BUILD), + enabled: ext.isEnabledFor(this._editor), callback: async () => { const snippets = await ext.createAndConfigureCodeSnippets(); From 04981d9a59d59f39620e1f41dea4e728980dee73 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Sat, 21 Oct 2023 04:14:49 -0400 Subject: [PATCH 46/58] New scene version (5). --- CHANGELOG.MD | 2 ++ source/editor/plugins/phasereditor2d.scene/src/ui/Scene.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index b35d955ff..1580227d2 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -12,6 +12,8 @@ * Adds new Animation section to Sprite game objects. It is about to auto-start a sprite animation. * Adds `showBeforeDelay` to the Animations Editor. * Shows Sprite Animation blocks in the Scene Editor's Blocks view. +* Aseprite animations support. +* New Code Snippets scene elements. * Optimizes pooling of WEBGL contexts. ## v3.63.0 - Sep 30, 2023 diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/Scene.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/Scene.ts index 7b58a8869..6c085c498 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 = 4; + static CURRENT_VERSION = 5; private _id: string; private _sceneType: core.json.SceneType; From d2572c92ef55f0f96b91f9bbb7b18d9de23d4839 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Sat, 21 Oct 2023 04:16:52 -0400 Subject: [PATCH 47/58] Updates changelog. --- CHANGELOG.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 1580227d2..bd72f333d 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -1,6 +1,6 @@ # Change Log -## dev +## v3.64.0 * Fixes making thumbnails of single-layer prefabs. * Allows merging Import and Open buttons in the Asset Pack Entry section. From 5b85dcd617877cc71802c0f6633a960d8e194b2b Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Sat, 21 Oct 2023 04:23:21 -0400 Subject: [PATCH 48/58] Update version. --- source/editor/product.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/editor/product.json b/source/editor/product.json index f92235f0f..ef6973ed9 100644 --- a/source/editor/product.json +++ b/source/editor/product.json @@ -1,4 +1,4 @@ { "title": "Phaser Editor 2D", - "version": "3.63.0" + "version": "3.64.0" } \ No newline at end of file From c7b0bfbfec4a7100a7eac80e4c38ecfc6e2b999d Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 26 Oct 2023 22:19:44 -0400 Subject: [PATCH 49/58] Reworks the buttons for previewing the sprite animations. --- .../sprite/SpriteAnimationSection.ts | 185 ++++++++++-------- .../ui/sceneobjects/sprite/SpriteComponent.ts | 2 +- 2 files changed, 100 insertions(+), 87 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts index ac46af49f..4c3864461 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts @@ -13,6 +13,30 @@ namespace phasereditor2d.scene.ui.sceneobjects { return "TODO"; } + createMenu(menu: controls.Menu): void { + + menu.addAction({ + text: "Open Animation File", + callback: async () => { + + const sprite = this.getSelectionFirstElement(); + + const finder = new pack.core.PackFinder(); + + await finder.preload(); + + const anim = finder.findAnimationByKey(sprite.animationKey); + + if (anim) { + + ScenePlugin.getInstance().openAnimationInEditor(anim); + } + } + }); + + super.createMenu(menu); + } + createForm(parent: HTMLDivElement) { const comp = this.createGridElement(parent, 4); @@ -23,133 +47,122 @@ namespace phasereditor2d.scene.ui.sceneobjects { btn.style.gridColumn = "3 / span 2"; } - { - // play animation + const animationToolbar = document.createElement("div"); + animationToolbar.style.display = "flex"; + animationToolbar.style.gap = "5px"; - this.createPropertyStringRow(comp, SpriteComponent.animationKey); + // play animation - const btnUI = this.createButtonDialog({ - dialogTittle: "Select Animation Key", - createDialogViewer: async (revealValue: string) => { + this.createPropertyStringRow(comp, SpriteComponent.animationKey); - const viewer = new controls.viewers.TreeViewer("phasereditor2d.scene.ui.sceneobjects.AnimationSection." + this.getId()); + comp.appendChild(animationToolbar); - viewer.setCellRendererProvider(new controls.viewers.EmptyCellRendererProvider(e => new pack.ui.viewers.AnimationConfigCellRenderer())); - viewer.setLabelProvider(new pack.ui.viewers.AssetPackLabelProvider()); - viewer.setTreeRenderer(new controls.viewers.TreeViewerRenderer(viewer)); - viewer.setContentProvider(new controls.viewers.ArrayTreeContentProvider()); + this.createAnimationKeyDialogButton(comp, animationToolbar); - const finder = new pack.core.PackFinder(); - await finder.preload(); + this.createPreviewDialogButton(animationToolbar); - const animations = finder - .getAssets(i => i instanceof pack.core.BaseAnimationsAssetPackItem) - .flatMap((i: pack.core.BaseAnimationsAssetPackItem) => i.getAnimations()); - - viewer.setInput(animations); - - viewer.revealAndSelect(animations.find(a => a.getKey() === revealValue)); - - return viewer; - }, - getValue: () => { + // enable config + this.createPropertyBoolean(comp, SpriteComponent.animationCustomConfig); + } - return this.getSelection()[0].animationKey || ""; - }, - onValueSelected: (value: string) => { + private createPreviewDialogButton(animationToolbar: HTMLDivElement) { - this.getEditor().getUndoManager().add( - new SimpleOperation(this.getEditor(), this.getSelection(), SpriteComponent.animationKey, value)); - }, - dialogElementToString: (viewer: controls.viewers.TreeViewer, value: pack.core.AnimationConfigInPackItem): string => { + const btn = this.createButton(animationToolbar, resources.getIcon(resources.ICON_PLAY), async () => { - return value.getKey(); - }, - updateIconCallback: async (iconControl, value) => { + const sprite = this.getSelectionFirstElement(); - const finder = new pack.core.PackFinder(); + const finder = new pack.core.PackFinder(); - await finder.preload(); + await finder.preload(); - const image = AnimationKeyPropertyType.getAnimationIcon(finder, value); + const anim = finder.findAnimationByKey(sprite.animationKey); - iconControl.setIcon(image); - }, - }); + if (anim) { - comp.appendChild(btnUI.buttonElement); + const config: Phaser.Types.Animations.PlayAnimationConfig = { + key: anim.getKey() + }; - this.addUpdater(() => { + if (sprite.animationCustomConfig) { - btnUI.buttonElement.disabled = this.getSelection() - .filter(sprite => !sprite.getEditorSupport() - .isUnlockedProperty(SpriteComponent.animationKey)) - .length > 0; + SpriteComponent.buildPlayConfig(sprite, config); + } - btnUI.updateDialogButtonIcon(); - }); - } - { - // enable config + const dlg = new AnimationPreviewDialog(anim.getParent(), config); - this.createPropertyBoolean(comp, SpriteComponent.animationCustomConfig); - } - { - // preview animations editor - const btn = this.createButton(comp, "Preview Animation", async () => { + dlg.create(); - const sprite = this.getSelectionFirstElement(); + } else { - const finder = new pack.core.PackFinder(); + alert("Animation not found."); + } + }); - await finder.preload(); + btn.style.gridColumn = "1 / span 4"; + } - const anim = finder.findAnimationByKey(sprite.animationKey); + private createAnimationKeyDialogButton(comp: HTMLDivElement, animationToolbar: HTMLDivElement) { - if (anim) { + const btnUI = this.createButtonDialog({ + dialogTittle: "Select Animation Key", + createDialogViewer: async (revealValue: string) => { - const config: Phaser.Types.Animations.PlayAnimationConfig = { - key: anim.getKey() - } + const viewer = new controls.viewers.TreeViewer("phasereditor2d.scene.ui.sceneobjects.AnimationSection." + this.getId()); - if (sprite.animationCustomConfig) { + viewer.setCellRendererProvider(new controls.viewers.EmptyCellRendererProvider(e => new pack.ui.viewers.AnimationConfigCellRenderer())); + viewer.setLabelProvider(new pack.ui.viewers.AssetPackLabelProvider()); + viewer.setTreeRenderer(new controls.viewers.TreeViewerRenderer(viewer)); + viewer.setContentProvider(new controls.viewers.ArrayTreeContentProvider()); - SpriteComponent.buildPlayConfig(sprite, config); - } + const finder = new pack.core.PackFinder(); + await finder.preload(); - const dlg = new AnimationPreviewDialog(anim.getParent(), config); + const animations = finder + .getAssets(i => i instanceof pack.core.BaseAnimationsAssetPackItem) + .flatMap((i: pack.core.BaseAnimationsAssetPackItem) => i.getAnimations()); - dlg.create(); + viewer.setInput(animations); - } else { + viewer.revealAndSelect(animations.find(a => a.getKey() === revealValue)); - alert("Animation not found."); - } - }); + return viewer; + }, + getValue: () => { - btn.style.gridColumn = "1 / span 4"; - } + return this.getSelection()[0].animationKey || ""; + }, + onValueSelected: (value: string) => { - { - // open animations editor - const btn = this.createButton(comp, "Open Animation File", async () => { + this.getEditor().getUndoManager().add( + new SimpleOperation(this.getEditor(), this.getSelection(), SpriteComponent.animationKey, value)); + }, + dialogElementToString: (viewer: controls.viewers.TreeViewer, value: pack.core.AnimationConfigInPackItem): string => { - const sprite = this.getSelectionFirstElement(); + return value.getKey(); + }, + updateIconCallback: async (iconControl, value) => { const finder = new pack.core.PackFinder(); await finder.preload(); - const anim = finder.findAnimationByKey(sprite.animationKey); + const image = AnimationKeyPropertyType.getAnimationIcon(finder, value); - if (anim) { + iconControl.setIcon(image); + }, + }); - ScenePlugin.getInstance().openAnimationInEditor(anim); - } - }); + animationToolbar.appendChild(btnUI.buttonElement); - btn.style.gridColumn = "1 / span 4"; - } + this.addUpdater(() => { + + btnUI.buttonElement.disabled = this.getSelection() + .filter(sprite => !sprite.getEditorSupport() + .isUnlockedProperty(SpriteComponent.animationKey)) + .length > 0; + + btnUI.updateDialogButtonIcon(); + }); } canEdit(obj: any, n: number): boolean { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteComponent.ts index 525678f8d..2a6bf8df1 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteComponent.ts @@ -25,7 +25,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { }; static animationKey = SimpleProperty({ name: "animationKey", codeName: "key" }, "", "Animation Key", "The animation key to auto-play."); - static animationCustomConfig = SimpleProperty("animationCustomConfig", false, "Animation Config", "Set a new configuration?"); + static animationCustomConfig = SimpleProperty("animationCustomConfig", false, "Custom Config", "Set a new configuration?"); static animationFrameRate = SimpleProperty({ name: "animationFrameRate", codeName: "frameRate" }, 24, "Frame Rate", "phaser:Phaser.Types.Animations.PlayAnimationConfig.frameRate"); static animationDelay = SimpleProperty({ name: "animationDelay", codeName: "delay" }, 0, "Delay", "phaser:Phaser.Types.Animations.PlayAnimationConfig.delay"); static animationRepeat = SimpleProperty({ name: "animationRepeat", codeName: "repeat" }, 0, "Repeat", "phaser:Phaser.Types.Animations.PlayAnimationConfig.repeat"); From 3697dbb1f726923ab72cb56ccef2d308befbf0b9 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 26 Oct 2023 22:22:53 -0400 Subject: [PATCH 50/58] Fixes Phaser canvas context initialization. --- .../src/ui/editors/AnimationsEditor.ts | 2 +- .../src/ui/sceneobjects/spine/SpinePreviewManager.ts | 2 +- .../src/ui/sceneobjects/sprite/AnimationPreviewManager.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/AnimationsEditor.ts b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/AnimationsEditor.ts index e87201734..5bfd3ad96 100644 --- a/source/editor/plugins/phasereditor2d.animations/src/ui/editors/AnimationsEditor.ts +++ b/source/editor/plugins/phasereditor2d.animations/src/ui/editors/AnimationsEditor.ts @@ -474,7 +474,7 @@ namespace phasereditor2d.animations.ui.editors { this._scene = new AnimationsScene(this); this._game = new Phaser.Game({ - type: Phaser.CANVAS, + type: scene.ScenePlugin.DEFAULT_EDITOR_CANVAS_CONTEXT, canvas: this._gameCanvas, scale: { mode: Phaser.Scale.NONE diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/spine/SpinePreviewManager.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/spine/SpinePreviewManager.ts index f24936264..3619a0b3e 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/spine/SpinePreviewManager.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/spine/SpinePreviewManager.ts @@ -59,7 +59,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { canvas.style.visibility = "hidden"; this._game = new Phaser.Game({ - type: ScenePlugin.DEFAULT_CANVAS_CONTEXT, + type: ScenePlugin.DEFAULT_EDITOR_CANVAS_CONTEXT, width, height, canvas, parent: this._parent, diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts index 7ef079e8e..ffee35296 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/AnimationPreviewManager.ts @@ -35,7 +35,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { width, height: height - 5, parent: this._parent, canvas, - type: ScenePlugin.DEFAULT_CANVAS_CONTEXT, + type: ScenePlugin.DEFAULT_EDITOR_CANVAS_CONTEXT, transparent: true, pixelArt: true, fps: { From 2c1ebcac71d1be0424154dd2c23c96c43d25cd4d Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 26 Oct 2023 23:21:23 -0400 Subject: [PATCH 51/58] Shows a preview button in the Animation Key user property. --- .../AbstractDialogPropertyType.ts | 37 ++++++++---- .../AnimationKeyPropertyType.ts | 59 ++++++++++++++++++- 2 files changed, 85 insertions(+), 11 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AbstractDialogPropertyType.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AbstractDialogPropertyType.ts index e64147273..7406a1ef7 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AbstractDialogPropertyType.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AbstractDialogPropertyType.ts @@ -35,7 +35,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { } createInspectorPropertyEditor( - section: SceneGameObjectSection, parent: HTMLElement, userProp: UserProperty, lockIcon: boolean): void { + section: SceneGameObjectSection, parent: HTMLElement, userProp: UserProperty, lockIcon: boolean, previewAction?: () => void): void { const prop = userProp.getComponentProperty(); @@ -47,7 +47,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { const label = section.createLabel(parent, prop.label, PhaserHelp(prop.tooltip)); label.style.gridColumn = "2"; - const comp = this.createEditorComp(); + const comp = this.createEditorComp(Boolean(previewAction)); parent.appendChild(comp); const text = section.createStringField(comp, prop); @@ -72,13 +72,21 @@ namespace phasereditor2d.scene.ui.sceneobjects { }); comp.appendChild(buttonElement); + + if (previewAction) { + + section.createButton(comp, resources.getIcon(resources.ICON_PLAY), () => { + + previewAction(); + }); + } } - private createEditorComp() { + private createEditorComp(withPreviewButton = false) { const comp = document.createElement("div"); comp.style.display = "grid"; - comp.style.gridTemplateColumns = "1fr auto"; + comp.style.gridTemplateColumns = withPreviewButton ? "1fr auto auto" : "1fr auto"; comp.style.gap = "5px"; comp.style.alignItems = "center"; @@ -188,14 +196,13 @@ namespace phasereditor2d.scene.ui.sceneobjects { } } - createEditorElement(getValue: () => any, setValue: (value: any) => void): IPropertyEditor { + createEditorElement(getValue: () => any, setValue: (value: any) => void, previewAction?: () => void): IPropertyEditor { + + const comp = this.createEditorComp(Boolean(previewAction)); - const comp = this.createEditorComp(); + const formBuilder = new controls.properties.FormBuilder(); - const inputElement = document.createElement("input"); - comp.appendChild(inputElement); - inputElement.type = "text"; - inputElement.classList.add("formText"); + const inputElement = formBuilder.createText(comp, false); inputElement.addEventListener("change", e => { @@ -215,6 +222,16 @@ namespace phasereditor2d.scene.ui.sceneobjects { this.updateIcon(iconControl, value); }; + if (previewAction) { + + formBuilder.createButton(comp, resources.getIcon(resources.ICON_PLAY), () => { + + console.log("here"); + + previewAction(); + }); + } + return { element: comp, update diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AnimationKeyPropertyType.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AnimationKeyPropertyType.ts index c7446ae93..b142f976f 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AnimationKeyPropertyType.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AnimationKeyPropertyType.ts @@ -14,7 +14,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { }); } - protected getIcon(finder: pack.core.PackFinder, value: string): controls.IImage { return AnimationKeyPropertyType.getAnimationIcon(finder, value); @@ -48,6 +47,64 @@ namespace phasereditor2d.scene.ui.sceneobjects { return viewer; } + + createInspectorPropertyEditor(section: SceneGameObjectSection, parent: HTMLElement, userProp: UserProperty, lockIcon: boolean): void { + + super.createInspectorPropertyEditor(section, parent, userProp, lockIcon, async () => { + + const finder = new pack.core.PackFinder(); + + await finder.preload(); + + const values = section.getSelection() + .map(o => userProp.getComponentProperty().getValue(o)); + + const key = section.flatValues_StringOneOrNothing( + values); + + const anim = finder.findAnimationByKey(key); + + if (anim) { + + const dlg = new AnimationPreviewDialog(anim.getParent(), { + key + }); + + dlg.create(); + + } else { + + alert("Animation key not found."); + } + }); + } + + createEditorElement(getValue: () => any, setValue: (value: any) => void): IPropertyEditor { + + return super.createEditorElement(getValue, setValue, async () => { + + const finder = new pack.core.PackFinder(); + + await finder.preload(); + + const key = getValue() as string; + + const anim = finder.findAnimationByKey(key); + + if (anim) { + + const dlg = new AnimationPreviewDialog(anim.getParent(), { + key + }); + + dlg.create(); + + } else { + + alert("Animation key not found."); + } + }); + } } class AnimationKeyContentProvider implements controls.viewers.ITreeContentProvider { From 034689d01162420b45777603c8e3c0ede137de0b Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 26 Oct 2023 23:21:56 -0400 Subject: [PATCH 52/58] Updates changelog. --- CHANGELOG.MD | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index bd72f333d..c6bc0e274 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -9,6 +9,7 @@ * Improves animations preview UI: - New Animation preview section. - New Animations Preview section. + - Shows a preview button in the Animation Key user property. * Adds new Animation section to Sprite game objects. It is about to auto-start a sprite animation. * Adds `showBeforeDelay` to the Animations Editor. * Shows Sprite Animation blocks in the Scene Editor's Blocks view. From 28955b75cc119f22e655e4998ec1e97ddd763aac Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 2 Nov 2023 13:12:52 -0400 Subject: [PATCH 53/58] #295 Fixes tilemapLayer nullable method factory. --- .../core/code/JavaScriptUnitCodeGenerator.ts | 18 ++++++++++++++++-- .../src/core/code/MethodCallCodeDOM.ts | 12 ++++++++++++ .../tilemap/TilemapLayerCodeDOMBuilder.ts | 1 + .../tilemap/TilemapLayerEditorSupport.ts | 1 + 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/core/code/JavaScriptUnitCodeGenerator.ts b/source/editor/plugins/phasereditor2d.scene/src/core/code/JavaScriptUnitCodeGenerator.ts index 8e7fe1f90..05360541e 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/core/code/JavaScriptUnitCodeGenerator.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/core/code/JavaScriptUnitCodeGenerator.ts @@ -418,11 +418,25 @@ namespace phasereditor2d.scene.core.code { this.join(args); if (this.isTypeScript() - && call.getExplicitType() + && (call.getExplicitType() || call.isDisableReturnTypeNullable()) && call.isDeclareReturnToVar() && call.getReturnToVar()) { - this.line(`) as ${call.getExplicitType()};`); + let line = ")"; + + if (call.isDisableReturnTypeNullable()) { + + line += "!"; + } + + if (call.getExplicitType()) { + + line += ` as ${call.getExplicitType()}`; + } + + line += ";"; + + this.line(line); } else { diff --git a/source/editor/plugins/phasereditor2d.scene/src/core/code/MethodCallCodeDOM.ts b/source/editor/plugins/phasereditor2d.scene/src/core/code/MethodCallCodeDOM.ts index 0effa31fe..2f630a24a 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/core/code/MethodCallCodeDOM.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/core/code/MethodCallCodeDOM.ts @@ -10,6 +10,7 @@ namespace phasereditor2d.scene.core.code { private _isConstructor: boolean; private _explicitType: string; private _optionalContext: boolean; + private _disableReturnTypeNullable: boolean; constructor(methodName: string, contextExpr = "") { super(); @@ -19,6 +20,17 @@ namespace phasereditor2d.scene.core.code { this._args = []; this._declareReturnToVar = false; this._isConstructor = false; + this._disableReturnTypeNullable = false; + } + + setDisableReturnTypeNullable(disableReturnTypeNullable: boolean) { + + this._disableReturnTypeNullable = disableReturnTypeNullable; + } + + isDisableReturnTypeNullable() { + + return this._disableReturnTypeNullable; } setOptionalContext(optionalContext: boolean) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/tilemap/TilemapLayerCodeDOMBuilder.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/tilemap/TilemapLayerCodeDOMBuilder.ts index 0318d5d3e..a5718cc7b 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/tilemap/TilemapLayerCodeDOMBuilder.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/tilemap/TilemapLayerCodeDOMBuilder.ts @@ -29,6 +29,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { call.arg(tilesetArray); call.argInt(tilemapLayer.x); call.argInt(tilemapLayer.y); + call.setDisableReturnTypeNullable(true); return call; } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/tilemap/TilemapLayerEditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/tilemap/TilemapLayerEditorSupport.ts index 54c12a4fa..e8d5522f4 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/tilemap/TilemapLayerEditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/tilemap/TilemapLayerEditorSupport.ts @@ -16,6 +16,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { new TransformComponent(obj as unknown as ITransformLikeObject), new VisibleComponent(obj as unknown as IVisibleLikeObject), + new TilemapLayerComponent(obj) ); this.setLabel(obj.layer.name); From 1d683bc74bd3a9e2dee6c965641b885a87e39b66 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 2 Nov 2023 13:13:29 -0400 Subject: [PATCH 54/58] Updates changelog. --- CHANGELOG.MD | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index c6bc0e274..d8e1ea7aa 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -16,6 +16,7 @@ * Aseprite animations support. * New Code Snippets scene elements. * Optimizes pooling of WEBGL contexts. +* [#295](https://github.com/PhaserEditor2D/PhaserEditor2D-v3/issues/295) Fixes tilemapLayer nullable method factory. ## v3.63.0 - Sep 30, 2023 From f1e2ba953bc56ef5ea8b7b6dde2bb9ad83e30052 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 2 Nov 2023 13:20:07 -0400 Subject: [PATCH 55/58] Renames. --- .../src/core/code/JavaScriptUnitCodeGenerator.ts | 4 ++-- .../src/core/code/MethodCallCodeDOM.ts | 12 ++++++------ .../tilemap/TilemapLayerCodeDOMBuilder.ts | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/core/code/JavaScriptUnitCodeGenerator.ts b/source/editor/plugins/phasereditor2d.scene/src/core/code/JavaScriptUnitCodeGenerator.ts index 05360541e..9fa992bef 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/core/code/JavaScriptUnitCodeGenerator.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/core/code/JavaScriptUnitCodeGenerator.ts @@ -418,13 +418,13 @@ namespace phasereditor2d.scene.core.code { this.join(args); if (this.isTypeScript() - && (call.getExplicitType() || call.isDisableReturnTypeNullable()) + && (call.getExplicitType() || call.isNonNullAssertion()) && call.isDeclareReturnToVar() && call.getReturnToVar()) { let line = ")"; - if (call.isDisableReturnTypeNullable()) { + if (call.isNonNullAssertion()) { line += "!"; } diff --git a/source/editor/plugins/phasereditor2d.scene/src/core/code/MethodCallCodeDOM.ts b/source/editor/plugins/phasereditor2d.scene/src/core/code/MethodCallCodeDOM.ts index 2f630a24a..58b81fa2a 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/core/code/MethodCallCodeDOM.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/core/code/MethodCallCodeDOM.ts @@ -10,7 +10,7 @@ namespace phasereditor2d.scene.core.code { private _isConstructor: boolean; private _explicitType: string; private _optionalContext: boolean; - private _disableReturnTypeNullable: boolean; + private _nonNullAssertion: boolean; constructor(methodName: string, contextExpr = "") { super(); @@ -20,17 +20,17 @@ namespace phasereditor2d.scene.core.code { this._args = []; this._declareReturnToVar = false; this._isConstructor = false; - this._disableReturnTypeNullable = false; + this._nonNullAssertion = false; } - setDisableReturnTypeNullable(disableReturnTypeNullable: boolean) { + setNonNullAssertion(nonNullAssertion: boolean) { - this._disableReturnTypeNullable = disableReturnTypeNullable; + this._nonNullAssertion = nonNullAssertion; } - isDisableReturnTypeNullable() { + isNonNullAssertion() { - return this._disableReturnTypeNullable; + return this._nonNullAssertion; } setOptionalContext(optionalContext: boolean) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/tilemap/TilemapLayerCodeDOMBuilder.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/tilemap/TilemapLayerCodeDOMBuilder.ts index a5718cc7b..634fbde86 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/tilemap/TilemapLayerCodeDOMBuilder.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/tilemap/TilemapLayerCodeDOMBuilder.ts @@ -29,7 +29,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { call.arg(tilesetArray); call.argInt(tilemapLayer.x); call.argInt(tilemapLayer.y); - call.setDisableReturnTypeNullable(true); + call.setNonNullAssertion(true); return call; } From 192c67c0656df2ab4a2d6ee86b5310ccddb73919 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Thu, 2 Nov 2023 14:48:10 -0400 Subject: [PATCH 56/58] Adds help links to the Animation sections. --- .../src/ui/sceneobjects/sprite/SpriteAnimationConfigSection.ts | 2 +- .../src/ui/sceneobjects/sprite/SpriteAnimationSection.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationConfigSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationConfigSection.ts index eaf2516ba..0a62d7e90 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationConfigSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationConfigSection.ts @@ -10,7 +10,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { protected getSectionHelpPath() { - return "TODO"; + return "scene-editor/animations-properties.html"; } createForm(parent: HTMLDivElement) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts index 4c3864461..05e87f579 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/sprite/SpriteAnimationSection.ts @@ -10,7 +10,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { protected getSectionHelpPath() { - return "TODO"; + return "scene-editor/animations-properties.html"; } createMenu(menu: controls.Menu): void { From bab596cff33def5a99a660a1a324fe9150c9dcb2 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Tue, 7 Nov 2023 15:34:20 -0500 Subject: [PATCH 57/58] Removes unused code. --- .../src/ui/sceneobjects/tilemap/TilemapLayerEditorSupport.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/tilemap/TilemapLayerEditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/tilemap/TilemapLayerEditorSupport.ts index e8d5522f4..2c9f5634e 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/tilemap/TilemapLayerEditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/tilemap/TilemapLayerEditorSupport.ts @@ -15,8 +15,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { this.addComponent( new TransformComponent(obj as unknown as ITransformLikeObject), - new VisibleComponent(obj as unknown as IVisibleLikeObject), - new TilemapLayerComponent(obj) + new VisibleComponent(obj as unknown as IVisibleLikeObject) ); this.setLabel(obj.layer.name); From 1f16f6e8423328606ed2afbadae4e106e375e851 Mon Sep 17 00:00:00 2001 From: PhaserEditor2D Date: Fri, 10 Nov 2023 06:48:31 -0500 Subject: [PATCH 58/58] Updates changelog. --- CHANGELOG.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index d8e1ea7aa..4d77d562d 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -1,6 +1,6 @@ # Change Log -## v3.64.0 +## v3.64.0 - Nov 10, 2023 * Fixes making thumbnails of single-layer prefabs. * Allows merging Import and Open buttons in the Asset Pack Entry section.