模块 0:Cocos Creator → Unity 迁移对照

Day 1 ⏱ 预计阅读 1.5 小时

💡 本模块目标

帮助有 Cocos Creator 2D 经验的开发者快速建立 Unity 的概念映射。每一项都给出 Cocos(TypeScript)和 Unity(C#)的并排完整代码。

引言:为什么学对照比从零学更快

你已经在 Cocos Creator 中掌握了游戏开发的核心概念:节点树、组件模式、游戏循环、Prefab、坐标系、资源加载。Unity 的底层逻辑完全一致,只是 API 命名、编辑器布局、语言(TypeScript → C#)不同。

本模块采用「先说你已经懂的,再告诉你 Unity 里怎么写」的策略。

0.1 编辑器概念对照

两个引擎的编辑器布局非常相似,但名称和细节不同:

Cocos CreatorUnity说明
场景编辑器(Canvas)Scene View可视化编辑场景中物体的位置、大小、旋转
层级管理器Hierarchy 窗口树状显示场景中所有节点/GameObject 的父子关系
属性检查器Inspector 窗口选中物体后显示并编辑其所有组件属性
资源管理器Project 窗口浏览 Assets/ 目录下所有资源文件
控制台Console 窗口显示 Log/Warning/Error 输出
节点(Node)GameObject场景中的一个实体对象
组件(Component)Component / MonoBehaviour挂载到节点上的功能模块
场景(.fire)Scene(.unity)一个游戏关卡/画面的数据文件
预制体(.prefab)Prefab(.prefab)可复用的节点/GameObject 模板
构建发布Build Settings → Build打包发布到目标平台
🔑 关键差异

Cocos 的节点默认有 UITransform 组件(2D),Unity 的 GameObject 默认有 Transform 组件(3D)。在 Unity 中做 2D 游戏时,使用 SpriteRendererRectTransform(UI)。

0.2 组件化架构对照

两个引擎都采用组合优于继承的组件模式,但语法完全不同。

Unity Hierarchy 父子层级

添加与获取组件

// === Cocos Creator (TypeScript) ===
import { _decorator, Component, Sprite } from 'cc';
const { ccclass, property } = _decorator;

@ccclass('Player')
export class Player extends Component {
    onLoad() {
        // 获取已有的 Sprite 组件
        let sprite = this.node.getComponent(Sprite);
        // 添加新的组件
        let col = this.node.addComponent(BoxCollider2D);
    }
}
// === Unity (C#) ===
using UnityEngine;

public class Player : MonoBehaviour
{
    void Awake()
    {
        // 获取已有的 SpriteRenderer 组件
        SpriteRenderer sprite = GetComponent<SpriteRenderer>();
        // 添加新的组件
        BoxCollider col = gameObject.AddComponent<BoxCollider>();
    }
}

装饰器 vs 特性(Attribute)

Cocos(装饰器)Unity(Attribute)作用
@ccclass('Name')无需标记注册为可序列化类
@property(Node)public GameObject target;Inspector 中可拖拽赋值
@property({ type: CCInteger })public int hp = 100;类型化 Inspector 字段
@property({ visible: false })[HideInInspector] public int x;隐藏字段
@property({ min: 0, max: 100 })[Range(0, 100)] public float x;限制数值范围
@property({ tooltip: '...' })[Tooltip("...")] public int x;鼠标悬停提示

完整对照:一个带属性的组件

// === Cocos Creator ===
@ccclass('FarmManager')
export class FarmManager extends Component {
    @property(Node)
    playerNode: Node = null;
    @property({ type: Prefab })
    cropPrefab: Prefab = null;
    @property
    gold: number = 100;
    @property({ min: 1, max: 10 })
    cropGrowSpeed: number = 3;

    start() {
        let crop = instantiate(this.cropPrefab);
        crop.setParent(this.node);
    }
}
// === Unity (C#) ===
public class FarmManager : MonoBehaviour
{
    public GameObject playerNode;     // 拖拽赋值
    public GameObject cropPrefab;     // 拖拽 Prefab 赋值
    public int gold = 100;
    [Range(1, 10)]
    public float cropGrowSpeed = 3f;

    void Start()
    {
        GameObject crop = Instantiate(cropPrefab);
        crop.transform.SetParent(transform);
    }
}
⚠️ 重要差异

Cocos 中 @property 显式标记要暴露的字段;Unity 中 public 字段自动暴露,private 字段需要加 [SerializeField] 才能暴露。

0.3 生命周期对照

这是迁移时最重要的概念。两个引擎的游戏循环逻辑完全一致,只是方法名不同:

Cocos CreatorUnity时机
onLoad()Awake()对象被创建/激活时立即调用,初始化自身引用
onEnable()OnEnable()组件启用时(每次 setActive(true) 都触发)
start()Start()第一次 update 之前调用一次
update(dt)Update()每帧调用,dt = Time.deltaTime
lateUpdate()LateUpdate()所有 Update 之后,适合跟随相机
onDisable()OnDisable()组件被禁用/对象被隐藏时
onDestroy()OnDestroy()对象被销毁时

生命周期执行顺序图

Unity MonoBehaviour 生命周期执行顺序 Awake() — 对象创建时 OnEnable() — 启用时 Start() — 首帧前一次 每帧循环 (Game Loop) FixedUpdate Update() LateUpdate() Coroutine yield 在此期间执行 渲染 → 物理 → 碰撞检测 → 回到下一帧 FixedUpdate: 固定时间步长(默认0.02s) | Update: 每帧 | LateUpdate: Update后 OnDisable → OnDestroy 循环

Unity MonoBehaviour 生命周期执行顺序图

完整生命周期代码对照

// === Cocos Creator ===
@ccclass('LifeDemo')
export class LifeDemo extends Component {
    onLoad()      { console.log('onLoad'); }
    onEnable()    { console.log('onEnable'); }
    start()       { console.log('start'); }
    update(dt)    { // dt = 帧间隔(秒)
        this.node.setPosition(this.node.position.x + 100 * dt, this.node.position.y);
    }
    lateUpdate()  {}
    onDisable()   { console.log('onDisable'); }
    onDestroy()   { console.log('onDestroy'); }
}
// === Unity (C#) ===
using UnityEngine;

public class LifeDemo : MonoBehaviour
{
    void Awake()     { Debug.Log("Awake"); }
    void OnEnable()   { Debug.Log("OnEnable"); }
    void Start()      { Debug.Log("Start"); }
    void Update()     {
        // Time.deltaTime 等价于 Cocos 的 dt
        transform.position += new Vector3(100f * Time.deltaTime, 0, 0);
    }
    void LateUpdate() {}
    void OnDisable()  { Debug.Log("OnDisable"); }
    void OnDestroy()  { Debug.Log("OnDestroy"); }
}
⚠️ Awake vs Start 陷阱

所有 Awake 先执行完,再执行所有 Start。如果 A 的 Awake 中引用 B 脚本,而 B 还没 Awake,会拿到 null。把依赖其他组件的初始化放在 Start 里更安全——和 Cocos 的 onLoad/start 完全一致。

0.4 Prefab 对照

Prefab(预制体)概念完全相同:把配置好的对象保存为模板,运行时动态创建实例。

操作Cocos CreatorUnity
创建 Prefab节点拖到资源管理器GameObject 拖到 Project 窗口
代码中引用@property(Prefab) 拖拽public GameObject prefab; 拖拽
实例化instantiate(prefab)Instantiate(prefab)
设父节点node.setParent(parent)obj.transform.SetParent(parent)
销毁node.destroy()Destroy(obj)
Prefab 变体不支持Prefab Variant(继承并覆盖)

完整代码:动态生成农作物

// Cocos Creator
@property({ type: Prefab })
cropPrefab: Prefab = null;

spawnCrop(x, y) {
    let crop = instantiate(this.cropPrefab);
    crop.setPosition(new Vec3(x, y, 0));
    crop.setParent(this.node);
}
// Unity (C#)
public GameObject cropPrefab; // Inspector 拖拽

public void SpawnCrop(float x, float z) {
    GameObject crop = Instantiate(cropPrefab);
    crop.transform.position = new Vector3(x, 0f, z);
    crop.transform.SetParent(transform);
}

0.5 坐标系与变换对照

这是从 Cocos 2D → Unity 3D 迁移时最大的思维转变。

Cocos Creator 2D X → Y ↑ (x, y) 原点 (0,0) Unity 3D X → Y ↑ Z (朝屏幕外) (x, y, z) 左手坐标系, Y轴朝上

Cocos Creator 2D vs Unity 3D 坐标系对比

概念Cocos 2DUnity 3D
维度2D:(x, y)3D:(x, y, z)
Z 轴节点排序(越小越前)深度轴(Z+ 朝屏幕外/前方)
位置node.position(相对父)localPosition(相对父)
position(世界坐标)
旋转angle: number(Z 轴度数)rotation: Quaternion
eulerAngles: Vector3
缩放scale: Vec3localScale: Vector3
锚点anchorPoint (0~1)Transform 无锚点;UI 用 RectTransform.pivot
⚠️ 从 2D 到 3D 的思维转变

在 Cocos 2D 中只关心 (x, y);Unity 3D 中地面是 XZ 平面(Y 为高度)。农场游戏中,地块排列是 X 和 Z 方向,Y = 0 是地面。

位置与旋转代码对照

// Cocos Creator
this.node.setPosition(new Vec3(100, 200, 0));
this.node.setRotationFromEuler(0, 0, 45);
this.node.setScale(new Vec3(2, 2, 1));
// Unity (C#)
transform.position = new Vector3(100f, 200f, 0f);
transform.localPosition = new Vector3(5f, 0f, 0f);
transform.rotation = Quaternion.Euler(0f, 45f, 0f);
transform.localScale = new Vector3(2f, 2f, 2f);

0.6 资源加载方式对照

场景Cocos CreatorUnity
Inspector 拖拽@property(SpriteFrame)public Sprite sprite;
Resources 加载resources.load(path, type, cb)Resources.Load<T>("path")
异步加载回调 或 assetManager.loadAny()Addressables / 协程
远程资源assetManager.loadRemote(url)UnityWebRequest
释放资源assetManager.releaseAsset()Resources.UnloadUnusedAssets()
// Cocos Creator
resources.load('textures/farm_bg/spriteFrame', SpriteFrame, (err, asset) => {
    if (err) return;
    this.node.getComponent(Sprite).spriteFrame = asset;
});
// Unity (C#)
Sprite bgSprite = Resources.Load<Sprite>("textures/farm_bg");
GetComponent<SpriteRenderer>().sprite = bgSprite;
// 注意:路径不含扩展名,不含 "Resources/" 前缀
💡 Resources vs Addressables

Resources.Load 最简单但不推荐生产环境:无法分包、无法卸载。Addressables 是官方推荐方案,支持异步加载和分包,非常适合抖音小游戏场景(模块 2 和 4 详讲)。

0.7 输入系统对照

操作Cocos CreatorUnity
触摸/点击node.on(TOUCH_START, cb)Input.GetMouseButtonDown(0)
键盘input.on(KEY_DOWN, cb)Input.GetKeyDown(KeyCode.Space)
鼠标位置event.getLocation()Input.mousePosition
射线检测无(2D 用碰撞)Physics.Raycast()
// Cocos Creator
input.on(Input.EventType.KEY_DOWN, (event: EventKeyboard) => {
    if (event.keyCode === 65) console.log('A pressed');
});
this.node.on(Node.EventType.TOUCH_START, (event) => {
    let pos = event.getLocation();
});
// Unity (C#)
void Update()
{
    if (Input.GetKeyDown(KeyCode.A))
        Debug.Log("A pressed");
    if (Input.GetKey(KeyCode.W))
        transform.Translate(Vector3.forward * 5f * Time.deltaTime);
    if (Input.GetMouseButtonDown(0))
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        if (Physics.Raycast(ray, out RaycastHit hit))
            Debug.Log($"Hit: {hit.collider.gameObject.name}");
    }
}

0.8 场景管理对照

操作Cocos CreatorUnity
加载场景director.loadScene('name')SceneManager.LoadScene("name")
异步加载director.preloadScene()SceneManager.LoadSceneAsync("name")
跨场景保留game.addPersistRootNode(node)DontDestroyOnLoad(gameObject)
场景注册构建面板注册Build Settings → Add Open Scenes
// Cocos Creator
director.loadScene('farm', () => { console.log('加载完成'); });
// Unity (C#) — 异步加载+进度条
IEnumerator LoadSceneAsync()
{
    AsyncOperation op = SceneManager.LoadSceneAsync("Farm");
    op.allowSceneActivation = false;
    while (!op.isDone)
    {
        float progress = Mathf.Clamp01(op.progress / 0.9f);
        Debug.Log($"Loading: {progress * 100}%");
        if (op.progress >= 0.9f) op.allowSceneActivation = true;
        yield return null;
    }
}
DontDestroyOnLoad(gameObject); // 跨场景保留

0.9 迁移速查总表

这张表是你迁移过程中最常用的参考。建议收藏本页。

类别Cocos Creator (TS)Unity (C#)
导入import { X } from 'cc'using UnityEngine;
类声明@ccclass class X extends Componentpublic class X : MonoBehaviour
获取组件this.node.getComponent(X)GetComponent<X>()
添加组件this.node.addComponent(X)gameObject.AddComponent<X>()
子节点this.node.childrentransform.childCount / GetChild(i)
查找节点find('path/to/node')GameObject.Find("path")
激活/禁用this.node.active = truegameObject.SetActive(true)
帧间隔update(dt) 参数Time.deltaTime 属性
定时器this.schedule(cb, interval)InvokeRepeating() 或协程
延时this.scheduleOnce(cb, delay)Invoke("M", delay) 或协程
实例化instantiate(prefab)Instantiate(prefab)
销毁node.destroy()Destroy(gameObject)
日志console.log()Debug.Log()
协程schedule/scheduleOnceStartCoroutine(Fn())
向量new Vec3(x, y, z)new Vector3(x, y, z)
加载场景director.loadScene('name')SceneManager.LoadScene("name")
加载资源resources.load(path, type, cb)Resources.Load<T>(path)
常驻节点game.addPersistRootNode()DontDestroyOnLoad()
碰撞体Collider2DBoxCollider / SphereCollider
刚体RigidBody2DRigidbody
碰撞回调onBeginContactOnCollisionEnter / OnTriggerEnter
射线检测physicsManager.raycast()Physics.Raycast()
音频AudioSource / AudioClipAudioSource / AudioClip(一致)
动画Animation / AnimationClipAnimator / AnimationClip
🎉 迁移完成检查清单

准备好了?进入 模块 1:Unity 编辑器 + C# 完整入门

← 返回目录 模块 1:编辑器 + C# →