cocos creator v3.8體驗歸檔

快速開始

跟著Cocos Creator用戶手冊完成step by step教程即可快速熟悉Cocos Creator的應(yīng)用钥勋。

可以通過右上角切換用戶手冊版本:


用戶手冊版本切換

IDE界面介紹

IDE管理

通過首頁下載Cocos Dashboard,可以管理不同版本的Cocos Creator Editor辆苔。

Cocos Dashboard
Cocos Creator Editor

語言設(shè)置

通過下述配置可以修改界面語言:


界面語言
  • 層級管理器:所有在場景編輯器中看到的內(nèi)容都可以在層級管理器中找到對應(yīng)的節(jié)點條目笔诵。

    場景中仍有一些不可見的私有節(jié)點,不會在此顯示姑子。

    在編輯場景時這兩個面板的內(nèi)容會同步顯示,一般我們也會同時使用這兩個面板來搭建場景测僵。

    • 創(chuàng)建UI 組件需要在帶有 Canvas 的父節(jié)點下才能顯示街佑,編輯器在發(fā)現(xiàn)沒有 Canvas 節(jié)點時會自動創(chuàng)建一個谢翎。
  • 場景編輯器:在場景編輯器中搭建場景,即可獲得所見即所得的場景預(yù)覽沐旨。

    • 點擊主窗口左上角工具欄第四個按鈕纬傲,或在編輯場景時按下 T 快捷鍵享扔,即可切換使用 矩形變換工具。需要注意的是,矩形變換工具只適用于 UI 節(jié)點鞍帝。

      矩形變換工具

    • 通過點擊工具欄上的 2D/3D 按鈕切換到 2D 編輯視圖下進(jìn)行 UI 編輯操作

  • 資源管理器:

  • 動畫編輯器:用于編輯并存儲動畫數(shù)據(jù)。

場景視角操作

在 3D 視圖下窑多,可以通過以下操作來移動和定位 場景編輯器 的視圖:

  • 鼠標(biāo)左鍵 + Alt:以視圖中心點為中心旋轉(zhuǎn)豆励。

  • 鼠標(biāo)中鍵:平移視圖。

  • 空格鍵 + 鼠標(biāo)/觸摸板拖拽:平移視圖统诺。

  • 鼠標(biāo)滾輪:以視圖中心點為中心縮放視圖歪脏。

  • 鼠標(biāo)右鍵 + WASD:攝像機漫游。

  • F 快捷鍵:攝像機聚焦到當(dāng)前選中節(jié)點粮呢。

渲染

列表渲染詳見

資源類型

目前婿失,Creator 支持 FBX 和 glTF 兩種格式的模型文件。

  • FBX(.fbx):支持 FBX 2020 及更早的文件格式啄寡。

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

動畫

骨骼動畫

對于mixamo官網(wǎng)來說挺物,處理骨骼動畫需滿足:

  • 僅支持特定格式的資源文件:fbx懒浮、objzip(fbx/obj模型 + 紋理打包的zip包)
  • 要求資源文件內(nèi)沒有骨骼信息姻乓,若有嵌溢,需要通過3D設(shè)計軟件e.g. blender提前清除再導(dǎo)入mixamo

mixamo下載TPose后蹋岩,下載所需的不同without skin骨骼動畫赖草,分別導(dǎo)入blender中重命名骨骼動畫(便于識別),然后刪除場景中的所有要素

骨骼動畫要素清除

最后導(dǎo)入TPose剪个,進(jìn)行下列步驟綁定骨骼動畫:

綁定骨骼動畫

在下方面板中[1]選擇[2]動畫攝影表秧骑,[3]選擇動作編輯器,[4]可以切換動畫Animation Clip扣囊,[5]為每個動畫創(chuàng)建不同的時間線乎折。

動作編輯器

從[4]可以看到,當(dāng)前添加了兩條時間線侵歇,可以通過關(guān)鍵幀上下鍵切換不同的動畫骂澄,空格鍵播放動畫。

導(dǎo)出fbx

導(dǎo)出fbx

[1]綁定紋理惕虑,[3]將blender坐標(biāo)系轉(zhuǎn)換為瀏覽器web坐標(biāo)系坟冲。

參考文檔

程序控制動畫

幀事件

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

@ccclass("MyScript")
class MyScript extends Component {  // 需要將該組件綁定到骨骼動畫節(jié)點上  
    public start() {
        const skeletalAnimation = this.node.getComponent(SkeletalAnimation);
        if (skeletalAnimation && skeletalAnimation .defaultClip) {
            const { defaultClip } = skeletalAnimation ;                        
            defaultClip.events = [ // 添加幀事件
                {
                    frame: 0.5, // 第 0.5 秒時觸發(fā)事件
                    func: 'onTriggered', // 事件觸發(fā)時調(diào)用的函數(shù)名稱
                    params: [ 0 ], // 向 `func` 傳遞的參數(shù)
                }
            ];                                         

            skeletalAnimation.clips = skeletalAnimation.clips;                        
        }
    }

    public onTriggered(arg: number) { // 骨骼動畫執(zhí)行到第0.5s觸發(fā)該函數(shù)
        console.log('I am triggered!', arg);
    }
}

