Cocos Creator 源碼解讀:siblingIndex 與 zIndex

前言

本文基于 Cocos Creator 2.4.5 撰寫蔑舞。

?? 普天同慶

來(lái)了來(lái)了皆串,《源碼解讀》系列文章終于又來(lái)了!

?? 溫馨提醒

本文包含大段引擎源碼追驴,使用大屏設(shè)備閱讀體驗(yàn)更佳械哟!

Hi There!

節(jié)點(diǎn)(cc.Node)作為 Cocos Creator 引擎中最基本的單位,所有組件都需要依附在節(jié)點(diǎn)上殿雪。

同時(shí)節(jié)點(diǎn)也是我們?nèi)粘i_(kāi)發(fā)中接觸最頻繁的東西暇咆。

我們經(jīng)常會(huì)需要「改變節(jié)點(diǎn)的排序」來(lái)完成一些效果(如圖像的遮擋)。

A Question?

?? 你有沒(méi)有想過(guò):

節(jié)點(diǎn)的排序是如何實(shí)現(xiàn)的丙曙?

Oops!

?? 我在分析了源碼后發(fā)現(xiàn):

節(jié)點(diǎn)的排序并沒(méi)有想象中那么簡(jiǎn)單爸业!

?? 渣皮語(yǔ)錄

聽(tīng)皮皮一句勸,zIndex 的水太深亏镰,你把握不壮犊酢!


正文

節(jié)點(diǎn)順序 (Node Order)

?? 如何修改節(jié)點(diǎn)的順序索抓?

首先钧忽,在 Cocos Creator 編輯器中的「層級(jí)管理器」中毯炮,我們可以隨意拖動(dòng)節(jié)點(diǎn)來(lái)改變節(jié)點(diǎn)的順序。

拖動(dòng)排序

?? 但是耸黑,在代碼中我們要怎么做呢桃煎?

我最先想到的是節(jié)點(diǎn)的 setSiblingIndex 函數(shù),然后是節(jié)點(diǎn)的 zIndex 屬性大刊。

我猜大多數(shù)人都不清楚這兩個(gè)方案有什么區(qū)別为迈。

那么接下來(lái)就讓我們深入源碼,一探究竟缺菌!

siblingIndex

「siblingIndex」即「同級(jí)索引」葫辐,意為「同一父節(jié)點(diǎn)下的兄弟節(jié)點(diǎn)間的位置」姓迅。

siblingIndex 越小的節(jié)點(diǎn)排越前葡公,索引最小值為 0,也就是第一個(gè)節(jié)點(diǎn)的索引值。

需要注意的是蛾绎,實(shí)際上節(jié)點(diǎn)并沒(méi)有 siblingIndex 屬性,只有 getSiblingIndexsetSiblingIndex 這兩個(gè)相關(guān)函數(shù)鸦列。

注:本文統(tǒng)一使用 siblingIndex 來(lái)代指 getSiblingIndexsetSiblingIndex 函數(shù)租冠。

另外,getSiblingIndexsetSiblingIndex 函數(shù)是由 cc._BaseNode 實(shí)現(xiàn)的薯嗤。

?? cc._BaseNode

大家對(duì)這個(gè)類可能會(huì)比較陌生顽爹,簡(jiǎn)單來(lái)說(shuō) cc._BaseNodecc.Node 的基類。

此類「定義了節(jié)點(diǎn)的基礎(chǔ)屬性和函數(shù)」骆姐,包括但不僅限于 setParent镜粤、addChildgetComponent 等常用函數(shù)...

?? 源碼節(jié)選:

函數(shù):cc._BaseNode.prototype.getSiblingIndex

getSiblingIndex() {
  if (this._parent) {
    return this._parent._children.indexOf(this);
  } else {
    return 0;
  }
},

函數(shù):cc._BaseNode.prototype.setSiblingIndex

