1. 项目概述:为什么选择Cocos Creator 3.8作为你的游戏开发起点?
如果你正站在游戏开发的大门前,看着Unity、Unreal、Godot这些名字眼花缭乱,不知道从何下手,那我建议你先把目光聚焦在Cocos Creator 3.8上。这不是说其他引擎不好,而是对于绝大多数想快速上手、独立完成一个可玩项目,尤其是对2D、轻度3D或者微信小游戏有想法的开发者来说,Cocos Creator 3.8提供了一个近乎完美的平衡点。我见过太多新手在Unity庞大的资产商店和复杂的渲染管线里迷失,也见过一些朋友被Godot的GDScript和相对小众的生态劝退。而Cocos Creator 3.8,它给我的感觉就像一把精心打磨的瑞士军刀——该有的功能一个不少,上手路径却异常清晰。
这个“保姆级”入门指南,就是要帮你绕开我当年踩过的所有坑。我们不会一上来就讲晦涩的图形学原理或者复杂的架构设计,而是从一个最朴素的目标开始: 让你在最短的时间内,亲手做出一个“能动起来”的东西 。这个“东西”可能是一个跳跃的小人,一个发射子弹的飞机,或者一个简单的点击消除游戏。关键在于,你能看到代码如何驱动画面,理解游戏循环的基本逻辑,并建立起“我能做到”的信心。Cocos Creator 3.8的编辑器高度一体化,从场景搭建、UI设计到脚本编写和预览调试,都在同一个界面里完成,这种“所见即所得”的体验对新手极其友好。它使用TypeScript作为主要脚本语言,这对于有JavaScript/前端背景的开发者来说是天然优势,对于纯新手,TypeScript的强类型提示也能帮你避免很多低级错误,学起来比C++或C#门槛更低。
更重要的是它的“轻量”与“跨平台”。安装包不到1G,对电脑配置要求亲民,启动速度飞快。当你完成开发后,一键就能发布到Web、iOS、Android、Windows、Mac,以及国内最重要的微信小游戏、抖音小游戏等平台。这意味着你的一次开发,可以触达几乎所有主流渠道,对于个人开发者或小团队来说,试错和推广成本大大降低。所以,无论你是心怀梦想的独立游戏制作人,是想拓展技能的前端工程师,还是对游戏创作充满好奇的学生,跟着这篇指南,用Cocos Creator 3.8踏出第一步,都是一个明智且高效的选择。
2. 环境准备与第一个项目创建
万事开头难,但配置开发环境这件事,Cocos Creator已经让它变得非常简单。我们一步步来,确保你的起跑线是平整的。
2.1 安装编辑器与必要环境
首先,访问Cocos官网的下载页面。这里我强烈建议你直接下载 Cocos Dashboard ,它是一个启动器和管理器,而不是只下载一个单独的编辑器。通过Dashboard,你可以方便地安装多个不同版本的Creator,管理项目,获取最新资讯和教程,这对于长期学习和开发非常有利。
下载安装Dashboard后,打开它。你会看到一个清晰的界面。在“编辑器”标签页,找到Cocos Creator 3.x版本,选择最新的 3.8.x 稳定版进行安装。为什么是3.8而不是最新的3.9或4.0?因为3.8是一个长期支持版本,社区资料、第三方插件和解决方案最为丰富,遇到问题更容易找到答案,稳定性也经过充分验证,最适合新手入门。
安装过程中,Dashboard通常会提示你安装必要的构建工具,比如Node.js。请务必同意安装。Node.js是现代前端和很多开发工具链的基础,Cocos Creator的构建发布流程依赖它。如果安装失败或你电脑上已有旧版本Node.js导致冲突,可以手动去Node.js官网下载 18.x 或 20.x 的LTS版本安装,这能避免一些潜在的兼容性问题。
注意:安装路径请务必使用 全英文目录 。不要包含中文、空格或特殊字符。这是很多开发工具的通用要求,可以避免后续构建、打包时出现各种诡异难寻的路径错误。
安装完成后,从Dashboard启动Cocos Creator 3.8。第一次启动可能会慢一些,因为它需要初始化一些本地配置。
2.2 创建并理解你的第一个项目
启动后,点击“新建”按钮。这里你会看到多种项目模板:空项目、3D项目、2D项目、官方示例等。对于纯新手,我建议选择 “Hello World” 3D模板 。别被“3D”吓到,这个模板非常简单,但它已经包含了完整的场景、相机、灯光和一个旋转的立方体,能让你最直观地感受一个Cocos项目的结构。
给项目起个名字,比如
MyFirstCocosGame
,选择好存放位置(同样是全英文路径),点击“创建”。编辑器会花一点时间初始化项目。
项目打开后,花几分钟熟悉一下编辑器界面,这是你未来战斗的主战场。主要分为这几个区域:
- 场景编辑器 :中央最大的区域,可视化地布置你的游戏场景,摆放物体(在Cocos里叫“节点”)。
- 层级管理器 :左上角,以树状结构展示场景中的所有节点。你可以把它理解为场景中所有物体的“家族谱系”。
- 资源管理器 :左下角,展示项目中的所有文件,如图片、脚本、声音、预制体等。这是你的“弹药库”。
- 属性检查器 :右侧,当你选中场景或资源管理器中的任何一个节点或资源时,这里会显示其所有可调整的属性。这是你给游戏对象“赋予能力”和“调整参数”的地方。
- 控制台/资源预览等 :底部区域,输出日志、错误信息,预览资源等。
现在,直接点击编辑器上方工具栏的 三角形播放按钮 。你会看到一个新窗口弹出,里面有一个白色的立方体在缓缓旋转。恭喜!你的第一个Cocos项目已经成功运行了。虽然它什么都没做,但你已经完成了从安装到运行的全流程,这本身就意义重大。点击播放按钮旁边的正方形停止按钮,结束预览。
2.3 项目目录结构解析
光会运行不够,我们得知道项目是怎么组织的。打开你的项目文件夹(或者看编辑器的“资源管理器”面板),核心结构如下:
MyFirstCocosGame/
├── assets/ # 核心目录!所有游戏资源(场景、脚本、图片、声音等)都放在这里
├── library/ # 编辑器生成的资源库和缓存,无需手动修改,通常加入.gitignore
├── local/ # 本地设置和临时数据,无需关心
├── profiles/ # 项目构建配置
├── settings/ # 项目设置
├── temp/ # 临时文件夹
└── package.json # 项目配置文件,记录名称、版本、依赖等
对于新手,你百分之九十九的时间都是在和
assets
目录打交道。在这个目录下,你可以自由创建文件夹来分类管理资源,比如
Scripts
放脚本,
Textures
放图片,
Sounds
放音频。编辑器会实时同步这些更改。
3. 核心概念拆解:场景、节点与组件
在Cocos Creator里做游戏,就像在导一场戏。你需要理解三个最核心的概念: 场景、节点、组件 。这是引擎设计的精髓,也是区别于你直接写代码控制Canvas画布的根本不同。
3.1 场景:游戏的舞台
场景
就是一个独立的游戏关卡、界面或者章节。你可以有“开始菜单场景”、“第一关场景”、“游戏结束场景”。一个游戏通常由多个场景组成,通过代码在不同场景间切换。我们刚才创建的“Hello World”模板,本身就是一个完整的场景。你可以在“资源管理器”中右键点击
assets
目录,选择“创建 -> 场景”,来建立一个新的空场景。双击场景文件,它就会在“场景编辑器”中打开。
3.2 节点:舞台上的演员
节点 是场景中一切物体的基本单位。那个旋转的立方体是一个节点,将来你控制的角色是一个节点,一颗子弹、一段文字、一个按钮,都是节点。节点本身是空白的,它只有一些基础的变换属性:位置(Position)、旋转(Rotation)、缩放(Scale)。你可以把节点想象成一个空的“容器”或“骨架”。
在“层级管理器”中,你可以看到节点的树状结构。节点之间可以有父子关系。子节点会继承父节点的变换。比如,你创建一个“Player”节点作为父节点,然后创建“Weapon”节点作为它的子节点。那么当“Player”移动时,“Weapon”会自动跟着移动,你只需要处理“Weapon”相对于“Player”的偏移即可,这大大简化了复杂对象的逻辑。
3.3 组件:赋予演员灵魂
一个只有位置的空白节点是没用的。 组件 就是用来赋予节点特定功能和行为的脚本。比如:
- Sprite 组件:让节点能显示一张图片。
- Label 组件:让节点能显示一段文字。
- RigidBody 组件:让节点拥有物理属性,能受重力影响、发生碰撞。
- 你自己写的TypeScript脚本 :也是组件,用来实现自定义的游戏逻辑,比如控制角色移动、发射子弹、计算分数。
为节点添加组件,是让它“活”起来的关键。在“层级管理器”选中一个节点,然后在右侧的“属性检查器”面板最下方,点击“添加组件”按钮,你可以选择内置的各类组件,或者选择“自定义脚本”来挂载你自己写的脚本。
一个节点可以挂载多个组件
。例如,你的主角节点可以同时拥有:Sprite组件(显示形象)、RigidBody组件(物理碰撞)、BoxCollider组件(碰撞形状)、以及一个你自己写的
PlayerController.ts
脚本(处理键盘输入和逻辑)。这种“实体-组件”架构非常灵活,是Cocos Creator构建游戏世界的基石。
4. 第一个可交互游戏:控制方块移动与碰撞
理解了核心概念,我们来动手做一个真正的小游戏:用键盘方向键控制一个方块移动,并让它能与场景中的其他方块发生碰撞。这将串联起脚本编写、用户输入、物理系统和碰撞检测这几个核心模块。
4.1 创建游戏元素与添加物理属性
首先,我们清理一下默认的Hello World场景。在“层级管理器”中,删除那个旋转的立方体节点(名为“Cube”),只保留“Main Camera”和“DirectionalLight”。
-
创建地面
:在“层级管理器”空白处右键,选择“创建 -> 3D对象 -> Cube”。重命名这个节点为
Ground。在“属性检查器”中,调整它的Scale为(20, 1, 20),让它变成一个巨大的平板作为地面。 -
创建玩家方块
:同样方式再创建一个Cube,重命名为
Player。把它向上移动一些,比如设置Position的Y轴为1,让它悬在地面上方。 -
创建障碍物
:再创建几个Cube,随意摆放在地面上,重命名为
Obstacle1,Obstacle2等。 -
添加物理组件
:现在它们只是视觉模型,没有物理特性。我们需要为它们添加刚体(RigidBody)和碰撞体(Collider)。
-
选中
Ground节点,点击“添加组件”,选择“Physics -> BoxCollider”。再点击“添加组件”,选择“Physics -> RigidBody”。对于一个静止的地面,我们需要将其RigidBody组件的Type属性设置为 Static (静态),这样它不会受外力影响。 -
选中
Player节点,同样添加“BoxCollider”和“RigidBody”。玩家是需要被控制的,所以它的RigidBody保持 Dynamic (动态)类型。为了移动更顺滑,可以将其Linear Damping(线性阻尼)设置为0.5,增加一点移动的惯性感。 -
为每个
Obstacle也添加“BoxCollider”和“RigidBody”,类型设为 Static 或 Dynamic 都可以,设为Static它们就不会被撞飞,设为Dynamic则可以被玩家推动。
-
选中
4.2 编写玩家控制脚本
现在我们来写代码让玩家方块动起来。在“资源管理器”的
assets
目录下,右键选择“创建 -> TypeScript”,命名为
PlayerController
。
双击打开这个脚本文件(编辑器通常会调用VSCode或你设置的默认代码编辑器)。你会看到一些自动生成的代码。我们将其修改如下:
import { _decorator, Component, input, Input, KeyCode, RigidBody, Vec3 } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('PlayerController')
export class PlayerController extends Component {
// 声明一个属性,用于在编辑器界面设置移动速度
@property({ type: Number, tooltip: '移动速度' })
public moveSpeed: number = 5;
// 声明一个属性,用于关联刚体组件(我们将在编辑器里拖拽赋值)
@property({ type: RigidBody })
public rigidBody: RigidBody | null = null;
// 用于存储输入方向的向量
private _moveDirection: Vec3 = new Vec3(0, 0, 0);
start() {
// 初始化键盘输入监听
input.on(Input.EventType.KEY_DOWN, this.onKeyDown, this);
input.on(Input.EventType.KEY_UP, this.onKeyUp, this);
}
onKeyDown(event: any) {
// 根据按下的键,设置移动方向
switch (event.keyCode) {
case KeyCode.KEY_W:
this._moveDirection.z = -1; // Cocos中Z轴正向是屏幕里,所以向前是Z负
break;
case KeyCode.KEY_S:
this._moveDirection.z = 1;
break;
case KeyCode.KEY_A:
this._moveDirection.x = -1;
break;
case KeyCode.KEY_D:
this._moveDirection.x = 1;
break;
}
}
onKeyUp(event: any) {
// 根据松开的键,清零对应方向
switch (event.keyCode) {
case KeyCode.KEY_W:
case KeyCode.KEY_S:
this._moveDirection.z = 0;
break;
case KeyCode.KEY_A:
case KeyCode.KEY_D:
this._moveDirection.x = 0;
break;
}
}
update(deltaTime: number) {
// 每一帧都根据当前方向施加力
if (this.rigidBody && (!this._moveDirection.equals(Vec3.ZERO))) {
// 创建一个力向量:方向 * 速度 * 时间增量
let force = new Vec3(
this._moveDirection.x * this.moveSpeed * deltaTime,
0,
this._moveDirection.z * this.moveSpeed * deltaTime
);
// 使用刚体的applyForce方法施加力
this.rigidBody.applyForce(force);
}
}
onDestroy() {
// 组件销毁时,移除输入监听,防止内存泄漏
input.off(Input.EventType.KEY_DOWN, this.onKeyDown, this);
input.off(Input.EventType.KEY_UP, this.onKeyUp, this);
}
}
让我解释一下关键点:
-
@property装饰器 :这是Cocos Creator的神器。用@property声明的变量,会在编辑器的“属性检查器”中显示为一个可配置的字段。这样,你不需要改代码,就能在编辑器里调整moveSpeed,或者将Player节点上的RigidBody组件拖拽赋值给rigidBody属性,实现了脚本与场景的直观联动。 -
输入系统
:
input.on用于监听键盘事件。我们在start生命周期函数中注册监听,在onDestroy中移除,这是良好习惯。 -
物理移动
:我们没有直接修改节点的
position,而是通过RigidBody组件的applyForce来施加力。这是符合物理模拟的正确方式,能让移动与碰撞更自然。直接改position会让物体“穿透”其他碰撞体。 -
update函数 :引擎每帧都会调用,deltaTime是上一帧到这一帧的时间间隔(秒)。用deltaTime参与速度计算,可以保证在不同帧率的设备上移动速度一致。
4.3 关联脚本与测试
保存脚本文件。回到Cocos Creator编辑器,在“资源管理器”中找到
PlayerController.ts
,将它
拖拽
到“层级管理器”中的
Player
节点上。这样,脚本组件就挂载到了节点上。
选中
Player
节点,看右侧“属性检查器”。在最下方,你应该能看到
PlayerController
组件。将
Move Speed
调整为10试试。然后,你需要将
Player
节点自身的
RigidBody
组件拖拽到
PlayerController
组件的
Rigid Body
属性槽中,完成关联。
注意:很多新手会忘记这一步——在脚本中声明了
@property属性后,必须在编辑器里手动完成拖拽赋值或填写,否则脚本里对应的变量就是null,代码会报错。
现在,点击编辑器顶部的播放按钮!你应该可以用WASD键控制方块在地面上移动,并且当撞到其他障碍物方块时,会发生碰撞。如果障碍物是Dynamic类型,你甚至能推动它们。一个最简单的可交互物理小游戏就完成了。
5. 资源管理与UI系统入门
游戏不能只有逻辑,还需要画面和界面。接下来我们学习如何导入图片资源,创建精灵(Sprite),并搭建一个简单的UI界面来显示分数。
5.1 导入与使用图片资源
将你的图片文件(如PNG, JPG)直接
拖拽
到“资源管理器”的
assets
目录下,或者其子文件夹内(例如
assets/Textures
)。编辑器会自动导入并生成对应的
.meta
元数据文件。这个文件很重要,不要删除,它记录了资源的导入设置。
要使用一张图片,通常有两种方式:
-
创建Sprite节点
:在“层级管理器”右键,“创建 -> 2D对象 -> Sprite”。然后在“属性检查器”中,找到
Sprite组件的Sprite Frame属性,点击旁边的小圆点,选择你导入的图片。这个节点就会显示该图片。 -
为已有节点添加Sprite组件
:比如你想把之前的
Player方块换成一张飞机图片。选中Player节点,在“属性检查器”中 先移除MeshRenderer组件(这是渲染3D模型的),然后“添加组件 -> 2D -> Sprite”。再将你的飞机图片赋值给Sprite Frame。别忘了调整节点的Scale,因为2D Sprite的默认尺寸可能很小。
5.2 构建一个简单的UI界面
Cocos Creator的UI系统基于Canvas(画布)和Widget( widget)组件。我们来做一个显示分数的UI。
-
创建Canvas
:在“层级管理器”右键,“创建 -> UI -> Canvas”。Canvas是所有UI元素的根容器,它会自动适配屏幕大小。创建后,场景中会出现一个名为
Canvas的节点。 -
创建背景板
:在
Canvas节点上右键,“创建 -> UI -> Sprite”。重命名为ScoreBackground。在它的Sprite组件上设置一张深色半透明的图片,或者直接使用纯色(在Sprite组件的Color属性里调整)。 -
创建文本标签
:在
ScoreBackground节点上右键,“创建 -> UI -> Label”。重命名为ScoreText。在“属性检查器”中,找到Label组件,可以设置字体大小(Font Size)、颜色(Color)、对齐方式(Horizontal Align,Vertical Align)等。在String属性里输入“分数:0”。 -
使用Widget组件进行布局
:选中
ScoreBackground节点,点击“添加组件”,选择“UI -> Widget”。Widget组件能帮助UI元素相对于父节点或屏幕进行定位。-
勾选
Top,并设置Top值为20,表示距离父节点(Canvas)顶部20像素。 -
勾选
Right,并设置Right值为20,表示距离右侧20像素。 -
将
Align Mode设置为ONCE,这样设置好一次后,当屏幕尺寸变化时,它会保持这个相对位置。你也可以设置为ALWAYS让它实时更新。
-
勾选
现在你运行游戏,会在屏幕右上角看到一个显示“分数:0”的UI面板。
5.3 用脚本动态更新UI
静态的UI没用,我们需要用代码来更新分数。创建一个新的TypeScript脚本,命名为
GameManager
。
import { _decorator, Component, Label, Node } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('GameManager')
export class GameManager extends Component {
// 关联分数文本Label组件
@property({ type: Label })
public scoreLabel: Label | null = null;
// 当前分数
private _currentScore: number = 0;
start() {
// 初始化分数显示
this.updateScoreDisplay();
// 模拟:每2秒增加10分
this.schedule(this.addScore, 2);
}
addScore() {
this._currentScore += 10;
this.updateScoreDisplay();
}
updateScoreDisplay() {
if (this.scoreLabel) {
this.scoreLabel.string = `分数:${this._currentScore}`;
}
}
// 提供一个公共方法,让其他脚本(如PlayerController)可以调用加分
public increaseScore(value: number) {
this._currentScore += value;
this.updateScoreDisplay();
}
}
将这个脚本挂载到场景根节点(比如
Canvas
或一个新建的空节点
GameManager
)上。然后,在“属性检查器”里,将
Canvas/ScoreBackground/ScoreText
节点上的
Label
组件,拖拽赋值给
GameManager
组件的
Score Label
属性。
运行游戏,你会看到右上角的分数每2秒自动增加10。这演示了如何通过脚本获取和修改UI组件的数据。
6. 动画系统与状态管理初探
让游戏角色动起来,动画是关键。Cocos Creator提供了强大的动画编辑器,可以制作序列帧动画、骨骼动画,以及属性动画(补间动画)。我们先用最简单的属性动画,让我们的障碍物方块上下跳动。
6.1 使用动画编辑器制作补间动画
-
创建动画剪辑
:在“资源管理器”右键,“创建 -> AnimationClip”,命名为
ObstacleBounce.anim。 -
为节点添加动画组件
:选中一个障碍物节点(如
Obstacle1),在“属性检查器”点击“添加组件”,选择“Animation”。 -
编辑动画
:确保
Obstacle1节点被选中,然后点击编辑器上方菜单栏的“动画 -> 动画编辑器”。下方会弹出动画编辑器面板。-
在动画编辑器里,点击“新建动画剪辑”,选择我们刚才创建的
ObstacleBounce.anim。 -
时间轴移动到0秒处,点击
Position属性旁边的“+”号,记录一个关键帧。此时节点的位置被记录。 -
将时间轴移动到1秒处,在“属性检查器”中修改节点的
Position.y(比如从1改为3),然后再次点击Position属性的“+”号,记录第二个关键帧。 -
将时间轴移动到2秒处,将
Position.y改回1,记录第三个关键帧。
-
在动画编辑器里,点击“新建动画剪辑”,选择我们刚才创建的
-
预览与设置
:点击动画编辑器上的播放按钮,可以看到方块在0-2秒内完成了一个上下跳动的循环。在
Animation组件上,你可以勾选Play On Load,这样游戏一开始它就会自动播放。还可以将Wrap Mode设置为Loop,让它循环播放。
6.2 通过代码控制动画与游戏状态
更复杂的动画(如角色跑、跳、攻击)通常需要通过代码来触发。这涉及到简单的
状态管理
概念。我们修改
PlayerController
,为其添加一个跳跃动画控制。
首先,为Player准备两个动画剪辑:
Idle.anim
(待机)和
Jump.anim
(跳跃)。假设你已经用上面的方法制作好了(Jump动画可以是一个Y轴位置先升后降的补间)。
修改
PlayerController.ts
:
import { _decorator, Component, input, Input, KeyCode, RigidBody, Vec3, Animation } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('PlayerController')
export class PlayerController extends Component {
@property({ type: Number })
public moveSpeed: number = 5;
@property({ type: RigidBody })
public rigidBody: RigidBody | null = null;
// 新增:关联动画组件
@property({ type: Animation })
public playerAnimation: Animation | null = null;
private _moveDirection: Vec3 = new Vec3(0, 0, 0);
private _isOnGround: boolean = true; // 新增:是否在地面状态
start() {
input.on(Input.EventType.KEY_DOWN, this.onKeyDown, this);
input.on(Input.EventType.KEY_UP, this.onKeyUp, this);
// 播放待机动画
if (this.playerAnimation) {
this.playerAnimation.play('Idle');
}
}
onKeyDown(event: any) {
switch (event.keyCode) {
case KeyCode.KEY_W:
this._moveDirection.z = -1;
break;
case KeyCode.KEY_S:
this._moveDirection.z = 1;
break;
case KeyCode.KEY_A:
this._moveDirection.x = -1;
break;
case KeyCode.KEY_D:
this._moveDirection.x = 1;
break;
case KeyCode.KEY_SPACE: // 新增:空格键跳跃
this.jump();
break;
}
}
jump() {
if (this._isOnGround && this.rigidBody) {
this._isOnGround = false;
// 施加一个向上的冲量
let impulse = new Vec3(0, 5, 0);
this.rigidBody.applyImpulse(impulse);
// 播放跳跃动画
if (this.playerAnimation) {
this.playerAnimation.play('Jump');
}
}
}
// 新增:碰撞检测回调,用于判断是否落地
onBeginContact(event: any) {
// 简单判断:如果碰撞法线朝上(粗略判断为地面),则认为落地
if (event.worldNormal.y > 0.5) {
this._isOnGround = true;
// 落地后播放待机动画
if (this.playerAnimation) {
this.playerAnimation.play('Idle');
}
}
}
// ... 其他部分(onKeyUp, update, onDestroy)保持不变 ...
}
这里引入了几个新东西:
-
动画组件引用
:通过
@property关联了Animation组件。 -
状态变量
:
_isOnGround用来标记玩家是否在地面,防止连续跳跃。 -
跳跃逻辑
:在
jump方法中,通过刚体的applyImpulse施加一个瞬间的冲量来实现跳跃。同时播放跳跃动画。 -
碰撞回调
:
onBeginContact是物理引擎提供的碰撞开始回调函数。当玩家的碰撞体与其他碰撞体接触时,这个函数会被调用。我们通过判断碰撞法线的Y分量来粗略判断是否碰撞到地面,然后重置_isOnGround状态并切换回待机动画。
为了让碰撞回调生效,你需要在
Player
节点的
RigidBody
组件上,勾选
Enabled Contact Listener
(启用接触监听器)。然后,在
PlayerController
组件脚本中,就能接收到碰撞事件了。
这个例子展示了如何将用户输入、物理模拟、动画播放和简单的状态逻辑结合起来,实现一个基本的交互反馈循环。
7. 预制体:重复利用你的游戏对象
当你需要创建大量相同的物体时,比如子弹、敌人、金币,一个个在场景里复制粘贴不仅效率低,而且难以管理。这时就需要 预制体 。
7.1 创建与使用预制体
假设我们要生成很多金币。首先,在场景中精心制作一个金币节点:创建一个Sprite节点,贴上金币图片,添加一个BoxCollider(用于触发拾取),还可以加一个旋转的动画。
制作好后,在“资源管理器”的
assets
目录下,创建一个文件夹叫
Prefabs
。然后,将场景中这个制作好的金币节点,
直接拖拽
到
Prefabs
文件夹里。你会看到它变成了一个蓝色的圆形图标,这就是预制体资产。此时,场景中的这个节点会变成蓝色,表示它是该预制体的一个
实例
。
你可以删除场景中的这个实例。当需要用到金币时,只需从“资源管理器”将
Coin
预制体
拖拽
到“场景编辑器”或“层级管理器”中,一个新的金币实例就创建好了。所有实例都共享预制体的原始结构和属性。
7.2 通过代码动态生成预制体
游戏运行时动态生成敌人或子弹,是更常见的需求。这需要用到
instantiate
方法。
首先,创建一个脚本
CoinManager.ts
来管理金币生成:
import { _decorator, Component, Prefab, instantiate, Node, Vec3 } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('CoinManager')
export class CoinManager extends Component {
// 关联金币预制体
@property({ type: Prefab })
public coinPrefab: Prefab | null = null;
// 生成金币的位置范围
@property
public spawnArea: Vec3 = new Vec3(10, 0, 10);
start() {
// 游戏开始时生成10个金币
this.spawnCoins(10);
}
spawnCoins(count: number) {
if (!this.coinPrefab) return;
for (let i = 0; i < count; i++) {
// 1. 实例化预制体
const coinNode = instantiate(this.coinPrefab);
// 2. 设置父节点(通常设为当前节点或场景根节点)
coinNode.setParent(this.node);
// 3. 设置随机位置
const randomPos = new Vec3(
(Math.random() - 0.5) * this.spawnArea.x,
1, // 离地高度
(Math.random() - 0.5) * this.spawnArea.z
);
coinNode.setPosition(randomPos);
}
}
// 一个公共方法,用于在其他地方(如玩家吃到金币时)调用,生成一个新金币
public spawnSingleCoinAt(position: Vec3) {
if (!this.coinPrefab) return;
const coinNode = instantiate(this.coinPrefab);
coinNode.setParent(this.node);
coinNode.setPosition(position);
}
}
将这个脚本挂载到一个空节点(如
GameManager
)上。然后,在“属性检查器”中,将
Prefabs
文件夹里的
Coin
预制体,拖拽赋值给
CoinManager
组件的
Coin Prefab
属性。
运行游戏,你会看到10个金币被随机生成在指定区域内。
instantiate
是运行时动态创建对象的唯一正确方式,它高效地复用了预制体的数据。
8. 构建与发布:让你的游戏跑起来
开发调试都在编辑器里进行,最终我们需要把游戏打包成可发布的产品。Cocos Creator的构建系统非常强大,支持一键打包到多个平台。
8.1 构建网页版
这是最简单的发布方式,可以快速得到一个能在浏览器中运行的版本。
- 点击编辑器顶部菜单的“项目 -> 构建发布”,打开构建发布面板。
-
构建平台
:选择
Web Mobile(针对移动端浏览器优化)或Web Desktop。对于入门项目,选哪个都可以。 - 初始场景 :确保是你当前正在编辑的主场景。
- 主包压缩类型 和 配置资源服务器地址 :初学者可以先保持默认。
- 点击 构建 按钮。构建过程可能会花一两分钟,完成后会弹出构建目录。
-
进入构建目录下的
web-mobile(或web-desktop)文件夹,找到index.html文件。 双击它 ,你的游戏就会在默认浏览器中运行。你也可以将这个文件夹整个部署到任何静态网站服务器(如GitHub Pages, Netlify)上分享。
8.2 构建微信小游戏
这是Cocos Creator在国内的一大优势。构建前,你需要先安装微信开发者工具。
-
在构建发布面板,选择
WeChat Game平台。 -
你需要填写
AppID。如果你只是个人测试,可以点击旁边的“获取”链接,使用测试号。如果是正式项目,需要去微信公众平台注册小游戏并获取AppID。 - 游戏包名 和 远程服务器地址 等根据情况填写,测试可留空。
- 点击 构建 。构建完成后,点击 运行 按钮。Cocos Creator会自动启动微信开发者工具,并打开你的小游戏项目。
- 在微信开发者工具中,你可以进行真机预览、调试和上传。
8.3 构建原生平台
对于iOS和Android,构建过程会稍微复杂一些,因为需要配置原生开发环境。
-
Android
:需要安装JDK、Android SDK和NDK。在构建面板选择
Android平台,配置好SDK路径、包名、签名等。构建产物是一个.apk文件或可用于Android Studio的工程。 -
iOS
:需要在macOS电脑上,安装Xcode。构建面板选择
iOS平台,配置包名、证书等。构建产物是一个Xcode工程,你需要用Xcode打开并编译到真机或模拟器。
重要提示:构建原生平台前,务必仔细阅读官方文档中关于环境配置的部分。常见的坑包括:JDK版本不对、Android SDK路径包含中文、iOS证书配置错误等。第一次配置可能会遇到问题,耐心根据错误信息搜索解决,配置好一次后就一劳永逸了。
9. 常见问题与调试技巧实录
即使跟着教程一步步走,你也一定会遇到各种报错和意外情况。这里我总结了一些新手高频问题和调试心法。
9.1 编辑器与脚本常见问题
问题1:脚本修改后,编辑器里不生效?
- 检查 :首先确保脚本文件已保存(Ctrl+S)。然后回到Cocos Creator编辑器,检查控制台是否有编译错误(红色错误信息)。最常见的错误是TypeScript语法错误,比如缺少分号、括号不匹配、使用了未定义的变量等。根据错误信息逐行检查脚本。
- 强制刷新 :有时编辑器热重载会卡住。可以尝试在“资源管理器”中右键点击脚本文件,选择“重新导入模块”。或者更直接地,关闭并重新打开该场景。
问题2:
@property
声明的属性,在编辑器里看不到或无法赋值?
-
检查
:确保脚本类名前的
@ccclass('ClassName')装饰器里的名字,与脚本文件名(不包含.ts)一致,且没有拼写错误。这是编辑器识别脚本的关键。 - 检查 :确保脚本已经成功挂载到场景中的某个节点上。只有挂载后,属性才会显示在检查器中。
-
检查
:属性的类型声明是否正确。比如
@property({ type: Label }),如果关联的不是Label组件,就会出错。
问题3:游戏运行时,控制台报错“Cannot read properties of null (reading ‘xxx‘)”?
-
原因
:这是最典型的空引用错误。意味着你代码里某个变量是
null,但你试图调用它的方法或属性。 -
排查
:99%的情况是,你在脚本中用
@property声明了一个需要关联的组件或节点(如public playerAnimation: Animation | null = null;),但 忘记在编辑器里拖拽赋值 。请务必检查所有带@property的变量,是否都在属性检查器中完成了关联。
9.2 物理与碰撞问题
问题4:物体直接穿过了另一个物体,没有发生碰撞?
-
检查清单
:
-
碰撞体
:双方节点都必须有
Collider组件(BoxCollider, SphereCollider等)。 -
刚体
:至少其中一方必须有
RigidBody组件。两个都是Static类型的刚体之间不会发生动力学碰撞(但会有触发检测)。 -
层级与分组
:检查“属性检查器”最顶部的
Layer属性。Cocos的物理碰撞基于层级过滤。默认情况下,所有节点都在DEFAULT层,且默认碰撞矩阵是开启的。如果你修改了层级,需要去“项目 -> 项目设置 -> 物理”中,检查碰撞矩阵是否允许这两个层级相互碰撞。 -
尺寸与位置
:碰撞体的尺寸(
Size)是否合适?是否完全包裹了模型?碰撞体的位置是否偏移了?可以在场景编辑器中勾选Collider组件的Edit选项,可视化查看碰撞体形状。
-
碰撞体
:双方节点都必须有
问题5:如何检测碰撞并做出反应(如吃到金币加分)?
-
两种方式
:
-
物理碰撞
:如之前
PlayerController例子中的onBeginContact。这需要双方都有Collider,且至少一方有RigidBody并开启Enabled Contact Listener。适用于需要物理反馈(如弹开)的情况。 -
触发器
:将
Collider组件的Is Trigger属性勾选上。这样碰撞体就变成了一个“传感器”,物理引擎不会计算碰撞力,但会触发回调函数onTriggerEnter。这非常适合用于拾取物品、进入区域等逻辑。在金币的脚本里监听onTriggerEnter,当进入者是玩家时,销毁自身并调用GameManager的加分方法。
-
物理碰撞
:如之前
9.3 性能与优化初步感知
对于入门项目,性能通常不是问题,但养成好习惯很重要。
-
Draw Call
:在编辑器顶部,点击“控制台”旁边的“预览”下拉菜单,选择“显示状态”。运行游戏后,你会看到
Draw Call(绘制调用)数。这个数字越少越好。减少Draw Call的主要方法是使用 合图 。将多个小图片打包成一张大图(可以使用编辑器内置的“自动图集”功能),这样引擎就能一次绘制多个精灵,极大提升性能。 -
节点数量
:避免在场景中创建成千上万个动态节点。对于大量重复物体(如子弹、粒子),使用
对象池
技术。对象池可以复用已销毁的对象,避免频繁的
instantiate和destroy操作,这是游戏开发中至关重要的优化手段。Cocos Creator在cc模块下提供了NodePool类,可以方便地实现对象池。 -
脚本优化
:在
update函数中避免进行复杂的计算或频繁的查找操作(如find)。将需要频繁访问的组件或节点引用在start或onLoad中缓存起来。
最后,也是最重要的调试技巧:
善用控制台
。使用
console.log()
输出变量值、函数调用信息。使用
debugger
语句在浏览器开发者工具中设置断点,单步执行代码,观察变量状态。遇到问题,先看控制台报错,再结合日志和断点,大部分问题都能定位。

6016

被折叠的 条评论
为什么被折叠?



