diff --git a/Frostbite2D/include/frostbite2D/2d/actor.h b/Frostbite2D/include/frostbite2D/2d/actor.h new file mode 100644 index 0000000..8bcc59e --- /dev/null +++ b/Frostbite2D/include/frostbite2D/2d/actor.h @@ -0,0 +1,90 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace frostbite2D { + +class Actor; +class Scene; + +using ActorList = IntrusiveList>; + +class Actor : public RefObject, protected IntrusiveListValue> { +public: + using IntrusiveListValue>::GetNext; + using IntrusiveListValue>::GetPrev; + + Actor(); + virtual ~Actor(); + + virtual void Update(float deltaTime); + virtual void Render(); + + void AddChild(RefPtr child); + void RemoveChild(RefPtr child); + void RemoveAllChildren(); + + Actor* GetParent() const { return parent_; } + Scene* GetScene() const { return scene_; } + + const std::string& GetName() const { return name_; } + void SetName(const std::string& name) { name_ = name; } + + const Vec2& GetPosition() const { return position_; } + void SetPosition(const Vec2& pos) { position_ = pos; } + void SetPosition(float x, float y) { position_ = Vec2(x, y); } + + float GetRotation() const { return rotation_; } + void SetRotation(float rotation) { rotation_ = rotation; } + + const Vec2& GetScale() const { return scale_; } + void SetScale(const Vec2& scale) { scale_ = scale; } + void SetScale(float scale) { scale_ = Vec2(scale, scale); } + + const Vec2& GetSize() const { return size_; } + void SetSize(const Vec2& size) { size_ = size; } + void SetSize(float width, float height) { size_ = Vec2(width, height); } + + bool IsVisible() const { return visible_; } + void SetVisible(bool visible) { visible_ = visible; } + + int GetZOrder() const { return zOrder_; } + void SetZOrder(int zOrder) { zOrder_ = zOrder; } + + float GetOpacity() const { return opacity_; } + void SetOpacity(float opacity) { opacity_ = opacity; } + + ActorList& GetChildren() { return children_; } + const ActorList& GetChildren() const { return children_; } + +protected: + void SetParent(Actor* parent) { parent_ = parent; } + void SetScene(Scene* scene) { scene_ = scene; } + + void UpdateChildren(float deltaTime); + void RenderChildren(); + +private: + Actor* parent_; + Scene* scene_; + ActorList children_; + + std::string name_; + Vec2 position_; + float rotation_; + Vec2 scale_; + Vec2 size_; + bool visible_; + int zOrder_; + float opacity_; + + friend class Scene; +}; + +} + diff --git a/Frostbite2D/include/frostbite2D/scene/scene.h b/Frostbite2D/include/frostbite2D/scene/scene.h new file mode 100644 index 0000000..d0c5b12 --- /dev/null +++ b/Frostbite2D/include/frostbite2D/scene/scene.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace frostbite2D { + +using ActorList = IntrusiveList>; + +class Scene : public RefObject { +public: + Scene(); + virtual ~Scene(); + + virtual void onEnter(); + virtual void onExit(); + + void Update(float deltaTime); + void Render(); + + void AddActor(RefPtr actor); + void RemoveActor(RefPtr actor); + void RemoveAllActors(); + +protected: + void UpdateActors(float deltaTime); + void RenderActors(); + +private: + ActorList actors_; + + friend class Actor; + friend class SceneManager; +}; + +} + diff --git a/Frostbite2D/include/frostbite2D/scene/scene_manager.h b/Frostbite2D/include/frostbite2D/scene/scene_manager.h new file mode 100644 index 0000000..d98a704 --- /dev/null +++ b/Frostbite2D/include/frostbite2D/scene/scene_manager.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +namespace frostbite2D { + +class Scene; + +class SceneManager { +public: + static SceneManager& get(); + + SceneManager(const SceneManager&) = delete; + SceneManager& operator=(const SceneManager&) = delete; + + void PushScene(Ptr scene); + void PopScene(); + void ReplaceScene(Ptr scene); + void ClearAll(); + + void Update(float deltaTime); + void Render(); + + Scene* GetCurrentScene() const; + bool HasActiveScene() const; + +private: + SceneManager() = default; + ~SceneManager(); + + std::vector> sceneStack_; +}; + +} + diff --git a/Frostbite2D/include/frostbite2D/utils/intrusive_list.hpp b/Frostbite2D/include/frostbite2D/utils/intrusive_list.hpp new file mode 100644 index 0000000..0413450 --- /dev/null +++ b/Frostbite2D/include/frostbite2D/utils/intrusive_list.hpp @@ -0,0 +1,395 @@ +#pragma once + +#include +#include +#include +#include + +namespace frostbite2D { + +template +class IntrusiveListValue; + +template +class IntrusiveList { +public: + using value_type = typename std::pointer_traits<_PtrTy>::pointer; + using pointer = value_type*; + using reference = value_type&; + + IntrusiveList() + : first_(nullptr) + , last_(nullptr) { + } + + ~IntrusiveList() { + Clear(); + } + + const value_type& GetFirst() const { + return first_; + } + + value_type& GetFirst() { + return first_; + } + + const value_type& GetLast() const { + return last_; + } + + value_type& GetLast() { + return last_; + } + + inline bool IsEmpty() const { + return first_ == nullptr; + } + + void PushBack(reference child) { + if (child->GetPrev()) { + child->GetPrev()->GetNext() = child->GetNext(); + } + if (child->GetNext()) { + child->GetNext()->GetPrev() = child->GetPrev(); + } + + child->GetPrev() = last_; + child->GetNext() = nullptr; + + if (first_) { + last_->GetNext() = child; + } else { + first_ = child; + } + + last_ = child; + } + + void PushFront(reference child) { + if (child->GetPrev()) { + child->GetPrev()->GetNext() = child->GetNext(); + } + if (child->GetNext()) { + child->GetNext()->GetPrev() = child->GetPrev(); + } + + child->GetPrev() = nullptr; + child->GetNext() = first_; + + if (first_) { + first_->GetPrev() = child; + } else { + last_ = child; + } + + first_ = child; + } + + void InsertBefore(reference child, reference before) { + if (child->GetPrev()) { + child->GetPrev()->GetNext() = child->GetNext(); + } + if (child->GetNext()) { + child->GetNext()->GetPrev() = child->GetPrev(); + } + + if (before->GetPrev()) { + before->GetPrev()->GetNext() = child; + } else { + first_ = child; + } + + child->GetPrev() = before->GetPrev(); + child->GetNext() = before; + before->GetPrev() = child; + } + + void InsertAfter(reference child, reference after) { + if (child->GetPrev()) { + child->GetPrev()->GetNext() = child->GetNext(); + } + if (child->GetNext()) { + child->GetNext()->GetPrev() = child->GetPrev(); + } + + if (after->GetNext()) { + after->GetNext()->GetPrev() = child; + } else { + last_ = child; + } + + child->GetNext() = after->GetNext(); + child->GetPrev() = after; + after->GetNext() = child; + } + + void Remove(reference child) { + if (child->GetNext()) { + child->GetNext()->GetPrev() = child->GetPrev(); + } else { + last_ = child->GetPrev(); + } + + if (child->GetPrev()) { + child->GetPrev()->GetNext() = child->GetNext(); + } else { + first_ = child->GetNext(); + } + + child->GetPrev() = nullptr; + child->GetNext() = nullptr; + } + + void Clear() { + value_type p = first_; + while (p) { + value_type tmp = p; + p = p->GetNext(); + if (tmp) { + tmp->GetNext() = nullptr; + tmp->GetPrev() = nullptr; + } + } + first_ = nullptr; + last_ = nullptr; + } + + bool CheckValid() { + if (!first_) { + return true; + } + + int pos = 0; + + value_type p = first_; + value_type tmp = p; + do { + tmp = p; + p = p->GetNext(); + ++pos; + + if (p) { + if (p->GetPrev() != tmp) { + return false; + } + } else { + if (tmp != last_) { + return false; + } + } + } while (p); + return true; + } + +public: + template + struct Iterator { + using iterator_category = std::bidirectional_iterator_tag; + using value_type = _IterPtrTy; + using pointer = _IterPtrTy*; + using reference = _IterPtrTy&; + using difference_type = ptrdiff_t; + + inline Iterator(value_type ptr = nullptr, bool is_end = false) + : base_(ptr) + , is_end_(is_end) { + } + + inline reference operator*() const { + assert(base_ && !is_end_); + return const_cast(base_); + } + + inline pointer operator->() const { + return std::pointer_traits::pointer_to(**this); + } + + inline Iterator& operator++() { + assert(base_ && !is_end_); + value_type next = base_->GetNext(); + if (next) { + base_ = next; + } else { + is_end_ = true; + } + return (*this); + } + + inline Iterator operator++(int) { + Iterator old = (*this); + ++(*this); + return old; + } + + inline Iterator& operator--() { + assert(base_); + if (is_end_) { + is_end_ = false; + } else { + base_ = base_->GetPrev(); + } + return (*this); + } + + inline Iterator operator--(int) { + Iterator old = (*this); + --(*this); + return old; + } + + inline bool operator==(const Iterator& other) const { + return base_ == other.base_ && is_end_ == other.is_end_; + } + + inline bool operator!=(const Iterator& other) const { + return !(*this == other); + } + + inline operator bool() const { + return base_ != nullptr && !is_end_; + } + + private: + bool is_end_; + + typename std::remove_const::type base_; + }; + +public: + using iterator = Iterator; + using const_iterator = Iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + inline iterator begin() { + return iterator(first_, first_ == nullptr); + } + + inline const_iterator begin() const { + return const_iterator(first_, first_ == nullptr); + } + + inline const_iterator cbegin() const { + return begin(); + } + + inline iterator end() { + return iterator(last_, true); + } + + inline const_iterator end() const { + return const_iterator(last_, true); + } + + inline const_iterator cend() const { + return end(); + } + + inline reverse_iterator rbegin() { + return reverse_iterator(end()); + } + + inline const_reverse_iterator rbegin() const { + return const_reverse_iterator(end()); + } + + inline const_reverse_iterator crbegin() const { + return rbegin(); + } + + inline reverse_iterator rend() { + return reverse_iterator(begin()); + } + + inline const_reverse_iterator rend() const { + return const_reverse_iterator(begin()); + } + + inline const_reverse_iterator crend() const { + return rend(); + } + + inline value_type& front() { + if (IsEmpty()) { + throw std::out_of_range("front() called on empty list"); + } + return first_; + } + + inline const value_type& front() const { + if (IsEmpty()) { + throw std::out_of_range("front() called on empty list"); + } + return first_; + } + + inline value_type& back() { + if (IsEmpty()) { + throw std::out_of_range("back() called on empty list"); + } + return last_; + } + + inline const value_type& back() const { + if (IsEmpty()) { + throw std::out_of_range("back() called on empty list"); + } + return last_; + } + +private: + value_type first_; + value_type last_; + + template + friend class IntrusiveListValue; +}; + +template +class IntrusiveListValue { +public: + using value_type = typename std::pointer_traits<_PtrTy>::pointer; + using reference = value_type&; + using pointer = value_type*; + + IntrusiveListValue() + : prev_(nullptr) + , next_(nullptr) { + } + + IntrusiveListValue(value_type rhs) + : prev_(nullptr) + , next_(nullptr) { + if (rhs) { + prev_ = rhs->GetPrev(); + next_ = rhs->GetNext(); + } + } + + const value_type& GetPrev() const { + return prev_; + } + + value_type& GetPrev() { + return prev_; + } + + const value_type& GetNext() const { + return next_; + } + + value_type& GetNext() { + return next_; + } + +private: + value_type prev_; + value_type next_; + + template + friend class IntrusiveList; +}; + +} + diff --git a/Frostbite2D/src/frostbite2D/2d/actor.cpp b/Frostbite2D/src/frostbite2D/2d/actor.cpp new file mode 100644 index 0000000..7a8408f --- /dev/null +++ b/Frostbite2D/src/frostbite2D/2d/actor.cpp @@ -0,0 +1,79 @@ +#include +#include + +namespace frostbite2D { + +Actor::Actor() + : parent_(nullptr) + , scene_(nullptr) + , rotation_(0.0f) + , visible_(true) + , zOrder_(0) + , opacity_(1.0f) { +} + +Actor::~Actor() { + RemoveAllChildren(); +} + +void Actor::Update(float deltaTime) { + UpdateChildren(deltaTime); +} + +void Actor::Render() { + RenderChildren(); +} + +void Actor::AddChild(Ptr child) { + if (!child) { + return; + } + + if (child->GetParent() == this) { + return; + } + + if (child->GetParent()) { + child->GetParent()->RemoveChild(child); + } + + child->SetParent(this); + child->SetScene(scene_); + children_.PushBack(child); +} + +void Actor::RemoveChild(Ptr child) { + if (!child || child->GetParent() != this) { + return; + } + + child->SetParent(nullptr); + child->SetScene(nullptr); + children_.Remove(child); +} + +void Actor::RemoveAllChildren() { + while (!children_.IsEmpty()) { + Ptr child = children_.GetFirst(); + RemoveChild(child); + } +} + +void Actor::UpdateChildren(float deltaTime) { + for (auto it = children_.begin(); it != children_.end(); ++it) { + if (*it && (*it)->IsVisible()) { + (*it)->Update(deltaTime); + } + } +} + +void Actor::RenderChildren() { + for (auto it = children_.begin(); it != children_.end(); ++it) { + if (*it && (*it)->IsVisible()) { + (*it)->Render(); + } + } +} + +} + diff --git a/Frostbite2D/src/frostbite2D/core/application.cpp b/Frostbite2D/src/frostbite2D/core/application.cpp index 936e7ad..f3053c9 100644 --- a/Frostbite2D/src/frostbite2D/core/application.cpp +++ b/Frostbite2D/src/frostbite2D/core/application.cpp @@ -1,10 +1,12 @@ #include #include +#include #include #include #include #include #include +#include namespace frostbite2D { Application &Application::get() { @@ -169,7 +171,6 @@ void Application::mainLoop() { break; } } - if (!paused_) { update(); } @@ -184,6 +185,8 @@ void Application::update() { lastFrameTime_ = currentTime; totalTime_ += deltaTime_; + SceneManager::get().Update(deltaTime_); + frameCount_++; fpsTimer_ += deltaTime_; if (fpsTimer_ >= fpsUpdateInterval_) { @@ -194,6 +197,8 @@ void Application::update() { } void Application::render() { + SceneManager::get().Render(); + if (window_) { window_->swap(); } diff --git a/Frostbite2D/src/frostbite2D/scene/scene.cpp b/Frostbite2D/src/frostbite2D/scene/scene.cpp new file mode 100644 index 0000000..a560d57 --- /dev/null +++ b/Frostbite2D/src/frostbite2D/scene/scene.cpp @@ -0,0 +1,76 @@ +#include +#include +#include + +namespace frostbite2D { + +Scene::Scene() { +} + +Scene::~Scene() { + RemoveAllActors(); +} + +void Scene::onEnter() { + SDL_Log("Scene onEnter"); +} + +void Scene::onExit() { + SDL_Log("Scene onExit"); +} + +void Scene::Update(float deltaTime) { + UpdateActors(deltaTime); +} + +void Scene::Render() { + RenderActors(); +} + +void Scene::AddActor(Ptr actor) { + if (!actor) { + return; + } + + if (actor->GetParent()) { + return; + } + + actor->SetScene(this); + actors_.PushBack(actor); +} + +void Scene::RemoveActor(Ptr actor) { + if (!actor || actor->GetScene() != this) { + return; + } + + actor->SetScene(nullptr); + actors_.Remove(actor); +} + +void Scene::RemoveAllActors() { + while (!actors_.IsEmpty()) { + Ptr actor = actors_.GetFirst(); + RemoveActor(actor); + } +} + +void Scene::UpdateActors(float deltaTime) { + for (auto it = actors_.begin(); it != actors_.end(); ++it) { + if (*it && (*it)->IsVisible()) { + (*it)->Update(deltaTime); + } + } +} + +void Scene::RenderActors() { + for (auto it = actors_.begin(); it != actors_.end(); ++it) { + if (*it && (*it)->IsVisible()) { + (*it)->Render(); + } + } +} + +} + diff --git a/Frostbite2D/src/frostbite2D/scene/scene_manager.cpp b/Frostbite2D/src/frostbite2D/scene/scene_manager.cpp new file mode 100644 index 0000000..e9ad258 --- /dev/null +++ b/Frostbite2D/src/frostbite2D/scene/scene_manager.cpp @@ -0,0 +1,82 @@ +#include +#include +#include + +namespace frostbite2D { + +SceneManager& SceneManager::get() { + static SceneManager instance; + return instance; +} + +SceneManager::~SceneManager() { + ClearAll(); +} + +void SceneManager::PushScene(Ptr scene) { + if (!scene) { + return; + } + + if (!sceneStack_.empty()) { + sceneStack_.back()->onExit(); + } + + sceneStack_.push_back(scene); + scene->onEnter(); +} + +void SceneManager::PopScene() { + if (sceneStack_.empty()) { + return; + } + + sceneStack_.back()->onExit(); + sceneStack_.pop_back(); + + if (!sceneStack_.empty()) { + sceneStack_.back()->onEnter(); + } +} + +void SceneManager::ReplaceScene(Ptr scene) { + if (!scene) { + return; + } + + PopScene(); + PushScene(scene); +} + +void SceneManager::ClearAll() { + while (!sceneStack_.empty()) { + sceneStack_.back()->onExit(); + sceneStack_.pop_back(); + } +} + +void SceneManager::Update(float deltaTime) { + if (!sceneStack_.empty()) { + sceneStack_.back()->Update(deltaTime); + } +} + +void SceneManager::Render() { + if (!sceneStack_.empty()) { + sceneStack_.back()->Render(); + } +} + +Scene* SceneManager::GetCurrentScene() const { + if (sceneStack_.empty()) { + return nullptr; + } + return sceneStack_.back().Get(); +} + +bool SceneManager::HasActiveScene() const { + return !sceneStack_.empty(); +} + +} + diff --git a/Game/src/main.cpp b/Game/src/main.cpp index f4a4cf0..aa0f293 100644 --- a/Game/src/main.cpp +++ b/Game/src/main.cpp @@ -6,6 +6,9 @@ #include #include +#include +#include + using namespace frostbite2D; int main(int argc, char **argv) {