简介:这是一份面向计算机、软件工程及人工智能相关专业师生的Cocos Creator v2.0二维闯关安卓游戏开发教学资源,可作为课程实践、毕业设计或项目实训的参考案例,帮助学习者理解移动端游戏开发流程,掌握二维场景渲染、物理引擎集成与交互逻辑设计等关键技术。资源包共375个文件,约8.93MB,以png图片素材、meta配置、js脚本、anim动画、prefab预制体、fire场景及mp3音效为主,另含docx系统开发说明、pptx课件与mp4演示视频,覆盖从素材到逻辑的完整工程结构。已有39人学习。具备编程基础者可在现有框架上扩展自定义关卡或优化角色行为,建议优先查阅指南文件了解环境配置要求,仅限教育科研用途。
1. 基于Cocos Creator v2.0的2D闯关安卓游戏:从课程设计到真机跑通
很多同学做课程设计时,选题一写“基于 Cocos Creator 的 2D 闯关安卓游戏”,脑子里想的是超级马里奥,手上做出来的却是一个方块在空白背景上左右横跳。问题不在引擎,在于没人告诉你从“能跑”到“像个游戏”之间到底缺了哪几块。Cocos Creator v2.0 这个版本虽然不算新,但它的组件化工作流、场景编辑器和一键打包 APK 的能力,放到课程设计场景里依然是最省事的组合。这篇文章面向的是需要在一到两周内交出一个可演示、可安装、有完整关卡流程的安卓游戏的学生或初级开发者。我会按实际做项目的顺序,把场景搭建、角色控制、关卡切换、碰撞检测、安卓打包这条链路拆开讲,每一步都给出能直接抄的代码和参数。你不需要先学完整套引擎文档,跟着走就能把 APK 装到手机上。
2. 场景搭建与节点组织:为什么你的层级一乱就全乱
2.1 先定层级再拖素材,别反过来
Cocos Creator v2.0 的场景编辑器是所见即所得的,很多人一上来就把背景图、角色图、地面图全拖进 Canvas 下面,结果节点列表长得像购物清单,改一个东西要找半天。正确的做法是先想清楚这个 2D 闯关游戏有哪些逻辑层,再按层建空节点,最后把美术资源挂到对应层下面。
一个典型的 2D 横版闯关游戏,场景层级至少分四层:背景层(Background)、地形层(Terrain)、角色层(Character)、UI 层(UI)。背景层放远景和视差图,地形层放地面、平台、障碍物,角色层放玩家和敌人,UI 层放血条、分数、暂停按钮。这样做的好处是,后面做摄像机跟随的时候,你只需要移动背景层和地形层的父节点,角色层和 UI 层不受影响。
在 Cocos Creator v2.0 里,Canvas 节点默认带一个 Widget 组件做屏幕适配。课程设计里最常见的适配方案是 Fit Height 模式,设计分辨率设成 960×640 或者 1280×720。如果你做的是竖屏闯关,就改成 Fit Width。这个参数在 Canvas 的 Widget 组件里改,不要手动去调节点位置,否则换一台手机就偏了。
节点命名也要有规矩。不要出现“Node”“Node1”“Sprite2”这种名字。用英文加下划线,比如 bg_far、bg_near、terrain_ground、player、enemy_slime、ui_hp_bar。后面写代码的时候,this.node.getChildByName('player')这种调用才不会写错。
2.2 用 Prefab 管敌人和道具,别在场景里复制粘贴
课程设计里经常需要放十几个同样的敌人或者金币。如果你直接在场景里复制粘贴,改一个敌人的属性就要改十几遍。Cocos Creator v2.0 的 Prefab 系统就是解决这个问题的。
做法是:在场景里做一个标准的敌人节点,挂好 Sprite、Collider、脚本,调好参数,然后把它从层级管理器拖到资源管理器的 assets/prefabs 文件夹里。这时候场景里的那个节点会变成蓝色,说明它已经是一个 Prefab 实例。之后你要放新敌人,直接从资源管理器把 Prefab 拖进场景就行。
代码里动态生成敌人也用 Prefab:
// EnemySpawner.js cc.Class({ extends: cc.Component, properties: { enemyPrefab: { default: null, type: cc.Prefab }, spawnInterval: 2.0, // 生成间隔,单位秒 maxEnemies: 5 // 场上最大敌人数 }, onLoad: function () { this.enemyCount = 0; this.schedule(this.spawnEnemy, this.spawnInterval); }, spawnEnemy: function () { if (this.enemyCount >= this.maxEnemies) return; var enemy = cc.instantiate(this.enemyPrefab); // 挂到当前节点下,保持层级整洁 this.node.addChild(enemy); // 随机放在屏幕右侧外 enemy.setPosition(cc.winSize.width / 2 + 100, -200); this.enemyCount++; } });这段代码的逻辑是:每隔 spawnInterval 秒检查一次场上敌人数量,没超过上限就在屏幕右侧外生成一个。cc.instantiate是 v2.0 里复制 Prefab 的标准方法,返回一个节点对象。this.node.addChild把它挂到当前节点下,这样敌人会跟着当前节点一起移动或销毁。参数方面,spawnInterval 设 2 到 3 秒比较合适,太快了手机卡,太慢了玩家觉得无聊。maxEnemies 根据你的屏幕宽度和敌人大小调,一般 5 到 8 个。
注意:Prefab 实例化出来的节点,它的脚本 onLoad 会在 addChild 之后才执行。如果你在 spawnEnemy 里立刻去取 enemy 上的组件,要确保组件已经挂载。
3. 角色控制与物理碰撞:让方块像人一样走路
3.1 用 cc.PhysicsBoxCollider 还是自己写 AABB
Cocos Creator v2.0 内置了 Box2D 物理引擎,但很多课程设计项目其实用不上完整的物理模拟。如果你只是做平台跳跃,角色踩地面、撞墙、吃金币,自己写 AABB(轴对齐包围盒)检测反而更轻量、更可控。
我一般会这样选:如果游戏里有斜坡、弹跳、重力加速度变化,用物理引擎;如果只是矩形平台和矩形角色,自己写。课程设计里 90% 的情况是后者。
自己写碰撞的核心是每帧检测两个矩形是否相交:
// PlayerController.js cc.Class({ extends: cc.Component, properties: { moveSpeed: 300, // 水平移动速度,像素/秒 jumpSpeed: 600, // 跳跃初速度,像素/秒 gravity: -1500, // 重力加速度,像素/秒² groundY: -200 // 地面的 Y 坐标 }, onLoad: function () { this.vx = 0; this.vy = 0; this.isOnGround = true; // 注册键盘事件 cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this); cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this); }, onKeyDown: function (event) { switch (event.keyCode) { case cc.KEY.left: this.vx = -this.moveSpeed; break; case cc.KEY.right: this.vx = this.moveSpeed; break; case cc.KEY.up: if (this.isOnGround) { this.vy = this.jumpSpeed; this.isOnGround = false; } break; } }, onKeyUp: function (event) { if (event.keyCode === cc.KEY.left || event.keyCode === cc.KEY.right) { this.vx = 0; } }, update: function (dt) { // 应用重力 this.vy += this.gravity * dt; // 更新位置 var pos = this.node.getPosition(); pos.x += this.vx * dt; pos.y += this.vy * dt; // 地面碰撞检测 if (pos.y <= this.groundY) { pos.y = this.groundY; this.vy = 0; this.isOnGround = true; } this.node.setPosition(pos); } });这段代码里,dt是每帧的时间增量,Cocos Creator 的 update 回调会自动传入。this.vy += this.gravity * dt是模拟重力加速度,gravity 设负值表示向下。pos.y <= this.groundY是简单的地面检测,实际项目里你会用多个平台的 Y 坐标来判断。参数方面,moveSpeed 300 在 960 宽的设计分辨率下大概 3 秒穿过屏幕,手感适中。jumpSpeed 600 配合 gravity -1500,跳跃高度大约是 120 像素,能跳过两个角色身高的障碍。
3.2 用碰撞分组管理“谁能撞谁”
Cocos Creator v2.0 的碰撞系统有分组概念。你可以在项目设置里定义分组,比如 player、enemy、coin、platform。然后在 Collider 组件上勾选 Group 和 Mask,决定这个节点和哪些组发生碰撞。
这个机制在闯关游戏里非常有用。比如玩家碰到金币要加分,碰到敌人要扣血,碰到平台要停住。如果你不用分组,就要在碰撞回调里写一堆 if-else 判断节点名字,代码又丑又容易漏。
设置方法:项目设置 → 分组管理 → 添加分组。然后在每个 Collider 组件的 Group 下拉框选自己的组,Mask 勾选要检测的组。代码里用onCollisionEnter回调:
// PlayerCollision.js cc.Class({ extends: cc.Component, properties: { hp: 3, score: 0 }, onCollisionEnter: function (other, self) { var group = other.node.group; if (group === 'coin') { this.score += 10; other.node.destroy(); // 吃掉金币 } else if (group === 'enemy') { this.hp -= 1; if (this.hp <= 0) { this.gameOver(); } } }, gameOver: function () { // 切换场景或弹出结束界面 cc.director.loadScene('GameOver'); } });other是碰撞到的另一个节点,self是自己。other.node.group返回分组名字符串。other.node.destroy()会销毁金币节点,注意销毁后不要再去访问它的属性。hp 和 score 这种参数,课程设计里设 3 条命、金币 10 分比较常见,你可以根据关卡长度调。
提示:碰撞回调只在两个节点的 Collider 都启用且分组匹配时触发。如果你发现碰撞没反应,先检查分组设置,再检查 Collider 的 Size 是不是太小。
4. 关卡切换与数据持久化:别让玩家每次重开都从第一关开始
4.1 用 cc.director.loadScene 做关卡切换
Cocos Creator v2.0 的场景切换很简单,cc.director.loadScene('Level2')就能加载下一个场景。但课程设计里经常遇到一个问题:玩家过了第一关,第二关加载了,但分数和生命值没了。这是因为场景切换会销毁当前场景的所有节点,包括你存在节点上的数据。
解决办法是用一个全局常驻节点来存数据。在游戏启动场景里创建一个节点,挂上cc.game.addPersistRootNode(this.node),这个节点就不会被场景切换销毁。
// GameData.js cc.Class({ extends: cc.Component, onLoad: function () { // 防止重复创建 if (cc.game.__gameData) { this.node.destroy(); return; } cc.game.__gameData = this; cc.game.addPersistRootNode(this.node); // 初始化数据 this.score = 0; this.hp = 3; this.currentLevel = 1; }, addScore: function (value) { this.score += value; }, resetData: function () { this.score = 0; this.hp = 3; this.currentLevel = 1; } });cc.game.addPersistRootNode是 v2.0 里让节点跨场景保留的 API。cc.game.__gameData是一个挂在全局对象上的引用,方便其他脚本通过cc.game.__gameData.addScore(10)来调用。注意在 onLoad 里先判断是否已经存在,避免从第二关返回第一关时重复创建。
关卡切换的代码:
// LevelManager.js cc.Class({ extends: cc.Component, properties: { nextLevelName: '' // 在编辑器里填下一关的场景名 }, goToNextLevel: function () { var gameData = cc.game.__gameData; gameData.currentLevel++; cc.director.loadScene(this.nextLevelName); } });nextLevelName在编辑器属性面板里填,比如 Level2 场景就填 'Level2'。这样每个关卡场景挂一个 LevelManager,到终点触发 goToNextLevel 就行。
4.2 用 localStorage 存最高分和关卡进度
课程设计答辩的时候,老师经常会问“你的数据能不能存下来”。Cocos Creator v2.0 支持cc.sys.localStorage,在安卓和浏览器上都能用。
// SaveManager.js var SaveManager = { save: function (key, value) { cc.sys.localStorage.setItem(key, JSON.stringify(value)); }, load: function (key, defaultValue) { var str = cc.sys.localStorage.getItem(key); if (str) { return JSON.parse(str); } return defaultValue; } }; module.exports = SaveManager;用法是在 GameData 的 onLoad 里读,在 addScore 和关卡切换时写:
// 在 GameData.js 里 onLoad: function () { // ... 前面的常驻节点逻辑 this.highScore = SaveManager.load('highScore', 0); }, addScore: function (value) { this.score += value; if (this.score > this.highScore) { this.highScore = this.score; SaveManager.save('highScore', this.highScore); } }cc.sys.localStorage在安卓上对应的是 SharedPreferences,在浏览器上对应 localStorage。存的数据类型是字符串,所以用 JSON.stringify 和 JSON.parse 做序列化。key 用英文,比如 'highScore'、'levelProgress',不要用中文。
注意:localStorage 在安卓真机上需要应用有存储权限,Cocos Creator 打包时会自动处理。如果你在浏览器预览时存了数据,换到手机上是读不到的,因为存储位置不同。
5. 安卓打包与真机调试:从构建到安装的完整链路
5.1 构建 APK 前必须检查的四个配置
Cocos Creator v2.0 打包安卓的入口在菜单栏的“项目 → 构建发布”。选 Android 平台,填包名,选 API Level,然后点构建。但直接点构建大概率会报错,因为有几个配置必须先改。
第一个是包名。格式是 com.你的名字.游戏名,比如 com.zhangsan.platformer。不要用 com.example,有些应用商店会拒。
第二个是 Target API Level。课程设计里选 Android 5.0 以上就行,对应 API 21。太低了有些新手机装不上,太高了老手机跑不了。
第三个是屏幕方向。2D 横版闯关选 Landscape,竖屏选 Portrait。这个在构建面板的“方向”里勾。
第四个是渲染模式。v2.0 默认用 WebGL,但有些老安卓机 WebGL 支持不好。如果你发现真机上黑屏,在构建面板里把渲染模式改成 Canvas,兼容性更好但性能差一点。
构建完成后,Cocos Creator 会在 build 文件夹下生成一个 android 工程。用 Android Studio 打开这个工程,连上手机,点运行就能装到手机上。如果你没有 Android Studio,也可以用命令行:
cd build/android ./gradlew assembleDebug生成的 APK 在 build/android/app/build/outputs/apk/debug/ 下面。用数据线传到手机,在文件管理器里点安装。第一次安装需要在手机设置里允许“安装未知来源应用”。
5.2 真机调试用 cc.log 和 Chrome 远程调试
游戏在电脑上跑得好好的,装到手机上就闪退或者卡住,这是课程设计里最常见的翻车现场。调试方法有两个。
第一个是在代码里打日志,用cc.log('player pos: ' + this.node.x + ',' + this.node.y)。然后在 Android Studio 的 Logcat 里过滤 cocos 关键字,就能看到输出。注意 cc.log 在发布版本里会被去掉,调试版本才有。
第二个是用 Chrome 远程调试。手机用 USB 连电脑,打开游戏的调试版本,然后在 Chrome 地址栏输入 chrome://inspect,找到你的设备,点 inspect 就能像调网页一样调游戏。这个功能在 Cocos Creator v2.0 的 WebGL 渲染模式下可用,Canvas 模式不支持。
真机上最常见的三个问题:一是资源加载失败,通常是路径大小写问题,Windows 不区分大小写但安卓区分;二是触摸事件偏移,通常是 Canvas 的适配模式没设对;三是帧率低,通常是同屏节点太多或者用了太多透明图片。资源路径统一用小写,适配模式用 Fit Height 或 Fit Width,同屏节点控制在 50 个以内,基本能解决。
提示:每次改完代码重新构建 APK 很慢,可以先用浏览器预览调好逻辑,最后再打包。浏览器预览用 Chrome 的开发者工具,和真机调试体验接近。
6. 用对象池优化金币和子弹:一个让帧率翻倍的技巧
课程设计做到后期,你可能会发现同屏金币一多,手机就开始发烫、掉帧。这不是手机不行,是你在频繁地创建和销毁节点。Cocos Creator v2.0 提供了cc.NodePool对象池组件,专门解决这个问题。
对象池的思路是:金币被吃掉的时候不销毁,而是放回池子里;需要新金币的时候从池子里取,而不是重新 instantiate。这样避免了频繁的内存分配和垃圾回收。
// CoinPool.js cc.Class({ extends: cc.Component, properties: { coinPrefab: { default: null, type: cc.Prefab }, poolSize: 20 // 池子初始大小 }, onLoad: function () { this.coinPool = new cc.NodePool('CoinPool'); for (var i = 0; i < this.poolSize; i++) { var coin = cc.instantiate(this.coinPrefab); this.coinPool.put(coin); } }, getCoin: function () { var coin = null; if (this.coinPool.size() > 0) { coin = this.coinPool.get(); } else { coin = cc.instantiate(this.coinPrefab); } return coin; }, recycleCoin: function (coin) { this.coinPool.put(coin); } });new cc.NodePool('CoinPool')创建一个对象池,参数是池子的名字,用于调试时区分。pool.put(coin)把节点放回池子,节点会被自动从父节点移除并设为不可见。pool.get()从池子里取一个节点,如果池子空了会返回 null,所以代码里做了判断,空了就重新 instantiate。
在金币的碰撞回调里,不要用destroy(),改成调用对象池的 recycleCoin:
// Coin.js cc.Class({ extends: cc.Component, properties: { coinPool: { default: null, type: cc.Node } }, onCollisionEnter: function (other, self) { if (other.node.group === 'player') { // 加分逻辑... var pool = this.coinPool.getComponent('CoinPool'); pool.recycleCoin(this.node); } } });参数方面,poolSize 设成同屏最大金币数的 1.5 倍比较合适。比如一关最多同时出现 10 个金币,池子设 15 到 20。太小了会频繁 instantiate,太大了浪费内存。
我自己的习惯是,任何会反复出现的节点——金币、子弹、敌人、特效——都走对象池。这个习惯让我的课程设计在红米 Note 这种千元机上也能稳定 60 帧。对象池的坑在于,放回池子的节点如果还挂着定时器或者动画,要先停掉,否则取出来的时候状态是乱的。我一般会在 recycle 之前调coin.stopAllActions()和coin.unscheduleAllCallbacks()。
希望帮到你。
本文还有配套的精品资源,点击获取