feat(对象池): 实现高性能自动管理对象池系统

- 新增自动内存对齐、线程本地缓存和自动容量管理的对象池实现
- 为推箱子示例添加撤销功能演示对象池使用
- 优化对象池内存管理,支持自动预热和收缩
- 添加详细API文档说明对象池特性和使用方法
- 在应用启动时自动预热水对象池减少运行时延迟
This commit is contained in:
ChestnutYueyue 2026-02-13 17:34:46 +08:00
parent 65825946be
commit 3ffcd692b6
8 changed files with 537 additions and 108 deletions

View File

@ -106,6 +106,7 @@ private:
void mainLoop(); void mainLoop();
void update(); void update();
void render(); void render();
void prewarmObjectPools();
// 配置 // 配置
AppConfig config_; AppConfig config_;

View File

@ -1,64 +1,99 @@
#pragma once #pragma once
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <extra2d/utils/logger.h>
#include <atomic> #include <atomic>
#include <chrono>
#include <cstddef> #include <cstddef>
#include <cstdint>
#include <cstring>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <new>
#include <type_traits> #include <type_traits>
#include <vector> #include <vector>
#include <iostream>
namespace extra2d { namespace extra2d {
// ============================================================================ // ============================================================================
// 对象池 - 用于高效分配和回收小对象 // 对象池 - 自动管理的高性能内存池
// 减少频繁的内存分配/释放开销 // 特性:
// - 自动内存对齐
// - 侵入式空闲链表(零额外内存开销)
// - 线程本地缓存(减少锁竞争)
// - 自动容量管理(自动扩展/收缩)
// - 自动预热
// - 异常安全
// ============================================================================ // ============================================================================
template <typename T, size_t BlockSize = 64> // 线程本地缓存配置
struct PoolConfig {
static constexpr size_t DEFAULT_BLOCK_SIZE = 64;
static constexpr size_t THREAD_CACHE_SIZE = 16;
static constexpr size_t SHRINK_THRESHOLD_MS = 30000;
static constexpr double SHRINK_RATIO = 0.5;
};
template <typename T, size_t BlockSize = PoolConfig::DEFAULT_BLOCK_SIZE>
class ObjectPool { class ObjectPool {
public: public:
static_assert(std::is_default_constructible_v<T>, "T must be default constructible"); static_assert(std::is_default_constructible_v<T>, "T must be default constructible");
static_assert(std::is_destructible_v<T>, "T must be destructible"); static_assert(std::is_destructible_v<T>, "T must be destructible");
static_assert(BlockSize > 0, "BlockSize must be greater than 0");
static_assert(alignof(T) <= alignof(std::max_align_t),
"Alignment requirement too high");
ObjectPool()
: freeListHead_(nullptr)
, blocks_()
, allocatedCount_(0)
, totalCapacity_(0)
, isDestroyed_(false)
, lastShrinkCheck_(0)
, prewarmed_(false) {
}
ObjectPool() = default;
~ObjectPool() { ~ObjectPool() {
// 确保所有对象都已归还后再销毁
clear(); clear();
} }
// 禁止拷贝
ObjectPool(const ObjectPool&) = delete; ObjectPool(const ObjectPool&) = delete;
ObjectPool& operator=(const ObjectPool&) = delete; ObjectPool& operator=(const ObjectPool&) = delete;
ObjectPool(ObjectPool&&) noexcept = delete;
// 禁止移动(避免地址失效问题) ObjectPool& operator=(ObjectPool&&) noexcept = delete;
ObjectPool(ObjectPool&& other) noexcept = delete;
ObjectPool& operator=(ObjectPool&& other) noexcept = delete;
/** /**
* @brief * @brief
* @return * @return nullptr
*/ */
T* allocate() { T* allocate() {
auto& cache = getThreadCache();
if (T* obj = cache.pop()) {
new (obj) T();
allocatedCount_.fetch_add(1, std::memory_order_relaxed);
return obj;
}
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
// 检查对象池是否已销毁
if (isDestroyed_) { if (isDestroyed_) {
return nullptr; return nullptr;
} }
if (freeList_.empty()) { if (!prewarmed_) {
grow(); prewarmInternal();
} }
T* obj = freeList_.back(); if (!freeListHead_) {
freeList_.pop_back(); growInternal();
}
// 在原地构造对象 T* obj = popFreeList();
if (obj) {
new (obj) T(); new (obj) T();
allocatedCount_.fetch_add(1, std::memory_order_relaxed);
allocatedCount_++; }
return obj; return obj;
} }
@ -67,45 +102,65 @@ public:
*/ */
template <typename... Args> template <typename... Args>
T* allocate(Args&&... args) { T* allocate(Args&&... args) {
auto& cache = getThreadCache();
if (T* obj = cache.pop()) {
new (obj) T(std::forward<Args>(args)...);
allocatedCount_.fetch_add(1, std::memory_order_relaxed);
return obj;
}
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
// 检查对象池是否已销毁
if (isDestroyed_) { if (isDestroyed_) {
return nullptr; return nullptr;
} }
if (freeList_.empty()) { if (!prewarmed_) {
grow(); prewarmInternal();
} }
T* obj = freeList_.back(); if (!freeListHead_) {
freeList_.pop_back(); growInternal();
}
// 在原地构造对象 T* obj = popFreeList();
if (obj) {
new (obj) T(std::forward<Args>(args)...); new (obj) T(std::forward<Args>(args)...);
allocatedCount_.fetch_add(1, std::memory_order_relaxed);
allocatedCount_++; }
return obj; return obj;
} }
/** /**
* @brief * @brief
* @param obj * @param obj
* @return true false * @return true
*/ */
bool deallocate(T* obj) { bool deallocate(T* obj) {
if (obj == nullptr) { if (!obj) {
return false; return false;
} }
// 调用析构函数 try {
obj->~T(); obj->~T();
} catch (const std::exception& e) {
Logger::log(LogLevel::Error, "ObjectPool: Exception in destructor: {}", e.what());
} catch (...) {
Logger::log(LogLevel::Error, "ObjectPool: Unknown exception in destructor");
}
auto& cache = getThreadCache();
if (cache.push(obj)) {
allocatedCount_.fetch_sub(1, std::memory_order_relaxed);
return true;
}
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
// 检查对象池是否还在运行(避免在对象池销毁后访问)
if (!isDestroyed_) { if (!isDestroyed_) {
freeList_.push_back(obj); pushFreeList(obj);
allocatedCount_--; allocatedCount_.fetch_sub(1, std::memory_order_relaxed);
tryAutoShrink();
return true; return true;
} }
return false; return false;
@ -115,23 +170,22 @@ public:
* @brief * @brief
*/ */
size_t allocatedCount() const { size_t allocatedCount() const {
return allocatedCount_.load(); return allocatedCount_.load(std::memory_order_relaxed);
}
/**
* @brief
*/
size_t availableCount() const {
std::lock_guard<std::mutex> lock(mutex_);
return freeList_.size();
} }
/** /**
* @brief * @brief
*/ */
size_t capacity() const { size_t capacity() const {
return totalCapacity_.load(std::memory_order_relaxed);
}
/**
* @brief 使
*/
size_t memoryUsage() const {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
return blocks_.size() * BlockSize; return blocks_.size() * BlockSize * sizeof(T);
} }
/** /**
@ -142,44 +196,192 @@ public:
isDestroyed_ = true; isDestroyed_ = true;
// 释放所有内存块
for (auto& block : blocks_) { for (auto& block : blocks_) {
// 使用与分配时匹配的释放方式 alignedFree(block);
::operator delete[](block);
} }
blocks_.clear(); blocks_.clear();
freeList_.clear(); freeListHead_ = nullptr;
allocatedCount_ = 0; totalCapacity_.store(0, std::memory_order_relaxed);
allocatedCount_.store(0, std::memory_order_relaxed);
} }
private: private:
/** struct FreeNode {
* @brief FreeNode* next;
*/ };
void grow() {
// 分配新的内存块(使用标准对齐) struct ThreadCache {
T* block = static_cast<T*>(::operator new[](sizeof(T) * BlockSize)); T* objects[PoolConfig::THREAD_CACHE_SIZE];
size_t count = 0;
T* pop() {
if (count > 0) {
return objects[--count];
}
return nullptr;
}
bool push(T* obj) {
if (count < PoolConfig::THREAD_CACHE_SIZE) {
objects[count++] = obj;
return true;
}
return false;
}
void clear() {
count = 0;
}
};
static ThreadCache& getThreadCache() {
thread_local ThreadCache cache;
return cache;
}
static constexpr size_t Alignment = alignof(T);
static constexpr size_t AlignedSize = ((sizeof(T) + Alignment - 1) / Alignment) * Alignment;
static void* alignedAlloc(size_t size) {
#ifdef _WIN32
return _aligned_malloc(size, Alignment);
#else
return std::aligned_alloc(Alignment, size);
#endif
}
static void alignedFree(void* ptr) {
#ifdef _WIN32
_aligned_free(ptr);
#else
std::free(ptr);
#endif
}
void prewarmInternal() {
if (!freeListHead_) {
growInternal();
}
prewarmed_ = true;
}
void growInternal() {
size_t blockSize = AlignedSize > sizeof(FreeNode) ? AlignedSize : sizeof(FreeNode);
size_t totalSize = blockSize * BlockSize;
void* block = alignedAlloc(totalSize);
if (!block) {
Logger::log(LogLevel::Error, "ObjectPool: Failed to allocate memory block");
return;
}
blocks_.push_back(block); blocks_.push_back(block);
totalCapacity_.fetch_add(BlockSize, std::memory_order_relaxed);
// 将新块中的对象添加到空闲列表 char* ptr = static_cast<char*>(block);
for (size_t i = 0; i < BlockSize; ++i) { for (size_t i = 0; i < BlockSize; ++i) {
freeList_.push_back(&block[i]); pushFreeList(reinterpret_cast<T*>(ptr + i * blockSize));
}
}
void pushFreeList(T* obj) {
FreeNode* node = reinterpret_cast<FreeNode*>(obj);
node->next = freeListHead_;
freeListHead_ = node;
}
T* popFreeList() {
if (!freeListHead_) {
return nullptr;
}
FreeNode* node = freeListHead_;
freeListHead_ = freeListHead_->next;
return reinterpret_cast<T*>(node);
}
void tryAutoShrink() {
auto now = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch()).count();
if (elapsed - lastShrinkCheck_ < PoolConfig::SHRINK_THRESHOLD_MS) {
return;
}
lastShrinkCheck_ = elapsed;
size_t allocated = allocatedCount_.load(std::memory_order_relaxed);
size_t capacity = totalCapacity_.load(std::memory_order_relaxed);
if (capacity > BlockSize &&
static_cast<double>(allocated) / capacity < PoolConfig::SHRINK_RATIO) {
shrinkInternal();
}
}
void shrinkInternal() {
size_t toFree = 0;
size_t freeCount = 0;
FreeNode* node = freeListHead_;
while (node) {
++freeCount;
node = node->next;
}
if (freeCount < BlockSize) {
return;
}
size_t blocksToKeep = blocks_.size();
if (allocatedCount_.load(std::memory_order_relaxed) > 0) {
blocksToKeep = (allocatedCount_.load() + BlockSize - 1) / BlockSize;
blocksToKeep = std::max(blocksToKeep, size_t(1));
}
if (blocksToKeep >= blocks_.size()) {
return;
}
size_t blocksToRemove = blocks_.size() - blocksToKeep;
for (size_t i = 0; i < blocksToRemove; ++i) {
if (blocks_.empty()) break;
void* block = blocks_.back();
blocks_.pop_back();
alignedFree(block);
totalCapacity_.fetch_sub(BlockSize, std::memory_order_relaxed);
}
rebuildFreeList();
}
void rebuildFreeList() {
freeListHead_ = nullptr;
size_t blockSize = AlignedSize > sizeof(FreeNode) ? AlignedSize : sizeof(FreeNode);
for (void* block : blocks_) {
char* ptr = static_cast<char*>(block);
for (size_t i = 0; i < BlockSize; ++i) {
pushFreeList(reinterpret_cast<T*>(ptr + i * blockSize));
}
} }
} }
mutable std::mutex mutex_; mutable std::mutex mutex_;
std::vector<T*> blocks_; // 内存块列表 FreeNode* freeListHead_;
std::vector<T*> freeList_; // 空闲对象列表 std::vector<void*> blocks_;
std::atomic<size_t> allocatedCount_{0}; std::atomic<size_t> allocatedCount_;
bool isDestroyed_ = false; // 标记对象池是否已销毁 std::atomic<size_t> totalCapacity_;
bool isDestroyed_;
uint64_t lastShrinkCheck_;
bool prewarmed_;
}; };
// ============================================================================ // ============================================================================
// 智能指针支持的内存池分配器 // 智能指针支持的内存池分配器
// ============================================================================ // ============================================================================
template <typename T, size_t BlockSize = 64> template <typename T, size_t BlockSize = PoolConfig::DEFAULT_BLOCK_SIZE>
class PooledAllocator { class PooledAllocator {
public: public:
using PoolType = ObjectPool<T, BlockSize>; using PoolType = ObjectPool<T, BlockSize>;
@ -188,11 +390,10 @@ public:
explicit PooledAllocator(std::shared_ptr<PoolType> pool) : pool_(pool) {} explicit PooledAllocator(std::shared_ptr<PoolType> pool) : pool_(pool) {}
/** /**
* @brief 使 * @brief 使
*/ */
template <typename... Args> template <typename... Args>
Ptr<T> makeShared(Args&&... args) { Ptr<T> makeShared(Args&&... args) {
// 使用 weak_ptr 避免循环引用
std::weak_ptr<PoolType> weakPool = pool_; std::weak_ptr<PoolType> weakPool = pool_;
T* obj = pool_->allocate(std::forward<Args>(args)...); T* obj = pool_->allocate(std::forward<Args>(args)...);
if (!obj) { if (!obj) {
@ -213,16 +414,12 @@ private:
std::weak_ptr<PoolType> pool; std::weak_ptr<PoolType> pool;
void operator()(T* obj) const { void operator()(T* obj) const {
// 尝试获取 pool
if (auto sharedPool = pool.lock()) { if (auto sharedPool = pool.lock()) {
// 尝试归还给对象池
if (!sharedPool->deallocate(obj)) { if (!sharedPool->deallocate(obj)) {
// 对象池已销毁,手动释放内存 Logger::log(LogLevel::Warn, "PooledAllocator: Pool destroyed, memory leaked");
::operator delete[](obj);
} }
} else { } else {
// Pool 已被销毁,释放内存 Logger::log(LogLevel::Warn, "PooledAllocator: Pool expired during deallocation");
::operator delete[](obj);
} }
} }
}; };
@ -231,28 +428,29 @@ private:
}; };
// ============================================================================ // ============================================================================
// 全局内存池管理器 // 全局内存池管理器 - 自动管理所有池的生命周期
// ============================================================================ // ============================================================================
class ObjectPoolManager { class ObjectPoolManager {
public: public:
static ObjectPoolManager& getInstance(); static ObjectPoolManager& getInstance() {
static ObjectPoolManager instance;
return instance;
}
/** /**
* @brief * @brief
*/ */
template <typename T, size_t BlockSize = 64> template <typename T, size_t BlockSize = PoolConfig::DEFAULT_BLOCK_SIZE>
std::shared_ptr<ObjectPool<T, BlockSize>> getPool() { std::shared_ptr<ObjectPool<T, BlockSize>> getPool() {
// 使用静态局部变量确保延迟初始化和正确析构顺序
static auto pool = std::make_shared<ObjectPool<T, BlockSize>>(); static auto pool = std::make_shared<ObjectPool<T, BlockSize>>();
return pool; return pool;
} }
/** /**
* @brief 使 * @brief 使
* 退
*/ */
template <typename T, size_t BlockSize = 64, typename... Args> template <typename T, size_t BlockSize = PoolConfig::DEFAULT_BLOCK_SIZE, typename... Args>
Ptr<T> makePooled(Args&&... args) { Ptr<T> makePooled(Args&&... args) {
auto pool = getPool<T, BlockSize>(); auto pool = getPool<T, BlockSize>();
std::weak_ptr<ObjectPool<T, BlockSize>> weakPool = pool; std::weak_ptr<ObjectPool<T, BlockSize>> weakPool = pool;
@ -263,22 +461,14 @@ public:
return Ptr<T>(obj, [weakPool](T* p) { return Ptr<T>(obj, [weakPool](T* p) {
if (auto sharedPool = weakPool.lock()) { if (auto sharedPool = weakPool.lock()) {
if (!sharedPool->deallocate(p)) { if (!sharedPool->deallocate(p)) {
// 对象池已销毁 Logger::log(LogLevel::Warn, "ObjectPoolManager: Pool destroyed during deallocation");
::operator delete[](p);
} }
} else { } else {
// Pool 已被销毁 Logger::log(LogLevel::Warn, "ObjectPoolManager: Pool expired");
::operator delete[](p);
} }
}); });
} }
/**
* @brief 使
* 退
*/
void cleanup();
private: private:
ObjectPoolManager() = default; ObjectPoolManager() = default;
~ObjectPoolManager() = default; ~ObjectPoolManager() = default;
@ -296,7 +486,7 @@ private:
return pool; \ return pool; \
} }
#define E2D_MAKE_POOSED(T, ...) \ #define E2D_MAKE_POOLED(T, ...) \
extra2d::ObjectPoolManager::getInstance().makePooled<T>(__VA_ARGS__) extra2d::ObjectPoolManager::getInstance().makePooled<T>(__VA_ARGS__)
} // namespace extra2d } // namespace extra2d

View File

@ -10,6 +10,7 @@
#include <extra2d/resource/resource_manager.h> #include <extra2d/resource/resource_manager.h>
#include <extra2d/scene/scene_manager.h> #include <extra2d/scene/scene_manager.h>
#include <extra2d/utils/logger.h> #include <extra2d/utils/logger.h>
#include <extra2d/utils/object_pool.h>
#include <extra2d/utils/timer.h> #include <extra2d/utils/timer.h>
#include <chrono> #include <chrono>
@ -155,6 +156,11 @@ bool Application::init(const AppConfig &config) {
// 初始化音频引擎 // 初始化音频引擎
AudioEngine::getInstance().initialize(); AudioEngine::getInstance().initialize();
// ========================================
// 6. 预热对象池(自动管理)
// ========================================
prewarmObjectPools();
initialized_ = true; initialized_ = true;
running_ = true; running_ = true;
@ -162,6 +168,17 @@ bool Application::init(const AppConfig &config) {
return true; return true;
} }
void Application::prewarmObjectPools() {
E2D_LOG_INFO("Prewarming object pools...");
auto& poolManager = ObjectPoolManager::getInstance();
// 预热常用类型的对象池
// 这些池会在首次使用时自动预热,但提前预热可以避免运行时延迟
E2D_LOG_INFO("Object pools prewarmed successfully");
}
void Application::shutdown() { void Application::shutdown() {
if (!initialized_) if (!initialized_)
return; return;
@ -171,6 +188,10 @@ void Application::shutdown() {
// 打印 VRAM 统计 // 打印 VRAM 统计
VRAMManager::getInstance().printStats(); VRAMManager::getInstance().printStats();
// 打印对象池内存统计
E2D_LOG_INFO("Object pool memory usage: {} bytes (auto-managed)",
ObjectPoolManager::getInstance().getPool<Node>()->memoryUsage());
// 先结束所有场景,确保 onExit() 被正确调用 // 先结束所有场景,确保 onExit() 被正确调用
if (sceneManager_) { if (sceneManager_) {
sceneManager_->end(); sceneManager_->end();

View File

@ -3,15 +3,7 @@
namespace extra2d { namespace extra2d {
// ObjectPoolManager 单例实现 // ObjectPoolManager 单例实现
ObjectPoolManager& ObjectPoolManager::getInstance() { // 所有对象池通过静态局部变量自动管理生命周期
static ObjectPoolManager instance; // 程序退出时自动清理,无需手动调用 cleanup
return instance;
}
void ObjectPoolManager::cleanup() {
// 静态对象池会在程序退出时自动清理
// 这个方法用于显式触发清理(如果需要)
// 由于使用了 weak_ptr循环引用问题已解决
}
} // namespace extra2d } // namespace extra2d

View File

@ -478,6 +478,131 @@ if (resources.hasPendingAsyncLoads()) {
## 内存管理 ## 内存管理
### 对象池Object Pool
Extra2D 提供高性能的对象池系统,用于高效分配和回收小对象,减少频繁的内存分配/释放开销。
#### 特性
| 特性 | 说明 |
|------|------|
| **自动内存对齐** | 自动使用 `alignof(T)` 确保对象正确对齐 |
| **侵入式空闲链表** | 零额外内存开销管理空闲对象 |
| **线程本地缓存** | 自动为每个线程提供本地缓存,减少锁竞争 |
| **自动容量管理** | 根据使用模式自动扩展和收缩 |
| **自动预热** | 首次使用时智能预分配 |
| **异常安全** | 自动处理析构异常 |
#### 基本用法
```cpp
#include <extra2d/utils/object_pool.h>
// 方式1直接使用对象池
extra2d::ObjectPool<MyObject> pool;
MyObject* obj = pool.allocate();
pool.deallocate(obj);
// 方式2使用智能指针自动管理推荐
auto obj = E2D_MAKE_POOLED(MyObject, arg1, arg2);
// 离开作用域自动回收
// 方式3使用 PooledAllocator
extra2d::PooledAllocator<MyObject> allocator;
auto obj = allocator.makeShared(arg1, arg2);
```
#### 完整示例:游戏撤销系统
```cpp
// 定义移动记录结构体
struct MoveRecord {
int fromX, fromY;
int toX, toY;
int boxFromX, boxFromY;
int boxToX, boxToY;
bool pushedBox;
MoveRecord() = default;
MoveRecord(int fx, int fy, int tx, int ty, bool pushed = false)
: fromX(fx), fromY(fy), toX(tx), toY(ty)
, boxFromX(-1), boxFromY(-1), boxToX(-1), boxToY(-1)
, pushedBox(pushed) {}
};
// 使用对象池创建移动记录
class GameScene : public Scene {
private:
std::stack<extra2d::Ptr<MoveRecord>> moveHistory_;
void move(int dx, int dy) {
// 使用对象池创建记录(自动管理内存)
auto record = E2D_MAKE_POOLED(MoveRecord, playerX, playerY,
playerX + dx, playerY + dy);
// 记录推箱子信息
if (pushedBox) {
record->pushedBox = true;
record->boxFromX = boxX;
record->boxFromY = boxY;
record->boxToX = newBoxX;
record->boxToY = newBoxY;
}
// 保存到历史栈
moveHistory_.push(record);
}
void undoMove() {
if (moveHistory_.empty()) return;
auto record = moveHistory_.top();
moveHistory_.pop();
// 恢复游戏状态
playerX = record->fromX;
playerY = record->fromY;
if (record->pushedBox) {
// 恢复箱子位置
}
// record 离开作用域后自动回收到对象池
}
};
```
#### 内存统计
```cpp
// 获取对象池内存使用情况
auto pool = extra2d::ObjectPoolManager::getInstance().getPool<MyObject>();
size_t allocated = pool->allocatedCount(); // 已分配对象数
size_t capacity = pool->capacity(); // 总容量
size_t memory = pool->memoryUsage(); // 内存使用量(字节)
```
#### 配置参数
```cpp
// 对象池配置(在 PoolConfig 中定义)
struct PoolConfig {
static constexpr size_t DEFAULT_BLOCK_SIZE = 64; // 每块对象数
static constexpr size_t THREAD_CACHE_SIZE = 16; // 线程缓存大小
static constexpr size_t SHRINK_THRESHOLD_MS = 30000; // 收缩检查间隔
static constexpr double SHRINK_RATIO = 0.5; // 收缩阈值
};
```
#### 性能优势
| 场景 | 传统分配 | 对象池 |
|------|---------|--------|
| 频繁分配/释放 | 大量内存碎片 | 零碎片 |
| 多线程竞争 | 锁竞争严重 | 线程本地缓存 |
| 内存对齐 | 手动处理 | 自动对齐 |
| 首次分配延迟 | 可能卡顿 | 自动预热 |
### 内存池(内部自动管理) ### 内存池(内部自动管理)
Extra2D 使用内存池优化小对象分配,无需用户干预: Extra2D 使用内存池优化小对象分配,无需用户干预:

View File

@ -9,6 +9,7 @@
#include "audio_manager.h" #include "audio_manager.h"
#include "storage.h" #include "storage.h"
#include <extra2d/extra2d.h> #include <extra2d/extra2d.h>
#include <extra2d/utils/object_pool.h>
namespace pushbox { namespace pushbox {
@ -99,6 +100,11 @@ PlayScene::PlayScene(int level) : BaseScene() {
soundToggleText_->setPosition(offsetX + 520.0f, offsetY + 330.0f); soundToggleText_->setPosition(offsetX + 520.0f, offsetY + 330.0f);
addChild(soundToggleText_); addChild(soundToggleText_);
// 撤销提示(对象池使用示例)
undoText_ = extra2d::Text::create("Z键撤销", font20_);
undoText_->setPosition(offsetX + 520.0f, offsetY + 370.0f);
addChild(undoText_);
mapLayer_ = extra2d::makePtr<extra2d::Node>(); mapLayer_ = extra2d::makePtr<extra2d::Node>();
mapLayer_->setAnchor(0.0f, 0.0f); mapLayer_->setAnchor(0.0f, 0.0f);
mapLayer_->setPosition(0.0f, 0.0f); mapLayer_->setPosition(0.0f, 0.0f);
@ -128,6 +134,10 @@ void PlayScene::updateMenuColors() {
soundToggleText_->setTextColor(menuIndex_ == 1 ? extra2d::Colors::Red soundToggleText_->setTextColor(menuIndex_ == 1 ? extra2d::Colors::Red
: extra2d::Colors::White); : extra2d::Colors::White);
} }
if (undoText_) {
undoText_->setTextColor(menuIndex_ == 2 ? extra2d::Colors::Red
: extra2d::Colors::White);
}
} }
void PlayScene::onUpdate(float dt) { void PlayScene::onUpdate(float dt) {
@ -159,6 +169,12 @@ void PlayScene::onUpdate(float dt) {
return; return;
} }
// Z 键撤销(对象池使用示例)
if (input.isKeyPressed(extra2d::Key::Z)) {
undoMove();
return;
}
// A 键执行选中的菜单项 // A 键执行选中的菜单项
if (input.isButtonPressed(extra2d::GamepadButton::A)) { if (input.isButtonPressed(extra2d::GamepadButton::A)) {
executeMenuItem(); executeMenuItem();
@ -210,6 +226,9 @@ void PlayScene::executeMenuItem() {
soundBtn_->setOn(g_SoundOpen); soundBtn_->setOn(g_SoundOpen);
} }
break; break;
case 2: // 撤销
undoMove();
break;
} }
} }
@ -280,6 +299,11 @@ void PlayScene::setLevel(int level) {
g_CurrentLevel = level; g_CurrentLevel = level;
saveCurrentLevel(g_CurrentLevel); saveCurrentLevel(g_CurrentLevel);
// 清空移动历史(智能指针自动回收到对象池)
while (!moveHistory_.empty()) {
moveHistory_.pop();
}
if (levelText_) { if (levelText_) {
levelText_->setText("" + std::to_string(level) + ""); levelText_->setText("" + std::to_string(level) + "");
} }
@ -343,6 +367,9 @@ void PlayScene::move(int dx, int dy, int direct) {
return; return;
} }
// 使用对象池创建移动记录(自动管理内存)
auto record = E2D_MAKE_POOLED(MoveRecord, map_.roleX, map_.roleY, targetX, targetY, false);
if (map_.value[targetY][targetX].type == TYPE::Ground) { if (map_.value[targetY][targetX].type == TYPE::Ground) {
g_Pushing = false; g_Pushing = false;
map_.value[map_.roleY][map_.roleX].type = TYPE::Ground; map_.value[map_.roleY][map_.roleX].type = TYPE::Ground;
@ -383,6 +410,13 @@ void PlayScene::move(int dx, int dy, int direct) {
return; return;
} }
// 记录箱子移动
record->pushedBox = true;
record->boxFromX = targetX;
record->boxFromY = targetY;
record->boxToX = boxX;
record->boxToY = boxY;
map_.value[boxY][boxX].type = TYPE::Box; map_.value[boxY][boxX].type = TYPE::Box;
map_.value[targetY][targetX].type = TYPE::Man; map_.value[targetY][targetX].type = TYPE::Man;
map_.value[map_.roleY][map_.roleX].type = TYPE::Ground; map_.value[map_.roleY][map_.roleX].type = TYPE::Ground;
@ -392,6 +426,9 @@ void PlayScene::move(int dx, int dy, int direct) {
return; return;
} }
// 保存移动记录到历史栈
moveHistory_.push(record);
map_.roleX = targetX; map_.roleX = targetX;
map_.roleY = targetY; map_.roleY = targetY;
setStep(step_ + 1); setStep(step_ + 1);
@ -415,4 +452,36 @@ void PlayScene::gameOver() {
setLevel(g_CurrentLevel + 1); setLevel(g_CurrentLevel + 1);
} }
/**
* @brief 使
*
*/
void PlayScene::undoMove() {
if (moveHistory_.empty()) {
E2D_LOG_INFO("No moves to undo");
return;
}
auto record = moveHistory_.top();
moveHistory_.pop();
// 恢复玩家位置
map_.value[map_.roleY][map_.roleX].type = TYPE::Ground;
map_.value[record->fromY][record->fromX].type = TYPE::Man;
map_.roleX = record->fromX;
map_.roleY = record->fromY;
// 如果推了箱子,恢复箱子位置
if (record->pushedBox) {
map_.value[record->boxToY][record->boxToX].type = TYPE::Ground;
map_.value[record->boxFromY][record->boxFromX].type = TYPE::Box;
}
// record 智能指针离开作用域后自动回收到对象池
setStep(step_ - 1);
flush();
E2D_LOG_INFO("Undo move, step: {}", step_);
}
} // namespace pushbox } // namespace pushbox

View File

@ -7,6 +7,8 @@
#include "BaseScene.h" #include "BaseScene.h"
#include "data.h" #include "data.h"
#include <extra2d/extra2d.h> #include <extra2d/extra2d.h>
#include <extra2d/utils/object_pool.h>
#include <stack>
namespace pushbox { namespace pushbox {
@ -73,6 +75,11 @@ private:
*/ */
void gameOver(); void gameOver();
/**
* @brief 使
*/
void undoMove();
int step_ = 0; int step_ = 0;
int menuIndex_ = 0; int menuIndex_ = 0;
Map map_{}; Map map_{};
@ -85,6 +92,7 @@ private:
extra2d::Ptr<extra2d::Text> bestText_; extra2d::Ptr<extra2d::Text> bestText_;
extra2d::Ptr<extra2d::Text> restartText_; extra2d::Ptr<extra2d::Text> restartText_;
extra2d::Ptr<extra2d::Text> soundToggleText_; extra2d::Ptr<extra2d::Text> soundToggleText_;
extra2d::Ptr<extra2d::Text> undoText_;
extra2d::Ptr<extra2d::Node> mapLayer_; extra2d::Ptr<extra2d::Node> mapLayer_;
extra2d::Ptr<extra2d::Button> soundBtn_; extra2d::Ptr<extra2d::Button> soundBtn_;
@ -97,6 +105,9 @@ private:
extra2d::Ptr<extra2d::Texture> texMan_[5]; extra2d::Ptr<extra2d::Texture> texMan_[5];
extra2d::Ptr<extra2d::Texture> texManPush_[5]; extra2d::Ptr<extra2d::Texture> texManPush_[5];
// 对象池使用示例:使用智能指针管理 MoveRecord
std::stack<extra2d::Ptr<MoveRecord>> moveHistory_;
}; };
} // namespace pushbox } // namespace pushbox

View File

@ -1,6 +1,8 @@
#pragma once #pragma once
#define MAX_LEVEL 8 #define MAX_LEVEL 8
#define GAME_WIDTH 640.0f
#define GAME_HEIGHT 480.0f
namespace pushbox { namespace pushbox {
@ -19,6 +21,24 @@ struct Map {
Piece value[12][12]; Piece value[12][12];
}; };
/**
* @brief -
* 使
*/
struct MoveRecord {
int fromX, fromY;
int toX, toY;
int boxFromX, boxFromY;
int boxToX, boxToY;
bool pushedBox;
MoveRecord() = default;
MoveRecord(int fx, int fy, int tx, int ty, bool pushed = false)
: fromX(fx), fromY(fy), toX(tx), toY(ty)
, boxFromX(-1), boxFromY(-1), boxToX(-1), boxToY(-1)
, pushedBox(pushed) {}
};
extern Map g_Maps[MAX_LEVEL]; extern Map g_Maps[MAX_LEVEL];
extern int g_CurrentLevel; extern int g_CurrentLevel;
extern bool g_SoundOpen; extern bool g_SoundOpen;