feat(场景): 添加BaseScene作为统一基础场景类
重构所有场景类继承自BaseScene,提供统一的视口适配功能 使用游戏逻辑分辨率(GAME_WIDTH/GAME_HEIGHT)替代直接获取窗口尺寸 优化资源加载和音效播放的错误处理
This commit is contained in:
parent
1b72a1c992
commit
3a9b44cbfe
|
|
@ -133,6 +133,55 @@ public:
|
|||
/// 卸载指定音效
|
||||
void unloadSound(const std::string &key);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文本文件资源
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 加载文本文件(带缓存)
|
||||
/// @param filepath 文件路径,支持 romfs:/ 前缀
|
||||
/// @return 文件内容字符串,加载失败返回空字符串
|
||||
std::string loadTextFile(const std::string &filepath);
|
||||
|
||||
/// 加载文本文件(指定编码)
|
||||
/// @param filepath 文件路径
|
||||
/// @param encoding 文件编码(默认 UTF-8)
|
||||
/// @return 文件内容字符串
|
||||
std::string loadTextFile(const std::string &filepath, const std::string &encoding);
|
||||
|
||||
/// 通过key获取已缓存的文本内容
|
||||
std::string getTextFile(const std::string &key) const;
|
||||
|
||||
/// 检查文本文件是否已缓存
|
||||
bool hasTextFile(const std::string &key) const;
|
||||
|
||||
/// 卸载指定文本文件
|
||||
void unloadTextFile(const std::string &key);
|
||||
|
||||
/// 清理所有文本文件缓存
|
||||
void clearTextFileCache();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// JSON 文件资源
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 加载并解析 JSON 文件
|
||||
/// @param filepath 文件路径,支持 romfs:/ 前缀
|
||||
/// @return JSON 字符串内容,加载或解析失败返回空字符串
|
||||
/// @note 返回的是原始 JSON 字符串,需要自行解析
|
||||
std::string loadJsonFile(const std::string &filepath);
|
||||
|
||||
/// 通过key获取已缓存的 JSON 内容
|
||||
std::string getJsonFile(const std::string &key) const;
|
||||
|
||||
/// 检查 JSON 文件是否已缓存
|
||||
bool hasJsonFile(const std::string &key) const;
|
||||
|
||||
/// 卸载指定 JSON 文件
|
||||
void unloadJsonFile(const std::string &key);
|
||||
|
||||
/// 清理所有 JSON 文件缓存
|
||||
void clearJsonFileCache();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 缓存清理
|
||||
// ------------------------------------------------------------------------
|
||||
|
|
@ -152,6 +201,8 @@ public:
|
|||
size_t getTextureCacheSize() const;
|
||||
size_t getFontCacheSize() const;
|
||||
size_t getSoundCacheSize() const;
|
||||
size_t getTextFileCacheSize() const;
|
||||
size_t getJsonFileCacheSize() const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// LRU 缓存管理
|
||||
|
|
@ -213,11 +264,17 @@ private:
|
|||
mutable std::mutex textureMutex_;
|
||||
mutable std::mutex fontMutex_;
|
||||
mutable std::mutex soundMutex_;
|
||||
mutable std::mutex textFileMutex_;
|
||||
mutable std::mutex jsonFileMutex_;
|
||||
|
||||
// 资源缓存 - 使用弱指针实现自动清理
|
||||
std::unordered_map<std::string, WeakPtr<FontAtlas>> fontCache_;
|
||||
std::unordered_map<std::string, WeakPtr<Sound>> soundCache_;
|
||||
|
||||
// 文本文件缓存 - 使用强引用(字符串值类型)
|
||||
std::unordered_map<std::string, std::string> textFileCache_;
|
||||
std::unordered_map<std::string, std::string> jsonFileCache_;
|
||||
|
||||
// ============================================================================
|
||||
// 纹理LRU缓存
|
||||
// ============================================================================
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include <SDL2/SDL_mixer.h>
|
||||
#include <extra2d/audio/sound.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
|
|
@ -20,22 +21,20 @@ Sound::~Sound() {
|
|||
|
||||
bool Sound::play() {
|
||||
if (!chunk_) {
|
||||
E2D_LOG_WARN("Sound::play() failed: chunk is null for {}", name_);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果已在播放,先停止
|
||||
if (channel_ >= 0 && Mix_Playing(channel_)) {
|
||||
Mix_HaltChannel(channel_);
|
||||
}
|
||||
|
||||
int loops = looping_ ? -1 : 0;
|
||||
channel_ = Mix_PlayChannel(-1, chunk_, loops); // -1 = 自动分配通道
|
||||
int newChannel = Mix_PlayChannel(-1, chunk_, loops);
|
||||
|
||||
if (channel_ < 0) {
|
||||
if (newChannel < 0) {
|
||||
E2D_LOG_WARN("Sound::play() failed: no free channel for {} ({})", name_, Mix_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 设置音量
|
||||
channel_ = newChannel;
|
||||
|
||||
int mixVol = static_cast<int>(volume_ * MIX_MAX_VOLUME);
|
||||
Mix_Volume(channel_, mixVol);
|
||||
|
||||
|
|
|
|||
|
|
@ -856,4 +856,220 @@ size_t ResourceManager::getSoundCacheSize() const {
|
|||
return soundCache_.size();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 文本文件资源
|
||||
// ============================================================================
|
||||
|
||||
std::string ResourceManager::loadTextFile(const std::string &filepath) {
|
||||
return loadTextFile(filepath, "UTF-8");
|
||||
}
|
||||
|
||||
std::string ResourceManager::loadTextFile(const std::string &filepath, const std::string &encoding) {
|
||||
(void)encoding; // 目前只支持 UTF-8
|
||||
|
||||
std::lock_guard<std::mutex> lock(textFileMutex_);
|
||||
|
||||
// 检查缓存
|
||||
auto it = textFileCache_.find(filepath);
|
||||
if (it != textFileCache_.end()) {
|
||||
E2D_LOG_TRACE("ResourceManager: text file cache hit: {}", filepath);
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// 解析资源路径
|
||||
std::string resolvedPath = resolveResourcePath(filepath);
|
||||
if (resolvedPath.empty()) {
|
||||
E2D_LOG_ERROR("ResourceManager: text file not found: {}", filepath);
|
||||
return "";
|
||||
}
|
||||
|
||||
// 打开文件
|
||||
FILE *file = nullptr;
|
||||
#ifdef _WIN32
|
||||
errno_t err = fopen_s(&file, resolvedPath.c_str(), "rb");
|
||||
if (err != 0 || !file) {
|
||||
E2D_LOG_ERROR("ResourceManager: failed to open text file: {}", resolvedPath);
|
||||
return "";
|
||||
}
|
||||
#else
|
||||
file = fopen(resolvedPath.c_str(), "rb");
|
||||
if (!file) {
|
||||
E2D_LOG_ERROR("ResourceManager: failed to open text file: {}", resolvedPath);
|
||||
return "";
|
||||
}
|
||||
#endif
|
||||
|
||||
// 获取文件大小
|
||||
fseek(file, 0, SEEK_END);
|
||||
long fileSize = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
if (fileSize <= 0) {
|
||||
fclose(file);
|
||||
E2D_LOG_WARN("ResourceManager: text file is empty: {}", resolvedPath);
|
||||
return "";
|
||||
}
|
||||
|
||||
// 读取文件内容
|
||||
std::string content;
|
||||
content.resize(fileSize);
|
||||
size_t readSize = fread(&content[0], 1, fileSize, file);
|
||||
fclose(file);
|
||||
|
||||
if (readSize != static_cast<size_t>(fileSize)) {
|
||||
E2D_LOG_ERROR("ResourceManager: failed to read text file: {}", resolvedPath);
|
||||
return "";
|
||||
}
|
||||
|
||||
// 缓存内容
|
||||
textFileCache_[filepath] = content;
|
||||
E2D_LOG_DEBUG("ResourceManager: loaded text file: {} ({} bytes)", filepath, content.size());
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
std::string ResourceManager::getTextFile(const std::string &key) const {
|
||||
std::lock_guard<std::mutex> lock(textFileMutex_);
|
||||
auto it = textFileCache_.find(key);
|
||||
if (it != textFileCache_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool ResourceManager::hasTextFile(const std::string &key) const {
|
||||
std::lock_guard<std::mutex> lock(textFileMutex_);
|
||||
return textFileCache_.find(key) != textFileCache_.end();
|
||||
}
|
||||
|
||||
void ResourceManager::unloadTextFile(const std::string &key) {
|
||||
std::lock_guard<std::mutex> lock(textFileMutex_);
|
||||
auto it = textFileCache_.find(key);
|
||||
if (it != textFileCache_.end()) {
|
||||
textFileCache_.erase(it);
|
||||
E2D_LOG_DEBUG("ResourceManager: unloaded text file: {}", key);
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceManager::clearTextFileCache() {
|
||||
std::lock_guard<std::mutex> lock(textFileMutex_);
|
||||
size_t count = textFileCache_.size();
|
||||
textFileCache_.clear();
|
||||
E2D_LOG_INFO("ResourceManager: cleared {} text files from cache", count);
|
||||
}
|
||||
|
||||
size_t ResourceManager::getTextFileCacheSize() const {
|
||||
std::lock_guard<std::mutex> lock(textFileMutex_);
|
||||
return textFileCache_.size();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// JSON 文件资源
|
||||
// ============================================================================
|
||||
|
||||
std::string ResourceManager::loadJsonFile(const std::string &filepath) {
|
||||
std::lock_guard<std::mutex> lock(jsonFileMutex_);
|
||||
|
||||
// 检查缓存
|
||||
auto it = jsonFileCache_.find(filepath);
|
||||
if (it != jsonFileCache_.end()) {
|
||||
E2D_LOG_TRACE("ResourceManager: JSON file cache hit: {}", filepath);
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// 解析资源路径
|
||||
std::string resolvedPath = resolveResourcePath(filepath);
|
||||
if (resolvedPath.empty()) {
|
||||
E2D_LOG_ERROR("ResourceManager: JSON file not found: {}", filepath);
|
||||
return "";
|
||||
}
|
||||
|
||||
// 打开文件
|
||||
FILE *file = nullptr;
|
||||
#ifdef _WIN32
|
||||
errno_t err = fopen_s(&file, resolvedPath.c_str(), "rb");
|
||||
if (err != 0 || !file) {
|
||||
E2D_LOG_ERROR("ResourceManager: failed to open JSON file: {}", resolvedPath);
|
||||
return "";
|
||||
}
|
||||
#else
|
||||
file = fopen(resolvedPath.c_str(), "rb");
|
||||
if (!file) {
|
||||
E2D_LOG_ERROR("ResourceManager: failed to open JSON file: {}", resolvedPath);
|
||||
return "";
|
||||
}
|
||||
#endif
|
||||
|
||||
// 获取文件大小
|
||||
fseek(file, 0, SEEK_END);
|
||||
long fileSize = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
if (fileSize <= 0) {
|
||||
fclose(file);
|
||||
E2D_LOG_WARN("ResourceManager: JSON file is empty: {}", resolvedPath);
|
||||
return "";
|
||||
}
|
||||
|
||||
// 读取文件内容
|
||||
std::string content;
|
||||
content.resize(fileSize);
|
||||
size_t readSize = fread(&content[0], 1, fileSize, file);
|
||||
fclose(file);
|
||||
|
||||
if (readSize != static_cast<size_t>(fileSize)) {
|
||||
E2D_LOG_ERROR("ResourceManager: failed to read JSON file: {}", resolvedPath);
|
||||
return "";
|
||||
}
|
||||
|
||||
// 简单验证 JSON 格式(检查是否以 { 或 [ 开头)
|
||||
size_t firstValid = content.find_first_not_of(" \t\n\r");
|
||||
if (firstValid == std::string::npos ||
|
||||
(content[firstValid] != '{' && content[firstValid] != '[')) {
|
||||
E2D_LOG_WARN("ResourceManager: file may not be valid JSON: {}", filepath);
|
||||
// 不阻止加载,只是警告
|
||||
}
|
||||
|
||||
// 缓存内容
|
||||
jsonFileCache_[filepath] = content;
|
||||
E2D_LOG_DEBUG("ResourceManager: loaded JSON file: {} ({} bytes)", filepath, content.size());
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
std::string ResourceManager::getJsonFile(const std::string &key) const {
|
||||
std::lock_guard<std::mutex> lock(jsonFileMutex_);
|
||||
auto it = jsonFileCache_.find(key);
|
||||
if (it != jsonFileCache_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool ResourceManager::hasJsonFile(const std::string &key) const {
|
||||
std::lock_guard<std::mutex> lock(jsonFileMutex_);
|
||||
return jsonFileCache_.find(key) != jsonFileCache_.end();
|
||||
}
|
||||
|
||||
void ResourceManager::unloadJsonFile(const std::string &key) {
|
||||
std::lock_guard<std::mutex> lock(jsonFileMutex_);
|
||||
auto it = jsonFileCache_.find(key);
|
||||
if (it != jsonFileCache_.end()) {
|
||||
jsonFileCache_.erase(it);
|
||||
E2D_LOG_DEBUG("ResourceManager: unloaded JSON file: {}", key);
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceManager::clearJsonFileCache() {
|
||||
std::lock_guard<std::mutex> lock(jsonFileMutex_);
|
||||
size_t count = jsonFileCache_.size();
|
||||
jsonFileCache_.clear();
|
||||
E2D_LOG_INFO("ResourceManager: cleared {} JSON files from cache", count);
|
||||
}
|
||||
|
||||
size_t ResourceManager::getJsonFileCacheSize() const {
|
||||
std::lock_guard<std::mutex> lock(jsonFileMutex_);
|
||||
return jsonFileCache_.size();
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@ void Scene::setViewportSize(float width, float height) {
|
|||
viewportSize_ = Size(width, height);
|
||||
if (defaultCamera_) {
|
||||
defaultCamera_->setViewport(0, width, height, 0);
|
||||
}
|
||||
if (camera_) {
|
||||
} else if (camera_) {
|
||||
camera_->setViewport(0, width, height, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
// ============================================================================
|
||||
// BaseScene.cpp - Flappy Bird 基础场景实现
|
||||
// ============================================================================
|
||||
|
||||
#include "BaseScene.h"
|
||||
#include <extra2d/utils/logger.h>
|
||||
|
||||
namespace flappybird {
|
||||
|
||||
BaseScene::BaseScene() {
|
||||
// 设置背景颜色为黑色(窗口四周会显示这个颜色)
|
||||
setBackgroundColor(extra2d::Color(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
}
|
||||
|
||||
void BaseScene::onEnter() {
|
||||
extra2d::Scene::onEnter();
|
||||
// 计算并更新视口
|
||||
updateViewport();
|
||||
}
|
||||
|
||||
void BaseScene::updateViewport() {
|
||||
auto &app = extra2d::Application::instance();
|
||||
float windowWidth = static_cast<float>(app.window().getWidth());
|
||||
float windowHeight = static_cast<float>(app.window().getHeight());
|
||||
|
||||
// 计算游戏内容在窗口中的居中位置
|
||||
// 保持游戏原始宽高比,进行"黑边"适配
|
||||
float scaleX = windowWidth / GAME_WIDTH;
|
||||
float scaleY = windowHeight / GAME_HEIGHT;
|
||||
// 使用较小的缩放比例,确保游戏内容完整显示在窗口中
|
||||
float scale = std::min(scaleX, scaleY);
|
||||
|
||||
scaledGameWidth_ = GAME_WIDTH * scale;
|
||||
scaledGameHeight_ = GAME_HEIGHT * scale;
|
||||
// 计算居中偏移,使游戏内容在窗口中水平和垂直居中
|
||||
viewportOffsetX_ = (windowWidth - scaledGameWidth_) * 0.5f;
|
||||
viewportOffsetY_ = (windowHeight - scaledGameHeight_) * 0.5f;
|
||||
|
||||
// 设置视口大小为游戏逻辑分辨率
|
||||
setViewportSize(GAME_WIDTH, GAME_HEIGHT);
|
||||
|
||||
// 创建并设置相机
|
||||
auto camera = extra2d::makePtr<extra2d::Camera>();
|
||||
// 设置正交投影,覆盖整个游戏逻辑区域
|
||||
// 注意:对于2D游戏,Y轴向下增长,所以bottom > top
|
||||
camera->setViewport(0.0f, GAME_WIDTH, GAME_HEIGHT, 0.0f);
|
||||
setCamera(camera);
|
||||
}
|
||||
|
||||
void BaseScene::onRender(extra2d::RenderBackend &renderer) {
|
||||
// 检查窗口大小是否改变,如果改变则更新视口
|
||||
auto &app = extra2d::Application::instance();
|
||||
float currentWindowWidth = static_cast<float>(app.window().getWidth());
|
||||
float currentWindowHeight = static_cast<float>(app.window().getHeight());
|
||||
|
||||
// 如果窗口大小改变,重新计算视口
|
||||
float expectedWidth = scaledGameWidth_ + viewportOffsetX_ * 2.0f;
|
||||
float expectedHeight = scaledGameHeight_ + viewportOffsetY_ * 2.0f;
|
||||
if (std::abs(currentWindowWidth - expectedWidth) > 1.0f ||
|
||||
std::abs(currentWindowHeight - expectedHeight) > 1.0f) {
|
||||
E2D_LOG_INFO("BaseScene::onRender - window size changed from ({} x {}) to "
|
||||
"({} x {}), updating viewport",
|
||||
expectedWidth, expectedHeight, currentWindowWidth,
|
||||
currentWindowHeight);
|
||||
updateViewport();
|
||||
}
|
||||
|
||||
// 设置视口为居中区域
|
||||
E2D_LOG_INFO(
|
||||
"BaseScene::onRender - setting viewport: x={}, y={}, width={}, height={}",
|
||||
static_cast<int>(viewportOffsetX_), static_cast<int>(viewportOffsetY_),
|
||||
static_cast<int>(scaledGameWidth_), static_cast<int>(scaledGameHeight_));
|
||||
renderer.setViewport(
|
||||
static_cast<int>(viewportOffsetX_), static_cast<int>(viewportOffsetY_),
|
||||
static_cast<int>(scaledGameWidth_), static_cast<int>(scaledGameHeight_));
|
||||
|
||||
// 调用父类的 onRender 进行实际渲染
|
||||
extra2d::Scene::onRender(renderer);
|
||||
}
|
||||
|
||||
} // namespace flappybird
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
// ============================================================================
|
||||
// BaseScene.h - Flappy Bird 基础场景类
|
||||
// 描述: 提供统一的居中视口适配功能,所有游戏场景都应继承此类
|
||||
// ============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <extra2d/extra2d.h>
|
||||
|
||||
namespace flappybird {
|
||||
|
||||
// 游戏逻辑分辨率(原始 Flappy Bird 尺寸)
|
||||
static constexpr float GAME_WIDTH = 288.0f;
|
||||
static constexpr float GAME_HEIGHT = 512.0f;
|
||||
|
||||
/**
|
||||
* @brief Flappy Bird 基础场景类
|
||||
* 所有游戏场景都应继承此类,以获得统一的居中视口适配功能
|
||||
*/
|
||||
class BaseScene : public extra2d::Scene {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
BaseScene();
|
||||
|
||||
/**
|
||||
* @brief 场景进入时调用
|
||||
*/
|
||||
void onEnter() override;
|
||||
|
||||
/**
|
||||
* @brief 渲染时调用,设置居中视口
|
||||
* @param renderer 渲染后端
|
||||
*/
|
||||
void onRender(extra2d::RenderBackend &renderer) override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 更新视口计算,使游戏内容在窗口中居中显示
|
||||
*/
|
||||
void updateViewport();
|
||||
|
||||
// 视口适配参数(用于在窗口中居中显示游戏内容)
|
||||
float scaledGameWidth_ = 0.0f; // 缩放后的游戏宽度
|
||||
float scaledGameHeight_ = 0.0f; // 缩放后的游戏高度
|
||||
float viewportOffsetX_ = 0.0f; // 视口水平偏移
|
||||
float viewportOffsetY_ = 0.0f; // 视口垂直偏移
|
||||
};
|
||||
|
||||
} // namespace flappybird
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
// ============================================================================
|
||||
|
||||
#include "GameOverLayer.h"
|
||||
#include "BaseScene.h"
|
||||
#include "GameScene.h"
|
||||
#include "Number.h"
|
||||
#include "ResLoader.h"
|
||||
|
|
@ -19,9 +20,9 @@ void GameOverLayer::onEnter() {
|
|||
Node::onEnter();
|
||||
|
||||
// 在 onEnter 中初始化,此时 weak_from_this() 可用
|
||||
auto &app = extra2d::Application::instance();
|
||||
float screenWidth = static_cast<float>(app.getConfig().width);
|
||||
float screenHeight = static_cast<float>(app.getConfig().height);
|
||||
// 使用游戏逻辑分辨率
|
||||
float screenWidth = GAME_WIDTH;
|
||||
float screenHeight = GAME_HEIGHT;
|
||||
|
||||
// 整体居中(x 坐标相对于屏幕中心)
|
||||
setPosition(extra2d::Vec2(screenWidth / 2.0f, screenHeight));
|
||||
|
|
@ -45,6 +46,15 @@ void GameOverLayer::onEnter() {
|
|||
// 创建向上移动的动画(从屏幕底部移动到正常位置)
|
||||
auto moveAction = extra2d::makePtr<extra2d::MoveBy>(
|
||||
1.0f, extra2d::Vec2(0.0f, -screenHeight));
|
||||
moveAction->setCompletionCallback([this]() {
|
||||
animationDone_ = true;
|
||||
if (restartBtn_)
|
||||
restartBtn_->setEnabled(true);
|
||||
if (menuBtn_)
|
||||
menuBtn_->setEnabled(true);
|
||||
if (shareBtn_)
|
||||
shareBtn_->setEnabled(true);
|
||||
});
|
||||
runAction(moveAction);
|
||||
}
|
||||
|
||||
|
|
@ -107,63 +117,60 @@ void GameOverLayer::initPanel(int score, float screenHeight) {
|
|||
}
|
||||
|
||||
void GameOverLayer::initButtons() {
|
||||
// 创建重新开始按钮(y=360)
|
||||
auto restartFrame = ResLoader::getKeyFrame("button_restart");
|
||||
if (restartFrame) {
|
||||
auto restartBtn = extra2d::Button::create();
|
||||
restartBtn->setBackgroundImage(restartFrame->getTexture(),
|
||||
restartBtn_ = extra2d::Button::create();
|
||||
restartBtn_->setBackgroundImage(restartFrame->getTexture(),
|
||||
restartFrame->getRect());
|
||||
restartBtn->setAnchor(extra2d::Vec2(0.5f, 0.5f));
|
||||
restartBtn->setPosition(
|
||||
extra2d::Vec2(0.0f, 360.0f)); // x=0 表示相对于中心点
|
||||
restartBtn->setOnClick([]() {
|
||||
restartBtn_->setAnchor(extra2d::Vec2(0.5f, 0.5f));
|
||||
restartBtn_->setPosition(extra2d::Vec2(0.0f, 360.0f));
|
||||
restartBtn_->setEnabled(false);
|
||||
restartBtn_->setOnClick([]() {
|
||||
ResLoader::playMusic(MusicType::Click);
|
||||
auto &app = extra2d::Application::instance();
|
||||
app.scenes().replaceScene(extra2d::makePtr<GameScene>(),
|
||||
extra2d::TransitionType::Fade, 0.5f);
|
||||
});
|
||||
addChild(restartBtn);
|
||||
addChild(restartBtn_);
|
||||
}
|
||||
|
||||
// 创建返回主菜单按钮(y=420)
|
||||
auto menuFrame = ResLoader::getKeyFrame("button_menu");
|
||||
if (menuFrame) {
|
||||
auto menuBtn = extra2d::Button::create();
|
||||
menuBtn->setBackgroundImage(menuFrame->getTexture(), menuFrame->getRect());
|
||||
menuBtn->setAnchor(extra2d::Vec2(0.5f, 0.5f));
|
||||
menuBtn->setPosition(extra2d::Vec2(0.0f, 420.0f)); // x=0 表示相对于中心点
|
||||
menuBtn->setOnClick([]() {
|
||||
menuBtn_ = extra2d::Button::create();
|
||||
menuBtn_->setBackgroundImage(menuFrame->getTexture(), menuFrame->getRect());
|
||||
menuBtn_->setAnchor(extra2d::Vec2(0.5f, 0.5f));
|
||||
menuBtn_->setPosition(extra2d::Vec2(0.0f, 420.0f));
|
||||
menuBtn_->setEnabled(false);
|
||||
menuBtn_->setOnClick([]() {
|
||||
ResLoader::playMusic(MusicType::Click);
|
||||
auto &app = extra2d::Application::instance();
|
||||
app.scenes().replaceScene(extra2d::makePtr<StartScene>(),
|
||||
extra2d::TransitionType::Fade, 0.5f);
|
||||
});
|
||||
addChild(menuBtn);
|
||||
addChild(menuBtn_);
|
||||
}
|
||||
|
||||
// 创建分享按钮(y=480,在 MENU 按钮下方)
|
||||
auto shareFrame = ResLoader::getKeyFrame("button_share");
|
||||
if (shareFrame) {
|
||||
auto shareBtn = extra2d::Button::create();
|
||||
shareBtn->setBackgroundImage(shareFrame->getTexture(),
|
||||
shareBtn_ = extra2d::Button::create();
|
||||
shareBtn_->setBackgroundImage(shareFrame->getTexture(),
|
||||
shareFrame->getRect());
|
||||
shareBtn->setAnchor(extra2d::Vec2(0.5f, 0.5f));
|
||||
shareBtn->setPosition(extra2d::Vec2(0.0f, 460.0f)); // x=0 表示相对于中心点
|
||||
shareBtn->setOnClick([]() {
|
||||
ResLoader::playMusic(MusicType::Click);
|
||||
// TODO: 实现分享功能
|
||||
});
|
||||
addChild(shareBtn);
|
||||
shareBtn_->setAnchor(extra2d::Vec2(0.5f, 0.5f));
|
||||
shareBtn_->setPosition(extra2d::Vec2(0.0f, 460.0f));
|
||||
shareBtn_->setEnabled(false);
|
||||
shareBtn_->setOnClick([]() { ResLoader::playMusic(MusicType::Click); });
|
||||
addChild(shareBtn_);
|
||||
}
|
||||
}
|
||||
|
||||
void GameOverLayer::onUpdate(float dt) {
|
||||
Node::onUpdate(dt);
|
||||
|
||||
// 检测手柄按键
|
||||
if (!animationDone_)
|
||||
return;
|
||||
|
||||
auto &input = extra2d::Application::instance().input();
|
||||
|
||||
// A 键重新开始游戏
|
||||
if (input.isButtonPressed(extra2d::GamepadButton::A)) {
|
||||
ResLoader::playMusic(MusicType::Click);
|
||||
auto &app = extra2d::Application::instance();
|
||||
|
|
@ -171,7 +178,6 @@ void GameOverLayer::onUpdate(float dt) {
|
|||
extra2d::TransitionType::Fade, 0.5f);
|
||||
}
|
||||
|
||||
// B 键返回主菜单
|
||||
if (input.isButtonPressed(extra2d::GamepadButton::B)) {
|
||||
ResLoader::playMusic(MusicType::Click);
|
||||
auto &app = extra2d::Application::instance();
|
||||
|
|
|
|||
|
|
@ -53,6 +53,10 @@ private:
|
|||
extra2d::Ptr<extra2d::SpriteFrame> getMedal(int score);
|
||||
|
||||
int score_ = 0; // 本局得分
|
||||
bool animationDone_ = false; // 动画是否完成
|
||||
extra2d::Ptr<extra2d::Button> restartBtn_; // 重新开始按钮
|
||||
extra2d::Ptr<extra2d::Button> menuBtn_; // 菜单按钮
|
||||
extra2d::Ptr<extra2d::Button> shareBtn_; // 分享按钮
|
||||
};
|
||||
|
||||
} // namespace flappybird
|
||||
|
|
|
|||
|
|
@ -10,20 +10,15 @@
|
|||
namespace flappybird {
|
||||
|
||||
GameScene::GameScene() {
|
||||
auto &app = extra2d::Application::instance();
|
||||
auto &config = app.getConfig();
|
||||
setViewportSize(static_cast<float>(config.width),
|
||||
static_cast<float>(config.height));
|
||||
// 设置背景颜色为黑色
|
||||
setBackgroundColor(extra2d::Color(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
// 基类 BaseScene 已经处理了视口设置和背景颜色
|
||||
}
|
||||
|
||||
void GameScene::onEnter() {
|
||||
extra2d::Scene::onEnter();
|
||||
BaseScene::onEnter();
|
||||
|
||||
auto &app = extra2d::Application::instance();
|
||||
float screenWidth = static_cast<float>(app.getConfig().width);
|
||||
float screenHeight = static_cast<float>(app.getConfig().height);
|
||||
// 游戏坐标系:使用游戏逻辑分辨率
|
||||
float screenWidth = GAME_WIDTH;
|
||||
float screenHeight = GAME_HEIGHT;
|
||||
|
||||
// 添加背景(使用左上角锚点,与原游戏一致)
|
||||
auto bgFrame = ResLoader::getKeyFrame("bg_day");
|
||||
|
|
@ -91,42 +86,30 @@ void GameScene::onEnter() {
|
|||
}
|
||||
|
||||
void GameScene::onUpdate(float dt) {
|
||||
// 注意:这里要先调用父类的 onUpdate,以确保 GameOverLayer 的动画能播放
|
||||
extra2d::Scene::onUpdate(dt);
|
||||
|
||||
// 游戏结束后不再更新游戏逻辑(但子节点的动画继续)
|
||||
if (gameOver_)
|
||||
return;
|
||||
|
||||
if (!gameOver_) {
|
||||
if (!bird_)
|
||||
return;
|
||||
|
||||
auto &input = extra2d::Application::instance().input();
|
||||
|
||||
// 检测跳跃按键(A键或空格)
|
||||
if (input.isButtonPressed(extra2d::GamepadButton::A) ||
|
||||
input.isMousePressed(extra2d::MouseButton::Left)) {
|
||||
if (!started_) {
|
||||
// 游戏还没开始,开始游戏
|
||||
started_ = true;
|
||||
startGame();
|
||||
}
|
||||
bird_->jump();
|
||||
}
|
||||
|
||||
// 游戏已经开始
|
||||
if (started_) {
|
||||
// 模拟小鸟下落
|
||||
bird_->fall(dt);
|
||||
|
||||
// 检查得分(小鸟飞过水管)
|
||||
if (pipes_) {
|
||||
Pipe *firstPipe = pipes_->getPipe(0);
|
||||
if (firstPipe && !firstPipe->scored) {
|
||||
float birdX = bird_->getPosition().x;
|
||||
float pipeX = firstPipe->getPosition().x;
|
||||
if (pipeX <= birdX) {
|
||||
// 小鸟飞过了水管
|
||||
score_++;
|
||||
scoreNumber_->setNumber(score_);
|
||||
firstPipe->scored = true;
|
||||
|
|
@ -135,25 +118,20 @@ void GameScene::onUpdate(float dt) {
|
|||
}
|
||||
}
|
||||
|
||||
// 检查碰撞
|
||||
if (bird_->isLiving() && checkCollision()) {
|
||||
onHit();
|
||||
}
|
||||
|
||||
// 检查是否撞到地面(原游戏使用 123 作为地面高度)
|
||||
auto &app = extra2d::Application::instance();
|
||||
float screenHeight = static_cast<float>(app.getConfig().height);
|
||||
|
||||
if (screenHeight - bird_->getPosition().y <= 123.0f) {
|
||||
// 小鸟撞到地面
|
||||
if (bird_->isLiving() && GAME_HEIGHT - bird_->getPosition().y <= 123.0f) {
|
||||
bird_->setPosition(
|
||||
extra2d::Vec2(bird_->getPosition().x, screenHeight - 123.0f));
|
||||
extra2d::Vec2(bird_->getPosition().x, GAME_HEIGHT - 123.0f));
|
||||
bird_->setStatus(Bird::Status::Still);
|
||||
onHit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gameOver();
|
||||
}
|
||||
}
|
||||
BaseScene::onUpdate(dt);
|
||||
}
|
||||
|
||||
void GameScene::startGame() {
|
||||
|
|
@ -232,18 +210,16 @@ void GameScene::onHit() {
|
|||
scoreNumber_->setVisible(false);
|
||||
}
|
||||
|
||||
// 设置游戏结束标志
|
||||
gameOver_ = true;
|
||||
|
||||
// 延迟显示游戏结束界面
|
||||
gameOver();
|
||||
}
|
||||
|
||||
void GameScene::gameOver() {
|
||||
if (gameOver_)
|
||||
return;
|
||||
|
||||
started_ = false;
|
||||
gameOver_ = true;
|
||||
|
||||
// 显示游戏结束层
|
||||
auto gameOverLayer = extra2d::makePtr<GameOverLayer>(score_);
|
||||
addChild(gameOverLayer);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <extra2d/extra2d.h>
|
||||
#include "BaseScene.h"
|
||||
#include "Bird.h"
|
||||
#include "Pipes.h"
|
||||
#include "Ground.h"
|
||||
|
|
@ -17,7 +17,7 @@ namespace flappybird {
|
|||
* @brief 游戏主场景类
|
||||
* 游戏的核心场景,处理游戏逻辑、碰撞检测和得分
|
||||
*/
|
||||
class GameScene : public extra2d::Scene {
|
||||
class GameScene : public BaseScene {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
#include "ResLoader.h"
|
||||
#include <json/json.hpp>
|
||||
#include <fstream>
|
||||
|
||||
namespace flappybird {
|
||||
|
||||
|
|
@ -22,17 +21,16 @@ void ResLoader::init() {
|
|||
return;
|
||||
}
|
||||
|
||||
// 读取 atlas.json 文件
|
||||
std::ifstream file("assets/images/atlas.json");
|
||||
if (!file.is_open()) {
|
||||
E2D_LOG_ERROR("无法打开 atlas.json 文件");
|
||||
// 使用资源管理器加载 JSON 文件
|
||||
std::string jsonContent = resources.loadJsonFile("assets/images/atlas.json");
|
||||
if (jsonContent.empty()) {
|
||||
E2D_LOG_ERROR("无法加载 atlas.json 文件");
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析 JSON 图集数据
|
||||
try {
|
||||
nlohmann::json jsonData;
|
||||
file >> jsonData;
|
||||
nlohmann::json jsonData = nlohmann::json::parse(jsonContent);
|
||||
|
||||
for (const auto &sprite : jsonData["sprites"]) {
|
||||
std::string name = sprite["name"];
|
||||
|
|
@ -48,12 +46,9 @@ void ResLoader::init() {
|
|||
E2D_LOG_INFO("成功加载 {} 个精灵帧", imageMap_.size());
|
||||
} catch (const std::exception &e) {
|
||||
E2D_LOG_ERROR("解析 atlas.json 失败: {}", e.what());
|
||||
file.close();
|
||||
return;
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
// 加载音效
|
||||
soundMap_[MusicType::Click] = resources.loadSound("assets/sound/click.wav");
|
||||
soundMap_[MusicType::Hit] = resources.loadSound("assets/sound/hit.wav");
|
||||
|
|
@ -64,7 +59,8 @@ void ResLoader::init() {
|
|||
E2D_LOG_INFO("资源加载完成");
|
||||
}
|
||||
|
||||
extra2d::Ptr<extra2d::SpriteFrame> ResLoader::getKeyFrame(const std::string& name) {
|
||||
extra2d::Ptr<extra2d::SpriteFrame>
|
||||
ResLoader::getKeyFrame(const std::string &name) {
|
||||
auto it = imageMap_.find(name);
|
||||
if (it == imageMap_.end()) {
|
||||
E2D_LOG_WARN("找不到精灵帧: %s", name.c_str());
|
||||
|
|
@ -72,24 +68,31 @@ extra2d::Ptr<extra2d::SpriteFrame> ResLoader::getKeyFrame(const std::string& nam
|
|||
}
|
||||
|
||||
const ImageInfo &info = it->second;
|
||||
E2D_LOG_INFO("加载精灵帧: name={}, w={}, h={}, x={}, y={}",
|
||||
name, info.width, info.height, info.x, info.y);
|
||||
E2D_LOG_INFO("加载精灵帧: name={}, w={}, h={}, x={}, y={}", name, info.width,
|
||||
info.height, info.x, info.y);
|
||||
|
||||
// 检查纹理尺寸
|
||||
if (atlasTexture_) {
|
||||
E2D_LOG_INFO("图集纹理尺寸: {}x{}", atlasTexture_->getWidth(), atlasTexture_->getHeight());
|
||||
E2D_LOG_INFO("图集纹理尺寸: {}x{}", atlasTexture_->getWidth(),
|
||||
atlasTexture_->getHeight());
|
||||
}
|
||||
|
||||
return extra2d::makePtr<extra2d::SpriteFrame>(
|
||||
atlasTexture_,
|
||||
extra2d::Rect(info.x, info.y, info.width, info.height)
|
||||
);
|
||||
atlasTexture_, extra2d::Rect(info.x, info.y, info.width, info.height));
|
||||
}
|
||||
|
||||
void ResLoader::playMusic(MusicType type) {
|
||||
auto it = soundMap_.find(type);
|
||||
if (it != soundMap_.end() && it->second) {
|
||||
it->second->play();
|
||||
if (it == soundMap_.end()) {
|
||||
E2D_LOG_WARN("ResLoader::playMusic: sound type not found");
|
||||
return;
|
||||
}
|
||||
if (!it->second) {
|
||||
E2D_LOG_WARN("ResLoader::playMusic: sound pointer is null");
|
||||
return;
|
||||
}
|
||||
if (!it->second->play()) {
|
||||
E2D_LOG_WARN("ResLoader::playMusic: failed to play sound");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,52 +3,35 @@
|
|||
// ============================================================================
|
||||
|
||||
#include "SplashScene.h"
|
||||
#include "StartScene.h"
|
||||
#include "ResLoader.h"
|
||||
#include "StartScene.h"
|
||||
#include <extra2d/utils/logger.h>
|
||||
|
||||
namespace flappybird {
|
||||
|
||||
SplashScene::SplashScene() {
|
||||
// 设置视口大小
|
||||
auto& app = extra2d::Application::instance();
|
||||
auto& config = app.getConfig();
|
||||
setViewportSize(static_cast<float>(config.width), static_cast<float>(config.height));
|
||||
// 基类 BaseScene 已经处理了视口设置和背景颜色
|
||||
}
|
||||
|
||||
void SplashScene::onEnter() {
|
||||
extra2d::Scene::onEnter();
|
||||
|
||||
// 设置黑色背景
|
||||
setBackgroundColor(extra2d::Color(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
|
||||
auto viewport = getViewportSize();
|
||||
float centerX = viewport.width / 2.0f;
|
||||
float centerY = viewport.height / 2.0f;
|
||||
BaseScene::onEnter();
|
||||
|
||||
// 尝试加载 splash 图片
|
||||
auto splashFrame = ResLoader::getKeyFrame("splash");
|
||||
if (splashFrame) {
|
||||
auto splash = extra2d::Sprite::create(splashFrame->getTexture(), splashFrame->getRect());
|
||||
auto splash = extra2d::Sprite::create(splashFrame->getTexture(),
|
||||
splashFrame->getRect());
|
||||
splash->setAnchor(0.5f, 0.5f);
|
||||
splash->setPosition(centerX, centerY);
|
||||
// splash 图片是全屏的(288x512),将其中心放在游戏区域中心
|
||||
splash->setPosition(GAME_WIDTH / 2.0f, GAME_HEIGHT / 2.0f);
|
||||
addChild(splash);
|
||||
} else {
|
||||
// 如果 splash 加载失败,尝试加载 title 图片作为备用
|
||||
auto titleFrame = ResLoader::getKeyFrame("title");
|
||||
if (titleFrame) {
|
||||
auto title = extra2d::Sprite::create(titleFrame->getTexture(), titleFrame->getRect());
|
||||
title->setAnchor(0.5f, 0.5f);
|
||||
title->setPosition(centerX, centerY);
|
||||
addChild(title);
|
||||
}
|
||||
}
|
||||
|
||||
// 播放转场音效
|
||||
ResLoader::playMusic(MusicType::Swoosh);
|
||||
}
|
||||
|
||||
void SplashScene::onUpdate(float dt) {
|
||||
extra2d::Scene::onUpdate(dt);
|
||||
BaseScene::onUpdate(dt);
|
||||
|
||||
// 计时
|
||||
timer_ += dt;
|
||||
|
|
@ -59,11 +42,8 @@ void SplashScene::onUpdate(float dt) {
|
|||
|
||||
void SplashScene::gotoStartScene() {
|
||||
auto &app = extra2d::Application::instance();
|
||||
app.scenes().replaceScene(
|
||||
extra2d::makePtr<StartScene>(),
|
||||
extra2d::TransitionType::Fade,
|
||||
0.5f
|
||||
);
|
||||
app.scenes().replaceScene(extra2d::makePtr<StartScene>(),
|
||||
extra2d::TransitionType::Fade, 0.5f);
|
||||
}
|
||||
|
||||
} // namespace flappybird
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <extra2d/extra2d.h>
|
||||
#include "BaseScene.h"
|
||||
|
||||
namespace flappybird {
|
||||
|
||||
|
|
@ -13,7 +13,7 @@ namespace flappybird {
|
|||
* @brief 启动场景类
|
||||
* 显示游戏 Logo,短暂延迟后进入主菜单
|
||||
*/
|
||||
class SplashScene : public extra2d::Scene {
|
||||
class SplashScene : public BaseScene {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
|
|
|
|||
|
|
@ -12,21 +12,15 @@
|
|||
namespace flappybird {
|
||||
|
||||
StartScene::StartScene() {
|
||||
auto &app = extra2d::Application::instance();
|
||||
auto &config = app.getConfig();
|
||||
setViewportSize(static_cast<float>(config.width),
|
||||
static_cast<float>(config.height));
|
||||
// 基类 BaseScene 已经处理了视口设置和背景颜色
|
||||
}
|
||||
|
||||
void StartScene::onEnter() {
|
||||
extra2d::Scene::onEnter();
|
||||
BaseScene::onEnter();
|
||||
|
||||
// 设置背景颜色为黑色(防止透明)
|
||||
setBackgroundColor(extra2d::Color(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
|
||||
auto &app = extra2d::Application::instance();
|
||||
float screenWidth = static_cast<float>(app.getConfig().width);
|
||||
float screenHeight = static_cast<float>(app.getConfig().height);
|
||||
// 使用游戏逻辑分辨率
|
||||
float screenWidth = GAME_WIDTH;
|
||||
float screenHeight = GAME_HEIGHT;
|
||||
|
||||
// 添加背景(使用左上角锚点)
|
||||
auto bgFrame = ResLoader::getKeyFrame("bg_day");
|
||||
|
|
@ -125,7 +119,7 @@ void StartScene::onEnter() {
|
|||
}
|
||||
|
||||
void StartScene::onUpdate(float dt) {
|
||||
extra2d::Scene::onUpdate(dt);
|
||||
BaseScene::onUpdate(dt);
|
||||
|
||||
// 检测 A 键或空格开始游戏
|
||||
auto &input = extra2d::Application::instance().input();
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <extra2d/extra2d.h>
|
||||
#include "BaseScene.h"
|
||||
|
||||
namespace flappybird {
|
||||
|
||||
|
|
@ -13,7 +13,7 @@ namespace flappybird {
|
|||
* @brief 开始场景类
|
||||
* 游戏主菜单,包含开始游戏按钮和版权信息
|
||||
*/
|
||||
class StartScene : public extra2d::Scene {
|
||||
class StartScene : public BaseScene {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
|
|
|
|||
|
|
@ -4,14 +4,15 @@
|
|||
|
||||
#include "Ground.h"
|
||||
#include "ResLoader.h"
|
||||
#include "BaseScene.h"
|
||||
|
||||
namespace flappybird {
|
||||
|
||||
Ground::Ground() {
|
||||
moving_ = true;
|
||||
|
||||
auto& app = extra2d::Application::instance();
|
||||
float screenHeight = static_cast<float>(app.getConfig().height);
|
||||
// 使用游戏逻辑高度,而不是窗口高度
|
||||
float screenHeight = GAME_HEIGHT;
|
||||
|
||||
// 获取地面纹理帧
|
||||
auto landFrame = ResLoader::getKeyFrame("land");
|
||||
|
|
|
|||
|
|
@ -4,17 +4,16 @@
|
|||
// 描述: 经典的 Flappy Bird 游戏实现
|
||||
// ============================================================================
|
||||
|
||||
#include <extra2d/extra2d.h>
|
||||
#include "SplashScene.h"
|
||||
#include "ResLoader.h"
|
||||
#include "SplashScene.h"
|
||||
#include <extra2d/extra2d.h>
|
||||
|
||||
using namespace extra2d;
|
||||
|
||||
/**
|
||||
* @brief 程序入口
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int main(int argc, char **argv) {
|
||||
// 初始化日志系统
|
||||
Logger::init();
|
||||
Logger::setLevel(LogLevel::Debug);
|
||||
|
|
@ -29,8 +28,8 @@ int main(int argc, char **argv)
|
|||
// 配置应用
|
||||
AppConfig config;
|
||||
config.title = "Extra2D - FlappyBird";
|
||||
config.width = 288; // 原始游戏宽度
|
||||
config.height = 512; // 原始游戏高度
|
||||
config.width = 1280; // 窗口宽度 (720P 分辨率)
|
||||
config.height = 720; // 窗口高度 (720P 分辨率)
|
||||
config.vsync = true;
|
||||
config.fpsLimit = 60;
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "Pipe.h"
|
||||
#include "ResLoader.h"
|
||||
#include "BaseScene.h"
|
||||
|
||||
namespace flappybird {
|
||||
|
||||
|
|
@ -18,8 +19,8 @@ void Pipe::onEnter() {
|
|||
|
||||
// 在 onEnter 中创建子节点,此时 weak_from_this() 可用
|
||||
if (!topPipe_ && !bottomPipe_) {
|
||||
auto& app = extra2d::Application::instance();
|
||||
float screenHeight = static_cast<float>(app.getConfig().height);
|
||||
// 使用游戏逻辑高度
|
||||
float screenHeight = GAME_HEIGHT;
|
||||
|
||||
// 获取地面高度
|
||||
auto landFrame = ResLoader::getKeyFrame("land");
|
||||
|
|
@ -62,8 +63,8 @@ extra2d::Rect Pipe::getBoundingBox() const {
|
|||
float pipeWidth = 52.0f;
|
||||
float halfWidth = pipeWidth / 2.0f;
|
||||
|
||||
auto& app = extra2d::Application::instance();
|
||||
float screenHeight = static_cast<float>(app.getConfig().height);
|
||||
// 使用游戏逻辑高度
|
||||
float screenHeight = GAME_HEIGHT;
|
||||
|
||||
return extra2d::Rect(
|
||||
pos.x - halfWidth,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
// ============================================================================
|
||||
|
||||
#include "Pipes.h"
|
||||
#include "BaseScene.h"
|
||||
|
||||
namespace flappybird {
|
||||
|
||||
|
|
@ -76,9 +77,8 @@ void Pipes::addPipe() {
|
|||
// 设置水管位置
|
||||
if (pipeCount_ == 0) {
|
||||
// 第一个水管在屏幕外 130 像素处
|
||||
auto& app = extra2d::Application::instance();
|
||||
pipe->setPosition(extra2d::Vec2(
|
||||
static_cast<float>(app.getConfig().width) + 130.0f,
|
||||
GAME_WIDTH + 130.0f,
|
||||
0.0f
|
||||
));
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Reference in New Issue