分享一个根据node z轴大小,调整渲染顺序的脚本,可用于实现2.5d游戏的遮挡关系,优化背包渲染顺序

使用方法,把脚本挂在一个空节点上,把需要z轴渲染顺序的根节点挂在renderNode上,根节点和空节点属性保持一致就好

@class ZRenderRoot
@author YI ZHANG
@date 2022/7/11
@desc
**/
import * as cc from "cc";
const {ccclass, property} = cc._decorator;
let oldFunc2 = cc.UIOpacity.prototype.onEnable;
cc.UIOpacity.prototype.onEnable = function (){
    oldFunc2.call(this);
    // @ts-ignore
    this.node._uiProps.localOpacityEx = this._opacity / 255;
}
let oldFunc3 = cc.UIOpacity.prototype.onEnable;
cc.UIOpacity.prototype.onDisable = function (){
    oldFunc3.call(this);
    // @ts-ignore
    this.node._uiProps.localOpacityEx = 1;
}
Object.defineProperty(cc.UIOpacity.prototype,"opacity",{
    get(): any {
        return this._opacity;
    },
    set(value: any) {
        if (this._opacity === value) {
            return;
        }
        value = ZRenderRoot.clamp(value, 0, 255);
        this._opacity = value;
        let opacity = value / 255;
        this.node._uiProps.localOpacity = opacity;
        this.node._uiProps.localOpacityEx = opacity;
    }
})
@ccclass("ZRenderRoot")
export default class ZRenderRoot extends cc.Component{
    @property(cc.Node)
    renderNode : cc.Node = null!;
    v3_1 = cc.v3();
    v3_2 = cc.v3();
    childrenList : cc.Node[] = [];
    static clamp(value : number,min : number, max :number){
        return value > max ? max : (value < min ? min : value);
    }
    protected onLoad() {
        let self = this;
        Object.defineProperty(this.node,"children",{
            get(): any {
                return self.childrenList;
            }
        });
    }

    protected onEnable() {
        this.renderNode._static = true;
        cc.director.on(cc.Director.EVENT_BEFORE_DRAW,()=>{//发出渲染前事件供自定义组件更新脏视图
            this.childrenList = this.getChildrenList();
        },this);
    }

    static forEachNode(node : cc.Node,fn : (node : cc.Node)=>boolean | void){
        if(fn(node)){
            return true;
        }
        let children = node.children;
        for(let i = 0;i < children.length;++i){
            if(this.forEachNode(children[i],fn)){
                return true;
            }
        }
        return false;
    }

    protected onDisable() {
        this.renderNode._static = false;
        ZRenderRoot.forEachNode(this.renderNode,node => {
            node._static = false;
        })
        this.childrenList.length = 0;
        cc.director.targetOff(this);
    }

    getChildrenList(){
        let list : cc.Node[] = [];
        let i = 0;
        ZRenderRoot.forEachNode(this.renderNode,node => {
            node._static = true;
            // @ts-ignore
            node._indexEx = i++;
            list.push(node);
        })
        list.sort((a,b)=>{
            let aZ = b.getWorldPosition(this.v3_2).z;
            let bZ = a.getWorldPosition(this.v3_1).z;
            if(aZ != bZ){
                return  aZ - bZ;
            }
            // @ts-ignore
            return a._indexEx - b._indexEx;
        });
        this.walk(this.renderNode,1);
        return list;
    }
    walk(node : cc.Node,opacity : number){
        // @ts-ignore
        let localOpacity = node._uiProps.localOpacityEx === undefined ? 1 : node._uiProps.localOpacityEx;
        // @ts-ignore
        opacity = opacity * localOpacity ;
        node._uiProps.localOpacity = opacity;
        if(node._uiProps.uiComp){
            node.layer = 1 << 30;
        }
        let  children = node.children;
        let length = children.length;
        for(let i = 0;i < length;++i){
            this.walk(children[i],opacity);
        }
    }

}
2赞

mark一下

楼主我对你贴图的游戏有兴趣,什么游戏

这个源码,我是没怎么看懂,有几个疑问。

  1. 2.5D游戏的z轴有啥用?如果不是3D的话,z轴一般都用不上吧?难道是层深?

2.这个源码里的 walk 方法看来看去好像是更多是根据z轴排序后,调整node 的 opacity 属性。

  1. 楼主发的那张图,跟源码之间,我没看懂,图中人物也没有 opacity 变化,小僵尸的遮挡关系在第一次渲染前不是已经根据z轴排好的吗? 难道是根据 源码中的方法排序的上下层的关系吗?

昨晚直接把这个代码放在现有的项目里,准备运行试试看,结果可想而知,问题大了,因为改变了原有opacity 定义,各种报错,我也无心继续研究了,放弃!

像是taptap上的 死亡突围:僵尸战争

没什么用,主要是想不改引擎代码的情况下重写children实现渲染顺序的调整。原生还是要改引擎代码的

我一般简单的用这段代码。rootNode.children.sort((a,b)=>b.y-a.y) :rofl: :rofl: :rofl:

主要需要不污染原来的children关系

一定要是同一父节点吧

大道至简.