自定義動畫磨镶,可以在動畫編輯器面板通過右鍵添加幀事件:


幀事件

其中:

  • func:表示事件觸發(fā)時回調(diào)的函數(shù)名稱。
    • 事件觸發(fā)時健提,動畫系統(tǒng)會搜索 動畫根節(jié)點中的所有組件琳猫,若組件中有實現(xiàn)動畫事件 func 中指定的函數(shù),便會對其進(jìn)行調(diào)用私痹,并傳入 params 中的參數(shù)脐嫂。

Notes:

  • 動畫根節(jié)點中的所有組件

    • 骨骼動畫的話,動畫根節(jié)點是骨骼動畫綁定的Character
  • 需要使用skeletalAnimation.clips = skeletalAnimation.clips;觸發(fā)幀事件紊遵。

動畫事件回調(diào)

目前支持的回調(diào)事件包括:

  • PLAY:開始播放時觸發(fā)

  • STOP:停止播放時觸發(fā)

  • PAUSE:暫停播放時觸發(fā)

  • RESUME:恢復(fù)播放時觸發(fā)

  • LASTFRAME:假如動畫循環(huán)次數(shù)大于 1账千,當(dāng)動畫播放到最后一幀時觸發(fā)。

  • FINISHED:動畫播放完成時觸發(fā)

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); // 綁定事件監(jiān)聽
    }

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

    }
}

通信

父子

事件冒泡

// 子節(jié)點的組件腳本中
this.node.dispatchEvent( new MyEvent('foobar', true, 'detail info') );
// 父節(jié)點的組件腳本中
this.node.on('foobar', (event: MyEvent) => {
  event.propagationStopped = true;
});

Notes

  • 如果我們希望在某一級節(jié)點截獲事件后就不再傳遞事件癞蚕,可以通過調(diào)用 event.propagationStopped = true 函數(shù)來完成蕊爵。

注入對象實例

調(diào)用實例節(jié)點方法

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

調(diào)用實例組件方法

  const item = instantiate(this.itemPrefab); // 實例化
  const data = this.items[i];
  this.node.addChild(item);
  item.getComponent('ItemTemplate').init(data) // 查找對應(yīng)組件,調(diào)用方法

其中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 類桦山,用以實現(xiàn)自定義事件的監(jiān)聽和發(fā)射

import { _decorator, Component, EventTarget } from 'cc';
const { ccclass } = _decorator;
const eventTarget = new EventTarget();

@ccclass("Example")
export class Example extends Component {
    onEnable () {
        // 監(jiān)聽事件可以通過 eventTarget.on() 接口來實現(xiàn)
        eventTarget.on('foobar', this._sayHello, this);
    }

    onDisable () {
        // 當(dāng)我們不再關(guān)心某個事件時攒射,我們可以使用 off 接口關(guān)閉對應(yīng)的監(jiān)聽事件。
        eventTarget.off('foobar', this._sayHello, this);
    }

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

// 發(fā)射事件可以通過 eventTarget.emit() 接口來實現(xiàn)恒水,參數(shù)最多只支持 5 個事件參數(shù)
eventTarget.emit(type, ...args);

適配

多分辨率適配

可以通過以下幾個部分完成多分辨率適配解決方案:

  • Canvas(畫布) 組件隨時獲得設(shè)備屏幕的實際分辨率并對場景中所有渲染元素進(jìn)行適當(dāng)?shù)目s放会放。

  • Widget(對齊掛件)能夠根據(jù)需要將元素對齊目標(biāo)節(jié)點(默認(rèn)是父節(jié)點)的不同參考位置,可實現(xiàn)彈性布局钉凌。

    對齊掛件

  • Label(文字) 組件內(nèi)置了提供各種動態(tài)文字排版模式的功能咧最,當(dāng)文字的約束框由于 Widget 對齊要求發(fā)生變化時,文字會根據(jù)需要呈現(xiàn)完美的排版效果御雕。

    • 共有 NONE矢沿、CLAMPSHRINK酸纲、RESIZE_HEIGHT 四種模式:

      • NONE 模式會自動根據(jù)文字尺寸捣鲸、行高等固定約束框尺寸,不支持自定義尺寸闽坡。

      • 后三種模式 下才能通過編輯器左上角的 矩形變換工具(也可以是按鍵盤按鍵 T)或者修改 屬性檢查器 中的 Size 大小或者添加 Widget 組件 來調(diào)整約束框的大小栽惶。

        • CLAMP:截斷,超出的部分會被隱藏疾嗅。

        • SHRINK

          • 自動縮小模式下外厂,如果文字按照原定尺寸渲染會超出約束框時,會自動縮小文字尺寸以顯示全部文字代承。

          • 自動縮小模式不會放大文字來適應(yīng)約束框汁蝶。

        • RESIZE_HEIGHT

          • 自動適應(yīng)高度模式會保證文字的約束框貼合文字的高度,不管文字有多少行论悴。這個模式非常適合顯示內(nèi)容量不固定的大段文字穿仪。
            文本約束
  • Layout(自動布局) 可以掛載在任何節(jié)點上席爽,將節(jié)點變成一個有自動布局功能的容器,實現(xiàn)flex啊片、grid布局

    自動布局

  • Cocos Creator v3.8版本中玖像,通過

    設(shè)計尺寸

    設(shè)定設(shè)計尺寸紫谷,需打包為web-mobile

