Browse Source

Updated for current Urho3D, modified isometric demo to include interactions similar to the 2d platformer.

thesquib 8 years ago
parent
commit
b46db86f24

+ 1 - 1
Source/Samples/39_Urho2DIsometricDemo/CMakeLists.txt → Source/Samples/49_Urho2DIsometricDemo/CMakeLists.txt

@@ -21,7 +21,7 @@
 #
 
 # Define target name
-set (TARGET_NAME 39_Urho2DIsometricDemo)
+set (TARGET_NAME 49_Urho2DIsometricDemo)
 
 # Define source files
 define_source_files (EXTRA_H_FILES ${COMMON_SAMPLE_H_FILES} ../Utilities2D/Sample2D.h ../Utilities2D/Sample2D.cpp ../Utilities2D/Mover.h ../Utilities2D/Mover.cpp)

+ 101 - 9
Source/Samples/39_Urho2DIsometricDemo/Character2D.cpp → Source/Samples/49_Urho2DIsometricDemo/Character2D.cpp

@@ -29,6 +29,8 @@
 #include <Urho3D/Urho2D/RigidBody2D.h>
 #include <Urho3D/Scene/Scene.h>
 #include <Urho3D/Scene/SceneEvents.h>
+#include <Urho3D/UI/Text.h>
+#include <Urho3D/UI/UI.h>
 
 #include "Character2D.h"
 
@@ -37,11 +39,14 @@
 // Character2D logic component
 Character2D::Character2D(Context* context) :
     LogicComponent(context),
-    moveSpeedScale_(1.0f),
-    zoom_(0.0f),
+    wounded_(false),
+    killed_(false),
+    timer_(0.0f),
     maxCoins_(0),
     remainingCoins_(0),
-    remainingLifes_(3)
+    remainingLifes_(3),
+    moveSpeedScale_(1.0f),
+    zoom_(0.0f)
 {
 }
 
@@ -51,15 +56,25 @@ void Character2D::RegisterObject(Context* context)
 
     // These macros register the class attributes to the Context for automatic load / save handling.
     // We specify the 'Default' attribute mode which means it will be used both for saving into file, and network replication.
-    ATTRIBUTE("Move Speed Scale", float, moveSpeedScale_, 1.0f, AM_DEFAULT);
-    ATTRIBUTE("Camera Zoom", float, zoom_, 0.0f, AM_DEFAULT);
-    ATTRIBUTE("Coins In Level", int, maxCoins_, 0, AM_DEFAULT);
-    ATTRIBUTE("Remaining Coins", int, remainingCoins_, 0, AM_DEFAULT);
-    ATTRIBUTE("Remaining Lifes", int, remainingLifes_, 3, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Move Speed Scale", float, moveSpeedScale_, 1.0f, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Camera Zoom", float, zoom_, 0.0f, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Coins In Level", int, maxCoins_, 0, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Remaining Coins", int, remainingCoins_, 0, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Remaining Lifes", int, remainingLifes_, 3, AM_DEFAULT);
 }
 
 void Character2D::Update(float timeStep)
 {
+    // Handle wounded/killed states
+    if (killed_)
+        return;
+    
+    if (wounded_)
+    {
+        HandleWoundedState(timeStep);
+        return;
+    }
+
     AnimatedSprite2D* animatedSprite = GetComponent<AnimatedSprite2D>();
     Input* input = GetSubsystem<Input>();
 
@@ -106,4 +121,81 @@ void Character2D::Update(float timeStep)
     {
         animatedSprite->SetAnimation("idle");
     }
-}
+}
+
+void Character2D::HandleWoundedState(float timeStep)
+{
+    RigidBody2D* body = GetComponent<RigidBody2D>();
+    AnimatedSprite2D* animatedSprite = GetComponent<AnimatedSprite2D>();
+    
+    // Play "hit" animation in loop
+    if (animatedSprite->GetAnimation() != "hit")
+        animatedSprite->SetAnimation("hit", LM_FORCE_LOOPED);
+    
+    // Update timer
+    timer_ += timeStep;
+    
+    if (timer_ > 2.0f)
+    {
+        // Reset timer
+        timer_ = 0.0f;
+        
+        // Clear forces (should be performed by setting linear velocity to zero, but currently doesn't work)
+        body->SetLinearVelocity(Vector2::ZERO);
+        body->SetAwake(false);
+        body->SetAwake(true);
+        
+        // Remove particle emitter
+        node_->GetChild("Emitter", true)->Remove();
+        
+        // Update lifes UI and counter
+        remainingLifes_ -= 1;
+        UI* ui = GetSubsystem<UI>();
+        Text* lifeText = static_cast<Text*>(ui->GetRoot()->GetChild("LifeText", true));
+        lifeText->SetText(String(remainingLifes_)); // Update lifes UI counter
+        
+        // Reset wounded state
+        wounded_ = false;
+        
+        // Handle death
+        if (remainingLifes_ == 0)
+        {
+            HandleDeath();
+            return;
+        }
+        
+        // Re-position the character to the nearest point
+        if (node_->GetPosition().x_ < 15.0f)
+            node_->SetPosition(Vector3(-5.0f, 11.0f, 0.0f));
+        else
+            node_->SetPosition(Vector3(18.8f, 9.2f, 0.0f));
+    }
+}
+
+void Character2D::HandleDeath()
+{
+    RigidBody2D* body = GetComponent<RigidBody2D>();
+    AnimatedSprite2D* animatedSprite = GetComponent<AnimatedSprite2D>();
+    
+    // Set state to 'killed'
+    killed_ = true;
+    
+    // Update UI elements
+    UI* ui = GetSubsystem<UI>();
+    Text* instructions = static_cast<Text*>(ui->GetRoot()->GetChild("Instructions", true));
+    instructions->SetText("!!! GAME OVER !!!");
+    static_cast<Text*>(ui->GetRoot()->GetChild("ExitButton", true))->SetVisible(true);
+    static_cast<Text*>(ui->GetRoot()->GetChild("PlayButton", true))->SetVisible(true);
+
+    // Show mouse cursor so that we can click
+    Input* input = GetSubsystem<Input>();
+    input->SetMouseVisible(true);
+    
+    // Put character outside of the scene and magnify him
+    node_->SetPosition(Vector3(-20.0f, 0.0f, 0.0f));
+    node_->SetScale(1.2f);
+    
+    // Play death animation once
+    if (animatedSprite->GetAnimation() != "dead")
+        animatedSprite->SetAnimation("dead");
+}

+ 16 - 4
Source/Samples/39_Urho2DIsometricDemo/Character2D.h → Source/Samples/49_Urho2DIsometricDemo/Character2D.h

@@ -27,13 +27,13 @@
 // All Urho3D classes reside in namespace Urho3D
 using namespace Urho3D;
 
-const float MOVE_SPEED_X = 1.5f;
+const float MOVE_SPEED_X = 4.0f;
 const int LIFES = 3;
 
 /// Character2D component controling Imp behavior.
 class Character2D : public LogicComponent
 {
-    OBJECT(Character2D)
+    URHO3D_OBJECT(Character2D, LogicComponent);
 
 public:
     /// Construct.
@@ -43,8 +43,20 @@ public:
     static void RegisterObject(Context* context);
 
     /// Handle update. Called by LogicComponent base class.
-    virtual void Update(float timeStep);
+    virtual void Update(float timeStep) override;
 
+    /// Handle player state/behavior when wounded.
+    void HandleWoundedState(float timeStep);
+    
+    /// Handle death of the player.
+    void HandleDeath();
+    
+    /// Flag when player is wounded.
+    bool wounded_;
+    /// Flag when player is dead.
+    bool killed_;
+    /// Timer for particle emitter duration.
+    float timer_;
     /// Number of coins in the current level.
     int maxCoins_;
     /// Counter for remaining coins to pick.
@@ -55,4 +67,4 @@ public:
     float moveSpeedScale_;
     /// Camera's zoom (used to scale movement speed based on camera zoom).
     float zoom_;
-};
+};

