diff --git a/projects/kiwano/kiwano.vcxproj b/projects/kiwano/kiwano.vcxproj
index 8883793a..f7d520dd 100644
--- a/projects/kiwano/kiwano.vcxproj
+++ b/projects/kiwano/kiwano.vcxproj
@@ -17,11 +17,13 @@
+
+
@@ -132,6 +134,7 @@
+
diff --git a/projects/kiwano/kiwano.vcxproj.filters b/projects/kiwano/kiwano.vcxproj.filters
index 9d5619f2..7e28e439 100644
--- a/projects/kiwano/kiwano.vcxproj.filters
+++ b/projects/kiwano/kiwano.vcxproj.filters
@@ -351,6 +351,12 @@
utils
+
+ base
+
+
+ core
+
@@ -575,6 +581,9 @@
utils
+
+ base
+
diff --git a/src/kiwano-audio/AudioModule.h b/src/kiwano-audio/AudioModule.h
index a97e4078..0e51eb72 100644
--- a/src/kiwano-audio/AudioModule.h
+++ b/src/kiwano-audio/AudioModule.h
@@ -68,11 +68,11 @@ public:
void DestroyModule() override;
+ ~AudioModule();
+
private:
AudioModule();
- ~AudioModule();
-
private:
IXAudio2* x_audio2_;
IXAudio2MasteringVoice* mastering_voice_;
diff --git a/src/kiwano-physics/PhysicBody.cpp b/src/kiwano-physics/PhysicBody.cpp
index b10f8fd2..e74675f0 100644
--- a/src/kiwano-physics/PhysicBody.cpp
+++ b/src/kiwano-physics/PhysicBody.cpp
@@ -21,6 +21,8 @@
#include
#include
+#define KGE_PHYSIC_COMP_NAME "__KGE_PHYSIC_BODY__"
+
namespace kiwano
{
namespace physics
@@ -48,6 +50,25 @@ PhysicBodyPtr PhysicBody::Create(PhysicWorld* world, Type type)
return nullptr;
}
+PhysicBody* PhysicBody::Get(Actor* actor)
+{
+ if (actor)
+ {
+ static size_t physic_comp_name_hash = 0;
+ if (physic_comp_name_hash == 0)
+ {
+ physic_comp_name_hash = std::hash{}(KGE_PHYSIC_COMP_NAME);
+ }
+ return (PhysicBody*)actor->GetComponent(physic_comp_name_hash);
+ }
+ return nullptr;
+}
+
+PhysicBody* PhysicBody::Get(ActorPtr actor)
+{
+ return PhysicBody::Get(actor.Get());
+}
+
PhysicBody::PhysicBody()
: body_(nullptr)
, world_(nullptr)
@@ -56,7 +77,7 @@ PhysicBody::PhysicBody()
, mask_bits_(0xFFFF)
, group_index_(0)
{
- SetName("KGE_PHYSIC_BODY");
+ SetName(KGE_PHYSIC_COMP_NAME);
}
PhysicBody::~PhysicBody() {}
@@ -65,8 +86,6 @@ void PhysicBody::InitComponent(Actor* actor)
{
Component::InitComponent(actor);
- actor->SetPhysicBody(this);
-
UpdateFromActor(actor);
}
diff --git a/src/kiwano-physics/PhysicBody.h b/src/kiwano-physics/PhysicBody.h
index 875e4a8a..638fd3ad 100644
--- a/src/kiwano-physics/PhysicBody.h
+++ b/src/kiwano-physics/PhysicBody.h
@@ -62,6 +62,16 @@ public:
/// @param type 物体类型
static PhysicBodyPtr Create(PhysicWorld* world, Type type);
+ /// \~chinese
+ /// @brief 获取角色的物理身体
+ /// @param actor 角色
+ static PhysicBody* Get(Actor* actor);
+
+ /// \~chinese
+ /// @brief 获取角色的物理身体
+ /// @param actor 角色
+ static PhysicBody* Get(ActorPtr actor);
+
PhysicBody();
virtual ~PhysicBody();
diff --git a/src/kiwano-physics/PhysicWorld.cpp b/src/kiwano-physics/PhysicWorld.cpp
index e97384b7..d8e447ab 100644
--- a/src/kiwano-physics/PhysicWorld.cpp
+++ b/src/kiwano-physics/PhysicWorld.cpp
@@ -413,7 +413,7 @@ void PhysicWorld::BeforeSimulation(Actor* parent, const Matrix3x2& parent_to_wor
{
Matrix3x2 child_to_world = child->GetTransformMatrixToParent() * parent_to_world;
- PhysicBody* body = child->GetPhysicBody();
+ PhysicBody* body = PhysicBody::Get(child);
if (body)
{
body->BeforeSimulation(child.Get(), parent_to_world, child_to_world, parent_rotation);
@@ -428,7 +428,7 @@ void PhysicWorld::AfterSimulation(Actor* parent, const Matrix3x2& parent_to_worl
{
for (auto child : parent->GetAllChildren())
{
- PhysicBody* body = child->GetPhysicBody();
+ PhysicBody* body = PhysicBody::Get(child);
if (body)
{
body->AfterSimulation(child.Get(), parent_to_world, parent_rotation);
diff --git a/src/kiwano/2d/Actor.cpp b/src/kiwano/2d/Actor.cpp
index 5f54647c..62324ee1 100644
--- a/src/kiwano/2d/Actor.cpp
+++ b/src/kiwano/2d/Actor.cpp
@@ -59,6 +59,7 @@ Actor::Actor()
, cascade_opacity_(false)
, show_border_(false)
, is_fast_transform_(true)
+ , evt_dispatch_enabled_(true)
, parent_(nullptr)
, stage_(nullptr)
, hash_name_(0)
@@ -66,7 +67,6 @@ Actor::Actor()
, opacity_(1.f)
, displayed_opacity_(1.f)
, anchor_(default_anchor_x, default_anchor_y)
- , physic_body_(nullptr)
{
}
@@ -192,7 +192,7 @@ bool Actor::CheckVisibility(RenderContext& ctx) const
bool Actor::DispatchEvent(Event* evt)
{
- if (!visible_)
+ if (!visible_ || !evt_dispatch_enabled_)
return true;
// Dispatch to children those are greater than 0 in Z-Order
@@ -221,6 +221,11 @@ bool Actor::DispatchEvent(Event* evt)
return true;
}
+void Actor::SetEventDispatchEnabled(bool enabled)
+{
+ evt_dispatch_enabled_ = enabled;
+}
+
void Actor::DoSerialize(Serializer* serializer) const
{
ObjectBase::DoSerialize(serializer);
diff --git a/src/kiwano/2d/Actor.h b/src/kiwano/2d/Actor.h
index f0e258a0..1af54468 100644
--- a/src/kiwano/2d/Actor.h
+++ b/src/kiwano/2d/Actor.h
@@ -33,11 +33,6 @@ class Stage;
class Director;
class RenderContext;
-namespace physics
-{
-class PhysicBody;
-}
-
KGE_DECLARE_SMART_PTR(Actor);
/// \~chinese
@@ -110,6 +105,10 @@ public:
/// @brief 是否启用级联透明度
bool IsCascadeOpacityEnabled() const;
+ /// \~chinese
+ /// @brief 是否启用事件分发
+ bool IsEventDispatchEnabled() const;
+
/// \~chinese
/// @brief 获取名称的 Hash 值
size_t GetHashName() const;
@@ -391,14 +390,6 @@ public:
/// @brief 获取更新时的回调函数
UpdateCallback GetCallbackOnUpdate() const;
- /// \~chinese
- /// @brief 获取物理身体,仅当kiwano-physics包启用时生效
- physics::PhysicBody* GetPhysicBody() const;
-
- /// \~chinese
- /// @brief 设置物理身体,仅当kiwano-physics包启用时生效
- void SetPhysicBody(physics::PhysicBody* body);
-
/// \~chinese
/// @brief 判断点是否在角色内
virtual bool ContainsPoint(const Point& point) const;
@@ -421,6 +412,11 @@ public:
/// @return 是否继续分发该事件
virtual bool DispatchEvent(Event* evt);
+ /// \~chinese
+ /// @brief 开启或关闭事件分发功能
+ /// @param enabled 是否开启
+ void SetEventDispatchEnabled(bool enabled);
+
/// \~chinese
/// @brief 序列化
void DoSerialize(Serializer* serializer) const override;
@@ -482,6 +478,7 @@ private:
bool hover_;
bool pressed_;
bool responsible_;
+ bool evt_dispatch_enabled_;
int z_order_;
float opacity_;
float displayed_opacity_;
@@ -502,8 +499,6 @@ private:
mutable Matrix3x2 transform_matrix_;
mutable Matrix3x2 transform_matrix_inverse_;
mutable Matrix3x2 transform_matrix_to_parent_;
-
- physics::PhysicBody* physic_body_;
};
/** @} */
@@ -533,6 +528,11 @@ inline bool Actor::IsCascadeOpacityEnabled() const
return cascade_opacity_;
}
+inline bool Actor::IsEventDispatchEnabled() const
+{
+ return evt_dispatch_enabled_;
+}
+
inline size_t Actor::GetHashName() const
{
return hash_name_;
@@ -688,16 +688,6 @@ inline Actor::UpdateCallback Actor::GetCallbackOnUpdate() const
return cb_update_;
}
-inline physics::PhysicBody* Actor::GetPhysicBody() const
-{
- return physic_body_;
-}
-
-inline void Actor::SetPhysicBody(physics::PhysicBody* body)
-{
- physic_body_ = body;
-}
-
inline void Actor::ShowBorder(bool show)
{
show_border_ = show;
diff --git a/src/kiwano/2d/action/ActionHelper.h b/src/kiwano/2d/action/ActionHelper.h
index a3428dd9..072ae0b5 100644
--- a/src/kiwano/2d/action/ActionHelper.h
+++ b/src/kiwano/2d/action/ActionHelper.h
@@ -36,11 +36,9 @@ namespace kiwano
/// @brief 动画辅助类
struct ActionHelper
{
- using DoneCallback = Action::DoneCallback;
-
/// \~chinese
/// @brief 设置循环次数
- inline ActionHelper& SetLoops(int loops)
+ inline ActionHelper& Loops(int loops)
{
ptr->SetLoops(loops);
return (*this);
@@ -48,7 +46,7 @@ struct ActionHelper
/// \~chinese
/// @brief 设置动画延迟
- inline ActionHelper& SetDelay(Duration delay)
+ inline ActionHelper& Delay(Duration delay)
{
ptr->SetDelay(delay);
return (*this);
@@ -56,7 +54,7 @@ struct ActionHelper
/// \~chinese
/// @brief 设置动画结束回调函数
- inline ActionHelper& SetDoneCallback(const DoneCallback& cb)
+ inline ActionHelper& DoneCallback(const Action::DoneCallback& cb)
{
ptr->SetDoneCallback(cb);
return (*this);
@@ -64,7 +62,7 @@ struct ActionHelper
/// \~chinese
/// @brief 设置动画循环结束时的回调函数
- inline ActionHelper& SetLoopDoneCallback(const DoneCallback& cb)
+ inline ActionHelper& LoopDoneCallback(const Action::DoneCallback& cb)
{
ptr->SetLoopDoneCallback(cb);
return (*this);
@@ -80,7 +78,7 @@ struct ActionHelper
/// \~chinese
/// @brief 设置名称
- inline ActionHelper& SetName(const String& name)
+ inline ActionHelper& Name(const String& name)
{
ptr->SetName(name);
return (*this);
@@ -116,11 +114,9 @@ private:
/// @brief 补间动画辅助类
struct TweenHelper
{
- using DoneCallback = Action::DoneCallback;
-
/// \~chinese
/// @brief 设置动画持续时长
- inline TweenHelper& SetDuration(Duration dur)
+ inline TweenHelper& Dur(Duration dur)
{
ptr->SetDuration(dur);
return (*this);
@@ -128,7 +124,7 @@ struct TweenHelper
/// \~chinese
/// @brief 设置循环次数
- inline TweenHelper& SetLoops(int loops)
+ inline TweenHelper& Loops(int loops)
{
ptr->SetLoops(loops);
return (*this);
@@ -136,7 +132,7 @@ struct TweenHelper
/// \~chinese
/// @brief 设置缓动函数
- inline TweenHelper& SetEaseFunc(EaseFunc ease)
+ inline TweenHelper& EaseFunc(EaseFunc ease)
{
ptr->SetEaseFunc(ease);
return (*this);
@@ -144,7 +140,7 @@ struct TweenHelper
/// \~chinese
/// @brief 设置动画延迟
- inline TweenHelper& SetDelay(Duration delay)
+ inline TweenHelper& Delay(Duration delay)
{
ptr->SetDelay(delay);
return (*this);
@@ -152,7 +148,7 @@ struct TweenHelper
/// \~chinese
/// @brief 设置动画结束回调函数
- inline TweenHelper& SetDoneCallback(const DoneCallback& cb)
+ inline TweenHelper& DoneCallback(const Action::DoneCallback& cb)
{
ptr->SetDoneCallback(cb);
return (*this);
@@ -160,7 +156,7 @@ struct TweenHelper
/// \~chinese
/// @brief 设置动画循环结束时的回调函数
- inline TweenHelper& SetLoopDoneCallback(const DoneCallback& cb)
+ inline TweenHelper& LoopDoneCallback(const Action::DoneCallback& cb)
{
ptr->SetLoopDoneCallback(cb);
return (*this);
@@ -176,7 +172,7 @@ struct TweenHelper
/// \~chinese
/// @brief 设置名称
- inline TweenHelper& SetName(const String& name)
+ inline TweenHelper& Name(const String& name)
{
ptr->SetName(name);
return (*this);
@@ -220,7 +216,7 @@ struct Tween
public:
/// \~chinese
/// @brief 构造相对位移动画
- /// @param duration 动画时长
+ /// @param dur 动画时长
/// @param vector 移动向量
static inline TweenHelper MoveBy(Duration dur, const Point& vector)
{
@@ -229,7 +225,7 @@ public:
/// \~chinese
/// @brief 构造位移动画
- /// @param duration 动画时长
+ /// @param dur 动画时长
/// @param pos 目的坐标
static inline TweenHelper MoveTo(Duration dur, const Point& pos)
{
@@ -238,29 +234,29 @@ public:
/// \~chinese
/// @brief 构造相对跳跃动画
- /// @param duration 动画时长
+ /// @param dur 动画时长
/// @param vec 跳跃位移向量
/// @param height 跳跃高度
/// @param jumps 跳跃次数
- static inline TweenHelper JumpBy(Duration duration, const Vec2& vec, float height, int jumps = 1)
+ static inline TweenHelper JumpBy(Duration dur, const Vec2& vec, float height, int jumps = 1)
{
- return TweenHelper(ActionJumpBy::Create(duration, vec, height, jumps));
+ return TweenHelper(ActionJumpBy::Create(dur, vec, height, jumps));
}
/// \~chinese
/// @brief 构造跳跃动画
- /// @param duration 动画时长
+ /// @param dur 动画时长
/// @param pos 目的坐标
/// @param height 跳跃高度
/// @param jumps 跳跃次数
- static inline TweenHelper JumpTo(Duration duration, const Point& pos, float height, int jumps = 1)
+ static inline TweenHelper JumpTo(Duration dur, const Point& pos, float height, int jumps = 1)
{
- return TweenHelper(ActionJumpTo::Create(duration, pos, height, jumps));
+ return TweenHelper(ActionJumpTo::Create(dur, pos, height, jumps));
}
/// \~chinese
/// @brief 构造相对缩放动画
- /// @param duration 动画时长
+ /// @param dur 动画时长
/// @param scale_x 横向缩放相对变化值
/// @param scale_y 纵向缩放相对变化值
static inline TweenHelper ScaleBy(Duration dur, float scale_x, float scale_y)
@@ -270,7 +266,7 @@ public:
/// \~chinese
/// @brief 构造缩放动画
- /// @param duration 动画时长
+ /// @param dur 动画时长
/// @param scale_x 横向缩放目标值
/// @param scale_y 纵向缩放目标值
static inline TweenHelper ScaleTo(Duration dur, float scale_x, float scale_y)
@@ -280,7 +276,7 @@ public:
/// \~chinese
/// @brief 构造透明度渐变动画
- /// @param duration 动画时长
+ /// @param dur 动画时长
/// @param opacity 目标透明度
static inline TweenHelper FadeTo(Duration dur, float opacity)
{
@@ -289,7 +285,7 @@ public:
/// \~chinese
/// @brief 构造淡入动画
- /// @param duration 动画时长
+ /// @param dur 动画时长
static inline TweenHelper FadeIn(Duration dur)
{
return TweenHelper(ActionFadeIn::Create(dur));
@@ -297,7 +293,7 @@ public:
/// \~chinese
/// @brief 构造淡出动画
- /// @param duration 动画时长
+ /// @param dur 动画时长
static inline TweenHelper FadeOut(Duration dur)
{
return TweenHelper(ActionFadeOut::Create(dur));
@@ -305,7 +301,7 @@ public:
/// \~chinese
/// @brief 构造相对旋转动画
- /// @param duration 动画时长
+ /// @param dur 动画时长
/// @param rotation 角度相对变化值
static inline TweenHelper RotateBy(Duration dur, float rotation)
{
@@ -314,7 +310,7 @@ public:
/// \~chinese
/// @brief 构造旋转动画
- /// @param duration 动画时长
+ /// @param dur 动画时长
/// @param rotation 目标角度
static inline TweenHelper RotateTo(Duration dur, float rotation)
{
@@ -323,20 +319,20 @@ public:
/// \~chinese
/// @brief 构造路径行走动画
- /// @param duration 持续时长
+ /// @param dur 持续时长
/// @param path 路径形状
/// @param rotating 是否沿路径切线方向旋转
/// @param start 路径起点(百分比)
/// @param end 路径终点(百分比)
- static inline TweenHelper Walk(Duration duration, ShapePtr path, bool rotating = false, float start = 0.f,
+ static inline TweenHelper Walk(Duration dur, ShapePtr path, bool rotating = false, float start = 0.f,
float end = 1.f)
{
- return TweenHelper(ActionWalk::Create(duration, path, rotating, start, end));
+ return TweenHelper(ActionWalk::Create(dur, path, rotating, start, end));
}
/// \~chinese
/// @brief 构建帧动画
- /// @param duration 动画时长
+ /// @param dur 动画时长
/// @param[in] frame_seq 序列帧
static inline TweenHelper Animation(Duration dur, FrameSequencePtr frames)
{
@@ -345,7 +341,7 @@ public:
/// \~chinese
/// @brief 构造自定义动画
- /// @param duration 动画时长
+ /// @param dur 动画时长
/// @param tween_func 动画回调函数
static inline TweenHelper Custom(Duration dur, ActionCustom::TweenFunc tween_func)
{
diff --git a/src/kiwano/base/Director.h b/src/kiwano/base/Director.h
index fd054672..6a775670 100644
--- a/src/kiwano/base/Director.h
+++ b/src/kiwano/base/Director.h
@@ -102,11 +102,11 @@ public:
void HandleEvent(Event* evt) override;
+ virtual ~Director();
+
private:
Director();
- virtual ~Director();
-
private:
bool render_border_enabled_;
Stack stages_;
diff --git a/src/kiwano/base/ObjectBase.cpp b/src/kiwano/base/ObjectBase.cpp
index 588e0a6e..629b9ef5 100644
--- a/src/kiwano/base/ObjectBase.cpp
+++ b/src/kiwano/base/ObjectBase.cpp
@@ -19,6 +19,7 @@
// THE SOFTWARE.
#include
+#include
#include
#include
#include
@@ -56,6 +57,11 @@ ObjectBase::~ObjectBase()
#endif
}
+void ObjectBase::AutoRelease()
+{
+ ObjectPool::GetInstance().AddObject(this);
+}
+
const Any& ObjectBase::GetUserData() const
{
return user_data_;
diff --git a/src/kiwano/base/ObjectBase.h b/src/kiwano/base/ObjectBase.h
index 6761c77c..927e2549 100644
--- a/src/kiwano/base/ObjectBase.h
+++ b/src/kiwano/base/ObjectBase.h
@@ -44,6 +44,10 @@ public:
virtual ~ObjectBase();
+ /// \~chinese
+ /// @brief 自动释放
+ void AutoRelease();
+
/// \~chinese
/// @brief 设置对象名
void SetName(const String& name);
diff --git a/src/kiwano/base/ObjectPool.cpp b/src/kiwano/base/ObjectPool.cpp
new file mode 100644
index 00000000..66ba259c
--- /dev/null
+++ b/src/kiwano/base/ObjectPool.cpp
@@ -0,0 +1,86 @@
+// Copyright (c) 2016-2018 Kiwano - Nomango
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#include
+
+namespace kiwano
+{
+
+List ObjectPool::pools_;
+
+ObjectPool& ObjectPool::GetInstance()
+{
+ static ObjectPool instance;
+ return *pools_.back();
+}
+
+ObjectPool::ObjectPool()
+{
+ pools_.push_back(this);
+}
+
+ObjectPool::~ObjectPool()
+{
+ Clear();
+
+ auto iter = std::find(pools_.begin(), pools_.end(), this);
+ if (iter != pools_.end())
+ pools_.erase(iter);
+}
+
+void ObjectPool::AddObject(ObjectBase* obj)
+{
+ if (obj)
+ {
+ if (!Contains(obj))
+ {
+ obj->Retain();
+
+ std::lock_guard lock(mutex_);
+ objects_.push_back(obj);
+ }
+ }
+}
+
+bool ObjectPool::Contains(ObjectBase* obj) const
+{
+ std::lock_guard lock(const_cast(mutex_));
+
+ for (auto iter = pools_.rbegin(); iter != pools_.rend(); iter++)
+ for (const auto o : (*iter)->objects_)
+ if (obj == o)
+ return true;
+ return false;
+}
+
+void ObjectPool::Clear()
+{
+ Vector copied;
+
+ {
+ std::lock_guard lock(mutex_);
+ copied = std::move(objects_);
+ }
+
+ for (auto obj : copied)
+ obj->Release();
+}
+
+} // namespace kiwano
diff --git a/src/kiwano/base/ObjectPool.h b/src/kiwano/base/ObjectPool.h
new file mode 100644
index 00000000..72ac3d5b
--- /dev/null
+++ b/src/kiwano/base/ObjectPool.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2016-2018 Kiwano - Nomango
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#pragma once
+#include
+#include
+
+namespace kiwano
+{
+/**
+ * \~chinese
+ * @brief 对象池
+ */
+class KGE_API ObjectPool
+ : public Noncopyable
+{
+public:
+ static ObjectPool& GetInstance();
+
+ ObjectPool();
+
+ virtual ~ObjectPool();
+
+ /**
+ * \~chinese
+ * @brief 添加对象到内存池
+ * @param[in] obj 基础对象
+ */
+ void AddObject(ObjectBase* obj);
+
+ /**
+ * \~chinese
+ * @brief 判断对象是否在对象池中
+ * @param[in] obj 基础对象
+ */
+ bool Contains(ObjectBase* obj) const;
+
+ /**
+ * \~chinese
+ * @brief 清空所有对象
+ */
+ void Clear();
+
+private:
+ ObjectPool(const ObjectPool&) = delete;
+
+ ObjectPool& operator=(const ObjectPool&) = delete;
+
+private:
+ std::mutex mutex_;
+ Vector objects_;
+
+ static List pools_;
+};
+} // namespace kiwano
diff --git a/src/kiwano/base/component/Button.cpp b/src/kiwano/base/component/Button.cpp
index a8150917..c8fbc49b 100644
--- a/src/kiwano/base/component/Button.cpp
+++ b/src/kiwano/base/component/Button.cpp
@@ -47,7 +47,7 @@ ButtonPtr Button::Create(const Callback& click, const Callback& pressed, const C
Button::Button()
: status_(Status::Normal)
{
- SetName("KGE_BUTTON");
+ SetName("__KGE_BUTTON__");
}
Button::~Button()
diff --git a/src/kiwano/base/component/Component.h b/src/kiwano/base/component/Component.h
index c48a168a..ccd31895 100644
--- a/src/kiwano/base/component/Component.h
+++ b/src/kiwano/base/component/Component.h
@@ -21,7 +21,6 @@
#pragma once
#include
#include
-#include
#include
namespace kiwano
@@ -49,10 +48,8 @@ KGE_DECLARE_SMART_PTR(Component);
*/
class KGE_API Component
: public ObjectBase
- , protected IntrusiveListValue
{
friend class ComponentManager;
- friend IntrusiveList;
public:
/// \~chinese
diff --git a/src/kiwano/base/component/ComponentManager.cpp b/src/kiwano/base/component/ComponentManager.cpp
index 8f99ce75..a0d3dbb7 100644
--- a/src/kiwano/base/component/ComponentManager.cpp
+++ b/src/kiwano/base/component/ComponentManager.cpp
@@ -19,6 +19,7 @@
// THE SOFTWARE.
#include
+#include
namespace kiwano
{
@@ -34,46 +35,74 @@ Component* ComponentManager::AddComponent(ComponentPtr component)
if (component)
{
- component->InitComponent(target_);
- components_.PushBack(component);
+ size_t hash = std::hash{}(component->GetName());
+ AddComponent(hash, component);
}
return component.Get();
}
-ComponentList& ComponentManager::GetAllComponents()
+Component* ComponentManager::AddComponent(size_t index, ComponentPtr component)
+{
+ KGE_ASSERT(component && "AddComponent failed, NULL pointer exception");
+
+ if (component)
+ {
+ component->InitComponent(target_);
+
+ components_.insert(std::make_pair(index, component));
+ }
+ return component.Get();
+}
+
+Component* ComponentManager::GetComponent(const String& name)
+{
+ size_t hash = std::hash{}(name);
+ return GetComponent(hash);
+}
+
+Component* ComponentManager::GetComponent(size_t name_hash)
+{
+ if (!components_.empty())
+ {
+ auto iter = components_.find(name_hash);
+ if (iter != components_.end())
+ {
+ return iter->second.Get();
+ }
+ }
+ return nullptr;
+}
+
+ComponentMap& ComponentManager::GetAllComponents()
{
return components_;
}
-const ComponentList& ComponentManager::GetAllComponents() const
+const ComponentMap& ComponentManager::GetAllComponents() const
{
return components_;
}
void ComponentManager::RemoveComponent(ComponentPtr component)
{
- auto iter = std::find(components_.begin(), components_.end(), component);
- if (iter != components_.end())
- {
- component->DestroyComponent();
- components_.Remove(component);
- }
+ RemoveComponent(component->GetName());
}
-void ComponentManager::RemoveComponents(const String& name)
+void ComponentManager::RemoveComponent(const String& name)
{
- if (!components_.IsEmpty())
- {
- ComponentPtr next;
- for (auto component = components_.GetFirst(); component; component = next)
- {
- next = component->GetNext();
+ size_t hash = std::hash{}(name);
+ RemoveComponent(hash);
+}
- if (component->IsName(name))
- {
- component->DestroyComponent();
- components_.Remove(component);
- }
+void ComponentManager::RemoveComponent(size_t name_hash)
+{
+ if (!components_.empty())
+ {
+ auto iter = components_.find(name_hash);
+ if (iter != components_.end())
+ {
+ iter->second->DestroyComponent();
+ components_.erase(iter);
}
}
}
@@ -81,31 +110,28 @@ void ComponentManager::RemoveComponents(const String& name)
void ComponentManager::RemoveAllComponents()
{
// Destroy all components
- if (!components_.IsEmpty())
+ if (!components_.empty())
{
- ComponentPtr next;
- for (auto component = components_.GetFirst(); component; component = next)
+ for (auto& p : components_)
{
- next = component->GetNext();
-
- component->DestroyComponent();
+ p.second->DestroyComponent();
}
}
- components_.Clear();
+ components_.clear();
}
void ComponentManager::Update(Duration dt)
{
- if (!components_.IsEmpty())
+ if (!components_.empty())
{
- ComponentPtr next;
- for (auto component = components_.GetFirst(); component; component = next)
+ if (!components_.empty())
{
- next = component->GetNext();
-
- if (component->IsEnable())
+ for (auto& p : components_)
{
- component->OnUpdate(dt);
+ if (p.second->IsEnable())
+ {
+ p.second->OnUpdate(dt);
+ }
}
}
}
@@ -113,16 +139,16 @@ void ComponentManager::Update(Duration dt)
void ComponentManager::Render(RenderContext& ctx)
{
- if (!components_.IsEmpty())
+ if (!components_.empty())
{
- ComponentPtr next;
- for (auto component = components_.GetFirst(); component; component = next)
+ if (!components_.empty())
{
- next = component->GetNext();
-
- if (component->IsEnable())
+ for (auto& p : components_)
{
- component->OnRender(ctx);
+ if (p.second->IsEnable())
+ {
+ p.second->OnRender(ctx);
+ }
}
}
}
@@ -130,16 +156,16 @@ void ComponentManager::Render(RenderContext& ctx)
void ComponentManager::DispatchToComponents(Event* evt)
{
- if (!components_.IsEmpty())
+ if (!components_.empty())
{
- ComponentPtr next;
- for (auto component = components_.GetFirst(); component; component = next)
+ if (!components_.empty())
{
- next = component->GetNext();
-
- if (component->IsEnable())
+ for (auto& p : components_)
{
- component->HandleEvent(evt);
+ if (p.second->IsEnable())
+ {
+ p.second->HandleEvent(evt);
+ }
}
}
}
diff --git a/src/kiwano/base/component/ComponentManager.h b/src/kiwano/base/component/ComponentManager.h
index 469e7eaa..139a2832 100644
--- a/src/kiwano/base/component/ComponentManager.h
+++ b/src/kiwano/base/component/ComponentManager.h
@@ -31,8 +31,8 @@ namespace kiwano
*/
/// \~chinese
-/// @brief 组件列表
-typedef IntrusiveList ComponentList;
+/// @brief 组件映射
+typedef UnorderedMap ComponentMap;
/**
* \~chinese
@@ -47,12 +47,26 @@ public:
Component* AddComponent(ComponentPtr component);
/// \~chinese
- /// @brief 获取所有组件
- ComponentList& GetAllComponents();
+ /// @brief 添加组件
+ /// @param index 索引值
+ /// @param component 组件
+ Component* AddComponent(size_t index, ComponentPtr component);
+
+ /// \~chinese
+ /// @brief 获取组件
+ Component* GetComponent(const String& name);
+
+ /// \~chinese
+ /// @brief 获取组件
+ Component* GetComponent(size_t name_hash);
/// \~chinese
/// @brief 获取所有组件
- const ComponentList& GetAllComponents() const;
+ ComponentMap& GetAllComponents();
+
+ /// \~chinese
+ /// @brief 获取所有组件
+ const ComponentMap& GetAllComponents() const;
/// \~chinese
/// @brief 移除组件
@@ -61,7 +75,12 @@ public:
/// \~chinese
/// @brief 移除组件
/// @param name 组件名称
- void RemoveComponents(const String& name);
+ void RemoveComponent(const String& name);
+
+ /// \~chinese
+ /// @brief 移除组件
+ /// @param name_hash 组件名称hash值
+ void RemoveComponent(size_t name_hash);
/// \~chinese
/// @brief 移除所有组件
@@ -83,8 +102,8 @@ protected:
ComponentManager(Actor* target);
private:
- Actor* target_;
- ComponentList components_;
+ Actor* target_;
+ ComponentMap components_;
};
/** @} */
diff --git a/src/kiwano/core/Defer.h b/src/kiwano/core/Defer.h
new file mode 100644
index 00000000..961c6c52
--- /dev/null
+++ b/src/kiwano/core/Defer.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2016-2018 Kiwano - Nomango
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#pragma once
+#include
+
+namespace kiwano
+{
+
+class Defer
+{
+public:
+ Defer() = default;
+
+ Defer(const Function& func)
+ : func_(func)
+ {
+ }
+
+ Defer(Defer&& other) noexcept
+ : func_(other.func_)
+ {
+ other.func_ = nullptr;
+ }
+
+ ~Defer()
+ {
+ if (func_)
+ func_();
+ }
+
+private:
+ Defer(const Defer&) = delete;
+ Defer& operator=(const Defer&) = delete;
+
+ Function func_;
+};
+
+class __DeferHelper
+{
+public:
+ Defer operator-(const Function& func) const
+ {
+ return Defer{ func };
+ }
+};
+
+#define KGE_DEFER auto __KGE_DEFER_VAR(__defer_line_, __LINE__, __) = ::kiwano::__DeferHelper() -
+#define __KGE_DEFER_VAR(a, b, c) __KGE_DEFER_TOKEN_CONNECT(a, b, c)
+#define __KGE_DEFER_TOKEN_CONNECT(a, b, c) a##b##c
+
+} // namespace kiwano
diff --git a/src/kiwano/core/Singleton.h b/src/kiwano/core/Singleton.h
index 7ed4e853..c067e1d4 100644
--- a/src/kiwano/core/Singleton.h
+++ b/src/kiwano/core/Singleton.h
@@ -19,12 +19,13 @@
// THE SOFTWARE.
#pragma once
+#include
namespace kiwano
{
template
-struct Singleton
+class Singleton
{
protected:
Singleton() = default;
@@ -32,29 +33,47 @@ protected:
Singleton& operator=(const Singleton&) = delete;
private:
- struct ObjectCreator
+ struct InstanceCreator
{
- ObjectCreator()
+ InstanceCreator()
{
- (void)Singleton<_Ty>::GetInstance();
+ (void)Singleton<_Ty>::GetInstancePtr();
}
inline void Dummy() const {}
};
- static ObjectCreator creator_;
+ static InstanceCreator creator_;
public:
using object_type = _Ty;
+ static std::unique_ptr instance_ptr_;
+
static inline object_type& GetInstance()
{
- static object_type instance;
+ return *GetInstancePtr();
+ }
+
+ static inline object_type* GetInstancePtr()
+ {
creator_.Dummy();
- return instance;
+ if (!instance_ptr_)
+ {
+ instance_ptr_.reset(new object_type);
+ }
+ return instance_ptr_.get();
+ }
+
+ static inline void DestroyInstance()
+ {
+ instance_ptr_.reset();
}
};
template
-typename Singleton<_Ty>::ObjectCreator Singleton<_Ty>::creator_;
+typename Singleton<_Ty>::InstanceCreator Singleton<_Ty>::creator_;
+
+template
+typename std::unique_ptr<_Ty> Singleton<_Ty>::instance_ptr_;
} // namespace kiwano
diff --git a/src/kiwano/event/Event.h b/src/kiwano/event/Event.h
index 16242d27..80fa0b76 100644
--- a/src/kiwano/event/Event.h
+++ b/src/kiwano/event/Event.h
@@ -54,7 +54,7 @@ public:
/// \~chinese
/// @brief 判断事件类型
- /// @return 是否是指定事件类型
+ /// @return 事件类型相同返回true,否则返回false
template
bool IsType() const;
@@ -75,19 +75,20 @@ private:
};
/// \~chinese
-/// @brief 事件特性:判断指定类型是否是事件
+/// @brief 事件特性:判断是否是事件
template
-struct IsEvent : public std::bool_constant::value || std::is_same::value>
+struct IsBaseOfEvent : public std::bool_constant::value || std::is_same::value>
{
};
/// \~chinese
-/// @brief 事件特性:判断一个事件能否安全转换到另一事件类型
-template ::value, int>::type>
-struct IsEventType
+/// @brief 事件特性:判断事件类型是否相同
+template
+struct IsSameEventType
{
inline bool operator()(const Event* evt) const
{
+ static_assert(kiwano::IsBaseOfEvent<_Ty>::value, "_Ty is not an event type.");
return evt->GetType() == KGE_EVENT(_Ty);
}
};
@@ -102,8 +103,8 @@ inline const EventType& Event::GetType() const
template
inline bool Event::IsType() const
{
- static_assert(kiwano::IsEvent<_Ty>::value, "_Ty is not an event type.");
- return kiwano::IsEventType<_Ty>()(this);
+ static_assert(kiwano::IsBaseOfEvent<_Ty>::value, "_Ty is not an event type.");
+ return IsSameEventType<_Ty>()(this);
}
template
diff --git a/src/kiwano/event/EventDispatcher.h b/src/kiwano/event/EventDispatcher.h
index 6e5a0861..6e46a9a4 100644
--- a/src/kiwano/event/EventDispatcher.h
+++ b/src/kiwano/event/EventDispatcher.h
@@ -59,7 +59,7 @@ public:
template
EventListener* AddListener(EventListener::Callback callback)
{
- static_assert(kiwano::IsEvent<_EventTy>::value, "_EventTy is not an event type.");
+ static_assert(kiwano::IsBaseOfEvent<_EventTy>::value, "_EventTy is not an event type.");
return AddListener(KGE_EVENT(_EventTy), callback);
}
@@ -71,7 +71,7 @@ public:
template
EventListener* AddListener(const String& name, EventListener::Callback callback)
{
- static_assert(kiwano::IsEvent<_EventTy>::value, "_EventTy is not an event type.");
+ static_assert(kiwano::IsBaseOfEvent<_EventTy>::value, "_EventTy is not an event type.");
return AddListener(name, KGE_EVENT(_EventTy), callback);
}
diff --git a/src/kiwano/event/EventListener.cpp b/src/kiwano/event/EventListener.cpp
index 1549d5d3..060ba62d 100644
--- a/src/kiwano/event/EventListener.cpp
+++ b/src/kiwano/event/EventListener.cpp
@@ -58,4 +58,12 @@ EventListener::EventListener()
EventListener::~EventListener() {}
+void EventListener::Receive(Event* evt)
+{
+ if (ShouldHandle(evt) && callback_)
+ {
+ callback_(evt);
+ }
+}
+
} // namespace kiwano
diff --git a/src/kiwano/event/EventListener.h b/src/kiwano/event/EventListener.h
index d179ba11..4baed41b 100644
--- a/src/kiwano/event/EventListener.h
+++ b/src/kiwano/event/EventListener.h
@@ -67,7 +67,7 @@ public:
template
static inline EventListenerPtr Create(const Callback& callback)
{
- static_assert(kiwano::IsEvent<_EventTy>::value, "_EventTy is not an event type.");
+ static_assert(kiwano::IsBaseOfEvent<_EventTy>::value, "_EventTy is not an event type.");
return EventListener::Create(KGE_EVENT(_EventTy), callback);
}
@@ -79,7 +79,7 @@ public:
template
static inline EventListenerPtr Create(const String& name, const Callback& callback)
{
- static_assert(kiwano::IsEvent<_EventTy>::value, "_EventTy is not an event type.");
+ static_assert(kiwano::IsBaseOfEvent<_EventTy>::value, "_EventTy is not an event type.");
return EventListener::Create(name, KGE_EVENT(_EventTy), callback);
}
@@ -132,12 +132,17 @@ public:
/// @brief 设置监听的事件类型
void SetEventType(const EventType& type);
+ /// \~chinese
+ /// @brief 判断是否处理事件
+ virtual bool ShouldHandle(Event* evt) const;
+
/// \~chinese
/// @brief 设置监听的事件类型
/// @tparam _EventTy 事件类型
- template ::value, int>::type>
+ template
inline void SetEventType()
{
+ static_assert(kiwano::IsBaseOfEvent<_EventTy>::value, "_EventTy is not an event type.");
SetEventType(KGE_EVENT(_EventTy));
}
@@ -208,13 +213,13 @@ inline void EventListener::SetEventType(const EventType& type)
type_ = type;
}
-inline void EventListener::Receive(Event* evt)
+inline bool EventListener::ShouldHandle(Event* evt) const
{
- KGE_ASSERT(evt != nullptr);
-
- if (type_ == evt->GetType() && callback_)
+ if (evt)
{
- callback_(evt);
+ return evt->GetType() == type_;
}
+ return false;
}
+
} // namespace kiwano
diff --git a/src/kiwano/event/KeyEvent.h b/src/kiwano/event/KeyEvent.h
index a56ed119..390cd01d 100644
--- a/src/kiwano/event/KeyEvent.h
+++ b/src/kiwano/event/KeyEvent.h
@@ -72,10 +72,8 @@ public:
KeyCharEvent();
};
-/** @} */
-
template <>
-struct IsEventType
+struct IsSameEventType
{
inline bool operator()(const Event* evt) const
{
@@ -84,4 +82,6 @@ struct IsEventType
}
};
+/** @} */
+
} // namespace kiwano
diff --git a/src/kiwano/event/MouseEvent.h b/src/kiwano/event/MouseEvent.h
index 29731d39..6a308d93 100644
--- a/src/kiwano/event/MouseEvent.h
+++ b/src/kiwano/event/MouseEvent.h
@@ -113,10 +113,8 @@ public:
MouseWheelEvent();
};
-/** @} */
-
template <>
-struct IsEventType
+struct IsSameEventType
{
inline bool operator()(const Event* evt) const
{
@@ -127,4 +125,6 @@ struct IsEventType
}
};
+/** @} */
+
} // namespace kiwano
diff --git a/src/kiwano/event/WindowEvent.h b/src/kiwano/event/WindowEvent.h
index 4370422d..d0c5da83 100644
--- a/src/kiwano/event/WindowEvent.h
+++ b/src/kiwano/event/WindowEvent.h
@@ -93,10 +93,8 @@ public:
WindowClosedEvent();
};
-/** @} */
-
template <>
-struct IsEventType
+struct IsSameEventType
{
inline bool operator()(const Event* evt) const
{
@@ -107,4 +105,6 @@ struct IsEventType
}
};
+/** @} */
+
} // namespace kiwano
diff --git a/src/kiwano/kiwano.h b/src/kiwano/kiwano.h
index 1bd37f0f..d4630520 100644
--- a/src/kiwano/kiwano.h
+++ b/src/kiwano/kiwano.h
@@ -44,6 +44,7 @@
//
#include
+#include
#include
#include
#include
diff --git a/src/kiwano/platform/Application.cpp b/src/kiwano/platform/Application.cpp
index 5a9edfc8..a117aa31 100644
--- a/src/kiwano/platform/Application.cpp
+++ b/src/kiwano/platform/Application.cpp
@@ -19,11 +19,12 @@
// THE SOFTWARE.
#include
-#include
+#include
#include
#include
#include
#include
+#include
namespace kiwano
{
@@ -42,15 +43,15 @@ Application::Application()
Application::~Application()
{
- this->Destroy();
}
void Application::Run(RunnerPtr runner)
{
KGE_ASSERT(runner);
- runner_ = runner;
- running_ = true;
+ running_ = true;
is_paused_ = false;
+ runner_ = runner;
+ timer_ = Timer::Create();
// Initialize runner
runner->InitSettings();
@@ -61,51 +62,43 @@ void Application::Run(RunnerPtr runner)
c->SetupModule();
}
+ // Ensure resources are destroyed before exiting
+ KGE_DEFER[=]()
+ {
+ this->Destroy();
+ };
+
// Everything is ready
runner->OnReady();
+ // Update everything
+ this->Update(0);
+
+ // Start the loop
while (running_)
{
- if (!frame_ticker_)
- {
- frame_ticker_ = Ticker::Create(0);
- }
+ timer_->Tick();
- if (frame_ticker_->Tick())
- {
- // Execute main loop
- if (!runner->MainLoop(frame_ticker_->GetDeltaTime()))
- running_ = false;
- }
- else
- {
- // Releases CPU
- Duration total_dt = frame_ticker_->GetDeltaTime() + frame_ticker_->GetErrorTime();
- Duration sleep_dt = frame_ticker_->GetInterval() - total_dt;
- if (sleep_dt.Milliseconds() > 1LL)
- {
- sleep_dt.Sleep();
- }
- }
+ // Execute main loop
+ if (!runner->MainLoop(timer_->GetDeltaTime()))
+ running_ = false;
}
-
- this->Destroy();
}
void Application::Pause()
{
is_paused_ = true;
- if (frame_ticker_)
- frame_ticker_->Pause();
+ if (timer_)
+ timer_->Pause();
}
void Application::Resume()
{
is_paused_ = false;
- if (frame_ticker_)
- frame_ticker_->Resume();
+ if (timer_)
+ timer_->Resume();
}
void Application::Quit()
@@ -113,6 +106,12 @@ void Application::Quit()
running_ = false;
}
+void Application::UpdateFrame(Duration dt)
+{
+ this->Render();
+ this->Update(dt);
+}
+
void Application::Destroy()
{
if (runner_)
diff --git a/src/kiwano/platform/Application.h b/src/kiwano/platform/Application.h
index 1107cde5..670440c3 100644
--- a/src/kiwano/platform/Application.h
+++ b/src/kiwano/platform/Application.h
@@ -27,7 +27,7 @@
#include
#include
#include
-#include
+#include
namespace kiwano
{
@@ -103,18 +103,6 @@ public:
*/
WindowPtr GetWindow() const;
- /**
- * \~chinese
- * @brief 获取帧报时器
- */
- TickerPtr GetFrameTicker() const;
-
- /**
- * \~chinese
- * @brief 设置帧报时器
- */
- void SetFrameTicker(TickerPtr ticker);
-
/**
* \~chinese
* @brief 设置时间缩放因子
@@ -150,16 +138,10 @@ public:
/**
* \~chinese
- * @brief 更新所有模块
+ * @brief 更新一帧
* @param dt 时间间隔
*/
- void Update(Duration dt);
-
- /**
- * \~chinese
- * @brief 创建渲染上下文并渲染画面
- */
- void Render();
+ void UpdateFrame(Duration dt);
/**
* \~chinese
@@ -167,12 +149,26 @@ public:
*/
void Destroy();
+private:
+ /**
+ * \~chinese
+ * @brief 更新所有模块
+ * @param dt 时间间隔
+ */
+ void Update(Duration dt);
+
+ /**
+ * \~chinese
+ * @brief 渲染画面
+ */
+ void Render();
+
private:
bool running_;
bool is_paused_;
float time_scale_;
RunnerPtr runner_;
- TickerPtr frame_ticker_;
+ TimerPtr timer_;
List modules_;
std::mutex perform_mutex_;
Queue> functions_to_perform_;
@@ -185,18 +181,9 @@ inline RunnerPtr Application::GetRunner() const
inline WindowPtr Application::GetWindow() const
{
- KGE_ASSERT(runner_);
- return runner_->GetWindow();
-}
-
-inline TickerPtr Application::GetFrameTicker() const
-{
- return frame_ticker_;
-}
-
-inline void Application::SetFrameTicker(TickerPtr ticker)
-{
- frame_ticker_ = ticker;
+ if (runner_)
+ return runner_->GetWindow();
+ return nullptr;
}
inline bool Application::IsPaused() const
diff --git a/src/kiwano/platform/FileSystem.h b/src/kiwano/platform/FileSystem.h
index 943fed7a..a012ff78 100644
--- a/src/kiwano/platform/FileSystem.h
+++ b/src/kiwano/platform/FileSystem.h
@@ -102,11 +102,11 @@ public:
*/
bool ExtractResourceToFile(const Resource& res, const String& dest_file_name) const;
+ ~FileSystem();
+
private:
FileSystem();
- ~FileSystem();
-
private:
Vector search_paths_;
UnorderedMap file_lookup_dict_;
diff --git a/src/kiwano/platform/Input.h b/src/kiwano/platform/Input.h
index d2a72793..2bd4ad84 100644
--- a/src/kiwano/platform/Input.h
+++ b/src/kiwano/platform/Input.h
@@ -112,11 +112,11 @@ public:
void HandleEvent(Event* evt) override;
+ ~Input();
+
private:
Input();
- ~Input();
-
void UpdateKey(KeyCode key, bool down);
void UpdateButton(MouseButton btn, bool down);
diff --git a/src/kiwano/platform/Runner.cpp b/src/kiwano/platform/Runner.cpp
index a289b6a5..90099f3a 100644
--- a/src/kiwano/platform/Runner.cpp
+++ b/src/kiwano/platform/Runner.cpp
@@ -82,8 +82,8 @@ void Runner::InitSettings()
}
// Create game window
- WindowPtr window =
- Window::Create(settings_.title, settings_.width, settings_.height, settings_.icon, settings_.resizable);
+ WindowPtr window = Window::Create(settings_.title, settings_.width, settings_.height, settings_.icon,
+ settings_.resizable, settings_.fullscreen);
SetWindow(window);
// Update renderer settings
@@ -101,6 +101,9 @@ void Runner::InitSettings()
Director::GetInstance().ShowDebugInfo(true);
Renderer::GetInstance().GetContext().SetCollectingStatus(true);
}
+
+ // Create frame ticker
+ frame_ticker_ = Ticker::Create(settings_.frame_interval, -1);
}
bool Runner::MainLoop(Duration dt)
@@ -118,9 +121,6 @@ bool Runner::MainLoop(Duration dt)
Application& app = Application::GetInstance();
- // Update modules before poll events
- app.Update(dt);
-
// Poll events
main_window_->PumpEvents();
while (EventPtr evt = main_window_->PollEvent())
@@ -128,12 +128,27 @@ bool Runner::MainLoop(Duration dt)
app.DispatchEvent(evt.Get());
}
- app.Render();
-
- if (app.IsPaused())
+ // Update frame ticker
+ if (frame_ticker_)
{
- // Slow down when the application is paused
- Duration(5).Sleep();
+ if (frame_ticker_->Tick(dt))
+ {
+ app.UpdateFrame(frame_ticker_->GetDeltaTime());
+ }
+ else
+ {
+ // Releases CPU
+ Duration total_dt = frame_ticker_->GetDeltaTime() + frame_ticker_->GetErrorTime();
+ Duration sleep_dt = frame_ticker_->GetInterval() - total_dt;
+ if (sleep_dt.Milliseconds() > 1LL)
+ {
+ sleep_dt.Sleep();
+ }
+ }
+ }
+ else
+ {
+ app.UpdateFrame(dt);
}
return true;
}
diff --git a/src/kiwano/platform/Runner.h b/src/kiwano/platform/Runner.h
index db1a6d38..68743408 100644
--- a/src/kiwano/platform/Runner.h
+++ b/src/kiwano/platform/Runner.h
@@ -24,6 +24,8 @@
#include
#include
#include
+#include
+
namespace kiwano
{
@@ -38,14 +40,16 @@ KGE_DECLARE_SMART_PTR(Runner);
*/
struct Settings
{
- uint32_t width; ///< 窗口宽度
- uint32_t height; ///< 窗口高度
- String title; ///< 窗口标题
- uint32_t icon; ///< 窗口图标
- bool resizable; ///< 窗口大小可调整
- Color bg_color; ///< 窗口背景色
- bool vsync_enabled; ///< 垂直同步
- bool debug_mode; ///< 调试模式
+ uint32_t width; ///< 窗口宽度
+ uint32_t height; ///< 窗口高度
+ String title; ///< 窗口标题
+ uint32_t icon; ///< 窗口图标
+ bool resizable; ///< 窗口大小可调整
+ bool fullscreen; ///< 窗口全屏
+ Color bg_color; ///< 窗口背景色
+ Duration frame_interval; ///< 帧间隔
+ bool vsync_enabled; ///< 垂直同步
+ bool debug_mode; ///< 调试模式
Settings()
: width(800)
@@ -53,8 +57,10 @@ struct Settings
, title("Kiwano")
, icon()
, resizable(false)
+ , fullscreen(false)
, bg_color(Color::Black)
- , vsync_enabled(true)
+ , frame_interval(16)
+ , vsync_enabled(false)
, debug_mode(false)
{
}
@@ -118,6 +124,14 @@ public:
/// @brief 获取设置
Settings GetSettings() const;
+ /// \~chinese
+ /// @brief 获取帧报时器
+ TickerPtr GetFrameTicker() const;
+
+ /// \~chinese
+ /// @brief 设置帧报时器
+ void SetFrameTicker(TickerPtr ticker);
+
protected:
/// \~chinese
/// @brief 修改设置
@@ -131,6 +145,7 @@ private:
private:
Settings settings_;
WindowPtr main_window_;
+ TickerPtr frame_ticker_;
};
inline void Runner::OnReady() {}
@@ -162,4 +177,14 @@ inline void Runner::SetSettings(Settings settings)
settings_ = settings;
}
+inline TickerPtr Runner::GetFrameTicker() const
+{
+ return frame_ticker_;
+}
+
+inline void Runner::SetFrameTicker(TickerPtr ticker)
+{
+ frame_ticker_ = ticker;
+}
+
} // namespace kiwano
diff --git a/src/kiwano/platform/Window.cpp b/src/kiwano/platform/Window.cpp
index e92b7212..3501ab6c 100644
--- a/src/kiwano/platform/Window.cpp
+++ b/src/kiwano/platform/Window.cpp
@@ -71,6 +71,11 @@ uint32_t Window::GetHeight() const
return height_;
}
+Resolution Window::GetCurrentResolution() const
+{
+ return resolution_;
+}
+
WindowHandle Window::GetHandle() const
{
return handle_;
diff --git a/src/kiwano/platform/Window.h b/src/kiwano/platform/Window.h
index 08b9d25a..5a6c89b6 100644
--- a/src/kiwano/platform/Window.h
+++ b/src/kiwano/platform/Window.h
@@ -80,7 +80,7 @@ public:
* @throw kiwano::SystemError 窗口创建失败时抛出
*/
static WindowPtr Create(const String& title, uint32_t width, uint32_t height, uint32_t icon = 0,
- bool resizable = false);
+ bool resizable = false, bool fullscreen = false);
/**
* \~chinese
@@ -110,6 +110,12 @@ public:
*/
uint32_t GetHeight() const;
+ /**
+ * \~chinese
+ * @brief 获取当前分辨率
+ */
+ Resolution GetCurrentResolution() const;
+
/**
* \~chinese
* @brief 获取窗口句柄
@@ -214,6 +220,7 @@ protected:
uint32_t min_height_;
uint32_t max_width_;
uint32_t max_height_;
+ Resolution resolution_;
WindowHandle handle_;
String title_;
std::queue event_queue_;
diff --git a/src/kiwano/platform/win32/WindowImpl.cpp b/src/kiwano/platform/win32/WindowImpl.cpp
index 6cba0482..21634135 100644
--- a/src/kiwano/platform/win32/WindowImpl.cpp
+++ b/src/kiwano/platform/win32/WindowImpl.cpp
@@ -30,7 +30,6 @@
#include
#include
#include
-#include
#include // GET_X_LPARAM, GET_Y_LPARAM
#include // ImmAssociateContext
#pragma comment(lib, "imm32.lib")
@@ -49,7 +48,7 @@ public:
virtual ~WindowWin32Impl();
- void Init(const String& title, uint32_t width, uint32_t height, uint32_t icon, bool resizable);
+ void Init(const String& title, uint32_t width, uint32_t height, uint32_t icon, bool resizable, bool fullscreen);
void SetTitle(const String& title) override;
@@ -69,6 +68,8 @@ public:
DWORD GetStyle() const;
+ void SetActive(bool active);
+
void UpdateCursor();
LRESULT MessageProc(HWND, UINT32, WPARAM, LPARAM);
@@ -86,12 +87,13 @@ private:
std::array key_map_;
};
-WindowPtr Window::Create(const String& title, uint32_t width, uint32_t height, uint32_t icon, bool resizable)
+WindowPtr Window::Create(const String& title, uint32_t width, uint32_t height, uint32_t icon, bool resizable,
+ bool fullscreen)
{
WindowWin32ImplPtr ptr = memory::New();
if (ptr)
{
- ptr->Init(title, width, height, icon, resizable);
+ ptr->Init(title, width, height, icon, resizable, fullscreen);
}
return ptr;
}
@@ -103,6 +105,7 @@ namespace kiwano
#define WINDOW_FIXED_STYLE WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX
#define WINDOW_RESIZABLE_STYLE WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_SIZEBOX | WS_MAXIMIZEBOX
+#define WINDOW_FULLSCREEN_STYLE WS_CLIPCHILDREN | WS_POPUP
namespace
{
@@ -202,7 +205,8 @@ WindowWin32Impl::~WindowWin32Impl()
::timeEndPeriod(0);
}
-void WindowWin32Impl::Init(const String& title, uint32_t width, uint32_t height, uint32_t icon, bool resizable)
+void WindowWin32Impl::Init(const String& title, uint32_t width, uint32_t height, uint32_t icon, bool resizable,
+ bool fullscreen)
{
HINSTANCE hinst = GetModuleHandle(nullptr);
WNDCLASSEXA wcex = { 0 };
@@ -249,10 +253,13 @@ void WindowWin32Impl::Init(const String& title, uint32_t width, uint32_t height,
width = win_width;
height = win_height;
- width_ = width;
- height_ = height;
- resizable_ = resizable;
- handle_ = ::CreateWindowExA(0, "KiwanoAppWnd", title.c_str(), GetStyle(), left, top, width, height, nullptr,
+ width_ = width;
+ height_ = height;
+ resizable_ = resizable;
+ is_fullscreen_ = fullscreen;
+ resolution_ = Resolution{ width_, height_, 0 };
+
+ handle_ = ::CreateWindowExA(0, "KiwanoAppWnd", title.c_str(), GetStyle(), left, top, width, height, nullptr,
nullptr, hinst, nullptr);
if (handle_ == nullptr)
@@ -269,6 +276,19 @@ void WindowWin32Impl::Init(const String& title, uint32_t width, uint32_t height,
::ShowWindow(handle_, SW_SHOWNORMAL);
::UpdateWindow(handle_);
+
+ if (is_fullscreen_)
+ {
+ MONITORINFOEXA info = GetMoniterInfoEx(handle_);
+ int x = (int)info.rcMonitor.left;
+ int y = (int)info.rcMonitor.top;
+ int cx = (int)(info.rcMonitor.right - info.rcMonitor.left);
+ int cy = (int)(info.rcMonitor.bottom - info.rcMonitor.top);
+
+ // Top the window
+ ::SetWindowPos(handle_, HWND_TOPMOST, x, y, cx, cy, SWP_NOACTIVATE);
+ ::ShowWindow(handle_, SW_SHOWNORMAL);
+ }
}
void WindowWin32Impl::PumpEvents()
@@ -318,71 +338,78 @@ void WindowWin32Impl::SetCursor(CursorType cursor)
void WindowWin32Impl::SetResolution(uint32_t width, uint32_t height, bool fullscreen)
{
- auto d3d = kiwano::graphics::directx::GetD3DDeviceResources();
-
- if (fullscreen)
+ if (is_fullscreen_ != fullscreen)
{
- HRESULT hr = d3d->ResizeTarget(width, height);
- KGE_THROW_IF_FAILED(hr, "DXGI ResizeTarget failed!");
+ is_fullscreen_ = fullscreen;
- hr = d3d->SetFullscreenState(fullscreen);
- KGE_THROW_IF_FAILED(hr, "DXGI SetFullscreenState failed!");
+ // Reset window style
+ ::SetWindowLongPtrA(handle_, GWL_STYLE, GetStyle());
+ }
+
+ if (is_fullscreen_)
+ {
+ MONITORINFOEXA info = GetMoniterInfoEx(handle_);
+ int x = (int)info.rcMonitor.left;
+ int y = (int)info.rcMonitor.top;
+ int cx = (int)(info.rcMonitor.right - info.rcMonitor.left);
+ int cy = (int)(info.rcMonitor.bottom - info.rcMonitor.top);
+
+ // Top the window
+ ::SetWindowPos(handle_, HWND_TOPMOST, x, y, cx, cy, SWP_NOACTIVATE);
+ ::ShowWindow(handle_, SW_SHOWNORMAL);
}
else
{
- HRESULT hr = d3d->SetFullscreenState(fullscreen);
- KGE_THROW_IF_FAILED(hr, "DXGI SetFullscreenState failed!");
+ // Adjust the rect of client area
+ RECT rc = { 0, 0, LONG(width), LONG(height) };
+ ::AdjustWindowRect(&rc, GetStyle(), false);
- hr = d3d->ResizeTarget(width, height);
- KGE_THROW_IF_FAILED(hr, "DXGI ResizeTarget failed!");
+ width = uint32_t(rc.right - rc.left);
+ height = uint32_t(rc.bottom - rc.top);
+
+ MONITORINFOEXA info = GetMoniterInfoEx(handle_);
+ uint32_t screenw = uint32_t(info.rcWork.right - info.rcWork.left);
+ uint32_t screenh = uint32_t(info.rcWork.bottom - info.rcWork.top);
+ int left = screenw > width ? ((screenw - width) / 2) : 0;
+ int top = screenh > height ? ((screenh - height) / 2) : 0;
+
+ // Reset window style
+ ::SetWindowLongPtrA(handle_, GWL_STYLE, GetStyle());
+
+ // Unpin the window
+ ::SetWindowPos(handle_, HWND_NOTOPMOST, left, top, width, height, SWP_DRAWFRAME | SWP_FRAMECHANGED);
+ ::ShowWindow(handle_, SW_SHOWNORMAL);
}
- is_fullscreen_ = fullscreen;
+ resolution_ = Resolution{ width, height, 0 };
+
+ // Resize render target
+ Renderer::GetInstance().Resize(width, height);
}
Vector WindowWin32Impl::GetResolutions()
{
if (resolutions_.empty())
{
- auto d3d = kiwano::graphics::directx::GetD3DDeviceResources();
+ Set resolution_list;
- DXGI_MODE_DESC* mode_descs = nullptr;
- int mode_num = 0;
+ DEVMODEA dmi;
+ ZeroMemory(&dmi, sizeof(dmi));
+ dmi.dmSize = sizeof(dmi);
- HRESULT hr = d3d->GetDisplaySettings(&mode_descs, &mode_num);
- if (SUCCEEDED(hr))
+ DWORD mode_count = 0;
+ while (EnumDisplaySettingsA(device_name_.c_str(), mode_count++, &dmi) != 0)
{
- std::unique_ptr mode_list(mode_descs);
+ StringStream ss;
+ ss << dmi.dmPelsWidth << 'x' << dmi.dmPelsHeight << ':' << dmi.dmDisplayFrequency;
- if (mode_list)
+ if (resolution_list.find(ss.str()) == resolution_list.end())
{
- for (int i = 0; i < mode_num; i++)
- {
- Resolution res;
- res.width = mode_descs[i].Width;
- res.height = mode_descs[i].Height;
- res.refresh_rate = 0;
-
- if (mode_descs[i].RefreshRate.Denominator > 0)
- {
- res.refresh_rate = mode_descs[i].RefreshRate.Numerator / mode_descs[i].RefreshRate.Denominator;
- }
-
- if (!resolutions_.empty())
- {
- auto& back = resolutions_.back();
- if (back.width == res.width && back.height == res.height
- && back.refresh_rate == res.refresh_rate)
- continue;
- }
-
- resolutions_.push_back(res);
- }
+ resolution_list.insert(ss.str());
+ resolutions_.push_back(Resolution{ uint32_t(dmi.dmPelsWidth), uint32_t(dmi.dmPelsHeight),
+ uint32_t(dmi.dmDisplayFrequency) });
}
- }
- else
- {
- KGE_THROW_IF_FAILED(hr, "DXGI GetDisplaySettings failed!");
+ ZeroMemory(&dmi, sizeof(dmi));
}
}
return resolutions_;
@@ -390,7 +417,30 @@ Vector WindowWin32Impl::GetResolutions()
DWORD WindowWin32Impl::GetStyle() const
{
- return (resizable_ ? (WINDOW_RESIZABLE_STYLE) : (WINDOW_FIXED_STYLE));
+ if (is_fullscreen_)
+ return WINDOW_FULLSCREEN_STYLE;
+ if (resizable_)
+ return WINDOW_RESIZABLE_STYLE;
+ return WINDOW_FIXED_STYLE;
+}
+
+void WindowWin32Impl::SetActive(bool active)
+{
+ if (!handle_)
+ return;
+
+ if (is_fullscreen_)
+ {
+ // Hide window when it is not active
+ if (active)
+ {
+ ::SetWindowPos(handle_, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+ }
+ else
+ {
+ ::SetWindowPos(handle_, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+ }
+ }
}
void WindowWin32Impl::UpdateCursor()
@@ -658,8 +708,7 @@ LRESULT WindowWin32Impl::MessageProc(HWND hwnd, UINT32 msg, WPARAM wparam, LPARA
{
if (is_fullscreen_)
{
- // TODO restore to fullscreen mode
- // SetResolution();
+ SetActive(true);
}
}
break;
@@ -668,8 +717,7 @@ LRESULT WindowWin32Impl::MessageProc(HWND hwnd, UINT32 msg, WPARAM wparam, LPARA
{
if (is_fullscreen_)
{
- // TODO exit fullscreen mode
- // ::ShowWindow(handle_, SW_MINIMIZE);
+ SetActive(false);
}
}
break;
@@ -695,18 +743,6 @@ LRESULT WindowWin32Impl::MessageProc(HWND hwnd, UINT32 msg, WPARAM wparam, LPARA
case WM_DISPLAYCHANGE:
{
KGE_SYS_LOG("The display resolution has changed");
-
- // Check fullscreen state
- auto d3d_res = graphics::directx::GetD3DDeviceResources();
- auto swap_chain = d3d_res->GetDXGISwapChain();
- if (swap_chain)
- {
- BOOL is_fullscreen = FALSE;
- if (SUCCEEDED(swap_chain->GetFullscreenState(&is_fullscreen, nullptr)))
- {
- is_fullscreen_ = !!is_fullscreen;
- }
- }
}
break;
diff --git a/src/kiwano/render/DirectX/D3D10DeviceResources.cpp b/src/kiwano/render/DirectX/D3D10DeviceResources.cpp
index e10427db..9b388d44 100644
--- a/src/kiwano/render/DirectX/D3D10DeviceResources.cpp
+++ b/src/kiwano/render/DirectX/D3D10DeviceResources.cpp
@@ -68,7 +68,7 @@ inline bool SdkLayersAvailable()
struct D3D10DeviceResources : public ID3D10DeviceResources
{
public:
- HRESULT Initialize(HWND hwnd) override;
+ HRESULT Initialize(HWND hwnd, Size logical_size) override;
HRESULT Present(bool vsync) override;
@@ -138,14 +138,10 @@ D3D10DeviceResources::~D3D10DeviceResources()
DiscardResources();
}
-HRESULT D3D10DeviceResources::Initialize(HWND hwnd)
+HRESULT D3D10DeviceResources::Initialize(HWND hwnd, Size logical_size)
{
- RECT rc;
- ::GetClientRect(hwnd, &rc);
-
this->hwnd_ = hwnd;
- this->logical_size_.x = float(rc.right - rc.left);
- this->logical_size_.y = float(rc.bottom - rc.top);
+ this->logical_size_ = logical_size;
HRESULT hr = this->CreateDeviceResources();
diff --git a/src/kiwano/render/DirectX/D3D11DeviceResources.cpp b/src/kiwano/render/DirectX/D3D11DeviceResources.cpp
index e767c848..31e0c3f4 100644
--- a/src/kiwano/render/DirectX/D3D11DeviceResources.cpp
+++ b/src/kiwano/render/DirectX/D3D11DeviceResources.cpp
@@ -60,7 +60,7 @@ inline bool SdkLayersAvailable()
struct D3D11DeviceResources : public ID3D11DeviceResources
{
public:
- HRESULT Initialize(HWND hwnd) override;
+ HRESULT Initialize(HWND hwnd, Size logical_size) override;
HRESULT Present(bool vsync) override;
@@ -133,14 +133,10 @@ D3D11DeviceResources::~D3D11DeviceResources()
DiscardResources();
}
-HRESULT D3D11DeviceResources::Initialize(HWND hwnd)
+HRESULT D3D11DeviceResources::Initialize(HWND hwnd, Size logical_size)
{
- RECT rc;
- ::GetClientRect(hwnd, &rc);
-
this->hwnd_ = hwnd;
- this->logical_size_.x = float(rc.right - rc.left);
- this->logical_size_.y = float(rc.bottom - rc.top);
+ this->logical_size_ = logical_size;
HRESULT hr = this->CreateDeviceResources();
diff --git a/src/kiwano/render/DirectX/D3DDeviceResourcesBase.h b/src/kiwano/render/DirectX/D3DDeviceResourcesBase.h
index a14ab67b..75961d89 100644
--- a/src/kiwano/render/DirectX/D3DDeviceResourcesBase.h
+++ b/src/kiwano/render/DirectX/D3DDeviceResourcesBase.h
@@ -32,7 +32,7 @@ MIDL_INTERFACE("fb99fa64-d9cf-4e0e-9c75-90514797b01d")
ID3DDeviceResourcesBase : public IUnknown
{
public:
- virtual HRESULT Initialize(HWND hwnd) = 0;
+ virtual HRESULT Initialize(HWND hwnd, Size logical_size) = 0;
virtual HRESULT Present(bool vsync) = 0;
diff --git a/src/kiwano/render/DirectX/RendererImpl.cpp b/src/kiwano/render/DirectX/RendererImpl.cpp
index 571f9213..2e039848 100644
--- a/src/kiwano/render/DirectX/RendererImpl.cpp
+++ b/src/kiwano/render/DirectX/RendererImpl.cpp
@@ -55,17 +55,18 @@ void RendererImpl::MakeContextForWindow(WindowPtr window)
KGE_THROW_IF_FAILED(::CoInitialize(nullptr), "CoInitialize failed");
- HWND target_window = window->GetHandle();
- output_size_ = window->GetSize();
+ HWND target_window = window->GetHandle();
+ Resolution resolution = window->GetCurrentResolution();
+ HRESULT hr = target_window ? S_OK : E_FAIL;
- HRESULT hr = target_window ? S_OK : E_FAIL;
+ output_size_ = Size{ float(resolution.width), float(resolution.height) };
// Initialize Direct3D resources
if (SUCCEEDED(hr))
{
auto d3d_res = graphics::directx::GetD3DDeviceResources();
- hr = d3d_res->Initialize(target_window);
+ hr = d3d_res->Initialize(target_window, output_size_);
if (FAILED(hr))
{
d3d_res->DiscardResources();
diff --git a/src/kiwano/render/TextureCache.h b/src/kiwano/render/TextureCache.h
index 76caef85..4f82832c 100644
--- a/src/kiwano/render/TextureCache.h
+++ b/src/kiwano/render/TextureCache.h
@@ -74,11 +74,11 @@ public:
/// @brief 清空缓存
void Clear();
+ virtual ~TextureCache();
+
private:
TextureCache();
- virtual ~TextureCache();
-
private:
using TextureMap = UnorderedMap;
TextureMap texture_cache_;
diff --git a/src/kiwano/utils/Logger.h b/src/kiwano/utils/Logger.h
index b4177507..d35fdc35 100644
--- a/src/kiwano/utils/Logger.h
+++ b/src/kiwano/utils/Logger.h
@@ -126,11 +126,11 @@ public:
/// @note 此操作会重定向输出流到标准输出流
void ShowConsole(bool show);
+ ~Logger();
+
private:
Logger();
- ~Logger();
-
void Prepare(Level level, StringStream& sstream);
void Output(Level level, StringStream& sstream);
diff --git a/src/kiwano/utils/ResourceCache.h b/src/kiwano/utils/ResourceCache.h
index e6aaa9e8..58430b32 100644
--- a/src/kiwano/utils/ResourceCache.h
+++ b/src/kiwano/utils/ResourceCache.h
@@ -88,11 +88,11 @@ public:
/// @brief 清空所有资源
void Clear();
+ virtual ~ResourceCache();
+
private:
ResourceCache();
- virtual ~ResourceCache();
-
private:
UnorderedMap object_cache_;
};