Cocos Creator 提供了兩種 Web 平臺的頁面模板捐寥,可以通過 發(fā)布平臺 的下拉菜單選擇 Web Mobile 或 Web Desktop笤昨,它們的區(qū)別主要在于:

  • Web Mobile 會默認(rèn)將游戲視圖撐滿整個瀏覽器窗口
  • Web Desktop 允許在發(fā)布時指定一個游戲視圖的分辨率握恳,而且之后游戲視圖也不會隨著瀏覽器窗口大小變化而變化瞒窒。

九宮格格式的圖像

如果圖像資源是用九宮格的形式生產(chǎn)的,那么不管 Sprite 如何放大乡洼,都不會產(chǎn)生模糊或變形崇裁。

切分圖像

切分圖像
  • 在[1]資源管理器中導(dǎo)入圖片,如[2]

  • 點擊圖片資源[2]束昵,查看屬性檢查器[3]

  • 在屬性檢查器[3]中修改圖片資源類型為sprite-frame[5]

  • 點擊[6]應(yīng)用修改

  • 點擊Edit編輯拔稳,打開Sprite編輯器:

    sprite-frame類型

資源原圖
九宮圖

應(yīng)用圖像

Sprite應(yīng)用
  • 在層級管理器[1]中的Cavans節(jié)點[2]下創(chuàng)建Sprite節(jié)點[3]

  • 拖拽資源管理器[4]中的圖片資源[5]到屬性檢查器[6],將 Sprite 的 Type 屬性設(shè)為 Sliced[7]锹雏。

三方庫支持

調(diào)試

  1. 在 偏好設(shè)置 面板中指定了 <u>默認(rèn)腳本編輯器</u>巴比,便可以在 資源管理器 中雙擊腳本文件打開代碼編輯器快速編輯代碼。

  2. Cocos Creator 在頂部菜單欄的 開發(fā)者 -> VS Code 工作流 中集成了 添加編譯任務(wù) 和 添加 Chrome Debug 配置 功能礁遵,分別點擊可給vs code添加調(diào)試任務(wù)task

    編譯任務(wù)

  • 添加編譯任務(wù):用于在 VS Code 中觸發(fā) Creator 的腳本編譯

    • 添加后轻绞,可通過vs code任務(wù)觸發(fā)Creator編譯,無需重新激活Creator佣耐;

    • 若不添加政勃,vs code修改腳本后,需要激活Creator進(jìn)行編譯

  • 添加 Chrome Debug 配置:用于調(diào)試網(wǎng)頁版游戲

    • 啟動專門的Chrome調(diào)試預(yù)覽效果

    • 可直接在vs code中調(diào)試代碼晰赞,指定Chrome工具調(diào)試

  1. 使用 VS Code 調(diào)試網(wǎng)頁版游戲

    1. 在 VS Code 中點擊左側(cè)欄的 調(diào)試 按鈕打開調(diào)試面板稼病,并在最上方的調(diào)試配置中選擇 Cocos Creator Launch Chrome against localhost,然后點擊左側(cè)綠色的開始按鈕進(jìn)行調(diào)試
      調(diào)試
2.  更新腳本后掖鱼,在VS Code 里按下快捷鍵 Cmd/Ctrl + P然走,激活 快速打開 輸入框,然后輸入`task CocosCreator compile`戏挡,選擇 `CocosCreator compile`芍瑞,即可在Chrome中查看最新腳本運行。
CocosCreator compile

性能優(yōu)化

靜態(tài)資源

在 屬性檢查器 里設(shè)置資源雖然很直觀褐墅,但資源只能在場景里預(yù)先設(shè)好拆檬,沒辦法動態(tài)切換洪己。

動態(tài)加載

動態(tài)加載應(yīng)用包內(nèi)的本地資源

通常我們會把項目中需要動態(tài)加載的資源放在 resources 目錄下,配合 resources.load 等接口動態(tài)加載竟贯。

  • 所有需要通過腳本動態(tài)加載的資源答捕,都必須放置在 resources 文件夾或它的子文件夾下。

    • resources 文件夾需要在 assets 根目錄 下手動創(chuàng)建屑那。
  • 配合 resources.load 等接口動態(tài)加載

    • 只要傳入相對 resources 的路徑即可拱镐,并且路徑的結(jié)尾處 不能 包含文件擴展名
    // 加載 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) {
    // ...
});

動態(tài)加載遠(yuǎn)程資源

直接調(diào)用 assetManager.loadRemote 方法

// 遠(yuǎn)程 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;
    // ...
});

// 遠(yuǎn)程 url 不帶圖片后綴名沃琅,此時必須指定遠(yuǎn)程圖片文件的類型
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;
    // ...
});

動態(tài)加載本地設(shè)備存儲資源

直接調(diào)用 assetManager.loadRemote 方法