+ 87 - 9
Source/Samples/39_Urho2DIsometricDemo/Urho2DIsometricDemo.cpp → Source/Samples/49_Urho2DIsometricDemo/Urho2DIsometricDemo.cpp

@@ -50,6 +50,8 @@
 #include <Urho3D/Urho2D/TmxFile2D.h>
 #include <Urho3D/UI/UIEvents.h>
 #include <Urho3D/Graphics/Zone.h>
+#include <Urho3D/Urho2D/PhysicsEvents2D.h>
+#include <Urho3D/Urho2D/PhysicsWorld2D.h>
 
 #include <Urho3D/DebugNew.h>
 
@@ -58,9 +60,6 @@
 #include "Utilities2D/Mover.h"
 #include "Urho2DIsometricDemo.h"
 
-
-DEFINE_APPLICATION_MAIN(Urho2DIsometricDemo)
-
 Urho2DIsometricDemo::Urho2DIsometricDemo(Context* context) :
     Sample(context),
     zoom_(2.0f),
@@ -72,6 +71,12 @@ Urho2DIsometricDemo::Urho2DIsometricDemo(Context* context) :
     Mover::RegisterObject(context);
 }
 
+void Urho2DIsometricDemo::Setup()
+{
+    Sample::Setup();
+    engineParameters_[EP_SOUND] = true;
+}
+
 void Urho2DIsometricDemo::Start()
 {
     // Execute base class startup
@@ -89,7 +94,7 @@ void Urho2DIsometricDemo::Start()
     sample2D_->CreateUIContent("ISOMETRIC 2.5D DEMO", character2D_->remainingLifes_, character2D_->remainingCoins_);
     UI* ui = GetSubsystem<UI>();
     Button* playButton = static_cast<Button*>(ui->GetRoot()->GetChild("PlayButton", true));
-    SubscribeToEvent(playButton, E_RELEASED, HANDLER(Urho2DIsometricDemo, HandlePlayButton));
+    SubscribeToEvent(playButton, E_RELEASED, URHO3D_HANDLER(Urho2DIsometricDemo, HandlePlayButton));
 
     // Hook up to the frame update events
     SubscribeToEvents();
@@ -143,8 +148,76 @@ void Urho2DIsometricDemo::CreateScene()
     // Instantiate enemies at each placeholder of "MovingEntities" layer (placeholders are Poly Line objects defining a path from points)
     sample2D_->PopulateMovingEntities(tileMap->GetLayer(tileMap->GetNumLayers() - 2));
 
+    // Instantiate coins to pick at each placeholder of "Coins" layer (placeholders for coins are Rectangle objects)
+    TileMapLayer2D* coinsLayer = tileMap->GetLayer(tileMap->GetNumLayers() - 3);
+    sample2D_->PopulateCoins(coinsLayer);
+    
+    // Init coins counters
+    character2D_->remainingCoins_ = coinsLayer->GetNumObjects();
+    character2D_->maxCoins_ = coinsLayer->GetNumObjects();
+    
     // Check when scene is rendered
-    SubscribeToEvent(E_ENDRENDERING, HANDLER(Urho2DIsometricDemo, HandleSceneRendered));
+    SubscribeToEvent(E_ENDRENDERING, URHO3D_HANDLER(Urho2DIsometricDemo, HandleSceneRendered));
+}
+
+void Urho2DIsometricDemo::HandleCollisionBegin(StringHash eventType, VariantMap& eventData)
+{
+    // Get colliding node
+    Node* hitNode = static_cast<Node*>(eventData[PhysicsBeginContact2D::P_NODEA].GetPtr());
+    if (hitNode->GetName() == "Imp")
+        hitNode = static_cast<Node*>(eventData[PhysicsBeginContact2D::P_NODEB].GetPtr());
+    String nodeName = hitNode->GetName();
+    Node* character2DNode = scene_->GetChild("Imp", true);
+    
+    // Handle coins picking
+    if (nodeName == "Coin")
+    {
+        hitNode->Remove();
+        character2D_->remainingCoins_ -= 1;
+        UI* ui = GetSubsystem<UI>();
+        if (character2D_->remainingCoins_ == 0)
+        {
+            Text* instructions = static_cast<Text*>(ui->GetRoot()->GetChild("Instructions", true));
+            instructions->SetText("!!! You have all the coins !!!");
+        }
+        Text* coinsText = static_cast<Text*>(ui->GetRoot()->GetChild("CoinsText", true));
+        coinsText->SetText(String(character2D_->remainingCoins_)); // Update coins UI counter
+        sample2D_->PlaySoundEffect("Powerup.wav");
+    }
+    
+    // Handle interactions with enemies
+    if (nodeName == "Orc")
+    {
+        AnimatedSprite2D* animatedSprite = character2DNode->GetComponent<AnimatedSprite2D>();
+        float deltaX = character2DNode->GetPosition().x_ - hitNode->GetPosition().x_;
+        
+        // Orc killed if character is fighting in its direction when the contact occurs
+        if (animatedSprite->GetAnimation() == "attack" && (deltaX < 0 == animatedSprite->GetFlipX()))
+        {
+            static_cast<Mover*>(hitNode->GetComponent<Mover>())->emitTime_ = 1;
+            if (!hitNode->GetChild("Emitter", true))
+            {
+                hitNode->GetComponent("RigidBody2D")->Remove(); // Remove Orc's body
+                sample2D_->SpawnEffect(hitNode);
+                sample2D_->PlaySoundEffect("BigExplosion.wav");
+            }
+        }
+        // Player killed if not fighting in the direction of the Orc when the contact occurs
+        else
+        {
+            if (!character2DNode->GetChild("Emitter", true))
+            {
+                character2D_->wounded_ = true;
+                if (nodeName == "Orc")
+                {
+                    Mover* orc = static_cast<Mover*>(hitNode->GetComponent<Mover>());
+                    orc->fightTimer_ = 1;
+                }
+                sample2D_->SpawnEffect(character2DNode);
+                sample2D_->PlaySoundEffect("BigExplosion.wav");
+            }
+        }
+    }
 }
 
 void Urho2DIsometricDemo::HandleSceneRendered(StringHash eventType, VariantMap& eventData)
