推荐一个偷懒的方式,使用装饰器自动绑定节点到脚本的属性
背景
用 Cocos Creator 写脚本组件的时候,有时需要场景中一个节点作为这个脚本的属性值。
按照官方文档推荐的方法,需要以下两步
添加一个
@property
属性,在场景中拖入这个节点。
为了省去场景中的拖拽,也有这样写法
添加属性
getChildByName
当属性多了,就要写一排相似的代码
使用
环境
Cocos Creator 3.8.1
只是为了偷懒
从上面的背景来看,相似的代码可以用装饰器去简化
添加一个
@child
属性,
这样就会直接去组件的子节点中寻找对应的需要的节点或组件,实现自动绑定啦!
代码
这代码不是我写的,是一起工作的扫地僧写的。他说这个东西没什么难度,可以分享给大家。
//Decorator.tstype 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-varsreturn ($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 扫地僧
“点赞“ ”在看” 鼓励一下▼