feat(场景管理): 实现场景管理系统及基础2D角色框架

添加场景管理器(SceneManager)和场景(Scene)类,用于管理游戏场景的压入、弹出和切换
实现基础2D角色(Actor)类,支持父子关系和场景管理
添加侵入式链表(IntrusiveList)工具类,用于高效管理场景中的角色对象
在应用主循环中集成场景更新和渲染逻辑
This commit is contained in:
2026-03-17 15:11:31 +08:00
parent 0995fb05f7
commit b35ecb197f
9 changed files with 807 additions and 1 deletions

View File

@@ -0,0 +1,90 @@
#pragma once
#include <frostbite2D/base/RefObject.h>
#include <frostbite2D/types/type_alias.h>
#include <frostbite2D/types/type_math.h>
#include <frostbite2D/utils/intrusive_list.hpp>
#include <string>
#include <vector>
namespace frostbite2D {
class Actor;
class Scene;
using ActorList = IntrusiveList<RefPtr<Actor>>;
class Actor : public RefObject, protected IntrusiveListValue<RefPtr<Actor>> {
public:
using IntrusiveListValue<RefPtr<Actor>>::GetNext;
using IntrusiveListValue<RefPtr<Actor>>::GetPrev;
Actor();
virtual ~Actor();
virtual void Update(float deltaTime);
virtual void Render();
void AddChild(RefPtr<Actor> child);
void RemoveChild(RefPtr<Actor> 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;
};
}

View File

@@ -0,0 +1,40 @@
#pragma once
#include <frostbite2D/base/RefObject.h>
#include <frostbite2D/types/type_alias.h>
#include <frostbite2D/utils/intrusive_list.hpp>
#include <frostbite2D/2d/actor.h>
#include <vector>
namespace frostbite2D {
using ActorList = IntrusiveList<RefPtr<Actor>>;
class Scene : public RefObject {
public:
Scene();
virtual ~Scene();
virtual void onEnter();
virtual void onExit();
void Update(float deltaTime);
void Render();
void AddActor(RefPtr<Actor> actor);
void RemoveActor(RefPtr<Actor> actor);
void RemoveAllActors();
protected:
void UpdateActors(float deltaTime);
void RenderActors();
private:
ActorList actors_;
friend class Actor;
friend class SceneManager;
};
}

View File

@@ -0,0 +1,36 @@
#pragma once
#include <frostbite2D/types/type_alias.h>
#include <vector>
namespace frostbite2D {
class Scene;
class SceneManager {
public:
static SceneManager& get();
SceneManager(const SceneManager&) = delete;
SceneManager& operator=(const SceneManager&) = delete;
void PushScene(Ptr<Scene> scene);
void PopScene();
void ReplaceScene(Ptr<Scene> scene);
void ClearAll();
void Update(float deltaTime);
void Render();
Scene* GetCurrentScene() const;
bool HasActiveScene() const;
private:
SceneManager() = default;
~SceneManager();
std::vector<Ptr<Scene>> sceneStack_;
};
}

View File

@@ -0,0 +1,395 @@
#pragma once
#include <type_traits>
#include <iterator>
#include <stdexcept>
#include <cassert>
namespace frostbite2D {
template <typename _PtrTy>
class IntrusiveListValue;
template <typename _PtrTy>
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 <typename _IterPtrTy>
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<reference>(base_);
}
inline pointer operator->() const {
return std::pointer_traits<pointer>::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<value_type>::type base_;
};
public:
using iterator = Iterator<value_type>;
using const_iterator = Iterator<const value_type>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_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 <typename T>
friend class IntrusiveListValue;
};
template <typename _PtrTy>
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 <typename T>
friend class IntrusiveList;
};
}

View File

@@ -0,0 +1,79 @@
#include <frostbite2D/2d/actor.h>
#include <SDL2/SDL.h>
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<Actor> 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<Actor> child) {
if (!child || child->GetParent() != this) {
return;
}
child->SetParent(nullptr);
child->SetScene(nullptr);
children_.Remove(child);
}
void Actor::RemoveAllChildren() {
while (!children_.IsEmpty()) {
Ptr<Actor> 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();
}
}
}
}

View File

@@ -1,10 +1,12 @@
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <algorithm> #include <algorithm>
#include <cstdio>
#include <frostbite2D/core/application.h> #include <frostbite2D/core/application.h>
#include <frostbite2D/graphics/renderer.h> #include <frostbite2D/graphics/renderer.h>
#include <frostbite2D/platform/switch.h> #include <frostbite2D/platform/switch.h>
#include <frostbite2D/types/type_math.h> #include <frostbite2D/types/type_math.h>
#include <frostbite2D/utils/asset.h> #include <frostbite2D/utils/asset.h>
#include <frostbite2D/scene/scene_manager.h>
namespace frostbite2D { namespace frostbite2D {
Application &Application::get() { Application &Application::get() {
@@ -169,7 +171,6 @@ void Application::mainLoop() {
break; break;
} }
} }
if (!paused_) { if (!paused_) {
update(); update();
} }
@@ -184,6 +185,8 @@ void Application::update() {
lastFrameTime_ = currentTime; lastFrameTime_ = currentTime;
totalTime_ += deltaTime_; totalTime_ += deltaTime_;
SceneManager::get().Update(deltaTime_);
frameCount_++; frameCount_++;
fpsTimer_ += deltaTime_; fpsTimer_ += deltaTime_;
if (fpsTimer_ >= fpsUpdateInterval_) { if (fpsTimer_ >= fpsUpdateInterval_) {
@@ -194,6 +197,8 @@ void Application::update() {
} }
void Application::render() { void Application::render() {
SceneManager::get().Render();
if (window_) { if (window_) {
window_->swap(); window_->swap();
} }

View File

@@ -0,0 +1,76 @@
#include <frostbite2D/scene/scene.h>
#include <frostbite2D/2d/actor.h>
#include <SDL2/SDL.h>
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> actor) {
if (!actor) {
return;
}
if (actor->GetParent()) {
return;
}
actor->SetScene(this);
actors_.PushBack(actor);
}
void Scene::RemoveActor(Ptr<Actor> actor) {
if (!actor || actor->GetScene() != this) {
return;
}
actor->SetScene(nullptr);
actors_.Remove(actor);
}
void Scene::RemoveAllActors() {
while (!actors_.IsEmpty()) {
Ptr<Actor> 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();
}
}
}
}

View File

@@ -0,0 +1,82 @@
#include <frostbite2D/scene/scene_manager.h>
#include <frostbite2D/scene/scene.h>
#include <SDL2/SDL.h>
namespace frostbite2D {
SceneManager& SceneManager::get() {
static SceneManager instance;
return instance;
}
SceneManager::~SceneManager() {
ClearAll();
}
void SceneManager::PushScene(Ptr<Scene> 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> 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();
}
}

View File

@@ -6,6 +6,9 @@
#include <frostbite2D/graphics/texture.h> #include <frostbite2D/graphics/texture.h>
#include <glad/glad.h> #include <glad/glad.h>
#include <frostbite2D/scene/scene.h>
#include <frostbite2D/scene/scene_manager.h>
using namespace frostbite2D; using namespace frostbite2D;
int main(int argc, char **argv) { int main(int argc, char **argv) {