@@ -159,16 +232,19 @@ void Urho2DIsometricDemo::HandleSceneRendered(StringHash eventType, VariantMap&
 void Urho2DIsometricDemo::SubscribeToEvents()
 {
     // Subscribe HandleUpdate() function for processing update events
-    SubscribeToEvent(E_UPDATE, HANDLER(Urho2DIsometricDemo, HandleUpdate));
+    SubscribeToEvent(E_UPDATE, URHO3D_HANDLER(Urho2DIsometricDemo, HandleUpdate));
 
     // Subscribe HandlePostUpdate() function for processing post update events
-    SubscribeToEvent(E_POSTUPDATE, HANDLER(Urho2DIsometricDemo, HandlePostUpdate));
+    SubscribeToEvent(E_POSTUPDATE, URHO3D_HANDLER(Urho2DIsometricDemo, HandlePostUpdate));
 
     // Subscribe to PostRenderUpdate to draw debug geometry
-    SubscribeToEvent(E_POSTRENDERUPDATE, HANDLER(Urho2DIsometricDemo, HandlePostRenderUpdate));
+    SubscribeToEvent(E_POSTRENDERUPDATE, URHO3D_HANDLER(Urho2DIsometricDemo, HandlePostRenderUpdate));
 
     // Unsubscribe the SceneUpdate event from base class to prevent camera pitch and yaw in 2D sample
     UnsubscribeFromEvent(E_SCENEUPDATE);
+    
+    // Subscribe to Box2D contact listeners
+    SubscribeToEvent(E_PHYSICSBEGINCONTACT2D, URHO3D_HANDLER(Urho2DIsometricDemo, HandleCollisionBegin));
 }
 
 void Urho2DIsometricDemo::HandleUpdate(StringHash eventType, VariantMap& eventData)
@@ -272,4 +348,6 @@ void Urho2DIsometricDemo::HandlePlayButton(StringHash eventType, VariantMap& eve
     // Hide mouse cursor
     Input* input = GetSubsystem<Input>();
     input->SetMouseVisible(false);
-}
+}
+
+URHO3D_DEFINE_APPLICATION_MAIN(Urho2DIsometricDemo)

+ 7 - 3
Source/Samples/39_Urho2DIsometricDemo/Urho2DIsometricDemo.h → Source/Samples/49_Urho2DIsometricDemo/Urho2DIsometricDemo.h

@@ -38,7 +38,7 @@ class Sample2D;
 /// Note that this sample uses some functions from Sample2D utility class.
 class Urho2DIsometricDemo : public Sample
 {
-    OBJECT(Urho2DIsometricDemo);
+    URHO3D_OBJECT(Urho2DIsometricDemo, Sample);
 
 public:
     /// Construct.
@@ -46,7 +46,9 @@ public:
 
     /// Setup after engine initialization and before running the main loop.
     virtual void Start();
-
+    /// Setup before engine initialization. Modifies the engine parameters.
+    virtual void Setup();
+    
 private:
     /// Construct the scene content.
     void CreateScene();
@@ -64,6 +66,8 @@ private:
     void HandleSceneRendered(StringHash eventType, VariantMap& eventData);
     /// Handle reloading the scene.
     void ReloadScene(bool reInit);
+    /// Handle the contact begin event (Box2D contact listener).
+    void HandleCollisionBegin(StringHash eventType, VariantMap& eventData);
     /// Handle 'PLAY' button released event.
     void HandlePlayButton(StringHash eventType, VariantMap& eventData);
 
@@ -76,4 +80,4 @@ private:
 
     /// Sample2D utility object.
     SharedPtr<Sample2D> sample2D_;
-};
+};

+ 1 - 1
Source/Samples/40_Urho2DPlatformer/CMakeLists.txt → Source/Samples/50_Urho2DPlatformer/CMakeLists.txt

@@ -21,7 +21,7 @@
 #
 
 # Define target name
-set (TARGET_NAME 40_Urho2DPlatformer)
+set (TARGET_NAME 50_Urho2DPlatformer)
 
 # Define source files
 define_source_files (EXTRA_H_FILES ${COMMON_SAMPLE_H_FILES} ../Utilities2D/Sample2D.h ../Utilities2D/Sample2D.cpp ../Utilities2D/Mover.h ../Utilities2D/Mover.cpp)

+ 10 - 10
Source/Samples/40_Urho2DPlatformer/Character2D.cpp → Source/Samples/50_Urho2DPlatformer/Character2D.cpp

@@ -60,16 +60,16 @@ void Character2D::RegisterObject(Context* context)
 
     // These macros register the class attributes to the Context for automatic load / save handling.
     // We specify the 'Default' attribute mode which means it will be used both for saving into file, and network replication.
-    ATTRIBUTE("Wounded", bool, wounded_, false, AM_DEFAULT);
-    ATTRIBUTE("Killed", bool, killed_, false, AM_DEFAULT);
-    ATTRIBUTE("Timer", float, timer_, 0.0f, AM_DEFAULT);
-    ATTRIBUTE("Coins In Level", int, maxCoins_, 0, AM_DEFAULT);
-    ATTRIBUTE("Remaining Coins", int, remainingCoins_, 0, AM_DEFAULT);
-    ATTRIBUTE("Remaining Lifes", int, remainingLifes_, 3, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Wounded", bool, wounded_, false, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Killed", bool, killed_, false, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Timer", float, timer_, 0.0f, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Coins In Level", int, maxCoins_, 0, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Remaining Coins", int, remainingCoins_, 0, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Remaining Lifes", int, remainingLifes_, 3, AM_DEFAULT);
     // Note that we don't load/save isClimbing_ as the contact listener already sets this bool.
-    ATTRIBUTE("Is Climbing Rope", bool, climb2_, false, AM_DEFAULT);
-    ATTRIBUTE("Is Above Climbable", bool, aboveClimbable_, false, AM_DEFAULT);
-    ATTRIBUTE("Is On Slope", bool, onSlope_, false, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Is Climbing Rope", bool, climb2_, false, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Is Above Climbable", bool, aboveClimbable_, false, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Is On Slope", bool, onSlope_, false, AM_DEFAULT);
 }
 
 void Character2D::Update(float timeStep)
@@ -234,4 +234,4 @@ void Character2D::HandleDeath()
     // Play death animation once
     if (animatedSprite->GetAnimation() != "dead2")
         animatedSprite->SetAnimation("dead2");
-}
+}

+ 3 - 3
Source/Samples/40_Urho2DPlatformer/Character2D.h → Source/Samples/50_Urho2DPlatformer/Character2D.h

@@ -33,7 +33,7 @@ const int LIFES = 3;
 /// Character2D component controling Imp behavior.
 class Character2D : public LogicComponent
 {
-    OBJECT(Character2D)
+    URHO3D_OBJECT(Character2D, LogicComponent);
 
 public:
     /// Construct.
@@ -43,7 +43,7 @@ public:
     static void RegisterObject(Context* context);
 
     /// Handle update. Called by LogicComponent base class.
-    virtual void Update(float timeStep);
+    virtual void Update(float timeStep) override;
     /// Handle player state/behavior when wounded.
     void HandleWoundedState(float timeStep);
     /// Handle death of the player.
@@ -69,4 +69,4 @@ public:
     bool aboveClimbable_;
     /// Indicate when the player is climbing a slope, so we can apply force to its body.
     bool onSlope_;
-};
+};

+ 21 - 14
Source/Samples/40_Urho2DPlatformer/Urho2DPlatformer.cpp → Source/Samples/50_Urho2DPlatformer/Urho2DPlatformer.cpp

@@ -22,6 +22,7 @@
 
 #include <Urho3D/Urho3D.h>
 
+#include <Urho3D/Audio/Audio.h>
 #include <Urho3D/Urho2D/AnimatedSprite2D.h>
 #include <Urho3D/Urho2D/AnimationSet2D.h>
 #include <Urho3D/UI/Button.h>
@@ -63,7 +64,7 @@
 #include "Urho2DPlatformer.h"
 
 
-DEFINE_APPLICATION_MAIN(Urho2DPlatformer)
+URHO3D_DEFINE_APPLICATION_MAIN(Urho2DPlatformer)
 
 Urho2DPlatformer::Urho2DPlatformer(Context* context) :
     Sample(context),
@@ -75,6 +76,12 @@ Urho2DPlatformer::Urho2DPlatformer(Context* context) :
     Mover::RegisterObject(context);
 }
 