setSiblingIndex(index) {
  if (!this._parent) {
    return;
  }
  if (this._parent._objFlags & Deactivating) {
    return;
  }
  var siblings = this._parent._children;
  index = index !== -1 ? index : siblings.length - 1;
  var oldIndex = siblings.indexOf(this);
  if (index !== oldIndex) {
    siblings.splice(oldIndex, 1);
    if (index < siblings.length) {
      siblings.splice(index, 0, this);
    } else {
      siblings.push(this);
    }
    this._onSiblingIndexChanged && this._onSiblingIndexChanged(index);
  }
},

[源碼] base-node.js#L514: https://github.com/cocos-creator/engine/blob/2.4.5/cocos2d/core/utils/base-node.js#L514

??? 做了什么?

扒拉源碼后發(fā)現(xiàn)玻褪,siblingIndex 的本質(zhì)其實(shí)很簡(jiǎn)單肉渴。

那就是「當(dāng)前節(jié)點(diǎn)在父節(jié)點(diǎn)的 _children 屬性中的下標(biāo)(位置)」。

getSiblingIndex 函數(shù)返回的是「當(dāng)前節(jié)點(diǎn)在父節(jié)點(diǎn)的 _children 屬性中的下標(biāo)(位置)」带射。

setSiblingIndex 函數(shù)則是設(shè)置「當(dāng)前節(jié)點(diǎn)在父節(jié)點(diǎn)的 _children 屬性中的下標(biāo)(位置)」同规。

?? cc._BaseNode.prototype._children

節(jié)點(diǎn)的 _children 屬性其實(shí)就是節(jié)點(diǎn)的 children 屬性。

children 屬性是一個(gè) getter窟社,返回的是自身的 _children 屬性券勺。

另外 children 屬性沒(méi)有實(shí)現(xiàn) setter,所以你直接給 children 屬性賦值是無(wú)效的灿里。

zIndex

「zIndex」是「用來(lái)對(duì)節(jié)點(diǎn)進(jìn)行排序的關(guān)鍵屬性」关炼,它決定了一個(gè)節(jié)點(diǎn)在兄弟節(jié)點(diǎn)之間的位置。

zIndex 的值介于 cc.macro.MIN_ZINDEXcc.macro.MAX_ZINDEX 之間匣吊。

另外盗扒,zIndex 屬性是在 cc.Node 內(nèi)使用 Cocos 定制版 gettersetter 實(shí)現(xiàn)的跪楞。

?? 源碼節(jié)選:

屬性: cc.Node.prototype.zIndex

// 為了減少篇幅,已省略部分不相關(guān)代碼
zIndex: {
  get() {
    return this._localZOrder >> 16;
  },
  set(value) {
    if (value > macro.MAX_ZINDEX) {
      value = macro.MAX_ZINDEX;
    } else if (value < macro.MIN_ZINDEX) {
      value = macro.MIN_ZINDEX;
    }
    if (this.zIndex !== value) {
      this._localZOrder = (this._localZOrder & 0x0000ffff) | (value << 16);
      this.emit(EventType.SIBLING_ORDER_CHANGED);
      this._onSiblingIndexChanged();
    }
  }
},

[源碼] CCNode.js#L1549: https://github.com/cocos-creator/engine/blob/2.4.5/cocos2d/core/CCNode.js#L1549

??? 做了什么侣灶?

扒拉源碼后發(fā)現(xiàn)甸祭,zIndex 的本質(zhì)其實(shí)也很簡(jiǎn)單。

那就是「返回或設(shè)置節(jié)點(diǎn)的 _localZOrder 屬性」褥影。

?? 沒(méi)那么簡(jiǎn)單池户!

有趣的是,在 getter 中并沒(méi)有直接返回 _localZOrder 屬性凡怎,而是返回了 _localZOrder 屬性右移(>>)16 位后的數(shù)值校焦。

setter 中設(shè)置 _localZOrder 屬性時(shí)也并非簡(jiǎn)單的賦值,又是進(jìn)行了一頓位操作:

