循环引用
问题背景
编辑器的错误反馈机制是在引用计数的基础上建立起来的。
假设 A 引用了 B ,如果 B 发生了错误, A 也会因此发生错误。
编辑器的引用链条可以抽象为下面这样:
A ➠ B ➠ C ➠ ・・・
而与之对应的错误反馈链条则是这样的:
・・・ C ➠ B ➠ A
这样一个错误反馈机制可以给我们带来开发上的便利,但同时他也引入了一个问题,那就是 循环引用 。
当出现下面这种情况时,
A ➠ B ➠ C ➠ A
A 最终间接引用了他自己,
错误反馈也将由此陷入调用地狱。
避免循环引用
目前编辑器层面上并不能解决此问题,开发者应该尽量避免出现循环引用的情况。
使用引用计数的对象
目前编辑器(非运行时)维护引用计数的对象有,
- 行为树组件
- 行为树任务
- 共享变量
也就是说,应该避免在使用这几个对象时出现循环引用。
控制台警告
当编辑器检测到循环引用时,会在 CocosCreator
的控制台打印一条警告:
circular reference detected [ 一个表示引用链条的数组 ]
开发者在看到这条警告时,应该根据提示在编辑器上取消引用相关对象,打断引用链。
通过脚本引用
如果需要引用相关对象,可以在运行时通过脚本动态引用,避免使用编辑器。
引用一个行为树组件
当前行为树
当你需要引用当前行为树时,可以不用通过行为树选择器,因为 任务基类 本身有提供相关接口来获取当前行为树实例。
外部行为树
当你需要引用一个外部行为树时,可以通过 cc.Node
节点的 getComponent
方法来获取到。因为每个行为树实例都是某个 cc.Node
节点上的一个组件。
看下面这个引用外部行为树的例子,
// ExampleAction.ts
import { btclass, btprop, ActionTask, BehaviorTree } from 'bt.ALL';
@btclass('ExampleAction')
export class ExampleAction extends ActionTask {
@btprop({ type: cc.Node })
public target: cc.Node;
/**
* 缓存外部行为树。
*/
private _externalTree: BehaviorTree;
/**
* 行为树加载完毕。
*/
protected onReady(): void {
if (this.target) {
//
// 获取行为树组件
//
this._externalTree = this.target.getComponent(BehaviorTree);
}
}
};
引用一个行为树任务
通过行为树组件提供的 getTask
等 接口 可以获取到任务实例。
看下面这个引用 Switch
任务的例子,
// ExampleAction.ts
import { btclass, ActionTask, Switch } from 'bt.ALL';
@btclass('ExampleAction')
export class ExmapleAction extends ActionTask {
/**
* 缓存 Switch 任务实例。
*/
private _switch: Switch;
/**
* 行为树加载完毕。
*/
protected onReady(): void {
//
// 在当前行为树搜索,并返回找到的第一个 Switch 任务。
//
this._switch = this.tree.getTask(Switch);
}
};
如果是分支作务,还可以通过自己本身继承的 getTask
等 接口 获取到分支上的任务实例。
看下面这个引用 Switch
任务的例子,
// ExampleComposite.ts
import { btclass, CompositeTask, Switch } from 'bt.ALL';
@btclass('ExampleComposite')
export class ExampleComposite extends CompositeTask {
/**
* 缓存 Switch 任务实例。
*/
private _switches: Switch[];
/**
* 行为树加载完毕。
*/
protected onReady(): void {
//
// 在当前分支上搜索,并返回找到的所有 Switch 任务。
//
this._switches = this.getTasks(Switch);
}
};
引用一个共享变量
目前不支持通过脚本引用共享变量。