+void Urho2DPlatformer::Setup()
+{
+    Sample::Setup();
+    engineParameters_[EP_SOUND] = true;
+}
+
 void Urho2DPlatformer::Start()
 {
     // Execute base class startup
@@ -92,7 +99,7 @@ void Urho2DPlatformer::Start()
     sample2D_->CreateUIContent("PLATFORMER 2D DEMO", character2D_->remainingLifes_, character2D_->remainingCoins_);
     UI* ui = GetSubsystem<UI>();
     Button* playButton = static_cast<Button*>(ui->GetRoot()->GetChild("PlayButton", true));
-    SubscribeToEvent(playButton, E_RELEASED, HANDLER(Urho2DPlatformer, HandlePlayButton));
+    SubscribeToEvent(playButton, E_RELEASED, URHO3D_HANDLER(Urho2DPlatformer, HandlePlayButton));
 
     // Hook up to the frame update events
     SubscribeToEvents();
@@ -106,7 +113,7 @@ void Urho2DPlatformer::CreateScene()
     // Create the Octree, DebugRenderer and PhysicsWorld2D components to the scene
     scene_->CreateComponent<Octree>();
     scene_->CreateComponent<DebugRenderer>();
-    PhysicsWorld2D* physicsWorld = scene_->CreateComponent<PhysicsWorld2D>();
+    /*PhysicsWorld2D* physicsWorld =*/ scene_->CreateComponent<PhysicsWorld2D>(); 
 
     // Create camera
     cameraNode_ = scene_->CreateChild("Camera");
@@ -159,7 +166,7 @@ void Urho2DPlatformer::CreateScene()
     sample2D_->CreateBackgroundSprite(info, 3.5, "Textures/HeightMap.png", true);
 
     // Check when scene is rendered
-    SubscribeToEvent(E_ENDRENDERING, HANDLER(Urho2DPlatformer, HandleSceneRendered));
+    SubscribeToEvent(E_ENDRENDERING, URHO3D_HANDLER(Urho2DPlatformer, HandleSceneRendered));
 }
 
 void Urho2DPlatformer::HandleSceneRendered(StringHash eventType, VariantMap& eventData)
@@ -174,17 +181,17 @@ void Urho2DPlatformer::HandleSceneRendered(StringHash eventType, VariantMap& eve
 void Urho2DPlatformer::SubscribeToEvents()
 {
     // Subscribe HandleUpdate() function for processing update events
-    SubscribeToEvent(E_UPDATE, HANDLER(Urho2DPlatformer, HandleUpdate));
+    SubscribeToEvent(E_UPDATE, URHO3D_HANDLER(Urho2DPlatformer, HandleUpdate));
 
     // Subscribe HandlePostUpdate() function for processing post update events
-    SubscribeToEvent(E_POSTUPDATE, HANDLER(Urho2DPlatformer, HandlePostUpdate));
+    SubscribeToEvent(E_POSTUPDATE, URHO3D_HANDLER(Urho2DPlatformer, HandlePostUpdate));
 
     // Subscribe to PostRenderUpdate to draw debug geometry
-    SubscribeToEvent(E_POSTRENDERUPDATE, HANDLER(Urho2DPlatformer, HandlePostRenderUpdate));
+    SubscribeToEvent(E_POSTRENDERUPDATE, URHO3D_HANDLER(Urho2DPlatformer, HandlePostRenderUpdate));
 
     // Subscribe to Box2D contact listeners
-    SubscribeToEvent(E_PHYSICSBEGINCONTACT2D, HANDLER(Urho2DPlatformer, HandleCollisionBegin));
-    SubscribeToEvent(E_PHYSICSENDCONTACT2D, HANDLER(Urho2DPlatformer, HandleCollisionEnd));
+    SubscribeToEvent(E_PHYSICSBEGINCONTACT2D, URHO3D_HANDLER(Urho2DPlatformer, HandleCollisionBegin));
+    SubscribeToEvent(E_PHYSICSENDCONTACT2D, URHO3D_HANDLER(Urho2DPlatformer, HandleCollisionEnd));
 
     // Unsubscribe the SceneUpdate event from base class to prevent camera pitch and yaw in 2D sample
     UnsubscribeFromEvent(E_SCENEUPDATE);
@@ -232,7 +239,7 @@ void Urho2DPlatformer::HandleCollisionBegin(StringHash eventType, VariantMap& ev
         }
         Text* coinsText = static_cast<Text*>(ui->GetRoot()->GetChild("CoinsText", true));
         coinsText->SetText(String(character2D_->remainingCoins_)); // Update coins UI counter
-        sample2D_->PlaySound("Powerup.wav");
+        sample2D_->PlaySoundEffect("Powerup.wav");
     }
 
     // Handle interactions with enemies
@@ -249,7 +256,7 @@ void Urho2DPlatformer::HandleCollisionBegin(StringHash eventType, VariantMap& ev
             {
                 hitNode->GetComponent("RigidBody2D")->Remove(); // Remove Orc's body
                 sample2D_->SpawnEffect(hitNode);
-                sample2D_->PlaySound("BigExplosion.wav");
+                sample2D_->PlaySoundEffect("BigExplosion.wav");
             }
         }
         // Player killed if not fighting in the direction of the Orc when the contact occurs, or when colliding with a flower
@@ -264,7 +271,7 @@ void Urho2DPlatformer::HandleCollisionBegin(StringHash eventType, VariantMap& ev
                     orc->fightTimer_ = 1;
                 }
                 sample2D_->SpawnEffect(character2DNode);
-                sample2D_->PlaySound("BigExplosion.wav");
+                sample2D_->PlaySoundEffect("BigExplosion.wav");
             }
         }
     }
@@ -291,7 +298,7 @@ void Urho2DPlatformer::HandleCollisionBegin(StringHash eventType, VariantMap& ev
         {
             character2D_->wounded_ = true;
             sample2D_->SpawnEffect(character2DNode);
-            sample2D_->PlaySound("BigExplosion.wav");
+            sample2D_->PlaySoundEffect("BigExplosion.wav");
         }
     }
 
@@ -437,4 +444,4 @@ void Urho2DPlatformer::HandlePlayButton(StringHash eventType, VariantMap& eventD
     // Hide mouse cursor
     Input* input = GetSubsystem<Input>();
     input->SetMouseVisible(false);
-}
+}

+ 5 - 3
Source/Samples/40_Urho2DPlatformer/Urho2DPlatformer.h → Source/Samples/50_Urho2DPlatformer/Urho2DPlatformer.h

@@ -40,7 +40,7 @@ class Sample2D;
 /// Note that this sample uses some functions from Sample2D utility class.
 class Urho2DPlatformer : public Sample
 {
-    OBJECT(Urho2DPlatformer);
+    URHO3D_OBJECT(Urho2DPlatformer, Sample);
 
 public:
     /// Construct.
@@ -48,7 +48,9 @@ public:
 
     /// Setup after engine initialization and before running the main loop.
     virtual void Start();
-
+    /// Setup before engine initialization. Modifies the engine parameters.
+    virtual void Setup();
+    
 private:
     /// Construct the scene content.
     void CreateScene();
@@ -82,4 +84,4 @@ private:
 
     /// Sample2D utility object.
     SharedPtr<Sample2D> sample2D_;
-};
+};

+ 7 - 7
Source/Samples/Utilities2D/Mover.cpp

@@ -51,12 +51,12 @@ void Mover::RegisterObject(Context* context)
 
     // These macros register the class attribute to the Context for automatic load / save handling.
     // We specify the Default attribute mode which means it will be used both for saving into file, and network replication.
