From 599ccb677f05359c9632b45d238b4614e72ae83a Mon Sep 17 00:00:00 2001 From: Harsimran Singh Virk Date: Fri, 20 Aug 2021 18:31:06 +0530 Subject: [PATCH] Add support for visual effects ("shaders") on objects. (#2901) * This allows to generate interesting visual effects, which can be controlled by events. For example, you can use an outline on an object to highlight, make the player glow when launching a spell, blur objects, etc... * A new "effects" tab is now present in the objects editor. From there, you can add visual effects, that were already available for layers, customize them from this editor. * Actions and conditions are available to manipulate effects and change their parameters during the game. * Learn more on the wiki: http://wiki.compilgames.net/doku.php/gdevelop5/objects/effects Co-authored-by: Florian Rival --- .../CodeGeneration/EffectsCodeGenerator.cpp | 25 +- .../Builtin/BaseObjectExtension.cpp | 68 ++++ .../Extensions/Metadata/EffectMetadata.cpp | 8 +- .../Extensions/Metadata/EffectMetadata.h | 21 +- Extensions/Effects/JsExtension.js | 9 +- Extensions/Effects/adjustment-pixi-filter.ts | 4 +- .../Effects/advanced-bloom-pixi-filter.ts | 4 +- Extensions/Effects/ascii-pixi-filter.ts | 4 +- Extensions/Effects/bevel-pixi-filter.ts | 4 +- .../Effects/black-and-white-pixi-filter.ts | 4 +- .../Effects/blending-mode-pixi-filter.ts | 4 +- Extensions/Effects/blur-pixi-filter.ts | 4 +- Extensions/Effects/brightness-pixi-filter.ts | 4 +- Extensions/Effects/bulge-pinch-pixi-filter.ts | 4 +- Extensions/Effects/color-map-pixi-filter.ts | 6 +- .../Effects/color-replace-pixi-filter.ts | 4 +- Extensions/Effects/crt-pixi-filter.ts | 6 +- .../Effects/displacement-pixi-filter.ts | 6 +- Extensions/Effects/dot-pixi-filter.ts | 4 +- Extensions/Effects/drop-shadow-pixi-filter.ts | 4 +- Extensions/Effects/glitch-pixi-filter.ts | 4 +- Extensions/Effects/glow-pixi-filter.ts | 4 +- Extensions/Effects/godray-pixi-filter.ts | 4 +- Extensions/Effects/kawase-blur-pixi-filter.ts | 4 +- Extensions/Effects/light-night-pixi-filter.ts | 4 +- Extensions/Effects/night-pixi-filter.ts | 4 +- Extensions/Effects/noise-pixi-filter.ts | 4 +- Extensions/Effects/old-film-pixi-filter.ts | 4 +- Extensions/Effects/outline-pixi-filter.ts | 4 +- Extensions/Effects/pixelate-pixi-filter.ts | 4 +- Extensions/Effects/radial-blur-pixi-filter.ts | 8 +- Extensions/Effects/reflection-pixi-filter.ts | 4 +- Extensions/Effects/rgb-split-pixi-filter.ts | 4 +- Extensions/Effects/sepia-pixi-filter.ts | 4 +- Extensions/Effects/tilt-shift-pixi-filter.ts | 4 +- Extensions/Effects/twist-pixi-filter.ts | 8 +- Extensions/Effects/zoom-blur-pixi-filter.ts | 8 +- Extensions/ExampleJsExtension/dummyeffect.ts | 11 +- .../Lighting/tests/lightruntimeobject.spec.js | 2 + .../LinkedObjects/tests/linkedobjects.spec.js | 6 + .../panelspriteruntimeobject.ts | 2 +- .../commonpathfindingruntimebehavior.spec.js | 2 + .../legacypathfindingruntimebehavior.spec.js | 2 + .../platformerobjectruntimebehavior.spec.js | 14 + .../TextEntryObject/textentryruntimeobject.ts | 2 +- Extensions/TextObject/textruntimeobject.ts | 2 +- .../flipped and animated tiles.json | 7 +- .../tests/topdownmovementbehavior.spec.js | 1 + .../Builtin/BaseObjectExtension.cpp | 15 + GDJS/GDJS/IDE/ExporterHelper.cpp | 1 + .../cocos-renderers/layer-cocos-renderer.ts | 2 +- GDJS/Runtime/layer.ts | 69 ++-- .../pixi-renderers/layer-pixi-renderer.ts | 148 +-------- .../pixi-renderers/pixi-effects-manager.ts | 286 +++++++++++++++++ .../pixi-renderers/pixi-filters-tools.ts | 10 +- GDJS/Runtime/runtimegame.ts | 11 + GDJS/Runtime/runtimeobject.ts | 189 ++++++++++- GDJS/Runtime/runtimescene.ts | 41 ++- .../websocket-debugger-client/hot-reloader.ts | 83 ++++- GDJS/tests/benchmarks/runtimeobject.js | 6 +- GDJS/tests/benchmarks/spriteruntimeobject.js | 1 + GDJS/tests/games/Sprite update test/game.json | 8 +- GDJS/tests/games/TweenBenchmark/game.json | 6 +- GDJS/tests/games/TweenTest/game.json | 30 +- ...immediately check if sound is stopped.json | 4 +- .../tests/games/effects/all effects demo.json | 4 +- .../Basic EventsBasedBehavior test.json | 13 +- .../platformer with events functions.json | 23 +- .../events-functions-nested-create/game.json | 3 +- ...h creation and deletion benchmark.gdg.json | 6 +- .../objects-benchmarks/rotating objects.json | 4 +- .../pin-object-to-another.json | 5 +- ...in-object-to-another-multiple-parents.json | 5 +- .../platformer sandbox.json | 24 +- ...t after animation is changed (simple).json | 5 +- ...point just after animation is changed.json | 4 +- .../ship-rotate-flip.json | 4 +- .../games/rotated-objects-hitboxes/game.json | 11 +- ...ported just after an animation change.json | 3 +- .../Trigger Once in Behaviors.json | 3 +- GDJS/tests/karma.conf.js | 2 + GDJS/tests/tests/common.js | 44 +-- GDJS/tests/tests/effects.js | 163 ++++++++++ GDJS/tests/tests/inputmanager.js | 1 + GDJS/tests/tests/runtimescene.js | 1 + GDJS/tests/tests/spriteruntimeobject.js | 2 + ...runtimeobject.pixiruntimegamewithassets.js | 1 + GDevelop.js/Bindings/Bindings.idl | 2 + GDevelop.js/types/gdeffectmetadata.js | 2 + .../EmptyBehaviorsPlaceholder.js | 52 ++- newIDE/app/src/BehaviorsEditor/index.js | 231 +++++++------ newIDE/app/src/CommandPalette/CommandsList.js | 10 + .../EffectsList/EmptyEffectsPlaceholder.js | 37 +++ .../app/src/EffectsList/EnumerateEffects.js | 2 + .../src/EffectsList/EnumerateEffects.spec.js | 11 +- newIDE/app/src/EffectsList/index.js | 303 +++++++++--------- .../EventsTree/EmptyEventsPlaceholder.js | 47 +-- .../app/src/LayersList/LayerEditorDialog.js | 185 +++++------ .../src/ObjectEditor/ObjectEditorDialog.js | 27 +- .../src/ObjectsList/EnumerateObjects.spec.js | 4 +- .../src/ObjectsList/UseObjectsListCommands.js | 28 +- newIDE/app/src/ObjectsList/index.js | 10 +- newIDE/app/src/SceneEditor/index.js | 7 +- newIDE/app/src/UI/EmptyMessage.js | 4 + newIDE/app/src/UI/EmptyPlaceholder.js | 32 ++ newIDE/app/src/fixtures/TestExtensions.js | 13 + newIDE/app/src/fixtures/TestProject.js | 37 +++ newIDE/app/src/stories/index.js | 49 ++- 108 files changed, 1882 insertions(+), 780 deletions(-) create mode 100644 GDJS/Runtime/pixi-renderers/pixi-effects-manager.ts create mode 100644 GDJS/tests/tests/effects.js create mode 100644 newIDE/app/src/EffectsList/EmptyEffectsPlaceholder.js create mode 100644 newIDE/app/src/UI/EmptyPlaceholder.js diff --git a/Core/GDCore/Events/CodeGeneration/EffectsCodeGenerator.cpp b/Core/GDCore/Events/CodeGeneration/EffectsCodeGenerator.cpp index b16895cd7759..313a02b74187 100644 --- a/Core/GDCore/Events/CodeGeneration/EffectsCodeGenerator.cpp +++ b/Core/GDCore/Events/CodeGeneration/EffectsCodeGenerator.cpp @@ -3,14 +3,17 @@ * Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights * reserved. This project is released under the MIT License. */ -#include #include "EffectsCodeGenerator.h" + +#include + #include "GDCore/Extensions/Metadata/EffectMetadata.h" #include "GDCore/Extensions/Metadata/MetadataProvider.h" #include "GDCore/Project/Effect.h" -#include "GDCore/Project/Layer.h" #include "GDCore/Project/EffectsContainer.h" +#include "GDCore/Project/Layer.h" #include "GDCore/Project/Layout.h" +#include "GDCore/Project/Object.h" #include "GDCore/Project/Project.h" namespace gd { @@ -33,6 +36,24 @@ void ExposeProjectEffects( worker(effect); } } + + for (std::size_t i; i < layout.GetObjectsCount(); i++) { + auto& object = layout.GetObject(i); + auto& effects = object.GetEffects(); + for (std::size_t e = 0; e < effects.GetEffectsCount(); e++) { + auto& effect = effects.GetEffect(e); + worker(effect); + } + } + } + + // Add global object effects + for (std::size_t s = 0; s < project.GetObjectsCount(); s++) { + auto& effects = project.GetObject(s).GetEffects(); + for (std::size_t e = 0; e < effects.GetEffectsCount(); e++) { + auto& effect = effects.GetEffect(e); + worker(effect); + } } } diff --git a/Core/GDCore/Extensions/Builtin/BaseObjectExtension.cpp b/Core/GDCore/Extensions/Builtin/BaseObjectExtension.cpp index b90696b1d52f..781930049a40 100644 --- a/Core/GDCore/Extensions/Builtin/BaseObjectExtension.cpp +++ b/Core/GDCore/Extensions/Builtin/BaseObjectExtension.cpp @@ -1072,6 +1072,74 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension( .AddParameter("expression", _("Target X position")) .AddParameter("expression", _("Target Y position")); + obj.AddAction("EnableEffect", + _("Enable an object effect"), + _("Enable an effect on the object"), + _("Enable effect _PARAM1_ on _PARAM0_: _PARAM2_"), + _("Effects"), + "res/actions/effect24.png", + "res/actions/effect.png") + .AddParameter("object", _("Object")) + .AddParameter("string", _("Effect Name")) + .AddParameter("yesorno", _("Enable?")) + .MarkAsSimple(); + + obj.AddAction("SetEffectDoubleParameter", + _("Effect parameter (number)"), + _("Change the value of a parameter of an effect.") + "\n" + + _("You can find the parameter names (and change the effect " + "names) in the effects window."), + _("Set _PARAM2_ to _PARAM3_ for effect _PARAM1_ of _PARAM0_"), + _("Effects"), + "res/actions/effect24.png", + "res/actions/effect.png") + .AddParameter("object", _("Object")) + .AddParameter("string", _("Effect Name")) + .AddParameter("string", _("Parameter name")) + .AddParameter("expression", _("New value")) + .MarkAsSimple(); + + obj.AddAction("SetEffectStringParameter", + _("Effect parameter (string)"), + _("Change the value (string) of a parameter of an effect.") + "\n" + + _("You can find the parameter names (and change the effect " + "names) in the effects window."), + _("Set _PARAM2_ to _PARAM3_ for effect _PARAM1_ of _PARAM0_"), + _("Effects"), + "res/actions/effect24.png", + "res/actions/effect.png") + .AddParameter("object", _("Object")) + .AddParameter("string", _("Effect Name")) + .AddParameter("string", _("Parameter name")) + .AddParameter("string", _("New value")) + .MarkAsSimple(); + + obj.AddAction("SetEffectBooleanParameter", + _("Effect parameter (enable or disable)"), + _("Enable or disable a parameter of an effect.") + "\n" + + _("You can find the parameter names (and change the effect " + "names) in the effects window."), + _("Enable _PARAM2_ for effect _PARAM1_ of _PARAM0_: _PARAM3_"), + _("Effects"), + "res/actions/effect24.png", + "res/actions/effect.png") + .AddParameter("object", _("Object")) + .AddParameter("string", _("Effect Name")) + .AddParameter("string", _("Parameter Name")) + .AddParameter("yesorno", _("Enable?")) + .MarkAsSimple(); + + obj.AddCondition("IsEffectEnabled", + _("Effect is enabled"), + _("Check if the effect on an object is enabled."), + _("Effect _PARAM1_ of _PARAM0_ is enabled"), + _("Effects"), + "res/actions/effect24.png", + "res/actions/effect.png") + .AddParameter("object", _("Object")) + .AddParameter("string", _("Effect Name")) + .MarkAsSimple(); + extension .AddAction("Create", _("Create an object"), diff --git a/Core/GDCore/Extensions/Metadata/EffectMetadata.cpp b/Core/GDCore/Extensions/Metadata/EffectMetadata.cpp index 432eba5c7ce2..0d5cad66ce8c 100644 --- a/Core/GDCore/Extensions/Metadata/EffectMetadata.cpp +++ b/Core/GDCore/Extensions/Metadata/EffectMetadata.cpp @@ -7,7 +7,8 @@ namespace gd { -EffectMetadata::EffectMetadata(const gd::String& type_) : type(type_) {} +EffectMetadata::EffectMetadata(const gd::String& type_) + : type(type_), isMarkedAsNotWorkingForObjects(false) {} EffectMetadata& EffectMetadata::SetIncludeFile(const gd::String& includeFile) { includeFiles.clear(); @@ -22,4 +23,9 @@ EffectMetadata& EffectMetadata::AddIncludeFile(const gd::String& includeFile) { return *this; } +EffectMetadata& EffectMetadata::MarkAsNotWorkingForObjects() { + isMarkedAsNotWorkingForObjects = true; + return *this; +} + } // namespace gd diff --git a/Core/GDCore/Extensions/Metadata/EffectMetadata.h b/Core/GDCore/Extensions/Metadata/EffectMetadata.h index 90d859bff0a8..7e18898afb0c 100644 --- a/Core/GDCore/Extensions/Metadata/EffectMetadata.h +++ b/Core/GDCore/Extensions/Metadata/EffectMetadata.h @@ -8,6 +8,7 @@ #include #include #include + #include "GDCore/Project/PropertyDescriptor.h" #include "GDCore/String.h" @@ -49,7 +50,8 @@ class GD_CORE_API EffectMetadata { }; /** - * Set the help path of the effect, relative to the GDevelop documentation root. + * Set the help path of the effect, relative to the GDevelop documentation + * root. */ EffectMetadata& SetHelpPath(const gd::String& path) { helpPath = path; @@ -66,6 +68,11 @@ class GD_CORE_API EffectMetadata { */ EffectMetadata& AddIncludeFile(const gd::String& includeFile); + /** + * \brief Mark the effect as not working as an object effect. + */ + EffectMetadata& MarkAsNotWorkingForObjects(); + /** * \brief Return a reference to the properties of this effect. */ @@ -81,12 +88,14 @@ class GD_CORE_API EffectMetadata { } /** - * \brief Get the help path of the effect, relative to the GDevelop documentation root. + * \brief Get the help path of the effect, relative to the GDevelop + * documentation root. */ const gd::String& GetHelpPath() const { return helpPath; } /** - * \brief Get the type of the effect (its internal name, like "BlackAndWhite"). + * \brief Get the type of the effect (its internal name, like + * "BlackAndWhite"). */ const gd::String& GetType() const { return type; } @@ -107,6 +116,11 @@ class GD_CORE_API EffectMetadata { return includeFiles; } + /** + * \brief Check if the effect is marked as not working as an object effect. + */ + bool IsMarkedAsNotWorkingForObjects() const { return isMarkedAsNotWorkingForObjects; }; + private: gd::String extensionNamespace; gd::String type; @@ -114,6 +128,7 @@ class GD_CORE_API EffectMetadata { gd::String fullname; gd::String description; std::vector includeFiles; + bool isMarkedAsNotWorkingForObjects; std::map properties; }; diff --git a/Extensions/Effects/JsExtension.js b/Extensions/Effects/JsExtension.js index c881ea356d70..424e1170b6fe 100644 --- a/Extensions/Effects/JsExtension.js +++ b/Extensions/Effects/JsExtension.js @@ -897,7 +897,8 @@ module.exports = { .setFullName(_('Radial Blur')) .setDescription(_('Applies a Motion blur to an object.')) .addIncludeFile('Extensions/Effects/pixi-filters/filter-radial-blur.js') - .addIncludeFile('Extensions/Effects/radial-blur-pixi-filter.js'); + .addIncludeFile('Extensions/Effects/radial-blur-pixi-filter.js') + .markAsNotWorkingForObjects(); // See https://github.com/pixijs/filters/issues/304 const radialBlurProperties = radialBlurEffect.getProperties(); radialBlurProperties .getOrCreate('radius') @@ -1084,7 +1085,8 @@ module.exports = { ) ) .addIncludeFile('Extensions/Effects/pixi-filters/filter-twist.js') - .addIncludeFile('Extensions/Effects/twist-pixi-filter.js'); + .addIncludeFile('Extensions/Effects/twist-pixi-filter.js') + .markAsNotWorkingForObjects(); // See https://github.com/pixijs/filters/issues/304 const twistProperties = twistEffect.getProperties(); twistProperties .getOrCreate('radius') @@ -1120,7 +1122,8 @@ module.exports = { .setFullName(_('Zoom blur')) .setDescription(_('Applies a Zoom blur.')) .addIncludeFile('Extensions/Effects/pixi-filters/filter-zoom-blur.js') - .addIncludeFile('Extensions/Effects/zoom-blur-pixi-filter.js'); + .addIncludeFile('Extensions/Effects/zoom-blur-pixi-filter.js') + .markAsNotWorkingForObjects(); // See https://github.com/pixijs/filters/issues/304 const zoomBlurProperties = zoomBlurEffect.getProperties(); zoomBlurProperties .getOrCreate('centerX') diff --git a/Extensions/Effects/adjustment-pixi-filter.ts b/Extensions/Effects/adjustment-pixi-filter.ts index 2488f74792c4..9fa37f6a4585 100644 --- a/Extensions/Effects/adjustment-pixi-filter.ts +++ b/Extensions/Effects/adjustment-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('Adjustment', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const adjustmentFilter = new PIXI.filters.AdjustmentFilter(); return adjustmentFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const adjustmentFilter = (filter as unknown) as PIXI.filters.AdjustmentFilter; if (parameterName === 'gamma') { diff --git a/Extensions/Effects/advanced-bloom-pixi-filter.ts b/Extensions/Effects/advanced-bloom-pixi-filter.ts index 29153075f7e0..33d249773117 100644 --- a/Extensions/Effects/advanced-bloom-pixi-filter.ts +++ b/Extensions/Effects/advanced-bloom-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('AdvancedBloom', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const advancedBloomFilter = new PIXI.filters.AdvancedBloomFilter(); return advancedBloomFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const advancedBloomFilter = (filter as unknown) as PIXI.filters.AdvancedBloomFilter; if (parameterName === 'threshold') { diff --git a/Extensions/Effects/ascii-pixi-filter.ts b/Extensions/Effects/ascii-pixi-filter.ts index 53faf4dbab13..ff19558be52c 100644 --- a/Extensions/Effects/ascii-pixi-filter.ts +++ b/Extensions/Effects/ascii-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('Ascii', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const asciiFilter = new PIXI.filters.AsciiFilter(); return asciiFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const asciiFilter = (filter as unknown) as PIXI.filters.AsciiFilter; if (parameterName === 'size') { diff --git a/Extensions/Effects/bevel-pixi-filter.ts b/Extensions/Effects/bevel-pixi-filter.ts index 40a60f935f9b..ae213896f0c1 100644 --- a/Extensions/Effects/bevel-pixi-filter.ts +++ b/Extensions/Effects/bevel-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('Bevel', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const bevelFilter = new PIXI.filters.BevelFilter(); return bevelFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter; if (parameterName === 'rotation') { diff --git a/Extensions/Effects/black-and-white-pixi-filter.ts b/Extensions/Effects/black-and-white-pixi-filter.ts index 58199661eed5..6d8460aaa4fc 100644 --- a/Extensions/Effects/black-and-white-pixi-filter.ts +++ b/Extensions/Effects/black-and-white-pixi-filter.ts @@ -1,12 +1,12 @@ namespace gdjs { import PIXI = GlobalPIXIModule.PIXI; gdjs.PixiFiltersTools.registerFilterCreator('BlackAndWhite', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const colorMatrix = new PIXI.filters.ColorMatrixFilter(); colorMatrix.blackAndWhite(false); return colorMatrix; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { // @ts-ignore - unsure why PIXI.filters is not recognised. const colorMatrix = (filter as unknown) as PIXI.filters.ColorMatrixFilter; diff --git a/Extensions/Effects/blending-mode-pixi-filter.ts b/Extensions/Effects/blending-mode-pixi-filter.ts index ad9d8debe197..ef01f4182590 100644 --- a/Extensions/Effects/blending-mode-pixi-filter.ts +++ b/Extensions/Effects/blending-mode-pixi-filter.ts @@ -1,11 +1,11 @@ namespace gdjs { import PIXI = GlobalPIXIModule.PIXI; gdjs.PixiFiltersTools.registerFilterCreator('BlendingMode', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const blendingModeFilter = new PIXI.filters.AlphaFilter(); return blendingModeFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { // @ts-ignore - unsure why PIXI.filters is not recognised. const blendingModeFilter = (filter as unknown) as PIXI.filters.AlphaFilter; diff --git a/Extensions/Effects/blur-pixi-filter.ts b/Extensions/Effects/blur-pixi-filter.ts index bc41e6540833..20ae38b50cc4 100644 --- a/Extensions/Effects/blur-pixi-filter.ts +++ b/Extensions/Effects/blur-pixi-filter.ts @@ -1,11 +1,11 @@ namespace gdjs { import PIXI = GlobalPIXIModule.PIXI; gdjs.PixiFiltersTools.registerFilterCreator('Blur', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const blur = new PIXI.filters.BlurFilter(); return blur; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { if ( parameterName !== 'blur' && diff --git a/Extensions/Effects/brightness-pixi-filter.ts b/Extensions/Effects/brightness-pixi-filter.ts index ae59ee41e520..0e177539502c 100644 --- a/Extensions/Effects/brightness-pixi-filter.ts +++ b/Extensions/Effects/brightness-pixi-filter.ts @@ -1,12 +1,12 @@ namespace gdjs { import PIXI = GlobalPIXIModule.PIXI; gdjs.PixiFiltersTools.registerFilterCreator('Brightness', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const brightness = new PIXI.filters.ColorMatrixFilter(); brightness.brightness(1, false); return brightness; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { // @ts-ignore - unsure why PIXI.filters is not recognised. const brightnessFilter = (filter as unknown) as PIXI.filters.ColorMatrixFilter; diff --git a/Extensions/Effects/bulge-pinch-pixi-filter.ts b/Extensions/Effects/bulge-pinch-pixi-filter.ts index 0aa94658f40e..a2dd4adb1edc 100644 --- a/Extensions/Effects/bulge-pinch-pixi-filter.ts +++ b/Extensions/Effects/bulge-pinch-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('BulgePinch', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const bulgePinchFilter = new PIXI.filters.BulgePinchFilter(); return bulgePinchFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const bulgePinchFilter = (filter as unknown) as PIXI.filters.BulgePinchFilter; if (parameterName === 'centerX') { diff --git a/Extensions/Effects/color-map-pixi-filter.ts b/Extensions/Effects/color-map-pixi-filter.ts index f4e443e924af..deb43d402c72 100644 --- a/Extensions/Effects/color-map-pixi-filter.ts +++ b/Extensions/Effects/color-map-pixi-filter.ts @@ -1,7 +1,7 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('ColorMap', { - makePIXIFilter: function (layer, effectData) { - const colorMapTexture = layer + makePIXIFilter: function (target, effectData) { + const colorMapTexture = target .getRuntimeScene() .getGame() .getImageManager() @@ -17,7 +17,7 @@ namespace gdjs { ); return colorMapFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const colorMapFilter = (filter as unknown) as PIXI.filters.ColorMapFilter; if (parameterName === 'mix') { diff --git a/Extensions/Effects/color-replace-pixi-filter.ts b/Extensions/Effects/color-replace-pixi-filter.ts index ee4475465c92..d1ecca500c17 100644 --- a/Extensions/Effects/color-replace-pixi-filter.ts +++ b/Extensions/Effects/color-replace-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('ColorReplace', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const colorReplaceFilter = new PIXI.filters.ColorReplaceFilter(); return colorReplaceFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter; if (parameterName === 'epsilon') { diff --git a/Extensions/Effects/crt-pixi-filter.ts b/Extensions/Effects/crt-pixi-filter.ts index 525cd621ccb3..fe8574b71558 100644 --- a/Extensions/Effects/crt-pixi-filter.ts +++ b/Extensions/Effects/crt-pixi-filter.ts @@ -7,14 +7,14 @@ namespace gdjs { crtFilter._animationTimer = 0; return crtFilter; }, - update: function (filter, layer) { + updatePreRender: function (filter, target) { if (filter.animationSpeed !== 0) { // Multiply by 10 so that the default value is a sensible speed filter.time += - (layer.getElapsedTime() / 1000) * 10 * filter.animationSpeed; + (target.getElapsedTime() / 1000) * 10 * filter.animationSpeed; } if (filter.animationFrequency !== 0) { - filter._animationTimer += layer.getElapsedTime() / 1000; + filter._animationTimer += target.getElapsedTime() / 1000; if (filter._animationTimer >= 1 / filter.animationFrequency) { filter.seed = Math.random(); filter._animationTimer = 0; diff --git a/Extensions/Effects/displacement-pixi-filter.ts b/Extensions/Effects/displacement-pixi-filter.ts index e9cf78d49362..06585538eaf0 100644 --- a/Extensions/Effects/displacement-pixi-filter.ts +++ b/Extensions/Effects/displacement-pixi-filter.ts @@ -1,8 +1,8 @@ namespace gdjs { import PIXI = GlobalPIXIModule.PIXI; gdjs.PixiFiltersTools.registerFilterCreator('Displacement', { - makePIXIFilter: function (layer, effectData) { - const displacementMapTexture = layer + makePIXIFilter: function (target, effectData) { + const displacementMapTexture = target .getRuntimeScene() .getGame() .getImageManager() @@ -14,7 +14,7 @@ namespace gdjs { ); return displacementFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { // @ts-ignore - unsure why PIXI.filters is not recognised. const displacementFilter = (filter as unknown) as PIXI.filters.DisplacementFilter; diff --git a/Extensions/Effects/dot-pixi-filter.ts b/Extensions/Effects/dot-pixi-filter.ts index 3be1227158ec..1486497f82fe 100644 --- a/Extensions/Effects/dot-pixi-filter.ts +++ b/Extensions/Effects/dot-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('Dot', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const dotFilter = new PIXI.filters.DotFilter(); return dotFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const dotFilter = (filter as unknown) as PIXI.filters.DotFilter; if (parameterName === 'scale') { diff --git a/Extensions/Effects/drop-shadow-pixi-filter.ts b/Extensions/Effects/drop-shadow-pixi-filter.ts index 539b96ef2d01..9ef270975c67 100644 --- a/Extensions/Effects/drop-shadow-pixi-filter.ts +++ b/Extensions/Effects/drop-shadow-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('DropShadow', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const dropShadowFilter = new PIXI.filters.DropShadowFilter(); return dropShadowFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter; if (parameterName === 'blur') { diff --git a/Extensions/Effects/glitch-pixi-filter.ts b/Extensions/Effects/glitch-pixi-filter.ts index ead4950c3bcf..62b54b5d3460 100644 --- a/Extensions/Effects/glitch-pixi-filter.ts +++ b/Extensions/Effects/glitch-pixi-filter.ts @@ -7,9 +7,9 @@ namespace gdjs { glitchFilter._animationTimer = 0; return glitchFilter; }, - update: function (filter, layer) { + updatePreRender: function (filter, target) { if (filter.animationFrequency !== 0) { - filter._animationTimer += layer.getElapsedTime() / 1000; + filter._animationTimer += target.getElapsedTime() / 1000; if (filter._animationTimer >= 1 / filter.animationFrequency) { filter.seed = Math.random(); filter._animationTimer = 0; diff --git a/Extensions/Effects/glow-pixi-filter.ts b/Extensions/Effects/glow-pixi-filter.ts index aee043e23b4e..7a8ba75e222e 100644 --- a/Extensions/Effects/glow-pixi-filter.ts +++ b/Extensions/Effects/glow-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('Glow', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const glowFilter = new PIXI.filters.GlowFilter(); return glowFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter; if (parameterName === 'innerStrength') { diff --git a/Extensions/Effects/godray-pixi-filter.ts b/Extensions/Effects/godray-pixi-filter.ts index 954fb3e7aafc..a7cc1215fb4e 100644 --- a/Extensions/Effects/godray-pixi-filter.ts +++ b/Extensions/Effects/godray-pixi-filter.ts @@ -6,9 +6,9 @@ namespace gdjs { const godrayFilter = new PIXI.filters.GodrayFilter(); return godrayFilter; }, - update: function (filter, layer) { + updatePreRender: function (filter, target) { if (filter.animationSpeed !== 0) { - filter.time += (layer.getElapsedTime() / 1000) * filter.animationSpeed; + filter.time += (target.getElapsedTime() / 1000) * filter.animationSpeed; } }, updateDoubleParameter: function (filter, parameterName, value) { diff --git a/Extensions/Effects/kawase-blur-pixi-filter.ts b/Extensions/Effects/kawase-blur-pixi-filter.ts index 8909d7b98578..3d0f50c1dd08 100644 --- a/Extensions/Effects/kawase-blur-pixi-filter.ts +++ b/Extensions/Effects/kawase-blur-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('KawaseBlur', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const kawaseBlurFilter = new PIXI.filters.KawaseBlurFilter(); return kawaseBlurFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const kawaseBlurFilter = (filter as unknown) as PIXI.filters.KawaseBlurFilter; if (parameterName === 'pixelizeX') { diff --git a/Extensions/Effects/light-night-pixi-filter.ts b/Extensions/Effects/light-night-pixi-filter.ts index e038c6757b04..5aec36c65f80 100644 --- a/Extensions/Effects/light-night-pixi-filter.ts +++ b/Extensions/Effects/light-night-pixi-filter.ts @@ -23,11 +23,11 @@ namespace gdjs { } LightNightPixiFilter.prototype.constructor = gdjs.LightNightPixiFilter; gdjs.PixiFiltersTools.registerFilterCreator('LightNight', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const filter = new gdjs.LightNightPixiFilter(); return filter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { if (parameterName !== 'opacity') { return; diff --git a/Extensions/Effects/night-pixi-filter.ts b/Extensions/Effects/night-pixi-filter.ts index 3dd38b82e532..ee258d017aa6 100644 --- a/Extensions/Effects/night-pixi-filter.ts +++ b/Extensions/Effects/night-pixi-filter.ts @@ -27,11 +27,11 @@ namespace gdjs { } NightPixiFilter.prototype.constructor = gdjs.NightPixiFilter; gdjs.PixiFiltersTools.registerFilterCreator('Night', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const filter = new gdjs.NightPixiFilter(); return filter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { if (parameterName !== 'intensity' && parameterName !== 'opacity') { return; diff --git a/Extensions/Effects/noise-pixi-filter.ts b/Extensions/Effects/noise-pixi-filter.ts index b4dacbce1e7c..ff6bbed76938 100644 --- a/Extensions/Effects/noise-pixi-filter.ts +++ b/Extensions/Effects/noise-pixi-filter.ts @@ -1,11 +1,11 @@ namespace gdjs { import PIXI = GlobalPIXIModule.PIXI; gdjs.PixiFiltersTools.registerFilterCreator('Noise', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const noiseFilter = new PIXI.filters.NoiseFilter(); return noiseFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { // @ts-ignore - unsure why PIXI.filters is not recognised. const noiseFilter = (filter as unknown) as PIXI.filters.NoiseFilter; diff --git a/Extensions/Effects/old-film-pixi-filter.ts b/Extensions/Effects/old-film-pixi-filter.ts index 3812be0f0c1d..6353382188f7 100644 --- a/Extensions/Effects/old-film-pixi-filter.ts +++ b/Extensions/Effects/old-film-pixi-filter.ts @@ -7,9 +7,9 @@ namespace gdjs { oldFilmFilter._animationTimer = 0; return oldFilmFilter; }, - update: function (filter, layer) { + updatePreRender: function (filter, target) { if (filter.animationFrequency !== 0) { - filter._animationTimer += layer.getElapsedTime() / 1000; + filter._animationTimer += target.getElapsedTime() / 1000; if (filter._animationTimer >= 1 / filter.animationFrequency) { filter.seed = Math.random(); filter._animationTimer = 0; diff --git a/Extensions/Effects/outline-pixi-filter.ts b/Extensions/Effects/outline-pixi-filter.ts index 2c498cc336fe..d129b9d5c514 100644 --- a/Extensions/Effects/outline-pixi-filter.ts +++ b/Extensions/Effects/outline-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('Outline', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const outlineFilter = new PIXI.filters.OutlineFilter(); return outlineFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const outlineFilter = (filter as unknown) as PIXI.filters.OutlineFilter; if (parameterName === 'thickness') { diff --git a/Extensions/Effects/pixelate-pixi-filter.ts b/Extensions/Effects/pixelate-pixi-filter.ts index 7aeab2a788b6..0c464b22fe80 100644 --- a/Extensions/Effects/pixelate-pixi-filter.ts +++ b/Extensions/Effects/pixelate-pixi-filter.ts @@ -1,12 +1,12 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('Pixelate', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const pixelateFilter = new PIXI.filters.PixelateFilter( effectData.doubleParameters.size ); return pixelateFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const pixelateFilter = (filter as unknown) as PIXI.filters.PixelateFilter; if (parameterName === 'size') { diff --git a/Extensions/Effects/radial-blur-pixi-filter.ts b/Extensions/Effects/radial-blur-pixi-filter.ts index 20607873c4a0..681310b5547e 100644 --- a/Extensions/Effects/radial-blur-pixi-filter.ts +++ b/Extensions/Effects/radial-blur-pixi-filter.ts @@ -1,18 +1,18 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('RadialBlur', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const radialBlurFilter = new PIXI.filters.RadialBlurFilter(); return radialBlurFilter; }, - update: function (filter, layer) { + updatePreRender: function (filter, target) { const radialBlurFilter = (filter as unknown) as PIXI.filters.RadialBlurFilter; radialBlurFilter.center[0] = Math.round( // @ts-ignore - extra properties are stored on the filter. - radialBlurFilter._centerX * layer.getWidth() + radialBlurFilter._centerX * target.getWidth() ); radialBlurFilter.center[1] = Math.round( // @ts-ignore - extra properties are stored on the filter. - radialBlurFilter._centerY * layer.getHeight() + radialBlurFilter._centerY * target.getHeight() ); }, updateDoubleParameter: function (filter, parameterName, value) { diff --git a/Extensions/Effects/reflection-pixi-filter.ts b/Extensions/Effects/reflection-pixi-filter.ts index 2433f0eca13e..1330f9d9cabc 100644 --- a/Extensions/Effects/reflection-pixi-filter.ts +++ b/Extensions/Effects/reflection-pixi-filter.ts @@ -23,9 +23,9 @@ namespace gdjs { ); return reflectionFilter; }, - update: function (filter, layer) { + updatePreRender: function (filter, target) { if (filter.animationSpeed !== 0) { - filter.time += (layer.getElapsedTime() / 1000) * filter.animationSpeed; + filter.time += (target.getElapsedTime() / 1000) * filter.animationSpeed; } }, updateDoubleParameter: function (filter, parameterName, value) { diff --git a/Extensions/Effects/rgb-split-pixi-filter.ts b/Extensions/Effects/rgb-split-pixi-filter.ts index ad0ef0cd7c8d..f176d7d6c34a 100644 --- a/Extensions/Effects/rgb-split-pixi-filter.ts +++ b/Extensions/Effects/rgb-split-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('RGBSplit', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const rgbSplitFilter = new PIXI.filters.RGBSplitFilter(); return rgbSplitFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const rgbSplitFilter = (filter as unknown) as PIXI.filters.RGBSplitFilter; if (parameterName === 'redX') { diff --git a/Extensions/Effects/sepia-pixi-filter.ts b/Extensions/Effects/sepia-pixi-filter.ts index f30b7d6f7069..bba31e1823e9 100644 --- a/Extensions/Effects/sepia-pixi-filter.ts +++ b/Extensions/Effects/sepia-pixi-filter.ts @@ -1,12 +1,12 @@ namespace gdjs { import PIXI = GlobalPIXIModule.PIXI; gdjs.PixiFiltersTools.registerFilterCreator('Sepia', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const colorMatrixFilter = new PIXI.filters.ColorMatrixFilter(); colorMatrixFilter.sepia(false); return colorMatrixFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { // @ts-ignore - unsure why PIXI.filters is not recognised. const colorMatrixFilter = (filter as unknown) as PIXI.filters.ColorMatrixFilter; diff --git a/Extensions/Effects/tilt-shift-pixi-filter.ts b/Extensions/Effects/tilt-shift-pixi-filter.ts index 19804a7015b0..83f3c45aaf2d 100644 --- a/Extensions/Effects/tilt-shift-pixi-filter.ts +++ b/Extensions/Effects/tilt-shift-pixi-filter.ts @@ -1,10 +1,10 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('TiltShift', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const tiltShiftFilter = new PIXI.filters.TiltShiftFilter(); return tiltShiftFilter; }, - update: function (filter, layer) {}, + updatePreRender: function (filter, target) {}, updateDoubleParameter: function (filter, parameterName, value) { const tiltShiftFilter = (filter as unknown) as PIXI.filters.TiltShiftFilter; if (parameterName === 'blur') { diff --git a/Extensions/Effects/twist-pixi-filter.ts b/Extensions/Effects/twist-pixi-filter.ts index fc67ed12fef1..8569ec9c0d44 100644 --- a/Extensions/Effects/twist-pixi-filter.ts +++ b/Extensions/Effects/twist-pixi-filter.ts @@ -5,20 +5,20 @@ namespace gdjs { import PIXI_ = GlobalPIXIModule.PIXI; gdjs.PixiFiltersTools.registerFilterCreator('Twist', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const twistFilter = new PIXI.filters.TwistFilter(); twistFilter.offset = new PIXI_.Point(0, 0); return twistFilter; }, - update: function (filter, layer) { + updatePreRender: function (filter, target) { const twistFilter = (filter as unknown) as PIXI.filters.TwistFilter; twistFilter.offset.x = Math.round( // @ts-ignore - extra properties are stored on the filter. - twistFilter._offsetX * layer.getWidth() + twistFilter._offsetX * target.getWidth() ); twistFilter.offset.y = Math.round( // @ts-ignore - extra properties are stored on the filter. - twistFilter._offsetY * layer.getHeight() + twistFilter._offsetY * target.getHeight() ); }, updateDoubleParameter: function (filter, parameterName, value) { diff --git a/Extensions/Effects/zoom-blur-pixi-filter.ts b/Extensions/Effects/zoom-blur-pixi-filter.ts index 2e671d733ea0..ef9ae39591f5 100644 --- a/Extensions/Effects/zoom-blur-pixi-filter.ts +++ b/Extensions/Effects/zoom-blur-pixi-filter.ts @@ -1,18 +1,18 @@ namespace gdjs { gdjs.PixiFiltersTools.registerFilterCreator('ZoomBlur', { - makePIXIFilter: function (layer, effectData) { + makePIXIFilter: function (target, effectData) { const zoomBlurFilter = new PIXI.filters.ZoomBlurFilter(); return zoomBlurFilter; }, - update: function (filter, layer) { + updatePreRender: function (filter, target) { const zoomBlurFilter = (filter as unknown) as PIXI.filters.ZoomBlurFilter; zoomBlurFilter.center[0] = Math.round( // @ts-ignore - extra properties are stored on the filter. - zoomBlurFilter._centerX * layer.getWidth() + zoomBlurFilter._centerX * target.getWidth() ); zoomBlurFilter.center[1] = Math.round( // @ts-ignore - extra properties are stored on the filter. - zoomBlurFilter._centerY * layer.getHeight() + zoomBlurFilter._centerY * target.getHeight() ); }, updateDoubleParameter: function (filter, parameterName, value) { diff --git a/Extensions/ExampleJsExtension/dummyeffect.ts b/Extensions/ExampleJsExtension/dummyeffect.ts index 403a7b7f92c2..acde7f0ba6bc 100644 --- a/Extensions/ExampleJsExtension/dummyeffect.ts +++ b/Extensions/ExampleJsExtension/dummyeffect.ts @@ -57,11 +57,12 @@ namespace gdjs { ); return filter; }, - // Function called at every frame rendered - update: function (filter, layer) {}, - // If your filter depends on the time, you can get the elapsed time - // with `layer.getElapsedTime()`. - // You can update the uniforms or other state of the filter. + // Function called at every frame, after events and before the frame is rendered. + updatePreRender: function (filter, layer) { + // If your filter depends on the time, you can get the elapsed time + // with `layer.getElapsedTime()`. + // You can update the uniforms or other state of the filter. + }, // Function that will be called to update a (number) parameter of the PIXI filter with a new value updateDoubleParameter: function (filter, parameterName, value) { if (parameterName === 'opacity') { diff --git a/Extensions/Lighting/tests/lightruntimeobject.spec.js b/Extensions/Lighting/tests/lightruntimeobject.spec.js index 9b12da04f9ff..fc51de7a08cc 100644 --- a/Extensions/Lighting/tests/lightruntimeobject.spec.js +++ b/Extensions/Lighting/tests/lightruntimeobject.spec.js @@ -14,6 +14,7 @@ const addLightObject = (runtimeScene, radius) => { type: 'Lighting::LightObject', variables: [], behaviors: [], + effects: [], content: { radius: radius, color: '#b4b4b4', @@ -41,6 +42,7 @@ const addLightObstacle = (runtimeScene, width, height) => { type: 'Lighting::LightObstacleBehavior', }, ], + effects: [], }); obstacle.getWidth = function () { return width; diff --git a/Extensions/LinkedObjects/tests/linkedobjects.spec.js b/Extensions/LinkedObjects/tests/linkedobjects.spec.js index e92c23f105ec..4fb00c5030b4 100644 --- a/Extensions/LinkedObjects/tests/linkedobjects.spec.js +++ b/Extensions/LinkedObjects/tests/linkedobjects.spec.js @@ -19,32 +19,38 @@ describe('gdjs.LinksManager', function () { name: 'obj1', type: '', behaviors: [], + effects: [], }); var object1B = new gdjs.RuntimeObject(runtimeScene, { name: 'obj1', type: '', behaviors: [], + effects: [], }); var object1C = new gdjs.RuntimeObject(runtimeScene, { name: 'obj1', type: '', behaviors: [], + effects: [], }); var object2A = new gdjs.RuntimeObject(runtimeScene, { name: 'obj1', type: '', behaviors: [], + effects: [], }); var object2B = new gdjs.RuntimeObject(runtimeScene, { name: 'obj1', type: '', behaviors: [], + effects: [], }); var object2C = new gdjs.RuntimeObject(runtimeScene, { name: 'obj1', type: '', behaviors: [], + effects: [], }); runtimeScene.addObject(object1A); diff --git a/Extensions/PanelSpriteObject/panelspriteruntimeobject.ts b/Extensions/PanelSpriteObject/panelspriteruntimeobject.ts index 3fe2a187dec9..1874455f5d10 100644 --- a/Extensions/PanelSpriteObject/panelspriteruntimeobject.ts +++ b/Extensions/PanelSpriteObject/panelspriteruntimeobject.ts @@ -121,7 +121,7 @@ namespace gdjs { } } - update(): void { + update(runtimeScene: gdjs.RuntimeScene): void { this._renderer.ensureUpToDate(); } diff --git a/Extensions/PathfindingBehavior/tests/commonpathfindingruntimebehavior.spec.js b/Extensions/PathfindingBehavior/tests/commonpathfindingruntimebehavior.spec.js index 783118cb0694..c2de695912de 100644 --- a/Extensions/PathfindingBehavior/tests/commonpathfindingruntimebehavior.spec.js +++ b/Extensions/PathfindingBehavior/tests/commonpathfindingruntimebehavior.spec.js @@ -66,6 +66,7 @@ describe('gdjs.PathfindingRuntimeBehavior', function () { collisionMethod: collisionMethod, }, ], + effects: [], }); player.getWidth = function () { return 90; @@ -89,6 +90,7 @@ describe('gdjs.PathfindingRuntimeBehavior', function () { cost: 2, }, ], + effects: [], }); obstacle.getWidth = function () { return 100; diff --git a/Extensions/PathfindingBehavior/tests/legacypathfindingruntimebehavior.spec.js b/Extensions/PathfindingBehavior/tests/legacypathfindingruntimebehavior.spec.js index 2b983ac6e6a4..318878b02282 100644 --- a/Extensions/PathfindingBehavior/tests/legacypathfindingruntimebehavior.spec.js +++ b/Extensions/PathfindingBehavior/tests/legacypathfindingruntimebehavior.spec.js @@ -77,6 +77,7 @@ describe('gdjs.PathfindingRuntimeBehavior', function () { ], }, ], + effects: [], behaviors: [ { type: 'PathfindingBehavior::PathfindingBehavior', @@ -131,6 +132,7 @@ describe('gdjs.PathfindingRuntimeBehavior', function () { ], }, ], + effects: [], behaviors: [ { type: 'PathfindingBehavior::PathfindingObstacleBehavior', diff --git a/Extensions/PlatformBehavior/tests/platformerobjectruntimebehavior.spec.js b/Extensions/PlatformBehavior/tests/platformerobjectruntimebehavior.spec.js index e6e4a8365344..ffd087af074a 100644 --- a/Extensions/PlatformBehavior/tests/platformerobjectruntimebehavior.spec.js +++ b/Extensions/PlatformBehavior/tests/platformerobjectruntimebehavior.spec.js @@ -34,6 +34,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { canBeGrabbed: true, }, ], + effects: [], }); platform.setCustomWidthAndHeight(60, 32); runtimeScene.addObject(platform); @@ -53,6 +54,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { canBeGrabbed: false, }, ], + effects: [], }); platform.setCustomWidthAndHeight(60, 32); runtimeScene.addObject(platform); @@ -72,6 +74,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { platformType: 'Ladder', }, ], + effects: [], }); ladder.setCustomWidthAndHeight(20, 60); runtimeScene.addObject(ladder); @@ -106,6 +109,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { slopeMaxAngle: 60, }, ], + effects: [], }); object.setCustomWidthAndHeight(10, 20); runtimeScene.addObject(object); @@ -302,6 +306,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { slopeMaxAngle: 60, }, ], + effects: [], }); object.setCustomWidthAndHeight(10, 20); runtimeScene.addObject(object); @@ -469,6 +474,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { roundCoordinates: true, }, ], + effects: [], }); object.setCustomWidthAndHeight(10, 20); runtimeScene.addObject(object); @@ -915,6 +921,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { slopeMaxAngle: 60, }, ], + effects: [], }); object.setCustomWidthAndHeight(10, 20); runtimeScene.addObject(object); @@ -937,6 +944,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { platformType: 'Jumpthru', }, ], + effects: [], }); jumpthru.setCustomWidthAndHeight(60, 5); runtimeScene.addObject(jumpthru); @@ -1051,6 +1059,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { slopeMaxAngle: 60, }, ], + effects: [], }); object.setCustomWidthAndHeight(10, 20); runtimeScene.addObject(object); @@ -1312,6 +1321,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { slopeMaxAngle: 60, }, ], + effects: [], }); object.setCustomWidthAndHeight(10, 20); runtimeScene.addObject(object); @@ -1441,6 +1451,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { slopeMaxAngle: 60, }, ], + effects: [], }); object.setCustomWidthAndHeight(10, 20); runtimeScene.addObject(object); @@ -1476,6 +1487,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { platformType: 'Platform', }, ], + effects: [], }); object2.setCustomWidthAndHeight(10, 20); runtimeScene.addObject(object2); @@ -1599,6 +1611,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { roundCoordinates: true, }, ], + effects: [], }); object.setCustomWidthAndHeight(10, 20); runtimeScene.addObject(object); @@ -1990,6 +2003,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () { roundCoordinates: true, }, ], + effects: [], }); object.setCustomWidthAndHeight(10, 20); runtimeScene.addObject(object); diff --git a/Extensions/TextEntryObject/textentryruntimeobject.ts b/Extensions/TextEntryObject/textentryruntimeobject.ts index 0741ddd770a3..ffe3e32df5fc 100644 --- a/Extensions/TextEntryObject/textentryruntimeobject.ts +++ b/Extensions/TextEntryObject/textentryruntimeobject.ts @@ -38,7 +38,7 @@ namespace gdjs { } } - update(): void { + update(runtimeScene: gdjs.RuntimeScene): void { if ((this._renderer as any).getString) { this._str = (this._renderer as any).getString(); } diff --git a/Extensions/TextObject/textruntimeobject.ts b/Extensions/TextObject/textruntimeobject.ts index 5f0366f134c5..c96039a757b5 100644 --- a/Extensions/TextObject/textruntimeobject.ts +++ b/Extensions/TextObject/textruntimeobject.ts @@ -131,7 +131,7 @@ namespace gdjs { return this._renderer.getRendererObject(); } - update(): void { + update(runtimeScene: gdjs.RuntimeScene): void { this._renderer.ensureUpToDate(); } diff --git a/Extensions/TileMap/tests/flipped and animated tiles/flipped and animated tiles.json b/Extensions/TileMap/tests/flipped and animated tiles/flipped and animated tiles.json index 259cba20a08b..17d198c251ef 100644 --- a/Extensions/TileMap/tests/flipped and animated tiles/flipped and animated tiles.json +++ b/Extensions/TileMap/tests/flipped and animated tiles/flipped and animated tiles.json @@ -246,6 +246,7 @@ "type": "TileMap::TileMap", "variables": [], "behaviors": [], + "effects": [], "content": { "tilemapJsonFile": "example.json", "tilesetJsonFile": "tileset.json", @@ -266,6 +267,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "FlipXY", "font": "", "characterSize": 10, @@ -285,6 +287,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "FlipX", "font": "", "characterSize": 10, @@ -304,6 +307,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "FlipY", "font": "", "characterSize": 10, @@ -323,6 +327,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Normal", "font": "", "characterSize": 10, @@ -365,4 +370,4 @@ "eventsFunctionsExtensions": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/Extensions/TopDownMovementBehavior/tests/topdownmovementbehavior.spec.js b/Extensions/TopDownMovementBehavior/tests/topdownmovementbehavior.spec.js index f06f54d90fef..26826dfc39a2 100644 --- a/Extensions/TopDownMovementBehavior/tests/topdownmovementbehavior.spec.js +++ b/Extensions/TopDownMovementBehavior/tests/topdownmovementbehavior.spec.js @@ -47,6 +47,7 @@ describe('gdjs.TopDownMovementRuntimeBehavior', function () { const player = new gdjs.TestRuntimeObject(runtimeScene, { name: 'player', type: '', + effects: [], behaviors: [ { type: 'TopDownMovementBehavior::TopDownMovementBehavior', diff --git a/GDJS/GDJS/Extensions/Builtin/BaseObjectExtension.cpp b/GDJS/GDJS/Extensions/Builtin/BaseObjectExtension.cpp index 36495e6c0e88..c53620a1b45c 100644 --- a/GDJS/GDJS/Extensions/Builtin/BaseObjectExtension.cpp +++ b/GDJS/GDJS/Extensions/Builtin/BaseObjectExtension.cpp @@ -85,6 +85,9 @@ BaseObjectExtension::BaseObjectExtension() { objectConditions["Invisible"] .SetFunctionName("isHidden") .SetIncludeFile("runtimeobject.js"); + objectConditions["IsEffectEnabled"] + .SetFunctionName("isEffectEnabled") + .SetIncludeFile("runtimeobject.js"); objectActions["Delete"].SetFunctionName("deleteFromScene"); objectActions["MettreAutourPos"].SetFunctionName("putAround"); objectActions["MettreAutour"] @@ -173,6 +176,18 @@ BaseObjectExtension::BaseObjectExtension() { objectActions["RemoveObjectTimer"] .SetFunctionName("removeTimer") .SetIncludeFile("runtimeobject.js"); + objectActions["EnableEffect"] + .SetFunctionName("enableEffect") + .SetIncludeFile("runtimeobject.js"); + objectActions["SetEffectDoubleParameter"] + .SetFunctionName("setEffectDoubleParameter") + .SetIncludeFile("runtimeobject.js"); + objectActions["SetEffectStringParameter"] + .SetFunctionName("setEffectStringParameter") + .SetIncludeFile("runtimeobject.js"); + objectActions["SetEffectBooleanParameter"] + .SetFunctionName("setEffectBooleanParameter") + .SetIncludeFile("runtimeobject.js"); objectExpressions["X"].SetFunctionName("getX"); objectExpressions["Y"].SetFunctionName("getY"); diff --git a/GDJS/GDJS/IDE/ExporterHelper.cpp b/GDJS/GDJS/IDE/ExporterHelper.cpp index 8e3f92bb82b6..5feac88a4528 100644 --- a/GDJS/GDJS/IDE/ExporterHelper.cpp +++ b/GDJS/GDJS/IDE/ExporterHelper.cpp @@ -672,6 +672,7 @@ void ExporterHelper::AddLibsInclude(bool pixiRenderers, "pixi-renderers/spriteruntimeobject-pixi-renderer.js"); InsertUnique(includesFiles, "pixi-renderers/loadingscreen-pixi-renderer.js"); + InsertUnique(includesFiles, "pixi-renderers/pixi-effects-manager.js"); InsertUnique(includesFiles, "howler-sound-manager/howler.min.js"); InsertUnique(includesFiles, "howler-sound-manager/howler-sound-manager.js"); InsertUnique(includesFiles, diff --git a/GDJS/Runtime/cocos-renderers/layer-cocos-renderer.ts b/GDJS/Runtime/cocos-renderers/layer-cocos-renderer.ts index 6c5175551443..af8130c98fbf 100644 --- a/GDJS/Runtime/cocos-renderers/layer-cocos-renderer.ts +++ b/GDJS/Runtime/cocos-renderers/layer-cocos-renderer.ts @@ -140,7 +140,7 @@ namespace gdjs { this._cocosLayer.setVisible(visible); } - update(): void {} + updatePreRender(): void {} // Unimplemented updateClearColor(): void {} diff --git a/GDJS/Runtime/layer.ts b/GDJS/Runtime/layer.ts index 8c7b6f90a368..d4b706f5ade9 100644 --- a/GDJS/Runtime/layer.ts +++ b/GDJS/Runtime/layer.ts @@ -9,7 +9,7 @@ namespace gdjs { * * Viewports and multiple cameras are not supported. */ - export class Layer { + export class Layer implements EffectsTarget { _name: string; _cameraRotation: float = 0; _zoomFactor: float = 1; @@ -21,13 +21,16 @@ namespace gdjs { _cameraY: float; _cachedGameResolutionWidth: integer; _cachedGameResolutionHeight: integer; + _runtimeScene: gdjs.RuntimeScene; + _effectsManager: gdjs.EffectsManager; // Lighting layer properties. _isLightingLayer: boolean; _followBaseLayerCamera: boolean; _clearColor: Array; + _rendererEffects: Record = {}; _renderer: LayerRenderer; /** @@ -47,6 +50,7 @@ namespace gdjs { .getGame() .getGameResolutionHeight(); this._runtimeScene = runtimeScene; + this._effectsManager = runtimeScene.getGame().getEffectsManager(); this._isLightingLayer = layerData.isLightingLayer; this._followBaseLayerCamera = layerData.followBaseLayerCamera; this._clearColor = [ @@ -120,13 +124,13 @@ namespace gdjs { /** * Called at each frame, after events are run and before rendering. - * @param [runtimeScene] The scene the layer belongs to. */ - update(runtimeScene?: gdjs.RuntimeScene): void { + updatePreRender(runtimeScene?: gdjs.RuntimeScene): void { if (this._followBaseLayerCamera) { this.followBaseLayer(); } - return this._renderer.update(); + this._renderer.updatePreRender(); + this._effectsManager.updatePreRender(this._rendererEffects, this); } /** @@ -318,6 +322,7 @@ namespace gdjs { /** * Return the initial effects data for the layer. Only to * be used by renderers. + * @deprecated */ getInitialEffectsData(): EffectData[] { return this._initialEffectsData; @@ -328,28 +333,12 @@ namespace gdjs { * @param effectData The data of the effect to add. */ addEffect(effectData: EffectData): void { - this._renderer.addEffect(effectData); - for (let name in effectData.doubleParameters) { - this.setEffectDoubleParameter( - effectData.name, - name, - effectData.doubleParameters[name] - ); - } - for (let name in effectData.stringParameters) { - this.setEffectStringParameter( - effectData.name, - name, - effectData.stringParameters[name] - ); - } - for (let name in effectData.booleanParameters) { - this.setEffectBooleanParameter( - effectData.name, - name, - effectData.booleanParameters[name] - ); - } + this._effectsManager.addEffect( + effectData, + this._rendererEffects, + this._renderer.getRendererObject(), + this + ); } /** @@ -357,7 +346,11 @@ namespace gdjs { * @param effectName The name of the effect. */ removeEffect(effectName: string): void { - this._renderer.removeEffect(effectName); + this._effectsManager.removeEffect( + this._rendererEffects, + this._renderer.getRendererObject(), + effectName + ); } /** @@ -371,7 +364,8 @@ namespace gdjs { parameterName: string, value: float ): void { - return this._renderer.setEffectDoubleParameter( + this._effectsManager.setEffectDoubleParameter( + this._rendererEffects, name, parameterName, value @@ -389,7 +383,8 @@ namespace gdjs { parameterName: string, value: string ): void { - return this._renderer.setEffectStringParameter( + this._effectsManager.setEffectStringParameter( + this._rendererEffects, name, parameterName, value @@ -407,7 +402,8 @@ namespace gdjs { parameterName: string, value: boolean ): void { - return this._renderer.setEffectBooleanParameter( + this._effectsManager.setEffectBooleanParameter( + this._rendererEffects, name, parameterName, value @@ -420,7 +416,7 @@ namespace gdjs { * @param enable true to enable, false to disable */ enableEffect(name: string, enable: boolean): void { - this._renderer.enableEffect(name, enable); + this._effectsManager.enableEffect(this._rendererEffects, name, enable); } /** @@ -429,7 +425,7 @@ namespace gdjs { * @return true if the effect is enabled, false otherwise. */ isEffectEnabled(name: string): boolean { - return this._renderer.isEffectEnabled(name); + return this._effectsManager.isEffectEnabled(this._rendererEffects, name); } /** @@ -438,7 +434,7 @@ namespace gdjs { * @return true if the effect exists, false otherwise. */ hasEffect(name: string): boolean { - return this._renderer.hasEffect(name); + return this._effectsManager.hasEffect(this._rendererEffects, name); } /** @@ -463,10 +459,9 @@ namespace gdjs { * Return the time elapsed since the last frame, * in milliseconds, for objects on the layer. */ - getElapsedTime(): float { - return ( - this._runtimeScene.getTimeManager().getElapsedTime() * this._timeScale - ); + getElapsedTime(runtimeScene?: RuntimeScene): float { + runtimeScene = runtimeScene || this._runtimeScene; + return runtimeScene.getTimeManager().getElapsedTime() * this._timeScale; } /** diff --git a/GDJS/Runtime/pixi-renderers/layer-pixi-renderer.ts b/GDJS/Runtime/pixi-renderers/layer-pixi-renderer.ts index 4566ddf215c4..d0291e3a657f 100644 --- a/GDJS/Runtime/pixi-renderers/layer-pixi-renderer.ts +++ b/GDJS/Runtime/pixi-renderers/layer-pixi-renderer.ts @@ -13,7 +13,6 @@ namespace gdjs { export class LayerPixiRenderer { _pixiContainer: PIXI.Container; - _filters: Record = {}; _layer: any; _renderTexture: PIXI.RenderTexture | null = null; _lightingSprite: PIXI.Sprite | null = null; @@ -83,66 +82,10 @@ namespace gdjs { this._pixiContainer.visible = !!visible; } - update(): void { + updatePreRender(): void { if (this._renderTexture) { this._updateRenderTexture(); } - for (const filterName in this._filters) { - const filter = this._filters[filterName]; - filter.update(filter.pixiFilter, this._layer); - } - } - - /** - * Add a new effect, or replace the one with the same name. - * @param effectData The data of the effect to add. - */ - addEffect(effectData: EffectData): void { - const filterCreator = gdjs.PixiFiltersTools.getFilterCreator( - effectData.effectType - ); - if (!filterCreator) { - console.log( - 'Filter "' + - effectData.name + - '" has an unknown effect type: "' + - effectData.effectType + - '". Was it registered properly? Is the effect type correct?' - ); - return; - } - - const filter: gdjs.PixiFiltersTools.Filter = { - pixiFilter: filterCreator.makePIXIFilter(this._layer, effectData), - updateDoubleParameter: filterCreator.updateDoubleParameter, - updateStringParameter: filterCreator.updateStringParameter, - updateBooleanParameter: filterCreator.updateBooleanParameter, - update: filterCreator.update, - }; - if (this._isLightingLayer) { - filter.pixiFilter.blendMode = PIXI.BLEND_MODES.ADD; - } - this._pixiContainer.filters = (this._pixiContainer.filters || []).concat( - filter.pixiFilter - ); - this._filters[effectData.name] = filter; - } - - /** - * Remove the effect with the specified name - * @param effectName The name of the effect. - */ - removeEffect(effectName: string): void { - const filter = this._filters[effectName]; - if (!filter) { - return; - } - this._pixiContainer.filters = (this._pixiContainer.filters || []).filter( - function (pixiFilter) { - return pixiFilter !== filter.pixiFilter; - } - ); - delete this._filters[effectName]; } /** @@ -188,95 +131,6 @@ namespace gdjs { this._pixiContainer.removeChild(child); } - /** - * Update the parameter of an effect (with a number). - * @param name The effect name - * @param parameterName The parameter name - * @param value The new value for the parameter - */ - setEffectDoubleParameter( - name: string, - parameterName: string, - value: float - ): void { - const filter = this._filters[name]; - if (!filter) { - return; - } - filter.updateDoubleParameter(filter.pixiFilter, parameterName, value); - } - - /** - * Update the parameter of an effect (with a string). - * @param name The effect name - * @param parameterName The parameter name - * @param value The new value for the parameter - */ - setEffectStringParameter( - name: string, - parameterName: string, - value: string - ): void { - const filter = this._filters[name]; - if (!filter) { - return; - } - filter.updateStringParameter(filter.pixiFilter, parameterName, value); - } - - /** - * Enable or disable the parameter of an effect (boolean). - * @param name The effect name - * @param parameterName The parameter name - * @param value The new value for the parameter - */ - setEffectBooleanParameter( - name: string, - parameterName: string, - value: boolean - ): void { - const filter = this._filters[name]; - if (!filter) { - return; - } - filter.updateBooleanParameter(filter.pixiFilter, parameterName, value); - } - - /** - * Check if an effect exists. - * @param name The effect name - * @returns True if the effect exists, false otherwise - */ - hasEffect(name: string): boolean { - return !!this._filters[name]; - } - - /** - * Enable an effect. - * @param name The effect name - * @param value Set to true to enable, false to disable - */ - enableEffect(name: string, value: boolean): void { - const filter = this._filters[name]; - if (!filter) { - return; - } - gdjs.PixiFiltersTools.enableEffect(filter, value); - } - - /** - * Check if an effect is enabled. - * @param name The effect name - * @return true if the filter is enabled - */ - isEffectEnabled(name: string): boolean { - const filter = this._filters[name]; - if (!filter) { - return false; - } - return gdjs.PixiFiltersTools.isEffectEnabled(filter); - } - updateClearColor(): void { this._clearColor = this._layer.getClearColor(); this._updateRenderTexture(); diff --git a/GDJS/Runtime/pixi-renderers/pixi-effects-manager.ts b/GDJS/Runtime/pixi-renderers/pixi-effects-manager.ts new file mode 100644 index 000000000000..0459f2197ce2 --- /dev/null +++ b/GDJS/Runtime/pixi-renderers/pixi-effects-manager.ts @@ -0,0 +1,286 @@ +/* + * GDevelop JS Platform + * Copyright 2013-2021 Florian Rival (Florian.Rival@gmail.com). All rights reserved. + * This project is released under the MIT License. + */ +namespace gdjs { + import PIXI = GlobalPIXIModule.PIXI; + + type RendererEffects = Record; + + export interface EffectsTarget { + getRuntimeScene: () => RuntimeScene; + getElapsedTime: (runtimeScene?: RuntimeScene) => number; + getHeight: () => number; + getWidth: () => number; + isLightingLayer?: () => boolean; + } + + /** + * Handle effects (aka PixiJS "filters") on PixiJS objects. + */ + class PixiEffectsManager { + /** + * Initialize the renderer effect (PixiJS filter) from the effect data. + * Call the applyEffect method afterwards, to correctly apply the + * initialized effects on the object. + * @param effectData The effect data + * @param rendererEffects The collection of PixiJS filters + * @param target The effects target + */ + initializeEffect( + effectData: EffectData, + rendererEffects: RendererEffects, + target: EffectsTarget + ): boolean { + const filterCreator = gdjs.PixiFiltersTools.getFilterCreator( + effectData.effectType + ); + if (!filterCreator) { + console.log( + 'Effect "' + + effectData.name + + '" has an unknown effect type: "' + + effectData.effectType + + '". Was it registered properly? Is the effect type correct?' + ); + return false; + } + + const filter: PixiFiltersTools.Filter = { + pixiFilter: filterCreator.makePIXIFilter(target, effectData), + updateDoubleParameter: filterCreator.updateDoubleParameter, + updateStringParameter: filterCreator.updateStringParameter, + updateBooleanParameter: filterCreator.updateBooleanParameter, + updatePreRender: filterCreator.updatePreRender, + }; + + if (target.isLightingLayer && target.isLightingLayer()) { + filter.pixiFilter.blendMode = PIXI.BLEND_MODES.ADD; + } + + rendererEffects[effectData.name] = filter; + return true; + } + + /** + * Apply the effect on the PixiJS DisplayObject. + * Called after the effect is initialized. + * @param rendererObject The renderer object + * @param effect The effect to be applied. + */ + applyEffect( + rendererObject: PIXI.DisplayObject, + effect: PixiFiltersTools.Filter + ): boolean { + rendererObject.filters = (rendererObject.filters || []).concat( + effect.pixiFilter + ); + return true; + } + + /** + * Update the filters applied on a PixiJS DisplayObject. + * This must be called after the events and before the rendering. + * + * This allows effects to be sure that they are up to date and ready + * to render. This is not called on objects that are not rendered on screen + * ("culling"). This is always called on layers. + */ + updatePreRender(rendererEffects: RendererEffects, target: EffectsTarget) { + for (const filterName in rendererEffects) { + const filter = rendererEffects[filterName]; + filter.updatePreRender(filter.pixiFilter, target); + } + } + + /** + * Add a new effect on a PixiJS DisplayObject, or replace the one + * with the same name. + * @param effectData The effect data + * @param rendererEffects The renderer effects + * @param rendererObject The renderer object + * @param target The effects target + */ + addEffect( + effectData: EffectData, + rendererEffects: RendererEffects, + rendererObject: PIXI.DisplayObject, + target: EffectsTarget + ): boolean { + let effectAdded = true; + effectAdded = + this.initializeEffect(effectData, rendererEffects, target) && + effectAdded; + effectAdded = + this.updateAllEffectParameters(rendererEffects, effectData) && + effectAdded; + effectAdded = + this.applyEffect(rendererObject, rendererEffects[effectData.name]) && + effectAdded; + return effectAdded; + } + + /** + * Remove the effect with the specified name from a PixiJS DisplayObject. + * @param rendererEffects The collection of PixiJS filters. + * @param rendererObject The renderer object. + * @param effectName The name of the effect. + */ + removeEffect( + rendererEffects: RendererEffects, + rendererObject: PIXI.DisplayObject, + effectName: string + ): boolean { + const filter = rendererEffects[effectName]; + if (!filter) return false; + rendererObject.filters = (rendererObject.filters || []).filter( + (pixiFilter) => pixiFilter !== filter.pixiFilter + ); + delete rendererEffects[effectName]; + return true; + } + + /** + * Update the parameter of an effect (with a number). + * @param rendererEffects The collection of PixiJS filters. + * @param name The effect name + * @param parameterName The parameter name + * @param value The new value for the parameter + */ + setEffectDoubleParameter( + rendererEffects: RendererEffects, + name: string, + parameterName: string, + value: float + ): boolean { + const filter = rendererEffects[name]; + if (!filter) return false; + filter.updateDoubleParameter(filter.pixiFilter, parameterName, value); + return true; + } + + /** + * Update the parameter of an effect (with a string). + * @param rendererEffects The collection of PixiJS filters. + * @param name The effect name + * @param parameterName The parameter name + * @param value The new value for the parameter + */ + setEffectStringParameter( + rendererEffects: RendererEffects, + name: string, + parameterName: string, + value: string + ): boolean { + const filter = rendererEffects[name]; + if (!filter) return false; + filter.updateStringParameter(filter.pixiFilter, parameterName, value); + return true; + } + + /** + * Enable or disable the parameter of an effect (boolean). + * @param rendererEffects The collection of PixiJS filters. + * @param name The effect name + * @param parameterName The parameter name + * @param value The new value for the parameter + */ + setEffectBooleanParameter( + rendererEffects: RendererEffects, + name: string, + parameterName: string, + value: boolean + ): boolean { + const filter = rendererEffects[name]; + if (!filter) return false; + filter.updateBooleanParameter(filter.pixiFilter, parameterName, value); + return true; + } + + /** + * Updates all the effect parameters. + * @param rendererEffects + * @param effectData + */ + updateAllEffectParameters( + rendererEffects: RendererEffects, + effectData: EffectData + ): boolean { + let updatedDoubles = true; + let updatedStrings = true; + let updatedBooleans = true; + for (let name in effectData.doubleParameters) { + updatedDoubles = + this.setEffectDoubleParameter( + rendererEffects, + effectData.name, + name, + effectData.doubleParameters[name] + ) && updatedDoubles; + } + for (let name in effectData.stringParameters) { + updatedStrings = + this.setEffectStringParameter( + rendererEffects, + effectData.name, + name, + effectData.stringParameters[name] + ) && updatedStrings; + } + for (let name in effectData.booleanParameters) { + updatedBooleans = + this.setEffectBooleanParameter( + rendererEffects, + effectData.name, + name, + effectData.booleanParameters[name] + ) && updatedBooleans; + } + + return updatedDoubles && updatedStrings && updatedBooleans; + } + + /** + * Check if an effect exists. + * @param rendererEffects The collection of PixiJS filters. + * @param name The effect name + * @returns True if the effect exists, false otherwise + */ + hasEffect(rendererEffects: RendererEffects, name: string): boolean { + return !!rendererEffects[name]; + } + + /** + * Enable an effect. + * @param rendererEffects The collection of PixiJS filters. + * @param name The effect name + * @param value Set to true to enable, false to disable + */ + enableEffect( + rendererEffects: RendererEffects, + name: string, + value: boolean + ): void { + const filter = rendererEffects[name]; + if (!filter) return; + gdjs.PixiFiltersTools.enableEffect(filter, value); + } + + /** + * Check if an effect is enabled. + * @param rendererEffects The collection of PixiJS filters. + * @param name The effect name + * @return true if the filter is enabled + */ + isEffectEnabled(rendererEffects: RendererEffects, name: string): boolean { + const filter = rendererEffects[name]; + if (!filter) return false; + return gdjs.PixiFiltersTools.isEffectEnabled(filter); + } + } + + // Expose the effect manager to the game engine. + export const EffectsManager = PixiEffectsManager; + export type EffectsManager = PixiEffectsManager; +} diff --git a/GDJS/Runtime/pixi-renderers/pixi-filters-tools.ts b/GDJS/Runtime/pixi-renderers/pixi-filters-tools.ts index 4a3dc4738c61..492c0587f71f 100644 --- a/GDJS/Runtime/pixi-renderers/pixi-filters-tools.ts +++ b/GDJS/Runtime/pixi-renderers/pixi-filters-tools.ts @@ -89,9 +89,9 @@ namespace gdjs { /** A wrapper allowing to create a PIXI filter and update it using a common interface */ export type FilterCreator = { /** Function to call to create the filter */ - makePIXIFilter: (layer: gdjs.Layer, effectData: EffectData) => any; - /** The function to be called to update the filter at every frame */ - update: (filter: PIXI.Filter, layer: gdjs.Layer) => any; + makePIXIFilter: (target: EffectsTarget, effectData: EffectData) => any; + /** The function to be called to update the filter at every frame before the rendering. */ + updatePreRender: (filter: PIXI.Filter, target: gdjs.EffectsTarget) => any; /** The function to be called to update a parameter (with a number) */ updateDoubleParameter: ( filter: PIXI.Filter, @@ -116,8 +116,8 @@ namespace gdjs { export type Filter = { /** The PIXI filter */ pixiFilter: PIXI.Filter; - /** The function to be called to update the filter at every frame */ - update: (filter: PIXI.Filter, layer: gdjs.Layer) => any; + /** The function to be called to update the filter at every frame before the rendering. */ + updatePreRender: (filter: PIXI.Filter, target: gdjs.EffectsTarget) => any; /** The function to be called to update a parameter (with a number) */ updateDoubleParameter: ( filter: PIXI.Filter, diff --git a/GDJS/Runtime/runtimegame.ts b/GDJS/Runtime/runtimegame.ts index e43e63dc569a..ca7e26b4ee94 100644 --- a/GDJS/Runtime/runtimegame.ts +++ b/GDJS/Runtime/runtimegame.ts @@ -40,6 +40,7 @@ namespace gdjs { _soundManager: SoundManager; _fontManager: FontManager; _jsonManager: JsonManager; + _effectsManager: EffectsManager; _bitmapFontManager: BitmapFontManager; _maxFPS: integer; _minFPS: integer; @@ -94,6 +95,7 @@ namespace gdjs { this._data.resources.resources, this._imageManager ); + this._effectsManager = new gdjs.EffectsManager(); this._maxFPS = this._data ? this._data.properties.maxFPS : 60; this._minFPS = this._data ? this._data.properties.minFPS : 15; this._gameResolutionWidth = this._data.properties.windowWidth; @@ -201,6 +203,15 @@ namespace gdjs { return this._jsonManager; } + /** + * Get the effects manager of the game, which allows to manage + * effects on runtime objects or runtime layers. + * @return The effects manager for the game + */ + getEffectsManager(): gdjs.EffectsManager { + return this._effectsManager; + } + /** * Get the object containing the game data * @return The object associated to the game. diff --git a/GDJS/Runtime/runtimeobject.ts b/GDJS/Runtime/runtimeobject.ts index a9fcba20c236..18588a295967 100644 --- a/GDJS/Runtime/runtimeobject.ts +++ b/GDJS/Runtime/runtimeobject.ts @@ -33,7 +33,7 @@ namespace gdjs { * A `gdjs.RuntimeObject` should not be instantiated directly, always a child class * (because gdjs.RuntimeObject don't call onCreated at the end of its constructor). */ - export class RuntimeObject { + export class RuntimeObject implements EffectsTarget { name: string; type: string; x: float = 0; @@ -70,6 +70,9 @@ namespace gdjs { //Variables: protected _variables: gdjs.VariablesContainer; + //Effects: + protected _rendererEffects: Record = {}; + //Forces: protected _forces: gdjs.Force[] = []; _averageForce: gdjs.Force; @@ -98,6 +101,13 @@ namespace gdjs { ); this._averageForce = new gdjs.Force(0, 0, 0); this._behaviorsTable = new Hashtable(); + for (let i = 0; i < objectData.effects.length; ++i) { + this._runtimeScene + .getGame() + .getEffectsManager() + .initializeEffect(objectData.effects[i], this._rendererEffects, this); + this.updateAllEffectParameters(objectData.effects[i]); + } //Also contains the behaviors: Used when a behavior is accessed by its name ( see getBehavior ). for (let i = 0, len = objectData.behaviors.length; i < len; ++i) { @@ -120,6 +130,16 @@ namespace gdjs { * (`RuntimeObject.prototype.onCreated.call(this);`). */ onCreated(): void { + for (const effectName in this._rendererEffects) { + this._runtimeScene + .getGame() + .getEffectsManager() + .applyEffect( + this.getRendererObject(), + this._rendererEffects[effectName] + ); + } + for (let i = 0; i < this._behaviors.length; ++i) { this._behaviors[i].onCreated(); } @@ -188,14 +208,20 @@ namespace gdjs { * * Objects can have different elapsed time if they are on layers with different time scales. * - * @param runtimeScene The RuntimeScene the object belongs to. + * @param runtimeScene The RuntimeScene the object belongs to (deprecated - can be omitted). */ - getElapsedTime(runtimeScene: gdjs.RuntimeScene): float { - //TODO: Memoize? - const theLayer = runtimeScene.getLayer(this.layer); + getElapsedTime(runtimeScene?: gdjs.RuntimeScene): float { + const theLayer = this._runtimeScene.getLayer(this.layer); return theLayer.getElapsedTime(); } + /** + * The gdjs.RuntimeScene the object belongs to. + */ + getRuntimeScene(): RuntimeScene { + return this._runtimeScene; + } + /** * Called once during the game loop, before events and rendering. * @param runtimeScene The gdjs.RuntimeScene the object belongs to. @@ -727,6 +753,159 @@ namespace gdjs { return this._variables.has(name); } + /** + * Returns the collection of effects to be rendered by the + * underlying renderer. + * @returns The renderer effects. + */ + getRendererEffects() { + return this._rendererEffects; + } + + /** + * Add a new effect, or replace the one with the same name. + * @param effectData The data describing the effect to add. + */ + addEffect(effectData: EffectData): boolean { + return this._runtimeScene + .getGame() + .getEffectsManager() + .addEffect( + effectData, + this._rendererEffects, + this.getRendererObject(), + this + ); + } + + /** + * Remove the effect with the specified name + * @param effectName The name of the effect. + */ + removeEffect(effectName: string): boolean { + return this._runtimeScene + .getGame() + .getEffectsManager() + .removeEffect( + this._rendererEffects, + this.getRendererObject(), + effectName + ); + } + + /** + * Change an effect parameter value (for parameters that are numbers). + * @param name The name of the effect to update. + * @param parameterName The name of the parameter to update. + * @param value The new value (number). + */ + setEffectDoubleParameter( + name: string, + parameterName: string, + value: float + ): boolean { + return this._runtimeScene + .getGame() + .getEffectsManager() + .setEffectDoubleParameter( + this._rendererEffects, + name, + parameterName, + value + ); + } + + /** + * Change an effect parameter value (for parameters that are strings). + * @param name The name of the effect to update. + * @param parameterName The name of the parameter to update. + * @param value The new value (string). + */ + setEffectStringParameter( + name: string, + parameterName: string, + value: string + ): boolean { + return this._runtimeScene + .getGame() + .getEffectsManager() + .setEffectStringParameter( + this._rendererEffects, + name, + parameterName, + value + ); + } + + /** + * Change an effect parameter value (for parameters that are booleans). + * @param name The name of the effect to update. + * @param parameterName The name of the parameter to update. + * @param value The new value (boolean). + */ + setEffectBooleanParameter( + name: string, + parameterName: string, + value: boolean + ): boolean { + return this._runtimeScene + .getGame() + .getEffectsManager() + .setEffectBooleanParameter( + this._rendererEffects, + name, + parameterName, + value + ); + } + + /** + * Updates all the parameters of an effect. + * @param effectData The data describing the effect + */ + updateAllEffectParameters(effectData: EffectData): boolean { + return this._runtimeScene + .getGame() + .getEffectsManager() + .updateAllEffectParameters(this._rendererEffects, effectData); + } + + /** + * Enable or disable an effect. + * @param name The name of the effect to enable or disable. + * @param enable true to enable, false to disable + */ + enableEffect(name: string, enable: boolean): void { + this._runtimeScene + .getGame() + .getEffectsManager() + .enableEffect(this._rendererEffects, name, enable); + } + + /** + * Check if an effect is enabled + * @param name The name of the effect + * @return true if the effect is enabled, false otherwise. + */ + isEffectEnabled(name: string): boolean { + return this._runtimeScene + .getGame() + .getEffectsManager() + .isEffectEnabled(this._rendererEffects, name); + } + + /** + * Check if an effect exists on this object + * @param name The name of the effect + * @return true if the effect exists, false otherwise. + */ + hasEffect(name: string): boolean { + return this._runtimeScene + .getGame() + .getEffectsManager() + .hasEffect(this._rendererEffects, name); + } + /** * Hide (or show) the object. * @param enable Set it to true to hide the object, false to show it. diff --git a/GDJS/Runtime/runtimescene.ts b/GDJS/Runtime/runtimescene.ts index 184dd58e5220..5ba914632fd4 100644 --- a/GDJS/Runtime/runtimescene.ts +++ b/GDJS/Runtime/runtimescene.ts @@ -509,16 +509,16 @@ namespace gdjs { this._profiler.end('callbacks and extensions (post-events)'); } if (this._profiler) { - this._profiler.begin('objects (pre-render)'); + this._profiler.begin('objects (pre-render, effects update)'); } this._updateObjectsPreRender(); if (this._profiler) { - this._profiler.end('objects (pre-render)'); + this._profiler.end('objects (pre-render, effects update)'); } if (this._profiler) { this._profiler.begin('layers (effects update)'); } - this._updateLayers(); + this._updateLayersPreRender(); if (this._profiler) { this._profiler.end('layers (effects update)'); } @@ -577,19 +577,22 @@ namespace gdjs { } } - _updateLayers() { + /** + * Called to update effects of layers before rendering. + */ + _updateLayersPreRender() { for (const name in this._layers.items) { if (this._layers.items.hasOwnProperty(name)) { - /** @type gdjs.Layer */ - const theLayer: gdjs.Layer = this._layers.items[name]; - theLayer.update(this); + const layer = this._layers.items[name]; + layer.updatePreRender(this); } } } /** * Called to update visibility of the renderers of objects - * rendered on the scene and give a last chance for objects to update before rendering. + * rendered on the scene ("culling"), update effects (of visible objects) + * and give a last chance for objects to update before rendering. * * Visibility is set to false if object is hidden, or if * object is too far from the camera of its layer ("culling"). @@ -601,8 +604,16 @@ namespace gdjs { const object = this._allInstancesList[i]; const rendererObject = object.getRendererObject(); if (rendererObject) { - object.getRendererObject().visible = !object.isHidden(); + rendererObject.visible = !object.isHidden(); + + // Update effects, only for visible objects. + if (rendererObject.visible) { + this._runtimeGame + .getEffectsManager() + .updatePreRender(object.getRendererEffects(), object); + } } + // Perform pre-render update. object.updatePreRender(this); } @@ -613,9 +624,11 @@ namespace gdjs { // TODO: For compatibility, pass a scale of `2`, // meaning that size of cameras will be multiplied by 2 and so objects // will be hidden if they are outside of this *larger* camera area. - // Useful for objects not properly reporting their visibility AABB, + // This is useful for: + // - objects not properly reporting their visibility AABB, // (so we have a "safety margin") but these objects should be fixed // instead. + // - objects having effects rendering outside of their visibility AABB. this._updateLayersCameraCoordinates(2); this._constructListOfAllInstances(); for (let i = 0, len = this._allInstancesList.length; i < len; ++i) { @@ -642,6 +655,14 @@ namespace gdjs { rendererObject.visible = true; } } + + // Update effects, only for visible objects. + if (rendererObject.visible) { + this._runtimeGame + .getEffectsManager() + .updatePreRender(object.getRendererEffects(), object); + } + // Perform pre-render update. object.updatePreRender(this); } diff --git a/GDJS/Runtime/websocket-debugger-client/hot-reloader.ts b/GDJS/Runtime/websocket-debugger-client/hot-reloader.ts index 0bf75540a863..0dbf6e3d5f78 100644 --- a/GDJS/Runtime/websocket-debugger-client/hot-reloader.ts +++ b/GDJS/Runtime/websocket-debugger-client/hot-reloader.ts @@ -791,7 +791,7 @@ namespace gdjs { hotReloadSucceeded; }); - // Don't update the variables and behaviors for each runtime object to avoid + // Don't update the variables, behaviors and effects for each runtime object to avoid // doing the check for differences for every single object. // Update variables @@ -809,6 +809,13 @@ namespace gdjs { newObjectData.behaviors, runtimeObjects ); + + // Update effects + this._hotReloadRuntimeObjectsEffects( + oldObjectData.effects, + newObjectData.effects, + runtimeObjects + ); } if (!hotReloadSucceeded) { this._logs.push({ @@ -898,6 +905,80 @@ namespace gdjs { }); } + _hotReloadRuntimeObjectsEffects( + oldEffects: EffectData[], + newEffects: EffectData[], + runtimeObjects: RuntimeObject[] + ): void { + oldEffects.forEach((oldEffectData) => { + const name = oldEffectData.name; + const newEffectData = newEffects.filter( + (effectData) => effectData.name === name + )[0]; + if (!newEffectData) { + // Effect was removed. + runtimeObjects.forEach((runtimeObject) => { + if (runtimeObject.hasEffect(name)) { + if (!runtimeObject.removeEffect(name)) { + this._logs.push({ + kind: 'error', + message: + 'Effect ' + + name + + ' could not be removed from object' + + runtimeObject.getName(), + }); + } + } + }); + } else { + if (!HotReloader.deepEqual(oldEffectData, newEffectData)) { + let hotReloadSucceeded = true; + runtimeObjects.forEach((runtimeObject) => { + if (oldEffectData.effectType === newEffectData.effectType) { + hotReloadSucceeded = + runtimeObject.updateAllEffectParameters(newEffectData) && + hotReloadSucceeded; + } else { + // Another effect type was applied + runtimeObject.removeEffect(oldEffectData.name); + runtimeObject.addEffect(newEffectData); + } + }); + if (!hotReloadSucceeded) { + this._logs.push({ + kind: 'error', + message: + newEffectData.name + ' effect could not be hot-reloaded.', + }); + } + } + } + }); + newEffects.forEach((newEffectData) => { + const name = newEffectData.name; + const oldEffectData = oldEffects.filter( + (oldEffectData) => oldEffectData.name === name + )[0]; + if (!oldEffectData) { + // Effect was added + let hotReloadSucceeded = true; + runtimeObjects.forEach((runtimeObject) => { + hotReloadSucceeded = + runtimeObject.addEffect(newEffectData) && hotReloadSucceeded; + }); + if (!hotReloadSucceeded) { + this._logs.push({ + kind: 'error', + message: + newEffectData.name + + ' effect could not be added during hot-reload.', + }); + } + } + }); + } + /** * @returns true if hot-reload succeeded, false otherwise. */ diff --git a/GDJS/tests/benchmarks/runtimeobject.js b/GDJS/tests/benchmarks/runtimeobject.js index a551884cb5f8..32b20db99f51 100644 --- a/GDJS/tests/benchmarks/runtimeobject.js +++ b/GDJS/tests/benchmarks/runtimeobject.js @@ -3,7 +3,7 @@ describe('gdjs.RuntimeObject', function() { it('benchmark getAABB of rotated vs non rotated objects', function(){ this.timeout(20000); - var object = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); + var object = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); object.getWidth = function() { return 10; }; object.getHeight = function() { return 20; }; object.setPosition(15, 20); @@ -27,7 +27,7 @@ describe('gdjs.RuntimeObject', function() { it('benchmark getAABB of rotated vs non rotated objects, with non default center', function(){ this.timeout(20000); - var object = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); + var object = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); object.getWidth = function() { return 10; }; object.getHeight = function() { return 20; }; object.getCenterX = function() { return 0 }; @@ -52,4 +52,4 @@ describe('gdjs.RuntimeObject', function() { console.log(benchmarkSuite.run()); }); -}); \ No newline at end of file +}); diff --git a/GDJS/tests/benchmarks/spriteruntimeobject.js b/GDJS/tests/benchmarks/spriteruntimeobject.js index 6c79d9bb7825..f28b04f02f17 100644 --- a/GDJS/tests/benchmarks/spriteruntimeobject.js +++ b/GDJS/tests/benchmarks/spriteruntimeobject.js @@ -16,6 +16,7 @@ describe('gdjs.SpriteRuntimeObject', function () { updateIfNotVisible: false, variables: [], behaviors: [], + effects: [], animations: [ { name: 'NewObject2', diff --git a/GDJS/tests/games/Sprite update test/game.json b/GDJS/tests/games/Sprite update test/game.json index 524a08303cfb..c57bbec10a2e 100644 --- a/GDJS/tests/games/Sprite update test/game.json +++ b/GDJS/tests/games/Sprite update test/game.json @@ -356,6 +356,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "sprite1", @@ -398,6 +399,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Press any key to reload the scene\nGreen objects loaded from external layout are displaced at first and flicker\nRed objects belongs to this scene are fine", "font": "", "characterSize": 20, @@ -414,6 +416,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "sprite2", @@ -456,6 +459,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Text from external Layout seems fine, no flicker", "font": "", "characterSize": 20, @@ -650,6 +654,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "Hurt", @@ -881,6 +886,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "The idle animation should be shown immediately. \nThe first \"hurt\" animation should not be visible even when the scene is just changed.", "font": "", "characterSize": 20, @@ -1206,4 +1212,4 @@ } ], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/TweenBenchmark/game.json b/GDJS/tests/games/TweenBenchmark/game.json index d01f3593c9b4..2a4b9fa2ad68 100644 --- a/GDJS/tests/games/TweenBenchmark/game.json +++ b/GDJS/tests/games/TweenBenchmark/game.json @@ -4250,6 +4250,7 @@ "type": "TextObject::Text", "underlined": false, "variables": [], + "effects": [], "behaviors": [ { "name": "TweenBehavior", @@ -4275,6 +4276,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Load Sprite scene", "font": "", "characterSize": 40, @@ -7351,6 +7353,7 @@ "type": "Sprite", "updateIfNotVisible": false, "variables": [], + "effects": [], "behaviors": [ { "name": "TweenBehavior", @@ -7399,6 +7402,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Load Text scene", "font": "", "characterSize": 40, @@ -7627,4 +7631,4 @@ "eventsFunctionsExtensions": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/TweenTest/game.json b/GDJS/tests/games/TweenTest/game.json index 8052ff40d3c9..6c770839d469 100644 --- a/GDJS/tests/games/TweenTest/game.json +++ b/GDJS/tests/games/TweenTest/game.json @@ -789,6 +789,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Variable tweens", "font": "", "characterSize": 20, @@ -811,6 +812,7 @@ "value": "0" } ], + "effects": [], "behaviors": [ { "name": "TweenBehavior", @@ -839,6 +841,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Position tweens", "font": "", "characterSize": 20, @@ -858,6 +861,7 @@ "value": "0" } ], + "effects": [], "behaviors": [ { "name": "TweenBehavior", @@ -909,6 +913,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Position X tweens", "font": "", "characterSize": 20, @@ -927,6 +932,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "0", "font": "", "characterSize": 20, @@ -956,6 +962,7 @@ } } ], + "effects": [], "animations": [ { "name": "Sprite", @@ -997,6 +1004,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Position Y\n tweens", "font": "", "characterSize": 20, @@ -1016,6 +1024,7 @@ "value": "0" } ], + "effects": [], "behaviors": [ { "name": "TweenBehavior", @@ -1067,6 +1076,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Angle \ntweens", "font": "", "characterSize": 20, @@ -1096,6 +1106,7 @@ } } ], + "effects": [], "animations": [ { "name": "Sprite", @@ -1137,6 +1148,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Scale tweens", "font": "", "characterSize": 20, @@ -1166,6 +1178,7 @@ } } ], + "effects": [], "animations": [ { "name": "Sprite", @@ -1207,6 +1220,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Scale-X tweens", "font": "", "characterSize": 20, @@ -1236,6 +1250,7 @@ } } ], + "effects": [], "animations": [ { "name": "Sprite", @@ -1277,6 +1292,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Scale-Y\ntweens", "font": "", "characterSize": 20, @@ -1306,6 +1322,7 @@ } } ], + "effects": [], "animations": [ { "name": "Sprite", @@ -1347,6 +1364,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Opacity tweens", "font": "", "characterSize": 20, @@ -1366,6 +1384,7 @@ "value": "0" } ], + "effects": [], "behaviors": [ { "name": "TweenBehavior", @@ -1417,6 +1436,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Pause/resume tweens", "font": "", "characterSize": 20, @@ -1430,6 +1450,7 @@ "name": "PauseResumeTweenSprite", "type": "Sprite", "updateIfNotVisible": false, + "effects": [], "variables": [ { "name": "var", @@ -1487,6 +1508,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Colour tweens", "font": "", "characterSize": 20, @@ -1505,6 +1527,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "0", "font": "", "characterSize": 20, @@ -1524,6 +1547,7 @@ "value": "0" } ], + "effects": [], "behaviors": [ { "name": "TweenBehavior", @@ -1575,6 +1599,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Character size", "font": "", "characterSize": 20, @@ -1599,6 +1624,7 @@ "content": {} } ], + "effects": [], "string": "Hello world", "font": "", "characterSize": 20, @@ -1614,6 +1640,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "Background", @@ -1652,6 +1679,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "Background_Wip", @@ -4680,4 +4708,4 @@ "eventsFunctionsExtensions": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/audio-play-stop-condition/play then immediately check if sound is stopped.json b/GDJS/tests/games/audio-play-stop-condition/play then immediately check if sound is stopped.json index 1cbd8c89f9f7..49b4182621af 100755 --- a/GDJS/tests/games/audio-play-stop-condition/play then immediately check if sound is stopped.json +++ b/GDJS/tests/games/audio-play-stop-condition/play then immediately check if sound is stopped.json @@ -170,6 +170,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Scene1 - Audio Status", "font": "", "characterSize": 40, @@ -432,6 +433,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Scene2 - Audio status...", "font": "", "characterSize": 40, @@ -538,4 +540,4 @@ "eventsFunctionsExtensions": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/effects/all effects demo.json b/GDJS/tests/games/effects/all effects demo.json index 44ed8199924e..7e8a4235c888 100644 --- a/GDJS/tests/games/effects/all effects demo.json +++ b/GDJS/tests/games/effects/all effects demo.json @@ -3467,6 +3467,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "t1", @@ -4725,6 +4726,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -5487,4 +5489,4 @@ "eventsFunctionsExtensions": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/events-based-behaviors/Basic EventsBasedBehavior test.json b/GDJS/tests/games/events-based-behaviors/Basic EventsBasedBehavior test.json index 7c2eafd1db24..ca1b621b5a34 100644 --- a/GDJS/tests/games/events-based-behaviors/Basic EventsBasedBehavior test.json +++ b/GDJS/tests/games/events-based-behaviors/Basic EventsBasedBehavior test.json @@ -312,6 +312,7 @@ "type": "TextObject::Text", "underlined": false, "variables": [], + "effects": [], "behaviors": [ { "name": "MyBehavior", @@ -338,6 +339,7 @@ "type": "TextObject::Text", "underlined": false, "variables": [], + "effects": [], "behaviors": [ { "name": "MyBehavior", @@ -362,6 +364,7 @@ "type": "TextObject::Text", "underlined": false, "variables": [], + "effects": [], "behaviors": [ { "name": "MyBehavior", @@ -390,6 +393,7 @@ "type": "TextObject::Text", "underlined": false, "variables": [], + "effects": [], "behaviors": [ { "name": "MyBehavior", @@ -430,6 +434,7 @@ "BooleanTestProperty": true } ], + "effects": [], "string": "This should be changed by MyBehavior, using ThirdBehavior of NewObject6. (seeing this is a FAIL)", "font": "", "characterSize": 20, @@ -454,6 +459,7 @@ "type": "NewEventsFunctionsExtension::ThirdBehavior" } ], + "effects": [], "string": "Unmodified text, just having ThirdBehavior (success 6)", "font": "", "characterSize": 20, @@ -485,6 +491,7 @@ "BooleanTestProperty": true } ], + "effects": [], "string": "This should be changed by MyBehavior, using \"BehaviorUsingMyBehaviorFromOtherExtension\" of the same object. (seeing this is a FAIL)", "font": "", "characterSize": 20, @@ -509,6 +516,7 @@ "type": "NewEventsFunctionsExtension::ThirdBehavior" } ], + "effects": [], "string": "Unmodified text, just having ThirdBehavior (success 8)", "font": "", "characterSize": 20, @@ -536,6 +544,7 @@ "BooleanTestProperty": true } ], + "effects": [], "string": "SHould be Modified by a function (seeing this is a FAIL)", "font": "", "characterSize": 20, @@ -563,6 +572,7 @@ "BooleanTestProperty": true } ], + "effects": [], "string": "SHould be Modified by NewObject 8 (seeing this is a FAIL)", "font": "", "characterSize": 20, @@ -590,6 +600,7 @@ "BooleanTestProperty": true } ], + "effects": [], "string": "This should rotate (if not FAIL, otherwise success 11)", "font": "", "characterSize": 20, @@ -1737,4 +1748,4 @@ ], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/events-function (and events-functions-extensions)/platformer with events functions.json b/GDJS/tests/games/events-function (and events-functions-extensions)/platformer with events functions.json index 2b245417990c..1f43fca39064 100644 --- a/GDJS/tests/games/events-function (and events-functions-extensions)/platformer with events functions.json +++ b/GDJS/tests/games/events-function (and events-functions-extensions)/platformer with events functions.json @@ -1860,6 +1860,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -2156,6 +2157,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "canBeGrabbed": true, @@ -2269,6 +2271,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "canBeGrabbed": true, @@ -2336,6 +2339,7 @@ "type": "TiledSpriteObject::TiledSprite", "width": 70, "variables": [], + "effects": [], "behaviors": [ { "canBeGrabbed": true, @@ -2353,6 +2357,7 @@ "type": "TiledSpriteObject::TiledSprite", "width": 70, "variables": [], + "effects": [], "behaviors": [ { "canBeGrabbed": true, @@ -2368,6 +2373,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "canBeGrabbed": true, @@ -2434,6 +2440,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -2491,6 +2498,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -2549,6 +2557,7 @@ "type": "TiledSpriteObject::TiledSprite", "width": 70, "variables": [], + "effects": [], "behaviors": [ { "canBeGrabbed": true, @@ -2564,6 +2573,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "acceleration": 1500, @@ -2642,6 +2652,7 @@ "value": "0" } ], + "effects": [], "behaviors": [ { "acceleration": 5000, @@ -2799,6 +2810,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "acceleration": 500, @@ -2956,6 +2968,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [], "animations": [ { @@ -3109,6 +3122,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [], "animations": [ { @@ -3265,6 +3279,7 @@ "type": "TextObject::Text", "underlined": false, "variables": [], + "effects": [], "behaviors": [], "string": "x 0", "font": "Bimbo_JVE.ttf", @@ -3280,6 +3295,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [], "animations": [ { @@ -3337,6 +3353,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [], "animations": [ { @@ -3394,6 +3411,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [], "animations": [ { @@ -3451,6 +3469,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [], "animations": [ { @@ -3508,6 +3527,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "bottomEdgeAnchor": 0, @@ -3575,6 +3595,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [], "animations": [ { @@ -5430,4 +5451,4 @@ ], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/events-functions-nested-create/game.json b/GDJS/tests/games/events-functions-nested-create/game.json index 2ba9ffc6666a..2eab7bc45d3f 100644 --- a/GDJS/tests/games/events-functions-nested-create/game.json +++ b/GDJS/tests/games/events-functions-nested-create/game.json @@ -149,6 +149,7 @@ "type": "Sprite", "updateIfNotVisible": false, "variables": [], + "effects": [], "behaviors": [], "animations": [ { @@ -357,4 +358,4 @@ ], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/memory-benchmarks/Collisions with creation and deletion benchmark.gdg.json b/GDJS/tests/games/memory-benchmarks/Collisions with creation and deletion benchmark.gdg.json index 15a48c9d63d7..57bfa963198a 100644 --- a/GDJS/tests/games/memory-benchmarks/Collisions with creation and deletion benchmark.gdg.json +++ b/GDJS/tests/games/memory-benchmarks/Collisions with creation and deletion benchmark.gdg.json @@ -202,6 +202,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "NewObject", @@ -241,6 +242,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "NewObject2", @@ -280,6 +282,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "NewObject3", @@ -322,6 +325,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Text", "font": "", "characterSize": 20, @@ -557,4 +561,4 @@ "eventsFunctionsExtensions": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/objects-benchmarks/rotating objects.json b/GDJS/tests/games/objects-benchmarks/rotating objects.json index a4ba2ee9b570..580ab7e1cd0a 100644 --- a/GDJS/tests/games/objects-benchmarks/rotating objects.json +++ b/GDJS/tests/games/objects-benchmarks/rotating objects.json @@ -163,6 +163,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -202,6 +203,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -403,4 +405,4 @@ "eventsFunctionsExtensions": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/pin-object-to-another (distance and put around)/pin-object-to-another.json b/GDJS/tests/games/pin-object-to-another (distance and put around)/pin-object-to-another.json index d8d637b05c60..1d45e31c5f1e 100644 --- a/GDJS/tests/games/pin-object-to-another (distance and put around)/pin-object-to-another.json +++ b/GDJS/tests/games/pin-object-to-another (distance and put around)/pin-object-to-another.json @@ -272,6 +272,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Add child objects in the editor and position, scale and rotate them as you like.\nIn preview, rotate the parent using A and S keys and drag the parent with the mouse.\nAll child object should keep it relative rotation and position to the parent object.", "font": "", "characterSize": 20, @@ -286,6 +287,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "name": "Draggable", @@ -349,6 +351,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -685,4 +688,4 @@ "externalEvents": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/pin-object-to-another-multiple-parents (linked objects, distance and put around)/pin-object-to-another-multiple-parents.json b/GDJS/tests/games/pin-object-to-another-multiple-parents (linked objects, distance and put around)/pin-object-to-another-multiple-parents.json index 36ce78df9db6..ba098a60c94e 100644 --- a/GDJS/tests/games/pin-object-to-another-multiple-parents (linked objects, distance and put around)/pin-object-to-another-multiple-parents.json +++ b/GDJS/tests/games/pin-object-to-another-multiple-parents (linked objects, distance and put around)/pin-object-to-another-multiple-parents.json @@ -342,6 +342,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Add child objects in the editor and position, scale and rotate them as you like.\nAdd a variable \"parent\" to each child object and a variable \"id\" to its parent.\nThe value of this variable must be the same, in order to have the child choose to which it belongs.\nIn preview, move the pointer over a parent, then you can rotate the parent using A and S keys.\nYou can also drag the parent with the mouse.\nAll child objects should keep its relative rotation and position to the parent object they belong to.", "font": "", "characterSize": 18, @@ -356,6 +357,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "name": "Draggable", @@ -419,6 +421,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -1077,4 +1080,4 @@ ], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/platformer sandbox/platformer sandbox.json b/GDJS/tests/games/platformer sandbox/platformer sandbox.json index 40c6e1183506..e18d2809211d 100644 --- a/GDJS/tests/games/platformer sandbox/platformer sandbox.json +++ b/GDJS/tests/games/platformer sandbox/platformer sandbox.json @@ -983,6 +983,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -1280,6 +1281,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "name": "Platform", @@ -1403,6 +1405,7 @@ "yGrabOffset": 0 } ], + "effects": [], "animations": [ { "name": "", @@ -1462,6 +1465,7 @@ "type": "TiledSpriteObject::TiledSprite", "width": 70, "variables": [], + "effects": [], "behaviors": [ { "name": "Platform", @@ -1480,6 +1484,7 @@ "type": "TiledSpriteObject::TiledSprite", "width": 70, "variables": [], + "effects": [], "behaviors": [ { "name": "Platform", @@ -1496,6 +1501,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "name": "Platform", @@ -1572,6 +1578,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "name": "Platform", @@ -1649,6 +1656,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -1707,6 +1715,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -1766,6 +1775,7 @@ "type": "TiledSpriteObject::TiledSprite", "width": 70, "variables": [], + "effects": [], "behaviors": [ { "name": "Platform", @@ -1782,6 +1792,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "name": "PlatformerObject", @@ -1862,6 +1873,7 @@ "value": "0" } ], + "effects": [], "behaviors": [ { "name": "PlatformerObject", @@ -2021,6 +2033,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "name": "PlatformerObject", @@ -2181,6 +2194,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -2335,6 +2349,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -2492,6 +2507,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "x 0", "font": "Bimbo_JVE.ttf", "characterSize": 40, @@ -2508,6 +2524,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -2566,6 +2583,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -2624,6 +2642,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -2682,6 +2701,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -2739,6 +2759,7 @@ "type": "Sprite", "updateIfNotVisible": true, "variables": [], + "effects": [], "behaviors": [ { "name": "Anchor", @@ -2808,6 +2829,7 @@ "updateIfNotVisible": true, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -4712,4 +4734,4 @@ ], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/points-and-changing-animations-tests/Create an object at a point just after animation is changed (simple).json b/GDJS/tests/games/points-and-changing-animations-tests/Create an object at a point just after animation is changed (simple).json index 673e06b2020f..132946415a10 100644 --- a/GDJS/tests/games/points-and-changing-animations-tests/Create an object at a point just after animation is changed (simple).json +++ b/GDJS/tests/games/points-and-changing-animations-tests/Create an object at a point just after animation is changed (simple).json @@ -290,6 +290,7 @@ "value": "3" } ], + "effects": [], "behaviors": [ { "name": "TopDownMovement", @@ -384,6 +385,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -481,6 +483,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "", @@ -760,4 +763,4 @@ "eventsFunctionsExtensions": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/points-and-changing-animations-tests/Create an object at a point just after animation is changed.json b/GDJS/tests/games/points-and-changing-animations-tests/Create an object at a point just after animation is changed.json index 8ac61618c03c..5991d403f4ed 100644 --- a/GDJS/tests/games/points-and-changing-animations-tests/Create an object at a point just after animation is changed.json +++ b/GDJS/tests/games/points-and-changing-animations-tests/Create an object at a point just after animation is changed.json @@ -223,6 +223,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "NewObject", @@ -372,6 +373,7 @@ "type": "Sprite", "updateIfNotVisible": false, "variables": [], + "effects": [], "behaviors": [ { "name": "DestroyOutside", @@ -699,4 +701,4 @@ "eventsFunctionsExtensions": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/rotate-flip-around-center-point/ship-rotate-flip.json b/GDJS/tests/games/rotate-flip-around-center-point/ship-rotate-flip.json index 26e9d30b0f54..bc8bbfdd214f 100644 --- a/GDJS/tests/games/rotate-flip-around-center-point/ship-rotate-flip.json +++ b/GDJS/tests/games/rotate-flip-around-center-point/ship-rotate-flip.json @@ -232,6 +232,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "Ship", @@ -290,6 +291,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "NewObject", @@ -578,4 +580,4 @@ "eventsFunctionsExtensions": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/rotated-objects-hitboxes/game.json b/GDJS/tests/games/rotated-objects-hitboxes/game.json index 72a3958da1e9..dc8e36bc13b6 100644 --- a/GDJS/tests/games/rotated-objects-hitboxes/game.json +++ b/GDJS/tests/games/rotated-objects-hitboxes/game.json @@ -502,6 +502,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "NewObject", @@ -540,6 +541,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "NewObject2", @@ -578,6 +580,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "NewObject2", @@ -619,6 +622,7 @@ "underlined": false, "variables": [], "behaviors": [], + "effects": [], "string": "Text blabl balbalbal", "font": "", "characterSize": 20, @@ -634,6 +638,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "NewObject2", @@ -691,6 +696,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "NewObject2", @@ -730,7 +736,8 @@ "type": "TiledSpriteObject::TiledSprite", "width": 32, "variables": [], - "behaviors": [] + "behaviors": [], + "effects": [] } ], "events": [ @@ -776,4 +783,4 @@ "eventsFunctionsExtensions": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/size-and-changing-animation-tests/Size is properly reported just after an animation change.json b/GDJS/tests/games/size-and-changing-animation-tests/Size is properly reported just after an animation change.json index 6ca633deaaf5..e18df0c2fc43 100644 --- a/GDJS/tests/games/size-and-changing-animation-tests/Size is properly reported just after an animation change.json +++ b/GDJS/tests/games/size-and-changing-animation-tests/Size is properly reported just after an animation change.json @@ -187,6 +187,7 @@ "updateIfNotVisible": false, "variables": [], "behaviors": [], + "effects": [], "animations": [ { "name": "Square", @@ -488,4 +489,4 @@ "eventsFunctionsExtensions": [], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/games/trigger-once-in-events-based-behaviors/Trigger Once in Behaviors.json b/GDJS/tests/games/trigger-once-in-events-based-behaviors/Trigger Once in Behaviors.json index 247cfe2cdc33..c58910571585 100644 --- a/GDJS/tests/games/trigger-once-in-events-based-behaviors/Trigger Once in Behaviors.json +++ b/GDJS/tests/games/trigger-once-in-events-based-behaviors/Trigger Once in Behaviors.json @@ -199,6 +199,7 @@ "type": "Sprite", "updateIfNotVisible": false, "variables": [], + "effects": [], "behaviors": [ { "name": "MyBehavior", @@ -632,4 +633,4 @@ ], "externalLayouts": [], "externalSourceFiles": [] -} \ No newline at end of file +} diff --git a/GDJS/tests/karma.conf.js b/GDJS/tests/karma.conf.js index e2305e4ebff4..9d6fd45ba626 100644 --- a/GDJS/tests/karma.conf.js +++ b/GDJS/tests/karma.conf.js @@ -76,6 +76,8 @@ module.exports = function (config) { '../../newIDE/app/resources/GDJS/Runtime/Extensions/TopDownMovementBehavior/topdownobstacleruntimebehavior.js', '../../newIDE/app/resources/GDJS/Runtime/Extensions/Firebase/A_firebasejs/*.js', '../../newIDE/app/resources/GDJS/Runtime/Extensions/Firebase/B_firebasetools/*.js', + '../../newIDE/app/resources/GDJS/Runtime/Extensions/Effects/kawase-blur-pixi-filter.js', + '../../newIDE/app/resources/GDJS/Runtime/Extensions/Effects/pixi-filters/filter-kawase-blur.js', // Test extensions: './tests/Extensions/**.js', diff --git a/GDJS/tests/tests/common.js b/GDJS/tests/tests/common.js index fbf3497e65d7..e3a2fdda7d93 100644 --- a/GDJS/tests/tests/common.js +++ b/GDJS/tests/tests/common.js @@ -14,12 +14,12 @@ describe('gdjs.evtTools.object.twoListsTest', function() { var map2 = new Hashtable(); var runtimeScene = new gdjs.RuntimeScene(null); - var obj1A = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj1B = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj1C = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj2A = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: []}); - var obj2B = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: []}); - var obj2C = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: []}); + var obj1A = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj1B = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj1C = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj2A = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: [], effects: []}); + var obj2B = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: [], effects: []}); + var obj2C = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: [], effects: []}); var list1 = [obj1A, obj1B, obj1C]; var list2 = [obj2A, obj2B, obj2C]; @@ -49,9 +49,9 @@ describe('gdjs.evtTools.object.pickObjectsIf', function() { var map1 = new Hashtable(); var runtimeScene = new gdjs.RuntimeScene(null); - var obj1A = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj1B = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj1C = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); + var obj1A = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj1B = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj1C = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); var list1 = [obj1A, obj1B, obj1C]; map1.put("obj1", list1); @@ -73,11 +73,11 @@ describe('gdjs.evtTools.object.pickObjectsIf', function() { describe('gdjs.evtTools.object.pickRandomObject', function() { it('should pick only one object', function(){ var runtimeScene = new gdjs.RuntimeScene(null); - var obj1A = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj1B = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj1C = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj2A = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: []}); - var obj2B = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: []}); + var obj1A = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj1B = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj1C = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj2A = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: [], effects: []}); + var obj2B = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: [], effects: []}); var map1 = new Hashtable(); var list1 = [obj1A, obj1B, obj1C]; @@ -103,11 +103,11 @@ describe('gdjs.evtTools.object.pickRandomObject', function() { describe('gdjs.evtTools.object.pickOnly', function() { it('picks only the object passed as parameter', function(){ var runtimeScene = new gdjs.RuntimeScene(null); - var obj1A = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj1B = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj1C = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj2A = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: []}); - var obj2B = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: []}); + var obj1A = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj1B = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj1C = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj2A = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: [], effects: []}); + var obj2B = new gdjs.RuntimeObject(runtimeScene, {name: "obj2", type: "", behaviors: [], effects: []}); var map1 = new Hashtable(); map1.put("obj1", [obj1A, obj1B, obj1C]); @@ -139,9 +139,9 @@ describe('gdjs.evtTools.object.pickNearestObject', function() { var map1 = new Hashtable(); var runtimeScene = new gdjs.RuntimeScene(null); - var obj1A = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj1B = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); - var obj1C = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: []}); + var obj1A = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj1B = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); + var obj1C = new gdjs.RuntimeObject(runtimeScene, {name: "obj1", type: "", behaviors: [], effects: []}); obj1A.setPosition(50, 50); obj1B.setPosition(160, 160); obj1C.setPosition(100, 300); diff --git a/GDJS/tests/tests/effects.js b/GDJS/tests/tests/effects.js new file mode 100644 index 000000000000..4deaa10bdeb0 --- /dev/null +++ b/GDJS/tests/tests/effects.js @@ -0,0 +1,163 @@ +// @ts-check + +describe('gdjs.EffectsManager', () => { + const runtimeGame = new gdjs.RuntimeGame({ + variables: [], + // @ts-ignore TODO: make a function to create an empty game and use it across tests. + properties: { windowWidth: 800, windowHeight: 600 }, + resources: { resources: [] }, + }); + + it('can add effects on a runtime object', () => { + const runtimeScene = new gdjs.RuntimeScene(runtimeGame); + const object = new gdjs.TestRuntimeObject(runtimeScene, { + name: 'obj1', + type: '', + variables: [], + behaviors: [], + effects: [ + { + name: 'InitialKawaseBlurEffect', + effectType: 'KawaseBlur', + stringParameters: {}, + booleanParameters: {}, + doubleParameters: { + pixelizeX: 1, + pixelizeY: 2, + blur: 3, + quality: 4, + }, + }, + ], + }); + expect(object.hasEffect('NonExistingEffect')).to.be(false); + expect(object.hasEffect('InitialKawaseBlurEffect')).to.be(true); + expect(object.isEffectEnabled('NonExistingEffect')).to.be(false); + expect(object.isEffectEnabled('InitialKawaseBlurEffect')).to.be(true); + + object.enableEffect('InitialKawaseBlurEffect', false); + expect(object.isEffectEnabled('InitialKawaseBlurEffect')).to.be(false); + object.enableEffect('InitialKawaseBlurEffect', true); + expect(object.isEffectEnabled('InitialKawaseBlurEffect')).to.be(true); + + object.setEffectDoubleParameter('InitialKawaseBlurEffect', 'pixelizeX', 10); + object.setEffectStringParameter( + 'InitialKawaseBlurEffect', + 'useless', + 'will-be-ignored' + ); + object.setEffectBooleanParameter( + 'InitialKawaseBlurEffect', + 'useless', + true + ); + + object.addEffect({ + name: 'AddedKawaseBlurEffect', + effectType: 'KawaseBlur', + stringParameters: {}, + booleanParameters: {}, + doubleParameters: { + pixelizeX: 1, + pixelizeY: 2, + blur: 3, + quality: 4, + }, + }); + expect(object.hasEffect('AddedKawaseBlurEffect')).to.be(true); + expect(object.hasEffect('InitialKawaseBlurEffect')).to.be(true); + expect(object.isEffectEnabled('AddedKawaseBlurEffect')).to.be(true); + expect(object.isEffectEnabled('InitialKawaseBlurEffect')).to.be(true); + }); + + it('can add effects on a runtime layer', () => { + const runtimeScene = new gdjs.RuntimeScene(runtimeGame); + runtimeScene.loadFromScene({ + layers: [ + { + name: '', + visibility: true, + effects: [ + { + name: 'InitialKawaseBlurEffect', + effectType: 'KawaseBlur', + stringParameters: {}, + booleanParameters: {}, + doubleParameters: { + pixelizeX: 1, + pixelizeY: 2, + blur: 3, + quality: 4, + }, + }, + ], + cameras: [], + + ambientLightColorR: 0, + ambientLightColorG: 0, + ambientLightColorB: 0, + isLightingLayer: false, + followBaseLayerCamera: true, + }, + ], + variables: [], + r: 0, + v: 0, + b: 0, + mangledName: 'Scene1', + name: 'Scene1', + stopSoundsOnStartup: false, + title: '', + behaviorsSharedData: [], + objects: [], + instances: [], + }); + + const runtimeLayer = runtimeScene.getLayer(''); + + expect(runtimeLayer.hasEffect('NonExistingEffect')).to.be(false); + expect(runtimeLayer.hasEffect('InitialKawaseBlurEffect')).to.be(true); + expect(runtimeLayer.isEffectEnabled('NonExistingEffect')).to.be(false); + expect(runtimeLayer.isEffectEnabled('InitialKawaseBlurEffect')).to.be(true); + + runtimeLayer.enableEffect('InitialKawaseBlurEffect', false); + expect(runtimeLayer.isEffectEnabled('InitialKawaseBlurEffect')).to.be( + false + ); + runtimeLayer.enableEffect('InitialKawaseBlurEffect', true); + expect(runtimeLayer.isEffectEnabled('InitialKawaseBlurEffect')).to.be(true); + + runtimeLayer.setEffectDoubleParameter( + 'InitialKawaseBlurEffect', + 'pixelizeX', + 10 + ); + runtimeLayer.setEffectStringParameter( + 'InitialKawaseBlurEffect', + 'useless', + 'will-be-ignored' + ); + runtimeLayer.setEffectBooleanParameter( + 'InitialKawaseBlurEffect', + 'useless', + true + ); + + runtimeLayer.addEffect({ + name: 'AddedKawaseBlurEffect', + effectType: 'KawaseBlur', + stringParameters: {}, + booleanParameters: {}, + doubleParameters: { + pixelizeX: 1, + pixelizeY: 2, + blur: 3, + quality: 4, + }, + }); + expect(runtimeLayer.hasEffect('AddedKawaseBlurEffect')).to.be(true); + expect(runtimeLayer.hasEffect('InitialKawaseBlurEffect')).to.be(true); + expect(runtimeLayer.isEffectEnabled('AddedKawaseBlurEffect')).to.be(true); + expect(runtimeLayer.isEffectEnabled('InitialKawaseBlurEffect')).to.be(true); + }); +}); diff --git a/GDJS/tests/tests/inputmanager.js b/GDJS/tests/tests/inputmanager.js index 0536c4317e41..71a2b175882a 100644 --- a/GDJS/tests/tests/inputmanager.js +++ b/GDJS/tests/tests/inputmanager.js @@ -155,6 +155,7 @@ describe('gdjs.RuntimeObject.cursorOnObject', function() { name: 'obj1', type: '', behaviors: [], + effects: [], }); object.setPosition(450, 500); diff --git a/GDJS/tests/tests/runtimescene.js b/GDJS/tests/tests/runtimescene.js index 4ce52610037d..010dc03b9357 100644 --- a/GDJS/tests/tests/runtimescene.js +++ b/GDJS/tests/tests/runtimescene.js @@ -129,6 +129,7 @@ describe('gdjs.RuntimeScene integration tests', function () { type: 'Sprite', name: 'MyObject', behaviors: [], + effects: [], // @ts-expect-error ts-migrate(2322) FIXME: Type '{ type: string; name: string; behaviors: nev... Remove this comment to see the full error message animations: [], updateIfNotVisible: false, diff --git a/GDJS/tests/tests/spriteruntimeobject.js b/GDJS/tests/tests/spriteruntimeobject.js index 2ec9a5590144..6d414b57647e 100644 --- a/GDJS/tests/tests/spriteruntimeobject.js +++ b/GDJS/tests/tests/spriteruntimeobject.js @@ -20,6 +20,7 @@ describe('gdjs.SpriteRuntimeObject', function() { variables: [], behaviors: [], animations: [], + effects: [], }); expect(object.getScaleX()).to.be(1); @@ -40,6 +41,7 @@ describe('gdjs.SpriteRuntimeObject', function() { updateIfNotVisible: false, behaviors: [], variables: [], + effects: [], animations: [ { name: 'firstAnimation', diff --git a/GDJS/tests/tests/spriteruntimeobject.pixiruntimegamewithassets.js b/GDJS/tests/tests/spriteruntimeobject.pixiruntimegamewithassets.js index 61f68b447db0..d4d876146b0f 100644 --- a/GDJS/tests/tests/spriteruntimeobject.pixiruntimegamewithassets.js +++ b/GDJS/tests/tests/spriteruntimeobject.pixiruntimegamewithassets.js @@ -25,6 +25,7 @@ describe('gdjs.SpriteRuntimeObject (using a PIXI RuntimeGame with assets)', func updateIfNotVisible: false, variables: [], behaviors: [], + effects: [], animations: [ { name: 'NewObject2', diff --git a/GDevelop.js/Bindings/Bindings.idl b/GDevelop.js/Bindings/Bindings.idl index 29d74ed75046..d7c1b8696739 100644 --- a/GDevelop.js/Bindings/Bindings.idl +++ b/GDevelop.js/Bindings/Bindings.idl @@ -1426,11 +1426,13 @@ interface EffectMetadata { [Ref] EffectMetadata SetHelpPath([Const] DOMString helpPath); [Ref] EffectMetadata SetIncludeFile([Const] DOMString includeFile); [Ref] EffectMetadata AddIncludeFile([Const] DOMString includeFile); + [Ref] EffectMetadata MarkAsNotWorkingForObjects(); [Const, Ref] DOMString GetType(); [Const, Ref] DOMString GetFullName(); [Const, Ref] DOMString GetDescription(); [Const, Ref] DOMString GetHelpPath(); + boolean IsMarkedAsNotWorkingForObjects(); [Ref] MapStringPropertyDescriptor GetProperties(); }; diff --git a/GDevelop.js/types/gdeffectmetadata.js b/GDevelop.js/types/gdeffectmetadata.js index 3c9a70789649..4633044817a6 100644 --- a/GDevelop.js/types/gdeffectmetadata.js +++ b/GDevelop.js/types/gdeffectmetadata.js @@ -5,10 +5,12 @@ declare class gdEffectMetadata { setHelpPath(helpPath: string): gdEffectMetadata; setIncludeFile(includeFile: string): gdEffectMetadata; addIncludeFile(includeFile: string): gdEffectMetadata; + markAsNotWorkingForObjects(): gdEffectMetadata; getType(): string; getFullName(): string; getDescription(): string; getHelpPath(): string; + isMarkedAsNotWorkingForObjects(): boolean; getProperties(): gdMapStringPropertyDescriptor; delete(): void; ptr: number; diff --git a/newIDE/app/src/BehaviorsEditor/EmptyBehaviorsPlaceholder.js b/newIDE/app/src/BehaviorsEditor/EmptyBehaviorsPlaceholder.js index 91aa357267ab..67b75da9b9f2 100644 --- a/newIDE/app/src/BehaviorsEditor/EmptyBehaviorsPlaceholder.js +++ b/newIDE/app/src/BehaviorsEditor/EmptyBehaviorsPlaceholder.js @@ -1,43 +1,29 @@ // @flow import { Trans } from '@lingui/macro'; - import * as React from 'react'; import HelpButton from '../UI/HelpButton'; import Text from '../UI/Text'; -import Paper from '@material-ui/core/Paper'; -import { Line, Column } from '../UI/Grid'; +import { EmptyPlaceholder } from '../UI/EmptyPlaceholder'; const EmptyBehaviorsPlaceholder = () => ( - - - - - There are no behaviors here. - - - - Behaviors are predefined actions that are assigned to objects. - Behaviors can have no or multiple parameters. - - - - - Add your first behavior using the button "Add a behavior to the - object". - - - - - - - - + } + > + + There are no behaviors here. + + + + Behaviors are predefined actions that are assigned to objects. Behaviors + can have no or multiple parameters. + + + + + Add your first behavior using the button "Add a behavior to the object". + + + ); export default EmptyBehaviorsPlaceholder; diff --git a/newIDE/app/src/BehaviorsEditor/index.js b/newIDE/app/src/BehaviorsEditor/index.js index 965b422bd507..28978dd697fd 100644 --- a/newIDE/app/src/BehaviorsEditor/index.js +++ b/newIDE/app/src/BehaviorsEditor/index.js @@ -109,29 +109,78 @@ const BehaviorsEditor = (props: Props) => { return ( - - {allBehaviorNames.length === 0 && ( -
- - - -
- )} - {allBehaviorNames.map((behaviorName, index) => { - const behaviorContent = object.getBehavior(behaviorName); - const behaviorTypeName = behaviorContent.getTypeName(); + {allBehaviorNames.length === 0 ? ( + + + + ) : ( + + {allBehaviorNames.map((behaviorName, index) => { + const behaviorContent = object.getBehavior(behaviorName); + const behaviorTypeName = behaviorContent.getTypeName(); + + const behaviorMetadata = gd.MetadataProvider.getBehaviorMetadata( + gd.JsPlatform.get(), + behaviorTypeName + ); + if (gd.MetadataProvider.isBadBehaviorMetadata(behaviorMetadata)) { + return ( + + { + ev.stopPropagation(); + onRemoveBehavior(behaviorName); + }} + > + + , + ]} + > + + Unknown behavior{' '} + + + + + + + + + This behavior is unknown. It might be a behavior that + was defined in an extension and that was later removed. + You should delete it. + + + + + ); + } + + const behavior = behaviorMetadata.get(); + const BehaviorComponent = BehaviorsEditorService.getEditor( + behaviorTypeName + ); + const tutorialHints = getBehaviorTutorialHints(behaviorTypeName); + const enabledTutorialHints = tutorialHints.filter( + hint => !values.hiddenTutorialHints[hint.identifier] + ); + const iconUrl = behaviorMetadata.getIconFilename(); - const behaviorMetadata = gd.MetadataProvider.getBehaviorMetadata( - gd.JsPlatform.get(), - behaviorTypeName - ); - if (gd.MetadataProvider.isBadBehaviorMetadata(behaviorMetadata)) { return ( , { ev.stopPropagation(); onRemoveBehavior(behaviorName); @@ -141,112 +190,62 @@ const BehaviorsEditor = (props: Props) => { , ]} > - - Unknown behavior{' '} - - - + {iconUrl ? ( + + ) : null} + + + onChangeBehaviorName(behaviorContent, text) + } + /> - - - This behavior is unknown. It might be a behavior that was - defined in an extension and that was later removed. You - should delete it. - - - - - ); - } - - const behavior = behaviorMetadata.get(); - const BehaviorComponent = BehaviorsEditorService.getEditor( - behaviorTypeName - ); - const tutorialHints = getBehaviorTutorialHints(behaviorTypeName); - const enabledTutorialHints = tutorialHints.filter( - hint => !values.hiddenTutorialHints[hint.identifier] - ); - const iconUrl = behaviorMetadata.getIconFilename(); - - return ( - - , - { - ev.stopPropagation(); - onRemoveBehavior(behaviorName); - }} + - - , - ]} - > - {iconUrl ? ( - - ) : null} - - - onChangeBehaviorName(behaviorContent, text) - } - /> - - - - - {enabledTutorialHints.length ? ( + {enabledTutorialHints.length ? ( + + + {tutorialHints.map(tutorialHint => ( + + ))} + + + ) : null} - - {tutorialHints.map(tutorialHint => ( - - ))} - + - ) : null} - - - - - - - ); - })} - +
+ + + ); + })} + + )} ( + ( + + )} + > + + + Effects can change how layers or objects are rendered on screen. + + + + + After adding an effect, set up its parameters. Launch a preview to see + the result. Using the events and the name of the effect, you can change + the parameters during the game. + + + +); diff --git a/newIDE/app/src/EffectsList/EnumerateEffects.js b/newIDE/app/src/EffectsList/EnumerateEffects.js index e2daa3c3309c..395eb61509eb 100644 --- a/newIDE/app/src/EffectsList/EnumerateEffects.js +++ b/newIDE/app/src/EffectsList/EnumerateEffects.js @@ -11,6 +11,7 @@ export type EnumeratedEffectMetadata = {| fullName: string, description: string, parametersSchema: Schema, + isMarkedAsNotWorkingForObjects: boolean, |}; /** @@ -115,6 +116,7 @@ export const enumerateEffectsMetadata = ( effectMetadata, fullName: effectMetadata.getFullName(), description: effectMetadata.getDescription(), + isMarkedAsNotWorkingForObjects: effectMetadata.isMarkedAsNotWorkingForObjects(), parametersSchema, }; }); diff --git a/newIDE/app/src/EffectsList/EnumerateEffects.spec.js b/newIDE/app/src/EffectsList/EnumerateEffects.spec.js index 6b76b8ae7389..dc584ebdff2c 100644 --- a/newIDE/app/src/EffectsList/EnumerateEffects.spec.js +++ b/newIDE/app/src/EffectsList/EnumerateEffects.spec.js @@ -5,7 +5,7 @@ import { makeTestProject } from '../fixtures/TestProject'; const gd: libGDevelop = global.gd; describe('EnumerateEffects', () => { - it('can enumerate effects', () => { + it('can enumerate effects for layers', () => { makeTestExtensions(gd); const { project } = makeTestProject(gd); const enumeratedEffectsMetadata = enumerateEffectsMetadata(project); @@ -14,12 +14,21 @@ describe('EnumerateEffects', () => { expect.objectContaining({ type: 'FakeSepia', fullName: 'Fake Sepia Effect', + isMarkedAsNotWorkingForObjects: false, + }) + ); + expect(enumeratedEffectsMetadata).toContainEqual( + expect.objectContaining({ + type: 'FakeSepiaThatWouldWorkOnlyForLayers', + fullName: 'Fake Sepia Effect only for layers', + isMarkedAsNotWorkingForObjects: true, }) ); expect(enumeratedEffectsMetadata).toContainEqual( expect.objectContaining({ type: 'FakeNight', fullName: 'Fake Night Effect', + isMarkedAsNotWorkingForObjects: false, }) ); }); diff --git a/newIDE/app/src/EffectsList/index.js b/newIDE/app/src/EffectsList/index.js index d6ab2e176325..594bbe69770e 100644 --- a/newIDE/app/src/EffectsList/index.js +++ b/newIDE/app/src/EffectsList/index.js @@ -10,7 +10,6 @@ import SelectOption from '../UI/SelectOption'; import { mapFor } from '../Utils/MapFor'; import RaisedButton from '../UI/RaisedButton'; import IconButton from '../UI/IconButton'; -import EmptyMessage from '../UI/EmptyMessage'; import ElementWithMenu from '../UI/Menu/ElementWithMenu'; import MoreVert from '@material-ui/icons/MoreVert'; import SemiControlledTextField from '../UI/SemiControlledTextField'; @@ -33,6 +32,8 @@ import { type ChooseResourceFunction, } from '../ResourcesList/ResourceSource.flow'; import { type ResourceExternalEditor } from '../ResourcesList/ResourceExternalEditor.flow'; +import ScrollView from '../UI/ScrollView'; +import { EmptyEffectsPlaceholder } from './EmptyEffectsPlaceholder'; type Props = {| project: gdProject, @@ -41,6 +42,7 @@ type Props = {| resourceExternalEditors: Array, effectsContainer: gdEffectsContainer, onEffectsUpdated: () => void, + target: 'object' | 'layer', |}; const getEnumeratedEffectMetadata = ( @@ -107,165 +109,158 @@ export default function EffectsList(props: Props) { return ( - {({ i18n }) => { - return ( - - - - - - Effects can change how the layer is rendered on screen. - After adding an effect, set up its parameters. Launch a - preview to see the result. Using the events and the name of - the effect, you can change the parameters during the game. - - - - - {effectsContainer.getEffectsCount() > 3 && ( - - - - - Using a lot of effects can have a severe negative impact - on the rendering performance, especially on low-end or - mobile devices. Consider using less effects if possible. - You can also disable and re-enable effects as needed using - events. - - - - - )} - {mapFor(0, effectsContainer.getEffectsCount(), (i: number) => { - const effect: gdEffect = effectsContainer.getEffectAt(i); - const effectType = effect.getEffectType(); - const effectMetadata = getEnumeratedEffectMetadata( - allEffectMetadata, - effectType - ); + {({ i18n }) => ( + + {effectsContainer.getEffectsCount() !== 0 ? ( + + {effectsContainer.getEffectsCount() > 3 && ( + + + + + Using a lot of effects can have a severe negative impact + on the rendering performance, especially on low-end or + mobile devices. Consider using less effects if possible. + You can also disable and re-enable effects as needed + using events. + + + + + )} + {mapFor(0, effectsContainer.getEffectsCount(), (i: number) => { + const effect: gdEffect = effectsContainer.getEffectAt(i); + const effectType = effect.getEffectType(); + const effectMetadata = getEnumeratedEffectMetadata( + allEffectMetadata, + effectType + ); - return ( - - - - Effect name: - - { - if (newName === effect.getName()) return; + return ( + + + + Effect name: + + { + if (newName === effect.getName()) return; - effect.setName(newName); - forceUpdate(); - onEffectsUpdated(); - }} - fullWidth - /> - - Type: - - - chooseEffectType(effect, newEffectType) - } - fullWidth - hintText={t`Choose the effect to apply`} - > - {allEffectMetadata.map(effectMetadata => ( - - ))} - - - - - } - buildMenuTemplate={(i18n: I18nType) => [ - { - label: i18n._(t`Delete`), - click: () => removeEffect(effect.getName()), - }, - { type: 'separator' }, - { - type: 'checkbox', - label: i18n._(t`Show Parameter Names`), - checked: showEffectParameterNames, - click: () => - setShowEffectParameterNames( - !showEffectParameterNames - ), - }, - ]} - /> - - - - {!!effectType && effectMetadata ? ( - - - - - - - - i18n._( - t`Parameter name in events: \`${parameterName}\` ` - ) - : undefined + effect.setName(newName); + forceUpdate(); + onEffectsUpdated(); + }} + fullWidth + /> + + Type: + + + chooseEffectType(effect, newEffectType) + } + fullWidth + hintText={t`Choose the effect to apply`} + > + {allEffectMetadata.map(effectMetadata => ( + - - ) : null} - - - - ); - })} - {effectsContainer.getEffectsCount() === 0 ? ( - - No effects applied. - - ) : null} - - - Add an effect} - onClick={addEffect} - icon={} - /> - + ))} + + + + + } + buildMenuTemplate={(i18n: I18nType) => [ + { + label: i18n._(t`Delete`), + click: () => removeEffect(effect.getName()), + }, + { type: 'separator' }, + { + type: 'checkbox', + label: i18n._(t`Show Parameter Names`), + checked: showEffectParameterNames, + click: () => + setShowEffectParameterNames( + !showEffectParameterNames + ), + }, + ]} + /> + + + + {!!effectType && effectMetadata ? ( + + + + + + + + i18n._( + t`Parameter name in events: \`${parameterName}\` ` + ) + : undefined + } + /> + + ) : null} + + + + ); + })} + + ) : ( + + + )} + + + Add an effect} + onClick={addEffect} + icon={} + /> + - ); - }} + + )} ); } diff --git a/newIDE/app/src/EventsSheet/EventsTree/EmptyEventsPlaceholder.js b/newIDE/app/src/EventsSheet/EventsTree/EmptyEventsPlaceholder.js index fe65c208172b..90685e444b87 100644 --- a/newIDE/app/src/EventsSheet/EventsTree/EmptyEventsPlaceholder.js +++ b/newIDE/app/src/EventsSheet/EventsTree/EmptyEventsPlaceholder.js @@ -4,40 +4,23 @@ import { Trans } from '@lingui/macro'; import * as React from 'react'; import HelpButton from '../../UI/HelpButton'; import Text from '../../UI/Text'; -import Paper from '@material-ui/core/Paper'; -import { Line, Column } from '../../UI/Grid'; +import { EmptyPlaceholder } from '../../UI/EmptyPlaceholder'; const EmptyEventsPlaceholder = () => ( - - - - - There are no events here. - - - - Events are composed of conditions (on the left of an event) and - actions (on the right). When conditions are fulfilled, the actions - are executed. - - - - - Add your first event using the button "Add a new event". - - - - - - - - + }> + + There are no events here. + + + + Events are composed of conditions (on the left of an event) and actions + (on the right). When conditions are fulfilled, the actions are executed. + + + + Add your first event using the button "Add a new event". + + ); export default EmptyEventsPlaceholder; diff --git a/newIDE/app/src/LayersList/LayerEditorDialog.js b/newIDE/app/src/LayersList/LayerEditorDialog.js index e43ffa50ab49..639e7799ee29 100644 --- a/newIDE/app/src/LayersList/LayerEditorDialog.js +++ b/newIDE/app/src/LayersList/LayerEditorDialog.js @@ -22,7 +22,7 @@ import HotReloadPreviewButton, { import HelpButton from '../UI/HelpButton'; import { Tab, Tabs } from '../UI/Tabs'; import EffectsList from '../EffectsList'; -import { Spacer } from '../UI/Grid'; +import { Column, Spacer } from '../UI/Grid'; const gd: libGDevelop = global.gd; type Props = {| @@ -100,99 +100,104 @@ const LayerEditorDialog = (props: Props) => { />, ]} onRequestClose={onCancelChanges} + fullHeight + flexBody > - - Properties} value="properties" /> - Effects} value="effects" /> - - {currentTab === 'properties' && ( - - {layer.isLightingLayer() ? ( - - - The lighting layer renders an ambient light on the scene. All - lights should be placed on this layer so that shadows are - properly rendered. By default, the layer follows the base layer - camera. Uncheck this if you want to manually move the camera - using events. - - - ) : null} - - There are {instancesCount} instances of objects on this layer. - - {!props.project.getUseDeprecatedZeroAsDefaultZOrder() && ( + + + Properties} value="properties" /> + Effects} value="effects" /> + + {currentTab === 'properties' && ( + + {layer.isLightingLayer() ? ( + + + The lighting layer renders an ambient light on the scene. All + lights should be placed on this layer so that shadows are + properly rendered. By default, the layer follows the base + layer camera. Uncheck this if you want to manually move the + camera using events. + + + ) : null} - Objects created using events on this layer will be given a "Z - order" of {highestZOrder + 1}, so that they appear in front of all - objects of this layer. You can change this using the action to - change an object Z order, after using an action to create it. + There are {instancesCount} instances of objects on this layer. - )} - Layer visible} - checked={layer.getVisibility()} - onCheck={(e, checked) => { - layer.setVisibility(checked); - forceUpdate(); - }} - /> - {layer.isLightingLayer() ? ( - - - Lighting settings + {!props.project.getUseDeprecatedZeroAsDefaultZOrder() && ( + + Objects created using events on this layer will be given a "Z + order" of {highestZOrder + 1}, so that they appear in front of + all objects of this layer. You can change this using the action + to change an object Z order, after using an action to create it. - Automatically follow the base layer.} - checked={layer.isFollowingBaseLayerCamera()} - onCheck={(e, checked) => { - layer.setFollowBaseLayerCamera(checked); - forceUpdate(); - }} - /> - Ambient light color} - disableAlpha - color={{ - r: layer.getAmbientLightColorRed(), - g: layer.getAmbientLightColorGreen(), - b: layer.getAmbientLightColorBlue(), - }} - onChange={(color: ColorResult) => { - layer.setAmbientLightColor( - color.rgb.r, - color.rgb.g, - color.rgb.b - ); - forceUpdate(); - }} - /> - - ) : ( - // Add some space to avoid a dialog to short that would show scrollbars - - - - - )} - - )} - {currentTab === 'effects' && ( - - )} + )} + Layer visible} + checked={layer.getVisibility()} + onCheck={(e, checked) => { + layer.setVisibility(checked); + forceUpdate(); + }} + /> + {layer.isLightingLayer() ? ( + + + Lighting settings + + Automatically follow the base layer.} + checked={layer.isFollowingBaseLayerCamera()} + onCheck={(e, checked) => { + layer.setFollowBaseLayerCamera(checked); + forceUpdate(); + }} + /> + Ambient light color} + disableAlpha + color={{ + r: layer.getAmbientLightColorRed(), + g: layer.getAmbientLightColorGreen(), + b: layer.getAmbientLightColorBlue(), + }} + onChange={(color: ColorResult) => { + layer.setAmbientLightColor( + color.rgb.r, + color.rgb.g, + color.rgb.b + ); + forceUpdate(); + }} + /> + + ) : ( + // Add some space to avoid a dialog to short that would show scrollbars + + + + + )} + + )} + {currentTab === 'effects' && ( + + )} + ); }; diff --git a/newIDE/app/src/ObjectEditor/ObjectEditorDialog.js b/newIDE/app/src/ObjectEditor/ObjectEditorDialog.js index b2589e22a5c7..d0d0dd3352fc 100644 --- a/newIDE/app/src/ObjectEditor/ObjectEditorDialog.js +++ b/newIDE/app/src/ObjectEditor/ObjectEditorDialog.js @@ -22,6 +22,7 @@ import useForceUpdate from '../Utils/UseForceUpdate'; import HotReloadPreviewButton, { type HotReloadPreviewButtonProps, } from '../HotReload/HotReloadPreviewButton'; +import EffectsList from '../EffectsList'; type Props = {| open: boolean, @@ -41,6 +42,7 @@ type Props = {| resourceExternalEditors: Array, unsavedChanges?: UnsavedChanges, onUpdateBehaviorsSharedData: () => void, + initialTab: ?string, // Preview: hotReloadPreviewButtonProps: HotReloadPreviewButtonProps, @@ -55,7 +57,9 @@ type InnerDialogProps = {| |}; const InnerDialog = (props: InnerDialogProps) => { - const [currentTab, setCurrentTab] = React.useState('properties'); + const [currentTab, setCurrentTab] = React.useState( + props.initialTab || 'properties' + ); const [newObjectName, setNewObjectName] = React.useState(props.objectName); const forceUpdate = useForceUpdate(); const onCancelChanges = useSerializableObjectCancelableEditor({ @@ -119,6 +123,11 @@ const InnerDialog = (props: InnerDialogProps) => { value={'behaviors'} key={'behaviors'} /> + Effects} + value={'effects'} + key={'effects'} + /> } @@ -179,6 +188,19 @@ const InnerDialog = (props: InnerDialogProps) => { onUpdateBehaviorsSharedData={props.onUpdateBehaviorsSharedData} /> )} + {currentTab === 'effects' && ( + + )} ); }; @@ -233,7 +255,7 @@ export default class ObjectEditorDialog extends Component { } render() { - const { object } = this.props; + const { object, initialTab } = this.props; const { editorComponent, castToObjectType, helpPagePath } = this.state; if (!object || !castToObjectType) return null; @@ -246,6 +268,7 @@ export default class ObjectEditorDialog extends Component { helpPagePath={helpPagePath} object={castToObjectType(object)} objectName={this.state.objectName} + initialTab={initialTab} /> ); } diff --git a/newIDE/app/src/ObjectsList/EnumerateObjects.spec.js b/newIDE/app/src/ObjectsList/EnumerateObjects.spec.js index b9e14978dead..7857532aefa0 100644 --- a/newIDE/app/src/ObjectsList/EnumerateObjects.spec.js +++ b/newIDE/app/src/ObjectsList/EnumerateObjects.spec.js @@ -11,9 +11,9 @@ describe('EnumerateObjects', () => { allObjectsList, } = enumerateObjects(project, testLayout); - expect(containerObjectsList).toHaveLength(12); + expect(containerObjectsList).toHaveLength(13); expect(projectObjectsList).toHaveLength(2); - expect(allObjectsList).toHaveLength(14); + expect(allObjectsList).toHaveLength(15); }); it('can do a case-insensitive search in the lists of objects', () => { diff --git a/newIDE/app/src/ObjectsList/UseObjectsListCommands.js b/newIDE/app/src/ObjectsList/UseObjectsListCommands.js index 6b6b2532693a..928dd14fe50f 100644 --- a/newIDE/app/src/ObjectsList/UseObjectsListCommands.js +++ b/newIDE/app/src/ObjectsList/UseObjectsListCommands.js @@ -8,11 +8,12 @@ import ObjectsRenderingService from '../ObjectsRendering/ObjectsRenderingService const generateLayoutObjectsOptions = ( project: gdProject, layout: gdLayout, - onChoose: (object: gdObject) => void + onChoose: (object: gdObject, arg: ?string) => void, + onChooseArg: ?string ): Array => { return enumerateObjects(project, layout).containerObjectsList.map(item => ({ text: item.object.getName(), - handler: () => onChoose(item.object), + handler: () => onChoose(item.object, onChooseArg), iconSrc: ObjectsRenderingService.getThumbnail.bind(ObjectsRenderingService)( project, item.object @@ -23,7 +24,7 @@ const generateLayoutObjectsOptions = ( type Props = {| project: gdProject, layout: gdLayout, - onEditObject: (object: gdObject) => void, + onEditObject: (object: gdObject, initialTab: ?string) => void, onEditObjectVariables: (object: gdObject) => void, |}; @@ -36,6 +37,27 @@ const useObjectsListCommands = (props: Props) => { ), }); + useCommandWithOptions('EDIT_OBJECT_BEHAVIORS', true, { + generateOptions: React.useCallback( + () => + generateLayoutObjectsOptions( + project, + layout, + onEditObject, + 'behaviors' + ), + [project, layout, onEditObject] + ), + }); + + useCommandWithOptions('EDIT_OBJECT_EFFECTS', true, { + generateOptions: React.useCallback( + () => + generateLayoutObjectsOptions(project, layout, onEditObject, 'effects'), + [project, layout, onEditObject] + ), + }); + useCommandWithOptions('EDIT_OBJECT_VARIABLES', true, { generateOptions: React.useCallback( () => diff --git a/newIDE/app/src/ObjectsList/index.js b/newIDE/app/src/ObjectsList/index.js index bd7f2a3e5f2a..cc42e182641f 100644 --- a/newIDE/app/src/ObjectsList/index.js +++ b/newIDE/app/src/ObjectsList/index.js @@ -106,7 +106,7 @@ type Props = {| getAllObjectTags: () => Tags, onChangeSelectedObjectTags: SelectedTags => void, - onEditObject: gdObject => void, + onEditObject: (object: gdObject, initialTab: ?string) => void, onObjectCreated: gdObject => void, onObjectSelected: string => void, onObjectPasted?: gdObject => void, @@ -466,6 +466,14 @@ export default class ObjectsList extends React.Component { label: i18n._(t`Edit object variables`), click: () => this._editVariables(object), }, + { + label: i18n._(t`Edit behaviors`), + click: () => this.props.onEditObject(object, 'behaviors'), + }, + { + label: i18n._(t`Edit effects`), + click: () => this.props.onEditObject(object, 'effects'), + }, { type: 'separator' }, { label: i18n._(t`Tags`), diff --git a/newIDE/app/src/SceneEditor/index.js b/newIDE/app/src/SceneEditor/index.js index 1706fc43b655..4668f6c72c35 100644 --- a/newIDE/app/src/SceneEditor/index.js +++ b/newIDE/app/src/SceneEditor/index.js @@ -134,6 +134,7 @@ type State = {| editedLayer: ?gdLayer, editedLayerInitialTab: 'properties' | 'effects', editedObjectWithContext: ?ObjectWithContext, + editedObjectInitialTab: ?string, variablesEditedInstance: ?gdInitialInstance, variablesEditedObject: ?gdObject, selectedObjectNames: Array, @@ -185,6 +186,7 @@ export default class SceneEditor extends React.Component { editedLayer: null, editedLayerInitialTab: 'properties', editedObjectWithContext: null, + editedObjectInitialTab: 'properties', variablesEditedInstance: null, variablesEditedObject: null, selectedObjectNames: [], @@ -364,7 +366,7 @@ export default class SceneEditor extends React.Component { this.setState({ layoutVariablesDialogOpen: open }); }; - editObject = (editedObject: ?gdObject) => { + editObject = (editedObject: ?gdObject, initialTab: ?string) => { const { project } = this.props; if (editedObject) { this.setState({ @@ -372,10 +374,12 @@ export default class SceneEditor extends React.Component { object: editedObject, global: project.hasObjectNamed(editedObject.getName()), }, + editedObjectInitialTab: initialTab || 'properties', }); } else { this.setState({ editedObjectWithContext: null, + editedObjectInitialTab: 'properties', }); } }; @@ -1170,6 +1174,7 @@ export default class SceneEditor extends React.Component { ( {muiTheme => ( diff --git a/newIDE/app/src/UI/EmptyPlaceholder.js b/newIDE/app/src/UI/EmptyPlaceholder.js new file mode 100644 index 000000000000..c551789c8d47 --- /dev/null +++ b/newIDE/app/src/UI/EmptyPlaceholder.js @@ -0,0 +1,32 @@ +// @flow +import * as React from 'react'; +import Paper from '@material-ui/core/Paper'; +import { Line, Column } from './Grid'; + +type Props = {| + children: React.Node, + renderButtons: () => React.Node, +|}; + +/** + * A placeholder for when there is no content to display. + * Also take a look at EmptyMessage for a less visible message. + */ +export const EmptyPlaceholder = (props: Props) => ( + + + + {props.children} + + {props.renderButtons()} + + + + +); diff --git a/newIDE/app/src/fixtures/TestExtensions.js b/newIDE/app/src/fixtures/TestExtensions.js index faee2c9df519..deef0798f0cd 100644 --- a/newIDE/app/src/fixtures/TestExtensions.js +++ b/newIDE/app/src/fixtures/TestExtensions.js @@ -35,6 +35,19 @@ export const makeTestExtensions = (gd: libGDevelop) => { .setLabel('Opacity (between 0 and 1)') .setType('number'); + const layerOnlySepiaEffect = extension + .addEffect('FakeSepiaThatWouldWorkOnlyForLayers') + .setFullName('Fake Sepia Effect only for layers') + .setDescription('A fake sepia effect only for layers') + .addIncludeFile('Extensions/Effects/fake-sepia-only-for-layers.js') + .markAsNotWorkingForObjects(); + const layerOnlySepiaProperties = layerOnlySepiaEffect.getProperties(); + layerOnlySepiaProperties + .getOrCreate('opacity') + .setValue('1') + .setLabel('Opacity (between 0 and 1)') + .setType('number'); + const nightEffect = extension .addEffect('FakeNight') .setFullName('Fake Night Effect') diff --git a/newIDE/app/src/fixtures/TestProject.js b/newIDE/app/src/fixtures/TestProject.js index 4d0a3ebb2bbd..00d30dc8c4ee 100644 --- a/newIDE/app/src/fixtures/TestProject.js +++ b/newIDE/app/src/fixtures/TestProject.js @@ -9,6 +9,7 @@ export type TestProject = {| panelSpriteObject: any, spriteObject: gdSpriteObject, spriteObjectWithBehaviors: gdSpriteObject, + spriteObjectWithoutBehaviors: gdSpriteObject, testLayout: gdLayout, group1: gdObjectGroup, group2: gdObjectGroup, @@ -29,6 +30,8 @@ export type TestProject = {| layerWithEffects: gdLayer, layerWithEffectWithoutEffectType: gdLayer, layerWithoutEffects: gdLayer, + spriteObjectWithEffects: gdSpriteObject, + spriteObjectWithoutEffects: gdSpriteObject, |}; */ @@ -73,6 +76,9 @@ export const makeTestProject = (gd /*: libGDevelop */) /*: TestProject */ => { const spriteObjectWithBehaviors = new gd.SpriteObject( 'MySpriteObjectWithBehaviors' ); + const spriteObjectWithoutBehaviors = new gd.SpriteObject( + 'MySpriteObjectWithoutBehaviors' + ); { const animation = new gd.Animation(); @@ -148,6 +154,7 @@ export const makeTestProject = (gd /*: libGDevelop */) /*: TestProject */ => { testLayout.insertObject(panelSpriteObject, 0); testLayout.insertObject(spriteObject, 0); testLayout.insertObject(spriteObjectWithBehaviors, 0); + testLayout.insertObject(spriteObjectWithoutBehaviors, 0); const group1 = new gd.ObjectGroup(); group1.setName('GroupOfSprites'); @@ -503,6 +510,33 @@ export const makeTestProject = (gd /*: libGDevelop */) /*: TestProject */ => { const layerWithoutEffects = new gd.Layer(); + const spriteObjectWithoutEffects = new gd.SpriteObject( + 'MySpriteObjectWithoutEffects' + ); + const spriteObjectWithEffects = new gd.SpriteObject( + 'MySpriteObjectWithEffects' + ); + { + const effect1 = spriteObjectWithEffects + .getEffects() + .insertNewEffect('MyEffect1', 0); + const effect2 = spriteObjectWithEffects + .getEffects() + .insertNewEffect('MyEffect2', 1); + const effect3 = spriteObjectWithEffects + .getEffects() + .insertNewEffect('MyEffect3', 1); + + effect1.setEffectType('FakeSepia'); + effect1.setDoubleParameter('opacity', 0.6); + effect2.setEffectType('FakeNight'); + effect2.setDoubleParameter('intensity', 0.1); + effect2.setDoubleParameter('opacity', 0.2); + effect3.setEffectType('FakeEffectWithVariousParameters'); + effect3.setDoubleParameter('intensity', 0.1); + effect3.setStringParameter('image', 'my-image'); + } + return { project, shapePainterObject, @@ -511,6 +545,7 @@ export const makeTestProject = (gd /*: libGDevelop */) /*: TestProject */ => { panelSpriteObject, spriteObject, spriteObjectWithBehaviors, + spriteObjectWithoutBehaviors, testLayout, group1, group2, @@ -531,5 +566,7 @@ export const makeTestProject = (gd /*: libGDevelop */) /*: TestProject */ => { layerWithEffects, layerWithEffectWithoutEffectType, layerWithoutEffects, + spriteObjectWithEffects, + spriteObjectWithoutEffects, }; }; diff --git a/newIDE/app/src/stories/index.js b/newIDE/app/src/stories/index.js index 3b7ea9c8bae3..639c4d01505d 100644 --- a/newIDE/app/src/stories/index.js +++ b/newIDE/app/src/stories/index.js @@ -3639,6 +3639,18 @@ storiesOf('BehaviorsEditor', module) onUpdateBehaviorsSharedData={() => {}} /> + )) + .add('without any behaviors', () => ( + + Promise.reject('Unimplemented')} + resourceExternalEditors={fakeResourceExternalEditors} + onUpdateBehaviorsSharedData={() => {}} + /> + )); storiesOf('VariablesList', module) @@ -4520,8 +4532,9 @@ storiesOf('LayersList', module) storiesOf('EffectsList', module) .addDecorator(paperDecorator) .addDecorator(muiDecorator) - .add('with some effects', () => ( + .add('with some effects (for a layer)', () => ( { @@ -4533,8 +4546,23 @@ storiesOf('EffectsList', module) onEffectsUpdated={action('effects updated')} /> )) - .add('with an effect without effect type', () => ( + .add('with some effects (for an object)', () => ( { + action('onChooseResource'); + return Promise.reject(); + }} + resourceSources={[]} + effectsContainer={testProject.spriteObjectWithEffects.getEffects()} + onEffectsUpdated={action('effects updated')} + /> + )) + .add('with an effect without effect type (for a layer)', () => ( + { @@ -4546,8 +4574,9 @@ storiesOf('EffectsList', module) onEffectsUpdated={action('effects updated')} /> )) - .add('without effects', () => ( + .add('without effects (for a layer)', () => ( { @@ -4558,6 +4587,20 @@ storiesOf('EffectsList', module) effectsContainer={testProject.layerWithoutEffects.getEffects()} onEffectsUpdated={action('effects updated')} /> + )) + .add('without effects (for an object)', () => ( + { + action('onChooseResource'); + return Promise.reject(); + }} + resourceSources={[]} + effectsContainer={testProject.spriteObjectWithoutEffects.getEffects()} + onEffectsUpdated={action('effects updated')} + /> )); storiesOf('NewObjectDialog', module)