绿泡泡、抖某音小游戏一份工程都能出包。摘要Cocos Creator 是一款开源的 2D/3D 游戏引擎用 TypeScript 写逻辑一次开发可发布到手机、桌面、网页和各家小游戏平台。本文从安装讲到脚本、资源、打包再到性能与版本选择。它是什么先理清一条脉络不然看资料容易搞混Cocos2d-x2010 年发布的 C 老引擎官方手册写明 2019 年已停止更新。Cocos Creator 2.x2018 年发布底层沿用 Cocos2d-x2023 年停止更新。Cocos Creator 3.x完全放弃 Cocos2d-x 底层换成全新的跨平台 3D 内核与 2.x 的 API 并不完全兼容。2026 年 1 月官方又推出了 COCOS 4引擎与新编辑器 PinK 拆分但其小游戏平台支持仍在陆续补齐所以目标是小游戏的新项目目前仍建议选 3.8 系列。引擎本体在 GitHub 上以 MIT 协议开源底层与渲染用 C面向开发者的 API 是 TypeScript编辑器基于 ElectronWindows 和 macOS 都能用。核心优势1. 发布目标多而且覆盖国内入口。官方手册列出的发布目标包括原生iOS、Android、Windows、macOS、华为鸿蒙、Google Play 等网页Web MobileH5与 Web Desktop小游戏绿泡泡小游戏、抖某音小游戏、OPPO、vivo、荣耀、华为快游戏以及海外社交平台的 Instant Games另外官方文档侧边栏里还能看到电商、支付和搜索类 App 的小游戏入口。OPPO、vivo、荣耀、华为的快游戏渠道都有对应的构建选项这是它在国内生态里的实际价值同一份工程切换构建目标就能出对应平台的包各渠道的适配和审核仍要单独处理。2. 2D 和 3D 都能做。同一个编辑器里动画、物理、粒子、UI、地形编辑和即时预览都是内置的Spine、DragonBones、TiledMap、Box2D 也有现成支持。3D 方面提供可扩展的前向与延迟渲染管线以及基于物理的材质与光照。3. 脚本语言友好。JavaScript 和 TypeScript 可以混用组件化写法和 Unity、Godot 思路相近有前端或这类引擎经验的人上手较快。4. 对小游戏的包体很上心。3.8.5 起支持 WASM 模块延迟加载Spine 模块体积比 3.8.4 缩小约 40%3.8.6 继续优化包体和性能。小游戏有主包大小限制这类改动直接决定能不能上线。面向人群想做小游戏、轻量手游的个人和小团队有 TypeScript 或前端基础想转游戏开发的人需要一次开发、多渠道分发的项目做过 Godot、Unity想了解国内常用方案的人如果你要做大型 3A 级 3D 项目或需要非常深的主机平台支持Cocos 不是首选。快速上手安装从官网下载 Cocos Dashboard在里面安装 Creator 3.8 系列的编辑器版本再新建项目模板选 2D 或 3D 空项目。认识四个核心概念场景Scene一个关卡或一个界面节点Node场景里的一切对象组件Component挂在节点上的功能脚本也是组件资源Asset图片、音频、预制体等写第一个脚本在资源管理器里新建 TypeScript 脚本拖到节点上。import{_decorator,Component}fromcc;const{ccclass,property}_decorator;ccclass(Mover)exportclassMoverextendsComponent{propertyspeed100;start(){// 第一帧更新之前执行}update(dt:number){constpthis.node.position;this.node.setPosition(p.xthis.speed*dt,p.y,p.z);}}property声明的变量会出现在属性检查器里策划不改代码也能调数值。onLoad、start、update是最常用的三个生命周期回调update的参数是上一帧耗时移动要乘上它才不会因为帧率不同而快慢不一。预览点编辑器顶部的预览按钮直接在浏览器或模拟器里跑。用法详解做一个打飞机小游戏下面用「打飞机」这个小游戏当线索把常用功能一个个串起来。每个案例都是独立的组件挂到对应节点上即可。案例一键盘与触摸控制玩家输入统一走input。在onEnable里注册onDisable里取消参数必须和注册时一致否则取消不掉节点销毁后还会收到回调。import{_decorator,Component,input,Input,EventKeyboard,EventTouch,KeyCode}fromcc;const{ccclass}_decorator;ccclass(PlayerInput)exportclassPlayerInputextendsComponent{onEnable(){input.on(Input.EventType.KEY_DOWN,this.onKeyDown,this);input.on(Input.EventType.TOUCH_MOVE,this.onTouchMove,this);}onDisable(){input.off(Input.EventType.KEY_DOWN,this.onKeyDown,this);input.off(Input.EventType.TOUCH_MOVE,this.onTouchMove,this);}onKeyDown(e:EventKeyboard){if(e.keyCodeKeyCode.KEY_A){/* 向左 */}if(e.keyCodeKeyCode.KEY_D){/* 向右 */}}onTouchMove(e:EventTouch){// 手指位置用 UI 坐标可以直接对应界面上的位置constpose.getUILocation();console.log(pos.x,pos.y);}}做小游戏时以触摸为主键盘只是方便在电脑上调试。案例二用缓动做动画不用写 update按钮缩放、飘字、移动这类一次性动画用缓动tween比自己在update里算更省事import{tween,Vec3}fromcc;// 受击时放大再复原然后执行回调tween(this.node).to(0.1,{scale:newVec3(1.2,1.2,1)}).to(0.1,{scale:newVec3(1,1,1)}).call(()console.log(动画结束)).start();to是变到目标值by是在当前值上增加还可以用sequence串联、repeat重复、union合并成一个整体并通过easing: backIn这类参数设置缓动曲线。案例三预制体发射子弹加对象池子弹要反复创建和销毁直接instantiate再destroy会产生垃圾回收压力。做法是把子弹做成预制体用NodePool回收复用import{_decorator,Component,Node,Prefab,NodePool,instantiate}fromcc;const{ccclass,property}_decorator;ccclass(Shooter)exportclassShooterextendsComponent{property(Prefab)bulletPrefab:Prefabnull!;property(Node)bulletRoot:Nodenull!;// 子弹挂在这个节点下privatepoolnewNodePool();shoot(){// 池里有就取没有再新建constbulletthis.pool.size()0?this.pool.get()!:instantiate(this.bulletPrefab);bullet.setParent(this.bulletRoot);bullet.setPosition(this.node.position);}recycle(bullet:Node){// 出屏或命中后回收而不是 destroythis.pool.put(bullet);}}property(Prefab)和property(Node)会在属性检查器里出现一个拖拽框把预制体和节点拖进去就绑定好了不用写路径字符串。案例四2D 碰撞与计分给子弹和敌机都加上 2D 碰撞体子弹带刚体就能在回调里处理命中import{_decorator,Component,Collider2D,Contact2DType,IPhysics2DContact}fromcc;const{ccclass}_decorator;ccclass(Enemy)exportclassEnemyextendsComponent{start(){constcolliderthis.getComponent(Collider2D);if(collider){collider.on(Contact2DType.BEGIN_CONTACT,this.onBeginContact,this);}}onBeginContact(self:Collider2D,other:Collider2D,contact:IPhysics2DContact|null){// 两个碰撞体开始接触时调用一次console.log(命中,other.node.name);// 回调里不要立刻销毁或创建物理对象改成标记下一帧在 update 里处理}}要注意两点用 Box2D 物理模块时需要在刚体的属性检查器里勾选「EnabledContactListener」否则收不到回调在回调里创建的刚体、关节不会立刻生效而是等本次物理更新结束后才创建。回调里拿到的碰撞信息也是缓存不要长期持有需要的话自己拷贝一份。案例五按钮与场景切换主菜单点「开始」进入游戏场景import{_decorator,Component,Button,director}fromcc;const{ccclass,property}_decorator;ccclass(StartButton)exportclassStartButtonextendsComponent{property(Button)startBtn:Buttonnull!;onEnable(){this.startBtn.node.on(Button.EventType.CLICK,this.onStart,this);}onDisable(){this.startBtn.node.off(Button.EventType.CLICK,this.onStart,this);}onStart(){// 参数是场景名场景需要在构建里勾选director.loadScene(game);}}不想写代码也行在编辑器里选中 Button 组件直接给「Click Events」绑定目标节点、组件和方法名效果一样。案例六本地存档最高分、关卡进度、音效开关这类数据用sys.localStorage保存接口和网页的 localStorage 一致在各个发布平台上都有对应实现import{sys}fromcc;constbestNumber(sys.localStorage.getItem(bestScore)??0);if(scorebest){sys.localStorage.setItem(bestScore,String(score));}存的都是字符串数字和对象需要自己转换。需要云端存档的话要使用各渠道自己的接口那部分属于渠道适配。进阶用法资源分包与动态加载小游戏的主包有体积上限做法是把关卡、大图、音频放进 Asset Bundle先在编辑器里把某个文件夹设为 Bundle运行时再按需加载。import{assetManager,Prefab,instantiate,director}fromcc;assetManager.loadBundle(level2,(err,bundle){if(err){console.error(err);return;}bundle.load(prefabs/boss,Prefab,(err2,prefab){if(err2){console.error(err2);return;}constbossinstantiate(prefab);director.getScene()!.addChild(boss);});});加载了就要考虑释放单个资源可以assetManager.releaseAsset(asset)整个 Bundle 的某个路径用bundle.release(path, type)bundle.releaseAll()会释放全部要谨慎使用。资源管理是 Cocos 项目里最容易出问题的地方加载了不释放内存会一路涨。按平台写分支同一份代码里个别能力在不同平台上并不一致比如只有小游戏才有的登录、分享接口。可以用sys.platform判断当前运行环境再走各自的分支其余逻辑保持统一import{sys}fromcc;if(sys.platformsys.Platform.WECHAT_GAME){// 绿泡泡小游戏专属逻辑比如调用渠道的分享接口}else{// 其他平台走通用逻辑}建议把这类分支集中在一个「平台适配层」里业务代码只调用统一接口换渠道时只改这一层。构建发布打开「项目 → 构建发布」快捷键 Ctrl/Cmd Shift B选择平台填好对应平台的参数比如小游戏的 AppID点构建即可。构建产物还要用对应平台的开发者工具预览和上传提前装好。性能与渲染3.8 起部分模块Spine、Bullet 与 PhysX 物理、WebGPU 渲染后端用 wasm 实现。WebGPU 后端对浏览器和设备要求较高社区论坛里有升级后启用 WebGPU 导致无法运行的反馈所以这一项在上线前一定要实机测试。原生平台在 iOS、Android 和鸿蒙上走的是 C 原生运行时需要对应的 Xcode、Android Studio 或 DevEco 环境。3.8.5 起已适配华为 HarmonyOS Next。几个常见的坑版本别混用。2.x 和 3.x 的 API 不兼容网上很多老教程是 2.x 的抄代码前先看版本。小游戏先看包体。先用空项目构建一次量出基础包体再决定怎么分包。各平台要分别实测。同一份代码在不同渠道的表现并不完全一致渠道的审核和接口也各有差异。结尾速查清单做小游戏先选 3.8 系列TypeScript 写逻辑关注 COCOS 4 的小游戏支持进度场景 → 节点 → 组件脚本就是组件先量包体再分包每个发布平台都实机测一遍输入用 input动画用 tween重复对象用预制体加对象池回调里别直接改物理对象资源加载了就要想好怎么释放之前聊过 Godot你会把下一个项目押在 Godot 还是 Cocos