| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- // Copyright (c) 2008-2023 the Urho3D project
- // License: MIT
- #include <Urho3D/Core/Context.h>
- #include <Urho3D/Input/Input.h>
- #include <Urho3D/Physics2D/RigidBody2D.h>
- #include <Urho3D/Scene/Scene.h>
- #include <Urho3D/Scene/SceneEvents.h>
- #include <Urho3D/UI/Text.h>
- #include <Urho3D/UI/UI.h>
- #include <Urho3D/Urho2D/AnimatedSprite2D.h>
- #include <Urho3D/Urho2D/AnimationSet2D.h>
- #include "Character2D.h"
- #include <Urho3D/DebugNew.h>
- // Character2D logic component
- Character2D::Character2D(Context* context) :
- LogicComponent(context),
- wounded_(false),
- killed_(false),
- timer_(0.0f),
- maxCoins_(0),
- remainingCoins_(0),
- remainingLifes_(3),
- moveSpeedScale_(1.0f),
- zoom_(0.0f)
- {
- }
- void Character2D::RegisterObject(Context* context)
- {
- context->RegisterFactory<Character2D>();
- // 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.
- URHO3D_ATTRIBUTE("Move Speed Scale", moveSpeedScale_, 1.0f, AM_DEFAULT);
- URHO3D_ATTRIBUTE("Camera Zoom", zoom_, 0.0f, AM_DEFAULT);
- URHO3D_ATTRIBUTE("Coins In Level", maxCoins_, 0, AM_DEFAULT);
- URHO3D_ATTRIBUTE("Remaining Coins", remainingCoins_, 0, AM_DEFAULT);
- URHO3D_ATTRIBUTE("Remaining Lifes", remainingLifes_, 3, AM_DEFAULT);
- }
- void Character2D::Update(float timeStep)
- {
- // Handle wounded/killed states
- if (killed_)
- return;
- if (wounded_)
- {
- HandleWoundedState(timeStep);
- return;
- }
- auto* animatedSprite = GetComponent<AnimatedSprite2D>();
- auto* input = GetSubsystem<Input>();
- // Set direction
- Vector3 moveDir = Vector3::ZERO; // Reset
- float speedX = Clamp(MOVE_SPEED_X / zoom_, 0.4f, 1.0f);
- float speedY = speedX;
- if (input->GetKeyDown(KEY_A) || input->GetKeyDown(KEY_LEFT))
- {
- moveDir = moveDir + Vector3::LEFT * speedX;
- animatedSprite->SetFlipX(false); // Flip sprite (reset to default play on the X axis)
- }
- if (input->GetKeyDown(KEY_D) || input->GetKeyDown(KEY_RIGHT))
- {
- moveDir = moveDir + Vector3::RIGHT * speedX;
- animatedSprite->SetFlipX(true); // Flip sprite (flip animation on the X axis)
- }
- if (!moveDir.Equals(Vector3::ZERO))
- speedY = speedX * moveSpeedScale_;
- if (input->GetKeyDown(KEY_W) || input->GetKeyDown(KEY_UP))
- moveDir = moveDir + Vector3::UP * speedY;
- if (input->GetKeyDown(KEY_S) || input->GetKeyDown(KEY_DOWN))
- moveDir = moveDir + Vector3::DOWN * speedY;
- // Move
- if (!moveDir.Equals(Vector3::ZERO))
- node_->Translate(moveDir * timeStep);
- // Animate
- if (input->GetKeyDown(KEY_SPACE))
- {
- if (animatedSprite->GetAnimation() != "attack")
- animatedSprite->SetAnimation("attack", LM_FORCE_LOOPED);
- }
- else if (!moveDir.Equals(Vector3::ZERO))
- {
- if (animatedSprite->GetAnimation() != "run")
- animatedSprite->SetAnimation("run");
- }
- else if (animatedSprite->GetAnimation() != "idle")
- {
- animatedSprite->SetAnimation("idle");
- }
- }
- void Character2D::HandleWoundedState(float timeStep)
- {
- auto* body = GetComponent<RigidBody2D>();
- auto* 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;
- auto* 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()
- {
- auto* body = GetComponent<RigidBody2D>();
- auto* animatedSprite = GetComponent<AnimatedSprite2D>();
- // Set state to 'killed'
- killed_ = true;
- // Update UI elements
- auto* 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
- auto* 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");
- }
|