// 用絕對路徑加載設(shè)備存儲內(nèi)的資源,比如相冊
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:

  1. assetManager.loadRemote這種加載方式只支持圖片蜘欲、聲音益眉、文本等原生資源類型,不支持 SpriteFrame姥份、SpriteAtlas郭脂、TiledMap 等資源的直接加載和解析。

  2. Web 端的遠(yuǎn)程加載受到瀏覽器的 <u>CORS 跨域策略限制</u>殿衰,如果對方服務(wù)器禁止跨域訪問朱庆,那么會加載失敗,而且由于 WebGL 安全策略的限制闷祥,即便對方服務(wù)器允許 http 請求成功之后也無法渲染娱颊。

預(yù)加載

場景預(yù)加載

director.preloadScene("table", function () {    
  console.log('Next scene preloaded');
});
// 在某個時機調(diào)用loadScene
// 就算預(yù)加載還沒完成,直接調(diào)用 director.loadScene凯砍,預(yù)加載完成后場景就會啟動
director.loadScene("table");

動態(tài)資源預(yù)加載

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

/**
* 開發(fā)者可以使用預(yù)加載相關(guān)接口提前加載資源箱硕,
* 不需要等到預(yù)加載結(jié)束即可使用正常加載接口進(jìn)行加載,
* 正常加載接口會直接復(fù)用預(yù)加載過程中已經(jīng)下載好的內(nèi)容悟衩,縮短加載時間剧罩。
*/ 
resources.load('test_assets/image/spriteFrame', SpriteFrame, (err, spriteFrame) => {
    this.node.getComponent(Sprite).spriteFrame = spriteFrame;
});

分包預(yù)加載

Asset Bundle 中提供了 preloadpreloadDir 接口用于預(yù)加載 Asset Bundle 中的資源,具體的使用方式和 assetManager 一致座泳。

分包

Asset Bundle 作為資源模塊化工具惠昔,允許開發(fā)者按照項目需求將貼圖、腳本挑势、場景等資源劃分在多個 Asset Bundle 中镇防,然后在游戲運行過程中,按照需求去加載不同的 Asset Bundle潮饱,以減少啟動時需要加載的資源數(shù)量来氧,從而減少首次下載和加載游戲時所需的時間。

Asset Bundle 可以按需求放置在不同地方,比如可以放在遠(yuǎn)程服務(wù)器啦扬、本地中狂、或者小游戲平臺的分包中。

  1. 自定義 Asset Bundle 是以 文件夾 為單位進(jìn)行配置的扑毡。當(dāng)我們在 資源管理器 中選中一個文件夾時胃榕,屬性檢查器 中就會出現(xiàn)一個 配置為 Bundle 的選項,勾選開啟Bundle分包:


    Bundle分包
  1. 定義配置僚楞,點擊面板右上方的 綠色打鉤按鈕勤晚,該文件夾就被配置為 Asset Bundle 的打包預(yù)設(shè)集合了,在放置需要的資源后泉褐,然后在 構(gòu)建發(fā)布 面板選擇對應(yīng)的平臺進(jìn)行構(gòu)建即可得到對應(yīng)的 Asset Bundle。
    Asset Bundle 配置面板
  1. 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) {
        // ...
    });
});

// 當(dāng)復(fù)用其他項目的 Asset Bundle 時
assetManager.loadBundle('https://othergame.com/remote/01_graphics', (err, bundle) => {
    bundle.load('xxx');
});

Notes:

  • 在通過 API 加載 Asset Bundle 時鸟蜡,引擎并沒有加載 Asset Bundle 中的所有資源膜赃,而是加載 Asset Bundle 的 資源清單,以及包含的 所有腳本揉忘。

  • 當(dāng) Asset Bundle 加載完成后跳座,會觸發(fā)回調(diào)并返回錯誤信息和 AssetManager.Bundle 類的實例,這個實例就是 Asset Bundle API 的主要入口泣矛,開發(fā)者可以使用它去加載 Asset Bundle 中的各類資源疲眷。

  1. 釋放 Asset Bundle 中的資源

在資源加載完成后,所有的資源都會被臨時緩存到 assetManager 中您朽,以避免重復(fù)加載狂丝。當(dāng)然,緩存中的資源也會占用內(nèi)存哗总,有些資源如果不再需要用到几颜,需要進(jìn)行釋放

// 方式一、使用常規(guī)的 assetManager.releaseAsset 方法進(jìn)行釋放讯屈。
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);

資源釋放

資源引用類型:

  • 當(dāng)開發(fā)者在編輯器中編輯資源時(例如場景叛本、預(yù)制件沪蓬、材質(zhì)等),需要在這些資源的屬性中配置一些其他的資源炮赦,例如在材質(zhì)中設(shè)置貼圖怜跑,在場景的 Sprite 組件上設(shè)置 SpriteFrame。那么這些引用關(guān)系會被記錄在資源的序列化數(shù)據(jù)中,引擎可以通過這些數(shù)據(jù)分析出依賴資源列表性芬,像這樣的引用關(guān)系就是靜態(tài)引用峡眶。

    • Asset Manager 提供了一套基于引用計數(shù)的資源釋放機制,會自動統(tǒng)計資源之間的靜態(tài)引用植锉。
  • 當(dāng)開發(fā)者在編輯器中沒有對資源做任何設(shè)置辫樱,而是通過代碼動態(tài)加載資源并設(shè)置到場景的組件上,則資源的引用關(guān)系不會記錄在序列化數(shù)據(jù)中俊庇,引擎無法統(tǒng)計到這部分的引用關(guān)系狮暑,這些引用關(guān)系就是動態(tài)引用

    • 如果動態(tài)加載出來的資源需要長期引用辉饱、持有搬男,或者復(fù)用時,建議使用 addRef 接口手動增加引用計數(shù)彭沼。