這里我們以二進(jìn)制數(shù)的視角來(lái)分解該函數(shù)內(nèi)的位操作统倒。

  1. 通過(guò) & 0x0000ffff 取出原 _localZOrder 的「低 16 位」寨典;
  2. 將目標(biāo)值 value「左移 16 位」;
  3. 將左移后的 value 作為「高 16 位」與原 _localZOrder 的「低 16 位」合并房匆;
  4. 最后得到一個(gè)「32 位的二進(jìn)制數(shù)」并賦予 _localZOrder耸成。

?? 嗯?

慢著浴鸿!_localZOrder 又是干啥用的井氢?咋這么繞!

別急岳链,答案在后面~

排序 (Sorting)

細(xì)心的朋友應(yīng)該發(fā)現(xiàn)了花竞,siblingIndex 和 zIndex 的源碼中都沒(méi)有包含實(shí)際的排序邏輯。

但是它們都有一個(gè)共同點(diǎn):「最后都調(diào)用了自身的 _onSiblingIndexChanged 函數(shù)」掸哑。

_onSiblingIndexChanged

?? 源碼節(jié)選:

函數(shù):cc.Node.prototype._onSiblingIndexChanged

_onSiblingIndexChanged() {
  if (this._parent) {
    this._parent._delaySort();
  }
},

??? 做了什么约急?

_onSiblingIndexChanged 函數(shù)內(nèi)則是調(diào)用了「父節(jié)點(diǎn)」的 _delaySort 函數(shù)。

_delaySort

?? 源碼節(jié)選:

函數(shù):cc.Node.prototype._delaySort

_delaySort() {
  if (!this._reorderChildDirty) {
    this._reorderChildDirty = true;
    cc.director.__fastOn(cc.Director.EVENT_AFTER_UPDATE, this.sortAllChildren, this);
  }
},

??? 做了什么苗分?

一頓操作順藤摸瓜后發(fā)現(xiàn)厌蔽,真正進(jìn)行排序的地方是「父節(jié)點(diǎn)」的 sortAllChildren 函數(shù)。

?? 盲生俭嘁,你發(fā)現(xiàn)了華點(diǎn)躺枕!

值得注意的是,_delaySort 函數(shù)中的 sortAllChildren 函數(shù)調(diào)用不是立即觸發(fā)的供填,而是會(huì)在下一次 update(生命周期)后觸發(fā)拐云。

延遲觸發(fā)的目的應(yīng)該是為了避免在同一幀內(nèi)的重復(fù)調(diào)用,從而減少不必要的性能損耗近她。

sortAllChildren

?? 源碼節(jié)選:

函數(shù):cc.Node.prototype.sortAllChildren

// 為了減少篇幅叉瘩,已省略部分不相關(guān)代碼
sortAllChildren() {
  if (this._reorderChildDirty) {
    this._reorderChildDirty = false;
    // Part 1
    var _children = this._children, child;
    this._childArrivalOrder = 1;
    for (let i = 0, len = _children.length; i < len; i++) {
      child = _children[i];
      child._updateOrderOfArrival();
    }
    eventManager._setDirtyForNode(this);
    // Part 2
    if (_children.length > 1) {
      let child, child2;
      for (let i = 1, count = _children.length; i < count; i++) {
        child = _children[i];
        let j = i;
        for (;
          j > 0 && (child2 = _children[j - 1])._localZOrder > child._localZOrder;
          j--
        ) {
          _children[j] = child2;
        }
        _children[j] = child;
      }
      this.emit(EventType.CHILD_REORDER, this);
    }
    cc.director.__fastOff(cc.Director.EVENT_AFTER_UPDATE, this.sortAllChildren, this);
  }
},

[源碼] CCNode.js#L3680: https://github.com/cocos-creator/engine/blob/2.4.5/cocos2d/core/CCNode.js#L3680

