Entity update + components
This commit is contained in:
@@ -15,6 +15,7 @@ namespace Game::AGame {
|
||||
setText("Hello, World!");
|
||||
|
||||
mIsActive = false;
|
||||
mIsVisible = false;
|
||||
}
|
||||
void update(float deltaTime) override {
|
||||
// Call the base class update to handle input and text refreshing
|
||||
|
||||
12
include/game/agame/trash.hpp
Normal file
12
include/game/agame/trash.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <game/gamemanager.hpp>
|
||||
#include <object/entity.hpp>
|
||||
#include <renderer/texture.hpp>
|
||||
#include <renderer/font.hpp>
|
||||
#include <object/sound.hpp>
|
||||
|
||||
namespace Game::AGame {
|
||||
GAME_ENTITY(Trash)
|
||||
END_GAME_ENTITY()
|
||||
}
|
||||
@@ -50,6 +50,13 @@ namespace Game {
|
||||
static GameStateEnum getCurrentGameState() { return mCurrentGameState; }
|
||||
static void setCurrentGameState(GameStateEnum newState) { mCurrentGameState = newState; }
|
||||
|
||||
template<typename T>
|
||||
static bool instantiateEntity(std::unique_ptr<T> entity);
|
||||
template<typename T>
|
||||
static T* getEntityByName(const std::string& name);
|
||||
template<typename T>
|
||||
static void destroyEntity(T* entity);
|
||||
|
||||
private:
|
||||
int mTargetUpdatesPerSecond = TARGET_UPDATE_RATE;
|
||||
clock::time_point mLastUpdate;
|
||||
@@ -114,4 +121,26 @@ namespace Game {
|
||||
|
||||
return T{};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool GameManager::instantiateEntity(std::unique_ptr<T> entity) {
|
||||
static_assert(std::is_base_of_v<Object::Entity, T>, "T must derive from Object::Entity");
|
||||
State::GameState::getInstance().addEntity(std::move(entity));
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* GameManager::getEntityByName(const std::string& name) {
|
||||
static_assert(std::is_base_of_v<Object::Entity, T>, "T must derive from Object::Entity");
|
||||
Object::Entity* entity = State::GameState::getInstance().getEntityByName(name);
|
||||
return dynamic_cast<T*>(entity);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void GameManager::destroyEntity(T* entity) {
|
||||
static_assert(std::is_base_of_v<Object::Entity, T>, "T must derive from Object::Entity");
|
||||
if (entity) {
|
||||
State::GameState::getInstance().removeEntity(entity->getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
36
include/object/components/component.hpp
Normal file
36
include/object/components/component.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <utils.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace Game::Object {
|
||||
class Entity;
|
||||
}
|
||||
|
||||
namespace Game::Object::Components {
|
||||
class Component {
|
||||
public:
|
||||
Component() = default;
|
||||
|
||||
// Declare copy and move; in case components declare some other things, we want to make sure those are properly copied/moved as well, so we'll just define these as virtual and implement them in the .cpp file
|
||||
Component(const Component&);
|
||||
Component& operator=(const Component&);
|
||||
Component(Component&&) noexcept;
|
||||
Component& operator=(Component&&) noexcept;
|
||||
|
||||
virtual ~Component() = 0;
|
||||
virtual void start() = 0;
|
||||
virtual void update(float deltaTime, Object::Entity* thisEntity) = 0;
|
||||
|
||||
// Getters and setters
|
||||
void setName(const std::string& name);
|
||||
std::string getName();
|
||||
void setActive(bool active);
|
||||
bool isActive();
|
||||
|
||||
protected:
|
||||
// Uniqueness is NOT enforced; be careful when naming!
|
||||
std::string mName;
|
||||
bool mIsActive = true;
|
||||
};
|
||||
}
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
#include <renderer/renderer.hpp>
|
||||
#include <vector>
|
||||
#include <object/components/component.hpp>
|
||||
|
||||
namespace Game::Renderer {
|
||||
class Renderer;
|
||||
@@ -44,14 +46,57 @@ namespace Game::Object {
|
||||
bool isActive() { return mIsActive; }
|
||||
int getZIndex() const { return mZIndex; }
|
||||
|
||||
// Component management
|
||||
template<typename T, typename... Args>
|
||||
T* addComponent(Args&&... args);
|
||||
template<typename T>
|
||||
T* getComponent();
|
||||
template<typename T>
|
||||
bool removeComponent();
|
||||
void updateComponents(float deltaTime);
|
||||
|
||||
protected:
|
||||
std::string mName;
|
||||
std::shared_ptr<Game::Renderer::Texture> mTex;
|
||||
Transform mTransform;
|
||||
bool mIsFlipped = false; // Whether the texture should be rendered flipped horizontally or not
|
||||
bool mIsActive;
|
||||
bool mIsActive = true;
|
||||
bool mIsVisible = true;
|
||||
int mZIndex = 0; // For rendering order; higher zIndex renders on top of lower zIndex
|
||||
float mTiledScale = 1.f; // Only used if the texture is tiled, to determine how much to scale the texture when rendering (since the entire texture is rendered as a single tile, this is necessary to be able to have different sized tiles)
|
||||
std::vector<std::unique_ptr<Object::Components::Component>> mComponents; // Components attached to this entity; TODO
|
||||
private:
|
||||
};
|
||||
|
||||
template<typename T, typename... Args>
|
||||
T* Entity::addComponent(Args&&... args) {
|
||||
static_assert(std::is_base_of_v<Object::Components::Component, T>, "T must derive from Component");
|
||||
auto component = std::make_unique<T>(std::forward<Args>(args)...);
|
||||
T* componentPtr = component.get();
|
||||
mComponents.push_back(std::move(component));
|
||||
return componentPtr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* Entity::getComponent() {
|
||||
static_assert(std::is_base_of_v<Object::Components::Component, T>, "T must derive from Component");
|
||||
for (const auto& component : mComponents) {
|
||||
if (auto casted = dynamic_cast<T*>(component.get())) {
|
||||
return casted;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool Entity::removeComponent() {
|
||||
static_assert(std::is_base_of_v<Object::Components::Component, T>, "T must derive from Component");
|
||||
for (auto it = mComponents.begin(); it != mComponents.end(); it++) {
|
||||
if (dynamic_cast<T*>(it->get())) {
|
||||
mComponents.erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -24,8 +24,7 @@ namespace Game::AGame {
|
||||
}
|
||||
|
||||
void Background::update(float deltaTime) {
|
||||
if (!mIsActive) return;
|
||||
|
||||
return;
|
||||
/*const bool* state = SDL_GetKeyboardState(nullptr);
|
||||
if (state[SDL_SCANCODE_P]) {
|
||||
mTransform.scaleX *= 2.f;
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace Game::AGame {
|
||||
}
|
||||
|
||||
void Player::update(float deltaTime) {
|
||||
if (!mIsActive) return;
|
||||
//mTransform.rotation += 1.f; // Rotate clockwise for testing
|
||||
//mTransform.scaleX = 1.f + 1.f * std::sin(RUNNING_TIME() / 0.5f); // Pulsate scale for testing
|
||||
//mTransform.scaleY = 1.f + 0.5f * std::cos(RUNNING_TIME() / 0.5f); // Pulsate scale for testing
|
||||
|
||||
11
src/game/agame/trash.cpp
Normal file
11
src/game/agame/trash.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include <game/agame/trash.hpp>
|
||||
|
||||
namespace Game::AGame {
|
||||
void Trash::start() {
|
||||
mZIndex = 50;
|
||||
}
|
||||
|
||||
void Trash::update(float deltaTime) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -29,9 +29,14 @@ namespace Game {
|
||||
Input::update(); // Update input states at the start of each frame
|
||||
auto entities = State::GameState::getInstance().getEntitiesSnapshot();
|
||||
for (auto* entity : entities) {
|
||||
if (entity) {
|
||||
entity->update(seconds);
|
||||
if (!entity || !entity->isActive()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update components first
|
||||
entity->updateComponents(seconds);
|
||||
|
||||
entity->update(seconds);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
ERROR("Exception in GameManager thread: " << e.what());
|
||||
@@ -69,4 +74,5 @@ namespace Game {
|
||||
mSharedBools.erase(key);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
43
src/object/components/component.cpp
Normal file
43
src/object/components/component.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#include <object/components/component.hpp>
|
||||
|
||||
namespace Game::Object::Components {
|
||||
Component::Component(const Component& other) : mName(other.mName), mIsActive(other.mIsActive) {
|
||||
LOG("Copied Component: " << mName);
|
||||
}
|
||||
|
||||
Component& Component::operator=(const Component& other) {
|
||||
if (this != &other) {
|
||||
mName = other.mName;
|
||||
mIsActive = other.mIsActive;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Component::Component(Component&& other) noexcept : mName(std::move(other.mName)), mIsActive(other.mIsActive) {
|
||||
LOG("Moved Component: " << mName);
|
||||
}
|
||||
|
||||
Component& Component::operator=(Component&& other) noexcept {
|
||||
if (this != &other) {
|
||||
mName = std::move(other.mName);
|
||||
mIsActive = other.mIsActive;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Component::setName(const std::string& name) {
|
||||
mName = name;
|
||||
}
|
||||
|
||||
std::string Component::getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
void Component::setActive(bool active) {
|
||||
mIsActive = active;
|
||||
}
|
||||
|
||||
bool Component::isActive() {
|
||||
return mIsActive;
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ namespace Game::Object {
|
||||
}
|
||||
|
||||
void Entity::render(Game::Renderer::Renderer* renderer, Game::Renderer::RendererConfig config) {
|
||||
if (!mIsActive || !mTex) return; // Don't render if not active or if there's no texture
|
||||
if (!mIsVisible || !mTex) return; // Don't render if not visible or if there's no texture
|
||||
|
||||
if (!mTex->isTiled()) {
|
||||
float w, h;
|
||||
@@ -98,4 +98,13 @@ namespace Game::Object {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Entity::updateComponents(float deltaTime) {
|
||||
for (const auto& component : mComponents) {
|
||||
if (!component || !component->isActive()) {
|
||||
continue;
|
||||
}
|
||||
component->update(deltaTime, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user