資源釋放類型:

  • 自動釋放

    • 自動釋放的優(yōu)勢在于不用顯式地調(diào)用釋放接口缔逛,開發(fā)者只需要維護(hù)好資源的引用計數(shù),Creator 會根據(jù)引用計數(shù)自動進(jìn)行釋放姓惑。

    • 其中褐奴,靜態(tài)引用的資源開發(fā)者不用維護(hù)引用計數(shù),Creator會自動統(tǒng)計于毙。動態(tài)資源引用需要開發(fā)者手動維護(hù)引用計數(shù)敦冬。

  • 手動釋放

    • 顯式調(diào)用 release 系列接口

    • 可以確保資源本身一定會被釋放

    • 資源本身不會進(jìn)行釋放檢查唯沮,只有其依賴資源會進(jìn)行釋放檢查

物理系統(tǒng)

物體需要具備完全物理特性的前提條件物體同時具備 剛體 和 碰撞體脖旱,并調(diào)整好其質(zhì)心位置和碰撞體的形狀。

碰撞體

碰撞組件屬性 IsTrigger 決定了組件為觸發(fā)器還是碰撞器烂翰。

  • 將 IsTrigger 設(shè)置為 true 時夯缺,組件為觸發(fā)器。

    • 觸發(fā)器只用于碰撞檢測和觸發(fā)事件甘耿,會被物理引擎忽略踊兜。

    • 觸發(fā)器不會與其它觸發(fā)器或者碰撞器做更精細(xì)的檢測。

  • 默認(rèn)設(shè)置 false佳恬,組件為碰撞器捏境,可以結(jié)合剛體產(chǎn)生碰撞效果。

    • 碰撞器與碰撞器會做更精細(xì)的檢測毁葱,并會產(chǎn)生碰撞數(shù)據(jù)垫言,如碰撞點、法線等倾剿。

剛體

碰撞體間定義碰撞發(fā)生的可能性是通過剛體的 Group 屬性筷频,而非 Node 的 Layer 屬性蚌成。

剛體類型

  • STATIC靜態(tài)剛體:

    • 靜態(tài)剛體在大多數(shù)情況下用于一些始終停留在一個地方,不會輕易移動的游戲物體凛捏,例如:建筑物担忧。

    • 靜態(tài)剛體與其他物體發(fā)生碰撞時,不會產(chǎn)生物理行為坯癣,因此瓶盛,也不會移動。

  • DYNAMIC動力學(xué)剛體:

    • 剛體碰撞完全由物理引擎模擬示罗,可以通過 力的作用 運動物體(需要保證質(zhì)量大于 0)

    • 僅有動力學(xué)剛體的相互碰撞才有真實世界的效果

      • 其中有一個是非動力學(xué)剛體惩猫,相互碰撞沒有真實世界的效果。
  • KINEMATIC運動學(xué)剛體:

    • 它與靜態(tài)剛體類似蚜点,只是靜態(tài)剛體與其他物體發(fā)生碰撞時轧房,不會產(chǎn)生物理行為,而運動學(xué)剛體會對其他對象施加摩擦力绍绘,并在接觸時喚醒其他剛體锯厢。

    • 通常用于表達(dá)電梯這類平臺運動的物體

物理材質(zhì)

物理材質(zhì)是一種資源,它記錄了物體的物理屬性脯倒,這些信息用來計算碰撞物體受到的摩擦力和彈力等。

物理事件

  • 觸發(fā)事件

    • 用于檢測兩個物體是否進(jìn)入或離開彼此的觸發(fā)區(qū)域捺氢。

    • 這種事件通常用于不需要物理碰撞效果的情況藻丢,例如進(jìn)入一個區(qū)域來觸發(fā)某個效果或行為。

      • 沒有物理反應(yīng):不會導(dǎo)致物理碰撞效果摄乒,如反彈或力的作用

      • 主要用于區(qū)域檢測:常用于檢測進(jìn)入悠反、停留和離開某個區(qū)域的情況

    • 接收到觸發(fā)事件的前提是兩者都必須帶有碰撞組件,并且至少有一個是觸發(fā)器類型馍佑。

  • 碰撞事件

    • 用于檢測兩個物體之間的物理碰撞斋否。

    • 這種情況通常用于需要物理反應(yīng)的情況,例如角色與障礙物的碰撞拭荤。

      • 有物理反應(yīng):碰撞事件通常會導(dǎo)致物理反應(yīng)茵臭,例如反彈、推力等舅世。

      • 用于物理交互:常用于檢測和處理實際的物理碰撞旦委。

    • 接收到碰撞事件的前提是兩者都必須帶有碰撞組件都不是觸發(fā)器類型雏亚、至少有一個是非靜態(tài)剛體并且使用的是非 builtin 的物理引擎缨硝。