-    MIXED_ACCESSOR_ATTRIBUTE("Path", GetPathAttr, SetPathAttr, PODVector<unsigned char>, Variant::emptyBuffer, AM_DEFAULT);
-    ATTRIBUTE("Speed", float, speed_, 0.8f, AM_DEFAULT);
-    ATTRIBUTE("Current Path ID", int, currentPathID_, 1, AM_DEFAULT);
-    ATTRIBUTE("Emit Time", float, emitTime_, 0.0f, AM_DEFAULT);
-    ATTRIBUTE("Fight Timer", float, fightTimer_, 0.0f, AM_DEFAULT);
-    ATTRIBUTE("Flip Animation", float, flip_, 0.0f, AM_DEFAULT);
+    URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Path", GetPathAttr, SetPathAttr, PODVector<unsigned char>, Variant::emptyBuffer, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Speed", float, speed_, 0.8f, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Current Path ID", int, currentPathID_, 1, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Emit Time", float, emitTime_, 0.0f, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Fight Timer", float, fightTimer_, 0.0f, AM_DEFAULT);
+    URHO3D_ATTRIBUTE("Flip Animation", float, flip_, 0.0f, AM_DEFAULT);
 }
 
 void Mover::SetPathAttr(const PODVector<unsigned char>& value)
@@ -163,4 +163,4 @@ void Mover::Update(float timeStep)
             }
         }
     }
-}
+}

+ 2 - 2
Source/Samples/Utilities2D/Mover.h

@@ -33,7 +33,7 @@ using namespace Urho3D;
 ///    - Default speed is 0.8 if 'Speed' property is not set in the tmx file objects
 class Mover : public LogicComponent
 {
-    OBJECT(Mover);
+    URHO3D_OBJECT(Mover, LogicComponent);
 
 public:
     /// Construct.
@@ -61,4 +61,4 @@ public:
     float fightTimer_;
     /// Flip animation based on direction, or player position when fighting.
     float flip_;
-};
+};

+ 26 - 15
Source/Samples/Utilities2D/Sample2D.cpp

@@ -179,8 +179,11 @@ Node* Sample2D::CreateCharacter(TileMapInfo2D info, float friction, Vector3 posi
     spriteNode->SetPosition(position);
     spriteNode->SetScale(scale);
     AnimatedSprite2D* animatedSprite = spriteNode->CreateComponent<AnimatedSprite2D>();
-    animatedSprite->SetAnimation(cache->GetResource<AnimationSet2D>("Urho2D/imp/imp.scml"), "idle"); // Get scml file and Play "idle" anim
-    animatedSprite->SetLayer(2); // Put character over tile map (which is on layer 0) and over Orcs (which are on layer 1)
+    // Get scml file and Play "idle" anim
+    AnimationSet2D* animationSet = cache->GetResource<AnimationSet2D>("Urho2D/imp/imp.scml");
+    animatedSprite->SetAnimationSet(animationSet);
+    animatedSprite->SetAnimation("idle");
+    animatedSprite->SetLayer(3); // Put character over tile map (which is on layer 0) and over Orcs (which are on layer 2)
     RigidBody2D* impBody = spriteNode->CreateComponent<RigidBody2D>();
     impBody->SetBodyType(BT_DYNAMIC);
     impBody->SetAllowSleep(false);
@@ -221,8 +224,10 @@ Node* Sample2D::CreateOrc()
     Node* node = scene_->CreateChild("Orc");
     node->SetScale(scene_->GetChild("Imp", true)->GetScale());
     AnimatedSprite2D* animatedSprite = node->CreateComponent<AnimatedSprite2D>();
-    animatedSprite->SetAnimation(cache->GetResource<AnimationSet2D>("Urho2D/Orc/Orc.scml"), "run"); // Get scml file and Play "run" anim
-    animatedSprite->SetLayer(1); // Make orc always visible
+    AnimationSet2D* animationSet = cache->GetResource<AnimationSet2D>("Urho2D/Orc/Orc.scml");
+    animatedSprite->SetAnimationSet(animationSet);
+    animatedSprite->SetAnimation("run"); // Get scml file and Play "run" anim
+    animatedSprite->SetLayer(2); // Make orc always visible
     RigidBody2D* body = node->CreateComponent<RigidBody2D>();
     CollisionCircle2D* shape = node->CreateComponent<CollisionCircle2D>();
     shape->SetRadius(1.3f); // Set shape size
@@ -236,7 +241,10 @@ Node* Sample2D::CreateCoin()
     Node* node = scene_->CreateChild("Coin");
     node->SetScale(0.5);
     AnimatedSprite2D* animatedSprite = node->CreateComponent<AnimatedSprite2D>();
-    animatedSprite->SetAnimation(cache->GetResource<AnimationSet2D>("Urho2D/GoldIcon.scml"), "idle"); // Get scml file and Play "idle" anim
+    AnimationSet2D* animationSet = cache->GetResource<AnimationSet2D>("Urho2D/GoldIcon.scml");
+    animatedSprite->SetAnimationSet(animationSet); // Get scml file and Play "idle" anim
+    animatedSprite->SetAnimation("idle");
+    animatedSprite->SetLayer(4);
     RigidBody2D* body = node->CreateComponent<RigidBody2D>();
     body->SetBodyType(BT_STATIC);
     CollisionCircle2D* shape = node->CreateComponent<CollisionCircle2D>(); // Create circle shape
@@ -320,6 +328,7 @@ void Sample2D::PopulateCoins(TileMapLayer2D* coinsLayer)
         TileMapObject2D* coinObject = coinsLayer->GetObject(i); // Get placeholder object
         Node* coinClone = coinNode->Clone();
         coinClone->SetPosition2D(coinObject->GetPosition() + coinObject->GetSize() / 2 + Vector2(0.0f, 0.16f));
+        
     }
 
     // Remove node used for cloning purpose
