Extra2D/examples/push_box/PlayScene.cpp

387 lines
12 KiB
C++
Raw Normal View History

#include "PlayScene.h"
#include "audio_manager.h"
#include "storage.h"
#include "StartScene.h"
#include "SuccessScene.h"
#include <extra2d/extra2d.h>
namespace pushbox {
static extra2d::Ptr<extra2d::FontAtlas> loadFont(int size) {
auto& resources = extra2d::Application::instance().resources();
auto font = resources.loadFont("assets/font.ttf", size);
return font;
}
PlayScene::PlayScene(int level) {
setBackgroundColor(extra2d::Colors::Black);
auto& app = extra2d::Application::instance();
auto& config = app.getConfig();
setViewportSize(static_cast<float>(config.width), static_cast<float>(config.height));
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 = static_cast<float>(app.getConfig().width);
float screenH = static_cast<float>(app.getConfig().height);
// 计算游戏区域居中偏移(假设游戏区域是 640x480
float gameWidth = 640.0f;
float gameHeight = 480.0f;
float offsetX = (screenW - gameWidth) / 2.0f;
float offsetY = (screenH - gameHeight) / 2.0f;
// 音效图标(左上角,与主界面一致)
auto soundOn = resources.loadTexture("assets/images/soundon.png");
auto soundOff = resources.loadTexture("assets/images/soundoff.png");
if (soundOn && soundOff) {
soundIcon_ = extra2d::Sprite::create(g_SoundOpen ? soundOn : soundOff);
soundIcon_->setPosition(offsetX + 50.0f, offsetY + 50.0f);
addChild(soundIcon_);
}
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_);
mapLayer_ = extra2d::makePtr<extra2d::Node>();
mapLayer_->setAnchor(0.0f, 0.0f);
mapLayer_->setPosition(0.0f, 0.0f);
addChild(mapLayer_);
setLevel(level);
}
void PlayScene::onEnter() {
Scene::onEnter();
updateSoundIcon();
updateMenuColors();
}
void PlayScene::updateMenuColors() {
// 选中的项用红色,未选中的用白色
if (restartText_) {
restartText_->setTextColor(menuIndex_ == 0 ? extra2d::Colors::Red : extra2d::Colors::White);
}
if (soundToggleText_) {
soundToggleText_->setTextColor(menuIndex_ == 1 ? extra2d::Colors::Red : extra2d::Colors::White);
}
}
void PlayScene::onUpdate(float dt) {
Scene::onUpdate(dt);
auto& app = extra2d::Application::instance();
auto& input = app.input();
// B 键返回主菜单
if (input.isButtonPressed(SDL_CONTROLLER_BUTTON_B)) {
app.scenes().replaceScene(
extra2d::makePtr<StartScene>(), extra2d::TransitionType::Fade, 0.2f);
return;
}
// Y 键重开
if (input.isButtonPressed(SDL_CONTROLLER_BUTTON_Y)) {
setLevel(g_CurrentLevel);
return;
}
// X键直接切换音效
if (input.isButtonPressed(SDL_CONTROLLER_BUTTON_X)) {
g_SoundOpen = !g_SoundOpen;
AudioManager::instance().setEnabled(g_SoundOpen);
updateSoundIcon();
return;
}
// A 键执行选中的菜单项
if (input.isButtonPressed(SDL_CONTROLLER_BUTTON_A)) {
executeMenuItem();
return;
}
// 方向键移动
if (input.isButtonPressed(SDL_CONTROLLER_BUTTON_DPAD_UP)) {
move(0, -1, 1);
flush();
} else if (input.isButtonPressed(SDL_CONTROLLER_BUTTON_DPAD_DOWN)) {
move(0, 1, 2);
flush();
} else if (input.isButtonPressed(SDL_CONTROLLER_BUTTON_DPAD_LEFT)) {
move(-1, 0, 3);
flush();
} else if (input.isButtonPressed(SDL_CONTROLLER_BUTTON_DPAD_RIGHT)) {
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) {
return;
}
}
}
gameOver();
}
void PlayScene::executeMenuItem() {
switch (menuIndex_) {
case 0: // 重开
setLevel(g_CurrentLevel);
break;
case 1: // 切换音效
g_SoundOpen = !g_SoundOpen;
AudioManager::instance().setEnabled(g_SoundOpen);
updateSoundIcon();
break;
}
}
void PlayScene::updateSoundIcon() {
if (!soundIcon_) return;
auto& app = extra2d::Application::instance();
auto& resources = app.resources();
auto soundOn = resources.loadTexture("assets/images/soundon.png");
auto soundOff = resources.loadTexture("assets/images/soundoff.png");
if (soundOn && soundOff) {
soundIcon_->setTexture(g_SoundOpen ? soundOn : soundOff);
}
}
void PlayScene::flush() {
mapLayer_->removeAllChildren();
int tileW = texFloor_ ? texFloor_->getWidth() : 32;
int tileH = texFloor_ ? texFloor_->getHeight() : 32;
// 获取窗口尺寸,计算游戏区域居中偏移
auto& app = extra2d::Application::instance();
float screenW = static_cast<float>(app.getConfig().width);
float screenH = static_cast<float>(app.getConfig().height);
float gameWidth = 640.0f;
float gameHeight = 480.0f;
float baseOffsetX = (screenW - gameWidth) / 2.0f;
float baseOffsetY = (screenH - gameHeight) / 2.0f;
// 在 12x12 网格中居中地图
float mapOffsetX = static_cast<float>((12 - map_.width) / 2) * tileW;
float mapOffsetY = static_cast<float>((12 - map_.height) / 2) * 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);
}
}
}
void PlayScene::setLevel(int level) {
g_CurrentLevel = level;
saveCurrentLevel(g_CurrentLevel);
if (levelText_) {
levelText_->setText("" + std::to_string(level) + "");
}
setStep(0);
int bestStep = loadBestStep(level, 0);
if (bestText_) {
if (bestStep != 0) {
bestText_->setText("最佳" + std::to_string(bestStep) + "");
} else {
bestText_->setText("");
}
}
// 深拷贝地图数据
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];
}
}
g_Direct = 2;
g_Pushing = false;
flush();
}
void PlayScene::setStep(int step) {
step_ = step;
if (stepText_) {
stepText_->setText("当前" + std::to_string(step) + "");
}
}
void PlayScene::move(int dx, int dy, int direct) {
int targetX = dx + map_.roleX;
int targetY = dy + map_.roleY;
g_Direct = direct;
if (targetX < 0 || targetX >= map_.width || targetY < 0 || targetY >= map_.height) {
return;
}
if (map_.value[targetY][targetX].type == TYPE::Wall) {
return;
}
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;
}
if (boxX < 0 || boxX >= map_.width || boxY < 0 || boxY >= map_.height) {
return;
}
if (map_.value[boxY][boxX].type == TYPE::Wall || map_.value[boxY][boxX].type == TYPE::Box) {
return;
}
map_.value[boxY][boxX].type = TYPE::Box;
map_.value[targetY][targetX].type = TYPE::Man;
map_.value[map_.roleY][map_.roleX].type = TYPE::Ground;
AudioManager::instance().playBoxMove();
} else {
return;
}
map_.roleX = targetX;
map_.roleY = targetY;
setStep(step_ + 1);
}
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.25f);
return;
}
setLevel(g_CurrentLevel + 1);
}
} // namespace pushbox