真實世界碰撞效果

    protected shoot(velocity: number) {
        // 獲取炮彈
        const bulletNode = this.generateBullet(),
            bulletRigidBody = bulletNode.getComponent(RigidBody);
        // 方向和速度(默認(rèn)前方為 -z 方向,需要反過來)
        const direction = bulletNode.forward.negative();
        direction.multiplyScalar(velocity);
        // 給剛體設(shè)置速度
        bulletRigidBody.setLinearVelocity(direction);
    }

Notes:

  • 產(chǎn)生真實世界的物理碰撞效果罢低,不應(yīng)該依賴使用position的變更產(chǎn)生碰撞查辩,這樣無法產(chǎn)生物理碰撞的動力學(xué)效果。

  • 應(yīng)該通過設(shè)置剛體的速度,讓其運動宜岛,與其他剛體產(chǎn)生碰撞长踊,從而產(chǎn)生真實世界的碰撞效果。

射線檢測

射線檢測是對一條射線和非動力學(xué)剛體碰撞組件進(jìn)行相交性判斷

檢測對象需要滿足:

  • 檢測的對象是物理碰撞器谬返,在場景面板上與之對應(yīng)的是碰撞器組件之斯,例如 BoxCollider。

  • 檢測的對象僅支持STATIC遣铝、KINEMATIC剛體類型佑刷,不支持****DYNAMIC****剛體類型

// bullet物理引擎測試結(jié)果:
const worldRay = new geometry.Ray(0, -1, 0, 0, 1, 0); // 世界坐標(biāo)下的射線
// 以下參數(shù)可選
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:

  • 射線起點需要是世界坐標(biāo)node.getWorldPosition(new Vec3())

  • 射線穿過平面的邊界時酿炸,浮點運算的精度問題瘫絮,可能會被記錄為多次碰撞。

    • 在某些(如 Bullet)物理后端中填硕,由于計算精度的原因麦萤,應(yīng)該避免使用比例很高的尺寸,這里建議低于1000扁眯。如某個盒碰撞器壮莹,其 Size 屬性的 Y 值為 40 而 Z 值為 0.01,此時他們的 Y姻檀、Z 的比例超過了 1000命满,此時可能會出現(xiàn)浮點數(shù)計算不準(zhǔn)確的問題。

    • 切換物理引擎試試绣版,如cannonjs沒有此問題胶台。

粒子系統(tǒng)

  1. 添加3D粒子系統(tǒng)


    創(chuàng)建3D粒子系統(tǒng)
    • 在[1]層級管理器中右鍵[2]創(chuàng)建特效 —> 粒子系統(tǒng)[3]
  2. 添加粒子材質(zhì)


    粒子材質(zhì)
  • 在[1]資源管理器中[2]右鍵 —> [3]創(chuàng)建 —> 材質(zhì)[4]
  1. 處理貼圖

使用Photoshop等DCC設(shè)計工具去除素材的顏色,僅保留明度的信息:

  • 打開圖片: 打開你想要處理的圖片杂抽。

  • 去色:

    • 依次點擊 圖像 > 調(diào)整 > 去色 或按快捷鍵 Shift+Ctrl+U诈唬,這樣會去除圖像中的所有顏色,僅保留灰度信息缩麸。
  • 調(diào)整對比度(可選):

    • 為了增強圖像效果铸磅,你可以調(diào)整對比度。依次點擊 圖像 > 調(diào)整 > 亮度/對比度匙睹,進(jìn)行適當(dāng)調(diào)整愚屁。
  • 保存圖像:

    • 選擇 文件 > 存儲為,保存為你需要的格式(如 PNG 或 JPEG)痕檬。
  1. 配置粒子貼圖


    粒子貼圖
    • 資源管理器中選中創(chuàng)建的粒子材質(zhì)霎槐,通過屬性檢查器[1]配置材質(zhì)屬性;

    • 粒子屬性Effect設(shè)定為particles/builtin-particle;

    • 展開[4]配置表梦谜,將貼圖資源拖拽到Main Texture丘跌,將材質(zhì)貼圖袭景;

Good to know:

  • Effect:用來定義材質(zhì)具體的渲染操作和邏輯的腳本。

    • 它通常由一個或多個著色器(Shader)程序組成

    • 這些程序在 GPU 上運行闭树,負(fù)責(zé)執(zhí)行頂點和像素的處理耸棒。

  • Techniques:定義渲染技術(shù)和流程,每個技術(shù)包含多個渲染階段(Pass)报辱。

    • add与殃、add-smoothpremultiply-blend 是常見的混合模式,用于控制不同材質(zhì)的混合方式碍现。
  • Passes:定義具體的渲染階段幅疼,包括頂點著色器和片段著色器。

  1. 粒子渲染貼圖


    渲染貼圖
    • 層級管理器中選中添加的粒子系統(tǒng)昼接,在屬性檢查器[1]配置渲染貼圖爽篷;

    • 展開[2]粒子渲染器配置表,將粒子材質(zhì)拖拽到Cpu Material[4]慢睡;

文件模板

自定義html構(gòu)建模板

創(chuàng)建html構(gòu)建模板

