快速开始

跟着Cocos Creator用户手册完成step by step教程即可快速熟悉Cocos Creator的应用。

可以通过右上角切换用户手册版本:
用户手册版本切换

IDE界面介绍

IDE管理

通过首页下载Cocos Dashboard,可以管理不同版本的Cocos Creator Editor

Cocos Dashboard

Cocos Creator Editor

语言设置

通过下述配置可以修改界面语言:
界面语言

  • 层级管理器:所有在场景编辑器中看到的内容都可以在层级管理器中找到对应的节点条目。

    场景中仍有一些不可见的私有节点,不会在此显示。

    在编辑场景时这两个面板的内容会同步显示,一般我们也会同时使用这两个面板来搭建场景。

    • 创建UI 组件需要在带有 Canvas 的父节点下才能显示,编辑器在发现没有 Canvas 节点时会自动创建一个。
  • 场景编辑器:在场景编辑器中搭建场景,即可获得所见即所得的场景预览。

    • 点击主窗口左上角工具栏第四个按钮,或在编辑场景时按下 T 快捷键,即可切换使用 矩形变换工具。需要注意的是,矩形变换工具只适用于 UI 节点
      矩形变换工具
    • 通过点击工具栏上的 2D/3D 按钮切换到 2D 编辑视图下进行 UI 编辑操作
  • 资源管理器:
  • 动画编辑器:用于编辑并存储动画数据。

场景视角操作

在 3D 视图下,可以通过以下操作来移动和定位 场景编辑器 的视图:

  • 鼠标左键 + Alt:以视图中心点为中心旋转。
  • 鼠标中键:平移视图。
  • 空格键 + 鼠标/触摸板拖拽:平移视图。
  • 鼠标滚轮:以视图中心点为中心缩放视图。
  • 鼠标右键 + WASD:摄像机漫游。
  • F 快捷键:摄像机聚焦到当前选中节点。

渲染

列表渲染详见

资源类型

目前,Creator 支持 FBX 和 glTF 两种格式的模型文件。

  • FBX(.fbx):支持 FBX 2020 及更早的文件格式。
  • glTF(.gltf 、.glb):支持 glTF 2.0 及更早的文件格式。

动画

骨骼动画

对于mixamo官网来说,处理骨骼动画需满足:

  • 仅支持特定格式的资源文件:fbxobjzip(fbx/obj模型 + 纹理打包的zip包)
  • 要求资源文件内没有骨骼信息,若有,需要通过3D设计软件e.g. blender提前清除再导入mixamo

mixamo下载TPose后,下载所需的不同without skin骨骼动画,分别导入blender中重命名骨骼动画(便于识别),然后删除场景中的所有要素

骨骼动画要素清除

最后导入TPose,进行下列步骤绑定骨骼动画:
绑定骨骼动画

在下方面板中[1]选择[2]动画摄影表,[3]选择动作编辑器,[4]可以切换动画Animation Clip,[5]为每个动画创建不同的时间线。
动作编辑器

从[4]可以看到,当前添加了两条时间线,可以通过关键帧上下键切换不同的动画,空格键播放动画。

导出fbx
导出fbx

[1]绑定纹理,[3]将blender坐标系转换为浏览器web坐标系。

参考文档

程序控制动画

帧事件

import { Animation, Component, _decorator } from 'cc';
const { ccclass, property } = _decorator;

@ccclass("MyScript")
class MyScript extends Component {  // 需要将该组件绑定到骨骼动画节点上  
    public start() {
        const skeletalAnimation = this.node.getComponent(SkeletalAnimation);
        if (skeletalAnimation && skeletalAnimation .defaultClip) {
            const { defaultClip } = skeletalAnimation ;                        
            defaultClip.events = [ // 添加帧事件
                {
                    frame: 0.5, // 第 0.5 秒时触发事件
                    func: 'onTriggered', // 事件触发时调用的函数名称
                    params: [ 0 ], // 向 `func` 传递的参数
                }
            ];                                         

            skeletalAnimation.clips = skeletalAnimation.clips;                        
        }
    }

    public onTriggered(arg: number) { // 骨骼动画执行到第0.5s触发该函数
        console.log('I am triggered!', arg);
    }
}

自定义动画,可以在动画编辑器面板通过右键添加帧事件:
帧事件
其中:

  • func:表示事件触发时回调的函数名称。

    • 事件触发时,动画系统会搜索 动画根节点中的所有组件,若组件中有实现动画事件 func 中指定的函数,便会对其进行调用,并传入 params 中的参数。

Notes:

  • 动画根节点中的所有组件

    • 骨骼动画的话,动画根节点是骨骼动画绑定的Character
  • 需要使用skeletalAnimation.clips = skeletalAnimation.clips;触发帧事件。

动画事件回调

目前支持的回调事件包括:

  • PLAY:开始播放时触发
  • STOP:停止播放时触发
  • PAUSE:暂停播放时触发
  • RESUME:恢复播放时触发
  • LASTFRAME:假如动画循环次数大于 1,当动画播放到最后一帧时触发。
  • FINISHED:动画播放完成时触发
import { _decorator, Component, Node, SkeletalAnimation, Animation, SkeletalAnimationState } from 'cc';
const { ccclass, property } = _decorator;

