init
This commit is contained in:
98
animator-editor/assets/script/editor/Editor.ts
Normal file
98
animator-editor/assets/script/editor/Editor.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import FsmCtr from "./fsm/FsmCtr";
|
||||
import InspectorCtr from "./inspector/InspectorCtr";
|
||||
import Menu from "./menu/Menu";
|
||||
import ParamCtr from "./parameters/ParamCtr";
|
||||
|
||||
const { ccclass, property } = cc._decorator;
|
||||
|
||||
@ccclass
|
||||
export default class Editor extends cc.Component {
|
||||
@property(FsmCtr) Fsm: FsmCtr = null;
|
||||
@property(InspectorCtr) Inspector: InspectorCtr = null;
|
||||
@property(ParamCtr) ParamCtr: ParamCtr = null;
|
||||
@property(Menu) Menu: Menu = null;
|
||||
|
||||
public static Inst: Editor = null;
|
||||
|
||||
/** 按下的按键 */
|
||||
private _keySet: Set<cc.macro.KEY> = new Set();
|
||||
|
||||
protected onLoad() {
|
||||
cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this);
|
||||
cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this);
|
||||
}
|
||||
|
||||
protected onDestroy() {
|
||||
cc.systemEvent.off(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this);
|
||||
cc.systemEvent.off(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this);
|
||||
}
|
||||
|
||||
private onKeyDown(event: cc.Event.EventKeyboard) {
|
||||
this._keySet.add(event.keyCode);
|
||||
|
||||
switch (event.keyCode) {
|
||||
case cc.macro.KEY.s:
|
||||
if (this._keySet.has(cc.macro.KEY.ctrl)) {
|
||||
// 保存工程文件
|
||||
this.saveProject();
|
||||
}
|
||||
break;
|
||||
case cc.macro.KEY.e:
|
||||
if (this._keySet.has(cc.macro.KEY.ctrl)) {
|
||||
// 导出状态机runtime数据
|
||||
this.exportRuntimeData();
|
||||
}
|
||||
break;
|
||||
case cc.macro.KEY.Delete:
|
||||
// 删除
|
||||
this.Fsm.deleteCurUnit();
|
||||
this.Fsm.deleteCurLine();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private onKeyUp(event: cc.Event.EventKeyboard) {
|
||||
this._keySet.delete(event.keyCode);
|
||||
}
|
||||
|
||||
private saveProject() {
|
||||
let data: any = this.Fsm.exportProject();
|
||||
data.parameters = this.ParamCtr.export();
|
||||
this.save('animator.json', data);
|
||||
}
|
||||
|
||||
private exportRuntimeData() {
|
||||
let data: any = this.Fsm.exportRuntimeData();
|
||||
data.parameters = this.ParamCtr.export();
|
||||
this.save('runtimeData.json', data);
|
||||
}
|
||||
|
||||
private save(fileName: string, data: any) {
|
||||
// 存储文件
|
||||
let content = JSON.stringify(data);
|
||||
let eleLink = document.createElement('a');
|
||||
eleLink.download = `${fileName}`;
|
||||
eleLink.style.display = 'none';
|
||||
// 字符内容转变成blob地址
|
||||
let blob = new Blob([content]);
|
||||
eleLink.href = URL.createObjectURL(blob);
|
||||
// 触发点击
|
||||
document.body.appendChild(eleLink);
|
||||
eleLink.click();
|
||||
// 移除
|
||||
document.body.removeChild(eleLink);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入工程文件
|
||||
*/
|
||||
public importProject(data: any) {
|
||||
if (!data.hasOwnProperty('animator')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.ParamCtr.import(data.parameters);
|
||||
this.Fsm.importProject(data);
|
||||
}
|
||||
}
|
||||
9
animator-editor/assets/script/editor/Editor.ts.meta
Normal file
9
animator-editor/assets/script/editor/Editor.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "2ddcfacf-d360-4822-844d-cb7bc61f0968",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
7
animator-editor/assets/script/editor/data.meta
Normal file
7
animator-editor/assets/script/editor/data.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"ver": "1.0.1",
|
||||
"uuid": "336c3839-4f7a-4def-b2db-0b89b2b72f99",
|
||||
"isSubpackage": false,
|
||||
"subpackageName": "",
|
||||
"subMetas": {}
|
||||
}
|
||||
39
animator-editor/assets/script/editor/data/Condition.ts
Normal file
39
animator-editor/assets/script/editor/data/Condition.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { ConditionData, LogicType } from "../../constant/BaseConst";
|
||||
import ParamItem from "../parameters/ParamItem";
|
||||
|
||||
/**
|
||||
* 管理运行时单个条件数据
|
||||
*/
|
||||
export default class Condition {
|
||||
private _paramItem: ParamItem = null;
|
||||
public get paramItem() { return this._paramItem; }
|
||||
|
||||
public value: number = 0;
|
||||
public logic: LogicType = LogicType.EQUAL;
|
||||
|
||||
constructor(paramItem: ParamItem) {
|
||||
this.reset(paramItem);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁
|
||||
*/
|
||||
public destroy() {
|
||||
|
||||
}
|
||||
|
||||
public reset(paramItem: ParamItem) {
|
||||
this._paramItem = paramItem;
|
||||
this.value = 0;
|
||||
this.logic = LogicType.EQUAL;
|
||||
}
|
||||
|
||||
public getConditionData() {
|
||||
let data: ConditionData = {
|
||||
param: this._paramItem.paramName,
|
||||
value: this.value,
|
||||
logic: this.logic
|
||||
};
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "2ad76e07-2aca-40af-8bb1-3a90782bf375",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
224
animator-editor/assets/script/editor/data/State.ts
Normal file
224
animator-editor/assets/script/editor/data/State.ts
Normal file
@@ -0,0 +1,224 @@
|
||||
import Events, { EventName } from "../../common/util/Events";
|
||||
import Tool from "../../common/util/Tool";
|
||||
import { TransitionData } from "../../constant/BaseConst";
|
||||
import ParamItem from "../parameters/ParamItem";
|
||||
import StateMachine from "./StateMachine";
|
||||
import Transition from "./Transition";
|
||||
|
||||
/**
|
||||
* 管理运行时状态数据
|
||||
*/
|
||||
export default class State {
|
||||
//#region 静态成员
|
||||
/** 记录除AnyState外所有状态数据 */
|
||||
private static _allStates: Set<State> = new Set();
|
||||
|
||||
public static getAllStates() {
|
||||
return this._allStates;
|
||||
}
|
||||
|
||||
private static add(s: State) {
|
||||
this._allStates.add(s);
|
||||
}
|
||||
|
||||
private static delete(s: State) {
|
||||
this._allStates.delete(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取唯一的状态名
|
||||
* @param state 需要命名的state
|
||||
* @param name 传入的命名
|
||||
*/
|
||||
private static getUniqueName(state: State, name: string = 'State') {
|
||||
let index = 0;
|
||||
let findName = false;
|
||||
|
||||
while (!findName) {
|
||||
findName = true;
|
||||
let values = this._allStates.values();
|
||||
for (let i = 0; i < this._allStates.size; i++) {
|
||||
let s: State = values.next().value;
|
||||
if (s === state) {
|
||||
continue;
|
||||
}
|
||||
if (s._name === `${name}${index > 0 ? index : ''}`) {
|
||||
index++;
|
||||
findName = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return `${name}${index > 0 ? index : ''}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取State数量(不包括AnyState)
|
||||
*/
|
||||
public static getStateNum(): number {
|
||||
return this._allStates.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机获取一个State
|
||||
*/
|
||||
public static getRandState(): State {
|
||||
if (this._allStates.size === 0) {
|
||||
return null;
|
||||
}
|
||||
let values = this._allStates.values();
|
||||
return values.next().value;
|
||||
}
|
||||
//#endregion
|
||||
|
||||
private _name: string = '';
|
||||
/** 状态名(唯一) */
|
||||
public get name() { return this._isAnyState ? 'AnyState' : this._name; }
|
||||
public set name(v: string) {
|
||||
if (this._isAnyState || this._name === v) {
|
||||
return;
|
||||
}
|
||||
this._name = State.getUniqueName(this, v);
|
||||
Events.emit(EventName.STATE_NAME_CHANGED, this);
|
||||
}
|
||||
|
||||
/** 动画名 */
|
||||
public motion: string = '';
|
||||
|
||||
private _speed: number = 1;
|
||||
/** 动画播放速度 */
|
||||
public get speed() { return this._speed; }
|
||||
public set speed(v: number) {
|
||||
this._speed = v;
|
||||
}
|
||||
|
||||
/** 动画播放速度混合的number类型参数 */
|
||||
public multiplierParam: ParamItem = null;
|
||||
/** 动画是否循环播放 */
|
||||
public loop: boolean = false;
|
||||
|
||||
/** 转向别的状态的转换数据 */
|
||||
private _transitions: Transition[] = [];
|
||||
|
||||
private _position: cc.Vec2 = cc.v2(0, 0);
|
||||
/** 此节点在父状态机中的坐标 */
|
||||
public get position() { return this._position; }
|
||||
|
||||
private _upStateMachine: StateMachine = null;
|
||||
/** 父状态机 */
|
||||
public get upStateMachine() { return this._upStateMachine; }
|
||||
|
||||
private _isAnyState: boolean = false;
|
||||
/** 是否为AnyState */
|
||||
public get isAnyState() { return this._isAnyState; }
|
||||
|
||||
constructor(upStateMachine: StateMachine, isAnyState: boolean) {
|
||||
this._isAnyState = isAnyState;
|
||||
if (!this._isAnyState) {
|
||||
this._upStateMachine = upStateMachine;
|
||||
this._upStateMachine.add(this);
|
||||
this._name = State.getUniqueName(this);
|
||||
State.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁
|
||||
*/
|
||||
public destroy() {
|
||||
if (!this._isAnyState) {
|
||||
State.delete(this);
|
||||
}
|
||||
this._transitions.forEach((e) => {
|
||||
e.destroy();
|
||||
});
|
||||
this._transitions.length = 0;
|
||||
}
|
||||
|
||||
public changeUpStateMachine(upStateMachine: StateMachine) {
|
||||
if (!this._isAnyState) {
|
||||
this._upStateMachine.delete(this, false);
|
||||
this._upStateMachine = upStateMachine;
|
||||
this._upStateMachine.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
public addTransition(toState: State): Transition {
|
||||
let transition = new Transition(this, toState);
|
||||
Tool.arrayAdd(this._transitions, transition);
|
||||
return transition;
|
||||
}
|
||||
|
||||
public deleteTransition(transition: Transition) {
|
||||
if (Tool.arrayDelete(this._transitions, transition)) {
|
||||
transition.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指向目标的Transition,不传参则返回全部
|
||||
* @param to
|
||||
* @param cur 当前编辑器视图所在状态机
|
||||
*/
|
||||
public getTransitions(to: State | StateMachine = null, cur: StateMachine = null): Transition[] {
|
||||
let transitionArr = [];
|
||||
if (to instanceof State) {
|
||||
this._transitions.forEach((e) => {
|
||||
if (to === e.toState) {
|
||||
transitionArr.push(e);
|
||||
}
|
||||
});
|
||||
} else if (to instanceof StateMachine) {
|
||||
if (to.has(this)) {
|
||||
if (!cur) {
|
||||
cc.error(`[State.getTransitions] error: cur is null`);
|
||||
return transitionArr;
|
||||
}
|
||||
this._transitions.forEach((e) => {
|
||||
if (!cur.has(e.toState)) {
|
||||
transitionArr.push(e);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this._transitions.forEach((e) => {
|
||||
if (to.has(e.toState)) {
|
||||
transitionArr.push(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
transitionArr = this._transitions;
|
||||
}
|
||||
return transitionArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据下标交换transition数组中元素
|
||||
*/
|
||||
public swapTransition(idx1: number, idx2: number) {
|
||||
Tool.arraySwap(this._transitions, idx1, idx2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将元素移动到目标下标的位置,其余元素相对位置不变
|
||||
*/
|
||||
public moveTransition(fromIdx: number, toIdx: number) {
|
||||
Tool.arrayMove(this._transitions, fromIdx, toIdx);
|
||||
}
|
||||
|
||||
public getMultiplierName() {
|
||||
return this.multiplierParam ? this.multiplierParam.paramName : '';
|
||||
}
|
||||
|
||||
public setPosition(x: number | cc.Vec2 | cc.Vec3, y: number = 0) {
|
||||
this._position = cc.v2(x, y);
|
||||
}
|
||||
|
||||
public getAllTransitionData(): TransitionData[] {
|
||||
let arr: TransitionData[] = [];
|
||||
this.getTransitions().forEach((e) => {
|
||||
arr.push(e.getTransitionData());
|
||||
});
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
9
animator-editor/assets/script/editor/data/State.ts.meta
Normal file
9
animator-editor/assets/script/editor/data/State.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "9cc65592-425e-4040-b1a4-6d7c08e12876",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
315
animator-editor/assets/script/editor/data/StateMachine.ts
Normal file
315
animator-editor/assets/script/editor/data/StateMachine.ts
Normal file
@@ -0,0 +1,315 @@
|
||||
import Events, { EventName } from "../../common/util/Events";
|
||||
import State from "./State";
|
||||
import Transition from "./Transition";
|
||||
|
||||
/**
|
||||
* 管理运行时状态机数据
|
||||
*/
|
||||
export default class StateMachine {
|
||||
//#region 静态成员
|
||||
/** 记录子状态机数据 */
|
||||
private static _allSubStateMachines: Set<StateMachine> = new Set();
|
||||
|
||||
private static add(s: StateMachine) {
|
||||
this._allSubStateMachines.add(s);
|
||||
}
|
||||
|
||||
private static delete(s: StateMachine) {
|
||||
this._allSubStateMachines.delete(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取唯一的状态机名
|
||||
* @param stateMachine 需要命名的StateMachine
|
||||
* @param name 传入的命名
|
||||
*/
|
||||
private static getUniqueName(stateMachine: StateMachine, name: string = 'StateMachine') {
|
||||
let index = 0;
|
||||
let findName = false;
|
||||
|
||||
while (!findName) {
|
||||
findName = true;
|
||||
let values = this._allSubStateMachines.values();
|
||||
for (let i = 0; i < this._allSubStateMachines.size; i++) {
|
||||
let s: StateMachine = values.next().value;
|
||||
if (s === stateMachine) {
|
||||
continue;
|
||||
}
|
||||
if (s._name === `${name}${index > 0 ? index : ''}`) {
|
||||
index++;
|
||||
findName = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return `${name}${index > 0 ? index : ''}`;
|
||||
}
|
||||
//#endregion
|
||||
|
||||
private _name: string = '';
|
||||
/** 状态机名(唯一) */
|
||||
public get name() { return this.isMain ? 'BaseLayer' : this._name; }
|
||||
public set name(v: string) {
|
||||
if (this._name === v || this.isMain) {
|
||||
return;
|
||||
}
|
||||
this._name = StateMachine.getUniqueName(this, v);
|
||||
Events.emit(EventName.STATE_MACHINE_NAME_CHANGED, this);
|
||||
}
|
||||
|
||||
private _position: cc.Vec2 = cc.v2(0, 0);
|
||||
/** 此节点在父状态机中的坐标 */
|
||||
public get position() { return this._position; }
|
||||
|
||||
private _layerPos: cc.Vec2 = cc.v2(0, 0);
|
||||
/** 此状态机视图坐标 */
|
||||
public get layerPos() { return this._layerPos; }
|
||||
|
||||
private _layerScale: number = 1;
|
||||
/** 此状态机视图缩放 */
|
||||
public get layerScale() { return this._layerScale; }
|
||||
|
||||
private _anyStatePos: cc.Vec2 = cc.v2(-360, 300);
|
||||
/** AnyState节点在此状态机视图中的坐标 */
|
||||
public get anyStatePos() { return this._anyStatePos; }
|
||||
|
||||
private _upStateMachinePos: cc.Vec2 = cc.v2(360, 300);
|
||||
/** 父状态机节点在此状态机视图中的坐标 */
|
||||
public get upStateMachinePos() { return this._upStateMachinePos; }
|
||||
|
||||
private _upStateMachine: StateMachine = null;
|
||||
/** 父状态机 */
|
||||
public get upStateMachine() { return this._upStateMachine; }
|
||||
|
||||
private _subStateMachines: Set<StateMachine> = new Set();
|
||||
/** 内部子状态机 */
|
||||
public get subStateMachines() { return this._subStateMachines; }
|
||||
|
||||
private _subStates: Set<State> = new Set();
|
||||
/** 内部状态 */
|
||||
public get subStates() { return this._subStates; }
|
||||
|
||||
/** 是否为主状态机 */
|
||||
public get isMain() { return this._upStateMachine === null; }
|
||||
|
||||
constructor(upStateMachine: StateMachine) {
|
||||
this._upStateMachine = upStateMachine;
|
||||
if (!this.isMain) {
|
||||
this._upStateMachine.add(this);
|
||||
this._name = StateMachine.getUniqueName(this);
|
||||
StateMachine.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁
|
||||
*/
|
||||
public destroy() {
|
||||
if (!this.isMain) {
|
||||
StateMachine.delete(this);
|
||||
}
|
||||
this._subStateMachines.forEach((e) => {
|
||||
e.destroy();
|
||||
});
|
||||
this._subStateMachines.clear();
|
||||
this._subStates.forEach((e) => {
|
||||
e.destroy();
|
||||
});
|
||||
this._subStates.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更改父状态机
|
||||
*/
|
||||
public changeUpStateMachine(upStateMachine: StateMachine) {
|
||||
if (!this.isMain) {
|
||||
this._upStateMachine.delete(this, false);
|
||||
this._upStateMachine = upStateMachine;
|
||||
this._upStateMachine.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断某个状态或状态机是否在当前状态机内部(默认递归)
|
||||
* @param sub
|
||||
* @param recursive 是否递归查找内部
|
||||
*/
|
||||
public has(sub: State | StateMachine, recursive: boolean = true): boolean {
|
||||
if (this.isMain && recursive) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sub instanceof State) {
|
||||
if (!recursive) {
|
||||
return this._subStates.has(sub);
|
||||
} else {
|
||||
if (this._subStates.has(sub)) {
|
||||
return true;
|
||||
}
|
||||
let values = this._subStateMachines.values();
|
||||
for (let i = 0; i < this._subStateMachines.size; i++) {
|
||||
let stateMachine: StateMachine = values.next().value;
|
||||
if (stateMachine.has(sub)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (sub instanceof StateMachine) {
|
||||
if (!recursive) {
|
||||
return this._subStateMachines.has(sub);
|
||||
} else {
|
||||
if (this._subStateMachines.has(sub)) {
|
||||
return true;
|
||||
}
|
||||
let values = this._subStateMachines.values();
|
||||
for (let i = 0; i < this._subStateMachines.size; i++) {
|
||||
let stateMachine: StateMachine = values.next().value;
|
||||
if (stateMachine.has(sub)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public add(sub: State | StateMachine) {
|
||||
if (sub instanceof State) {
|
||||
if (sub.isAnyState) {
|
||||
return;
|
||||
}
|
||||
this._subStates.add(sub);
|
||||
} else if (sub instanceof StateMachine) {
|
||||
this._subStateMachines.add(sub);
|
||||
}
|
||||
}
|
||||
|
||||
public delete(sub: State | StateMachine, destroy: boolean = true) {
|
||||
if (destroy) {
|
||||
sub.destroy();
|
||||
}
|
||||
if (sub instanceof State) {
|
||||
this._subStates.delete(sub);
|
||||
} else if (sub instanceof StateMachine) {
|
||||
this._subStateMachines.delete(sub);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将目标从别的状态机移入当前状态机内
|
||||
* @param target 目标状态或状态机
|
||||
* @returns 是否成功进行移入操作
|
||||
*/
|
||||
public moveTargetIn(target: State | StateMachine): boolean {
|
||||
if (target instanceof State) {
|
||||
if (this._subStates.has(target)) {
|
||||
return false;
|
||||
}
|
||||
target.changeUpStateMachine(this);
|
||||
return true;
|
||||
} else if (target instanceof StateMachine) {
|
||||
if (this === target || this._subStateMachines.has(target) || target.has(this)) {
|
||||
return false;
|
||||
}
|
||||
target.changeUpStateMachine(this);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归查找内部所有子状态
|
||||
*/
|
||||
public getAllSubStates(states: Set<State> = new Set()): Set<State> {
|
||||
this._subStates.forEach((e) => {
|
||||
states.add(e);
|
||||
});
|
||||
this._subStateMachines.forEach((e) => {
|
||||
e.getAllSubStates(states);
|
||||
});
|
||||
return states;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找所有外部状态
|
||||
*/
|
||||
public getAllOutStates(): Set<State> {
|
||||
let states: Set<State> = new Set()
|
||||
let allSub = this.getAllSubStates();
|
||||
State.getAllStates().forEach((e) => {
|
||||
if (!allSub.has(e)) {
|
||||
states.add(e);
|
||||
}
|
||||
});
|
||||
return states;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有子状态指向目标的Transition(递归查找)
|
||||
* @param cur 当前编辑器视图所在状态机
|
||||
*/
|
||||
private getSubTransitions(to: State | StateMachine, transitionArr: Transition[], cur: StateMachine) {
|
||||
this._subStates.forEach((e) => {
|
||||
transitionArr = transitionArr.concat(e.getTransitions(to, cur));
|
||||
});
|
||||
this._subStateMachines.forEach((e) => {
|
||||
transitionArr = e.getSubTransitions(to, transitionArr, cur);
|
||||
});
|
||||
return transitionArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有外部状态指向目标的Transition
|
||||
* @param exclude 遍历子状态机时过滤的状态机
|
||||
* @param cur 当前编辑器视图所在状态机
|
||||
*/
|
||||
private getOutTransitions(to: State | StateMachine, transitionArr: Transition[], exclude: StateMachine, cur: StateMachine) {
|
||||
this._subStates.forEach((e) => {
|
||||
transitionArr = transitionArr.concat(e.getTransitions(to, cur));
|
||||
});
|
||||
this._subStateMachines.forEach((e) => {
|
||||
if (e !== exclude)
|
||||
transitionArr = e.getSubTransitions(to, transitionArr, cur);
|
||||
});
|
||||
|
||||
if (this.isMain) {
|
||||
return transitionArr;
|
||||
}
|
||||
transitionArr = this._upStateMachine.getOutTransitions(to, transitionArr, this, cur);
|
||||
return transitionArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指向目标的Transition
|
||||
* @param to
|
||||
*/
|
||||
public getTransitions(to: State | StateMachine): Transition[] {
|
||||
let transitionArr = [];
|
||||
if (this.has(to)) {
|
||||
transitionArr = this.getOutTransitions(to, transitionArr, to.upStateMachine, to.upStateMachine);
|
||||
} else {
|
||||
transitionArr = this.getSubTransitions(to, transitionArr, this._upStateMachine);
|
||||
}
|
||||
return transitionArr;
|
||||
}
|
||||
|
||||
public setPosition(x: number | cc.Vec2 | cc.Vec3, y: number = 0) {
|
||||
this._position = cc.v2(x, y);
|
||||
}
|
||||
|
||||
public setLayerPos(x: number | cc.Vec2 | cc.Vec3, y: number = 0) {
|
||||
this._layerPos = cc.v2(x, y);
|
||||
}
|
||||
|
||||
public setLayerScale(scale: number) {
|
||||
this._layerScale = scale;
|
||||
}
|
||||
|
||||
public setAnyStatePos(x: number | cc.Vec2 | cc.Vec3, y: number = 0) {
|
||||
this._anyStatePos = cc.v2(x, y);
|
||||
}
|
||||
|
||||
public setUpStateMachinePos(x: number | cc.Vec2 | cc.Vec3, y: number = 0) {
|
||||
this._upStateMachinePos = cc.v2(x, y);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "81405035-481c-437c-93ec-8a889596391a",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
89
animator-editor/assets/script/editor/data/Transition.ts
Normal file
89
animator-editor/assets/script/editor/data/Transition.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import Events, { EventName, preloadEvent } from "../../common/util/Events";
|
||||
import Tool from "../../common/util/Tool";
|
||||
import { TransitionData } from "../../constant/BaseConst";
|
||||
import ParamItem from "../parameters/ParamItem";
|
||||
import Condition from "./Condition";
|
||||
import State from "./State";
|
||||
|
||||
/**
|
||||
* 管理运行时单个状态转换数据
|
||||
*/
|
||||
export default class Transition {
|
||||
private _fromState: State = null;
|
||||
public get fromState() { return this._fromState; }
|
||||
|
||||
private _toState: State = null;
|
||||
public get toState() { return this._toState; }
|
||||
|
||||
/** 状态转换的条件 */
|
||||
private _conditions: Condition[] = [];
|
||||
public get conditions() { return this._conditions; }
|
||||
|
||||
/** 状态转换是否需要满足动画播放结束 */
|
||||
public hasExitTime: boolean = false;
|
||||
|
||||
constructor(from: State, to: State) {
|
||||
this._fromState = from;
|
||||
this._toState = to;
|
||||
|
||||
Events.targetOn(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁
|
||||
*/
|
||||
public destroy() {
|
||||
this._conditions.forEach((e) => {
|
||||
e.destroy();
|
||||
});
|
||||
this._conditions.length = 0;
|
||||
Events.targetOff(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 状态转换字符串
|
||||
*/
|
||||
public getTransStr() {
|
||||
return `${this._fromState.name} -> ${this._toState.name}`;
|
||||
}
|
||||
|
||||
public addCondition(paramItem: ParamItem) {
|
||||
let condition = new Condition(paramItem);
|
||||
Tool.arrayAdd(this._conditions, condition);
|
||||
return condition;
|
||||
}
|
||||
|
||||
public deleteCondition(condition: Condition) {
|
||||
if (Tool.arrayDelete(this._conditions, condition)) {
|
||||
condition.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将元素移动到目标下标的位置,其余元素相对位置不变
|
||||
*/
|
||||
public moveCondition(fromIdx: number, toIdx: number) {
|
||||
Tool.arrayMove(this._conditions, fromIdx, toIdx);
|
||||
}
|
||||
|
||||
public getTransitionData() {
|
||||
let data: TransitionData = {
|
||||
toState: this.toState.name,
|
||||
hasExitTime: this.hasExitTime,
|
||||
conditions: []
|
||||
};
|
||||
this._conditions.forEach((e) => {
|
||||
data.conditions.push(e.getConditionData());
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.PARAM_DELETE)
|
||||
private onEventParamDelete(paramItem: ParamItem) {
|
||||
for (let i = this._conditions.length - 1; i >= 0; i--) {
|
||||
if (this._conditions[i].paramItem === paramItem) {
|
||||
this._conditions.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "c5d54a4e-d4e7-4c79-ab84-4ee81a0633db",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
7
animator-editor/assets/script/editor/fsm.meta
Normal file
7
animator-editor/assets/script/editor/fsm.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"ver": "1.0.1",
|
||||
"uuid": "cd45d1cc-4d5c-4e5c-9728-42693d0ffadf",
|
||||
"isSubpackage": false,
|
||||
"subpackageName": "",
|
||||
"subMetas": {}
|
||||
}
|
||||
64
animator-editor/assets/script/editor/fsm/BarItem.ts
Normal file
64
animator-editor/assets/script/editor/fsm/BarItem.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import Events, { EventName, preloadEvent } from "../../common/util/Events";
|
||||
import { RecycleNode } from "../../common/util/RecyclePool";
|
||||
import StateMachine from "../data/StateMachine";
|
||||
|
||||
const { ccclass, property } = cc._decorator;
|
||||
|
||||
@ccclass
|
||||
export default class BarItem extends cc.Component implements RecycleNode {
|
||||
@property(cc.Node) BgNode: cc.Node = null;
|
||||
@property(cc.Label) NameLabel: cc.Label = null;
|
||||
@property(cc.SpriteFrame) BgFrames: cc.SpriteFrame[] = [];
|
||||
|
||||
private _stateMachine: StateMachine = null;
|
||||
private _needUpdate: boolean = false;
|
||||
|
||||
public reuse() {
|
||||
}
|
||||
|
||||
public unuse() {
|
||||
Events.targetOff(this);
|
||||
}
|
||||
|
||||
public onInit(stateMachine: StateMachine, isCur: boolean) {
|
||||
this._stateMachine = stateMachine;
|
||||
this._needUpdate = true;
|
||||
this.BgNode.getComponent(cc.Sprite).spriteFrame = this._stateMachine.isMain ? this.BgFrames[0] : this.BgFrames[1];
|
||||
this.NameLabel.string = stateMachine.name;
|
||||
this.getComponent(cc.Button).interactable = !isCur;
|
||||
|
||||
this.NameLabel.node.on(cc.Node.EventType.SIZE_CHANGED, this.onLabSizeChanged, this);
|
||||
Events.targetOn(this);
|
||||
}
|
||||
|
||||
protected onDestroy() {
|
||||
Events.targetOff(this);
|
||||
}
|
||||
|
||||
protected lateUpdate() {
|
||||
if (!this._needUpdate) {
|
||||
return;
|
||||
}
|
||||
this._needUpdate = false;
|
||||
|
||||
let left = this._stateMachine.isMain ? 35 : 40;
|
||||
this.node.width = this.NameLabel.node.width + left + 15;
|
||||
this.BgNode.width = (this.node.width + 30) * 2;
|
||||
}
|
||||
|
||||
private onLabSizeChanged() {
|
||||
this._needUpdate = true;
|
||||
}
|
||||
|
||||
private onClick() {
|
||||
Events.emit(EventName.SET_CUR_STATE_MACHINE, this._stateMachine);
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.STATE_MACHINE_NAME_CHANGED)
|
||||
private onEventStateNameChanged(stateMachine: StateMachine) {
|
||||
if (this._stateMachine !== stateMachine) {
|
||||
return;
|
||||
}
|
||||
this.NameLabel.string = stateMachine.name;
|
||||
}
|
||||
}
|
||||
9
animator-editor/assets/script/editor/fsm/BarItem.ts.meta
Normal file
9
animator-editor/assets/script/editor/fsm/BarItem.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "685739a3-7df5-4f61-99e1-efbf0175cf35",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
607
animator-editor/assets/script/editor/fsm/FsmCtr.ts
Normal file
607
animator-editor/assets/script/editor/fsm/FsmCtr.ts
Normal file
@@ -0,0 +1,607 @@
|
||||
import Events, { EventName, preloadEvent } from "../../common/util/Events";
|
||||
import { ANIMATOR_VERSION } from "../../constant/BaseConst";
|
||||
import Condition from "../data/Condition";
|
||||
import State from "../data/State";
|
||||
import StateMachine from "../data/StateMachine";
|
||||
import Transition from "../data/Transition";
|
||||
import Editor from "../Editor";
|
||||
import ParamItem from "../parameters/ParamItem";
|
||||
import Line from "./Line";
|
||||
import MachineLayer from "./MachineLayer";
|
||||
import UnitBase from "./UnitBase";
|
||||
import UnitState from "./UnitState";
|
||||
import UnitStateMachine from "./UnitStateMachine";
|
||||
|
||||
const { ccclass, property } = cc._decorator;
|
||||
|
||||
@ccclass
|
||||
export default class FsmCtr extends cc.Component {
|
||||
@property(MachineLayer) MachineLayer: MachineLayer = null;
|
||||
@property(cc.Node) Cross: cc.Node = null;
|
||||
|
||||
/** 当前按下的鼠标按键 */
|
||||
private _curMouseBtn: number = null;
|
||||
/** moveUnit跟随鼠标的偏移值 */
|
||||
private _moveUnitOffset: cc.Vec2 = cc.v2(0, 0);
|
||||
/** 跟随鼠标移动的unit */
|
||||
private _moveUnit: UnitBase = null;
|
||||
/** 当前选中的unit */
|
||||
private _curUnit: UnitBase = null;
|
||||
/** 临时连线 */
|
||||
private _tempLine: Line = null;
|
||||
/** 当前选中的line */
|
||||
private _curLine: Line = null;
|
||||
|
||||
/** 上一次点击到StateMachine的时间 ms */
|
||||
private _lastClickTime: number = 0;
|
||||
|
||||
protected onLoad() {
|
||||
this.node.on(cc.Node.EventType.MOUSE_DOWN, this.onMouseDown, this);
|
||||
this.node.on(cc.Node.EventType.MOUSE_UP, this.onMouseUp, this);
|
||||
this.node.on(cc.Node.EventType.MOUSE_ENTER, this.onMouseEnter, this);
|
||||
this.node.on(cc.Node.EventType.MOUSE_MOVE, this.onMouseMove, this);
|
||||
this.node.on(cc.Node.EventType.MOUSE_LEAVE, this.onMouseLeave, this);
|
||||
this.node.on(cc.Node.EventType.MOUSE_WHEEL, this.onMouseWheel, this);
|
||||
|
||||
Events.targetOn(this);
|
||||
}
|
||||
|
||||
protected onDestroy() {
|
||||
Events.targetOff(this);
|
||||
}
|
||||
|
||||
protected lateUpdate() {
|
||||
if (this._moveUnit && this.MachineLayer.checkMoveUnit(this._moveUnit)) {
|
||||
this.Cross.active = true;
|
||||
} else {
|
||||
this.Cross.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
//#region import and export
|
||||
private importTransitions(transitionsData: any[], state: State, stateMap: Map<string, State>, paramMap: Map<string, ParamItem>) {
|
||||
transitionsData.forEach((e) => {
|
||||
let toState: State = stateMap.get(e.toState);
|
||||
let transition: Transition = state.addTransition(toState);
|
||||
transition.hasExitTime = e.hasExitTime;
|
||||
e.conditions.forEach((cData) => {
|
||||
let paramItem = paramMap.get(cData.param);
|
||||
let condition: Condition = transition.addCondition(paramItem);
|
||||
condition.value = cData.value;
|
||||
condition.logic = cData.logic;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private importSubState(upData: any, upMachine: StateMachine, stateDataMap: Map<string, any>, stateMap: Map<string, State>, paramMap: Map<string, ParamItem>) {
|
||||
upData.subStates.forEach((name: string) => {
|
||||
let state = new State(upMachine, false);
|
||||
stateMap.set(name, state);
|
||||
let data = stateDataMap.get(name);
|
||||
state.setPosition(data.position[0], data.position[1]);
|
||||
state.name = data.state;
|
||||
state.motion = data.motion;
|
||||
state.speed = data.speed;
|
||||
state.multiplierParam = paramMap.get(data.multiplier) || null;
|
||||
state.loop = data.loop;
|
||||
|
||||
upMachine.add(state);
|
||||
});
|
||||
}
|
||||
|
||||
private importSubMachine(upData: any, upMachine: StateMachine, subMachineDataMap: Map<string, any>, subMachineMap: Map<string, StateMachine>, stateDataMap: Map<string, any>, stateMap: Map<string, State>, paramMap: Map<string, ParamItem>) {
|
||||
upData.subStateMachines.forEach((name: string) => {
|
||||
let stateMachine = new StateMachine(upMachine);
|
||||
subMachineMap.set(name, stateMachine);
|
||||
let data = subMachineDataMap.get(name);
|
||||
stateMachine.setLayerPos(data.layerPos[0], data.layerPos[1]);
|
||||
stateMachine.setLayerScale(data.layerScale);
|
||||
stateMachine.setAnyStatePos(data.anyStatePos[0], data.anyStatePos[1]);
|
||||
stateMachine.name = data.name;
|
||||
stateMachine.setPosition(data.position[0], data.position[1]);
|
||||
stateMachine.setUpStateMachinePos(data.upStateMachinePos[0], data.upStateMachinePos[1]);
|
||||
|
||||
upMachine.add(stateMachine);
|
||||
|
||||
this.importSubState(data, stateMachine, stateDataMap, stateMap, paramMap);
|
||||
this.importSubMachine(data, stateMachine, subMachineDataMap, subMachineMap, stateDataMap, stateMap, paramMap);
|
||||
});
|
||||
}
|
||||
|
||||
private exportAllSubMachine(arr: any[], stateMachine: StateMachine) {
|
||||
stateMachine.subStateMachines.forEach((sub) => {
|
||||
let data = {
|
||||
layerPos: [sub.layerPos.x, sub.layerPos.y],
|
||||
layerScale: sub.layerScale,
|
||||
anyStatePos: [sub.anyStatePos.x, sub.anyStatePos.y],
|
||||
name: sub.name,
|
||||
position: [sub.position.x, sub.position.y],
|
||||
upStateMachine: sub.upStateMachine.name,
|
||||
upStateMachinePos: [sub.upStateMachinePos.x, sub.upStateMachinePos.y],
|
||||
subStates: [],
|
||||
subStateMachines: [],
|
||||
}
|
||||
sub.subStates.forEach((e) => {
|
||||
data.subStates.push(e.name);
|
||||
});
|
||||
sub.subStateMachines.forEach((e) => {
|
||||
data.subStateMachines.push(e.name);
|
||||
});
|
||||
arr.push(data);
|
||||
this.exportAllSubMachine(arr, sub);
|
||||
});
|
||||
}
|
||||
|
||||
private exportAllState(arr: any[], stateMachine: StateMachine, isRuntimeData: boolean = false) {
|
||||
stateMachine.subStates.forEach((e) => {
|
||||
let data = null;
|
||||
if (isRuntimeData) {
|
||||
data = {
|
||||
state: e.name,
|
||||
motion: e.motion,
|
||||
speed: e.speed,
|
||||
multiplier: e.getMultiplierName(),
|
||||
loop: e.loop,
|
||||
transitions: e.getAllTransitionData()
|
||||
}
|
||||
} else {
|
||||
data = {
|
||||
position: [e.position.x, e.position.y],
|
||||
upStateMachine: e.upStateMachine.name,
|
||||
state: e.name,
|
||||
motion: e.motion,
|
||||
speed: e.speed,
|
||||
multiplier: e.getMultiplierName(),
|
||||
loop: e.loop,
|
||||
transitions: e.getAllTransitionData()
|
||||
}
|
||||
}
|
||||
arr.push(data);
|
||||
});
|
||||
stateMachine.subStateMachines.forEach((sub) => {
|
||||
this.exportAllState(arr, sub);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入工程数据
|
||||
*/
|
||||
public importProject(data: any) {
|
||||
let paramMap: Map<string, ParamItem> = Editor.Inst.ParamCtr.getParamMap();
|
||||
|
||||
let mainStateMachineData = data.mainStateMachine;
|
||||
let subStateMachinesData = data.subStateMachines;
|
||||
let defaultStateData: string = data.defaultState;
|
||||
let anyStateData = data.anyState;
|
||||
let statesData = data.states;
|
||||
|
||||
let stateDataMap: Map<string, any> = new Map();
|
||||
statesData.forEach((e: any) => { stateDataMap.set(e.state, e); });
|
||||
let stateMap: Map<string, State> = new Map();
|
||||
|
||||
let subMachineDataMap: Map<string, any> = new Map();
|
||||
subStateMachinesData.forEach((e: any) => { subMachineDataMap.set(e.name, e) });
|
||||
let subMachineMap: Map<string, StateMachine> = new Map();
|
||||
|
||||
let main = this.MachineLayer.mainStateMachine;
|
||||
main.setLayerPos(mainStateMachineData.layerPos[0], mainStateMachineData.layerPos[1]);
|
||||
main.setLayerScale(mainStateMachineData.layerScale);
|
||||
main.setAnyStatePos(mainStateMachineData.anyStatePos[0], mainStateMachineData.anyStatePos[1]);
|
||||
this.importSubState(mainStateMachineData, main, stateDataMap, stateMap, paramMap);
|
||||
this.importSubMachine(mainStateMachineData, main, subMachineDataMap, subMachineMap, stateDataMap, stateMap, paramMap);
|
||||
|
||||
if (stateMap.has(defaultStateData))
|
||||
this.MachineLayer.defaultState = stateMap.get(defaultStateData);
|
||||
|
||||
this.importTransitions(anyStateData.transitions, this.MachineLayer.anyState.state, stateMap, paramMap);
|
||||
statesData.forEach((e: any) => {
|
||||
let state: State = stateMap.get(e.state);
|
||||
if (!state) {
|
||||
cc.error('error');
|
||||
}
|
||||
this.importTransitions(e.transitions, state, stateMap, paramMap);
|
||||
});
|
||||
|
||||
this.MachineLayer.setCurStateMachine();
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出工程数据
|
||||
*/
|
||||
public exportProject() {
|
||||
let main = this.MachineLayer.mainStateMachine;
|
||||
let animator = ANIMATOR_VERSION;
|
||||
let mainStateMachine = {
|
||||
layerPos: [main.layerPos.x, main.layerPos.y],
|
||||
layerScale: main.layerScale,
|
||||
anyStatePos: [main.anyStatePos.x, main.anyStatePos.y],
|
||||
subStates: [],
|
||||
subStateMachines: [],
|
||||
};
|
||||
main.subStates.forEach((e) => {
|
||||
mainStateMachine.subStates.push(e.name);
|
||||
});
|
||||
main.subStateMachines.forEach((e) => {
|
||||
mainStateMachine.subStateMachines.push(e.name);
|
||||
});
|
||||
let subStateMachines = [];
|
||||
this.exportAllSubMachine(subStateMachines, main);
|
||||
|
||||
let defaultState: string = this.MachineLayer.defaultState ? this.MachineLayer.defaultState.name : '';
|
||||
let anyState = {
|
||||
transitions: this.MachineLayer.anyState.state.getAllTransitionData()
|
||||
};
|
||||
let states = [];
|
||||
this.exportAllState(states, main);
|
||||
return {
|
||||
animator: animator,
|
||||
mainStateMachine: mainStateMachine,
|
||||
subStateMachines: subStateMachines,
|
||||
defaultState: defaultState,
|
||||
anyState: anyState,
|
||||
states: states
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出runtime数据
|
||||
*/
|
||||
public exportRuntimeData() {
|
||||
let main = this.MachineLayer.mainStateMachine;
|
||||
let defaultState: string = this.MachineLayer.defaultState ? this.MachineLayer.defaultState.name : '';
|
||||
let anyState = {
|
||||
transitions: this.MachineLayer.anyState.state.getAllTransitionData()
|
||||
};
|
||||
let states = [];
|
||||
this.exportAllState(states, main, true);
|
||||
return {
|
||||
defaultState: defaultState,
|
||||
anyState: anyState,
|
||||
states: states
|
||||
};
|
||||
}
|
||||
//#endregion
|
||||
|
||||
/**
|
||||
* 按下鼠标左键的处理
|
||||
*/
|
||||
private onMouseDownLeft(worldPos: cc.Vec2, posInCurLayer: cc.Vec2) {
|
||||
let nextUnit = this.MachineLayer.getUnitByPos(posInCurLayer);
|
||||
if (nextUnit instanceof UnitState) {
|
||||
// 点击到AnyState删除临时连线
|
||||
if (nextUnit.isAnyState) {
|
||||
this.deleteLine(this._tempLine);
|
||||
}
|
||||
|
||||
this._curLine && this._curLine.select(false);
|
||||
this._curLine = null;
|
||||
this._moveUnitOffset = posInCurLayer.sub(nextUnit.node.position);
|
||||
this._moveUnit = nextUnit;
|
||||
|
||||
if (this._curUnit !== nextUnit) {
|
||||
if (this._tempLine) {
|
||||
this._moveUnit = null;
|
||||
|
||||
// 处理临时连线
|
||||
let line = this.MachineLayer.getLineByUnit(this._curUnit, nextUnit);
|
||||
// 新增transition
|
||||
this._curUnit.addTransition(nextUnit, nextUnit.state);
|
||||
if (line) {
|
||||
// 删除临时连线
|
||||
this.deleteLine(this._tempLine);
|
||||
} else {
|
||||
// 连接line
|
||||
this._tempLine.onInit(this._curUnit, nextUnit);
|
||||
// 清除
|
||||
this._tempLine = null;
|
||||
}
|
||||
} else {
|
||||
// 选中state
|
||||
this._curUnit && this._curUnit.select(false);
|
||||
this._curUnit = nextUnit;
|
||||
this._curUnit.select(true);
|
||||
Events.emit(EventName.INSPECTOR_SHOW_UNIT, this._curUnit);
|
||||
}
|
||||
}
|
||||
} else if (nextUnit instanceof UnitStateMachine) {
|
||||
let now = Date.now();
|
||||
let delt = now - this._lastClickTime;
|
||||
this._lastClickTime = now;
|
||||
if (this._curUnit === nextUnit && delt < 500) {
|
||||
this.setCurStateMachine(nextUnit.stateMachine);
|
||||
return;
|
||||
}
|
||||
|
||||
this._curLine && this._curLine.select(false);
|
||||
this._curLine = null;
|
||||
this._moveUnitOffset = posInCurLayer.sub(nextUnit.node.position);
|
||||
this._moveUnit = nextUnit;
|
||||
|
||||
if (this._curUnit !== nextUnit) {
|
||||
if (this._tempLine) {
|
||||
this._moveUnit = null;
|
||||
|
||||
// 弹出选择菜单,显示状态机中所有状态
|
||||
Events.emit(EventName.SHOW_LINE_TO_List, worldPos, nextUnit, this.MachineLayer.curStateMachine);
|
||||
} else {
|
||||
// 选中unit
|
||||
this._curUnit && this._curUnit.select(false);
|
||||
this._curUnit = nextUnit;
|
||||
this._curUnit.select(true);
|
||||
Events.emit(EventName.INSPECTOR_SHOW_UNIT, this._curUnit);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this._moveUnit = null;
|
||||
this._curUnit && this._curUnit.select(false);
|
||||
this._curUnit = null;
|
||||
let nextLine = this.MachineLayer.getLineByPos(posInCurLayer);
|
||||
if (nextLine) {
|
||||
if (this._curLine !== nextLine) {
|
||||
// 选中line
|
||||
this._curLine && this._curLine.select(false);
|
||||
this._curLine = nextLine;
|
||||
this._curLine.select(true);
|
||||
Events.emit(EventName.INSPECTOR_SHOW_LINE, this._curLine);
|
||||
}
|
||||
} else {
|
||||
this._curLine && this._curLine.select(false);
|
||||
this._curLine = null;
|
||||
Events.emit(EventName.INSPECTOR_HIDE);
|
||||
}
|
||||
|
||||
// 删除临时连线
|
||||
this.deleteLine(this._tempLine);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 按下鼠标右键的处理
|
||||
*/
|
||||
private onMouseDownRight(posInCurLayer: cc.Vec2) {
|
||||
// 判断是否选中state
|
||||
let nextUnit = this.MachineLayer.getUnitByPos(posInCurLayer);
|
||||
if (nextUnit) {
|
||||
this._curLine && this._curLine.select(false);
|
||||
this._curLine = null;
|
||||
this._moveUnitOffset = posInCurLayer.sub(nextUnit.node.position);
|
||||
|
||||
if (this._curUnit !== nextUnit) {
|
||||
this._curUnit && this._curUnit.select(false);
|
||||
this._curUnit = nextUnit;
|
||||
this._curUnit.select(true);
|
||||
Events.emit(EventName.INSPECTOR_SHOW_UNIT, this._curUnit);
|
||||
}
|
||||
}
|
||||
|
||||
// 删除临时连线
|
||||
this.deleteLine(this._tempLine);
|
||||
}
|
||||
|
||||
private onMouseDown(event: cc.Event.EventMouse) {
|
||||
this._curMouseBtn = event.getButton();
|
||||
let posInCtr: cc.Vec2 = this.node.convertToNodeSpaceAR(event.getLocation());
|
||||
let posInCurLayer: cc.Vec2 = this.MachineLayer.node.convertToNodeSpaceAR(event.getLocation());
|
||||
|
||||
if (this._curMouseBtn === cc.Event.EventMouse.BUTTON_LEFT) {
|
||||
// 按下鼠标左键
|
||||
this.onMouseDownLeft(event.getLocation(), posInCurLayer);
|
||||
} else if (this._curMouseBtn === cc.Event.EventMouse.BUTTON_RIGHT) {
|
||||
// 按下鼠标右键
|
||||
this.onMouseDownRight(posInCurLayer);
|
||||
} else if (this._curMouseBtn === cc.Event.EventMouse.BUTTON_MIDDLE) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private onMouseUp(event: cc.Event.EventMouse) {
|
||||
let posInCtr: cc.Vec2 = this.node.convertToNodeSpaceAR(event.getLocation());
|
||||
let posInCurLayer: cc.Vec2 = this.MachineLayer.node.convertToNodeSpaceAR(event.getLocation());
|
||||
|
||||
if (this._curMouseBtn === cc.Event.EventMouse.BUTTON_LEFT) {
|
||||
// bug: 没处理跨越多层transition
|
||||
if (this._moveUnit && this.MachineLayer.moveIntoStateMachine(this._moveUnit)) {
|
||||
this._moveUnit = null;
|
||||
this._curUnit = null;
|
||||
Events.emit(EventName.INSPECTOR_HIDE);
|
||||
}
|
||||
} else if (this._curMouseBtn === cc.Event.EventMouse.BUTTON_RIGHT) {
|
||||
// 松开鼠标右键 弹出右键菜单
|
||||
let state = this.MachineLayer.getUnitByPos(posInCurLayer);
|
||||
let curState = (this._curUnit && this._curUnit === state) ? this._curUnit : null;
|
||||
Events.emit(EventName.SHOW_RIGHT_MENU, event.getLocation(), curState);
|
||||
} else if (this._curMouseBtn === cc.Event.EventMouse.BUTTON_MIDDLE) {
|
||||
}
|
||||
|
||||
// 松开鼠标按键时清空
|
||||
this._curMouseBtn = null;
|
||||
}
|
||||
|
||||
private onMouseEnter(event: cc.Event.EventMouse) {
|
||||
if (event.getButton() === null) {
|
||||
this._curMouseBtn = null;
|
||||
}
|
||||
}
|
||||
|
||||
private onMouseMove(event: cc.Event.EventMouse) {
|
||||
let posInCtr: cc.Vec2 = this.node.convertToNodeSpaceAR(event.getLocation());
|
||||
let posInCurLayer: cc.Vec2 = this.MachineLayer.node.convertToNodeSpaceAR(event.getLocation());
|
||||
|
||||
if (event.getButton() === null) {
|
||||
this._curMouseBtn = null;
|
||||
}
|
||||
|
||||
// 移动临时连线
|
||||
if (this._curUnit && this._tempLine) {
|
||||
let unit = this.MachineLayer.getUnitByPos(posInCurLayer);
|
||||
if (unit && unit !== this._curUnit && ((unit instanceof UnitState && !unit.isAnyState) || unit instanceof UnitStateMachine)) {
|
||||
this._tempLine.setLine(this._curUnit.node.position, unit.node.position);
|
||||
} else {
|
||||
this._tempLine.setLine(this._curUnit.node.position, posInCurLayer);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._curMouseBtn === cc.Event.EventMouse.BUTTON_LEFT) {
|
||||
// 按住鼠标左键 移动选中的状态
|
||||
if (this._moveUnit) {
|
||||
this._moveUnit.setPos(posInCurLayer.sub(this._moveUnitOffset));
|
||||
this.Cross.position = posInCtr;
|
||||
}
|
||||
} else if (this._curMouseBtn === cc.Event.EventMouse.BUTTON_RIGHT) {
|
||||
|
||||
} else if (this._curMouseBtn === cc.Event.EventMouse.BUTTON_MIDDLE) {
|
||||
// 按住鼠标中键 移动当前MachineLayer
|
||||
this.MachineLayer.setPos(this.MachineLayer.node.position.add(event.getDelta()));
|
||||
}
|
||||
}
|
||||
|
||||
private onMouseLeave(event: cc.Event.EventMouse) {
|
||||
if (event.getButton() === null) {
|
||||
this._curMouseBtn = null;
|
||||
}
|
||||
}
|
||||
|
||||
private onMouseWheel(event: cc.Event.EventMouse) {
|
||||
// 滚动鼠标滚轮 缩放当前MachineLayer
|
||||
this.MachineLayer.changeScale(event.getScrollY() > 0, event.getLocation());
|
||||
}
|
||||
|
||||
private setCurStateMachine(stateMachine: StateMachine) {
|
||||
if (this.MachineLayer.curStateMachine === stateMachine) {
|
||||
return;
|
||||
}
|
||||
this._lastClickTime = 0;
|
||||
this._moveUnit = null;
|
||||
this._curUnit = null;
|
||||
this._tempLine = null;
|
||||
this._curLine = null;
|
||||
this.MachineLayer.setCurStateMachine(stateMachine);
|
||||
Events.emit(EventName.INSPECTOR_HIDE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除line
|
||||
*/
|
||||
private deleteLine(line: Line) {
|
||||
if (!line) {
|
||||
return;
|
||||
}
|
||||
if (this._curLine === line) {
|
||||
this._curLine = null;
|
||||
}
|
||||
if (this._tempLine === line) {
|
||||
this._tempLine = null;
|
||||
}
|
||||
this.MachineLayer.deleteLine(line);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除当前选中的line
|
||||
*/
|
||||
public deleteCurLine() {
|
||||
if (!this._curLine) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.deleteLine(this._curLine);
|
||||
Events.emit(EventName.INSPECTOR_HIDE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除当前选中的unit
|
||||
*/
|
||||
public deleteCurUnit() {
|
||||
if (!this._curUnit) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._curUnit instanceof UnitState) {
|
||||
if (this._curUnit.isAnyState) {
|
||||
return;
|
||||
}
|
||||
this.MachineLayer.deleteState(this._curUnit);
|
||||
} else if (this._curUnit instanceof UnitStateMachine) {
|
||||
if (this._curUnit.isUp) {
|
||||
return;
|
||||
}
|
||||
this.MachineLayer.deleteStateMachine(this._curUnit);
|
||||
}
|
||||
this._curUnit = null;
|
||||
this._moveUnit = null;
|
||||
this.deleteLine(this._tempLine);
|
||||
Events.emit(EventName.INSPECTOR_HIDE);
|
||||
}
|
||||
|
||||
//#region 按钮回调
|
||||
private onClickCreateState(event: cc.Event) {
|
||||
Events.emit(EventName.CLOSE_MENU);
|
||||
let menuNode: cc.Node = Editor.Inst.Menu.RightMenu;
|
||||
let posInCurLayer: cc.Vec2 = this.MachineLayer.node.convertToNodeSpaceAR(menuNode.parent.convertToWorldSpaceAR(menuNode.position));
|
||||
this.MachineLayer.createState(posInCurLayer);
|
||||
}
|
||||
|
||||
private onClickCreateSubMachine() {
|
||||
Events.emit(EventName.CLOSE_MENU);
|
||||
let menuNode: cc.Node = Editor.Inst.Menu.RightMenu;
|
||||
let posInCurLayer: cc.Vec2 = this.MachineLayer.node.convertToNodeSpaceAR(menuNode.parent.convertToWorldSpaceAR(menuNode.position));
|
||||
this.MachineLayer.createStateMachine(posInCurLayer);
|
||||
}
|
||||
|
||||
private onClickMakeTrasition() {
|
||||
Events.emit(EventName.CLOSE_MENU);
|
||||
if (!this._curUnit) {
|
||||
return;
|
||||
}
|
||||
this._tempLine = this.MachineLayer.createLine();
|
||||
this._tempLine.setLine(this._curUnit.node.position, this._curUnit.node.position);
|
||||
}
|
||||
|
||||
private onClickSetDefault() {
|
||||
Events.emit(EventName.CLOSE_MENU);
|
||||
if (!this._curUnit || !(this._curUnit instanceof UnitState) || this._curUnit.isAnyState) {
|
||||
return;
|
||||
}
|
||||
this.MachineLayer.setDefaultState(this._curUnit);
|
||||
}
|
||||
|
||||
private onClickDeleteCurUnit() {
|
||||
Events.emit(EventName.CLOSE_MENU);
|
||||
this.deleteCurUnit();
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region 事件监听
|
||||
@preloadEvent(EventName.LINE_TO_MACHINE_STATE)
|
||||
private onEventLineToMachineState(state: State, to: UnitStateMachine) {
|
||||
if (!this._curUnit || !this._tempLine) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._moveUnit = null;
|
||||
|
||||
// 处理临时连线
|
||||
let line = this.MachineLayer.getLineByUnit(this._curUnit, to);
|
||||
// 新增transition
|
||||
this._curUnit.addTransition(to, state);
|
||||
if (line || (this._curUnit instanceof UnitState && this._curUnit.isAnyState)) {
|
||||
// 删除临时连线
|
||||
this.deleteLine(this._tempLine);
|
||||
} else {
|
||||
// 连接line
|
||||
this._tempLine.onInit(this._curUnit, to);
|
||||
// 清除
|
||||
this._tempLine = null;
|
||||
}
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.LINE_DELETE)
|
||||
private onEventLineDelete(line: Line) {
|
||||
this.deleteLine(line);
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.SET_CUR_STATE_MACHINE)
|
||||
private onEventSetCurStateMachine(stateMachine: StateMachine) {
|
||||
this.setCurStateMachine(stateMachine);
|
||||
}
|
||||
//#endregion
|
||||
}
|
||||
9
animator-editor/assets/script/editor/fsm/FsmCtr.ts.meta
Normal file
9
animator-editor/assets/script/editor/fsm/FsmCtr.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "323aebfe-14d1-4acd-8e95-b6085c8599dd",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
173
animator-editor/assets/script/editor/fsm/Line.ts
Normal file
173
animator-editor/assets/script/editor/fsm/Line.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
import Events, { EventName, preloadEvent } from "../../common/util/Events";
|
||||
import Transition from "../data/Transition";
|
||||
import UnitBase from "./UnitBase";
|
||||
|
||||
const { ccclass, property } = cc._decorator;
|
||||
|
||||
const RADIUS = 10;
|
||||
const Color = {
|
||||
NORMAL: cc.color(255, 255, 255),
|
||||
SELECT: cc.color(137, 161, 255),
|
||||
TEMP: cc.color(50, 200, 40)
|
||||
};
|
||||
|
||||
@ccclass
|
||||
export default class Line extends cc.Component {
|
||||
@property(cc.Node) SprNode: cc.Node = null;
|
||||
@property(cc.Node) TriNodes: cc.Node[] = [];
|
||||
|
||||
private _hasInit: boolean = false;
|
||||
private _needUpdate: boolean = false;
|
||||
|
||||
/** 连线起点 */
|
||||
private _fromPos: cc.Vec2 = cc.v2(0, 0);
|
||||
/** 连线终点 */
|
||||
private _toPos: cc.Vec2 = cc.v2(0, 0);
|
||||
/** 连线与节点中心的的偏移值 */
|
||||
private _offset: cc.Vec2 = cc.v2(0, 0);
|
||||
|
||||
private _fromUnit: UnitBase = null;
|
||||
public get fromUnit() { return this._fromUnit; }
|
||||
|
||||
private _toUnit: UnitBase = null;
|
||||
public get toUnit() { return this._toUnit; }
|
||||
|
||||
protected onLoad() {
|
||||
this.setColor(Color.TEMP);
|
||||
}
|
||||
|
||||
public onInit(from: UnitBase, to: UnitBase) {
|
||||
this._hasInit = true;
|
||||
this._needUpdate = true;
|
||||
this._fromUnit = from;
|
||||
this._toUnit = to;
|
||||
|
||||
this.setColor(Color.NORMAL);
|
||||
this.checkSize(this.getTransitions().length);
|
||||
|
||||
this._fromUnit.node.on(cc.Node.EventType.POSITION_CHANGED, this.onStateMoved, this);
|
||||
this._toUnit.node.on(cc.Node.EventType.POSITION_CHANGED, this.onStateMoved, this);
|
||||
|
||||
Events.targetOn(this);
|
||||
}
|
||||
|
||||
protected onDestroy() {
|
||||
if (!this._hasInit) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._fromUnit.node && this._fromUnit.node.off(cc.Node.EventType.POSITION_CHANGED, this.onStateMoved, this);
|
||||
this._toUnit.node && this._toUnit.node.off(cc.Node.EventType.POSITION_CHANGED, this.onStateMoved, this);
|
||||
|
||||
Events.targetOff(this);
|
||||
}
|
||||
|
||||
protected lateUpdate() {
|
||||
if (!this._needUpdate) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._needUpdate = true;
|
||||
this.setLine(this._fromUnit.node.position, this._toUnit.node.position);
|
||||
}
|
||||
|
||||
private onStateMoved() {
|
||||
this._needUpdate = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据transitionSet数量更改line上三角的显示
|
||||
*/
|
||||
private checkSize(size: number) {
|
||||
if (size > 1) {
|
||||
this.TriNodes[1].active = true;
|
||||
this.TriNodes[2].active = true;
|
||||
} else {
|
||||
this.TriNodes[1].active = false;
|
||||
this.TriNodes[2].active = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置连线颜色
|
||||
*/
|
||||
private setColor(color: cc.Color) {
|
||||
this.SprNode.color = color;
|
||||
this.TriNodes.forEach((e) => {
|
||||
e.color = color;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取连线所代表的所有Transition
|
||||
*/
|
||||
public getTransitions(): Transition[] {
|
||||
if (!this._hasInit) {
|
||||
return [];
|
||||
}
|
||||
return this._fromUnit.getTransitions(this._toUnit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据起点坐标与终点坐标设置线段
|
||||
* @param fromPos 起点坐标
|
||||
* @param toPos 终点坐标
|
||||
*/
|
||||
public setLine(fromPos: cc.Vec2, toPos: cc.Vec2) {
|
||||
let dir = cc.v2(toPos.sub(fromPos));
|
||||
this._offset = this._hasInit ? dir.rotate(-Math.PI / 2).normalize().mul(RADIUS) : cc.v2(0, 0);
|
||||
this._fromPos = fromPos.add(this._offset);
|
||||
this._toPos = toPos.add(this._offset);
|
||||
|
||||
this.node.position = this._fromPos.add(this._toPos).mul(0.5);
|
||||
this.node.angle = dir.equals(cc.Vec2.ZERO) ? 0 : -cc.misc.radiansToDegrees(dir.signAngle(cc.v2(0, 1)));
|
||||
this.node.height = dir.mag();
|
||||
this.SprNode.height = this.node.height;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断参数是否为连线端点的state
|
||||
*/
|
||||
public relatedState(state: UnitBase) {
|
||||
return this._fromUnit === state || this._toUnit === state;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断Layer层坐标是否在line节点内
|
||||
* @param pos
|
||||
*/
|
||||
public contains(pos: cc.Vec2) {
|
||||
if (!this._hasInit) {
|
||||
return false;
|
||||
}
|
||||
let delt = this._offset;
|
||||
let points: cc.Vec2[] = [this._fromPos.add(delt), this._toPos.add(delt), this._toPos.sub(delt), this._fromPos.sub(delt)];
|
||||
return cc.Intersection.pointInPolygon(pos, points);
|
||||
}
|
||||
|
||||
/**
|
||||
* 选中line
|
||||
* @param value true:选中 false:取消选中
|
||||
*/
|
||||
public select(value: boolean) {
|
||||
this.setColor(value ? Color.SELECT : Color.NORMAL);
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.TRANSITION_ADD)
|
||||
private onEventTransitionAdd(fromUnit: UnitBase, toUnit: UnitBase, transition: Transition) {
|
||||
if (this._fromUnit === fromUnit && this._toUnit === toUnit) {
|
||||
this.checkSize(this.getTransitions().length);
|
||||
}
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.TRANSITION_DELETE)
|
||||
private onEventTransitionDelete(transition: Transition) {
|
||||
let arr = this.getTransitions();
|
||||
if (arr.length <= 0) {
|
||||
// 删除线条
|
||||
Events.emit(EventName.LINE_DELETE, this);
|
||||
} else {
|
||||
this.checkSize(arr.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
9
animator-editor/assets/script/editor/fsm/Line.ts.meta
Normal file
9
animator-editor/assets/script/editor/fsm/Line.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "2aff73f9-2e11-422c-bcc4-c338379b1548",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
479
animator-editor/assets/script/editor/fsm/MachineLayer.ts
Normal file
479
animator-editor/assets/script/editor/fsm/MachineLayer.ts
Normal file
@@ -0,0 +1,479 @@
|
||||
import Events, { EventName, preloadEvent } from "../../common/util/Events";
|
||||
import Res from "../../common/util/Res";
|
||||
import { ResUrl } from "../../constant/ResUrl";
|
||||
import State from "../data/State";
|
||||
import StateMachine from "../data/StateMachine";
|
||||
import Transition from "../data/Transition";
|
||||
import Line from "./Line";
|
||||
import NavBar from "./NavBar";
|
||||
import UnitBase from "./UnitBase";
|
||||
import UnitState from "./UnitState";
|
||||
import UnitStateMachine from "./UnitStateMachine";
|
||||
|
||||
const { ccclass, property } = cc._decorator;
|
||||
|
||||
@ccclass
|
||||
export default class MachineLayer extends cc.Component {
|
||||
@property(cc.Node) Grid: cc.Node = null;
|
||||
@property(cc.Node) UnitContent: cc.Node = null;
|
||||
@property(cc.Node) LineContent: cc.Node = null;
|
||||
@property(NavBar) NavBar: NavBar = null;
|
||||
|
||||
/** 默认状态,整个状态机运行的入口 */
|
||||
private _defaultState: State = null;
|
||||
public get defaultState() { return this._defaultState; }
|
||||
public set defaultState(v: State) { this._defaultState = v; }
|
||||
/** 主状态机 */
|
||||
private _mainStateMachine: StateMachine = null;
|
||||
public get mainStateMachine() { return this._mainStateMachine; }
|
||||
/** 当前视图显示的状态机 */
|
||||
private _curStateMachine: StateMachine = null;
|
||||
public get curStateMachine() { return this._curStateMachine; }
|
||||
/** AnyState节点 */
|
||||
private _anyState: UnitState = null;
|
||||
public get anyState() { return this._anyState; }
|
||||
/** 父状态机节点 */
|
||||
private _upUnit: UnitStateMachine = null;
|
||||
|
||||
protected onLoad() {
|
||||
this.node.setContentSize(6750, 6750);
|
||||
this.Grid.setContentSize(6750, 6750);
|
||||
this._mainStateMachine = new StateMachine(null);
|
||||
this._curStateMachine = this._mainStateMachine;
|
||||
this._anyState = this.createState(cc.v2(-360, 300), true);
|
||||
this._curStateMachine.setAnyStatePos(this._anyState.node.position);
|
||||
|
||||
this.NavBar.refreshBar([this._mainStateMachine]);
|
||||
|
||||
Events.targetOn(this);
|
||||
}
|
||||
|
||||
protected onDestroy() {
|
||||
this.clear();
|
||||
Events.targetOff(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新面包屑导航栏的显示
|
||||
*/
|
||||
private refreshNavBar(curStateMachine: StateMachine) {
|
||||
// 更新面包屑导航栏的显示
|
||||
let arr: StateMachine[] = [curStateMachine];
|
||||
while (curStateMachine.upStateMachine) {
|
||||
arr.unshift(curStateMachine.upStateMachine);
|
||||
curStateMachine = curStateMachine.upStateMachine;
|
||||
}
|
||||
this.NavBar.refreshBar(arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空状态机数据
|
||||
*/
|
||||
public clear() {
|
||||
this._mainStateMachine.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* 改变当前视图显示的状态机,不传参则刷新当前视图
|
||||
*/
|
||||
public setCurStateMachine(stateMachine: StateMachine = null) {
|
||||
if (this._curStateMachine === stateMachine) {
|
||||
return;
|
||||
}
|
||||
if (stateMachine === null) {
|
||||
stateMachine = this._curStateMachine;
|
||||
}
|
||||
this._curStateMachine = stateMachine;
|
||||
|
||||
this.refreshNavBar(stateMachine);
|
||||
|
||||
// 处理父状态机节点和AnyState
|
||||
if (!stateMachine.upStateMachine) {
|
||||
if (this._upUnit) {
|
||||
this._upUnit.node.removeFromParent();
|
||||
this._upUnit.node.destroy();
|
||||
this._upUnit = null;
|
||||
}
|
||||
} else {
|
||||
if (!this._upUnit) {
|
||||
let node: cc.Node = cc.instantiate(Res.getLoaded(ResUrl.PREFAB.STATE_MACHINE_NODE));
|
||||
this.UnitContent.addChild(node);
|
||||
let unitStateMachine = node.getComponent(UnitStateMachine);
|
||||
this._upUnit = unitStateMachine;
|
||||
}
|
||||
this._upUnit.initByStateMachine(stateMachine.upStateMachine, stateMachine.upStateMachinePos);
|
||||
this._upUnit.isDefault = stateMachine.upStateMachine.has(this._defaultState, false);
|
||||
}
|
||||
this._anyState.setPos(stateMachine.anyStatePos);
|
||||
this.node.position = stateMachine.layerPos;
|
||||
this.node.scale = stateMachine.layerScale;
|
||||
|
||||
// 清理连线、状态、状态机节点
|
||||
this.LineContent.destroyAllChildren();
|
||||
this.LineContent.removeAllChildren();
|
||||
for (let i = this.UnitContent.childrenCount - 1; i >= 0; i--) {
|
||||
let node = this.UnitContent.children[i];
|
||||
let unit = node.getComponent(UnitBase);
|
||||
if (unit === this._anyState || unit === this._upUnit) {
|
||||
continue;
|
||||
}
|
||||
node.removeFromParent();
|
||||
node.destroy();
|
||||
}
|
||||
|
||||
// 生成状态机、状态、连线节点
|
||||
let stateMap: Map<State, UnitState> = new Map();
|
||||
let machineMap: Map<StateMachine, UnitStateMachine> = new Map();
|
||||
this._upUnit && machineMap.set(stateMachine.upStateMachine, this._upUnit);
|
||||
stateMachine.subStates.forEach((e) => {
|
||||
let node: cc.Node = cc.instantiate(Res.getLoaded(ResUrl.PREFAB.STATE_NODE));
|
||||
this.UnitContent.addChild(node);
|
||||
let unitState = node.getComponent(UnitState);
|
||||
unitState.initByState(e);
|
||||
unitState.isDefault = e === this._defaultState;
|
||||
stateMap.set(e, unitState);
|
||||
});
|
||||
stateMachine.subStateMachines.forEach((e) => {
|
||||
let node: cc.Node = cc.instantiate(Res.getLoaded(ResUrl.PREFAB.STATE_MACHINE_NODE));
|
||||
this.UnitContent.addChild(node);
|
||||
let unitStateMachine = node.getComponent(UnitStateMachine);
|
||||
unitStateMachine.initByStateMachine(e);
|
||||
unitStateMachine.isDefault = e.has(this._defaultState);
|
||||
if (machineMap.size >= 1) {
|
||||
machineMap.forEach((v, k) => {
|
||||
// 状态机节点相互之间的连线
|
||||
if (k.getTransitions(e).length > 0) {
|
||||
let line = this.createLine();
|
||||
line.onInit(v, unitStateMachine);
|
||||
}
|
||||
if (e.getTransitions(k).length > 0) {
|
||||
let line = this.createLine();
|
||||
line.onInit(unitStateMachine, v);
|
||||
}
|
||||
});
|
||||
}
|
||||
machineMap.set(e, unitStateMachine);
|
||||
});
|
||||
|
||||
let stateKeys = stateMap.keys();
|
||||
for (let i = 0; i < stateMap.size; i++) {
|
||||
let state: State = stateKeys.next().value;
|
||||
let fromUnit: UnitBase = stateMap.get(state);
|
||||
let toUnitSet: Set<UnitBase> = new Set();
|
||||
let transitions = state.getTransitions();
|
||||
transitions.forEach((t: Transition) => {
|
||||
let toUnit: UnitBase = stateMap.get(t.toState);
|
||||
if (!toUnit) {
|
||||
if (stateMachine.has(t.toState)) {
|
||||
let machineKeys = machineMap.keys();
|
||||
for (let j = 0; j < machineMap.size; j++) {
|
||||
let machine: StateMachine = machineKeys.next().value;
|
||||
if (machine !== stateMachine.upStateMachine && machine.has(t.toState)) {
|
||||
toUnit = machineMap.get(machine);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
toUnit = this._upUnit;
|
||||
}
|
||||
if (!toUnit) {
|
||||
cc.error(`[MachineLayer.setCurStateMachine] error transition: ${t}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// fromUnit向其余状态、状态机的连线
|
||||
if (!toUnitSet.has(toUnit)) {
|
||||
toUnitSet.add(toUnit);
|
||||
let line = this.createLine();
|
||||
line.onInit(fromUnit, toUnit);
|
||||
}
|
||||
});
|
||||
|
||||
// AnyState连向fromUnit的连线
|
||||
if (this._anyState.state.getTransitions(state).length > 0) {
|
||||
let line = this.createLine();
|
||||
line.onInit(this._anyState, fromUnit);
|
||||
}
|
||||
|
||||
// 状态机节点连向fromUnit的连线
|
||||
let machineKeys = machineMap.keys();
|
||||
for (let j = 0; j < machineMap.size; j++) {
|
||||
let machine: StateMachine = machineKeys.next().value;
|
||||
if (machine.getTransitions(state).length > 0) {
|
||||
let line = this.createLine();
|
||||
line.onInit(machineMap.get(machine), fromUnit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新建状态机节点
|
||||
*/
|
||||
public createStateMachine(pos: cc.Vec2): UnitStateMachine {
|
||||
let node: cc.Node = cc.instantiate(Res.getLoaded(ResUrl.PREFAB.STATE_MACHINE_NODE));
|
||||
this.UnitContent.addChild(node);
|
||||
let unitStateMachine = node.getComponent(UnitStateMachine);
|
||||
unitStateMachine.onInit(this._curStateMachine);
|
||||
unitStateMachine.setPos(pos);
|
||||
|
||||
return unitStateMachine;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新建状态节点
|
||||
*/
|
||||
public createState(pos: cc.Vec2, isAnyState: boolean = false): UnitState {
|
||||
let node: cc.Node = cc.instantiate(Res.getLoaded(ResUrl.PREFAB.STATE_NODE));
|
||||
this.UnitContent.addChild(node);
|
||||
let unitState = node.getComponent(UnitState);
|
||||
unitState.onInit(this._curStateMachine, isAnyState);
|
||||
unitState.setPos(pos);
|
||||
|
||||
// 新建状态时,如果为当前唯一一个状态则设置为默认状态
|
||||
if (State.getStateNum() === 1) {
|
||||
this.setDefaultState(unitState);
|
||||
}
|
||||
|
||||
return unitState;
|
||||
}
|
||||
|
||||
public createLine(): Line {
|
||||
let node: cc.Node = cc.instantiate(Res.getLoaded(ResUrl.PREFAB.LINE));
|
||||
this.LineContent.addChild(node);
|
||||
return node.getComponent(Line);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除子状态机
|
||||
*/
|
||||
public deleteStateMachine(unitStateMachine: UnitStateMachine) {
|
||||
// 先删除连线,再删除状态机(删除顺序倒过来会影响查找到的连线数据)
|
||||
for (let i = this.LineContent.childrenCount - 1; i >= 0; i--) {
|
||||
let line: Line = this.LineContent.children[i].getComponent(Line);
|
||||
if (line.relatedState(unitStateMachine)) {
|
||||
this.deleteLine(line);
|
||||
}
|
||||
}
|
||||
this._curStateMachine.delete(unitStateMachine.stateMachine);
|
||||
unitStateMachine.node.removeFromParent();
|
||||
unitStateMachine.node.destroy();
|
||||
|
||||
// 删除默认状态时,更改另一个状态为默认状态
|
||||
if (unitStateMachine.isDefault) {
|
||||
this.setDefaultState();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除状态
|
||||
*/
|
||||
public deleteState(unitState: UnitState) {
|
||||
// 先删除连线,再删除状态(删除顺序倒过来会影响查找到的连线数据)
|
||||
for (let i = this.LineContent.childrenCount - 1; i >= 0; i--) {
|
||||
let line: Line = this.LineContent.children[i].getComponent(Line);
|
||||
if (line.relatedState(unitState)) {
|
||||
this.deleteLine(line);
|
||||
}
|
||||
}
|
||||
this._curStateMachine.delete(unitState.state);
|
||||
unitState.node.removeFromParent();
|
||||
unitState.node.destroy();
|
||||
|
||||
// 删除默认状态时,更改另一个状态为默认状态
|
||||
if (unitState.isDefault) {
|
||||
this.setDefaultState();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除连线
|
||||
*/
|
||||
public deleteLine(line: Line) {
|
||||
line.getTransitions().forEach((e) => {
|
||||
e.fromState.deleteTransition(e);
|
||||
});
|
||||
|
||||
line.node.removeFromParent();
|
||||
line.node.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据Layer层坐标获取点击到的unit
|
||||
*/
|
||||
public getUnitByPos(pos: cc.Vec2): UnitBase {
|
||||
for (let i = this.UnitContent.childrenCount - 1; i >= 0; i--) {
|
||||
let node = this.UnitContent.children[i];
|
||||
let rect = node.getBoundingBox();
|
||||
if (rect.contains(pos)) {
|
||||
return node.getComponent(UnitBase);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据Layer层坐标获取点击到的line
|
||||
*/
|
||||
public getLineByPos(pos: cc.Vec2): Line {
|
||||
for (let i = this.LineContent.childrenCount - 1; i >= 0; i--) {
|
||||
let node = this.LineContent.children[i];
|
||||
let line = node.getComponent(Line);
|
||||
if (line.contains(pos)) {
|
||||
return line;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据连线两端节点获取line
|
||||
*/
|
||||
public getLineByUnit(from: UnitBase, to: UnitBase): Line {
|
||||
for (let i = this.LineContent.childrenCount - 1; i >= 0; i--) {
|
||||
let node = this.LineContent.children[i];
|
||||
let line = node.getComponent(Line);
|
||||
if (line.fromUnit === from && line.toUnit === to) {
|
||||
return line;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断moveUnit节点是否可移入某个状态机节点内
|
||||
* @param moveUnit 跟随鼠标移动的unit
|
||||
*/
|
||||
public checkMoveUnit(moveUnit: UnitBase): UnitStateMachine {
|
||||
if (moveUnit === this._anyState || moveUnit === this._upUnit) {
|
||||
return null;
|
||||
}
|
||||
for (let i = this.UnitContent.childrenCount - 1; i >= 0; i--) {
|
||||
let node = this.UnitContent.children[i];
|
||||
let unit = node.getComponent(UnitStateMachine);
|
||||
if (!unit || unit === moveUnit) {
|
||||
continue;
|
||||
}
|
||||
let rect = node.getBoundingBox();
|
||||
if (rect.contains(moveUnit.node.position)) {
|
||||
return unit;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试将moveUnit节点移入重叠的状态机内
|
||||
* @param moveUnit
|
||||
* @returns 是否进行移入操作
|
||||
*/
|
||||
public moveIntoStateMachine(moveUnit: UnitBase): boolean {
|
||||
let unit: UnitStateMachine = this.checkMoveUnit(moveUnit);
|
||||
if (!unit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let needRefresh: boolean = false;
|
||||
if (moveUnit instanceof UnitState) {
|
||||
needRefresh = unit.stateMachine.moveTargetIn(moveUnit.state);
|
||||
} else if (moveUnit instanceof UnitStateMachine) {
|
||||
needRefresh = unit.stateMachine.moveTargetIn(moveUnit.stateMachine);
|
||||
}
|
||||
if (!needRefresh) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.setCurStateMachine();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置默认状态
|
||||
* @param unitState 不传参则表示随机一个设置为默认状态
|
||||
*/
|
||||
public setDefaultState(unitState: UnitState = null) {
|
||||
if (!unitState) {
|
||||
if (this._curStateMachine.subStates.size === 0) {
|
||||
// 当前视图没有State
|
||||
this._defaultState = State.getRandState();
|
||||
if (!this._defaultState) {
|
||||
return;
|
||||
}
|
||||
for (let i = this.UnitContent.childrenCount - 1; i >= 0; i--) {
|
||||
let node = this.UnitContent.children[i];
|
||||
let v = node.getComponent(UnitStateMachine);
|
||||
if (v) {
|
||||
v.isDefault = v.stateMachine.has(this._defaultState);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let i = this.UnitContent.childrenCount - 1; i >= 0; i--) {
|
||||
let node = this.UnitContent.children[i];
|
||||
let v = node.getComponent(UnitState);
|
||||
if (v && !v.isAnyState) {
|
||||
v.isDefault = true;
|
||||
this._defaultState = v.state;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (unitState.state === this._defaultState || unitState.isAnyState) {
|
||||
return;
|
||||
}
|
||||
this.UnitContent.children.forEach((e) => {
|
||||
let v = e.getComponent(UnitBase);
|
||||
if (v === this._anyState) {
|
||||
return;
|
||||
}
|
||||
if (v === unitState) {
|
||||
v.isDefault = true;
|
||||
this._defaultState = unitState.state;
|
||||
} else {
|
||||
v.isDefault = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 限制节点修改坐标时不超出边缘
|
||||
* @param pos
|
||||
*/
|
||||
public setPos(pos: cc.Vec2) {
|
||||
let rect = this.node.getBoundingBox();
|
||||
let x = cc.misc.clampf(pos.x, -this.node.parent.width / 2 + rect.width / 2, this.node.parent.width / 2 - rect.width / 2);
|
||||
let y = cc.misc.clampf(pos.y, -this.node.parent.height / 2 + rect.height / 2, this.node.parent.height / 2 - rect.height / 2);
|
||||
this.node.x = x;
|
||||
this.node.y = y;
|
||||
|
||||
this._curStateMachine.setLayerPos(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* 缩放
|
||||
* @param value true为放大, false为缩小
|
||||
* @param worldPos 鼠标所在的世界坐标
|
||||
*/
|
||||
public changeScale(value: boolean, worldPos: cc.Vec2) {
|
||||
let localPos1 = this.node.convertToNodeSpaceAR(worldPos);
|
||||
this.node.scale = cc.misc.clampf(value ? this.node.scale + 0.1 : this.node.scale - 0.1, 0.3, 3);
|
||||
let localPos2 = this.node.convertToNodeSpaceAR(worldPos);
|
||||
let delta = localPos2.sub(localPos1).mul(this.node.scale);
|
||||
this.setPos(this.node.position.add(delta));
|
||||
|
||||
this._curStateMachine.setLayerScale(this.node.scale);
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.ANY_STATE_MOVE)
|
||||
private onEventAnyStateMove(pos: cc.Vec2) {
|
||||
this._curStateMachine.setAnyStatePos(pos);
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.UP_STATE_MACHINE_MOVE)
|
||||
private onEventUpStateMachineMove(pos: cc.Vec2) {
|
||||
this._curStateMachine.setUpStateMachinePos(pos);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "8366ea51-c189-47b4-ba38-2dd088175777",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
52
animator-editor/assets/script/editor/fsm/NavBar.ts
Normal file
52
animator-editor/assets/script/editor/fsm/NavBar.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import Events, { EventName, preloadEvent } from "../../common/util/Events";
|
||||
import RecyclePool from "../../common/util/RecyclePool";
|
||||
import Res from "../../common/util/Res";
|
||||
import { ResUrl } from "../../constant/ResUrl";
|
||||
import StateMachine from "../data/StateMachine";
|
||||
import Editor from "../Editor";
|
||||
import BarItem from "./BarItem";
|
||||
|
||||
const { ccclass, property } = cc._decorator;
|
||||
|
||||
@ccclass
|
||||
export default class NavBar extends cc.Component {
|
||||
@property(cc.Node) Content: cc.Node = null;
|
||||
|
||||
private _widget: cc.Widget = null;
|
||||
private _contentWidget: cc.Widget = null;
|
||||
|
||||
protected onLoad() {
|
||||
this._widget = this.getComponent(cc.Widget);
|
||||
this._contentWidget = this.Content.getComponent(cc.Widget);
|
||||
Events.targetOn(this);
|
||||
}
|
||||
|
||||
protected onDestroy() {
|
||||
Events.targetOff(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新面包屑导航栏显示
|
||||
* @param stateMachines 按层级顺序排列的状态机数组
|
||||
*/
|
||||
public refreshBar(stateMachines: StateMachine[]) {
|
||||
for (let i = this.Content.childrenCount - 1; i >= 0; i--) {
|
||||
RecyclePool.put(BarItem, this.Content.children[i]);
|
||||
}
|
||||
|
||||
stateMachines.forEach((e, index) => {
|
||||
let node: cc.Node = RecyclePool.get(BarItem) || cc.instantiate(Res.getLoaded(ResUrl.PREFAB.BAR_ITEM));
|
||||
this.Content.addChild(node);
|
||||
let bar = node.getComponent(BarItem);
|
||||
bar.onInit(e, index === stateMachines.length - 1);
|
||||
});
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.RESIZE)
|
||||
private onEventResize(node: cc.Node) {
|
||||
this._widget.left = Editor.Inst.ParamCtr.node.width;
|
||||
this._widget.right = Editor.Inst.Inspector.node.width;
|
||||
this._widget.updateAlignment();
|
||||
this._contentWidget.updateAlignment();
|
||||
}
|
||||
}
|
||||
9
animator-editor/assets/script/editor/fsm/NavBar.ts.meta
Normal file
9
animator-editor/assets/script/editor/fsm/NavBar.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "c7581de7-e34c-4d10-81a7-7cb05e82f531",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
67
animator-editor/assets/script/editor/fsm/UnitBase.ts
Normal file
67
animator-editor/assets/script/editor/fsm/UnitBase.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import State from "../data/State";
|
||||
import Transition from "../data/Transition";
|
||||
|
||||
const { ccclass, property } = cc._decorator;
|
||||
|
||||
export const UnitColor = {
|
||||
NORMAL: cc.color(150, 150, 150),
|
||||
DEFAULT: cc.color(210, 120, 70),
|
||||
ANY_STATE: cc.color(92, 190, 199)
|
||||
}
|
||||
|
||||
/**
|
||||
* 状态节点或者状态机节点的基类
|
||||
*/
|
||||
@ccclass
|
||||
export default class UnitBase extends cc.Component {
|
||||
@property(cc.Node) SelectNode: cc.Node = null;
|
||||
@property(cc.Node) BgNode: cc.Node = null;
|
||||
@property(cc.Label) NameLabel: cc.Label = null;
|
||||
|
||||
protected _isDefault: boolean = false;
|
||||
/**
|
||||
* 是否为默认状态
|
||||
* @virtual
|
||||
*/
|
||||
public get isDefault() { return this._isDefault; }
|
||||
public set isDefault(v: boolean) {
|
||||
this._isDefault = v;
|
||||
this.BgNode.color = this._isDefault ? UnitColor.DEFAULT : UnitColor.NORMAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 选中节点
|
||||
* @param value true:选中 false:取消选中
|
||||
*/
|
||||
public select(value: boolean) {
|
||||
this.SelectNode.active = value;
|
||||
this.node.setSiblingIndex(this.node.parent.childrenCount - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加Transition
|
||||
* @virtual
|
||||
*/
|
||||
public addTransition(toUnit: UnitBase, toState: State) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取此节点到目标节点的所有Transition
|
||||
* @virtual
|
||||
* @param toUnit 目标节点
|
||||
*/
|
||||
public getTransitions(toUnit: UnitBase = null): Transition[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置layer层坐标系下坐标
|
||||
* @virtual
|
||||
*/
|
||||
public setPos(x: number | cc.Vec2 | cc.Vec3, y: number = 0) {
|
||||
let pos: cc.Vec2 = cc.v2(x, y);
|
||||
pos = cc.v2(Math.round(pos.x / 30) * 30, Math.round(pos.y / 30) * 30);
|
||||
this.node.position = pos;
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "de0ee56a-435e-4d63-b773-c33f0739a9f5",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
106
animator-editor/assets/script/editor/fsm/UnitState.ts
Normal file
106
animator-editor/assets/script/editor/fsm/UnitState.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import Events, { EventName, preloadEvent } from "../../common/util/Events";
|
||||
import State from "../data/State";
|
||||
import StateMachine from "../data/StateMachine";
|
||||
import Transition from "../data/Transition";
|
||||
import UnitBase, { UnitColor } from "./UnitBase";
|
||||
import UnitStateMachine from "./UnitStateMachine";
|
||||
|
||||
const { ccclass, property } = cc._decorator;
|
||||
|
||||
/**
|
||||
* 状态节点
|
||||
*/
|
||||
@ccclass
|
||||
export default class UnitState extends UnitBase {
|
||||
private _state: State = null;
|
||||
public get state() { return this._state; }
|
||||
|
||||
/** 是否为AnyState */
|
||||
public get isAnyState() { return this._state.isAnyState; }
|
||||
|
||||
/**
|
||||
* 是否为默认状态
|
||||
* @override
|
||||
*/
|
||||
public get isDefault() { return this._isDefault; }
|
||||
public set isDefault(v: boolean) {
|
||||
if (this.isAnyState) {
|
||||
return;
|
||||
}
|
||||
this._isDefault = v;
|
||||
this.BgNode.color = this._isDefault ? UnitColor.DEFAULT : UnitColor.NORMAL;
|
||||
}
|
||||
|
||||
public onInit(upStateMachine: StateMachine, isAnyState: boolean = false) {
|
||||
this._state = new State(upStateMachine, isAnyState);
|
||||
if (isAnyState) {
|
||||
this.node.scale *= 0.8;
|
||||
this.NameLabel.node.scale *= 1 / 0.8;
|
||||
this.BgNode.color = UnitColor.ANY_STATE;
|
||||
this.NameLabel.string = 'AnyState';
|
||||
} else {
|
||||
this.NameLabel.string = this._state.name;
|
||||
}
|
||||
}
|
||||
|
||||
public initByState(state: State) {
|
||||
this._state = state;
|
||||
this.NameLabel.string = this._state.name;
|
||||
this.node.position = state.position
|
||||
}
|
||||
|
||||
protected onLoad() {
|
||||
Events.targetOn(this);
|
||||
}
|
||||
|
||||
protected onDestroy() {
|
||||
Events.targetOff(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加Transition
|
||||
* @override
|
||||
*/
|
||||
public addTransition(toUnit: UnitBase, toState: State) {
|
||||
let transition = this.state.addTransition(toState);
|
||||
Events.emit(EventName.TRANSITION_ADD, this, toUnit, transition);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取此节点到目标节点的所有Transition,不传参则返回所有从此节点出发的Transition
|
||||
* @override
|
||||
* @param toUnit 目标节点
|
||||
*/
|
||||
public getTransitions(toUnit: UnitBase = null): Transition[] {
|
||||
if (toUnit instanceof UnitState) {
|
||||
return this.state.getTransitions(toUnit.state);
|
||||
} else if (toUnit instanceof UnitStateMachine) {
|
||||
return this.state.getTransitions(toUnit.stateMachine, this._state.upStateMachine);
|
||||
} else {
|
||||
return this.state.getTransitions();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置layer层坐标系下坐标
|
||||
* @override
|
||||
*/
|
||||
public setPos(x: number | cc.Vec2 | cc.Vec3, y: number = 0) {
|
||||
let pos: cc.Vec2 = super.setPos(x, y);
|
||||
if (!this.state.position.equals(pos)) {
|
||||
this.state.setPosition(pos);
|
||||
if (this.isAnyState) {
|
||||
Events.emit(EventName.ANY_STATE_MOVE, pos);
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.STATE_NAME_CHANGED)
|
||||
private onEventStateNameChanged(state: State) {
|
||||
if (this.state !== state) {
|
||||
return;
|
||||
}
|
||||
this.NameLabel.string = state.name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "930040b8-2275-47ce-b7e0-3161308cfc6f",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
92
animator-editor/assets/script/editor/fsm/UnitStateMachine.ts
Normal file
92
animator-editor/assets/script/editor/fsm/UnitStateMachine.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import Events, { EventName, preloadEvent } from "../../common/util/Events";
|
||||
import StateMachine from "../data/StateMachine";
|
||||
import Transition from "../data/Transition";
|
||||
import UnitBase, { UnitColor } from "./UnitBase";
|
||||
import UnitState from "./UnitState";
|
||||
|
||||
const { ccclass, property } = cc._decorator;
|
||||
|
||||
/**
|
||||
* 状态机节点
|
||||
*/
|
||||
@ccclass
|
||||
export default class UnitStateMachine extends UnitBase {
|
||||
private _stateMachine: StateMachine = null;
|
||||
public get stateMachine() { return this._stateMachine; }
|
||||
|
||||
private _isUp: boolean = false;
|
||||
/** 是否为当前状态机视图的父状态机节点 */
|
||||
public get isUp() { return this._isUp; }
|
||||
public set isUp(v: boolean) {
|
||||
this._isUp = v;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为默认状态
|
||||
* @override
|
||||
*/
|
||||
public get isDefault() { return this._isDefault; }
|
||||
public set isDefault(v: boolean) {
|
||||
this._isDefault = v;
|
||||
this.BgNode.color = this._isDefault ? UnitColor.DEFAULT : UnitColor.NORMAL;
|
||||
}
|
||||
|
||||
public onInit(upStateMachine: StateMachine) {
|
||||
this._stateMachine = new StateMachine(upStateMachine);
|
||||
this.NameLabel.string = `${this.isUp ? '(up)' : ''}${this._stateMachine.name}`;
|
||||
}
|
||||
|
||||
public initByStateMachine(stateMachine: StateMachine, upPos: cc.Vec2 = null) {
|
||||
this._stateMachine = stateMachine;
|
||||
this.isUp = !!upPos;
|
||||
this.NameLabel.string = `${this.isUp ? '(up)' : ''}${this._stateMachine.name}`;
|
||||
this.node.position = this.isUp ? upPos : stateMachine.position;
|
||||
}
|
||||
|
||||
protected onLoad() {
|
||||
Events.targetOn(this);
|
||||
}
|
||||
|
||||
protected onDestroy() {
|
||||
Events.targetOff(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取此节点到目标节点的所有Transition
|
||||
* @override
|
||||
* @param toUnit 目标节点
|
||||
*/
|
||||
public getTransitions(toUnit: UnitBase = null): Transition[] {
|
||||
if (toUnit instanceof UnitState) {
|
||||
return this.stateMachine.getTransitions(toUnit.state);
|
||||
} else if (toUnit instanceof UnitStateMachine) {
|
||||
return this.stateMachine.getTransitions(toUnit.stateMachine);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置layer层坐标系下坐标
|
||||
* @override
|
||||
*/
|
||||
public setPos(x: number | cc.Vec2 | cc.Vec3, y: number = 0) {
|
||||
let pos: cc.Vec2 = super.setPos(x, y);
|
||||
if (!this.stateMachine.position.equals(pos)) {
|
||||
if (this.isUp) {
|
||||
Events.emit(EventName.UP_STATE_MACHINE_MOVE, pos);
|
||||
} else {
|
||||
this.stateMachine.setPosition(pos);
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.STATE_MACHINE_NAME_CHANGED)
|
||||
private onEventStateNameChanged(stateMachine: StateMachine) {
|
||||
if (this.stateMachine !== stateMachine) {
|
||||
return;
|
||||
}
|
||||
this.NameLabel.string = `${this.isUp ? '(up)' : ''}${this.stateMachine.name}`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "f37b5690-9da0-4141-ae3f-88548e459831",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
7
animator-editor/assets/script/editor/inspector.meta
Normal file
7
animator-editor/assets/script/editor/inspector.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"ver": "1.0.1",
|
||||
"uuid": "4d63cdfb-8707-4e19-bea3-9f35e8983f0b",
|
||||
"isSubpackage": false,
|
||||
"subpackageName": "",
|
||||
"subMetas": {}
|
||||
}
|
||||
169
animator-editor/assets/script/editor/inspector/ConditionItem.ts
Normal file
169
animator-editor/assets/script/editor/inspector/ConditionItem.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
import Events, { EventName, preloadEvent } from "../../common/util/Events";
|
||||
import { RecycleNode } from "../../common/util/RecyclePool";
|
||||
import { LogicType, ParamType } from "../../constant/BaseConst";
|
||||
import Condition from "../data/Condition";
|
||||
import ParamItem from "../parameters/ParamItem";
|
||||
|
||||
const { ccclass, property } = cc._decorator;
|
||||
|
||||
@ccclass
|
||||
export default class ConditionItem extends cc.Component implements RecycleNode {
|
||||
@property(cc.Node) Bg: cc.Node = null;
|
||||
@property(cc.Label) ParamName: cc.Label = null;
|
||||
@property(cc.Node) LogicNode: cc.Node = null;
|
||||
@property(cc.Node) ValueNode: cc.Node = null;
|
||||
|
||||
/** 组件是否初始化完成 */
|
||||
private _hasInit: boolean = false;
|
||||
|
||||
public condition: Condition = null;
|
||||
|
||||
public reuse() {
|
||||
}
|
||||
|
||||
public unuse() {
|
||||
this._hasInit = false;
|
||||
Events.targetOff(this);
|
||||
}
|
||||
|
||||
public onInit(condition: Condition) {
|
||||
this.Bg.opacity = 0;
|
||||
this.condition = condition;
|
||||
this.ParamName.string = this.condition.paramItem.paramName;
|
||||
if (this.condition.paramItem.type === ParamType.BOOLEAN) {
|
||||
this.LogicNode.active = false;
|
||||
this.ValueNode.active = true;
|
||||
this.ValueNode.getChildByName('number').active = false;
|
||||
this.ValueNode.getChildByName('boolean').active = true;
|
||||
|
||||
let toggle = this.ValueNode.getChildByName('boolean').getComponent(cc.Toggle);
|
||||
toggle.isChecked = this.condition.value !== 0 ? true : false;
|
||||
} else if (this.condition.paramItem.type === ParamType.NUMBER) {
|
||||
this.LogicNode.active = true;
|
||||
this.ValueNode.active = true;
|
||||
this.ValueNode.getChildByName('number').active = true;
|
||||
this.ValueNode.getChildByName('boolean').active = false;
|
||||
|
||||
this.changeLogic(this.condition.logic);
|
||||
let edit = this.ValueNode.getChildByName('number').getComponent(cc.EditBox);
|
||||
edit.string = `${this.condition.value}`;
|
||||
} else {
|
||||
this.LogicNode.active = false;
|
||||
this.ValueNode.active = false;
|
||||
}
|
||||
|
||||
this._hasInit = true;
|
||||
Events.targetOn(this);
|
||||
}
|
||||
|
||||
protected onLoad() {
|
||||
this.Bg.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);
|
||||
}
|
||||
|
||||
protected onDestroy() {
|
||||
Events.targetOff(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置参数
|
||||
* @param paramItem
|
||||
*/
|
||||
public onReset(paramItem: ParamItem) {
|
||||
this.condition.reset(paramItem);
|
||||
this.ParamName.string = this.condition.paramItem.paramName;
|
||||
if (this.condition.paramItem.type === ParamType.BOOLEAN) {
|
||||
this.LogicNode.active = false;
|
||||
this.ValueNode.active = true;
|
||||
this.ValueNode.getChildByName('number').active = false;
|
||||
this.ValueNode.getChildByName('boolean').active = true;
|
||||
|
||||
let toggle = this.ValueNode.getChildByName('boolean').getComponent(cc.Toggle);
|
||||
toggle.isChecked = this.condition.value !== 0 ? true : false;
|
||||
} else if (this.condition.paramItem.type === ParamType.NUMBER) {
|
||||
this.LogicNode.active = true;
|
||||
this.ValueNode.active = true;
|
||||
this.ValueNode.getChildByName('number').active = true;
|
||||
this.ValueNode.getChildByName('boolean').active = false;
|
||||
|
||||
this.changeLogic(this.condition.logic);
|
||||
let edit = this.ValueNode.getChildByName('number').getComponent(cc.EditBox);
|
||||
edit.string = `${this.condition.value}`;
|
||||
} else {
|
||||
this.LogicNode.active = false;
|
||||
this.ValueNode.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换logic选项
|
||||
*/
|
||||
public changeLogic(logic: LogicType) {
|
||||
this.condition.logic = logic;
|
||||
let logicLab: cc.Label = this.LogicNode.getChildByName('lab').getComponent(cc.Label);
|
||||
if (logic === LogicType.EQUAL) {
|
||||
logicLab.string = '===';
|
||||
} else if (logic === LogicType.NOTEQUAL) {
|
||||
logicLab.string = '!==';
|
||||
} else if (logic === LogicType.GREATER) {
|
||||
logicLab.string = '>';
|
||||
} else if (logic === LogicType.LESS) {
|
||||
logicLab.string = '<';
|
||||
} else if (logic === LogicType.GREATER_EQUAL) {
|
||||
logicLab.string = '>=';
|
||||
} else if (logic === LogicType.LESS_EQUAL) {
|
||||
logicLab.string = '<=';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 选中
|
||||
*/
|
||||
public select(value: boolean) {
|
||||
this.Bg.opacity = value ? 255 : 0;
|
||||
}
|
||||
|
||||
private onTouchStart() {
|
||||
this._hasInit && Events.emit(EventName.CONDITION_SELECT, this);
|
||||
}
|
||||
|
||||
private onClickParamSelect(event: cc.Event) {
|
||||
let target: cc.Node = event.target;
|
||||
let worldPos: cc.Vec2 = target.parent.convertToWorldSpaceAR(target.position.sub(cc.v2(0, 0)));
|
||||
Events.emit(EventName.SHOW_PARAM_SELECT, worldPos, 30, this);
|
||||
this._hasInit && Events.emit(EventName.CONDITION_SELECT, this);
|
||||
}
|
||||
|
||||
private onClickLogicSelect(event: cc.Event) {
|
||||
let target: cc.Node = event.target;
|
||||
let worldPos: cc.Vec2 = target.parent.convertToWorldSpaceAR(target.position.sub(cc.v2(0, 0)));
|
||||
Events.emit(EventName.SHOW_LOGIC, worldPos, 30, this);
|
||||
this._hasInit && Events.emit(EventName.CONDITION_SELECT, this);
|
||||
}
|
||||
|
||||
private onValueBoolCheckd(toggle: cc.Toggle) {
|
||||
this.condition.value = toggle.isChecked ? 1 : 0;
|
||||
this._hasInit && Events.emit(EventName.CONDITION_SELECT, this);
|
||||
}
|
||||
|
||||
private onValueNumberEditBegan() {
|
||||
this._hasInit && Events.emit(EventName.CONDITION_SELECT, this);
|
||||
}
|
||||
|
||||
private onValueNumberChanged(str: string, edit: cc.EditBox) {
|
||||
str = str.replace(/[^-.\d]/g, '');
|
||||
if (str === '') {
|
||||
return;
|
||||
}
|
||||
let num = parseFloat(str);
|
||||
this.condition.value = isNaN(num) ? 0 : num;
|
||||
edit.string = `${this.condition.value}`;
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.PARAM_NAME_CHANGED)
|
||||
private onEventParamChanged(paramItem: ParamItem) {
|
||||
if (this.condition.paramItem !== paramItem) {
|
||||
return;
|
||||
}
|
||||
this.ParamName.string = this.condition.paramItem.paramName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "34d79d5d-a60b-4d61-a02c-9f2d4cdfeaea",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
480
animator-editor/assets/script/editor/inspector/InspectorCtr.ts
Normal file
480
animator-editor/assets/script/editor/inspector/InspectorCtr.ts
Normal file
@@ -0,0 +1,480 @@
|
||||
import DragList from "../../common/cmpt/DragList";
|
||||
import Events, { EventName, preloadEvent } from "../../common/util/Events";
|
||||
import RecyclePool from "../../common/util/RecyclePool";
|
||||
import Res from "../../common/util/Res";
|
||||
import { ResUrl } from "../../constant/ResUrl";
|
||||
import Transition from "../data/Transition";
|
||||
import Editor from "../Editor";
|
||||
import Line from "../fsm/Line";
|
||||
import UnitBase from "../fsm/UnitBase";
|
||||
import UnitState from "../fsm/UnitState";
|
||||
import UnitStateMachine from "../fsm/UnitStateMachine";
|
||||
import ParamItem from "../parameters/ParamItem";
|
||||
import ConditionItem from "./ConditionItem";
|
||||
import TransitionItem from "./TransitionItem";
|
||||
|
||||
const { ccclass, property } = cc._decorator;
|
||||
|
||||
@ccclass
|
||||
export default class InspectorCtr extends cc.Component {
|
||||
@property(cc.Node) UnitInfo: cc.Node = null;
|
||||
@property(cc.Node) NameNode: cc.Node = null;
|
||||
@property(cc.Node) MotionNode: cc.Node = null;
|
||||
@property(cc.Node) SpeedNode: cc.Node = null;
|
||||
@property(cc.Node) MultiplierNode: cc.Node = null;
|
||||
@property(cc.Node) LoopNode: cc.Node = null;
|
||||
|
||||
@property(cc.Node) TransitionInfo: cc.Node = null;
|
||||
@property(DragList) TransitionList: DragList = null;
|
||||
|
||||
@property(cc.Node) ConditionInfo: cc.Node = null;
|
||||
@property(DragList) ConditionList: DragList = null;
|
||||
@property(cc.Label) CurTransLab: cc.Label = null;
|
||||
@property(cc.Toggle) HasExitTime: cc.Toggle = null;
|
||||
|
||||
private _layout: cc.Layout = null;
|
||||
private _transitionInfoLayout: cc.Layout = null;
|
||||
private _conditionInfoLayout: cc.Layout = null;
|
||||
|
||||
private _nameEdit: cc.EditBox = null;
|
||||
private _motionEdit: cc.EditBox = null;
|
||||
private _speedEdit: cc.EditBox = null;
|
||||
private _loopToggle: cc.Toggle = null;
|
||||
private _multiplierLabel: cc.Label = null;
|
||||
|
||||
/** fsm视图中选中的节点 */
|
||||
private _unit: UnitBase = null;
|
||||
/** fsm视图选中的连线 */
|
||||
private _line: Line = null;
|
||||
|
||||
/** 选中的TransitionItem */
|
||||
private _transitionItem: TransitionItem = null;
|
||||
private _transLayoutDirty: boolean = false;
|
||||
|
||||
/** 选中的ConditionItem */
|
||||
private _conditionItem: ConditionItem = null;
|
||||
private _conLayoutDirty: boolean = false;
|
||||
|
||||
protected onLoad() {
|
||||
this.UnitInfo.active = false;
|
||||
this.TransitionInfo.active = false;
|
||||
this.ConditionInfo.active = false;
|
||||
|
||||
this._layout = this.getComponent(cc.ScrollView).content.getComponent(cc.Layout);
|
||||
this._transitionInfoLayout = this.TransitionInfo.getComponent(cc.Layout);
|
||||
this._conditionInfoLayout = this.ConditionInfo.getComponent(cc.Layout);
|
||||
|
||||
this._nameEdit = this.NameNode.children[0].getComponent(cc.EditBox);
|
||||
this._motionEdit = this.MotionNode.children[0].getComponent(cc.EditBox);
|
||||
this._speedEdit = this.SpeedNode.children[0].getComponent(cc.EditBox);
|
||||
this._loopToggle = this.LoopNode.children[0].getComponent(cc.Toggle);
|
||||
this._multiplierLabel = this.MultiplierNode.children[1].getComponent(cc.Label);
|
||||
|
||||
// 注册拖拽回调
|
||||
this.TransitionList.setDragCall(this.transitionDragCall, this);
|
||||
this.ConditionList.setDragCall(this.conditionDragCall, this);
|
||||
|
||||
Events.targetOn(this);
|
||||
}
|
||||
|
||||
protected onEnable() {
|
||||
// 屏蔽scrollView内部touch事件,防止与拖拽行为产生冲突
|
||||
let scrollView = this.getComponent(cc.ScrollView);
|
||||
scrollView.node.off(cc.Node.EventType.TOUCH_START, scrollView['_onTouchBegan'], scrollView, true);
|
||||
scrollView.node.off(cc.Node.EventType.TOUCH_MOVE, scrollView['_onTouchMoved'], scrollView, true);
|
||||
scrollView.node.off(cc.Node.EventType.TOUCH_END, scrollView['_onTouchEnded'], scrollView, true);
|
||||
scrollView.node.off(cc.Node.EventType.TOUCH_CANCEL, scrollView['_onTouchCancelled'], scrollView, true);
|
||||
}
|
||||
|
||||
protected onDestroy() {
|
||||
Events.targetOff(this);
|
||||
}
|
||||
|
||||
protected lateUpdate() {
|
||||
let doUpdate: boolean = false;
|
||||
if (this._transLayoutDirty) {
|
||||
this._transLayoutDirty = false;
|
||||
this.TransitionList.node.height = 40;
|
||||
this.TransitionList.layout.updateLayout();
|
||||
this._transitionInfoLayout.updateLayout();
|
||||
doUpdate = true;
|
||||
}
|
||||
if (this._conLayoutDirty) {
|
||||
this._conLayoutDirty = false;
|
||||
this.ConditionList.node.height = 40;
|
||||
this.ConditionList.layout.updateLayout();
|
||||
this._conditionInfoLayout.updateLayout();
|
||||
doUpdate = true;
|
||||
}
|
||||
if (doUpdate) {
|
||||
this._layout.updateLayout();
|
||||
}
|
||||
}
|
||||
|
||||
private transitionDragCall(dragIdx: number, toIdx: number) {
|
||||
if (!(this._unit instanceof UnitState) || !this._transitionItem) {
|
||||
return;
|
||||
}
|
||||
this._unit.state.moveTransition(dragIdx, toIdx);
|
||||
}
|
||||
|
||||
private conditionDragCall(dragIdx: number, toIdx: number) {
|
||||
if (!this._transitionItem || !this._conditionItem) {
|
||||
return;
|
||||
}
|
||||
this._transitionItem.transition.moveCondition(dragIdx, toIdx);
|
||||
}
|
||||
|
||||
private getTransitionItem() {
|
||||
let prefab = Res.getLoaded(ResUrl.PREFAB.TRANSITION_ITEM);
|
||||
let node: cc.Node = RecyclePool.get(TransitionItem) || cc.instantiate(prefab);
|
||||
node.width = this.TransitionList.node.width;
|
||||
return node;
|
||||
}
|
||||
|
||||
private putTransitionItem(node: cc.Node) {
|
||||
this._transLayoutDirty = true;
|
||||
RecyclePool.put(TransitionItem, node);
|
||||
}
|
||||
|
||||
private getConditionItem() {
|
||||
let prefab = Res.getLoaded(ResUrl.PREFAB.CONDITION_ITEM);
|
||||
let node: cc.Node = RecyclePool.get(ConditionItem) || cc.instantiate(prefab);
|
||||
node.width = this.ConditionList.node.width;
|
||||
return node;
|
||||
}
|
||||
|
||||
private putConditionItem(node: cc.Node) {
|
||||
this._conLayoutDirty = true;
|
||||
RecyclePool.put(ConditionItem, node);
|
||||
}
|
||||
|
||||
private showUnitInfo() {
|
||||
if (!this._unit) {
|
||||
this.UnitInfo.active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._unit instanceof UnitState) {
|
||||
if (this._unit.isAnyState) {
|
||||
this.UnitInfo.active = false;
|
||||
return;
|
||||
}
|
||||
this.NameNode.active = true;
|
||||
this.MotionNode.active = true;
|
||||
this.SpeedNode.active = true;
|
||||
this.MultiplierNode.active = true
|
||||
this.LoopNode.active = true;
|
||||
this._nameEdit.string = this._unit.state.name;
|
||||
this._motionEdit.string = this._unit.state.motion;
|
||||
this._speedEdit.string = `${this._unit.state.speed}`;
|
||||
this._loopToggle.isChecked = this._unit.state.loop;
|
||||
this._multiplierLabel.string = this._unit.state.getMultiplierName();
|
||||
} else if (this._unit instanceof UnitStateMachine) {
|
||||
this.NameNode.active = true;
|
||||
this.MotionNode.active = false;
|
||||
this.SpeedNode.active = false;
|
||||
this.MultiplierNode.active = false
|
||||
this.LoopNode.active = false;
|
||||
this._nameEdit.string = this._unit.stateMachine.name;
|
||||
}
|
||||
this.UnitInfo.active = true;
|
||||
}
|
||||
|
||||
private showTransitionInfo() {
|
||||
this.TransitionInfo.active = true;
|
||||
|
||||
this._transitionItem = null;
|
||||
for (let i = this.TransitionList.node.childrenCount - 1; i >= 0; i--) {
|
||||
this.putTransitionItem(this.TransitionList.node.children[i]);
|
||||
}
|
||||
|
||||
if (this._unit) {
|
||||
if (this._unit instanceof UnitStateMachine) {
|
||||
this.TransitionInfo.active = false;
|
||||
return;
|
||||
}
|
||||
this._unit.getTransitions().forEach((data: Transition) => {
|
||||
let item = this.getTransitionItem();
|
||||
this.TransitionList.node.addChild(item);
|
||||
item.getComponent(TransitionItem).onInit(data, this._unit);
|
||||
});
|
||||
this.TransitionList.canDrag = true;
|
||||
} else if (this._line) {
|
||||
let isFirst: boolean = true;
|
||||
this._line.getTransitions().forEach((data: Transition) => {
|
||||
let item = this.getTransitionItem();
|
||||
this.TransitionList.node.addChild(item);
|
||||
let transitionItem: TransitionItem = item.getComponent(TransitionItem);
|
||||
transitionItem.onInit(data, this._unit);
|
||||
if (isFirst) {
|
||||
transitionItem.select(isFirst);
|
||||
this._transitionItem = transitionItem;
|
||||
isFirst = false;
|
||||
}
|
||||
});
|
||||
this.TransitionList.canDrag = false;
|
||||
}
|
||||
}
|
||||
|
||||
private showConditionInfo() {
|
||||
if (!this._transitionItem) {
|
||||
this.ConditionInfo.active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.ConditionInfo.active = true;
|
||||
this.CurTransLab.string = this._transitionItem.transition.getTransStr();
|
||||
this.HasExitTime.isChecked = this._transitionItem.transition.hasExitTime;
|
||||
|
||||
// 根据transition生成condition
|
||||
this._conditionItem = null;
|
||||
for (let i = this.ConditionList.node.childrenCount - 1; i >= 0; i--) {
|
||||
this.putConditionItem(this.ConditionList.node.children[i]);
|
||||
}
|
||||
this._transitionItem.transition.conditions.forEach((e) => {
|
||||
let item = this.getConditionItem();
|
||||
this.ConditionList.node.addChild(item);
|
||||
item.getComponent(ConditionItem).onInit(e);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏inspector显示的内容
|
||||
*/
|
||||
private hide() {
|
||||
this._unit = null;
|
||||
this._line = null;
|
||||
this.UnitInfo.active = false;
|
||||
this.TransitionInfo.active = false;
|
||||
this.ConditionInfo.active = false;
|
||||
}
|
||||
|
||||
//#region editbox、toggle、按钮回调
|
||||
private onNameChanged() {
|
||||
if (this._unit instanceof UnitState) {
|
||||
if (this._nameEdit.string === '') {
|
||||
this._nameEdit.string = this._unit.state.name;
|
||||
return;
|
||||
}
|
||||
this._unit.state.name = this._nameEdit.string;
|
||||
this._nameEdit.string = this._unit.state.name;
|
||||
} else if (this._unit instanceof UnitStateMachine) {
|
||||
if (this._nameEdit.string === '') {
|
||||
this._nameEdit.string = this._unit.stateMachine.name;
|
||||
return;
|
||||
}
|
||||
this._unit.stateMachine.name = this._nameEdit.string;
|
||||
this._nameEdit.string = this._unit.stateMachine.name;
|
||||
}
|
||||
}
|
||||
|
||||
private onMotionChanged() {
|
||||
if (!(this._unit instanceof UnitState)) {
|
||||
return;
|
||||
}
|
||||
this._unit.state.motion = this._motionEdit.string;
|
||||
}
|
||||
|
||||
private onSpeedChanged() {
|
||||
if (!(this._unit instanceof UnitState)) {
|
||||
return;
|
||||
}
|
||||
this._speedEdit.string = this._speedEdit.string.replace(/[^.\d]/g, '');
|
||||
if (this._speedEdit.string === '') {
|
||||
return;
|
||||
}
|
||||
let speed = parseFloat(this._speedEdit.string);
|
||||
this._unit.state.speed = isNaN(speed) ? 1 : speed;
|
||||
this._speedEdit.string = `${this._unit.state.speed}`;
|
||||
}
|
||||
|
||||
private onLoopCheckd() {
|
||||
if (!(this._unit instanceof UnitState)) {
|
||||
return;
|
||||
}
|
||||
this._unit.state.loop = this._loopToggle.isChecked;
|
||||
}
|
||||
|
||||
private onClickMultiplier(event: cc.Event) {
|
||||
if (!(this._unit instanceof UnitState)) {
|
||||
return;
|
||||
}
|
||||
let target = event.target;
|
||||
let worldPos = target.parent.convertToWorldSpaceAR(target.position);
|
||||
worldPos.y -= target.height / 2;
|
||||
Events.emit(EventName.SHOW_MULTIPLIER, worldPos);
|
||||
}
|
||||
|
||||
private onClickDeleteTransition() {
|
||||
if (!this._transitionItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._transitionItem.delete();
|
||||
this.putTransitionItem(this._transitionItem.node);
|
||||
this._transitionItem = null;
|
||||
|
||||
if (this._unit) {
|
||||
this.ConditionInfo.active = false;
|
||||
} else {
|
||||
if (this.TransitionList.node.childrenCount <= 0) {
|
||||
this.hide();
|
||||
} else {
|
||||
this.ConditionInfo.active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private onHasExitTimeCheckd() {
|
||||
if (!this._transitionItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._transitionItem.transition.hasExitTime = this.HasExitTime.isChecked;
|
||||
}
|
||||
|
||||
private onClickAddCondition() {
|
||||
if (!this._transitionItem) {
|
||||
return;
|
||||
}
|
||||
if (Editor.Inst.ParamCtr.ParamContent.childrenCount <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let paramItem: ParamItem = Editor.Inst.ParamCtr.ParamContent.children[0].getComponent(ParamItem);
|
||||
let data = this._transitionItem.transition.addCondition(paramItem);
|
||||
let node = this.getConditionItem();
|
||||
this.ConditionList.node.addChild(node);
|
||||
node.getComponent(ConditionItem).onInit(data);
|
||||
}
|
||||
|
||||
private onClickDeleteCondition() {
|
||||
if (!this._transitionItem || !this._conditionItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._transitionItem.transition.deleteCondition(this._conditionItem.condition);
|
||||
this.putConditionItem(this._conditionItem.node);
|
||||
this._conditionItem = null;
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region 事件监听
|
||||
@preloadEvent(EventName.STATE_NAME_CHANGED)
|
||||
private onEventStateChanged(unit: UnitState) {
|
||||
if (!this._transitionItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.CurTransLab.string = this._transitionItem.transition.getTransStr();
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.TRANSITION_ADD)
|
||||
private onEventTransitionAdd(fromUnit: UnitBase, toUnit: UnitBase, transition: Transition) {
|
||||
if (this._unit === fromUnit) {
|
||||
let item = this.getTransitionItem();
|
||||
this.TransitionList.node.addChild(item);
|
||||
item.getComponent(TransitionItem).onInit(transition, this._unit);
|
||||
}
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.TRANSITION_SELECT)
|
||||
private onEventTransitionSelect(item: TransitionItem) {
|
||||
this._transitionItem = item;
|
||||
this.TransitionList.node.children.forEach((e) => {
|
||||
let ti = e.getComponent(TransitionItem);
|
||||
ti.select(ti === item);
|
||||
});
|
||||
|
||||
this.showConditionInfo();
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.CONDITION_SELECT)
|
||||
private onEventConditionSelect(item: ConditionItem) {
|
||||
this._conditionItem = item;
|
||||
this.ConditionList.node.children.forEach((e) => {
|
||||
let ti = e.getComponent(ConditionItem);
|
||||
ti.select(ti === item);
|
||||
});
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.MULTIPLIER_SELECT)
|
||||
private onEventMultiplierSelect(paramItem: ParamItem) {
|
||||
if (!(this._unit instanceof UnitState)) {
|
||||
return;
|
||||
}
|
||||
this._unit.state.multiplierParam = paramItem;
|
||||
this._multiplierLabel.string = this._unit.state.getMultiplierName();
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.PARAM_DELETE)
|
||||
private onEventParamDelete(paramItem: ParamItem) {
|
||||
if (!this._transitionItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 删除
|
||||
if (this._conditionItem && this._conditionItem.condition.paramItem === paramItem) {
|
||||
this._conditionItem = null;
|
||||
}
|
||||
for (let i = this.ConditionList.node.childrenCount - 1; i >= 0; i--) {
|
||||
let ci: ConditionItem = this.ConditionList.node.children[i].getComponent(ConditionItem);
|
||||
if (ci.condition.paramItem === paramItem) {
|
||||
this.putConditionItem(this.ConditionList.node.children[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.PARAM_NAME_CHANGED)
|
||||
private onEventParamChanged(paramItem: ParamItem) {
|
||||
if (!(this._unit instanceof UnitState) || this._unit.state.multiplierParam !== paramItem) {
|
||||
return;
|
||||
}
|
||||
this._multiplierLabel.string = this._unit.state.multiplierParam.paramName;
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.INSPECTOR_HIDE)
|
||||
private onEventInspectorHide() {
|
||||
this.hide();
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.INSPECTOR_SHOW_UNIT)
|
||||
private onEventShowUnit(unit: UnitBase) {
|
||||
this._unit = unit;
|
||||
this._line = null;
|
||||
this.showUnitInfo();
|
||||
this.showTransitionInfo();
|
||||
this.showConditionInfo();
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.INSPECTOR_SHOW_LINE)
|
||||
private onEventShowLine(line: Line) {
|
||||
this._unit = null;
|
||||
this._line = line;
|
||||
|
||||
this.showUnitInfo();
|
||||
this.showTransitionInfo();
|
||||
this.showConditionInfo();
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.RESIZE)
|
||||
private onEventResize(node: cc.Node) {
|
||||
if (node !== this.node) {
|
||||
return;
|
||||
}
|
||||
if (!this.TransitionInfo.active) {
|
||||
return;
|
||||
}
|
||||
this.TransitionList.node.children.forEach((e) => {
|
||||
e.width = this.TransitionList.node.width;
|
||||
});
|
||||
if (!this.ConditionInfo.active) {
|
||||
return;
|
||||
}
|
||||
this.ConditionList.node.children.forEach((e) => {
|
||||
e.width = this.ConditionList.node.width;
|
||||
});
|
||||
}
|
||||
//#endregion
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "af929db1-5975-4775-8a63-387f2b62e542",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import Events, { EventName, preloadEvent } from "../../common/util/Events";
|
||||
import { RecycleNode } from "../../common/util/RecyclePool";
|
||||
import Transition from "../data/Transition";
|
||||
import UnitBase from "../fsm/UnitBase";
|
||||
import UnitState from "../fsm/UnitState";
|
||||
|
||||
const { ccclass, property } = cc._decorator;
|
||||
|
||||
@ccclass
|
||||
export default class TransitionItem extends cc.Component implements RecycleNode {
|
||||
@property(cc.Node) Bg: cc.Node = null;
|
||||
@property(cc.Node) Arrow: cc.Node = null;
|
||||
@property(cc.Label) Label: cc.Label = null;
|
||||
|
||||
/** 对应的transition */
|
||||
public transition: Transition = null;
|
||||
|
||||
public reuse() {
|
||||
}
|
||||
|
||||
public unuse() {
|
||||
Events.targetOff(this);
|
||||
}
|
||||
|
||||
public onInit(transition: Transition, curUnit: UnitBase) {
|
||||
this.Bg.opacity = 0;
|
||||
this.Arrow.active = !!curUnit;
|
||||
this.transition = transition;
|
||||
this.Label.string = this.transition.getTransStr();
|
||||
Events.targetOn(this);
|
||||
}
|
||||
|
||||
protected onLoad() {
|
||||
this.Bg.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);
|
||||
}
|
||||
|
||||
protected onDestroy() {
|
||||
Events.targetOff(this);
|
||||
}
|
||||
|
||||
private onTouchStart() {
|
||||
Events.emit(EventName.TRANSITION_SELECT, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 选中
|
||||
*/
|
||||
public select(value: boolean) {
|
||||
this.Bg.opacity = value ? 255 : 0;
|
||||
}
|
||||
|
||||
public delete() {
|
||||
this.transition.fromState.deleteTransition(this.transition);
|
||||
Events.emit(EventName.TRANSITION_DELETE, this.transition);
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.STATE_NAME_CHANGED)
|
||||
private onEventStateChanged(unit: UnitState) {
|
||||
this.Label.string = this.transition.getTransStr();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "7372e675-95d1-4bc6-96a5-265acafe4c58",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
7
animator-editor/assets/script/editor/menu.meta
Normal file
7
animator-editor/assets/script/editor/menu.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"ver": "1.0.1",
|
||||
"uuid": "e80b312d-0547-4343-8ce0-a5117ab3502c",
|
||||
"isSubpackage": false,
|
||||
"subpackageName": "",
|
||||
"subMetas": {}
|
||||
}
|
||||
31
animator-editor/assets/script/editor/menu/LineToSubItem.ts
Normal file
31
animator-editor/assets/script/editor/menu/LineToSubItem.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import Events, { EventName } from "../../common/util/Events";
|
||||
import State from "../data/State";
|
||||
import UnitStateMachine from "../fsm/UnitStateMachine";
|
||||
|
||||
const { ccclass, property } = cc._decorator;
|
||||
|
||||
@ccclass
|
||||
export default class LineToSubItem extends cc.Component {
|
||||
@property(cc.Label) NameLabel: cc.Label = null;
|
||||
|
||||
private _state: State = null;
|
||||
/**
|
||||
* 连线目标状态机
|
||||
*/
|
||||
private _toStateMachine: UnitStateMachine = null;
|
||||
|
||||
public onInit(state: State = null, to: UnitStateMachine = null) {
|
||||
if (state) {
|
||||
this._state = state;
|
||||
this._toStateMachine = to;
|
||||
this.NameLabel.string = state.name;
|
||||
} else {
|
||||
this.NameLabel.string = '<- empty ->';
|
||||
}
|
||||
}
|
||||
|
||||
private onClickSelect() {
|
||||
Events.emit(EventName.CLOSE_MENU);
|
||||
this._state && Events.emit(EventName.LINE_TO_MACHINE_STATE, this._state, this._toStateMachine);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "6945f8e0-a227-4ab6-abe4-7f4f95d398d5",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
215
animator-editor/assets/script/editor/menu/Menu.ts
Normal file
215
animator-editor/assets/script/editor/menu/Menu.ts
Normal file
@@ -0,0 +1,215 @@
|
||||
import Events, { EventName, preloadEvent } from "../../common/util/Events";
|
||||
import RecyclePool from "../../common/util/RecyclePool";
|
||||
import Res from "../../common/util/Res";
|
||||
import { ParamType } from "../../constant/BaseConst";
|
||||
import { ResUrl } from "../../constant/ResUrl";
|
||||
import State from "../data/State";
|
||||
import StateMachine from "../data/StateMachine";
|
||||
import Editor from "../Editor";
|
||||
import UnitBase from "../fsm/UnitBase";
|
||||
import UnitState from "../fsm/UnitState";
|
||||
import UnitStateMachine from "../fsm/UnitStateMachine";
|
||||
import ConditionItem from "../inspector/ConditionItem";
|
||||
import ParamItem from "../parameters/ParamItem";
|
||||
import LineToSubItem from "./LineToSubItem";
|
||||
import MultiplierItem from "./MultiplierItem";
|
||||
import ParamSelectItem from "./ParamSelectItem";
|
||||
|
||||
const { ccclass, property } = cc._decorator;
|
||||
|
||||
@ccclass
|
||||
export default class Menu extends cc.Component {
|
||||
@property(cc.Node) RightMenu: cc.Node = null;
|
||||
@property(cc.ScrollView) LineToSubList: cc.ScrollView = null;
|
||||
@property(cc.ScrollView) MultiplierList: cc.ScrollView = null;
|
||||
@property(cc.Node) ParamAdd: cc.Node = null;
|
||||
@property(cc.ScrollView) ParamSelect: cc.ScrollView = null;
|
||||
@property(cc.Node) ConditionLogic: cc.Node = null;
|
||||
|
||||
/**
|
||||
* 选中弹出ConditionLogic菜单的conditionItem
|
||||
*/
|
||||
private _conditionItem: ConditionItem = null;
|
||||
|
||||
protected onLoad() {
|
||||
this.node.active = false;
|
||||
this.node.on(cc.Node.EventType.TOUCH_START, this.close, this);
|
||||
Events.targetOn(this);
|
||||
}
|
||||
|
||||
protected onDestroy() {
|
||||
Events.targetOff(this);
|
||||
}
|
||||
|
||||
private show(node: cc.Node) {
|
||||
this.node.active = true;
|
||||
this.RightMenu.active = this.RightMenu === node;
|
||||
this.LineToSubList.node.active = this.LineToSubList.node === node;
|
||||
this.MultiplierList.node.active = this.MultiplierList.node === node;
|
||||
this.ParamAdd.active = this.ParamAdd === node;
|
||||
this.ParamSelect.node.active = this.ParamSelect.node === node;
|
||||
this.ConditionLogic.active = this.ConditionLogic === node;
|
||||
}
|
||||
|
||||
private close() {
|
||||
this.node.active = false;
|
||||
}
|
||||
|
||||
private onClickConditionLogic(event: cc.Event, str: string) {
|
||||
this.close();
|
||||
this._conditionItem && this._conditionItem.changeLogic(parseInt(str));
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.CLOSE_MENU)
|
||||
private onEventCloseMenu() {
|
||||
this.close();
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.SHOW_RIGHT_MENU)
|
||||
private onEventShowRightMenu(worldPos: cc.Vec2, curUnit: UnitBase = null) {
|
||||
this.show(this.RightMenu);
|
||||
|
||||
if (curUnit) {
|
||||
this.RightMenu.getChildByName('create').active = false;
|
||||
this.RightMenu.getChildByName('state').active = true;
|
||||
if (curUnit instanceof UnitState) {
|
||||
this.RightMenu.getChildByName('state').children[0].active = true;
|
||||
if (curUnit.isAnyState) {
|
||||
this.RightMenu.getChildByName('state').children[1].active = false;
|
||||
this.RightMenu.getChildByName('state').children[2].active = false;
|
||||
} else {
|
||||
this.RightMenu.getChildByName('state').children[1].active = true;
|
||||
this.RightMenu.getChildByName('state').children[2].active = true;
|
||||
}
|
||||
} else if (curUnit instanceof UnitStateMachine) {
|
||||
if (curUnit.isUp) {
|
||||
this.close();
|
||||
return;
|
||||
}
|
||||
this.RightMenu.getChildByName('state').children[0].active = false;
|
||||
this.RightMenu.getChildByName('state').children[1].active = false;
|
||||
this.RightMenu.getChildByName('state').children[2].active = true;
|
||||
}
|
||||
this.RightMenu.height = this.RightMenu.getChildByName('state').height;
|
||||
} else {
|
||||
this.RightMenu.getChildByName('create').active = true;
|
||||
this.RightMenu.getChildByName('state').active = false;
|
||||
this.RightMenu.height = this.RightMenu.getChildByName('create').height;
|
||||
}
|
||||
|
||||
// 设置右键菜单坐标
|
||||
let pos = this.node.convertToNodeSpaceAR(worldPos);
|
||||
let x = pos.x;
|
||||
let y = pos.y - this.RightMenu.height < -this.node.height / 2 ? pos.y + this.RightMenu.height : pos.y;
|
||||
this.RightMenu.position = cc.v2(x, y);
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.SHOW_LINE_TO_List)
|
||||
private onEventShowLineToList(worldPos: cc.Vec2, toUnit: UnitStateMachine, cur: StateMachine) {
|
||||
this.show(this.LineToSubList.node);
|
||||
|
||||
// 生成item
|
||||
for (let i = this.LineToSubList.content.childrenCount - 1; i >= 0; i--) {
|
||||
RecyclePool.put(LineToSubItem, this.LineToSubList.content.children[i]);
|
||||
}
|
||||
let states: Set<State> = null;
|
||||
if (toUnit.isUp) {
|
||||
states = cur.getAllOutStates();
|
||||
} else {
|
||||
states = toUnit.stateMachine.getAllSubStates();
|
||||
}
|
||||
if (states.size === 0) {
|
||||
let node: cc.Node = RecyclePool.get(LineToSubItem) || cc.instantiate(Res.getLoaded(ResUrl.PREFAB.LINE_TO_SUB_ITEM));
|
||||
this.LineToSubList.content.addChild(node);
|
||||
node.getComponent(LineToSubItem).onInit();
|
||||
} else {
|
||||
states.forEach((e) => {
|
||||
let node: cc.Node = RecyclePool.get(LineToSubItem) || cc.instantiate(Res.getLoaded(ResUrl.PREFAB.LINE_TO_SUB_ITEM));
|
||||
this.LineToSubList.content.addChild(node);
|
||||
node.getComponent(LineToSubItem).onInit(e, toUnit);
|
||||
});
|
||||
}
|
||||
|
||||
// 修改高度
|
||||
let num = cc.misc.clampf(this.LineToSubList.content.childrenCount, 1, 10);
|
||||
this.LineToSubList.node.height = 40 * num + 20;
|
||||
this.LineToSubList.content.parent.height = 40 * num + 20;
|
||||
this.LineToSubList.node.getChildByName('scrollBar').getComponent(cc.Widget).updateAlignment();
|
||||
// 设置坐标
|
||||
let pos = this.node.convertToNodeSpaceAR(worldPos);
|
||||
let x = pos.x;
|
||||
let y = pos.y - this.LineToSubList.node.height < -this.node.height / 2 ? pos.y + this.LineToSubList.node.height : pos.y;
|
||||
this.LineToSubList.node.position = cc.v2(x, y);
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.SHOW_MULTIPLIER)
|
||||
private onEventShowMultiplierSelect(worldPos: cc.Vec2) {
|
||||
this.show(this.MultiplierList.node);
|
||||
|
||||
this.MultiplierList.node.position = this.node.convertToNodeSpaceAR(worldPos);
|
||||
for (let i = this.MultiplierList.content.childrenCount - 1; i >= 0; i--) {
|
||||
RecyclePool.put(MultiplierItem, this.MultiplierList.content.children[i]);
|
||||
}
|
||||
let node: cc.Node = RecyclePool.get(MultiplierItem) || cc.instantiate(Res.getLoaded(ResUrl.PREFAB.MULTIPLIER_ITEM));
|
||||
this.MultiplierList.content.addChild(node);
|
||||
node.getComponent(MultiplierItem).onInit();
|
||||
Editor.Inst.ParamCtr.ParamContent.children.forEach((e) => {
|
||||
let paramItem = e.getComponent(ParamItem);
|
||||
if (paramItem.type !== ParamType.NUMBER) {
|
||||
return;
|
||||
}
|
||||
let node: cc.Node = RecyclePool.get(MultiplierItem) || cc.instantiate(Res.getLoaded(ResUrl.PREFAB.MULTIPLIER_ITEM));
|
||||
this.MultiplierList.content.addChild(node);
|
||||
node.getComponent(MultiplierItem).onInit(paramItem);
|
||||
});
|
||||
let num = cc.misc.clampf(this.MultiplierList.content.childrenCount, 1, 10);
|
||||
this.MultiplierList.node.height = 40 * num + 20;
|
||||
this.MultiplierList.content.parent.height = 40 * num + 20;
|
||||
this.MultiplierList.node.getChildByName('scrollBar').getComponent(cc.Widget).updateAlignment();
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.SHOW_PARAM_ADD)
|
||||
private onEventShowParamAdd(worldPos: cc.Vec2) {
|
||||
this.show(this.ParamAdd);
|
||||
|
||||
this.ParamAdd.position = this.node.convertToNodeSpaceAR(worldPos);
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.SHOW_PARAM_SELECT)
|
||||
private onEventShowParamSelect(worldPos: cc.Vec2, targetHeight: number, conditionItem: ConditionItem) {
|
||||
this.show(this.ParamSelect.node);
|
||||
|
||||
for (let i = this.ParamSelect.content.childrenCount - 1; i >= 0; i--) {
|
||||
RecyclePool.put(ParamSelectItem, this.ParamSelect.content.children[i]);
|
||||
}
|
||||
// 生成item
|
||||
Editor.Inst.ParamCtr.ParamContent.children.forEach((e) => {
|
||||
let node: cc.Node = RecyclePool.get(ParamSelectItem) || cc.instantiate(Res.getLoaded(ResUrl.PREFAB.PARAM_SELECT_ITEM));
|
||||
this.ParamSelect.content.addChild(node);
|
||||
node.getComponent(ParamSelectItem).onInit(e.getComponent(ParamItem), conditionItem);
|
||||
});
|
||||
|
||||
let num = cc.misc.clampf(this.ParamSelect.content.childrenCount, 1, 10);
|
||||
this.ParamSelect.node.height = 40 * num + 10;
|
||||
this.ParamSelect.content.parent.height = 40 * num + 10;
|
||||
this.ParamSelect.node.getChildByName('scrollBar').getComponent(cc.Widget).updateAlignment();
|
||||
if (worldPos.y - targetHeight / 2 - this.ParamSelect.node.height > 0) {
|
||||
this.ParamSelect.node.position = this.node.convertToNodeSpaceAR(cc.v2(worldPos.x, worldPos.y - targetHeight / 2));
|
||||
} else {
|
||||
this.ParamSelect.node.position = this.node.convertToNodeSpaceAR(cc.v2(worldPos.x, worldPos.y + targetHeight / 2 + this.ParamSelect.node.height));
|
||||
}
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.SHOW_LOGIC)
|
||||
private onEventShowLogic(worldPos: cc.Vec2, targetHeight: number, conditionItem: ConditionItem) {
|
||||
this.show(this.ConditionLogic);
|
||||
|
||||
if (worldPos.y - targetHeight / 2 - this.ConditionLogic.height > 0) {
|
||||
this.ConditionLogic.position = this.node.convertToNodeSpaceAR(cc.v2(worldPos.x, worldPos.y - targetHeight / 2));
|
||||
} else {
|
||||
this.ConditionLogic.position = this.node.convertToNodeSpaceAR(cc.v2(worldPos.x, worldPos.y + targetHeight / 2 + this.ConditionLogic.height));
|
||||
}
|
||||
|
||||
this._conditionItem = conditionItem;
|
||||
}
|
||||
}
|
||||
9
animator-editor/assets/script/editor/menu/Menu.ts.meta
Normal file
9
animator-editor/assets/script/editor/menu/Menu.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "c2fcbafb-c8f2-4444-8f22-33745b12a4ea",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
25
animator-editor/assets/script/editor/menu/MultiplierItem.ts
Normal file
25
animator-editor/assets/script/editor/menu/MultiplierItem.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import Events, { EventName } from "../../common/util/Events";
|
||||
import ParamItem from "../parameters/ParamItem";
|
||||
|
||||
const { ccclass, property } = cc._decorator;
|
||||
|
||||
@ccclass
|
||||
export default class MultiplierItem extends cc.Component {
|
||||
@property(cc.Label) NameLabel: cc.Label = null;
|
||||
|
||||
private _paramItem: ParamItem = null;
|
||||
|
||||
public onInit(paramItem: ParamItem = null) {
|
||||
this._paramItem = paramItem;
|
||||
if (this._paramItem) {
|
||||
this.NameLabel.string = this._paramItem.paramName;
|
||||
} else {
|
||||
this.NameLabel.string = '<- empty ->';
|
||||
}
|
||||
}
|
||||
|
||||
private onClick() {
|
||||
Events.emit(EventName.MULTIPLIER_SELECT, this._paramItem);
|
||||
Events.emit(EventName.CLOSE_MENU);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "dbdb2d2e-a525-48d5-9bf8-233a03791537",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
25
animator-editor/assets/script/editor/menu/ParamSelectItem.ts
Normal file
25
animator-editor/assets/script/editor/menu/ParamSelectItem.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import Events, { EventName } from "../../common/util/Events";
|
||||
import ConditionItem from "../inspector/ConditionItem";
|
||||
import ParamItem from "../parameters/ParamItem";
|
||||
|
||||
const { ccclass, property } = cc._decorator;
|
||||
|
||||
@ccclass
|
||||
export default class ParamSelectItem extends cc.Component {
|
||||
@property(cc.Label) ParamName: cc.Label = null;
|
||||
|
||||
private _paramItem: ParamItem = null;
|
||||
private _conditionItem: ConditionItem = null;
|
||||
|
||||
public onInit(paramItem: ParamItem, conditionItem: ConditionItem) {
|
||||
this._paramItem = paramItem;
|
||||
this._conditionItem = conditionItem;
|
||||
|
||||
this.ParamName.string = this._paramItem.paramName;
|
||||
}
|
||||
|
||||
private onClick() {
|
||||
this._conditionItem.onReset(this._paramItem);
|
||||
Events.emit(EventName.CLOSE_MENU);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "93f5ccc1-bad2-49cf-ad8d-597bea2169e3",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
7
animator-editor/assets/script/editor/parameters.meta
Normal file
7
animator-editor/assets/script/editor/parameters.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"ver": "1.0.1",
|
||||
"uuid": "4fc6b9b3-62f2-46f5-a4d9-46ec5e3393f6",
|
||||
"isSubpackage": false,
|
||||
"subpackageName": "",
|
||||
"subMetas": {}
|
||||
}
|
||||
153
animator-editor/assets/script/editor/parameters/ParamCtr.ts
Normal file
153
animator-editor/assets/script/editor/parameters/ParamCtr.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import Events, { EventName, preloadEvent } from "../../common/util/Events";
|
||||
import RecyclePool from "../../common/util/RecyclePool";
|
||||
import Res from "../../common/util/Res";
|
||||
import { ParameterData } from "../../constant/BaseConst";
|
||||
import { ResUrl } from "../../constant/ResUrl";
|
||||
import ParamItem from "./ParamItem";
|
||||
|
||||
const { ccclass, property } = cc._decorator;
|
||||
|
||||
@ccclass
|
||||
export default class ParamCtr extends cc.Component {
|
||||
@property(cc.Node) ParamContent: cc.Node = null;
|
||||
@property(cc.Node) SelectPosNode: cc.Node = null;
|
||||
|
||||
protected onLoad() {
|
||||
Events.targetOn(this);
|
||||
}
|
||||
|
||||
protected onEnable() {
|
||||
// 屏蔽scrollView内部touch事件,防止与拖拽行为产生冲突
|
||||
let scrollView = this.getComponent(cc.ScrollView);
|
||||
scrollView.node.off(cc.Node.EventType.TOUCH_START, scrollView['_onTouchBegan'], scrollView, true);
|
||||
scrollView.node.off(cc.Node.EventType.TOUCH_MOVE, scrollView['_onTouchMoved'], scrollView, true);
|
||||
scrollView.node.off(cc.Node.EventType.TOUCH_END, scrollView['_onTouchEnded'], scrollView, true);
|
||||
scrollView.node.off(cc.Node.EventType.TOUCH_CANCEL, scrollView['_onTouchCancelled'], scrollView, true);
|
||||
}
|
||||
|
||||
protected onDestroy() {
|
||||
Events.targetOff(this);
|
||||
}
|
||||
|
||||
//#region import and export
|
||||
/**
|
||||
* 导入数据
|
||||
*/
|
||||
public import(arr: ParameterData[]) {
|
||||
arr.forEach((data: ParameterData) => {
|
||||
let node = this.getParamItem();
|
||||
this.ParamContent.addChild(node);
|
||||
let pi = node.getComponent(ParamItem);
|
||||
pi.onInit(data.param, data.type, data.init);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出数据
|
||||
*/
|
||||
public export(): ParameterData[] {
|
||||
let arr: ParameterData[] = [];
|
||||
this.ParamContent.children.forEach((e) => {
|
||||
let pi = e.getComponent(ParamItem);
|
||||
let data: ParameterData = {
|
||||
param: pi.paramName,
|
||||
type: pi.type,
|
||||
init: pi.init
|
||||
};
|
||||
arr.push(data);
|
||||
});
|
||||
return arr;
|
||||
}
|
||||
//#endregion
|
||||
|
||||
public getParamMap() {
|
||||
let map: Map<string, ParamItem> = new Map();
|
||||
this.ParamContent.children.forEach((e) => {
|
||||
let pi = e.getComponent(ParamItem);
|
||||
map.set(pi.paramName, pi);
|
||||
});
|
||||
return map;
|
||||
}
|
||||
|
||||
private getParamItem() {
|
||||
let prefab = Res.getLoaded(ResUrl.PREFAB.PARAM_ITEM);
|
||||
let node: cc.Node = RecyclePool.get(ParamItem) || cc.instantiate(prefab);
|
||||
node.width = this.ParamContent.width;
|
||||
return node;
|
||||
}
|
||||
|
||||
private putParamItem(node: cc.Node) {
|
||||
RecyclePool.put(ParamItem, node);
|
||||
}
|
||||
|
||||
public getParamItemByName(name: string) {
|
||||
for (let i = 0; i < this.ParamContent.childrenCount; i++) {
|
||||
let pi = this.ParamContent.children[i].getComponent(ParamItem);
|
||||
if (name === pi.paramName) {
|
||||
return pi;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个不重复的参数名
|
||||
*/
|
||||
public getParamName(paramItem: ParamItem, name: string = 'param'): string {
|
||||
let index = 0;
|
||||
let findName = false;
|
||||
|
||||
while (!findName) {
|
||||
findName = true;
|
||||
for (let i = 0; i < this.ParamContent.childrenCount; i++) {
|
||||
let pi = this.ParamContent.children[i].getComponent(ParamItem);
|
||||
if (pi === paramItem) {
|
||||
continue;
|
||||
}
|
||||
if (pi.paramName === `${name}${index > 0 ? index : ''}`) {
|
||||
index++;
|
||||
findName = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return `${name}${index > 0 ? index : ''}`;
|
||||
}
|
||||
|
||||
private onClickAddParam(event: cc.Event, typeStr: string) {
|
||||
Events.emit(EventName.CLOSE_MENU);
|
||||
|
||||
let type = parseInt(typeStr);
|
||||
let node = this.getParamItem();
|
||||
this.ParamContent.addChild(node);
|
||||
let pi = node.getComponent(ParamItem);
|
||||
pi.onInit(this.getParamName(pi), type);
|
||||
}
|
||||
|
||||
private onClickOpenSelect() {
|
||||
Events.emit(EventName.SHOW_PARAM_ADD, this.SelectPosNode.parent.convertToWorldSpaceAR(this.SelectPosNode.position));
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.PARAM_DELETE)
|
||||
private onEventParamDelete(item: ParamItem) {
|
||||
this.putParamItem(item.node);
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.PARAM_SELECT)
|
||||
private onEventParamSelect(item: ParamItem) {
|
||||
this.ParamContent.children.forEach((e) => {
|
||||
let ti = e.getComponent(ParamItem);
|
||||
ti.select(ti === item);
|
||||
});
|
||||
}
|
||||
|
||||
@preloadEvent(EventName.RESIZE)
|
||||
private onEventResize(node: cc.Node) {
|
||||
if (node !== this.node) {
|
||||
return;
|
||||
}
|
||||
this.ParamContent.children.forEach((e) => {
|
||||
e.width = this.ParamContent.width;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "86c89dff-cf3e-406e-a9d5-3da7cd5e86cf",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
128
animator-editor/assets/script/editor/parameters/ParamItem.ts
Normal file
128
animator-editor/assets/script/editor/parameters/ParamItem.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import Events, { EventName } from "../../common/util/Events";
|
||||
import { RecycleNode } from "../../common/util/RecyclePool";
|
||||
import { ParamType } from "../../constant/BaseConst";
|
||||
import Editor from "../Editor";
|
||||
|
||||
const { ccclass, property } = cc._decorator;
|
||||
|
||||
@ccclass
|
||||
export default class ParamItem extends cc.Component implements RecycleNode {
|
||||
@property(cc.Node) Bg: cc.Node = null;
|
||||
@property(cc.EditBox) NameEdit: cc.EditBox = null;
|
||||
@property(cc.Node) InitValue: cc.Node = null;
|
||||
|
||||
/** 组件是否初始化完成 */
|
||||
private _hasInit: boolean = false;
|
||||
|
||||
private _paramName: string = '';
|
||||
/** 参数名 */
|
||||
public get paramName() {
|
||||
return this._paramName;
|
||||
}
|
||||
public set paramName(v: string) {
|
||||
if (this._paramName === v) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._paramName = v;
|
||||
this.NameEdit.string = v;
|
||||
Events.emit(EventName.PARAM_NAME_CHANGED, this);
|
||||
}
|
||||
|
||||
/** 参数类型 */
|
||||
public type: ParamType = null;
|
||||
/** 初始值 */
|
||||
public init: number = 0;
|
||||
|
||||
public reuse() {
|
||||
}
|
||||
|
||||
public unuse() {
|
||||
this._hasInit = false;
|
||||
}
|
||||
|
||||
public onInit(param: string, type: ParamType, init: number = 0) {
|
||||
this.Bg.opacity = 0;
|
||||
this.paramName = param;
|
||||
this.type = type;
|
||||
this.init = init;
|
||||
this.showInitValue();
|
||||
|
||||
this._hasInit = true;
|
||||
}
|
||||
|
||||
protected onLoad() {
|
||||
this.Bg.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);
|
||||
}
|
||||
|
||||
private showInitValue() {
|
||||
this.InitValue.children.forEach((e, index) => {
|
||||
e.active = (this.type === (index + 1));
|
||||
if (e.active) {
|
||||
if (this.type === ParamType.NUMBER) {
|
||||
e.getComponent(cc.EditBox).string = `${this.init}`;
|
||||
} else {
|
||||
e.getComponent(cc.Toggle).isChecked = this.init === 1 ? true : false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private onTouchStart() {
|
||||
this._hasInit && Events.emit(EventName.PARAM_SELECT, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 选中
|
||||
*/
|
||||
public select(value: boolean) {
|
||||
this.Bg.opacity = value ? 255 : 0;
|
||||
}
|
||||
|
||||
private onNameEditBegan() {
|
||||
this._hasInit && Events.emit(EventName.PARAM_SELECT, this);
|
||||
}
|
||||
|
||||
private onNameChanged() {
|
||||
if (this.NameEdit.string === '') {
|
||||
this.NameEdit.string = this.paramName;
|
||||
return;
|
||||
}
|
||||
|
||||
this.NameEdit.string = Editor.Inst.ParamCtr.getParamName(this, this.NameEdit.string);
|
||||
this.paramName = this.NameEdit.string;
|
||||
}
|
||||
|
||||
private onNumberEditBegan() {
|
||||
this._hasInit && Events.emit(EventName.PARAM_SELECT, this);
|
||||
}
|
||||
|
||||
private onNumberChanged(str: string, edit: cc.EditBox) {
|
||||
str = str.replace(/[^-.\d]/g, '');
|
||||
if (str === '') {
|
||||
return;
|
||||
}
|
||||
let num = parseFloat(str);
|
||||
this.init = isNaN(num) ? 0 : num;
|
||||
edit.string = `${this.init}`;
|
||||
}
|
||||
|
||||
private onBooleanCheckd(toggle: cc.Toggle) {
|
||||
this.init = toggle.isChecked ? 1 : 0;
|
||||
this._hasInit && Events.emit(EventName.PARAM_SELECT, this);
|
||||
}
|
||||
|
||||
private onTriggerCheckd(toggle: cc.Toggle) {
|
||||
this.init = toggle.isChecked ? 1 : 0;
|
||||
this._hasInit && Events.emit(EventName.PARAM_SELECT, this);
|
||||
}
|
||||
|
||||
private onAutoTriggerCheckd(toggle: cc.Toggle) {
|
||||
this.init = toggle.isChecked ? 1 : 0;
|
||||
this._hasInit && Events.emit(EventName.PARAM_SELECT, this);
|
||||
}
|
||||
|
||||
private onClickDelete() {
|
||||
Events.emit(EventName.PARAM_DELETE, this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.0.5",
|
||||
"uuid": "37ea8c36-974d-41ce-bcae-451ac3ca5776",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
Reference in New Issue
Block a user