2026-02-13 13:56:18 +08:00
|
|
|
|
// ============================================================================
|
|
|
|
|
|
// PlayScene.cpp - Push Box 游戏场景实现
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
2026-02-11 19:40:26 +08:00
|
|
|
|
#include "PlayScene.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include "StartScene.h"
|
|
|
|
|
|
#include "SuccessScene.h"
|
2026-02-13 13:56:18 +08:00
|
|
|
|
#include "audio_manager.h"
|
|
|
|
|
|
#include "storage.h"
|
2026-02-11 19:40:26 +08:00
|
|
|
|
#include <extra2d/extra2d.h>
|
2026-02-13 17:34:46 +08:00
|
|
|
|
#include <extra2d/utils/object_pool.h>
|
2026-02-11 19:40:26 +08:00
|
|
|
|
|
|
|
|
|
|
namespace pushbox {
|
|
|
|
|
|
|
2026-02-13 13:56:18 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 加载字体
|
|
|
|
|
|
* @param size 字体大小
|
|
|
|
|
|
*/
|
2026-02-11 19:40:26 +08:00
|
|
|
|
static extra2d::Ptr<extra2d::FontAtlas> loadFont(int size) {
|
2026-02-13 13:56:18 +08:00
|
|
|
|
auto &resources = extra2d::Application::instance().resources();
|
|
|
|
|
|
auto font = resources.loadFont("assets/font.ttf", size);
|
|
|
|
|
|
return font;
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-13 13:56:18 +08:00
|
|
|
|
PlayScene::PlayScene(int level) : BaseScene() {
|
|
|
|
|
|
auto &app = extra2d::Application::instance();
|
|
|
|
|
|
auto &resources = app.resources();
|
|
|
|
|
|
|
|
|
|
|
|
E2D_LOG_INFO("PlayScene: Loading textures...");
|
|
|
|
|
|
|
|
|
|
|
|
texWall_ = resources.loadTexture("assets/images/wall.gif");
|
|
|
|
|
|
texPoint_ = resources.loadTexture("assets/images/point.gif");
|
|
|
|
|
|
texFloor_ = resources.loadTexture("assets/images/floor.gif");
|
|
|
|
|
|
texBox_ = resources.loadTexture("assets/images/box.gif");
|
|
|
|
|
|
texBoxInPoint_ = resources.loadTexture("assets/images/boxinpoint.gif");
|
|
|
|
|
|
|
|
|
|
|
|
texMan_[1] = resources.loadTexture("assets/images/player/manup.gif");
|
|
|
|
|
|
texMan_[2] = resources.loadTexture("assets/images/player/mandown.gif");
|
|
|
|
|
|
texMan_[3] = resources.loadTexture("assets/images/player/manleft.gif");
|
|
|
|
|
|
texMan_[4] = resources.loadTexture("assets/images/player/manright.gif");
|
|
|
|
|
|
|
|
|
|
|
|
texManPush_[1] = resources.loadTexture("assets/images/player/manhandup.gif");
|
|
|
|
|
|
texManPush_[2] =
|
|
|
|
|
|
resources.loadTexture("assets/images/player/manhanddown.gif");
|
|
|
|
|
|
texManPush_[3] =
|
|
|
|
|
|
resources.loadTexture("assets/images/player/manhandleft.gif");
|
|
|
|
|
|
texManPush_[4] =
|
|
|
|
|
|
resources.loadTexture("assets/images/player/manhandright.gif");
|
|
|
|
|
|
|
|
|
|
|
|
font28_ = loadFont(28);
|
|
|
|
|
|
font20_ = loadFont(20);
|
|
|
|
|
|
|
|
|
|
|
|
// 使用游戏逻辑分辨率
|
|
|
|
|
|
float screenW = GAME_WIDTH;
|
|
|
|
|
|
float screenH = GAME_HEIGHT;
|
|
|
|
|
|
|
|
|
|
|
|
// 计算游戏区域居中偏移
|
|
|
|
|
|
float offsetX = (screenW - GAME_WIDTH) / 2.0f;
|
|
|
|
|
|
float offsetY = (screenH - GAME_HEIGHT) / 2.0f;
|
|
|
|
|
|
|
|
|
|
|
|
// 音效开关按钮(使用 Button 的切换模式)
|
|
|
|
|
|
auto soundOn = resources.loadTexture("assets/images/soundon.png");
|
|
|
|
|
|
auto soundOff = resources.loadTexture("assets/images/soundoff.png");
|
|
|
|
|
|
if (soundOn && soundOff) {
|
|
|
|
|
|
soundBtn_ = extra2d::Button::create();
|
|
|
|
|
|
soundBtn_->setToggleMode(true);
|
|
|
|
|
|
soundBtn_->setStateBackgroundImage(soundOff, soundOn);
|
|
|
|
|
|
soundBtn_->setOn(g_SoundOpen);
|
|
|
|
|
|
soundBtn_->setAnchor(0.0f, 0.0f);
|
|
|
|
|
|
soundBtn_->setPosition(offsetX + 50.0f, offsetY + 50.0f);
|
|
|
|
|
|
soundBtn_->setOnStateChange([](bool isOn) {
|
|
|
|
|
|
g_SoundOpen = isOn;
|
|
|
|
|
|
AudioManager::instance().setEnabled(isOn);
|
|
|
|
|
|
});
|
|
|
|
|
|
addChild(soundBtn_);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
levelText_ = extra2d::Text::create("", font28_);
|
|
|
|
|
|
levelText_->setPosition(offsetX + 520.0f, offsetY + 30.0f);
|
|
|
|
|
|
levelText_->setTextColor(extra2d::Colors::White);
|
|
|
|
|
|
addChild(levelText_);
|
|
|
|
|
|
|
|
|
|
|
|
stepText_ = extra2d::Text::create("", font20_);
|
|
|
|
|
|
stepText_->setPosition(offsetX + 520.0f, offsetY + 100.0f);
|
|
|
|
|
|
stepText_->setTextColor(extra2d::Colors::White);
|
|
|
|
|
|
addChild(stepText_);
|
|
|
|
|
|
|
|
|
|
|
|
bestText_ = extra2d::Text::create("", font20_);
|
|
|
|
|
|
bestText_->setPosition(offsetX + 520.0f, offsetY + 140.0f);
|
|
|
|
|
|
bestText_->setTextColor(extra2d::Colors::White);
|
|
|
|
|
|
addChild(bestText_);
|
|
|
|
|
|
|
|
|
|
|
|
// 创建菜单文本(使用颜色变化指示选中)
|
|
|
|
|
|
restartText_ = extra2d::Text::create("Y键重开", font20_);
|
|
|
|
|
|
restartText_->setPosition(offsetX + 520.0f, offsetY + 290.0f);
|
|
|
|
|
|
addChild(restartText_);
|
|
|
|
|
|
|
|
|
|
|
|
soundToggleText_ = extra2d::Text::create("X键切换音效", font20_);
|
|
|
|
|
|
soundToggleText_->setPosition(offsetX + 520.0f, offsetY + 330.0f);
|
|
|
|
|
|
addChild(soundToggleText_);
|
|
|
|
|
|
|
2026-02-13 17:34:46 +08:00
|
|
|
|
// 撤销提示(对象池使用示例)
|
|
|
|
|
|
undoText_ = extra2d::Text::create("Z键撤销", font20_);
|
|
|
|
|
|
undoText_->setPosition(offsetX + 520.0f, offsetY + 370.0f);
|
|
|
|
|
|
addChild(undoText_);
|
|
|
|
|
|
|
2026-02-13 13:56:18 +08:00
|
|
|
|
mapLayer_ = extra2d::makePtr<extra2d::Node>();
|
|
|
|
|
|
mapLayer_->setAnchor(0.0f, 0.0f);
|
|
|
|
|
|
mapLayer_->setPosition(0.0f, 0.0f);
|
|
|
|
|
|
addChild(mapLayer_);
|
|
|
|
|
|
|
|
|
|
|
|
setLevel(level);
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PlayScene::onEnter() {
|
2026-02-13 13:56:18 +08:00
|
|
|
|
BaseScene::onEnter();
|
|
|
|
|
|
if (soundBtn_) {
|
|
|
|
|
|
soundBtn_->setOn(g_SoundOpen);
|
|
|
|
|
|
}
|
|
|
|
|
|
updateMenuColors();
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-13 13:56:18 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 更新菜单颜色
|
|
|
|
|
|
*/
|
2026-02-11 19:40:26 +08:00
|
|
|
|
void PlayScene::updateMenuColors() {
|
2026-02-13 13:56:18 +08:00
|
|
|
|
// 选中的项用红色,未选中的用白色
|
|
|
|
|
|
if (restartText_) {
|
|
|
|
|
|
restartText_->setTextColor(menuIndex_ == 0 ? extra2d::Colors::Red
|
|
|
|
|
|
: extra2d::Colors::White);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (soundToggleText_) {
|
|
|
|
|
|
soundToggleText_->setTextColor(menuIndex_ == 1 ? extra2d::Colors::Red
|
|
|
|
|
|
: extra2d::Colors::White);
|
|
|
|
|
|
}
|
2026-02-13 17:34:46 +08:00
|
|
|
|
if (undoText_) {
|
|
|
|
|
|
undoText_->setTextColor(menuIndex_ == 2 ? extra2d::Colors::Red
|
|
|
|
|
|
: extra2d::Colors::White);
|
|
|
|
|
|
}
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PlayScene::onUpdate(float dt) {
|
2026-02-13 13:56:18 +08:00
|
|
|
|
BaseScene::onUpdate(dt);
|
|
|
|
|
|
|
|
|
|
|
|
auto &app = extra2d::Application::instance();
|
|
|
|
|
|
auto &input = app.input();
|
|
|
|
|
|
|
|
|
|
|
|
// B 键返回主菜单
|
|
|
|
|
|
if (input.isButtonPressed(extra2d::GamepadButton::B)) {
|
|
|
|
|
|
app.scenes().replaceScene(extra2d::makePtr<StartScene>(),
|
|
|
|
|
|
extra2d::TransitionType::Fade, 0.5f);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Y 键重开
|
|
|
|
|
|
if (input.isButtonPressed(extra2d::GamepadButton::Y)) {
|
|
|
|
|
|
setLevel(g_CurrentLevel);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// X键直接切换音效(备用,按钮也可点击切换)
|
|
|
|
|
|
if (input.isButtonPressed(extra2d::GamepadButton::X)) {
|
|
|
|
|
|
g_SoundOpen = !g_SoundOpen;
|
|
|
|
|
|
AudioManager::instance().setEnabled(g_SoundOpen);
|
|
|
|
|
|
if (soundBtn_) {
|
|
|
|
|
|
soundBtn_->setOn(g_SoundOpen);
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
2026-02-13 13:56:18 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-13 17:34:46 +08:00
|
|
|
|
// Z 键撤销(对象池使用示例)
|
|
|
|
|
|
if (input.isKeyPressed(extra2d::Key::Z)) {
|
|
|
|
|
|
undoMove();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-13 13:56:18 +08:00
|
|
|
|
// A 键执行选中的菜单项
|
|
|
|
|
|
if (input.isButtonPressed(extra2d::GamepadButton::A)) {
|
|
|
|
|
|
executeMenuItem();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 方向键移动
|
|
|
|
|
|
if (input.isButtonPressed(extra2d::GamepadButton::DPadUp)) {
|
|
|
|
|
|
move(0, -1, 1);
|
|
|
|
|
|
flush();
|
|
|
|
|
|
} else if (input.isButtonPressed(extra2d::GamepadButton::DPadDown)) {
|
|
|
|
|
|
move(0, 1, 2);
|
|
|
|
|
|
flush();
|
|
|
|
|
|
} else if (input.isButtonPressed(extra2d::GamepadButton::DPadLeft)) {
|
|
|
|
|
|
move(-1, 0, 3);
|
|
|
|
|
|
flush();
|
|
|
|
|
|
} else if (input.isButtonPressed(extra2d::GamepadButton::DPadRight)) {
|
|
|
|
|
|
move(1, 0, 4);
|
|
|
|
|
|
flush();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否通关
|
|
|
|
|
|
for (int i = 0; i < map_.width; i++) {
|
|
|
|
|
|
for (int j = 0; j < map_.height; j++) {
|
|
|
|
|
|
Piece p = map_.value[j][i];
|
|
|
|
|
|
if (p.type == TYPE::Box && p.isPoint == false) {
|
2026-02-11 19:40:26 +08:00
|
|
|
|
return;
|
2026-02-13 13:56:18 +08:00
|
|
|
|
}
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
2026-02-13 13:56:18 +08:00
|
|
|
|
}
|
2026-02-11 19:40:26 +08:00
|
|
|
|
|
2026-02-13 13:56:18 +08:00
|
|
|
|
gameOver();
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-13 13:56:18 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 执行选中的菜单项
|
|
|
|
|
|
*/
|
2026-02-11 19:40:26 +08:00
|
|
|
|
void PlayScene::executeMenuItem() {
|
2026-02-13 13:56:18 +08:00
|
|
|
|
switch (menuIndex_) {
|
|
|
|
|
|
case 0: // 重开
|
|
|
|
|
|
setLevel(g_CurrentLevel);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 1: // 切换音效
|
|
|
|
|
|
g_SoundOpen = !g_SoundOpen;
|
|
|
|
|
|
AudioManager::instance().setEnabled(g_SoundOpen);
|
|
|
|
|
|
if (soundBtn_) {
|
|
|
|
|
|
soundBtn_->setOn(g_SoundOpen);
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
2026-02-13 13:56:18 +08:00
|
|
|
|
break;
|
2026-02-13 17:34:46 +08:00
|
|
|
|
case 2: // 撤销
|
|
|
|
|
|
undoMove();
|
|
|
|
|
|
break;
|
2026-02-13 13:56:18 +08:00
|
|
|
|
}
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-13 13:56:18 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 刷新地图显示
|
|
|
|
|
|
*/
|
2026-02-11 19:40:26 +08:00
|
|
|
|
void PlayScene::flush() {
|
2026-02-13 13:56:18 +08:00
|
|
|
|
mapLayer_->removeAllChildren();
|
|
|
|
|
|
|
|
|
|
|
|
int tileW = texFloor_ ? texFloor_->getWidth() : 32;
|
|
|
|
|
|
int tileH = texFloor_ ? texFloor_->getHeight() : 32;
|
|
|
|
|
|
|
|
|
|
|
|
// 使用游戏逻辑分辨率
|
|
|
|
|
|
float gameWidth = GAME_WIDTH;
|
|
|
|
|
|
float gameHeight = GAME_HEIGHT;
|
|
|
|
|
|
float baseOffsetX = 0.0f;
|
|
|
|
|
|
float baseOffsetY = 0.0f;
|
|
|
|
|
|
|
|
|
|
|
|
// 在 12x12 网格中居中地图
|
|
|
|
|
|
float mapOffsetX = static_cast<float>((12.0f - map_.width) / 2.0f) * tileW;
|
|
|
|
|
|
float mapOffsetY = static_cast<float>((12.0f - map_.height) / 2.0f) * tileH;
|
|
|
|
|
|
|
|
|
|
|
|
float offsetX = baseOffsetX + mapOffsetX;
|
|
|
|
|
|
float offsetY = baseOffsetY + mapOffsetY;
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < map_.width; i++) {
|
|
|
|
|
|
for (int j = 0; j < map_.height; j++) {
|
|
|
|
|
|
Piece piece = map_.value[j][i];
|
|
|
|
|
|
|
|
|
|
|
|
extra2d::Ptr<extra2d::Texture> tex;
|
|
|
|
|
|
|
|
|
|
|
|
if (piece.type == TYPE::Wall) {
|
|
|
|
|
|
tex = texWall_;
|
|
|
|
|
|
} else if (piece.type == TYPE::Ground && piece.isPoint) {
|
|
|
|
|
|
tex = texPoint_;
|
|
|
|
|
|
} else if (piece.type == TYPE::Ground) {
|
|
|
|
|
|
tex = texFloor_;
|
|
|
|
|
|
} else if (piece.type == TYPE::Box && piece.isPoint) {
|
|
|
|
|
|
tex = texBoxInPoint_;
|
|
|
|
|
|
} else if (piece.type == TYPE::Box) {
|
|
|
|
|
|
tex = texBox_;
|
|
|
|
|
|
} else if (piece.type == TYPE::Man && g_Pushing) {
|
|
|
|
|
|
tex = texManPush_[g_Direct];
|
|
|
|
|
|
} else if (piece.type == TYPE::Man) {
|
|
|
|
|
|
tex = texMan_[g_Direct];
|
|
|
|
|
|
} else {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!tex) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto sprite = extra2d::Sprite::create(tex);
|
|
|
|
|
|
sprite->setAnchor(0.0f, 0.0f);
|
|
|
|
|
|
sprite->setPosition(offsetX + static_cast<float>(i * tileW),
|
|
|
|
|
|
offsetY + static_cast<float>(j * tileH));
|
|
|
|
|
|
mapLayer_->addChild(sprite);
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
2026-02-13 13:56:18 +08:00
|
|
|
|
}
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-13 13:56:18 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 设置关卡
|
|
|
|
|
|
* @param level 关卡编号
|
|
|
|
|
|
*/
|
2026-02-11 19:40:26 +08:00
|
|
|
|
void PlayScene::setLevel(int level) {
|
2026-02-13 13:56:18 +08:00
|
|
|
|
g_CurrentLevel = level;
|
|
|
|
|
|
saveCurrentLevel(g_CurrentLevel);
|
2026-02-13 17:34:46 +08:00
|
|
|
|
|
|
|
|
|
|
// 清空移动历史(智能指针自动回收到对象池)
|
|
|
|
|
|
while (!moveHistory_.empty()) {
|
|
|
|
|
|
moveHistory_.pop();
|
|
|
|
|
|
}
|
2026-02-11 19:40:26 +08:00
|
|
|
|
|
2026-02-13 13:56:18 +08:00
|
|
|
|
if (levelText_) {
|
|
|
|
|
|
levelText_->setText("第" + std::to_string(level) + "关");
|
|
|
|
|
|
}
|
2026-02-11 19:40:26 +08:00
|
|
|
|
|
2026-02-13 13:56:18 +08:00
|
|
|
|
setStep(0);
|
2026-02-11 19:40:26 +08:00
|
|
|
|
|
2026-02-13 13:56:18 +08:00
|
|
|
|
int bestStep = loadBestStep(level, 0);
|
|
|
|
|
|
if (bestText_) {
|
|
|
|
|
|
if (bestStep != 0) {
|
|
|
|
|
|
bestText_->setText("最佳" + std::to_string(bestStep) + "步");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
bestText_->setText("");
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
2026-02-13 13:56:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 深拷贝地图数据
|
|
|
|
|
|
Map &sourceMap = g_Maps[level - 1];
|
|
|
|
|
|
map_.width = sourceMap.width;
|
|
|
|
|
|
map_.height = sourceMap.height;
|
|
|
|
|
|
map_.roleX = sourceMap.roleX;
|
|
|
|
|
|
map_.roleY = sourceMap.roleY;
|
|
|
|
|
|
for (int i = 0; i < 12; i++) {
|
|
|
|
|
|
for (int j = 0; j < 12; j++) {
|
|
|
|
|
|
map_.value[i][j] = sourceMap.value[i][j];
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
2026-02-13 13:56:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
g_Direct = 2;
|
|
|
|
|
|
g_Pushing = false;
|
|
|
|
|
|
flush();
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-13 13:56:18 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 设置步数
|
|
|
|
|
|
* @param step 步数
|
|
|
|
|
|
*/
|
2026-02-11 19:40:26 +08:00
|
|
|
|
void PlayScene::setStep(int step) {
|
2026-02-13 13:56:18 +08:00
|
|
|
|
step_ = step;
|
|
|
|
|
|
if (stepText_) {
|
|
|
|
|
|
stepText_->setText("当前" + std::to_string(step) + "步");
|
|
|
|
|
|
}
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-13 13:56:18 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 移动玩家
|
|
|
|
|
|
* @param dx X方向偏移
|
|
|
|
|
|
* @param dy Y方向偏移
|
|
|
|
|
|
* @param direct 方向(1=上,2=下,3=左,4=右)
|
|
|
|
|
|
*/
|
2026-02-11 19:40:26 +08:00
|
|
|
|
void PlayScene::move(int dx, int dy, int direct) {
|
2026-02-13 13:56:18 +08:00
|
|
|
|
int targetX = dx + map_.roleX;
|
|
|
|
|
|
int targetY = dy + map_.roleY;
|
|
|
|
|
|
g_Direct = direct;
|
2026-02-11 19:40:26 +08:00
|
|
|
|
|
2026-02-13 13:56:18 +08:00
|
|
|
|
if (targetX < 0 || targetX >= map_.width || targetY < 0 ||
|
|
|
|
|
|
targetY >= map_.height) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (map_.value[targetY][targetX].type == TYPE::Wall) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-13 17:34:46 +08:00
|
|
|
|
// 使用对象池创建移动记录(自动管理内存)
|
|
|
|
|
|
auto record = E2D_MAKE_POOLED(MoveRecord, map_.roleX, map_.roleY, targetX, targetY, false);
|
|
|
|
|
|
|
2026-02-13 13:56:18 +08:00
|
|
|
|
if (map_.value[targetY][targetX].type == TYPE::Ground) {
|
|
|
|
|
|
g_Pushing = false;
|
|
|
|
|
|
map_.value[map_.roleY][map_.roleX].type = TYPE::Ground;
|
|
|
|
|
|
map_.value[targetY][targetX].type = TYPE::Man;
|
|
|
|
|
|
AudioManager::instance().playManMove();
|
|
|
|
|
|
} else if (map_.value[targetY][targetX].type == TYPE::Box) {
|
|
|
|
|
|
g_Pushing = true;
|
|
|
|
|
|
|
|
|
|
|
|
int boxX = 0;
|
|
|
|
|
|
int boxY = 0;
|
|
|
|
|
|
switch (g_Direct) {
|
|
|
|
|
|
case 1:
|
|
|
|
|
|
boxX = targetX;
|
|
|
|
|
|
boxY = targetY - 1;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 2:
|
|
|
|
|
|
boxX = targetX;
|
|
|
|
|
|
boxY = targetY + 1;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 3:
|
|
|
|
|
|
boxX = targetX - 1;
|
|
|
|
|
|
boxY = targetY;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 4:
|
|
|
|
|
|
boxX = targetX + 1;
|
|
|
|
|
|
boxY = targetY;
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
return;
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-13 13:56:18 +08:00
|
|
|
|
if (boxX < 0 || boxX >= map_.width || boxY < 0 || boxY >= map_.height) {
|
|
|
|
|
|
return;
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-13 13:56:18 +08:00
|
|
|
|
if (map_.value[boxY][boxX].type == TYPE::Wall ||
|
|
|
|
|
|
map_.value[boxY][boxX].type == TYPE::Box) {
|
|
|
|
|
|
return;
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-13 17:34:46 +08:00
|
|
|
|
// 记录箱子移动
|
|
|
|
|
|
record->pushedBox = true;
|
|
|
|
|
|
record->boxFromX = targetX;
|
|
|
|
|
|
record->boxFromY = targetY;
|
|
|
|
|
|
record->boxToX = boxX;
|
|
|
|
|
|
record->boxToY = boxY;
|
|
|
|
|
|
|
2026-02-13 13:56:18 +08:00
|
|
|
|
map_.value[boxY][boxX].type = TYPE::Box;
|
|
|
|
|
|
map_.value[targetY][targetX].type = TYPE::Man;
|
|
|
|
|
|
map_.value[map_.roleY][map_.roleX].type = TYPE::Ground;
|
2026-02-11 19:40:26 +08:00
|
|
|
|
|
2026-02-13 13:56:18 +08:00
|
|
|
|
AudioManager::instance().playBoxMove();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-02-11 19:40:26 +08:00
|
|
|
|
|
2026-02-13 17:34:46 +08:00
|
|
|
|
// 保存移动记录到历史栈
|
|
|
|
|
|
moveHistory_.push(record);
|
|
|
|
|
|
|
2026-02-13 13:56:18 +08:00
|
|
|
|
map_.roleX = targetX;
|
|
|
|
|
|
map_.roleY = targetY;
|
|
|
|
|
|
setStep(step_ + 1);
|
|
|
|
|
|
}
|
2026-02-11 19:40:26 +08:00
|
|
|
|
|
2026-02-13 13:56:18 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 游戏通关
|
|
|
|
|
|
*/
|
|
|
|
|
|
void PlayScene::gameOver() {
|
|
|
|
|
|
int bestStep = loadBestStep(g_CurrentLevel, 0);
|
|
|
|
|
|
if (bestStep == 0 || step_ < bestStep) {
|
|
|
|
|
|
saveBestStep(g_CurrentLevel, step_);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (g_CurrentLevel == MAX_LEVEL) {
|
|
|
|
|
|
extra2d::Application::instance().scenes().pushScene(
|
|
|
|
|
|
extra2d::makePtr<SuccessScene>(), extra2d::TransitionType::Fade, 0.5f);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setLevel(g_CurrentLevel + 1);
|
2026-02-11 19:40:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-13 17:34:46 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @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_);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-11 19:40:26 +08:00
|
|
|
|
} // namespace pushbox
|