@ccclass('SkeletonAnimationEvent')
export class SkeletonAnimationEvent extends Component {
    start() {

        const skeletalAnimation = this.node.getComponent(SkeletalAnimation); // 获取动画组件
        skeletalAnimation.on(Animation.EventType.FINISHED, this.onAnimationFinished, this); // 绑定事件监听
    }

    onAnimationFinished(type:Animation.EventType, state:SkeletalAnimationState){

    }
}

通信

父子

事件冒泡
// 子节点的组件脚本中
this.node.dispatchEvent( new MyEvent('foobar', true, 'detail info') );
// 父节点的组件脚本中
this.node.on('foobar', (event: MyEvent) => {
  event.propagationStopped = true;
});

Notes

  • 如果我们希望在某一级节点截获事件后就不再传递事件,可以通过调用 event.propagationStopped = true 函数来完成。

注入对象实例

调用实例节点方法

if(this.playerCtrl){
    // 禁止接收用户操作人物移动指令
    this.playerCtrl.setInputActive(false);
    // 重置人物位置
    this.playerCtrl.node.setPosition(Vec3.ZERO);
}

调用实例组件方法

  const item = instantiate(this.itemPrefab); // 实例化
  const data = this.items[i];
  this.node.addChild(item);
  item.getComponent('ItemTemplate').init(data) // 查找对应组件,调用方法

其中ItemTemplate组件定义:

import { _decorator, Component, Label, Sprite } from "cc";
import { Item } from "./ItemManager";
const { ccclass, property } = _decorator;

@ccclass("ItemTemplate")
export class ItemTemplate extends Component {
  @property
  public id = 0;
  @property(Sprite)
  public icon: Sprite | null = null;
  @property(Label)
  public itemName: Label | null = null;
  @property(Label)
  public itemPrice: Label | null = null;
  init(data: Item) {
    this.id = data.id;
    this.itemName.string = data.itemName;
    this.itemPrice.string = data.itemPrice;
  }
}

事件线程

Cocos Creator 引擎提供了 EventTarget 类,用以实现自定义事件的监听和发射
import { _decorator, Component, EventTarget } from 'cc';
const { ccclass } = _decorator;
const eventTarget = new EventTarget();

@ccclass("Example")
export class Example extends Component {
    onEnable () {
        // 监听事件可以通过 eventTarget.on() 接口来实现
        eventTarget.on('foobar', this._sayHello, this);
    }

    onDisable () {
        // 当我们不再关心某个事件时,我们可以使用 off 接口关闭对应的监听事件。
        eventTarget.off('foobar', this._sayHello, this);
    }

    _sayHello () {
        console.log('Hello World');
    }
}

// 发射事件可以通过 eventTarget.emit() 接口来实现,参数最多只支持 5 个事件参数
eventTarget.emit(type, ...args);

适配

多分辨率适配

可以通过以下几个部分完成多分辨率适配解决方案:

  • Canvas(画布) 组件随时获得设备屏幕的实际分辨率并对场景中所有渲染元素进行适当的缩放。
  • Widget(对齐挂件)能够根据需要将元素对齐目标节点(默认是父节点)的不同参考位置,可实现弹性布局
    对齐挂件
  • Label(文字) 组件内置了提供各种动态文字排版模式的功能,当文字的约束框由于 Widget 对齐要求发生变化时,文字会根据需要呈现完美的排版效果。

    • 共有 NONECLAMPSHRINKRESIZE_HEIGHT 四种模式:

      • NONE 模式会自动根据文字尺寸、行高等固定约束框尺寸,不支持自定义尺寸
      • 后三种模式 下才能通过编辑器左上角的 矩形变换工具(也可以是按键盘按键 T)或者修改 属性检查器 中的 Size 大小或者添加 Widget 组件 来调整约束框的大小

        • CLAMP:截断,超出的部分会被隐藏。
        • SHRINK

          • 自动缩小模式下,如果文字按照原定尺寸渲染会超出约束框时,会自动缩小文字尺寸以显示全部文字。
          • 自动缩小模式不会放大文字来适应约束框。
        • RESIZE_HEIGHT

          • 自动适应高度模式会保证文字的约束框贴合文字的高度,不管文字有多少行。这个模式非常适合显示内容量不固定的大段文字
            文本约束
  • Layout(自动布局) 可以挂载在任何节点上,将节点变成一个有自动布局功能的容器,实现flex、grid布局
    自动布局
  • Cocos Creator v3.8版本中,通过
    设计尺寸
    设定设计尺寸,需打包为web-mobile

Cocos Creator 提供了两种 Web 平台的页面模板,可以通过 发布平台 的下拉菜单选择 Web Mobile 或 Web Desktop,它们的区别主要在于:

  • Web Mobile 会默认将游戏视图撑满整个浏览器窗口
  • Web Desktop 允许在发布时指定一个游戏视图的分辨率,而且之后游戏视图也不会随着浏览器窗口大小变化而变化。

九宫格格式的图像

如果图像资源是用九宫格的形式生产的,那么不管 Sprite 如何放大,都不会产生模糊或变形。

切分图像

切分图像

  • 在[1]资源管理器中导入图片,如[2]
  • 点击图片资源[2],查看属性检查器[3]
  • 在属性检查器[3]中修改图片资源类型为sprite-frame[5]
  • 点击[6]应用修改
  • 点击Edit编辑,打开Sprite编辑器:
    sprite-frame类型

