Cocos Creator 3.8保姆级入门:从零到一构建可交互游戏

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”。

  1. 创建地面 :在“层级管理器”空白处右键,选择“创建 -> 3D对象 -> Cube”。重命名这个节点为 Ground 。在“属性检查器”中,调整它的 Scale 为(20, 1, 20),让它变成一个巨大的平板作为地面。
  2. 创建玩家方块 :同样方式再创建一个Cube,重命名为 Player 。把它向上移动一些,比如设置 Position 的Y轴为1,让它悬在地面上方。
  3. 创建障碍物 :再创建几个Cube,随意摆放在地面上,重命名为 Obstacle1 Obstacle2 等。
  4. 添加物理组件 :现在它们只是视觉模型,没有物理特性。我们需要为它们添加刚体(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);
    }
}

让我解释一下关键点:

  1. @property 装饰器 :这是Cocos Creator的神器。用 @property 声明的变量,会在编辑器的“属性检查器”中显示为一个可配置的字段。这样,你不需要改代码,就能在编辑器里调整 moveSpeed ,或者将 Player 节点上的 RigidBody 组件拖拽赋值给 rigidBody 属性,实现了脚本与场景的直观联动。
  2. 输入系统 input.on 用于监听键盘事件。我们在 start 生命周期函数中注册监听,在 onDestroy 中移除,这是良好习惯。
  3. 物理移动 :我们没有直接修改节点的 position ,而是通过 RigidBody 组件的 applyForce 来施加力。这是符合物理模拟的正确方式,能让移动与碰撞更自然。直接改 position 会让物体“穿透”其他碰撞体。
  4. 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 元数据文件。这个文件很重要,不要删除,它记录了资源的导入设置。

要使用一张图片,通常有两种方式:

  1. 创建Sprite节点 :在“层级管理器”右键,“创建 -> 2D对象 -> Sprite”。然后在“属性检查器”中,找到 Sprite 组件的 Sprite Frame 属性,点击旁边的小圆点,选择你导入的图片。这个节点就会显示该图片。
  2. 为已有节点添加Sprite组件 :比如你想把之前的 Player 方块换成一张飞机图片。选中 Player 节点,在“属性检查器”中 先移除 MeshRenderer 组件(这是渲染3D模型的),然后“添加组件 -> 2D -> Sprite”。再将你的飞机图片赋值给 Sprite Frame 。别忘了调整节点的 Scale ,因为2D Sprite的默认尺寸可能很小。

5.2 构建一个简单的UI界面

Cocos Creator的UI系统基于Canvas(画布)和Widget( widget)组件。我们来做一个显示分数的UI。

  1. 创建Canvas :在“层级管理器”右键,“创建 -> UI -> Canvas”。Canvas是所有UI元素的根容器,它会自动适配屏幕大小。创建后,场景中会出现一个名为 Canvas 的节点。
  2. 创建背景板 :在 Canvas 节点上右键,“创建 -> UI -> Sprite”。重命名为 ScoreBackground 。在它的 Sprite 组件上设置一张深色半透明的图片,或者直接使用纯色(在 Sprite 组件的 Color 属性里调整)。
  3. 创建文本标签 :在 ScoreBackground 节点上右键,“创建 -> UI -> Label”。重命名为 ScoreText 。在“属性检查器”中,找到 Label 组件,可以设置字体大小( Font Size )、颜色( Color )、对齐方式( Horizontal Align Vertical Align )等。在 String 属性里输入“分数:0”。
  4. 使用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 使用动画编辑器制作补间动画

  1. 创建动画剪辑 :在“资源管理器”右键,“创建 -> AnimationClip”,命名为 ObstacleBounce.anim
  2. 为节点添加动画组件 :选中一个障碍物节点(如 Obstacle1 ),在“属性检查器”点击“添加组件”,选择“Animation”。
  3. 编辑动画 :确保 Obstacle1 节点被选中,然后点击编辑器上方菜单栏的“动画 -> 动画编辑器”。下方会弹出动画编辑器面板。
    • 在动画编辑器里,点击“新建动画剪辑”,选择我们刚才创建的 ObstacleBounce.anim
    • 时间轴移动到0秒处,点击 Position 属性旁边的“+”号,记录一个关键帧。此时节点的位置被记录。
    • 将时间轴移动到1秒处,在“属性检查器”中修改节点的 Position.y (比如从1改为3),然后再次点击 Position 属性的“+”号,记录第二个关键帧。
    • 将时间轴移动到2秒处,将 Position.y 改回1,记录第三个关键帧。
  4. 预览与设置 :点击动画编辑器上的播放按钮,可以看到方块在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)保持不变 ...
}