@@ -405,8 +414,7 @@ void Sample2D::CreateUIContent(String demoTitle, int remainingLifes, int remaini
     // Create the UI for displaying the remaining lifes
     BorderImage* lifeUI = ui->GetRoot()->CreateChild<BorderImage>("Life");
     lifeUI->SetTexture(cache->GetResource<Texture2D>("Urho2D/imp/imp_all.png"));
-    lifeUI->SetImageRect(IntRect(2, 153, 238, 298));
-    lifeUI->SetSize(80, 50);
+    lifeUI->SetSize(70, 80);
     lifeUI->SetAlignment(HA_RIGHT, VA_TOP);
     lifeUI->SetPosition(-5, 5);
     Text* lifeText = lifeUI->CreateChild<Text>("LifeText");
@@ -435,8 +443,7 @@ void Sample2D::CreateUIContent(String demoTitle, int remainingLifes, int remaini
     // Create the image
     BorderImage* spriteUI = fullUI->CreateChild<BorderImage>("Sprite");
     spriteUI->SetTexture(cache->GetResource<Texture2D>("Urho2D/imp/imp_all.png"));
-    spriteUI->SetSize(240, 150);
-    spriteUI->SetImageRect(IntRect(2, 153, 238, 149));
+    spriteUI->SetSize(238, 271);
     spriteUI->SetAlignment(HA_CENTER, VA_CENTER);
     spriteUI->SetPosition(0, - ui->GetRoot()->GetHeight() / 4);
 
@@ -451,7 +458,7 @@ void Sample2D::CreateUIContent(String demoTitle, int remainingLifes, int remaini
     exitText->SetAlignment(HA_CENTER, VA_CENTER);
     exitText->SetFont(font, 24);
     exitText->SetText("EXIT");
-    SubscribeToEvent(exitButton, E_RELEASED, HANDLER(Sample2D, HandleExitButton));
+    SubscribeToEvent(exitButton, E_RELEASED, URHO3D_HANDLER(Sample2D, HandleExitButton));
 
     // Create the 'PLAY' button
     Button* playButton = ui->GetRoot()->CreateChild<Button>("PlayButton");
@@ -522,13 +529,17 @@ void Sample2D::SpawnEffect(Node* node)
     Node* particleNode = node->CreateChild("Emitter");
     particleNode->SetScale(0.5 / node->GetScale().x_);
     ParticleEmitter2D* particleEmitter = particleNode->CreateComponent<ParticleEmitter2D>();
+    particleEmitter->SetLayer(2);
     particleEmitter->SetEffect(cache->GetResource<ParticleEffect2D>("Urho2D/sun.pex"));
 }
 
-void Sample2D::PlaySound(String soundName)
+void Sample2D::PlaySoundEffect(String soundName)
 {
     ResourceCache* cache = GetSubsystem<ResourceCache>();
-    Node* soundNode = scene_->CreateChild("Sound");
-    SoundSource* source = soundNode->CreateComponent<SoundSource>();
-    source->Play(cache->GetResource<Sound>("Sounds/" + soundName));
-}
+    SoundSource* source = scene_->CreateComponent<SoundSource>();
+    Sound* sound = cache->GetResource<Sound>("Sounds/" + soundName);
+    if (sound != NULL) {
+        source->SetAutoRemoveMode(REMOVE_COMPONENT);
+        source->Play(sound);
+    }
+}

+ 3 - 4
Source/Samples/Utilities2D/Sample2D.h

@@ -24,7 +24,6 @@
 
 #include <Urho3D/Core/Object.h>
 
-
 namespace Urho3D
 {
 
@@ -55,7 +54,7 @@ const float MOVE_SPEED_SCALE = 1.0f; // Scaling factor based on tiles' aspect ra
 ///    - Create XML patch instructions for screen joystick layout
 class Sample2D : public Object
 {
-    OBJECT(Sample2D);
+    URHO3D_OBJECT(Sample2D, Object);
 
 public:
     /// Construct.
@@ -106,7 +105,7 @@ public:
     /// Create a particle emitter attached to the given node.
     void SpawnEffect(Node* node);
     /// Play a non-looping sound effect.
-    void PlaySound(String soundName);
+    void PlaySoundEffect(String soundName);
 
     /// Filename used in load/save functions.
     String demoFilename_;
@@ -135,4 +134,4 @@ protected:
         "    </add>"
         "</patch>";
     }
-};
+};

+ 3 - 0
bin/Data/LuaScripts/39_Urho2DIsometricDemo.lua → bin/Data/LuaScripts/49_Urho2DIsometricDemo.lua

@@ -66,6 +66,9 @@ function CreateScene()
     -- Instantiate enemies and moving platforms at each placeholder of "MovingEntities" layer (placeholders are Poly Line objects defining a path from points)
     PopulateMovingEntities(tileMap:GetLayer(tileMap.numLayers - 2))
 
+    -- Instantiate coins to pick at each placeholder of "Coins" layer (placeholders for coins are Rectangle objects)
+    PopulateCoins(tileMap:GetLayer(tileMap.numLayers - 3))
+
     -- Check when scene is rendered
     SubscribeToEvent("EndRendering", HandleSceneRendered)
 end

+ 4 - 4
bin/Data/LuaScripts/40_Urho2DPlatformer.lua → bin/Data/LuaScripts/50_Urho2DPlatformer.lua

@@ -139,9 +139,9 @@ end
 
 function HandleCollisionBegin(eventType, eventData)
     -- Get colliding node
-    local hitNode = eventData:GetPtr("Node", "NodeA")
+    local hitNode = eventData["NodeA"]:GetPtr("Node")
     if hitNode.name == "Imp" then
-        hitNode = eventData:GetPtr("Node", "NodeB")
+        hitNode = eventData["NodeB"]:GetPtr("Node")
     end
     local nodeName = hitNode.name
     local character = character2DNode:GetScriptObject()
@@ -237,9 +237,9 @@ end
 
 function HandleCollisionEnd(eventType, eventData)
     -- Get colliding node
-    local hitNode = eventData:GetPtr("Node", "NodeA")
+    local hitNode = eventData["NodeA"]:GetPtr("Node")
     if hitNode.name == "Imp" then
-        hitNode = eventData:GetPtr("Node", "NodeB")
+        hitNode = eventData["NodeB"]:GetPtr("Node")
     end
     local nodeName = hitNode.name
     local character = character2DNode:GetScriptObject()

+ 19 - 10
bin/Data/LuaScripts/Utilities/2D/Sample2D.lua

@@ -15,7 +15,7 @@ CAMERA_MIN_DIST = 0.1
 CAMERA_MAX_DIST = 6
 
 MOVE_SPEED = 23 -- Movement speed as world units per second
-MOVE_SPEED_X = 1.5 -- Movement speed for isometric maps
+MOVE_SPEED_X = 2.5 -- Movement speed for isometric maps
 MOVE_SPEED_SCALE = 1 -- Scaling factor based on tiles' aspect ratio
 
 LIFES = 3
@@ -87,11 +87,15 @@ function CreateCharacter(info, createObject, friction, position, scale)
     character2DNode.position = position
     character2DNode:SetScale(scale)
     local animatedSprite = character2DNode:CreateComponent("AnimatedSprite2D")
-    animatedSprite:SetAnimation(cache:GetResource("AnimationSet2D", "Urho2D/imp/imp.scml"), "idle") -- Get scml file and Play "idle" anim
-    animatedSprite:SetLayer(2) -- Put character over tile map (which is on layer 0) and over Orcs (which are on layer 1)
+    local animationSet = cache:GetResource("AnimationSet2D", "Urho2D/imp/imp.scml")
+    animatedSprite.animationSet = animationSet
+    animatedSprite.animation = "idle"
+    animatedSprite:SetLayer(3) -- Put character over tile map (which is on layer 0) and over Orcs (which are on layer 1)
+-- 
     local body = character2DNode:CreateComponent("RigidBody2D")
     body.bodyType = BT_DYNAMIC
     body.allowSleep = false
+
     local shape = character2DNode:CreateComponent("CollisionCircle2D")
     shape.radius = 1.1 -- Set shape size
     shape.friction = friction -- Set friction
@@ -128,8 +132,11 @@ function CreateOrc()
     local node = scene_:CreateChild("Orc")
     node.scale = character2DNode.scale -- Use same scale as player character
     local animatedSprite = node:CreateComponent("AnimatedSprite2D")
-    animatedSprite:SetAnimation(cache:GetResource("AnimationSet2D", "Urho2D/Orc/Orc.scml"), "run") -- Get scml file and Play "run" anim
-    animatedSprite:SetLayer(1) -- Make orc always visible
+    -- Get scml file and Play "run" anim
+    local animationSet = cache:GetResource("AnimationSet2D", "Urho2D/Orc/Orc.scml")
+    animatedSprite.animationSet = animationSet
+    animatedSprite.animation = "run"
+    animatedSprite:SetLayer(2) -- Make orc always visible
     local body = node:CreateComponent("RigidBody2D")
     local shape = node:CreateComponent("CollisionCircle2D") -- Create circle shape
     shape.radius = 1.3 -- Set shape size