修改index.ejs模板
  1. cocos creator編輯器中添加構(gòu)建模版逐工;

  2. 在代碼編輯器中修改index.ejs模板;

  3. build-templates/web-desktop目錄下的資源會自動打包到build目錄中

    • build-templates/web-desktop放置ico圖標(biāo)可以用于修改favicon.ico

構(gòu)建模板更多詳見

腳本模板

.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) {

    }
}

腳本模板更多詳見

打包

構(gòu)建支持平臺

平臺 平臺構(gòu)建插件名 支持的特殊文件類型
華為 AGC huawei-agc 暫不支持
支付寶小游戲 alipay-mini-game game.json
字節(jié)跳動小游戲 bytedance-mini-game game.ejs漂辐、game.json泪喊、project.config.json
OPPO 小游戲 oppo-mini-game manifest.json
華為快游戲 huawei-quick-game 暫不支持
Cocos Play cocos-play game.config.json
vivo 小游戲 vivo-mini-game project.config.json
小米快游戲 xiaomi-quick-game manifest.json
百度小游戲 baidu-mini-game game.json髓涯、project.swan.json
微信小游戲 wechatgame game.ejs复凳、game.json辙浑、project.config.json
Web Desktop web-desktop index.ejs
Web Mobile web-mobile index.ejs
原生平臺 native 暫不支持
支持平臺

打包

在[1]項目菜單中選擇[2]構(gòu)建發(fā)布


構(gòu)建發(fā)布

自定義插屏

場景初始化加載預(yù)覽圖片

  • 通過[1]開啟/關(guān)閉插屏效果改执。

  • 點擊[2]可以開啟自定義插屏面板赦邻。


    開啟插屏
插屏配置

社區(qū)

引擎對比

基本情況 Three.js Unity3D Cocos Creator
定位 輕量級3D庫 游戲開發(fā)引擎 輕量級游戲開發(fā)引擎
二次開發(fā)語言 Javascript開發(fā) 需安裝Unity編輯器恬吕,C#語言開發(fā) 需安裝Cocos Creator編輯器,Typescript開發(fā)
模型來源 三方提供渐裂,手動維護(hù) 三方提供柒凉,手動維護(hù) 三方提供膝捞,手動維護(hù)
輔助器 提供了各種輔助工具蔬咬,如射線輔助線计盒、坐標(biāo)輔助線 提供了部分輔助工具,如射線輔助線 沒有輔助工具北启,需要自己通過現(xiàn)有資源模擬

小技巧

通過常駐節(jié)點進(jìn)行場景資源管理和參數(shù)傳遞

引擎同時只會運行一個場景咕村,當(dāng)切換場景時懈涛,默認(rèn)會將場景內(nèi)所有節(jié)點和其他實例銷毀批钠。如果我們需要用一個組件控制所有場景的加載埋心,或在場景之間傳遞參數(shù)數(shù)據(jù)拷呆,就需要將該組件所在節(jié)點標(biāo)記為「常駐節(jié)點」茬斧,使它在場景切換時不被自動銷毀项秉,常駐內(nèi)存娄蔼。

// 添加一個節(jié)點的常駐屬性:
director.addPersistRootNode(myNode);
// 要取消一個節(jié)點的常駐屬性:
director.removePersistRootNode(myNode);

Notes:

  • 目標(biāo)節(jié)點必須為位于層級的根節(jié)點,否則設(shè)置無效艘虎。

  • removePersistRootNode并不會立即銷毀指定節(jié)點野建,只是將節(jié)點還原為可在場景切換時銷毀的節(jié)點。

Blender使用mixamo處理骨骼動畫

  1. github中直接通過zip包下載源碼

  2. 在[編輯]菜單中選擇[偏好設(shè)置]


    偏好設(shè)置
  3. 在[偏好設(shè)置]面板中選擇[插件]唯鸭。

  4. 在[2]選擇zip安裝包進(jìn)行安裝目溉,安裝完成后缭付,可在[3][4]的檢索的基礎(chǔ)上看到mixamo列表項陷猫。

  5. 勾選[5]開啟mixamo插件绣檬。

    mixamo插件安裝應(yīng)用

  1. mixamo開啟插件設(shè)置
    mixamo插件應(yīng)用

預(yù)覽看不到地形的貼圖

僅僅需要在cocos編輯器中保存一下即可。

概念

引擎:

  1. 內(nèi)置完整的工具集,而非生態(tài)擴展

  2. 特定領(lǐng)域,而非通用

  3. 專門性

運行時:

  1. 可以是通用的(如語言運行時)或特定功能的(如游戲引擎運行時)

  2. 包括解釋器护糖,解釋和執(zhí)行代碼

Node vs. Component

Node:

  • Node是 Cocos Creator 中場景的基本構(gòu)建塊嫡良,所有的場景寝受、UI 元素很澄、精靈甩苛、動畫等都是通過節(jié)點來組織和管理的讯蒲。

  • 節(jié)點本身不包含具體的功能或行為墨林,它們只是承載其他組件的容器赞哗。

  • 類似threejs中的Three.mesh

Component:

  • Component 是附加在節(jié)點上的腳本或功能模塊肪笋,用于定義節(jié)點的具體行為和功能藤乙。

  • 組件不能獨立存在坛梁,它們必須附加到一個節(jié)點上划咐,節(jié)點可以附加多個組件褐缠。

  • 組件決定了節(jié)點的行為和功能。

  • 類似threejs中的Three.geomotry胡桨、Three.material

