#include "PlayScene.h" #include "audio_manager.h" #include "storage.h" #include "StartScene.h" #include "SuccessScene.h" #include namespace pushbox { static extra2d::Ptr 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(config.width), static_cast(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(app.getConfig().width); float screenH = static_cast(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(); 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(extra2d::GamepadButton::B)) { app.scenes().replaceScene( extra2d::makePtr(), extra2d::TransitionType::Fade, 0.2f); 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); updateSoundIcon(); return; } // 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) { 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(app.getConfig().width); float screenH = static_cast(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((12 - map_.width) / 2) * tileW; float mapOffsetY = static_cast((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 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(i * tileW), offsetY + static_cast(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(), extra2d::TransitionType::Fade, 0.25f); return; } setLevel(g_CurrentLevel + 1); } } // namespace pushbox