资讯动态

Cocos2d-x横版跑酷游戏开发实战:从零构建“萝莉快跑”

发布时间:2026/8/5 3:19:35 来源:尧图企业网站定制
1. 项目概述与核心思路“萝莉快跑”这个项目一听名字就知道是个典型的横版跑酷游戏。这类游戏的核心玩法简单直接——控制角色在一条无限延伸的跑道上通过跳跃、下滑等动作躲避障碍同时尽可能收集金币或道具跑得越远分数越高。听起来简单但要把手感做顺滑、节奏做爽快里面的门道可不少。我这次选择用 Cocos2d-x 这个老牌引擎来开发一方面是因为它跨平台能力极强一套代码能轻松发布到 iOS、Android 甚至 Windows 上对于想快速验证玩法的独立开发者或者小团队来说非常友好另一方面Cocos2d-x 在 2D 游戏开发领域积累了大量的社区资源和成熟方案很多坑前人已经帮你踩过了。这个项目的核心目标不仅仅是实现一个能跑能跳的 demo而是要打造一个具备完整游戏循环、手感优秀、有一定扩展性的横版跑酷游戏。这意味着我们需要处理好几个关键模块首先是角色的控制与动画要让“萝莉”的跑、跳、下滑等动作流畅自然其次是关卡与障碍物的生成逻辑如何做到既随机又有节奏感让玩家不会觉得单调然后是游戏的核心循环包括分数计算、生命值管理、游戏状态切换开始、进行中、结束最后是视听效果的打磨比如音效、粒子特效和 UI 交互。整个开发过程我会从最基础的场景搭建开始一步步把这些模块串联起来过程中会分享很多从实际编码中总结出来的技巧和避坑指南。2. 开发环境搭建与项目初始化2.1 Cocos2d-x 引擎选择与环境配置Cocos2d-x 发展到今天版本已经相当多了。对于新项目我强烈推荐使用 Cocos2d-x 4.0 或更新版本。4.x 版本对渲染器进行了重构性能有显著提升同时更好地支持了现代 C 特性代码写起来更舒服。官方提供了 Cocos Creator 这个编辑器但对于习惯纯代码开发或者需要更深度定制的开发者使用命令行工具创建和构建 C 项目依然是主流且高效的方式。第一步是下载引擎。不要去官网找那些零散的下载链接直接用 Git 克隆官方仓库是最稳妥的能确保拿到最新的稳定代码和示例。打开终端Windows 用 PowerShell 或 CMDMac/Linux 用 Terminal执行以下命令git clone https://github.com/cocos2d/cocos2d-x.git cd cocos2d-x git checkout v4.0 # 切换到4.0稳定分支你也可以用最新的分支克隆完成后需要安装 Python 依赖。Cocos2d-x 的构建系统依赖 Python 2.7 或 3.x以及一些 Python 模块。在引擎根目录下通常会有requirements.txt文件用 pip 安装即可pip install -r requirements.txt。这里有个坑需要注意如果你的系统同时有 Python2 和 Python3要确保 pip 命令对应的是正确的 Python 版本否则后续的创建项目脚本可能会报错。一个简单的检查方法是运行python --version和pip --version看它们是否来自同一个 Python 安装。接下来是设置环境变量。你需要把 Cocos2d-x 的根目录路径以及其tools/cocos2d-console/bin目录添加到系统的 PATH 环境变量中。这样你就可以在任意位置使用cocos这个命令行工具了。在 Windows 上可以通过“系统属性 - 高级 - 环境变量”来设置在 Mac/Linux 上可以修改~/.bash_profile或~/.zshrc文件添加类似export PATH/path/to/cocos2d-x/tools/cocos2d-console/bin:$PATH的语句然后执行source ~/.zshrc使其生效。注意很多新手在这一步会遇到“cocos命令未找到”的问题。除了检查PATH还要确保你关闭并重新打开了终端窗口或者在新开的终端标签页中操作因为环境变量的更改不会自动应用到已经打开的终端会话中。2.2 创建“萝莉快跑”项目骨架环境配置好后就可以创建我们的游戏项目了。使用cocos new命令指定项目名称、包名、使用的编程语言和引擎路径cocos new LoliRun -p com.yourcompany.lolirun -l cpp -d /path/to/your/projects解释一下这几个参数LoliRun项目名称也是生成的文件夹名。-p com.yourcompany.lolirun包名Package Name遵循 Java 包名的反向域名格式这在后续部署到移动设备时非常重要必须是唯一的。-l cpp指定使用 C 语言。Cocos2d-x 也支持 Lua 和 JavaScript但 C 性能最好适合对性能要求较高的跑酷游戏。-d /path/to/your/projects指定项目创建的目录。命令执行成功后你会得到一个标准的 Cocos2d-x 项目结构。主要目录和作用如下Classes/存放所有的 C 源代码文件这是我们主要工作的地方。Resources/存放所有的资源文件如图片.png, .jpg、音效.mp3, .wav、字体、配置文件等。proj.android/和proj.ios/分别是 Android 和 iOS 平台的特定工程文件。CMakeLists.txt用于跨平台编译的 CMake 配置文件。创建完项目先别急着写代码。我建议先编译运行一下确保基础环境没问题。进入项目根目录对于桌面平台如 Mac 或 Windows可以这样编译运行cd LoliRun cocos run -p mac # 如果是Mac # 或 cocos run -p win32 # 如果是Windows如果一切顺利你应该能看到一个默认的“Hello World”场景窗口弹出来。这证明你的引擎、项目配置和编译链都是正常的。这个默认场景位于Classes/AppDelegate.cpp和Classes/HelloWorldScene.cpp中我们接下来就会把它改造成我们的游戏主场景。3. 游戏核心场景与角色构建3.1 设计游戏主场景与基础图层一个典型的横版跑酷游戏场景从视觉上可以分解为几个层次最底层是背景可能有多层滚动背景以营造景深效果中间是跑道和障碍物最上层是角色、UI分数、生命值和特效。在 Cocos2d-x 中我们使用Scene和Layer来组织这些元素。首先我们重命名或替换掉默认的HelloWorldScene。我创建了一个新的GameScene类。在GameScene.h中我定义了场景的几个核心图层class GameScene : public cocos2d::Scene { public: static cocos2d::Scene* createScene(); virtual bool init(); CREATE_FUNC(GameScene); private: cocos2d::Layer* _backgroundLayer; // 背景层 cocos2d::Layer* _gameLayer; // 游戏层跑道、障碍物、角色 cocos2d::Layer* _uiLayer; // UI层 void initBackground(); // 初始化背景 void initGameElements(); // 初始化游戏元素 void initUI(); // 初始化用户界面 };在init()方法中我们依次创建并添加这些图层。这里有一个重要的细节图层的添加顺序决定了它们的绘制顺序后添加的在上层。所以_backgroundLayer最先添加_uiLayer最后添加确保UI永远在最前面。背景的实现通常采用“视差滚动”技术来增强动感。我们准备两张可以无缝衔接的背景图比如天空和远山。在initBackground()中我们将每张背景图创建两个精灵Sprite并排排列。然后在每帧的更新回调里让这些背景以不同的速度向左移动模拟摄像机跟随角色向右移动当某张图完全移出屏幕左侧时立即将其位置重置到队列的最右侧从而实现无限循环滚动。远山的移动速度比近处的天空慢一些就能产生景深效果。void GameScene::initBackground() { _backgroundLayer Layer::create(); this-addChild(_backgroundLayer); // 创建天空背景快速滚动 auto skySprite1 Sprite::create(background/sky.png); auto skySprite2 Sprite::create(background/sky.png); skySprite1-setAnchorPoint(Vec2::ZERO); skySprite2-setAnchorPoint(Vec2::ZERO); skySprite1-setPosition(0, 0); skySprite2-setPosition(skySprite1-getContentSize().width, 0); _backgroundLayer-addChild(skySprite1); _backgroundLayer-addChild(skySprite2); _skySprites.pushBack(skySprite1); _skySprites.pushBack(skySprite2); // 类似地创建远山背景慢速滚动 // ... }3.2 实现“萝莉”角色与控制角色是游戏的核心。我们需要一个Player类来封装角色的所有属性和行为。这个类继承自cocos2d::Sprite以便于显示和进行物理碰撞检测。首先定义角色的状态。一个跑酷角色通常有几种状态奔跑IDLE/RUN、跳跃JUMP、下滑SLIDE、受伤HURT和死亡DEAD。我们用枚举来管理class Player : public cocos2d::Sprite { public: enum class State { RUNNING, JUMPING, SLIDING, HURT, DEAD }; // ... 其他方法和属性 private: State _currentState; float _velocityY; // Y轴速度用于跳跃计算 bool _isOnGround; // 是否在地面上 // ... };角色的动画使用 Cocos2d-x 的Animation和Animate动作来实现。我们需要为每种状态准备一组帧动画。例如奔跑动画可能是由8张连续图片组成的。在Player的初始化方法中我们预先创建这些动画并缓存起来避免在游戏运行时重复创建影响性能。bool Player::init() { if (!Sprite::initWithFile(player/run_1.png)) { // 用第一帧初始化 return false; } // 创建奔跑动画 VectorSpriteFrame* runFrames; for (int i 1; i 8; i) { auto frame SpriteFrameCache::getInstance()-getSpriteFrameByName( StringUtils::format(run_%d.png, i)); if (frame) { runFrames.pushBack(frame); } } auto runAnimation Animation::createWithSpriteFrames(runFrames, 0.1f); // 每帧0.1秒 AnimationCache::getInstance()-addAnimation(runAnimation, run); // 类似地创建跳跃、下滑动画... _currentState State::RUNNING; this-runAction(RepeatForever::create(Animate::create(runAnimation))); return true; }接下来是最关键的控制逻辑。我们通过监听键盘事件桌面调试和触摸事件移动设备来控制角色。对于跑酷游戏常见的操作是点击屏幕左侧区域跳跃点击右侧区域下滑。我们在GameScene的init()方法中设置触摸监听器auto touchListener EventListenerTouchOneByOne::create(); touchListener-onTouchBegan [this](Touch* touch, Event* event) - bool { auto location touch-getLocation(); // 假设屏幕宽度为 visibleSize.width if (location.x visibleSize.width / 2) { // 点击左半屏触发跳跃 _player-jump(); } else { // 点击右半屏触发下滑 _player-slide(); } return true; }; _eventDispatcher-addEventListenerWithSceneGraphPriority(touchListener, this);在Player::jump()方法中我们需要处理跳跃的物理逻辑。一个简单的模拟方法是给角色一个向上的初始速度然后在每帧更新中施加一个向下的重力加速度直到角色落回地面。void Player::jump() { if (_currentState ! State::RUNNING _currentState ! State::SLIDING) { return; // 只有在奔跑或下滑结束时才能起跳 } _currentState State::JUMPING; _velocityY 450.0f; // 初始跳跃速度像素/秒 _isOnGround false; // 切换为跳跃动画 this-stopAllActions(); this-runAction(_jumpAnimateAction); }然后在Player的update(float dt)方法中这个方法需要被定时调度我们根据状态更新角色的位置void Player::update(float dt) { switch (_currentState) { case State::JUMPING: _velocityY - GRAVITY * dt; // GRAVITY 是重力加速度比如 980.0f this-setPositionY(this-getPositionY() _velocityY * dt); // 检测是否落地 if (this-getPositionY() _groundHeight) { this-setPositionY(_groundHeight); _isOnGround true; _currentState State::RUNNING; // 切换回奔跑动画 this-stopAllActions(); this-runAction(_runAnimateAction); } break; // ... 处理其他状态 } }实操心得跳跃手感是跑酷游戏的核心。_velocityY和GRAVITY的数值需要反复调试。数值太大会让跳跃感觉“飘”太小则感觉“沉”。一个技巧是参考现实世界的物理比例但通常需要夸张一些。可以先设定一个目标跳跃高度比如200像素和跳跃持续时间比如0.6秒然后反推出合适的初速度和重力值。另外允许玩家在跳跃途中再次点击进行二段跳是提升操作爽快感的常见设计可以在jump()方法中加入对_isDoubleJumpUsed标志位的判断来实现。4. 关卡逻辑与障碍物系统4.1 设计可扩展的障碍物与道具一个有趣的跑酷游戏障碍物和道具的种类必须丰富。我们可以设计几种基础类型地面障碍矮墙、钉子板。需要跳跃越过。空中障碍摆锤、飞鸟。需要把握时机跳跃或下滑通过。陷阱地刺、落穴。需要提前预判。道具金币加分、护盾短暂无敌、磁铁自动吸金币、冲刺加速。为了实现这种多样性我们最好采用面向对象的设计定义一个基类GameObject然后派生出Obstacle和Item等子类。每个游戏对象都应该有类型标识、碰撞体积、分数价值等属性以及spawn(),update(),collideWithPlayer()等方法。class GameObject : public cocos2d::Node { public: enum class Type { OBSTACLE_LOW, // 低障碍 OBSTACLE_HIGH, // 高障碍 TRAP, COIN, SHIELD, MAGNET, SPEED_UP }; virtual void spawn(float startX); // 在指定X坐标生成 virtual void update(float dt); // 每帧更新移动 virtual bool checkCollision(Player* player); // 检测与玩家碰撞 // ... protected: Type _type; cocos2d::Rect _collisionBox; int _scoreValue; };在GameScene中我们维护一个游戏对象池VectorGameObject*。对象池是游戏开发中一个非常重要的优化技巧。与其不停地new和delete对象这会产生内存碎片并影响性能不如在游戏初始化时就创建一定数量的对象放入池中需要时从池中取出并激活不用时放回池中并隐藏。对于跑酷游戏这种对象频繁生成和消失的场景对象池能极大提升性能。4.2 实现无限关卡与动态难度跑酷游戏的关卡本质上是障碍物和道具的序列。我们不可能手动设计无限长的序列因此需要一套算法来动态生成。一个经典的方法是使用“节拍”和“模板”。首先我们定义游戏的基本“节拍”或“单元”。例如每 100 像素可以看作一个单元。在每个单元上我们可以放置一种或多种游戏对象也可以什么都不放。我们可以预先设计一些“模板”每个模板描述了在连续几个单元内如何放置对象。例如一个“简单跳跃”模板可能是在第1个单元放一个矮障碍第3个单元放一个金币。struct SpawnTemplate { std::string name; int lengthInUnits; // 模板占几个单元 std::vectorstd::pairint, GameObject::Type placements; // (单元偏移量, 对象类型) int weight; // 模板被随机选中的权重 };在游戏运行时我们维护一个“已生成距离”和“当前关卡长度”。在每帧更新中检查屏幕最右侧之外还有多少“预备区域”比如200像素。如果预备区域空了就根据当前游戏难度比如玩家跑了多远、当前分数从模板库中随机选取一个模板计算其实际像素位置然后从对象池中取出对应的游戏对象调用其spawn()方法放置在场景中并将其加入活动对象列表。动态难度可以通过调整模板库的权重来实现。游戏开始时“简单”模板的权重大“复杂”模板权重小。随着玩家跑动距离增加我们逐渐降低简单模板的权重提高复杂模板的权重甚至引入新的、更难的模板。同时生成对象的间隔即单元长度也可以逐渐缩短让障碍物出现得更密集。void GameScene::spawnNextSegment() { float currentDifficulty calculateDifficulty(); // 根据距离、分数计算难度系数 SpawnTemplate* chosenTemplate chooseTemplateByWeight(currentDifficulty); float spawnStartX _lastSpawnX; for (auto placement : chosenTemplate-placements) { float objX spawnStartX placement.first * UNIT_LENGTH; GameObject* obj _objectPool.getObject(placement.second); // 从对象池获取 if (obj) { obj-spawn(objX); _activeObjects.pushBack(obj); _gameLayer-addChild(obj); } } _lastSpawnX chosenTemplate-lengthInUnits * UNIT_LENGTH; }注意事项动态生成的关卡一定要保证“可玩性”。这意味着生成的障碍物序列必须给玩家合理的反应时间和通过路径。不能生成“死局”比如两个必须跳跃的障碍物之间距离小于角色跳跃跨度导致无论如何都会撞上。在模板设计时就要考虑这一点或者可以在生成后增加一个“可行性校验”步骤对极端不合理的组合进行微调。5. 碰撞检测、分数与游戏状态管理5.1 高效的碰撞检测实现碰撞检测是游戏逻辑的基石。对于2D横版游戏我们通常使用轴对齐包围盒AABB进行粗略检测因为它计算速度快。Cocos2d-x 的Rect类提供了intersectsRect()方法可以方便地判断两个矩形是否相交。我们在GameObject基类中定义了一个_collisionBox成员变量它表示对象相对于其自身锚点的碰撞区域。在Player类中也定义一个碰撞框。每一帧在GameScene的更新函数中我们遍历所有活动的游戏对象计算它们在世界坐标系下的碰撞框并与玩家的碰撞框进行检测。void GameScene::checkCollisions(float dt) { auto playerBox _player-getCollisionBox(); // 将玩家碰撞框转换到世界坐标系 playerBox.origin _player-getPosition(); for (auto obj : _activeObjects) { auto objBox obj-getCollisionBox(); objBox.origin obj-getPosition(); if (playerBox.intersectsRect(objBox)) { // 发生碰撞 handleCollision(_player, obj); // 通常碰撞后该对象应被回收 recycleObject(obj); break; // 一帧内通常只处理一次碰撞 } } }但是逐帧遍历所有对象进行两两检测当对象数量很多时比如上百个金币性能会成为问题。这里就需要引入空间划分优化。对于横版跑酷这种对象基本沿X轴线性分布的游戏一个简单有效的优化是使用“标记-清理”法。我们给每个游戏对象增加一个_isActive标记。在更新对象位置时如果发现对象的X坐标已经远小于屏幕左边界即完全移出屏幕就将其标记为不活跃并从活动列表移到待回收列表。这样碰撞检测只需要遍历活动列表而这个列表的长度会被控制在屏幕内对象数量的规模通常不会很大。另一个优化是针对不同碰撞类型的分层检测。比如玩家与金币的碰撞收集和与障碍物的碰撞受伤可以分开处理。我们可以先快速检测可能与玩家发生“危险碰撞”的对象如障碍物、陷阱这些对象数量较少。如果没发生危险碰撞再检测“收集类”对象如金币。这可以通过给对象设置碰撞层级Collision Layer来实现。5.2 构建完整的游戏循环与UI反馈游戏需要清晰的状态管理开始前READY、进行中PLAYING、暂停PAUSED、结束GAME_OVER。我们在GameScene中用一个枚举变量_gameState来记录当前状态。状态的切换会触发一系列动作比如从 READY 进入 PLAYING 时开始滚动背景、开始生成障碍物、启动分数计时等。分数系统是驱动玩家不断挑战的核心。我们的分数可以由几部分组成距离分每跑过一定像素如10像素加1分。这通过一个定时器或根据背景滚动速度来累加。金币分每收集一个金币加固定分数如100分。连击分/表现分连续收集金币而不碰到障碍可以获得递增的额外奖励分数。分数需要实时显示在屏幕上。我们在_uiLayer上添加一个Label对象来显示分数。为了性能不要每帧都更新 Label 的字符串特别是分数很高时数字转换有开销。可以设置一个分数变量_currentScore一个显示分数变量_displayScore。在每帧更新中让_displayScore以一定的速度向_currentScore平滑过渡插值然后每隔几帧或当分数变化较大时再更新 Label 的文本。void GameScene::updateScore(float dt) { // 增加距离分 _currentScore _scrollSpeed * dt * SCORE_PER_PIXEL; // 平滑显示分数 _displayScore lerp(_displayScore, _currentScore, 0.1f); // lerp是线性插值函数 if (fabs(_displayScore - _lastDisplayScore) 10) { // 分数变化超过10再更新文本 _scoreLabel-setString(StringUtils::format(Score: %d, (int)_displayScore)); _lastDisplayScore _displayScore; } }生命值或护盾系统增加了游戏的容错率和策略性。我们可以给玩家设定初始生命值为3。每次与障碍物发生碰撞生命值减1。当生命值为0时游戏结束。如果玩家获得了“护盾”道具则进入短暂的无敌状态期间碰撞不会扣减生命值。无敌状态可以用一个定时器和一个视觉特效比如角色半透明闪烁来表现。游戏结束的逻辑需要处理好。当玩家生命值为0或掉入深渊时触发gameOver()方法。这个方法里需要做几件事将游戏状态改为 GAME_OVER。停止所有背景滚动、对象生成和自动分数增加。播放角色死亡动画和音效。弹出游戏结束界面显示本次最终分数、历史最高分并提供“重新开始”和“返回主菜单”按钮。重新开始游戏时切忌简单地重新加载整个场景因为这样会有黑屏延迟。更好的做法是重置所有游戏数据将玩家位置、生命值、分数归零回收所有活跃的游戏对象回对象池重置关卡生成器的种子或位置然后将游戏状态切回 READY 或直接切回 PLAYING。这样体验会更加流畅。6. 性能优化与发布准备6.1 渲染与内存优化实战当游戏内容逐渐丰富特效和对象增多时性能问题就会浮现。Cocos2d-x 提供了强大的性能分析工具Profiler在调试模式下可以通过Director::getInstance()-setDisplayStats(true);在屏幕角落显示帧率FPS、绘制调用次数Draw Calls等信息。Draw Calls 是衡量渲染性能的关键指标次数越少越好。优化 Draw Calls 的核心是“合批”Batching。对于大量静态或变化不频繁的精灵比如背景、远处的装饰物如果它们使用同一张纹理图集Texture AtlasCocos2d-x 的渲染器会自动将它们合并到一个 Draw Call 中。因此将游戏中的小图片打包成图集是必须的步骤。可以使用 TexturePacker 等工具来打包生成一个.plist文件描述信息和一个.png文件合并后的图片。在游戏启动时用SpriteFrameCache::getInstance()-addSpriteFramesWithFile(game.plist);加载图集之后创建精灵时使用帧名而非单独的文件名。// 优化前每个精灵单独加载文件产生多个Draw Call auto sprite1 Sprite::create(coin1.png); auto sprite2 Sprite::create(coin2.png); // 优化后使用图集多个精灵可能共享一个Draw Call SpriteFrameCache::getInstance()-addSpriteFramesWithFile(game-assets.plist); auto sprite1 Sprite::createWithSpriteFrameName(coin_01.png); auto sprite2 Sprite::createWithSpriteFrameName(coin_02.png);对象池我们已经提过了它对性能提升至关重要。特别是对于频繁创建销毁的对象如子弹、特效、金币。另一个内存优化点是纹理的延迟加载和及时卸载。不要在游戏一开始就加载所有资源而是根据场景需要加载。当切换场景比如从游戏场景回到主菜单时要记得卸载不再使用的纹理和精灵帧调用TextureCache::getInstance()-removeUnusedTextures();和SpriteFrameCache::getInstance()-removeUnusedSpriteFrames();。对于移动设备还要注意电量消耗。减少不必要的每帧逻辑。如果某个节点暂时不需要更新可以调用node-unscheduleUpdate();来停止它的更新回调。对于远离屏幕、对游戏没有影响的对象比如已经滚到屏幕左边很远的背景板可以直接将其设置为不可见setVisible(false)这样渲染器会跳过它。6.2 多平台适配与打包发布Cocos2d-x 最大的优势之一就是跨平台。我们的代码在桌面Windows/Mac上调试完成后需要编译到真机iOS/Android上进行测试和发布。对于 Android 平台进入项目的proj.android目录使用 Gradle 进行编译。首先确保你的系统已经配置好了 Android SDK 和 NDK版本需要与proj.android/app/build.cfg或gradle.properties中的配置匹配。然后在项目根目录或proj.android目录下执行cocos compile -p android --android-studio--android-studio参数会生成一个可用于 Android Studio 打开的工程方便进行更详细的配置和调试。编译成功后会在proj.android/app/build/outputs/apk/目录下生成 APK 文件。在将 APK 安装到真机前需要处理签名问题。调试版本可以使用调试密钥发布版本则必须使用你自己生成的密钥库进行签名。对于 iOS 平台进入proj.ios目录你会看到一个.xcodeproj文件。用 Xcode 打开它。你需要有一个有效的 Apple 开发者账号并在 Xcode 中设置好你的团队Team和 Bundle Identifier。在真机上运行前需要在苹果开发者网站为你的设备创建配置文件Provisioning Profile。相比 AndroidiOS 的打包流程对证书和配置的依赖更强需要仔细按照苹果官方的步骤进行。避坑指南跨平台开发时要特别注意文件路径和大小写问题。Windows 不区分大小写而 Linux 和 Mac 是区分的。所有资源文件的引用在代码中最好使用统一的小写并且使用相对路径。另外移动设备的屏幕尺寸和长宽比千差万别我们的游戏需要做多分辨率适配。不要在代码中写死位置坐标如setPosition(400, 300)而应该使用相对坐标。Cocos2d-x 提供了Director::getInstance()-getVisibleSize()和getVisibleOrigin()来获取可视区域的大小和起点。所有 UI 元素和游戏布局都应该基于这两个值来计算位置例如将角色放在屏幕水平中央、底部向上 20% 的位置player-setPosition(visibleSize.width/2, visibleSize.height*0.2)。对于背景图等资源可能需要准备不同分辨率的版本如 sd, hd, xhd并通过FileUtils的搜索路径机制来自动加载合适尺寸的图片。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价