上半部分 (Part 1)

隨著一步步深入,我們終于來(lái)到了關(guān)鍵部分粘捎。

現(xiàn)在讓我們琢磨琢磨這個(gè) sortAllChildren 函數(shù)薇缅。

進(jìn)入該函數(shù)的前半段危彩,映入眼簾的是一行賦值語(yǔ)句,將 _childArrivalOrder 屬性設(shè)(重置)為 1泳桦;

緊跟其后的是一個(gè) for 循環(huán)汤徽,遍歷了當(dāng)前節(jié)點(diǎn)的所有「子節(jié)點(diǎn)」,并一一執(zhí)行「子節(jié)點(diǎn)」的 _updateOrderOfArrival 函數(shù)灸撰。

?? 嗯谒府?這個(gè) _updateOrderOfArrival 函數(shù)又是何方神圣?

_updateOrderOfArrival

?? 源碼節(jié)選:

函數(shù):cc.Node.prototype._updateOrderOfArrival

_updateOrderOfArrival() {
  var arrivalOrder = this._parent ? ++this._parent._childArrivalOrder : 0;
  this._localZOrder = (this._localZOrder & 0xffff0000) | arrivalOrder;
  this.emit(EventType.SIBLING_ORDER_CHANGED);
},

??? 做了什么浮毯?

顯而易見(jiàn)的是完疫,_updateOrderOfArrival 函數(shù)的作用就是「更新節(jié)點(diǎn)的 _localZOrder 屬性」。

?? 該函數(shù)中同樣也使用了位操作:

同上债蓝,以二進(jìn)制數(shù)的視角來(lái)進(jìn)行分解這里的位操作壳鹤。

  1. 將父節(jié)點(diǎn)的 _childArrivalOrder(前置)自增 1,并賦予 arrivalOrder(如無(wú)父節(jié)點(diǎn)則為 0)饰迹;
  2. 通過(guò) & 0xffff0000 取出當(dāng)前節(jié)點(diǎn)的 _localZOrder 的「高 16 位」芳誓;
  3. arrivalOrder 作為「低 16 位」與當(dāng)前節(jié)點(diǎn)的 _localZOrder 的「高 16 位」合并;
  4. 最后得到一個(gè)新的「32 位的二進(jìn)制數(shù)」并賦予當(dāng)前節(jié)點(diǎn)的 _localZOrder 屬性蹦锋。

?? 看到這里你是不是已經(jīng)開(kāi)始迷惑了兆沙?

別擔(dān)心欧芽,答案即將揭曉莉掂!

下半部分 (Part 2)

sortAllChildren 函數(shù)的下半部分就比較好理解了。

基本就是通過(guò)「插入排序(Insertion Sort)」來(lái)「排序當(dāng)前節(jié)點(diǎn)的 _children 屬性(子節(jié)點(diǎn)數(shù)組)」千扔。

其中主要根據(jù)子節(jié)點(diǎn)的 _localZOrder 屬性的值來(lái)進(jìn)行排序憎妙,_localZOrder 屬性值小的子節(jié)點(diǎn)排前面,反之排后面曲楚。

排序的關(guān)鍵 (Key of sorting)

?? 分析完源碼后發(fā)現(xiàn)厘唾,節(jié)點(diǎn)的排序并沒(méi)有想象中那么簡(jiǎn)單。

我們可以先得出幾個(gè)結(jié)論:

  1. siblingIndex 是節(jié)點(diǎn)在父節(jié)點(diǎn)的 children 屬性中的下標(biāo)龙誊;
  2. zIndex 是一個(gè)獨(dú)立的屬性抚垃,和 siblingIndex 沒(méi)有直接聯(lián)系;
  3. siblingIndex 和 zIndex 的改變都會(huì)觸發(fā)排序趟大;
  4. siblingIndex 和 zIndex 共同組成了節(jié)點(diǎn)的 _localZOrder鹤树;
  5. zIndex 的權(quán)重比 siblingIndex 大;
  6. 節(jié)點(diǎn)的 _localZOrder 直接決定了節(jié)點(diǎn)的最終順序逊朽。