资源原图

九宫图

应用图像

Sprite应用

  • 在层级管理器[1]中的Cavans节点[2]下创建Sprite节点[3]
  • 拖拽资源管理器[4]中的图片资源[5]到属性检查器[6],将 Sprite 的 Type 属性设为 Sliced[7]。

三方库支持

调试

  1. 在 偏好设置 面板中指定了默认脚本编辑器,便可以在 资源管理器 中双击脚本文件打开代码编辑器快速编辑代码。
  2. Cocos Creator 在顶部菜单栏的 开发者 -> VS Code 工作流 中集成了 添加编译任务 和 添加 Chrome Debug 配置 功能,分别点击可给vs code添加调试任务task
    编译任务
  • 添加编译任务:用于在 VS Code 中触发 Creator 的脚本编译

    • 添加后,可通过vs code任务触发Creator编译,无需重新激活Creator
    • 若不添加,vs code修改脚本后,需要激活Creator进行编译
  • 添加 Chrome Debug 配置:用于调试网页版游戏

    • 启动专门的Chrome调试预览效果
    • 可直接在vs code中调试代码,指定Chrome工具调试
  1. 使用 VS Code 调试网页版游戏

    • 在 VS Code 中点击左侧栏的 调试 按钮打开调试面板,并在最上方的调试配置中选择 Cocos Creator Launch Chrome against localhost,然后点击左侧绿色的开始按钮进行调试

    调试

    • 更新脚本后,在VS Code 里按下快捷键 Cmd/Ctrl + P,激活 快速打开 输入框,然后输入task CocosCreator compile,选择 CocosCreator compile,即可在Chrome中查看最新脚本运行。

    CocosCreator compile

性能优化

静态资源

在 属性检查器 里设置资源虽然很直观,但资源只能在场景里预先设好,没办法动态切换。

动态加载

动态加载应用包内的本地资源

通常我们会把项目中需要动态加载的资源放在 resources 目录下,配合 resources.load 等接口动态加载。
  • 所有需要通过脚本动态加载的资源,都必须放置在 resources 文件夹或它的子文件夹下。

    • resources 文件夹需要在 assets 根目录 下手动创建。
  • 配合 resources.load 等接口动态加载

    • 只要传入相对 resources 的路径即可,并且路径的结尾处 不能 包含文件扩展名
    // 加载 Prefab
    resources.load("test_assets/prefab", Prefab, (err, prefab) => {
        const newNode = instantiate(prefab);
        this.node.addChild(newNode);
    });
    
    // 加载 AnimationClip
    resources.load("test_assets/anim", AnimationClip, (err, clip) => {s
        this.node.getComponent(Animation).addClip(clip, "anim");
    });
  • resources.loadDir 可以加载相同路径下的多个资源

    // 加载 test_assets 目录下所有资源
    resources.loadDir("test_assets", function (err, assets) {
    // ...
    });
    
    // 加载 test_assets 目录下所有 SpriteFrame,并且获取它们的路径
    resources.loadDir("test_assets", SpriteFrame, function (err, assets) {
    // ...
    });

动态加载远程资源

直接调用 assetManager.loadRemote 方法
// 远程 url 带图片后缀名
let remoteUrl = "http://unknown.org/someres.png";
assetManager.loadRemote<ImageAsset>(remoteUrl, function (err, imageAsset) {
    const spriteFrame = new SpriteFrame();
    const texture = new Texture2D();
    texture.image = imageAsset;
    spriteFrame.texture = texture;
    // ...
});

// 远程 url 不带图片后缀名,此时必须指定远程图片文件的类型
remoteUrl = "http://unknown.org/emoji?id=124982374";
assetManager.loadRemote<ImageAsset>(remoteUrl, {ext: '.png'}, function (err, imageAsset) {
    const spriteFrame = new SpriteFrame();
    const texture = new Texture2D();
    texture.image = imageAsset;
    spriteFrame.texture = texture;
    // ...
});

动态加载本地设备存储资源

直接调用 assetManager.loadRemote 方法
// 用绝对路径加载设备存储内的资源,比如相册
const absolutePath = "/dara/data/some/path/to/image.png";
assetManager.loadRemote<ImageAsset>(absolutePath, function (err, imageAsset) {
    const spriteFrame = new SpriteFrame();
    const texture = new Texture2D();
    texture.image = imageAsset;
    spriteFrame.texture = texture;
    // ...
});

Notes:

  • assetManager.loadRemote这种加载方式只支持图片、声音、文本等原生资源类型,不支持 SpriteFrame、SpriteAtlas、TiledMap 等资源的直接加载和解析。
  • Web 端的远程加载受到浏览器的 <u>CORS 跨域策略限制</u>,如果对方服务器禁止跨域访问,那么会加载失败,而且由于 WebGL 安全策略的限制,即便对方服务器允许 http 请求成功之后也无法渲染。

预加载

场景预加载

director.preloadScene("table", function () {    
  console.log('Next scene preloaded');
});
// 在某个时机调用loadScene
// 就算预加载还没完成,直接调用 director.loadScene,预加载完成后场景就会启动
director.loadScene("table");

动态资源预加载

resources.preload('test_assets/image/spriteFrame', SpriteFrame);

