This commit is contained in:
YipLee
2021-01-19 22:30:12 +08:00
commit c4f716c8e9
165 changed files with 61216 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
{
"ver": "1.0.1",
"uuid": "f1f329e2-8089-463b-909b-727a96fcc954",
"isSubpackage": false,
"subpackageName": "",
"subMetas": {}
}

View File

@@ -0,0 +1,7 @@
{
"ver": "1.0.1",
"uuid": "4d50adb5-7ad5-496a-bfdc-4d59f1630bab",
"isSubpackage": false,
"subpackageName": "",
"subMetas": {}
}

View File

@@ -0,0 +1,24 @@
const { ccclass, property } = cc._decorator;
@ccclass
export default class CanvasAdapt extends cc.Component {
protected onLoad() {
this.adapt();
// 仅web有效
cc.view.setResizeCallback(() => {
this.adapt();
});
}
private adapt() {
let resolutionRatio = cc.Canvas.instance.designResolution.width / cc.Canvas.instance.designResolution.height;
let ratio = cc.winSize.width / cc.winSize.height;
if (ratio > resolutionRatio) {
cc.Canvas.instance.fitHeight = true;
cc.Canvas.instance.fitWidth = false;
} else {
cc.Canvas.instance.fitHeight = false;
cc.Canvas.instance.fitWidth = true;
}
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "75034da7-93d7-420e-b1e7-aabae21a607e",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,36 @@
const { ccclass, property } = cc._decorator;
/** 移动速度 px/s */
const MOVE_SPEED = 200;
/**
* 用于拖拽排序的元素
*/
@ccclass
export default class DragItem extends cc.Component {
/** 触摸开始时的boundingbox */
private _startRect: cc.Rect = null;
public get startRect() { return this._startRect; }
/** 移动动画的目标下标 */
private _toIdx: number = 0;
public onInit(idx: number) {
this._toIdx = idx;
this._startRect = this.node.getBoundingBox();
}
public moveTo(toIdx: number, toY: number) {
if (toIdx === this._toIdx) {
return;
}
this._toIdx = toIdx;
this.node.stopAllActions();
let moveTo = cc.moveTo(Math.abs(this.node.y - toY) / MOVE_SPEED, cc.v2(0, toY));
this.node.runAction(moveTo);
}
public stop() {
this.node.stopAllActions();
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "472003c1-2fb4-444f-a4cb-af3c1a70e9c1",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,184 @@
import Tool from "../util/Tool";
import DragItem from "./DragItem";
const { ccclass, property } = cc._decorator;
/**
* 拖拽排序列表
*/
@ccclass
export default class DragList extends cc.Component {
/** 进行拖拽操作的元素下标 */
private _dragIdx: number = -1;
/** 所有元素 */
private _items: DragItem[] = [];
/**
* 拖拽回调
* @param dragIdx 拖拽元素初始下标
* @param toIdx 拖拽元素完成拖拽后所在的下标
*/
private _dragCall: (dragIdx: number, toIdx: number) => void = null;
/** 调用拖拽回调传入的this对象 */
private _target: any = null;
private _layout: cc.Layout = null;
/** 元素容器 */
public get layout() {
if (!this._layout) {
this._layout = this.getComponent(cc.Layout);
}
return this._layout;
}
/** 拖拽开关 */
public canDrag: boolean = true;
protected onLoad() {
this.node.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);
this.node.on(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
this.node.on(cc.Node.EventType.TOUCH_END, this.onTouchEnd, this);
this.node.on(cc.Node.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
}
private onTouchStart(event: cc.Event.EventTouch) {
if (!this.canDrag || this.node.childrenCount <= 1) {
this._dragIdx = -1;
return;
}
let pos = this.node.convertToNodeSpaceAR(event.getLocation());
this._dragIdx = this.getItemIdx(pos);
if (this._dragIdx < 0) {
return;
}
this.layout.enabled = false;
this._items = [];
this.node.children.forEach((e: cc.Node, idx: number) => {
let item = e.getComponent(DragItem);
if (!item) {
item = e.addComponent(DragItem);
}
item.onInit(idx);
this._items.push(item);
});
this._items[this._dragIdx].node.setSiblingIndex(this.node.childrenCount - 1);
}
private onTouchMove(event: cc.Event.EventTouch) {
if (this._dragIdx < 0) {
return;
}
let pos = this.node.convertToNodeSpaceAR(event.getLocation());
// 进行拖拽操作
let yMax = this._items[0].startRect.center.y;
let yMin = this._items[this._items.length - 1].startRect.center.y;
this._items[this._dragIdx].node.y = cc.misc.clampf(pos.y, yMin, yMax);
let curIdx = this.getCurIdx(pos);
if (curIdx < this._dragIdx) {
this._items.forEach((item: DragItem, idx: number) => {
if (idx === this._dragIdx) {
return;
}
if (Tool.inRange(curIdx, this._dragIdx - 1, idx)) {
item.moveTo(idx + 1, this._items[idx + 1].startRect.center.y);
} else {
item.moveTo(idx, this._items[idx].startRect.center.y);
}
});
} else {
this._items.forEach((item: DragItem, idx: number) => {
if (idx === this._dragIdx) {
return;
}
if (Tool.inRange(this._dragIdx + 1, curIdx, idx)) {
item.moveTo(idx - 1, this._items[idx - 1].startRect.center.y);
} else {
item.moveTo(idx, this._items[idx].startRect.center.y);
}
});
}
}
private onTouchEnd(event: cc.Event.EventTouch) {
if (this._dragIdx < 0) {
return;
}
let pos = this.node.convertToNodeSpaceAR(event.getLocation());
// 结束拖拽操作
let curIdx = this.getCurIdx(pos);
this._items[this._dragIdx].node.setSiblingIndex(curIdx);
this._items.forEach((item: DragItem) => {
item.stop();
});
// 触发回调
if (curIdx !== this._dragIdx && this._dragCall) {
if (this._target)
this._dragCall.call(this._target, this._dragIdx, curIdx);
else
this._dragCall(this._dragIdx, curIdx);
}
// 重置
this.layout.enabled = true;
this.layout.updateLayout();
this._dragIdx = -1;
this._items = [];
}
/**
* 获取选中的元素下标
*/
private getItemIdx(pos: cc.Vec2) {
for (let i = 0; i < this.node.childrenCount; i++) {
let item = this.node.children[i];
if (item.getBoundingBox().contains(pos)) {
return i;
}
}
return -1;
}
/**
* 根据坐标获取当前移动到哪个下标的位置
*/
private getCurIdx(pos: cc.Vec2) {
let yMax = this._items[0].startRect.center.y;
let yMin = this._items[this._items.length - 1].startRect.center.y;
if (pos.y >= yMax) {
return 0;
} else if (pos.y <= yMin) {
return this._items.length - 1;
}
let idx: number = 0;
let minDis: number = Math.abs(this._items[0].startRect.center.y - pos.y);
for (let i = 1; i < this._items.length; i++) {
let item = this._items[i];
let dis = Math.abs(item.startRect.center.y - pos.y);
if (dis < minDis) {
idx = i;
minDis = dis;
} else {
break;
}
}
return idx;
}
/**
* 注册拖拽回调
*/
public setDragCall(call: (dragIdx: number, toIdx: number) => void, target: any) {
this._dragCall = call;
this._target = target;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "5b5cd3c9-8509-4ab7-82d7-50cc0d99ffe2",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,72 @@
import Events, { EventName } from "../util/Events";
const { ccclass, property } = cc._decorator;
/**
* 鼠标拉伸调节节点大小组件
*/
@ccclass
export default class ResizeArea extends cc.Component {
@property(cc.Widget) Target: cc.Widget = null;
@property(cc.Vec2) Limit: cc.Vec2 = cc.v2();
@property({ tooltip: CC_DEV && '节点对齐的是否为左侧' }) isLeft: boolean = true;
private _canvas: HTMLElement = null;
private _startPos: cc.Vec2 = null;
private _startWidth: number = 0;
private _updateDirty: boolean = false;
protected onLoad() {
this._canvas = document.getElementById('GameCanvas');
this.node.on(cc.Node.EventType.TOUCH_START, this.onTouchStart, this);
this.node.on(cc.Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
this.node.on(cc.Node.EventType.MOUSE_ENTER, this.onMouseEnter, this);
this.node.on(cc.Node.EventType.MOUSE_LEAVE, this.onMouseLeave, this);
}
protected lateUpdate() {
if (!this._updateDirty) {
return;
}
this._updateDirty = false;
this.Target.left = 0;
this.Target.right = 0;
this.Target.updateAlignment();
Events.emit(EventName.RESIZE, this.Target.node);
this.updateWidget(this.Target.node);
}
private onTouchStart(event: cc.Event.EventTouch) {
this._canvas.style.cursor = 'w-resize';
this._startPos = event.getLocation();
this._startWidth = this.Target.node.width;
}
private onTouchMove(event: cc.Event.EventTouch) {
let delt = event.getLocation().x - this._startPos.x;
if (!this.isLeft) {
delt = -delt;
}
this.Target.node.width = cc.misc.clampf(this._startWidth + delt, this.Limit.x, this.Limit.y);
this._updateDirty = true;
}
private onMouseEnter(event: cc.Event.EventMouse) {
this._canvas.style.cursor = 'w-resize';
}
private onMouseLeave(event: cc.Event.EventMouse) {
this._canvas.style.cursor = 'default ';
}
private updateWidget(node: cc.Node) {
node.children.forEach((c) => {
let widget = c.getComponent(cc.Widget);
widget && widget.updateAlignment();
this.updateWidget(c);
});
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "ea7dfc04-311b-4106-ac76-07793dca50ae",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,7 @@
{
"ver": "1.0.1",
"uuid": "98542223-77d4-420b-9a90-26bc5ab0b531",
"isSubpackage": false,
"subpackageName": "",
"subMetas": {}
}

View File

@@ -0,0 +1,242 @@
/**
* 事件名
*/
export enum EventName {
/** 调节节点大小 */
RESIZE,
/** 删除参数 */
PARAM_DELETE,
/** 参数名更改 */
PARAM_NAME_CHANGED,
/** 选中ParamItem */
PARAM_SELECT,
/** 设置状态机视图的显示 */
SET_CUR_STATE_MACHINE,
/** 状态名更改 */
STATE_NAME_CHANGED,
/** AnyState改变坐标 */
ANY_STATE_MOVE,
/** 状态机名更改 */
STATE_MACHINE_NAME_CHANGED,
/** 父状态机节点改变坐标 */
UP_STATE_MACHINE_MOVE,
/** 选择连线连向子状态机内部的状态 */
LINE_TO_MACHINE_STATE,
/** 删除line */
LINE_DELETE,
/** 新增transition */
TRANSITION_ADD,
/** 删除transition */
TRANSITION_DELETE,
/** 点击按钮选中TransitionItem */
TRANSITION_SELECT,
/** 点击按钮选中ConditionItem */
CONDITION_SELECT,
/** 选中multiplier参数 */
MULTIPLIER_SELECT,
/** 隐藏inspector显示的内容 */
INSPECTOR_HIDE,
/** 显示unit信息 */
INSPECTOR_SHOW_UNIT,
/** 显示line信息 */
INSPECTOR_SHOW_LINE,
/** 关闭菜单层 */
CLOSE_MENU,
/** 显示状态机界面右键菜单 */
SHOW_RIGHT_MENU,
/** 显示连线目标状态机内部所有状态选择界面 */
SHOW_LINE_TO_List,
/** 显示添加参数时选择界面 */
SHOW_PARAM_ADD,
/** 显示condition的参数选择界面 */
SHOW_PARAM_SELECT,
/** 显示condition的logic选项 */
SHOW_LOGIC,
/** 显示multiplier选择界面 */
SHOW_MULTIPLIER,
};
/**
* 非静态成员函数装饰器用于预先载入待注册的事件配合targetOn使用
* @param event 事件名
*/
export function preloadEvent(event: EventName) {
return function (target: any, funcName: string, desc: PropertyDescriptor) {
let arr = Events.classMap.get(target.constructor);
if (arr === undefined) {
arr = [];
Events.classMap.set(target.constructor, arr);
} else {
let find = arr.find((e) => {
return e.event === event && e.funcName === funcName;
});
if (find) {
cc.error(`event: ${EventName[event]} 重复载入`);
return;
}
}
arr.push({
event: event,
funcName: funcName
});
};
}
/**
* 监听函数类型
*/
type Listener = (arg: any) => void;
/**
* 事件收发管理类
*/
export default class Events {
/**
* 存储监听事件、监听函数与监听对象
*/
private static _eventsMap: Map<EventName, Map<Object, Listener[]>> = new Map();
/**
* 存储构造函数、监听事件、监听函数名,用于实例化时注册事件
*/
public static classMap: Map<Function, Array<{ event: EventName, funcName: string }>> = new Map();
/**
* 注册与target构造函数预先绑定的所有事件
* @param target 注册目标
* @param onSuper 是否注册父类成员函数上绑定的事件
*/
public static targetOn(target: Object, onSuper: boolean = true) {
if (onSuper) {
this.classMap.forEach((value: Array<{ event: EventName, funcName: string }>, key: Function) => {
if (target instanceof key) {
value.forEach((e) => {
this.on(e.event, target[e.funcName], target);
});
}
});
} else {
let arr = this.classMap.get(target.constructor);
if (arr) {
arr.forEach((e) => {
this.on(e.event, target[e.funcName], target);
});
}
}
}
/**
* 注册事件
* @param event 事件名
* @param listener 处理事件的监听函数
* @param target 注册目标
*/
public static on(event: EventName, listener: Listener, target: Object) {
if (!listener || !target) {
cc.error(`event: ${EventName[event]} listener或target不能为空`);
return;
}
let map: Map<Object, Listener[]> = this._eventsMap.get(event);
let list: Listener[] = [];
if (map === undefined) {
map = new Map();
map.set(target, list);
this._eventsMap.set(event, map);
} else {
list = map.get(target);
if (list === undefined) {
list = [];
map.set(target, list);
} else {
let result = list.find((e) => { return e === listener });
if (result) {
cc.error(`event: ${EventName[event]} 重复注册`);
return;
}
}
}
list.push(listener);
}
/**
* 移除事件
* @param event 事件名
* @param listener 处理事件的监听函数
* @param target 注册目标
*/
public static off(event: EventName, listener: Listener, target: Object) {
if (!listener || !target) {
cc.error(`event: ${EventName[event]} listener或target不能为空`);
return;
}
let map: Map<Object, Listener[]> = this._eventsMap.get(event);
if (map === undefined) {
cc.error(`event: ${EventName[event]} 未注册该事件`);
return;
}
let list: Listener[] = map.get(target);
if (list === undefined) {
cc.error(`event: ${EventName[event]} target上未注册该事件`);
return;
}
let index = list.findIndex((e) => { return e === listener; });
if (index < 0) {
cc.error(`event: ${EventName[event]} target上未以该listener注册该事件`);
return;
}
list.splice(index, 1);
if (list.length <= 0) {
map.delete(target);
map.size <= 0 && this._eventsMap.delete(event);
}
}
/**
* 移除target上注册的所有事件
* @param target 注册目标
*/
public static targetOff(target: Object) {
if (!target) {
cc.error(`event: ${target} target不能为空`);
return;
}
this._eventsMap.forEach((map, event) => {
map.delete(target);
map.size <= 0 && this._eventsMap.delete(event);
});
}
/**
* 派发事件
* @param event 事件名
* @param args 事件参数
*/
public static emit(event: EventName, ...args: any[]) {
let map: Map<Object, Listener[]> = this._eventsMap.get(event);
if (map === undefined) {
cc.warn(`event: ${EventName[event]} 未注册该事件`);
return;
}
map.forEach((list, target) => {
list.forEach((listener) => {
listener.call(target, ...args);
});
});
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "438a264f-5181-4f14-8330-5b700ab8eac7",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,110 @@
/**
* 用于使用节点池的节点所绑定脚本组件实现
*/
export interface RecycleNode {
/**
* 回收前调用
*/
unuse(): void;
/**
* 取出前调用
*/
reuse(): void;
}
/**
* 节点池
*/
export default class RecyclePool {
private static _poolMap: Map<{ prototype: cc.Component }, cc.Node[]> = new Map();
/**
* 根据类型判断节点池中节点数量
*/
public static size(type: { prototype: cc.Component }): number {
let list = this._poolMap.get(type);
if (list === undefined) {
return 0;
}
return list.length;
}
/**
* 根据类型清空节点
*/
public static clear(type: { prototype: cc.Component }) {
let list = this._poolMap.get(type);
if (list === undefined) {
return;
}
let count = list.length;
for (let i = 0; i < count; ++i) {
list[i].destroy();
}
list.length = 0;
this._poolMap.delete(type);
}
/**
* 清空全部节点
*/
public static clearAll() {
this._poolMap.forEach((list: cc.Node[]) => {
list.forEach((node: cc.Node) => {
node.destroy();
});
});
this._poolMap.clear();
}
/**
* 根据类型从节点池取出节点
*/
public static get(type: { prototype: cc.Component }): cc.Node {
let list = this._poolMap.get(type);
if (list === undefined || list.length <= 0) {
return null;
}
let last = list.length - 1;
let node = list[last];
list.length = last;
// Invoke pool handler
let handler: any = node.getComponent(type);
if (handler && handler.reuse) {
handler.reuse();
}
return node;
}
/**
* 根据类型将节点放入节点池
*/
public static put(type: { prototype: cc.Component }, node: cc.Node) {
if (!node) {
cc.error(`[RecyclePool.put] error: 传入节点为空`);
return;
}
let list = this._poolMap.get(type);
if (list === undefined) {
list = [];
this._poolMap.set(type, list);
} else if (list.indexOf(node) !== -1) {
cc.error(`[RecyclePool.put] error: 不可将节点重复放入节点池中`);
return;
}
node.removeFromParent(false);
// Invoke pool handler
let handler: any = node.getComponent(type);
if (handler && handler.unuse) {
handler.unuse();
}
list.push(node);
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "2650e9f4-6f08-4845-bf8b-31afece7a66b",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,68 @@
/**
* 资源管理类
*/
export default class Res {
/**
* 资源缓存
*/
private static _cacheMap: Map<string, cc.Asset> = new Map();
/**
* 获取已经预加载的资源。!!!调用前需确保资源已预加载
* @param url 资源路径
*/
public static getLoaded(url: string): any {
let asset = this._cacheMap.get(url);
if (asset === undefined) {
cc.error(`[Res.getLoaded] error: 资源未加载`);
return null;
}
return asset;
}
/**
* 加载resources文件夹下单个资源
* @param url 资源路径
* @param type 资源类型
*/
public static async load(url: string, type: typeof cc.Asset): Promise<any> {
let asset = this._cacheMap.get(url);
if (asset) {
return asset;
}
return await new Promise((resolve, reject) => {
cc.loader.loadRes(url, type, (error: Error, resource: cc.Asset) => {
if (error) {
cc.error(`[Res.load] error: ${error}`);
resolve(null);
} else {
this._cacheMap.set(url, resource);
resolve(resource);
}
});
});
}
/**
* 加载resources文件夹下某个文件夹内全部资源
* @param url 资源路径
* @param type 资源类型
*/
public static async loadDir(url: string, type: typeof cc.Asset): Promise<any[]> {
return await new Promise((resolve, reject) => {
cc.loader.loadResDir(url, type, (error: Error, resource: any[], urls: string[]) => {
if (error) {
cc.error(`[Res.loadDir] error: ${error}`);
resolve([]);
} else {
urls.forEach((v: string, i: number) => {
this._cacheMap.set(v, resource[i]);
});
resolve(resource);
}
});
});
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "1d8423c7-71dd-417b-8df3-8b9c83e1496b",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,108 @@
/**
* 工具类
*/
export default class Tool {
/**
* 随机返回数组中的某个元素
* @param arr
*/
public static randArray<T>(arr: Array<T>): T {
if (arr.length <= 0) {
return null;
}
return arr[this.randInt(0, arr.length - 1)];
}
/**
* 获取 [min, max] 区间的随机整数
* @param min
* @param max
*/
public static randInt(min: number, max: number) {
min = Math.ceil(min);
max = Math.floor(max);
if (min >= max) {
return max;
}
return Math.floor(Math.random() * (max - min + 1)) + min;
}
/**
* 获取 [min, max) 区间的随机浮点数
* @param min
* @param max
*/
public static randFloat(min: number, max: number) {
if (min >= max) {
return max;
}
return Math.random() * (max - min) + min;
}
/**
* 返回value是否在 [min, max] 区间内
* @param min
* @param max
* @param value
* @param includeEdge 是否包含边界值min和max默认包含
*/
public static inRange(min: number, max: number, value: number, includeEdge: boolean = true) {
return includeEdge ? value >= min && value <= max : value > min && value < max;
}
/**
* 判断数组中是否有某个元素
*/
public static arrayHas<T>(arr: T[], ele: T): boolean {
let idx = arr.findIndex((e) => { return e === ele; });
return idx >= 0;
}
/**
* 根据下标交换数组两个元素位置
*/
public static arraySwap<T>(arr: T[], idx1: number, idx2: number) {
if (idx1 === idx2 || !this.inRange(0, arr.length - 1, idx1) || !this.inRange(0, arr.length - 1, idx2)) {
return;
}
[arr[idx1], arr[idx2]] = [arr[idx2], arr[idx1]];
}
/**
* 将元素从fromIdx位置移到toIdx位置其余元素相对位置不变
*/
public static arrayMove<T>(arr: T[], fromIdx: number, toIdx: number) {
if (fromIdx === toIdx || !this.inRange(0, arr.length - 1, fromIdx) || !this.inRange(0, arr.length - 1, toIdx)) {
return;
}
let from: T[] = arr.splice(fromIdx, 1);
arr.splice(toIdx, 0, from[0]);
}
/**
* 添加元素
* @param canRepeat 是否可重复添加相同元素 默认false
* @returns 是否确实执行了添加操作
*/
public static arrayAdd<T>(arr: T[], ele: T, canRepeat: boolean = false): boolean {
if (!canRepeat && this.arrayHas(arr, ele)) {
return false;
}
arr.push(ele);
return true;
}
/**
* 删除元素
* @returns 是否确实执行了删除操作
*/
public static arrayDelete<T>(arr: T[], ele: T): boolean {
let idx = arr.findIndex((e) => { return e === ele; });
if (idx === -1) {
return false;
}
arr.splice(idx, 1);
return true
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "8a83b733-dd4e-4b3d-aa1e-121f58ba4f88",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,7 @@
{
"ver": "1.0.1",
"uuid": "e3cf2cb1-5584-4eff-bc09-d578df78b66c",
"isSubpackage": false,
"subpackageName": "",
"subMetas": {}
}

View File

@@ -0,0 +1,86 @@
/** 参数类型 */
export enum ParamType {
COMPLETE = 0,
BOOLEAN = 1,
NUMBER = 2,
TRIGGER = 3,
AUTO_TRIGGER = 4
}
/** 逻辑类型 */
export enum LogicType {
EQUAL = 0,
NOTEQUAL = 1,
GREATER = 2,
LESS = 3,
GREATER_EQUAL = 4,
LESS_EQUAL = 5
}
/** 调用时机 */
export enum CheckType {
/** 每帧调用 */
CHECK_ON_UPDATE = 1,
/** 动画结束 */
CHECK_ON_COMPLETE = 2,
CHECK_ON_TRIGGER = 3
}
/**
* 参数数据
*/
export interface ParameterData {
/** 参数名 */
param: string;
/** 参数类型 */
type: ParamType;
/** 初始值 */
init: number;
}
/**
* 状态数据
*/
export interface StateData {
/** 状态名 */
state: string;
/** 动画名 */
motion: string;
/** 动画播放速度 */
speed: number;
/** number类型的参数名用于speed的乘积 */
multiplier: string;
/** 动画是否循环播放 */
loop: boolean;
/** 连线 */
transitions: TransitionData[];
}
/**
* 连线数据
*/
export interface TransitionData {
/** 目标状态 */
toState: string;
/** 是否等动画播放完跳转 */
hasExitTime: boolean;
/** 条件 */
conditions: ConditionData[];
}
/**
* 条件数据
*/
export interface ConditionData {
/** 此条件对应的参数名 */
param: string;
/** 此条件对应的值 */
value: number;
/** 此条件与值比较的逻辑 */
logic: LogicType;
}
/**
* 编辑器版本号
*/
export const ANIMATOR_VERSION = '1.0.0';

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "19b66911-9936-4b92-a13e-de8e933f7fed",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,26 @@
/**
* 需要动态加载的resources单个资源路径
*/
export const ResUrl = {
PREFAB: {
EDITOR: 'prefab/Editor',
STATE_NODE: 'prefab/StateNode',
STATE_MACHINE_NODE: 'prefab/StateMachineNode',
LINE: 'prefab/Line',
BAR_ITEM: 'prefab/item/BarItem',
TRANSITION_ITEM: 'prefab/item/TransitionItem',
CONDITION_ITEM: 'prefab/item/ConditionItem',
PARAM_ITEM: 'prefab/item/ParamItem',
PARAM_SELECT_ITEM: 'prefab/item/ParamSelectItem',
LINE_TO_SUB_ITEM: 'prefab/item/LineToSubItem',
MULTIPLIER_ITEM: 'prefab/item/MultiplierItem'
}
}
/**
* 需要动态加载的resources文件夹路径
*/
export const ResDirUrl = {
PREFAB: 'prefab/'
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "e31cd169-3e95-4915-a257-e11daf14f81e",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,7 @@
{
"ver": "1.0.1",
"uuid": "0032de99-30c2-4717-8591-e6ef5510b760",
"isSubpackage": false,
"subpackageName": "",
"subMetas": {}
}

View 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);
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "2ddcfacf-d360-4822-844d-cb7bc61f0968",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,7 @@
{
"ver": "1.0.1",
"uuid": "336c3839-4f7a-4def-b2db-0b89b2b72f99",
"isSubpackage": false,
"subpackageName": "",
"subMetas": {}
}

View 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;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "2ad76e07-2aca-40af-8bb1-3a90782bf375",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View 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;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "9cc65592-425e-4040-b1a4-6d7c08e12876",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View 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);
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "81405035-481c-437c-93ec-8a889596391a",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View 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);
}
}
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "c5d54a4e-d4e7-4c79-ab84-4ee81a0633db",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,7 @@
{
"ver": "1.0.1",
"uuid": "cd45d1cc-4d5c-4e5c-9728-42693d0ffadf",
"isSubpackage": false,
"subpackageName": "",
"subMetas": {}
}

View 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;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "685739a3-7df5-4f61-99e1-efbf0175cf35",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View 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
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "323aebfe-14d1-4acd-8e95-b6085c8599dd",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View 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);
}
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "2aff73f9-2e11-422c-bcc4-c338379b1548",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View 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);
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "8366ea51-c189-47b4-ba38-2dd088175777",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View 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();
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "c7581de7-e34c-4d10-81a7-7cb05e82f531",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View 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;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "de0ee56a-435e-4d63-b773-c33f0739a9f5",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View 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;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "930040b8-2275-47ce-b7e0-3161308cfc6f",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View 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}`;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "f37b5690-9da0-4141-ae3f-88548e459831",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,7 @@
{
"ver": "1.0.1",
"uuid": "4d63cdfb-8707-4e19-bea3-9f35e8983f0b",
"isSubpackage": false,
"subpackageName": "",
"subMetas": {}
}

View 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;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "34d79d5d-a60b-4d61-a02c-9f2d4cdfeaea",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View 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
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "af929db1-5975-4775-8a63-387f2b62e542",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -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();
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "7372e675-95d1-4bc6-96a5-265acafe4c58",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,7 @@
{
"ver": "1.0.1",
"uuid": "e80b312d-0547-4343-8ce0-a5117ab3502c",
"isSubpackage": false,
"subpackageName": "",
"subMetas": {}
}

View 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);
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "6945f8e0-a227-4ab6-abe4-7f4f95d398d5",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View 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;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "c2fcbafb-c8f2-4444-8f22-33745b12a4ea",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View 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);
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "dbdb2d2e-a525-48d5-9bf8-233a03791537",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View 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);
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "93f5ccc1-bad2-49cf-ad8d-597bea2169e3",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,7 @@
{
"ver": "1.0.1",
"uuid": "4fc6b9b3-62f2-46f5-a4d9-46ec5e3393f6",
"isSubpackage": false,
"subpackageName": "",
"subMetas": {}
}

View 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;
});
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "86c89dff-cf3e-406e-a9d5-3da7cd5e86cf",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View 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);
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "37ea8c36-974d-41ce-bcae-451ac3ca5776",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@@ -0,0 +1,7 @@
{
"ver": "1.0.1",
"uuid": "a2a8ab4d-4cf4-483f-b78c-0813b0c1f463",
"isSubpackage": false,
"subpackageName": "",
"subMetas": {}
}

View File

@@ -0,0 +1,87 @@
import Res from "../common/util/Res";
import { ResDirUrl, ResUrl } from "../constant/ResUrl";
import Editor from "../editor/Editor";
const { ccclass, property } = cc._decorator;
@ccclass
export default class Main extends cc.Component {
private _editorNode: cc.Node = null;
protected onLoad() {
cc.debug.setDisplayStats(false);
this.onInit();
}
private async onInit() {
await Res.loadDir(ResDirUrl.PREFAB, cc.Prefab);
this.resetEditor();
// 注册拖拽文件事件监听
this.dragOn();
}
private resetEditor(): Editor {
if (this._editorNode) {
this._editorNode.getComponent(Editor).Fsm.MachineLayer.clear();
this._editorNode.removeFromParent();
this._editorNode.destroy();
}
this._editorNode = cc.instantiate(Res.getLoaded(ResUrl.PREFAB.EDITOR));
this.node.addChild(this._editorNode);
let editor = this._editorNode.getComponent(Editor);
Editor.Inst = editor;
return editor;
}
/**
* 注册拖拽文件事件监听
*/
private dragOn() {
if (!cc.sys.isBrowser) {
return;
}
let canvas = document.getElementById('GameCanvas');
canvas.addEventListener("dragenter", (e) => {
e.preventDefault();
e.stopPropagation();
}, false);
canvas.addEventListener("dragover", (e) => {
e.preventDefault();
e.stopPropagation();
}, false);
canvas.addEventListener("dragleave", (e) => {
e.preventDefault();
e.stopPropagation();
}, false);
canvas.addEventListener("drop", (e) => {
e.preventDefault();
e.stopPropagation();
// 处理拖拽文件的逻辑
let files = e.dataTransfer.files;
let reg = /\.json$/;
if (!reg.test(files[0].name)) {
return;
}
this.readProject(files[0]);
}, false);
}
/**
* 读取工程文件
*/
private readProject(file: File) {
let fileReader = new FileReader();
fileReader.readAsText(file);
fileReader.onload = () => {
cc.log(fileReader.result);
let editor = this.resetEditor();
editor.importProject(JSON.parse(fileReader.result as string));
};
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.0.5",
"uuid": "c88fc210-391c-43c2-8fb0-516a7bee3600",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}