siblingIndex 如何影響排序 (How siblingIndex affects sorting)

我們前面有提到:

  • getSiblingIndex 函數(shù)「返回了當(dāng)前節(jié)點(diǎn)在父節(jié)點(diǎn)的 _children 屬性中的下標(biāo)(位置)」罕伯。
  • setSiblingIndex 函數(shù)「設(shè)置了當(dāng)前節(jié)點(diǎn)在父節(jié)點(diǎn)的 _children 屬性中的下標(biāo)(位置),并通知父節(jié)點(diǎn)進(jìn)行排序」叽讳。

隨后在父節(jié)點(diǎn)的 sortAllChildren 函數(shù)中的上半部分追他,會(huì)以這個(gè)下標(biāo)作為節(jié)點(diǎn) _localZOrder 的低 16 位坟募。

?? 所以我們可以這樣理解:

siblingIndex 是元素下標(biāo),在排序過(guò)程中邑狸,其決定了 _localZOrder 的「低 16 位」懈糯。

zIndex 如何影響排序 (How zIndex affects sorting)

我們前面有提到:

  • zIndexgetter「返回了 _localZOrder 的高 16 位」。
  • zIndexsetter「設(shè)置了 _localZOrder 的高 16 位单雾,并通知父節(jié)點(diǎn)進(jìn)行排序」昂利。

?? 所以我們可以這樣理解:

zIndex 實(shí)際上只是一個(gè)軀殼,其本質(zhì)是 _localZOrder 的「高 16 位」铁坎。

_localZOrder 如何決定順序 (How _localZOrder works)

父節(jié)點(diǎn)的 sortAllChildren 函數(shù)中根據(jù)子節(jié)點(diǎn)的 _localZOrder 大小來(lái)進(jìn)行最終排序蜂奸。

我們可以將 _localZOrder 看做一個(gè)「32 位二進(jìn)制數(shù)」,其由 siblingIndex 和 zIndex 共同組成硬萍。

但是扩所,為什么說(shuō)「zIndex 的權(quán)重比 siblingIndex 大」呢?

因?yàn)?zIndex 決定了 _localZOrder 的「高 16 位」朴乖,而 siblingIndex 決定了 _localZOrder 的「低 16 位」祖屏。

所以,只有在 zIndex 相等的情況下买羞,siblingIndex 的大小才有決定性意義袁勺。

而在 zIndex 不相等的情況下,siblingIndex 的大小就無(wú)所謂了畜普。

?? 舉個(gè)栗子

這里有兩個(gè) 32 位二進(jìn)制數(shù)(偽代碼):

  • A: 0000 0000 0000 0001 xxxx xxxx xxxx xxxx
  • B: 0000 0000 0000 0010 xxxx xxxx xxxx xxxx

由于 B 的「高 16 位」(0000 0000 0000 0010)比 A 的「高 16 位」(0000 0000 0000 0001)大期丰,所以無(wú)論他們的「低 16 位」中的 x 是什么,B 都會(huì)永遠(yuǎn)大于 A吃挑。

實(shí)驗(yàn)一下 (Experiment)

我們可以寫個(gè)小組件來(lái)測(cè)試下 siblingIndex 和 zIndex 對(duì)于 _localZOrder 的影響钝荡。

?? 一頓打碼:

const { ccclass, property, executeInEditMode } = cc._decorator;

@ccclass
@executeInEditMode
export default class Test_NodeOrder extends cc.Component {

  @property({ displayName: 'siblingIndex' })
  get siblingIndex() {
    return this.node.getSiblingIndex();
  }
  set siblingIndex(value) {
    this.node.setSiblingIndex(value);
  }