/**
* 开发者可以使用预加载相关接口提前加载资源,
* 不需要等到预加载结束即可使用正常加载接口进行加载,
* 正常加载接口会直接复用预加载过程中已经下载好的内容,缩短加载时间。
*/ 
resources.load('test_assets/image/spriteFrame', SpriteFrame, (err, spriteFrame) => {
    this.node.getComponent(Sprite).spriteFrame = spriteFrame;
});

分包预加载

Asset Bundle 中提供了 preloadpreloadDir 接口用于预加载 Asset Bundle 中的资源,具体的使用方式和 assetManager 一致。

分包

Asset Bundle 作为资源模块化工具,允许开发者按照项目需求将贴图、脚本、场景等资源划分在多个 Asset Bundle 中,然后在游戏运行过程中,按照需求去加载不同的 Asset Bundle,以减少启动时需要加载的资源数量,从而减少首次下载和加载游戏时所需的时间。

Asset Bundle 可以按需求放置在不同地方,比如可以放在远程服务器、本地、或者小游戏平台的分包中。

  1. 自定义 Asset Bundle 是以 文件夹 为单位进行配置的。当我们在 资源管理器 中选中一个文件夹时,属性检查器 中就会出现一个 配置为 Bundle 的选项,勾选开启Bundle分包:
    Bundle分包
  2. 定义配置,点击面板右上方的 绿色打钩按钮,该文件夹就被配置为 Asset Bundle 的打包预设集合了,在放置需要的资源后,然后在 构建发布 面板选择对应的平台进行构建即可得到对应的 Asset Bundle。
    Asset Bundle 配置面板
  3. assetManager.loadBundleAPI传入 Asset Bundle 配置面板中的 Bundle 名称 或者 Asset Bundle 的 url来加载 Asset Bundle
assetManager.loadBundle('01_graphics', (err, bundle) => {
    bundle.load(`prefab`, Prefab, function (err, prefab) {
        let newNode = instantiate(prefab);
        director.getScene().addChild(newNode);
    });
    // 加载 textures 目录下的所有资源
    bundle.loadDir("textures", function (err, assets) {
        // ...
    });

    // 加载 textures 目录下的所有 Texture 资源
    bundle.loadDir("textures", Texture2D, function (err, assets) {
        // ...
    });
});

// 当复用其他项目的 Asset Bundle 时
assetManager.loadBundle('https://othergame.com/remote/01_graphics', (err, bundle) => {
    bundle.load('xxx');
});

Notes:

  • 在通过 API 加载 Asset Bundle 时,引擎并没有加载 Asset Bundle 中的所有资源,而是加载 Asset Bundle 的 资源清单,以及包含的 所有脚本
  • 当 Asset Bundle 加载完成后,会触发回调并返回错误信息和 AssetManager.Bundle 类的实例,这个实例就是 Asset Bundle API 的主要入口,开发者可以使用它去加载 Asset Bundle 中的各类资源。
  1. 释放 Asset Bundle 中的资源
在资源加载完成后,所有的资源都会被临时缓存到 assetManager 中,以避免重复加载。当然,缓存中的资源也会占用内存,有些资源如果不再需要用到,需要进行释放
// 方式一、使用常规的 assetManager.releaseAsset 方法进行释放。
bundle.load(`image/spriteFrame`, SpriteFrame, function (err, spriteFrame) {
    assetManager.releaseAsset(spriteFrame);
});
// 方式二、使用 Asset Bundle 提供的 release 方法,释放在 Asset Bundle 中的单个资源。
bundle.load(`image/spriteFrame`, SpriteFrame, function (err, spriteFrame) {
    bundle.release(`image`, SpriteFrame);
});
// 方式三、使用 Asset Bundle 提供的 releaseAll 方法,释放所有属于该 bundle 的资源
bundle.load(`image/spriteFrame`, SpriteFrame, function (err, spriteFrame) {
    bundle.releaseAll();
});
  1. 移除 Asset Bundle
let bundle = assetManager.getBundle('bundle1');
// 释放在 Asset Bundle 中的单个资源
bundle.release(`image`, SpriteFrame);
assetManager.removeBundle(bundle);

let bundle = assetManager.getBundle('bundle1');
// 释放所有属于 Asset Bundle 的资源
bundle.releaseAll();
assetManager.removeBundle(bundle);

资源释放

资源引用类型:

  • 当开发者在编辑器中编辑资源时(例如场景、预制件、材质等),需要在这些资源的属性中配置一些其他的资源,例如在材质中设置贴图,在场景的 Sprite 组件上设置 SpriteFrame。那么这些引用关系会被记录在资源的序列化数据中,引擎可以通过这些数据分析出依赖资源列表,像这样的引用关系就是静态引用

    • Asset Manager 提供了一套基于引用计数的资源释放机制,会自动统计资源之间的静态引用。
  • 当开发者在编辑器中没有对资源做任何设置,而是通过代码动态加载资源并设置到场景的组件上,则资源的引用关系不会记录在序列化数据中,引擎无法统计到这部分的引用关系,这些引用关系就是动态引用

    • 如果动态加载出来的资源需要长期引用、持有,或者复用时,建议使用 addRef 接口手动增加引用计数。