@@ -141,7 +148,11 @@ function CreateCoin()
     local node = scene_:CreateChild("Coin")
     node:SetScale(0.5)
     local animatedSprite = node:CreateComponent("AnimatedSprite2D")
-    animatedSprite:SetAnimation(cache:GetResource("AnimationSet2D", "Urho2D/GoldIcon.scml"), "idle") -- Get scml file and Play "idle" anim
+    -- Get scml file and Play "idle" anim
+    local animationSet = cache:GetResource("AnimationSet2D", "Urho2D/GoldIcon.scml")
+    animatedSprite.animationSet = animationSet
+    animatedSprite.animation = "idle"
+    animatedSprite:SetLayer(2)
     local body = node:CreateComponent("RigidBody2D")
     body.bodyType = BT_STATIC
     local shape = node:CreateComponent("CollisionCircle2D") -- Create circle shape
@@ -292,8 +303,7 @@ function CreateUIContent(demoTitle)
     -- Create the UI for displaying the remaining lifes
     local lifeUI = ui.root:CreateChild("BorderImage", "Life")
     lifeUI.texture = cache:GetResource("Texture2D", "Urho2D/imp/imp_all.png")
-    lifeUI.imageRect = IntRect(2, 153, 238, 298)
-    lifeUI:SetSize(80, 50)
+    lifeUI:SetSize(70, 80)
     lifeUI:SetAlignment(HA_RIGHT, VA_TOP)
     lifeUI:SetPosition(-5, 5);
     local lifeText = lifeUI:CreateChild("Text", "LifeText")
@@ -322,8 +332,7 @@ function CreateUIContent(demoTitle)
     -- Create the image
     local spriteUI = fullUI:CreateChild("BorderImage", "Sprite")
     spriteUI.texture = cache:GetResource("Texture2D", "Urho2D/imp/imp_all.png")
-    spriteUI:SetSize(240, 150)
-    spriteUI.imageRect = IntRect(2, 153, 238, 149)
+    spriteUI:SetSize(238, 271)
     spriteUI:SetAlignment(HA_CENTER, VA_CENTER)
     spriteUI:SetPosition(0, - ui.root.height / 4)
 

+ 147 - 1
bin/Data/Scripts/39_Urho2DIsometricDemo.as → bin/Data/Scripts/49_Urho2DIsometricDemo.as

@@ -64,6 +64,9 @@ void CreateScene()
     // Instantiate enemies and moving platforms at each placeholder of "MovingEntities" layer (placeholders are Poly Line objects defining a path from points)
     PopulateMovingEntities(tileMap.GetLayer(tileMap.numLayers - 2));
 
+    // Instantiate coins to pick at each placeholder of "Coins" layer (in this sample, placeholders for coins are Rectangle objects)
+    PopulateCoins(tileMap.GetLayer(tileMap.numLayers - 3));
+
     // Check when scene is rendered
     SubscribeToEvent("EndRendering", "HandleSceneRendered");
 }
@@ -88,6 +91,9 @@ void SubscribeToEvents()
     // Subscribe to PostRenderUpdate to draw physics shapes
     SubscribeToEvent("PostRenderUpdate", "HandlePostRenderUpdate");
 
+    // Subscribe to Box2D contact listeners
+    SubscribeToEvent("PhysicsBeginContact2D", "HandleCollisionBegin");
+
     // Unsubscribe the SceneUpdate event from base class to prevent camera pitch and yaw in 2D sample
     UnsubscribeFromEvent("SceneUpdate");
 }
@@ -131,6 +137,62 @@ void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
     }
 }
 
+void HandleCollisionBegin(StringHash eventType, VariantMap& eventData)
+{
+    // Get colliding node
+    Node@ hitNode = eventData["NodeA"].GetPtr();
+    if (hitNode.name == "Imp")
+        hitNode = eventData["NodeB"].GetPtr();
+    String nodeName = hitNode.name;
+    Character2D@ character = cast<Character2D>(character2DNode.scriptObject);
+
+    // Handle coins picking
+    if (nodeName == "Coin")
+    {
+        hitNode.Remove();
+        character.remainingCoins = character.remainingCoins - 1;
+        if (character.remainingCoins == 0)
+        {
+            Text@ instructions = ui.root.GetChild("Instructions", true);
+            instructions.text = "!!! You got all the coins !!!";
+        }
+        Text@ coinsText = ui.root.GetChild("CoinsText", true);
+        coinsText.text = character.remainingCoins; // Update coins UI counter
+        PlaySound("Powerup.wav");
+    }
+
+    // Handle interactions with enemies
+    if (nodeName == "Orc")
+    {
+        AnimatedSprite2D@ animatedSprite = character2DNode.GetComponent("AnimatedSprite2D");
+        float deltaX = character2DNode.position.x - hitNode.position.x;
+
+        // Orc killed if character is fighting in its direction when the contact occurs
+        if (animatedSprite.animation == "attack" && (deltaX < 0 == animatedSprite.flipX))
+        {
+            cast<Mover>(hitNode.scriptObject).emitTime = 1;
+            if (hitNode.GetChild("Emitter", true) is null)
+            {
+                hitNode.GetComponent("RigidBody2D").Remove(); // Remove Orc's body
+                SpawnEffect(hitNode);
+                PlaySound("BigExplosion.wav");
+            }
+        }
+        // Player killed if not fighting in the direction of the Orc when the contact occurs
+        else
+        {
+            if (character2DNode.GetChild("Emitter", true) is null)
+            {
+                character.wounded = true;
+                if (nodeName == "Orc")
+                    cast<Mover>(hitNode.scriptObject).fightTimer = 1;
+                SpawnEffect(character2DNode);
+                PlaySound("BigExplosion.wav");
+            }
+        }
+    }
+}
+
 // Character2D script object class
 class Character2D : ScriptObject
 {
@@ -146,8 +208,18 @@ class Character2D : ScriptObject
         if (character2DNode is null)
             return;
 
-        AnimatedSprite2D@ animatedSprite = character2DNode.GetComponent("AnimatedSprite2D");
+        // Handle wounded/killed states
+        if (killed)
+            return;
 
+        if (wounded)
+        {
+            HandleWoundedState(timeStep);
+            return;
+        }
+
+        AnimatedSprite2D@ animatedSprite = character2DNode.GetComponent("AnimatedSprite2D");
+        
         // Set direction
         Vector3 moveDir = Vector3(0.0f, 0.0f, 0.0f); // Reset
         float speedX = Clamp(MOVE_SPEED_X / zoom, 0.4f, 1.0f);
@@ -192,4 +264,78 @@ class Character2D : ScriptObject
             animatedSprite.SetAnimation("idle");
         }
     }