  @property({ displayName: 'zIndex' })
  get zIndex() {
    return this.node.zIndex;
  }
  set zIndex(value) {
    this.node.zIndex = value;
  }

  @property({ displayName: '_localZOrder' })
  get localZOrder() {
    return this.node._localZOrder;
  }

  @property({ displayName: '_localZOrder (二進(jìn)制)' })
  get localZOrderBinary() {
    return this.node._localZOrder.toString(2).padStart(32, 0);
  }

}

場(chǎng)景一 (Scene 1)

在 1 個(gè)節(jié)點(diǎn)下放置了 1 個(gè)子節(jié)點(diǎn)。

?? 子節(jié)點(diǎn)的排序信息:

zIndex 0

一般來(lái)說(shuō)舶衬,由于節(jié)點(diǎn)的 _childArrivalOrder 是從 1 開(kāi)始的埠通,并且在計(jì)算時(shí)會(huì)先自增 1

所以子節(jié)點(diǎn)的 _localZOrder 的「低 16 位」總會(huì)比其 siblingIndex 大 2 個(gè)數(shù)逛犹。

場(chǎng)景二 (Scene 2)

在 1 個(gè)節(jié)點(diǎn)下放置了 1 個(gè)子節(jié)點(diǎn)端辱,并將子節(jié)點(diǎn)的 zIndex 設(shè)為 1

?? 子節(jié)點(diǎn)的排序信息:

zIndex 1

可以看到虽画,僅僅將節(jié)點(diǎn)的 zIndex 屬性設(shè)為 1舞蔽,其 _localZOrder 就高達(dá) 65538

?? 大概的計(jì)算過(guò)程如下(極為抽象的偽代碼):

1. zIndex = 1 = 0b0000000000000001
2. siblingIndex = 0
3. arrivalOrder = 1 + (siblingIndex + 1)
4. arrivalOrder = 0b0000000000000010
5. _localZOrder = (zIndex << 16) | arrivalOrder
6. _localZOrder = 0b00000000000000010000000000000000 | 0b0000000000000010
7. _localZOrder = 0b00000000000000010000000000000010 = 65538

?? 繼續(xù)簡(jiǎn)化后的偽代碼:

_localZOrder = (zIndex << 16) | (siblingIndex + 2)

?? By the way

當(dāng)一個(gè)節(jié)點(diǎn)沒(méi)有父節(jié)點(diǎn)時(shí)狸捕,它的 arrivalOrder 永遠(yuǎn)是 0喷鸽。

其實(shí)此時(shí)它是啥已經(jīng)不重要了,畢竟沒(méi)有父節(jié)點(diǎn)的節(jié)點(diǎn)本來(lái)就不可能會(huì)被排序灸拍。

場(chǎng)景三 (Scene 3)

在同 1 個(gè)節(jié)點(diǎn)下放置了 6 個(gè)子節(jié)點(diǎn)做祝,將所有子節(jié)點(diǎn)的 zIndex 都設(shè)為 0砾省。

?? 各個(gè)子節(jié)點(diǎn)的排序信息:

zIndex 0 & siblingIndex 0~5

場(chǎng)景四 (Scene 4)

在同 1 個(gè)節(jié)點(diǎn)下放置了 6 個(gè)子節(jié)點(diǎn),將這 6 個(gè)子節(jié)點(diǎn)的 zIndex 設(shè)為 05混槐。

?? 各個(gè)子節(jié)點(diǎn)的排序信息:

zIndex 0~5

可以看到编兄,zIndex 的值會(huì)直接體現(xiàn)在 _localZOrder 的「高 16 位」;每當(dāng) zIndex 增加 1声登,_localZOrder 就會(huì)增加 65537狠鸳。

所以說(shuō) siblingIndex 怎么可能打得過(guò) zIndex

場(chǎng)景五 (Scene 5)

在同 1 個(gè)節(jié)點(diǎn)下放置了 6 個(gè)子節(jié)點(diǎn)悯嗓,將這 6 個(gè)子節(jié)點(diǎn)的 zIndex 設(shè)為 05件舵。