资源释放类型:

  • 自动释放

    • 自动释放的优势在于不用显式地调用释放接口,开发者只需要维护好资源的引用计数,Creator 会根据引用计数自动进行释放。
    • 其中,静态引用的资源开发者不用维护引用计数,Creator会自动统计。动态资源引用需要开发者手动维护引用计数。
  • 手动释放

    • 显式调用 release 系列接口
    • 可以确保资源本身一定会被释放
    • 资源本身不会进行释放检查,只有其依赖资源会进行释放检查

物理系统

物体需要具备完全物理特性的前提条件物体同时具备 刚体 和 碰撞体,并调整好其质心位置和碰撞体的形状。

碰撞体

碰撞组件属性 IsTrigger 决定了组件为触发器还是碰撞器。
  • 将 IsTrigger 设置为 true 时,组件为触发器。

    • 触发器只用于碰撞检测和触发事件,会被物理引擎忽略。
    • 触发器不会与其它触发器或者碰撞器做更精细的检测。
  • 默认设置 false,组件为碰撞器,可以结合刚体产生碰撞效果。

    • 碰撞器与碰撞器会做更精细的检测,并会产生碰撞数据,如碰撞点、法线等。

刚体

碰撞体间定义碰撞发生的可能性是通过刚体的 Group 属性,而非 Node 的 Layer 属性。

刚体类型

  • STATIC静态刚体:

    • 静态刚体在大多数情况下用于一些始终停留在一个地方,不会轻易移动的游戏物体,例如:建筑物。
    • 静态刚体与其他物体发生碰撞时,不会产生物理行为,因此,也不会移动。
  • DYNAMIC动力学刚体:

    • 刚体碰撞完全由物理引擎模拟,可以通过 力的作用 运动物体(需要保证质量大于 0)
    • 仅有动力学刚体的相互碰撞才有真实世界的效果

      • 其中有一个是非动力学刚体,相互碰撞没有真实世界的效果。
  • KINEMATIC运动学刚体:

    • 它与静态刚体类似,只是静态刚体与其他物体发生碰撞时,不会产生物理行为,而运动学刚体会对其他对象施加摩擦力,并在接触时唤醒其他刚体。
    • 通常用于表达电梯这类平台运动的物体

物理材质

物理材质是一种资源,它记录了物体的物理属性,这些信息用来计算碰撞物体受到的摩擦力和弹力等。

物理事件

  • 触发事件

    • 用于检测两个物体是否进入或离开彼此的触发区域。
    • 这种事件通常用于不需要物理碰撞效果的情况,例如进入一个区域来触发某个效果或行为。

      • 没有物理反应:不会导致物理碰撞效果,如反弹或力的作用
      • 主要用于区域检测:常用于检测进入、停留和离开某个区域的情况
    • 接收到触发事件的前提是两者都必须带有碰撞组件,并且至少有一个是触发器类型
  • 碰撞事件

    • 用于检测两个物体之间的物理碰撞。
    • 这种情况通常用于需要物理反应的情况,例如角色与障碍物的碰撞。

      • 有物理反应:碰撞事件通常会导致物理反应,例如反弹、推力等。
      • 用于物理交互:常用于检测和处理实际的物理碰撞。
    • 接收到碰撞事件的前提是两者都必须带有碰撞组件都不是触发器类型、至少有一个是非静态刚体并且使用的是非 builtin 的物理引擎。

真实世界碰撞效果

    protected shoot(velocity: number) {
        // 获取炮弹
        const bulletNode = this.generateBullet(),
            bulletRigidBody = bulletNode.getComponent(RigidBody);
        // 方向和速度(默认前方为 -z 方向,需要反过来)
        const direction = bulletNode.forward.negative();
        direction.multiplyScalar(velocity);
        // 给刚体设置速度
        bulletRigidBody.setLinearVelocity(direction);
    }

Notes:

  • 产生真实世界的物理碰撞效果,不应该依赖使用position的变更产生碰撞,这样无法产生物理碰撞的动力学效果。
  • 应该通过设置刚体的速度,让其运动,与其他刚体产生碰撞,从而产生真实世界的碰撞效果。

射线检测

射线检测是对一条射线和非动力学刚体碰撞组件进行相交性判断

检测对象需要满足:

  • 检测的对象是物理碰撞器,在场景面板上与之对应的是碰撞器组件,例如 BoxCollider。
  • 检测的对象仅支持STATICKINEMATIC刚体类型,不支持DYNAMIC刚体类型
// bullet物理引擎测试结果:
const worldRay = new geometry.Ray(0, -1, 0, 0, 1, 0); // 世界坐标下的射线
// 以下参数可选
const mask = 0xffffffff;
const maxDistance = 10000000;
const queryTrigger = true;

const rayCollision = PhysicsSystem.instance.raycast(worldRay, mask, maxDistance, queryTrigger);
if(rayCollision){
    const results = PhysicsSystem.instance.raycastResults;
    for (let i = 0; i < results.length; i++) {
        const result = results[i];
        const collider = result.collider;
        const distance = result.distance;
        const hitPoint = result.hitPoint;
        const hitNormal = result.hitNormal;
    }
}

