Cocos Creator 中使用装饰器进行自动绑定

推荐一个偷懒的方式,使用装饰器自动绑定节点到脚本的属性

背景

Cocos Creator 写脚本组件的时候,有时需要场景中一个节点作为这个脚本的属性值。

按照官方文档推荐的方法,需要以下两步

  1. 添加一个 @property 属性,

  2. 在场景中拖入这个节点。

7a54b8c149dc996566224ceea77e29ef.png

为了省去场景中的拖拽,也有这样写法

  1. 添加属性

  2. getChildByName

11e83b4a5644cb0487803ded291ecec3.png

当属性多了,就要写一排相似的代码

b39b7d711d3fe530e986ab89cfa6b25c.png

使用

环境

Cocos Creator 3.8.1

只是为了偷懒

从上面的背景来看,相似的代码可以用装饰器去简化

  1. 添加一个 @child 属性,

2fe63adaa47849862c03e0cfbfa3658e.png

这样就会直接去组件的子节点中寻找对应的需要的节点或组件,实现自动绑定啦!

代码

这代码不是我写的,是一起工作的扫地僧写的。他说这个东西没什么难度,可以分享给大家。

//Decorator.ts
 
type PropertyDecorator = (
    $class: Record<string, any>, $propertyKey: string | symbol, $descriptorOrInitializer?: any,
) => void;
 
import { Node } from "cc"
 
const searchChild = function (node: Node, name: string) {
    let ret = node.getChildByName(name);
    if (ret) return ret;
    for (let i = 0; i < node.children.length; i++) {
        let child = node.children[i];
        if (!child.isValid) continue;
        ret = searchChild(child, name);
        if (ret) return ret;
    }
    return null;
}
 
const CookDecoratorKey = ($desc: string) => `__ccc_decorator_${$desc}__`
 
const KeyChild = CookDecoratorKey("child_cache");
type ParamType = {
    name?: string,
};
 
export function child($opt?: ParamType): PropertyDecorator {
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    return ($target, $propertyKey: string, $descriptorOrInitializer) => {
        const cache: { propertyKey: string, childName: string }[] = $target[KeyChild] ??= [];
        if (!cache.some($vo => $vo.propertyKey === $propertyKey)) {
            cache.push({ propertyKey: $propertyKey, childName: $opt?.name || $propertyKey });
        } else {
            throw new Error(`child 装饰器重复绑定属性:${$propertyKey},class:${$target.name}`);
        }
        if (cache.length === 1) {
            const oldOnLoad: () => void = $target.onLoad || undefined;//$target.onLoad也可以拿到父类的实现
            $target.onLoad = function () {
                cache.forEach($vo => this[$vo.propertyKey] = searchChild(this.node, $vo.childName));
                oldOnLoad && oldOnLoad.apply(this);
            };
        }
    };
}
 
import { Component } from "cc";
 
interface INewable<T = any> extends Function {
    new(...args: any[]): T;
}
 
const KeyComp = CookDecoratorKey("comp_cache");
 
export function comp($compoentClass: INewable<Component>, $childName?: string, $mute = false): PropertyDecorator {
    return ($target, $propertyKey: string, $descriptorOrInitializer) => {
        const cache: { propertyKey: string, compClass: INewable<Component>, childName: string }[] = $target[KeyComp] ??= [];
        if (!cache.some($vo => $vo.propertyKey === $propertyKey)) {
            cache.push({ propertyKey: $propertyKey, compClass: $compoentClass, childName: $childName || $propertyKey });
        } else {
            if (!$mute) {
                throw new Error(`comp装饰器重复绑定属性:${$propertyKey},class:${$target.name}`);
            }
            return;
        }
        if (cache.length === 1) {
            const oldOnLoad: () => void = $target.onLoad || undefined;//$target.onLoad也可以拿到父类的实现
            $target.onLoad = function () {
                cache.forEach($vo => {
                    const node = ($vo.childName ? searchChild(this.node, $vo.childName) : this.node);
                    if (!node) {
                        if (!$mute) {
                            throw new Error(`comp装饰器没有找到适合的node节点:class:${$target.name},组件:${$compoentClass.name},childName:${$childName}`);
                        } else {
                            return;
                        }
                    }
                    this[$vo.propertyKey] = node.getComponent($vo.compClass) || node.addComponent($vo.compClass);
                });
                oldOnLoad && oldOnLoad.apply(this);
            };
        }
    };
}

小结

装饰器实现其实就是面向切面的编程思想吧,貌似,可以在这个切面上面进行封装,偷懒写少点代码,然后高阶的实现目的就是依赖注入之类的思想,其实都是为了极限解耦   --BY 扫地僧

a28b644075d19ce0cbb97d92acf7d63c.jpeg

“点赞“ ”在看” 鼓励一下c94881163aa99866e21630a9e9493f05.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值