?? 修改第 6 個(gè)子節(jié)點(diǎn)的 siblingIndex04,其排序信息:

zIndex 5 & siblingIndex 0~4

可以看到脯厨,此時(shí)無(wú)論我們?cè)趺葱薷牡?6 個(gè)子節(jié)點(diǎn)的 siblingIndex铅祸,它都會(huì)自動(dòng)變回 5(也就是同級(jí)節(jié)點(diǎn)中的最大值)。

因?yàn)檫@個(gè)子節(jié)點(diǎn)的 zIndex 在其同級(jí)節(jié)點(diǎn)之中有著絕對(duì)的優(yōu)勢(shì)合武。

不太對(duì)勁 (Something wrong)

?? 這里有一個(gè)看起來(lái)不太對(duì)勁的現(xiàn)象!

比如临梗,當(dāng)我們把 siblingIndex5 修改為 0 時(shí),_localZOrder 也相應(yīng)從 327687 變成 327682稼跳;但是當(dāng) siblingIndex 自動(dòng)變回 5 時(shí)盟庞,_localZOrder 也還是 327682,并沒(méi)有變回 327687汤善。

?? 為什么會(huì)這樣什猖?

原因其實(shí)很簡(jiǎn)單:

當(dāng)我們修改節(jié)點(diǎn)的 siblingIndex 時(shí)會(huì)觸發(fā)排序,排序過(guò)程中會(huì)「根據(jù)節(jié)點(diǎn)當(dāng)前時(shí)刻的 siblingIndex 和 zIndex 生成新的 _localZOrder」萎津;

最后在父節(jié)點(diǎn)的 sortAllChildren 函數(shù)中會(huì)根據(jù)子節(jié)點(diǎn)的 _localZOrder 來(lái)對(duì) _children 數(shù)組進(jìn)行排序卸伞,此時(shí)「子節(jié)點(diǎn)的 siblingIndex 也會(huì)被動(dòng)更新」抹镊,「但是 _localZOrder 卻沒(méi)有重新生成」锉屈。

但是,由于 zIndex 存在「絕對(duì)優(yōu)勢(shì)」垮耳,這種“奇怪的現(xiàn)象”其實(shí)并不會(huì)影響到節(jié)點(diǎn)的正常排序~

總結(jié) (Summary)

分析完源碼后颈渊,我們來(lái)總結(jié)一下。

在代碼中修改節(jié)點(diǎn)順序的方法主要有兩種:

  1. 修改節(jié)點(diǎn)的 zIndex 屬性
  2. 通過(guò) setSiblingIndex 函數(shù)設(shè)置

無(wú)論使用以上哪種方法终佛,最終都會(huì)「通過(guò) zIndex 和 siblingIndex 的組合作為依據(jù)來(lái)進(jìn)行排序」俊嗽。

在多數(shù)情況下,「修改節(jié)點(diǎn)的 zIndex 屬性會(huì)使其 setSiblingIndex 函數(shù)失效」铃彰。

這無(wú)形中增加了編碼時(shí)的心智負(fù)擔(dān)绍豁,也增加了問(wèn)題排查的難度。

引擎內(nèi)的用法 (Usage in engine)

出于好奇牙捉,我在引擎源碼中搜了搜竹揍,想看看引擎內(nèi)部有沒(méi)有使用到 zIndex 屬性敬飒。

結(jié)果是:只有幾處與「調(diào)試」相關(guān)的地方使用到了節(jié)點(diǎn)的 zIndex 屬性。

Usage in engine

例如:預(yù)覽模式下芬位,左下角的 Profiler 節(jié)點(diǎn)无拗。

Profiler Node

以及碰撞組件的調(diào)試框等等,這里就不在贅述了昧碉。

建議 (Suggestion)