Notes:

  • 射线起点需要是世界坐标node.getWorldPosition(new Vec3())
  • 射线穿过平面的边界时,浮点运算的精度问题,可能会被记录为多次碰撞。

    • 在某些(如 Bullet)物理后端中,由于计算精度的原因,应该避免使用比例很高的尺寸,这里建议低于1000。如某个盒碰撞器,其 Size 属性的 Y 值为 40 而 Z 值为 0.01,此时他们的 Y、Z 的比例超过了 1000,此时可能会出现浮点数计算不准确的问题。
    • 切换物理引擎试试,如cannonjs没有此问题。

粒子系统

  1. 添加3D粒子系统
    创建3D粒子系统

    • 在[1]层级管理器中右键[2]创建特效 —> 粒子系统[3]
  2. 添加粒子材质
    粒子材质

    • 在[1]资源管理器中[2]右键 —> [3]创建 —> 材质[4]
  3. 处理贴图

使用Photoshop等DCC设计工具去除素材的颜色,仅保留明度的信息:

  • 打开图片: 打开你想要处理的图片。
  • 去色:

    • 依次点击 图像 > 调整 > 去色 或按快捷键 Shift+Ctrl+U,这样会去除图像中的所有颜色,仅保留灰度信息。
  • 调整对比度(可选):

    • 为了增强图像效果,你可以调整对比度。依次点击 图像 > 调整 > 亮度/对比度,进行适当调整。
  • 保存图像:

    • 选择 文件 > 存储为,保存为你需要的格式(如 PNG 或 JPEG)。
  1. 配置粒子贴图
    粒子贴图

    • 资源管理器中选中创建的粒子材质,通过属性检查器[1]配置材质属性;
    • 粒子属性Effect设定为particles/builtin-particle;
    • 展开[4]配置表,将贴图资源拖拽到Main Texture,将材质贴图;

Good to know:

  • Effect:用来定义材质具体的渲染操作和逻辑的脚本。

    • 它通常由一个或多个着色器(Shader)程序组成
    • 这些程序在 GPU 上运行,负责执行顶点和像素的处理。
  • Techniques:定义渲染技术和流程,每个技术包含多个渲染阶段(Pass)。

    • addadd-smoothpremultiply-blend 是常见的混合模式,用于控制不同材质的混合方式。
  • Passes:定义具体的渲染阶段,包括顶点着色器和片段着色器。
  1. 粒子渲染贴图
    渲染贴图

    • 层级管理器中选中添加的粒子系统,在属性检查器[1]配置渲染贴图;
    • 展开[2]粒子渲染器配置表,将粒子材质拖拽到Cpu Material[4];

文件模板

自定义html构建模板

创建html构建模板
修改index.ejs模板

  1. cocos creator编辑器中添加构建模版;
  2. 在代码编辑器中修改index.ejs模板;
  3. build-templates/web-desktop目录下的资源会自动打包到build目录中

    • build-templates/web-desktop放置ico图标可以用于修改favicon.ico

构建模板更多详见

脚本模板

.creator/asset-template/typescript/CustomComponent添加脚本模板:

import { _decorator, Component, Node } from 'cc';
const { ccclass, property } = _decorator;

/**
 * 
 * <%UnderscoreCaseClassName%>
 * <%CamelCaseClassName%>
 * <%Author%>
 * <%DateTime%>
 * <%FileBasename%>
 * <%FileBasenameNoExtension%>
 * <%URL%>
 * <%ManualUrl%>
 *
 */

@ccclass('Robot<%CamelCaseClassName%>')
export class Robot<%CamelCaseClassName%> extends Component {
    start() {

    }

    update(deltaTime: number) {

    }
}

脚本模板更多详见

打包

构建支持平台

平台平台构建插件名支持的特殊文件类型
华为 AGChuawei-agc暂不支持
支付宝小游戏alipay-mini-gamegame.json
字节跳动小游戏bytedance-mini-gamegame.ejs、game.json、project.config.json
OPPO 小游戏oppo-mini-gamemanifest.json
华为快游戏huawei-quick-game暂不支持
Cocos Playcocos-playgame.config.json
vivo 小游戏vivo-mini-gameproject.config.json
小米快游戏xiaomi-quick-gamemanifest.json
百度小游戏baidu-mini-gamegame.json、project.swan.json
微信小游戏wechatgamegame.ejs、game.json、project.config.json
Web Desktopweb-desktopindex.ejs
Web Mobileweb-mobileindex.ejs
原生平台native暂不支持

支持平台

打包

在[1]项目菜单中选择[2]构建发布
构建发布

自定义插屏

场景初始化加载预览图片
  • 通过[1]开启/关闭插屏效果。
  • 点击[2]可以开启自定义插屏面板。
    开启插屏

插屏配置

社区

引擎对比

基本情况Three.jsUnity3DCocos Creator
定位轻量级3D库游戏开发引擎轻量级游戏开发引擎
二次开发语言Javascript开发需安装Unity编辑器,C#语言开发需安装Cocos Creator编辑器,Typescript开发
模型来源三方提供,手动维护三方提供,手动维护三方提供,手动维护
辅助器提供了各种辅助工具,如射线辅助线、坐标辅助线提供了部分辅助工具,如射线辅助线没有辅助工具,需要自己通过现有资源模拟

小技巧

通过常驻节点进行场景资源管理和参数传递