这里引入了几个新东西:

  1. 动画组件引用 :通过 @property 关联了 Animation 组件。
  2. 状态变量 _isOnGround 用来标记玩家是否在地面,防止连续跳跃。
  3. 跳跃逻辑 :在 jump 方法中,通过刚体的 applyImpulse 施加一个瞬间的冲量来实现跳跃。同时播放跳跃动画。
  4. 碰撞回调 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 构建网页版

这是最简单的发布方式,可以快速得到一个能在浏览器中运行的版本。

  1. 点击编辑器顶部菜单的“项目 -> 构建发布”,打开构建发布面板。
  2. 构建平台 :选择 Web Mobile (针对移动端浏览器优化)或 Web Desktop 。对于入门项目,选哪个都可以。
  3. 初始场景 :确保是你当前正在编辑的主场景。
  4. 主包压缩类型 配置资源服务器地址 :初学者可以先保持默认。
  5. 点击 构建 按钮。构建过程可能会花一两分钟,完成后会弹出构建目录。
  6. 进入构建目录下的 web-mobile (或 web-desktop )文件夹,找到 index.html 文件。 双击它 ,你的游戏就会在默认浏览器中运行。你也可以将这个文件夹整个部署到任何静态网站服务器(如GitHub Pages, Netlify)上分享。

8.2 构建微信小游戏

这是Cocos Creator在国内的一大优势。构建前,你需要先安装微信开发者工具。

  1. 在构建发布面板,选择 WeChat Game 平台。
  2. 你需要填写 AppID 。如果你只是个人测试,可以点击旁边的“获取”链接,使用测试号。如果是正式项目,需要去微信公众平台注册小游戏并获取AppID。
  3. 游戏包名 远程服务器地址 等根据情况填写,测试可留空。
  4. 点击 构建 。构建完成后,点击 运行 按钮。Cocos Creator会自动启动微信开发者工具,并打开你的小游戏项目。
  5. 在微信开发者工具中,你可以进行真机预览、调试和上传。

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:物体直接穿过了另一个物体,没有发生碰撞?

  • 检查清单
    1. 碰撞体 :双方节点都必须有 Collider 组件(BoxCollider, SphereCollider等)。
    2. 刚体 :至少其中一方必须有 RigidBody 组件。两个都是 Static 类型的刚体之间不会发生动力学碰撞(但会有触发检测)。
    3. 层级与分组 :检查“属性检查器”最顶部的 Layer 属性。Cocos的物理碰撞基于层级过滤。默认情况下,所有节点都在 DEFAULT 层,且默认碰撞矩阵是开启的。如果你修改了层级,需要去“项目 -> 项目设置 -> 物理”中,检查碰撞矩阵是否允许这两个层级相互碰撞。
    4. 尺寸与位置 :碰撞体的尺寸( Size )是否合适?是否完全包裹了模型?碰撞体的位置是否偏移了?可以在场景编辑器中勾选 Collider 组件的 Edit 选项,可视化查看碰撞体形状。

问题5:如何检测碰撞并做出反应(如吃到金币加分)?

  • 两种方式
    1. 物理碰撞 :如之前 PlayerController 例子中的 onBeginContact 。这需要双方都有 Collider ,且至少一方有 RigidBody 并开启 Enabled Contact Listener 。适用于需要物理反馈(如弹开)的情况。
    2. 触发器 :将 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 语句在浏览器开发者工具中设置断点,单步执行代码,观察变量状态。遇到问题,先看控制台报错,再结合日志和断点,大部分问题都能定位。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值