diff --git a/examples/particle-system/README.md b/examples/particle-system/README.md new file mode 100644 index 0000000..4302f9e --- /dev/null +++ b/examples/particle-system/README.md @@ -0,0 +1,27 @@ +Particles System Example +======================== + +This example shows how the Particle system can be used. + +It shows three effects: + +* Fire Effect: + + This is a simple effect that shows fire particles rising from the center of the window. + + ![](./images/fire-effect.png) + +* Sparkle Effect: + + This shows multiple sized particles starting at the center of the window and going in all directions. + + ![](./images/sparkle-effect.png) + +* Image Source Effect: + + This takes an image source and creates particles from that image and shows them waving. + + ![](./images/image-source-effect.png) + +By tapping the screen, you can move between one effect to the next. +You can also press any key to move to the next effect apart from the ESC or Back keys which will exit the application. \ No newline at end of file diff --git a/examples/particle-system/images/fire-effect.png b/examples/particle-system/images/fire-effect.png new file mode 100644 index 0000000..d46f1a0 --- /dev/null +++ b/examples/particle-system/images/fire-effect.png diff --git a/examples/particle-system/images/image-source-effect.png b/examples/particle-system/images/image-source-effect.png new file mode 100644 index 0000000..60ace37 --- /dev/null +++ b/examples/particle-system/images/image-source-effect.png diff --git a/examples/particle-system/images/sparkle-effect.png b/examples/particle-system/images/sparkle-effect.png new file mode 100644 index 0000000..3a0a103 --- /dev/null +++ b/examples/particle-system/images/sparkle-effect.png diff --git a/examples/particle-system/particle-system-example.cpp b/examples/particle-system/particle-system-example.cpp index cda518d..3dd6b9e 100644 --- a/examples/particle-system/particle-system-example.cpp +++ b/examples/particle-system/particle-system-example.cpp @@ -17,18 +17,14 @@ #include -#include -#include #include +#include #include #include +#include #include -#include -#include #include -#include -#include #include "effects/particle-effect.h" @@ -39,13 +35,16 @@ using Dali::Toolkit::TextLabel; using namespace Dali::ParticleEffect; +constexpr uint16_t NUMBER_OF_EFFECTS = 3; +constexpr float TEXT_LABEL_ANIMATION_TIME = 5.0f; +const TimePeriod TEXT_LABEL_ANIMATION_TIME_PERIOD(3.0, 2.0f); + /** * This example shows Particle System feature */ class ParticleEffectController : public ConnectionTracker { public: - ParticleEffectController(Application& application) : mApplication(application) { @@ -55,135 +54,105 @@ public: ~ParticleEffectController() = default; // Nothing to do in destructor - template - ButtonType MakeButton( std::string title, - Vector2 position, - Vector2 size, - bool toggleable, - std::function onClick ) +private: + // The Init signal is received once (only) during the Application lifetime + void Create(Application& application) { - ButtonType button = ButtonType::New(); - button.SetProperty( Button::Property::LABEL, title); - button.SetProperty( Actor::Property::POSITION, position); - button.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); - button.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT); - button.SetProperty(Button::Property::TOGGLABLE, toggleable); - static std::map> callbackMap; - struct OnClick - { - static bool Slot(Button btn) - { - auto ptr = btn.GetObjectPtr(); - return callbackMap[ptr](btn); - } - }; + Window window = application.GetWindow(); + window.SetBackgroundColor(Color::BLACK); + auto windowSize = window.GetSize(); + + // Set up emitter actor to be the full size of the window as some particles may be outside a particular size + mEmitterActor = Handle::New({{Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER}, + {Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER}, + {Actor::Property::POSITION, Vector2::ZERO}, + {Actor::Property::SIZE, Vector2(windowSize.GetWidth(), windowSize.GetHeight())}}); + window.Add(mEmitterActor); - mUILastControlPosition = position; - mUILastControlSize = (size == Vector2::ZERO ? Vector2(button.GetNaturalSize()) : size); + // Create a tap gesture detector, attach the actor & connect + mTapDetector = TapGestureDetector::New(); + mTapDetector.Attach(mEmitterActor); + mTapDetector.DetectedSignal().Connect(this, [&](Actor actor, const TapGesture& tap) { NextEffect(); }); - callbackMap[button.GetObjectPtr()] = onClick; - button.ClickedSignal().Connect(OnClick::Slot); - return button; + // Respond to key events + window.KeyEventSignal().Connect(this, &ParticleEffectController::OnKeyEvent); + + // Create a Text Label at the bottom of the screen + mTextLabel = Handle::New({{Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_CENTER}, + {Actor::Property::PARENT_ORIGIN, ParentOrigin::BOTTOM_CENTER}, + {TextLabel::Property::MULTI_LINE, true}, + {TextLabel::Property::HORIZONTAL_ALIGNMENT, HorizontalAlignment::CENTER}, + {TextLabel::Property::TEXT_COLOR, Color::WHITE}}); + window.Add(mTextLabel); + + // Create a fade out animation for the text label after a few seconds + mTextLabelAnimation = Animation::New(TEXT_LABEL_ANIMATION_TIME); + mTextLabelAnimation.AnimateTo(Property(mTextLabel, Actor::Property::COLOR_ALPHA), 0.0f, TEXT_LABEL_ANIMATION_TIME_PERIOD); + mTextLabelAnimation.Play(); + + // Start the default effect + StartEffect(EffectType(mCurrentEffectType)); + + // Add extra line to text for further instructions only the first time + std::string label = mTextLabel[TextLabel::Property::TEXT]; + label += "\nTap to change particle effect"; + mTextLabel[TextLabel::Property::TEXT] = label; } - // The Init signal is received once (only) during the Application lifetime - void Create(Application& application) + void StartEffect(EffectType effectType) { - using namespace Dali::ParticleEffect; + if(mCurrentEmitter) + { + mCurrentEmitter.Stop(); + mCurrentEmitter.Reset(); + } - // Get a handle to the window - Window window = application.GetWindow(); - window.SetBackgroundColor(Color::BLACK); + ParticleEffectParams params{}; + std::string effectName; + + switch(effectType) { - Actor emitterActor = Actor::New(); - emitterActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); - emitterActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); - emitterActor.SetProperty(Actor::Property::POSITION, Vector2(0.0, 0.0f)); - emitterActor.SetProperty(Actor::Property::SIZE, Vector2(1.0, 1.0f)); - emitterActor.SetProperty(Actor::Property::UPDATE_AREA_HINT, Vector4(0, 0, window.GetSize().GetWidth(), window.GetSize().GetHeight())); - - window.Add(emitterActor); - - mEmitterActor = emitterActor; - PushButton lastButton; - window.Add( - MakeButton("Fire Effect", Vector2::ZERO, {}, true, [&](Button button){ - - if(mCurrentEmitter) - { - mCurrentEmitter.Stop(); - mCurrentEmitter.Reset(); - } - - ParticleEffectParams params{}; - params.particleCount = 5000; - params.emissionRate = 1000; - params.initialParticleCount = 0; - params.sourceSize = Vector2(200, 10); - params.strTexture = "sparkle-part1.png"; - - mCurrentEmitter = mParticleSystem->CreateEffectEmitter( EffectType::FIRE_RING, mEmitterActor, params ); - mCurrentEmitter.Start(); - return true; - }) - ); - - window.Add( - MakeButton("Sparkle Effect", Vector2(0.0f, mUILastControlSize.height), {}, true, [&](Button button){ - if(mCurrentEmitter) - { - mCurrentEmitter.Stop(); - mCurrentEmitter.Reset(); - } - - ParticleEffectParams params{}; - params.particleCount = 10000; - params.emissionRate = 500; - params.initialParticleCount = 0; - params.sourceSize = Vector2(10, 10); - params.strTexture = "blue-part2.png"; - - mCurrentEmitter = mParticleSystem->CreateEffectEmitter( EffectType::SPARKLES, mEmitterActor, params ); - mCurrentEmitter.Start(); - return true; - }) - ); - - window.Add( - MakeButton("Image Source Effect", Vector2(0.0f, mUILastControlPosition.y + mUILastControlSize.height), {}, true, [&](Button button){ - if(mCurrentEmitter) - { - mCurrentEmitter.Stop(); - mCurrentEmitter.Reset(); - } - - ParticleEffectParams params{}; - params.particleCount = 20000; - params.emissionRate = 0; - params.initialParticleCount = 10; - params.sourceSize = Vector2(64, 64); - params.strImageSourceName = "particle-image-source.jpg"; - - mCurrentEmitter = mParticleSystem->CreateEffectEmitter( EffectType::IMAGE_EXPLOSION, mEmitterActor, params ); - mCurrentEmitter.Start(); - return true; - }) - ); - window.Add( - MakeButton("Quit", Vector2(0.0f, mUILastControlPosition.y + mUILastControlSize.height * 2), {}, true, [&](Button button){ - if(mCurrentEmitter) - { - mCurrentEmitter.Stop(); - mCurrentEmitter.Reset(); - } - mApplication.Quit(); - return true; - }) - ); + case EffectType::FIRE_RING: + params.particleCount = 5000; + params.emissionRate = 1000; + params.initialParticleCount = 0; + params.sourceSize = Vector2(200, 10); + params.strTexture = "sparkle-part1.png"; + effectName = "Fire Effect"; + break; + + case EffectType::SPARKLES: + params.particleCount = 10000; + params.emissionRate = 500; + params.initialParticleCount = 0; + params.sourceSize = Vector2(10, 10); + params.strTexture = "blue-part2.png"; + effectName = "Sparkle Effect"; + break; + + case EffectType::IMAGE_EXPLOSION: + params.particleCount = 20000; + params.emissionRate = 0; + params.initialParticleCount = 10; + params.sourceSize = Vector2(64, 64); + params.strImageSourceName = "particle-image-source.jpg"; + effectName = "Image Source Effect"; + break; } - // Respond to key events - window.KeyEventSignal().Connect(this, &ParticleEffectController::OnKeyEvent); + mCurrentEmitter = mParticleSystem->CreateEffectEmitter(effectType, mEmitterActor, params); + mCurrentEmitter.Start(); + + // Set text and reset TextLabel properties and animation + mTextLabel[Toolkit::TextLabel::Property::TEXT] = effectName; + mTextLabel[Actor::Property::COLOR_ALPHA] = 1.0f; + mTextLabelAnimation.SetCurrentProgress(0.0f); + mTextLabelAnimation.Play(); + } + + void NextEffect() + { + StartEffect(EffectType(++mCurrentEffectType %= NUMBER_OF_EFFECTS)); } void OnKeyEvent(const KeyEvent& event) @@ -194,25 +163,31 @@ public: { mApplication.Quit(); } + else + { + NextEffect(); + } } } private: + using ParticleEffectPtr = std::unique_ptr; - Application& mApplication; - std::unique_ptr mParticleSystem; - ParticleEmitter mCurrentEmitter; - Actor mEmitterActor; + Application& mApplication; + ParticleEffectPtr mParticleSystem; + ParticleEmitter mCurrentEmitter; + Actor mEmitterActor; - // Needed for buttons - Vector2 mUILastControlPosition; - Vector2 mUILastControlSize; + TapGestureDetector mTapDetector; + uint32_t mCurrentEffectType{0u}; + Actor mTextLabel; + Animation mTextLabelAnimation; }; int DALI_EXPORT_API main(int argc, char** argv) { - Application application = Application::New(&argc, &argv); + Application application = Application::New(&argc, &argv); ParticleEffectController test(application); application.MainLoop(); return 0;