+
+    void HandleWoundedState(float timeStep)
+    {
+        RigidBody2D@ body = node.GetComponent("RigidBody2D");
+        AnimatedSprite2D@ animatedSprite = node.GetComponent("AnimatedSprite2D");
+
+        // Play "hit" animation in loop
+        if (animatedSprite.animation != "hit")
+            animatedSprite.SetAnimation("hit", LM_FORCE_LOOPED);
+
+        // Update timer
+        timer = timer + timeStep;
+
+        if (timer > 2.0f)
+        {
+            // Reset timer
+            timer = 0.0f;
+
+            // Clear forces (should be performed by setting linear velocity to zero, but currently doesn't work)
+            body.linearVelocity = Vector2(0.0f, 0.0f);
+            body.awake = false;
+            body.awake = true;
+
+            // Remove particle emitter
+            node.GetChild("Emitter", true).Remove();
+
+            // Update lifes UI and counter
+            remainingLifes = remainingLifes - 1;
+            Text@ lifeText = ui.root.GetChild("LifeText", true);
+            lifeText.text = remainingLifes; // Update lifes UI counter
+
+            // Reset wounded state
+            wounded = false;
+
+            // Handle death
+            if (remainingLifes == 0)
+            {
+                HandleDeath();
+                return;
+            }
+
+            // Re-position the character to the nearest point
+            if (node.position.x < 15.0f)
+                node.position = Vector3(1.0f, 8.0f, 0.0f);
+            else
+                node.position = Vector3(18.8f, 9.2f, 0.0f);
+        }
+    }
+
+    void HandleDeath()
+    {
+        RigidBody2D@ body = node.GetComponent("RigidBody2D");
+        AnimatedSprite2D@ animatedSprite = node.GetComponent("AnimatedSprite2D");
+
+        // Set state to 'killed'
+        killed = true;
+
+        // Update UI elements
+        Text@ instructions = ui.root.GetChild("Instructions", true);
+        instructions.text = "!!! GAME OVER !!!";
+        ui.root.GetChild("ExitButton", true).visible = true;
+        ui.root.GetChild("PlayButton", true).visible = true;
+
+        // Show mouse cursor so that we can click
+        input.mouseVisible = true;
+
+        // Put character outside of the scene and magnify him
+        node.position = Vector3(-20.0f, 0.0f, 0.0f);
+        node.SetScale(1.2f);
+
+        // Play death animation once
+        if (animatedSprite.animation != "dead2")
+            animatedSprite.SetAnimation("dead2");
+    }
 }

+ 0 - 0
bin/Data/Scripts/40_Urho2DPlatformer.as → bin/Data/Scripts/50_Urho2DPlatformer.as


+ 26 - 9
bin/Data/Scripts/Utilities/2D/Sample2D.as

@@ -124,8 +124,14 @@ void CreateCharacter(TileMapInfo2D@ info, bool createObject, float friction, Vec
     character2DNode.position = position;
     character2DNode.SetScale(scale);
     AnimatedSprite2D@ animatedSprite = character2DNode.CreateComponent("AnimatedSprite2D");
-    animatedSprite.SetAnimation(cache.GetResource("AnimationSet2D", "Urho2D/imp/imp.scml"), "idle"); // Get scml file and Play "idle" anim
-    animatedSprite.layer = 2; // Put character over tile map (which is on layer 0) and over Orcs (which are on layer 1)
+    
+    AnimationSet2D@ spriterAnimationSet = cache.GetResource("AnimationSet2D", "Urho2D/imp/imp.scml");
+    if (spriterAnimationSet is null)
+        return;
+
+    animatedSprite.animationSet = spriterAnimationSet;
+    animatedSprite.SetAnimation("idle"); // Get scml file and Play "idle" anim
+    animatedSprite.layer = 3; // Put character over tile map (which is on layer 0) and over Orcs (which are on layer 1)
     RigidBody2D@ characterBody = character2DNode.CreateComponent("RigidBody2D");
     characterBody.bodyType = BT_DYNAMIC;
     characterBody.allowSleep = false;
@@ -167,8 +173,14 @@ Node@ CreateOrc()
     Node@ node = scene_.CreateChild("Orc");
     node.scale = character2DNode.scale; // Use same scale as player character
     AnimatedSprite2D@ animatedSprite = node.CreateComponent("AnimatedSprite2D");
-    animatedSprite.SetAnimation(cache.GetResource("AnimationSet2D", "Urho2D/Orc/Orc.scml"), "run"); // Get scml file and Play "run" anim
-    animatedSprite.layer = 1; // Make orc always visible
+
+    AnimationSet2D@ spriterAnimationSet = cache.GetResource("AnimationSet2D", "Urho2D/Orc/Orc.scml");
+    if (spriterAnimationSet is null)
+        return null;
+
+    animatedSprite.animationSet = spriterAnimationSet;
+    animatedSprite.SetAnimation("run"); // Get scml file and Play "run" anim
+    animatedSprite.layer = 2; // Make orc always visible
     RigidBody2D@ body = node.CreateComponent("RigidBody2D");
     CollisionCircle2D@ shape = node.CreateComponent("CollisionCircle2D"); // Create circle shape
     shape.radius = 1.3f; // Set shape size
@@ -181,7 +193,13 @@ Node@ CreateCoin()
     Node@ node = scene_.CreateChild("Coin");
     node.SetScale(0.5);
     AnimatedSprite2D@ animatedSprite = node.CreateComponent("AnimatedSprite2D");
-    animatedSprite.SetAnimation(cache.GetResource("AnimationSet2D", "Urho2D/GoldIcon.scml"), "idle"); // Get scml file and Play "idle" anim
+    animatedSprite.layer = 4;
+    AnimationSet2D@ spriterAnimationSet = cache.GetResource("AnimationSet2D", "Urho2D/GoldIcon.scml");
+    if (spriterAnimationSet is null)
+        return null;
+
+    animatedSprite.animationSet = spriterAnimationSet;
+    animatedSprite.SetAnimation("idle"); // Get scml file and Play "idle" anim
     RigidBody2D@ body = node.CreateComponent("RigidBody2D");
     body.bodyType = BT_STATIC;
     CollisionCircle2D@ shape = node.CreateComponent("CollisionCircle2D"); // Create circle shape
@@ -344,8 +362,7 @@ void CreateUIContent(String demoTitle)
     // Create the UI for displaying the remaining lifes
     BorderImage@ lifeUI = ui.root.CreateChild("BorderImage", "Life");
     lifeUI.texture = cache.GetResource("Texture2D", "Urho2D/imp/imp_all.png");
-    lifeUI.imageRect = IntRect(2, 153, 238, 298);
-    lifeUI.SetSize(80, 50);
+    lifeUI.SetSize(70, 80);
     lifeUI.SetAlignment(HA_RIGHT, VA_TOP);
     lifeUI.SetPosition(-5, 5);
     Text@ lifeText = lifeUI.CreateChild("Text", "LifeText");
@@ -374,8 +391,7 @@ void CreateUIContent(String demoTitle)
     // Create the image
     BorderImage@ spriteUI = fullUI.CreateChild("BorderImage", "Sprite");
     spriteUI.texture = cache.GetResource("Texture2D", "Urho2D/imp/imp_all.png");
-    spriteUI.SetSize(240, 150);
-    spriteUI.imageRect = IntRect(2, 153, 238, 149);
+    spriteUI.SetSize(238, 271);
     spriteUI.SetAlignment(HA_CENTER, VA_CENTER);
     spriteUI.SetPosition(0, - ui.root.height / 4);
 
@@ -499,6 +515,7 @@ void SpawnEffect(Node@ node)
     particleNode.SetScale(0.5 / node.scale.x);
     ParticleEmitter2D@ particleEmitter = particleNode.CreateComponent("ParticleEmitter2D");
     particleEmitter.effect = cache.GetResource("ParticleEffect2D", "Urho2D/sun.pex");
+    particleEmitter.layer = 2;
 }
 
 void PlaySound(String soundName)

BIN
bin/Data/Urho2D/GoldIcon.png


File diff suppressed because it is too large
+ 3 - 1029
bin/Data/Urho2D/Tilesets/Ortho.tmx


File diff suppressed because it is too large
+ 3 - 1203
bin/Data/Urho2D/Tilesets/atrium.tmx


BIN
bin/Data/Urho2D/imp/imp_all.png


Some files were not shown because too many files changed in this diff