所以英染,為了避免一些不必要的 BUG 和邏輯沖突。

我的建議是:

「少用甚至不用 zIndex被饿,而優(yōu)先使用 siblingIndex 相關(guān)函數(shù)四康。」

?? 聽(tīng)皮皮一句勸狭握,zIndex 的水太深箭养,你把握不住哥牍!


傳送門

微信推文版本

個(gè)人博客:菜鳥(niǎo)小棧

開(kāi)源主頁(yè):陳皮皮

Eazax Cocos 游戲開(kāi)發(fā)工具包


更多分享

《Cocos Creator 性能優(yōu)化:DrawCall》

《在 Cocos Creator 里畫個(gè)炫酷的雷達(dá)圖》

《用 Shader 寫個(gè)完美的波浪》

《在 Cocos Creator 中優(yōu)雅且高效地管理彈窗》

《JavaScript 內(nèi)存詳解 & 分析指南》

《Cocos Creator 編輯器擴(kuò)展:Quick Finder》

《JavaScript 原始值與包裝對(duì)象》

《Cocos Creator 源碼解讀:引擎啟動(dòng)與主循環(huán)》


公眾號(hào)

菜鳥(niǎo)小棧

?? 我是陳皮皮毕泌,一個(gè)還在不斷學(xué)習(xí)的游戲開(kāi)發(fā)者,一個(gè)熱愛(ài)分享的 Cocos Star Writer嗅辣。

?? 這是我的個(gè)人公眾號(hào)撼泛,專注但不僅限于游戲開(kāi)發(fā)和前端技術(shù)分享。

?? 每一篇原創(chuàng)都非常用心澡谭,你的關(guān)注就是我原創(chuàng)的動(dòng)力愿题!

Input and output.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蛙奖,隨后出現(xiàn)的幾起案子潘酗,更是在濱河造成了極大的恐慌,老刑警劉巖雁仲,帶你破解...
    沈念sama閱讀 221,888評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仔夺,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡攒砖,警方通過(guò)查閱死者的電腦和手機(jī)缸兔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)吹艇,“玉大人惰蜜,你說(shuō)我怎么就攤上這事∈苌瘢” “怎么了抛猖?”我有些...
    開(kāi)封第一講書人閱讀 168,386評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我财著,道長(zhǎng)养交,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 59,726評(píng)論 1 297
  • 正文 為了忘掉前任瓢宦,我火速辦了婚禮碎连,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘驮履。我一直安慰自己鱼辙,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布玫镐。 她就那樣靜靜地躺著倒戏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪恐似。 梳的紋絲不亂的頭發(fā)上杜跷,一...
    開(kāi)封第一講書人閱讀 52,337評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音矫夷,去河邊找鬼葛闷。 笑死,一個(gè)胖子當(dāng)著我的面吹牛双藕,可吹牛的內(nèi)容都是我干的淑趾。 我是一名探鬼主播,決...
    沈念sama閱讀 40,902評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼忧陪,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼扣泊!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起嘶摊,我...
    開(kāi)封第一講書人閱讀 39,807評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤延蟹,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后叶堆,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體阱飘,經(jīng)...
    沈念sama閱讀 46,349評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評(píng)論 3 340
  • 正文 我和宋清朗相戀三年蹂空,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了俯萌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,567評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡上枕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出弱恒,到底是詐尸還是另有隱情辨萍,我是刑警寧澤,帶...
    沈念sama閱讀 36,242評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站锈玉,受9級(jí)特大地震影響爪飘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拉背,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評(píng)論 3 334
  • 文/蒙蒙 一师崎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧椅棺,春花似錦犁罩、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,420評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至诱渤,卻和暖如春丐巫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背勺美。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,531評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工递胧, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人赡茸。 一個(gè)月前我還...
    沈念sama閱讀 48,995評(píng)論 3 377
  • 正文 我出身青樓谓着,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親坛掠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子赊锚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評(píng)論 2 359

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