引擎同时只会运行一个场景,当切换场景时,默认会将场景内所有节点和其他实例销毁。如果我们需要用一个组件控制所有场景的加载,或在场景之间传递参数数据,就需要将该组件所在节点标记为「常驻节点」,使它在场景切换时不被自动销毁,常驻内存。
// 添加一个节点的常驻属性:
director.addPersistRootNode(myNode);
// 要取消一个节点的常驻属性:
director.removePersistRootNode(myNode);

Notes:

  • 目标节点必须为位于层级的根节点,否则设置无效。
  • removePersistRootNode并不会立即销毁指定节点,只是将节点还原为可在场景切换时销毁的节点。

Blender使用mixamo处理骨骼动画

  1. github中直接通过zip包下载源码
  2. 在[编辑]菜单中选择[偏好设置]
    偏好设置
  3. 在[偏好设置]面板中选择[插件]。
  4. 在[2]选择zip安装包进行安装,安装完成后,可在3的检索的基础上看到mixamo列表项。
  5. 勾选[5]开启mixamo插件。
    mixamo插件安装应用
  6. mixamo开启插件设置
    mixamo插件应用

预览看不到地形的贴图

仅仅需要在cocos编辑器中保存一下即可。

鼠标控制相机巡航

import { _decorator, Component, Event, Input, input, math, Node, Quat, Vec2 } from "cc";
const { ccclass, property } = _decorator;

/**
 * 该组件绑定在场景根相机节点上
 * 脚本中的this.node为相机节点
 */
@ccclass("CameraController")
export class CameraController extends Component {
  private _isDrag = false // 鼠标事件触发
  private _triggerMove = false // 移动状态:避免两次直接点击产生场景旋转
  private _currentEulerAngles = new Quat() // 记录鼠标事件触发前的四元数
  private _previousLocation = Vec2.ZERO // 记录鼠标事件触发前的节点位置
  private _currentLocation = Vec2.ZERO // 记录拖拽过程中节点的位置
  private _rotateSpeed = 0.02 // 节点位置与旋转角度切换比例
  start() {
    input.on(Input.EventType.MOUSE_DOWN, this.mouseDown, this);
    input.on(Input.EventType.MOUSE_MOVE, this.mouseMove, this);
    input.on(Input.EventType.MOUSE_UP, this.mouseUp, this);
  }
  onDestroy() {
    input.off(Input.EventType.MOUSE_DOWN, this.mouseDown, this);
    input.off(Input.EventType.MOUSE_MOVE, this.mouseMove, this);
    input.off(Input.EventType.MOUSE_UP, this.mouseUp, this);
  }
  // 每一帧更新时,处理旋转角度;
  update(deltaTime: number) {
    if (this._isDrag && this._triggerMove) { // 只有处于移动状态下,才处理场景旋转
      const delta = new Vec2()
      Vec2.subtract(delta, this._currentLocation, this._previousLocation) // 获取前后节点位置的坐标差
      const quat = new Quat()
      Quat.fromEuler(quat, delta.y * this._rotateSpeed, -delta.x * this._rotateSpeed, 0) // 将节点位置坐标差按_rotateSpeed比例转换为旋转角度四元数
      Quat.multiply(this._currentEulerAngles, quat, this._currentEulerAngles) // 计算目标四元数
      this.node.setRotation(this._currentEulerAngles) // 设置节点旋转角度
    }
  }
  mouseDown(e: Event) {
    console.log("🚀 ~ CameraController ~ mouseDown ~ e:", e)
    this._isDrag = true;
    this._triggerMove = false;
    const downPoint = e.getLocation()
    this._previousLocation = downPoint
    this._currentEulerAngles = this.node.getRotation()
  }
  mouseMove(e: Event) {
    if (this._isDrag) { // 鼠标事件触发后,才处理鼠标移动事件
      this._triggerMove = true
      console.log("🚀 ~ CameraController ~ mouseMove ~ e:", e)
      const currentPoint = e.getLocation()
      this._currentLocation = currentPoint;
    }
  }
  // 鼠标事件结束后,重置状态
  mouseUp(e: Event) {
    this._isDrag = false
    this._triggerMove = false;
    this._currentEulerAngles = new Quat()
    this._previousLocation = Vec2.ZERO
    this._currentLocation = Vec2.ZERO
  }
}

控制子节点活动范围

  onTouchMove(event: EventTouch) {
    console.log("🚀 ~ UIJoyStick ~ onTouchMove ~ onTouchMove:")
    // 获取当前光标所在位置
    const ux = event.touch.getUILocationX();
    const uy = event.touch.getUILocationY();
    const worldPosition = new Vec3(ux, uy, 0);
    const localPosition = v3();
    /**
     * Start:控制子节点活动范围
     */
    // 父子节点相对位置:当前光标节点相对于父节点joyStick坐标
    this.joyStick.inverseTransformPoint(localPosition, worldPosition);
    console.log("🚀 ~ UIJoyStick ~ onTouchMove ~ localPosition:", localPosition.clone())
    let figurePosition = v3();
    // 子节点相对于父节点的距离
    let len = localPosition.length();
    // 子节点相对于父节点的方向矢量
    localPosition.normalize();
    // 防止子节点移动超出父节点范围
    Vec3.scaleAndAdd(figurePosition, v3(), localPosition, math.clamp(len, 0, this.radius));
    /**
     * End:控制子节点活动范围
     */
    this.joyStickFigure.setPosition(figurePosition);
  }

image.png