Prefab(預(yù)制)資源

Prefab(預(yù)制)資源昧谊,作為我們動態(tài)生成節(jié)點時使用的模板揽浙。

cc類

裝飾器 ccclass 應(yīng)用在類上時馅巷,此類稱為 cc 類稍刀。

  • 未聲明 ccclass 的組件類账月,也無法作為組件添加到節(jié)點上局齿。
  • ccclass 裝飾器的參數(shù) name 指定了 cc 類的名稱抓歼,cc 類名是 獨一無二 的谣妻,這意味著即便在不同目錄下的同名類也是不允許的。
  • 類名不應(yīng)該以 cc.减江、internal. 作為前綴辈灼,這是 Cocos Creator 的保留類名前綴。

組件類裝飾器

ccclass裝飾器 修飾榕莺,且繼承 Component 的子類钉鸯,此類稱為 cc 組件類唠雕。

executeInEditMode:動態(tài)加載的預(yù)制體

import {
  _decorator,
  Component
} from "cc";
const { ccclass, property, executeInEditMode } = _decorator;

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

}

默認(rèn)在層級管理器和場景管理器中看不到動態(tài)加載的預(yù)制體或者動態(tài)創(chuàng)建的節(jié)點 new Node()岩睁,可以通過[@executeInEditMode裝飾器](https://docs.cocos.com/creator/3.8/api/en/function/_decorator.executeInEditMode)使繼承自組件的ccclass在編輯模式下執(zhí)行。

Notes:

  • 使用@executeInEditMode執(zhí)行過的ccclass無法還原成未執(zhí)行狀態(tài)刘莹。

更多內(nèi)容詳見

executionOrder:生命周期回調(diào)的執(zhí)行優(yōu)先級

小于 0 的腳本將優(yōu)先執(zhí)行点弯,大于 0 的腳本將最后執(zhí)行。排序方式如下:

  • 對于同一節(jié)點上的不同組件雌团,數(shù)值小的先執(zhí)行锦援,數(shù)值相同的按組件添加先后順序執(zhí)行

  • 對于不同節(jié)點上的同一組件灵寺,按節(jié)點樹排列決定執(zhí)行的先后順序

可以通過組件類裝飾器executionOrder 用來指定腳本生命周期回調(diào)的執(zhí)行優(yōu)先級。

const { ccclass, executionOrder } = _decorator;

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

屬性裝飾器

在編輯器 屬性檢查器 中展示的屬性叮称,屬性名開頭不應(yīng)該帶 _瓤檐,否則會識別為 private 屬性祭示,private 屬性不會在編輯器組件屬性面板上顯示质涛。

屬性裝飾器參數(shù)詳見

基礎(chǔ)屬性類型

CCInteger汇陆、CCFloatCCBoolean月趟、CCStringCocos Creator基礎(chǔ)屬性類型標(biāo)識

  • 當(dāng)聲明 JavaScript 內(nèi)置構(gòu)造函數(shù) Number孝宗、String因妇、Boolean 用作類型時將給出警告,并且將分別視為 cc 類型中的 CCFloat址芯、CCString谷炸、CCBoolean

group分組

當(dāng)腳本中定義的屬性過多且雜時,可通過 group 對屬性進(jìn)行分組描孟、排序匿醒,方便管理。同時還支持對組內(nèi)屬性進(jìn)行分類蜜另。

group 寫法包括以下兩種:

  • @property({ group: { name } })

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市此迅,隨后出現(xiàn)的幾起案子耸序,更是在濱河造成了極大的恐慌,老刑警劉巖搅窿,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異沐飘,居然都是意外死亡,警方通過查閱死者的電腦和手機隔箍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門滨达,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捡遍,“玉大人,你說我怎么就攤上這事谓传。” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵直颅,是天一觀的道長。 經(jīng)常有香客問我脖含,道長,這世上最難降的妖魔是什么关拒? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮归露,結(jié)果婚禮上恐锦,老公的妹妹穿的比我還像新娘一铅。我一直安慰自己,他們只是感情好福也,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布峦甩。 她就那樣靜靜地躺著犬辰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪涵卵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音昆婿,去河邊找鬼。 笑死挎春,一個胖子當(dāng)著我的面吹牛多律,可吹牛的內(nèi)容都是我干的痴突。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼狼荞,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了相味?” 一聲冷哼從身側(cè)響起拾积,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎丰涉,沒想到半個月后拓巧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡一死,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年肛度,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片投慈。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡承耿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出伪煤,到底是詐尸還是另有隱情加袋,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布抱既,位于F島的核電站职烧,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏防泵。R本人自食惡果不足惜蚀之,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望捷泞。 院中可真熱鬧恬总,春花似錦、人聲如沸肚邢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽骡湖。三九已至贱纠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間响蕴,已是汗流浹背谆焊。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留浦夷,地道東北人辖试。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓辜王,卻偏偏與公主長得像,于是被迫代替她去往敵國和親罐孝。 傳聞我的和親對象是個殘疾皇子呐馆,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容