多相机渲染

Cocos Creator 中,camera.priority属性用于控制多个相机渲染的顺序。
这个属性的作用是确定相机在渲染场景时的优先级。优先级高的相机会在优先级低的相机之后渲染,这样可以实现多种效果,如覆盖、叠加、后处理等。
  • 多相机渲染:如果场景中有多个相机(例如,一个用于主场景渲染,另一个用于UI渲染),通过设置 priority 可以确保UI相机在主场景相机之后渲染,以便UI能正确地显示在前面。

    • 优先级高的相机在后面渲染:通过设置 priority 属性,我们可以控制多个相机的渲染顺序。优先级值较高的相机会在优先级值较低的相机之后渲染。这样可以确保一些需要覆盖在其他渲染内容之上的效果可以正确显示。
  • 后处理效果:在一些高级效果中,可以通过多相机设置不同的渲染顺序,结合 RenderTexture 实现后处理效果,比如屏幕后处理、特殊滤镜等。

    • 灵活的渲染顺序控制:利用 priority 属性,可以实现灵活的渲染顺序控制。例如,在复杂的场景中,可以通过调整相机的优先级实现不同层次的渲染和叠加效果。
  • 镜头特效:在某些特效需要不同的相机角度或者视图时,可以使用多个相机并调整优先级来达到预期效果。

Notes:

  • 性能考虑:虽然多个相机可以实现复杂的效果,但需要注意渲染性能。每增加一个相机都会增加一次渲染操作,可能会影响性能。
  • 合理设置相机视口:确保每个相机的视口设置合理,避免不必要的渲染操作和性能浪费。
    通过合理设置camera.priority,可以在Cocos Creator中实现复杂的多相机渲染效果,提升游戏的视觉表现力。

概念

引擎:

  1. 内置完整的工具集,而非生态扩展
  2. 特定领域,而非通用
  3. 专门性

运行时:

  1. 可以是通用的(如语言运行时)或特定功能的(如游戏引擎运行时)
  2. 包括解释器,解释和执行代码

Node vs. Component

Node:

  • Node是 Cocos Creator 中场景的基本构建块,所有的场景、UI 元素、精灵、动画等都是通过节点来组织和管理的。
  • 节点本身不包含具体的功能或行为,它们只是承载其他组件的容器。
  • 类似threejs中的Three.mesh

Component:

  • Component 是附加在节点上的脚本或功能模块,用于定义节点的具体行为和功能。
  • 组件不能独立存在,它们必须附加到一个节点上,节点可以附加多个组件。
  • 组件决定了节点的行为和功能。
  • 类似threejs中的Three.geomotry、Three.material

Prefab(预制)资源

Prefab(预制)资源,作为我们动态生成节点时使用的模板。

cc类

装饰器 ccclass 应用在类上时,此类称为 cc 类。

  • 未声明 ccclass 的组件类,也无法作为组件添加到节点上。
  • ccclass 装饰器的参数 name 指定了 cc 类的名称,cc 类名是 独一无二 的,这意味着即便在不同目录下的同名类也是不允许的。
  • 类名不应该以 cc.internal. 作为前缀,这是 Cocos Creator 的保留类名前缀。

组件类装饰器

ccclass装饰器 修饰,且继承 Component 的子类,此类称为 cc 组件类。
executeInEditMode:动态加载的预制体
import {
  _decorator,
  Component
} from "cc";
const { ccclass, property, executeInEditMode } = _decorator;

@ccclass("GameManager")
@executeInEditMode
export class GameManager extends Component {

}

默认在层级管理器和场景管理器中看不到动态加载的预制体或者动态创建的节点 new Node(),可以通过[@executeInEditMode装饰器](https://docs.cocos.com/creator/3.8/api/en/function/_decorator.executeInEditMode)使继承自组件的ccclass在编辑模式下执行。

Notes:

  • 使用@executeInEditMode执行过的ccclass无法还原成未执行状态。

更多内容详见

executionOrder:生命周期回调的执行优先级

小于 0 的脚本将优先执行,大于 0 的脚本将最后执行。排序方式如下:

  • 对于同一节点上的不同组件,数值小的先执行,数值相同的按组件添加先后顺序执行
  • 对于不同节点上的同一组件,按节点树排列决定执行的先后顺序

可以通过组件类装饰器executionOrder 用来指定脚本生命周期回调的执行优先级。

const { ccclass, executionOrder } = _decorator;

@ccclass('Example')
@executionOrder(3)
export class Example extends Component {
}

属性装饰器

在编辑器 属性检查器 中展示的属性,属性名开头不应该带 _,否则会识别为 private 属性,private 属性不会在编辑器组件属性面板上显示

属性装饰器参数详见

基础属性类型
CCIntegerCCFloatCCBooleanCCStringCocos Creator基础属性类型标识
  • 当声明 JavaScript 内置构造函数 NumberStringBoolean 用作类型时将给出警告,并且将分别视为 cc 类型中的 CCFloatCCStringCCBoolean
group分组
当脚本中定义的属性过多且杂时,可通过 group 对属性进行分组、排序,方便管理。同时还支持对组内属性进行分类。

group 写法包括以下两种:

  • @property({ group: { name } })
  • @property({ group: { id, name, displayOrder, style } })

米花儿团儿
1.3k 声望75 粉丝