This commit is contained in:
honmono 2022-03-21 17:27:37 +08:00
commit 91e741a895
320 changed files with 42373 additions and 0 deletions

51
.gitignore vendored Normal file
View File

@ -0,0 +1,51 @@
#/////////////////////////////////////////////////////////////////////////////
# Fireball Projects
#/////////////////////////////////////////////////////////////////////////////
/library/
/temp/
/local/
/build/
#/////////////////////////////////////////////////////////////////////////////
# npm files
#/////////////////////////////////////////////////////////////////////////////
npm-debug.log
node_modules/
#/////////////////////////////////////////////////////////////////////////////
# Logs and databases
#/////////////////////////////////////////////////////////////////////////////
*.log
*.sql
*.sqlite
#/////////////////////////////////////////////////////////////////////////////
# files for debugger
#/////////////////////////////////////////////////////////////////////////////
*.sln
*.pidb
*.suo
#/////////////////////////////////////////////////////////////////////////////
# OS generated files
#/////////////////////////////////////////////////////////////////////////////
.DS_Store
ehthumbs.db
Thumbs.db
#/////////////////////////////////////////////////////////////////////////////
# WebStorm files
#/////////////////////////////////////////////////////////////////////////////
.idea/
#//////////////////////////
# VS Code files
#//////////////////////////
.vscode/

2
README.md Executable file
View File

@ -0,0 +1,2 @@
# hello-world
Hello world new project template.

13
assets/Scene.meta Executable file
View File

@ -0,0 +1,13 @@
{
"ver": "1.1.3",
"uuid": "29f52784-2fca-467b-92e7-8fd9ef8c57b7",
"importer": "folder",
"isBundle": false,
"bundleName": "",
"priority": 1,
"compressionType": {},
"optimizeHotUpdate": {},
"inlineSpriteFrames": {},
"isRemoteBundle": {},
"subMetas": {}
}

1313
assets/Scene/helloworld.fire Executable file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
{
"ver": "1.3.2",
"uuid": "2d2f792f-a40c-49bb-a189-ed176a246e49",
"importer": "scene",
"asyncLoadAssets": false,
"autoReleaseAssets": false,
"subMetas": {}
}

13
assets/Script.meta Executable file
View File

@ -0,0 +1,13 @@
{
"ver": "1.1.3",
"uuid": "4734c20c-0db8-4eb2-92ea-e692f4d70934",
"importer": "folder",
"isBundle": false,
"bundleName": "",
"priority": 1,
"compressionType": {},
"optimizeHotUpdate": {},
"inlineSpriteFrames": {},
"isRemoteBundle": {},
"subMetas": {}
}

13
assets/Script/Common.meta Normal file
View File

@ -0,0 +1,13 @@
{
"ver": "1.1.3",
"uuid": "07d0024f-7d77-45fb-84e1-676ae4645108",
"importer": "folder",
"isBundle": false,
"bundleName": "",
"priority": 1,
"compressionType": {},
"optimizeHotUpdate": {},
"inlineSpriteFrames": {},
"isRemoteBundle": {},
"subMetas": {}
}

View File

@ -0,0 +1,644 @@
import { ComAttackable } from "../ECS/components/ComAttackable";
import { ComCocosNode } from "../ECS/components/ComCocosNode";
import { ComMonitor } from "../ECS/components/ComMonitor";
import { ComMovable } from "../ECS/components/ComMovable";
import { ComRoleConfig } from "../ECS/components/ComRoleConfig";
import { ComTransform } from "../ECS/components/ComTransform";
import { ECSWorld } from "../ECS/lib/ECSWorld";
import { SysBehaviorTree } from "../ECS/systems/SysBehaviorTree";
import { EventAttack, EventHPChange, EventHurt } from "../Struct/NodeEvent";
export namespace BT {
export class BlackBoard {
}
export class ExecuteContext {
executor: SysBehaviorTree;
world: ECSWorld;
bb: BlackBoard;
entity: number;
dt: number;
public init(executor: SysBehaviorTree, world: ECSWorld) {
this.executor = executor;
this.world = world;
}
public set(entity: number, dt: number, bb: BlackBoard) {
this.entity = entity;
this.dt = dt;
this.bb = bb;
}
}
/** 节点状态 */
export enum NodeState {
Executing, // 执行中
Success, // 成功
Fail // 失败
}
/** 节点类型 */
export enum NodeType {
// 组合节点
Sequence, // 顺序节点
Selector, // 选择节点
RandomSelector, // 随机选择节点
Parallel, // 并行节点
// 修饰节点
Inverter, // 逆变节点
Success, // 成功节点
Fail, // 失败节点
Repeater, // 重复节点
RetryTillSuccess, // 重复直到成功
// 叶子结点
Wait, // 等待
Action,
WalkToPos,
WalkToRandomPos, //
WalkToTarget,
Monitor, // 监视
Attack,
EnoughAttr, // 死亡
GoDeath, // 检查是否
}
export class NodeBase {
public type: NodeType;
public state: NodeState = NodeState.Success;
constructor(type: NodeType) {
this.type = type;
}
}
/** 组合节点 */
class CombineNode extends NodeBase {
public children: NodeBase[] = [];
public constructor(type: NodeType, children: NodeBase[]) {
super(type);
this.children = children;
}
}
/** 依次执行子节点, 遇到执行失败的则退出并返回失败, 全部执行成功则返回成功 */
export class SequenceNode extends CombineNode {
public currIdx = 0;
public ignoreFailure = false;
constructor(children: NodeBase[], ignoreFailture = false) {
super(NodeType.Sequence, children);
this.ignoreFailure = ignoreFailture;
}
}
/** 依次执行子节点, 遇到执行成功的则退出并返回成功, 全部执行失败则返回失败 */
export class SelectorNode extends CombineNode {
public currIdx:number = -1;
constructor(children: NodeBase[], ) {
super(NodeType.Selector, children);
}
}
/** 根据权重随机选择执行某个子节点 */
export class RandomSelectorNode extends CombineNode {
public weights: number[]; // 权重
public currIdx = -1; // 选中的节点
constructor(children: NodeBase[], weigets?: number[]) {
super(NodeType.RandomSelector, children);
this.weights = weigets ? weigets : new Array(children.length).fill(1);
}
}
/** 并行执行所有子节点, 全部执行完毕后返回 */
export class ParallelNode extends CombineNode {
public ignoreFailture = true;
constructor(children: NodeBase[], ignoreFailture: boolean) {
super(NodeType.Parallel, children);
this.ignoreFailture = ignoreFailture;
}
}
/** 修饰节点 */
class DecoratorNode extends NodeBase {
public child: NodeBase = null;
public constructor(type: NodeType, child: NodeBase) {
super(type);
this.child = child;
}
}
/** 返回子节点执行结果的取反值 */
export class InverterNode extends DecoratorNode {
constructor(child: NodeBase) {
super(NodeType.Inverter, child);
}
}
/** 子节点执行完毕后, 必定返回成功 */
export class SuccessNode extends DecoratorNode {
constructor(child: NodeBase) {
super(NodeType.Success, child);
}
}
/** 子节点执行完毕后, 必定返回失败 */
export class FailNode extends DecoratorNode {
constructor(child: NodeBase) {
super(NodeType.Fail, child);
}
}
/** 子节点执行重复repeatCount次后返回成功 */
export class RepeaterNode extends DecoratorNode {
public repeatCount = 1;
public currRepeatCount = 0;
public mustSuccess = false; // 子节点必须支持成功才增加重复次数
constructor(child: NodeBase, repeatCount: number, mustSuccess = false) {
super(NodeType.Repeater, child);
this.repeatCount = repeatCount;
this.mustSuccess = mustSuccess;
}
}
/** 子节点重复执行直到返回成功 */
export class RetryTillSuccess extends DecoratorNode {
timeout: number; // 超时时间
countDown: number; // 剩余时间
constructor(child: NodeBase, timeout:number) {
super(NodeType.RetryTillSuccess, child);
this.timeout = timeout;
}
}
/** 叶子结点 */
export class WaitNode extends NodeBase {
public waitSeconds:number;
public countDown:number;
constructor(seconds:number) {
super(NodeType.Wait);
this.waitSeconds = seconds;
}
}
/** 移动到目标位置后 返回成功 */
export class WalkToPosNode extends NodeBase {
public speed:number;
public targetPos: cc.Vec2;
constructor(speed: number, pos: cc.Vec2) {
super(NodeType.WalkToPos);
this.speed = speed;
this.targetPos = pos;
}
}
export class WalkToRandomPosNode extends NodeBase {
public speed: number;
public size: cc.Size;
constructor(speed: number, size: cc.Size) {
super(NodeType.WalkToRandomPos);
this.speed = speed;
this.size = size;
}
}
export class WalkToTargetNode extends NodeBase {
public speed: number;
constructor(speed: number) {
super(NodeType.WalkToTarget);
this.speed = speed;
}
}
export class MonitorNode extends NodeBase {
constructor() {
super(NodeType.Monitor);
}
}
export class AttackNode extends NodeBase {
constructor(waitSeconds: number) {
super(NodeType.Attack);
}
}
export class EnoughAttrNode extends NodeBase {
public com: {prototype: any};
public attr: string;
public value: number;
constructor(com: {prototype: any}, attr: string, value: number) {
super(NodeType.EnoughAttr);
this.com = com;
this.attr = attr;
this.value = value;
}
}
export class GoDeathNode extends NodeBase {
public waitSeconds: number;
public countDown: number;
constructor(waitSeconds: number) {
super(NodeType.GoDeath);
this.waitSeconds = waitSeconds;
}
}
class NodeHandler {
onEnter:(node: NodeBase, context: ExecuteContext) => void;
onUpdate:(node: NodeBase, context: ExecuteContext) => void;
}
export const NodeHandlers : NodeHandler[] = [];
/** Sequence node */
NodeHandlers[NodeType.Sequence] = {
onEnter(node: SequenceNode, context: ExecuteContext) : void {
node.currIdx = 0;
context.executor.onEnterBTNode(node.children[node.currIdx], context);
node.state = NodeState.Executing;
},
onUpdate(node: SequenceNode, context: ExecuteContext) : void {
if(node.state !== NodeState.Executing) return ;
if(node.currIdx < 0 || node.currIdx >= node.children.length) {
// 越界了, 不应该发生, 直接认为是失败了
node.state = NodeState.Fail;
return;
}
context.executor.updateBTNode(node.children[node.currIdx], context);
let state = node.children[node.currIdx].state;
if(state == NodeState.Executing) return;
if(state === NodeState.Fail && !node.ignoreFailure) {
node.state = NodeState.Fail;
return;
}
if(state === NodeState.Success && node.currIdx == node.children.length-1) {
node.state = NodeState.Success;
return ;
}
context.executor.onEnterBTNode(node.children[++node.currIdx], context);
}
};
/** Selector node */
NodeHandlers[NodeType.Selector] = {
onEnter(node: SelectorNode, context: ExecuteContext) : void {
node.currIdx = 0;
context.executor.onEnterBTNode(node.children[node.currIdx], context);
node.state = NodeState.Executing;
},
onUpdate(node: SelectorNode, context: ExecuteContext) : void {
if(node.state !== NodeState.Executing) return ;
if(node.currIdx < 0 || node.currIdx >= node.children.length) {
// 越界了, 认为是失败了
node.state = NodeState.Fail;
return;
}
context.executor.updateBTNode(node.children[node.currIdx], context);
let state = node.children[node.currIdx].state;
if(state == NodeState.Executing) return;
// 执行到最后一个都失败了, 那边selector失败了
if(state === NodeState.Fail && node.currIdx == node.children.length-1) {
node.state = NodeState.Fail;
return;
}
if(state == NodeState.Success) {
node.state = NodeState.Success;
return ;
}
context.executor.onEnterBTNode(node.children[++node.currIdx], context);
}
};
/** Selector node */
NodeHandlers[NodeType.RandomSelector] = {
onEnter(node: RandomSelectorNode, context: ExecuteContext) : void {
// 根据权重随机获取idx
let totalWeight = 0;
for(const weight of node.weights) {
totalWeight += weight;
}
let randomWeight = Math.random() * totalWeight;
for(let i=0; i<node.weights.length; i++) {
randomWeight -= node.weights[i];
if(randomWeight <= 0) {
node.currIdx = i;
break;
}
}
context.executor.onEnterBTNode(node.children[node.currIdx], context);
node.state = NodeState.Executing;
},
onUpdate(node: RandomSelectorNode, context: ExecuteContext) : void {
if(node.state !== NodeState.Executing) return ;
let n = node.children[node.currIdx];
context.executor.updateBTNode(n, context);
node.state = n.state;
}
};
/** Parallel node */
NodeHandlers[NodeType.Parallel] = {
onEnter(node: ParallelNode, context: ExecuteContext) : void {
for(const n of node.children) {
context.executor.onEnterBTNode(n, context);
}
node.state = NodeState.Executing;
},
onUpdate(node: ParallelNode, context: ExecuteContext) : void {
if(node.state !== NodeState.Executing) return ;
let end = true;
for(const child of node.children) {
context.executor.updateBTNode(child, context);
if(child.state === NodeState.Executing) {
end = false;
continue;
}
if(child.state == NodeState.Fail) {
node.state = NodeState.Fail;
return ;
}
}
if(end) {
node.state = NodeState.Success;
}
}
};
/** Inverter node */
NodeHandlers[NodeType.Inverter] = {
onEnter(node: InverterNode, context: ExecuteContext) : void {
context.executor.onEnterBTNode(node.child, context);
node.state = NodeState.Executing;
},
onUpdate(node: InverterNode, context: ExecuteContext) : void {
context.executor.updateBTNode(node.child, context);
if(node.child.state === NodeState.Executing) return ;
if(node.child.state == NodeState.Success) node.state = NodeState.Fail;
if(node.child.state == NodeState.Fail) node.state = NodeState.Success;
}
};
/** Success node */
NodeHandlers[NodeType.Success] = {
onEnter(node: SuccessNode, context: ExecuteContext) : void {
context.executor.onEnterBTNode(node.child, context);
node.state = NodeState.Executing;
},
onUpdate(node: SuccessNode, context: ExecuteContext) : void {
if(node.state !== NodeState.Executing) return ;
context.executor.updateBTNode(node.child, context);
if(node.child.state === NodeState.Executing) return ;
node.state = NodeState.Success;
}
};
/** Fail node */
NodeHandlers[NodeType.Fail] = {
onEnter(node: FailNode, context: ExecuteContext) : void {
context.executor.onEnterBTNode(node.child, context);
node.state = NodeState.Executing;
},
onUpdate(node: FailNode, context: ExecuteContext) : void {
if(node.state !== NodeState.Executing) return ;
context.executor.updateBTNode(node.child, context);
if(node.child.state === NodeState.Executing) return ;
node.state = NodeState.Fail;
}
};
/** Repeater node */
NodeHandlers[NodeType.Repeater] = {
onEnter(node: RepeaterNode, context: ExecuteContext) : void {
node.currRepeatCount = 0;
context.executor.onEnterBTNode(node.child, context);
node.state = NodeState.Executing;
},
onUpdate(node: RepeaterNode, context: ExecuteContext) : void {
if(node.state !== NodeState.Executing) return ;
context.executor.updateBTNode(node.child, context);
if(node.child.state === NodeState.Executing) return ;
if(!node.mustSuccess || node.child.state == NodeState.Success) node.currRepeatCount ++;
if(node.currRepeatCount >= node.repeatCount) {
node.state = NodeState.Success;
return ;
}
context.executor.onEnterBTNode(node.child, context);
}
};
/** RetryTillSuccess node */
NodeHandlers[NodeType.RetryTillSuccess] = {
onEnter(node: RetryTillSuccess, context: ExecuteContext) : void {
node.countDown = node.timeout;
context.executor.onEnterBTNode(node.child, context);
node.state = NodeState.Executing;
},
onUpdate(node: RetryTillSuccess, context: ExecuteContext) : void {
if(node.state !== NodeState.Executing) return ;
node.countDown -= context.dt;
context.executor.updateBTNode(node.child, context);
if(node.child.state === NodeState.Executing) return ;
if(node.child.state == NodeState.Success) {
node.state = NodeState.Success;
return ;
}
if(node.countDown > 0) {
context.executor.onEnterBTNode(node.child, context);
return ;
}
node.state = NodeState.Fail;
}
};
/** Wait node */
NodeHandlers[NodeType.Wait] = {
onEnter(node: WaitNode, context: ExecuteContext) : void {
node.countDown = node.waitSeconds;
node.state = NodeState.Executing;
},
onUpdate(node: WaitNode, context: ExecuteContext) : void {
if(node.state !== NodeState.Executing) return ;
node.countDown -= context.dt;
if(node.countDown <= 0) {
node.state = NodeState.Success;
}
}
};
/** Wait node */
NodeHandlers[NodeType.WalkToPos] = {
onEnter(node: WalkToPosNode, context: ExecuteContext) : void {
let comTrans = context.world.getComponent(context.entity, ComTransform);
let comMovable = context.world.getComponent(context.entity, ComMovable);
comMovable.pointIdx = 0;
comMovable.points.length = 0;
comMovable.points.push(cc.v2(comTrans.x, comTrans.y), node.targetPos);
comMovable.speed = node.speed;
comMovable.speedDirty = true;
node.state = NodeState.Executing;
comMovable.running = false;
},
onUpdate(node: WalkToPosNode, context: ExecuteContext) : void {
if(node.state !== NodeState.Executing) return ;
let comMovable = context.world.getComponent(context.entity, ComMovable);
if(comMovable.points.length == 0 || comMovable.pointIdx < 0 || comMovable.pointIdx >= comMovable.points.length) {
node.state = BT.NodeState.Success;
}
}
};
/** WalkToRandomPos node */
NodeHandlers[NodeType.WalkToRandomPos] = {
onEnter(node: WalkToRandomPosNode, context: ExecuteContext) : void {
let comTrans = context.world.getComponent(context.entity, ComTransform);
let comMovable = context.world.getComponent(context.entity, ComMovable);
comMovable.pointIdx = 0;
comMovable.points.length = 0;
let targetX = node.size.width * Math.random() - node.size.width/2;
let targetY = node.size.height * Math.random() - node.size.height/2;
comMovable.points.push(cc.v2(comTrans.x, comTrans.y), cc.v2(targetX, targetY));
comMovable.speed = node.speed;
comMovable.speedDirty = true;
node.state = NodeState.Executing;
comMovable.running = false;
},
onUpdate(node: WalkToPosNode, context: ExecuteContext) : void {
if(node.state !== NodeState.Executing) return ;
let comMovable = context.world.getComponent(context.entity, ComMovable);
if(comMovable.points.length == 0 || comMovable.pointIdx < 0 || comMovable.pointIdx >= comMovable.points.length) {
node.state = BT.NodeState.Success;
}
}
};
/** WalkToTarget node */
NodeHandlers[NodeType.WalkToTarget] = {
onEnter(node: WalkToTargetNode, context: ExecuteContext) : void {
let comTrans = context.world.getComponent(context.entity, ComTransform);
let comMovable = context.world.getComponent(context.entity, ComMovable);
let comMonitor = context.world.getComponent(context.entity, ComMonitor);
if(comMonitor.others.length <= 0) return ;
let target = context.world.getComponent(comMonitor.others[0], ComTransform);
let xOffdet = Math.sign(comTrans.x - target.x) * 10;
comMovable.pointIdx = 0;
comMovable.points.length = 0;
comMovable.points.push(cc.v2(comTrans.x, comTrans.y), cc.v2(target.x + xOffdet, target.y));
comMovable.speed = node.speed;
comMovable.speedDirty = true;
node.state = NodeState.Executing;
comMovable.running = false;
},
onUpdate(node: WalkToTargetNode, context: ExecuteContext) : void {
if(node.state !== NodeState.Executing) return ;
let comTrans = context.world.getComponent(context.entity, ComTransform);
let comMonitor = context.world.getComponent(context.entity, ComMonitor);
let comMovable = context.world.getComponent(context.entity, ComMovable);
if(comMovable.points.length == 0 || comMovable.pointIdx < 0 || comMovable.pointIdx >= comMovable.points.length) {
node.state = BT.NodeState.Success;
return ;
}
if(comMonitor.others.length <= 0) {
node.state = BT.NodeState.Fail;
return ;
}
let target = context.world.getComponent(comMonitor.others[0], ComTransform);
let xOffdet = Math.sign(comTrans.x - target.x) * 10;
comMovable.points[1].x = target.x + xOffdet;
comMovable.points[1].y = target.y;
}
};
/** Monitor node */
NodeHandlers[NodeType.Monitor] = {
onEnter(node: MonitorNode, context: ExecuteContext) : void {
let comMonitor = context.world.getComponent(context.entity, ComMonitor);
if(!comMonitor) return ;
node.state = NodeState.Executing;
},
onUpdate(node: MonitorNode, context: ExecuteContext) : void {
let comMonitor = context.world.getComponent(context.entity, ComMonitor);
node.state = comMonitor.others.length > 0 ? BT.NodeState.Success : BT.NodeState.Fail;
}
};
/** Monitor node */
NodeHandlers[NodeType.Attack] = {
onEnter(node: AttackNode, context: ExecuteContext) : void {
node.state = NodeState.Executing;
let comCocosNode = context.world.getComponent(context.entity, ComCocosNode);
if(!comCocosNode.loaded) return ;
comCocosNode.events.push(new (EventAttack));
let comAttackable = context.world.getComponent(context.entity, ComAttackable);
comAttackable.duration = 1.2;
comAttackable.countDown = comAttackable.duration;
comAttackable.dirty = true;
comAttackable.hurtArea = cc.v2(20, 10);
comAttackable.hurtFrame = 0.5;
comAttackable.mustAttackFrame = 0.6;
comAttackable.attack = 10;
},
onUpdate(node: AttackNode, context: ExecuteContext) : void {
if(node.state !== NodeState.Executing) return ;
let comAttackable = context.world.getComponent(context.entity, ComAttackable);
if(comAttackable.countDown <= 0) {
node.state = NodeState.Success;
}
}
};
/** EnoughAttr node */
NodeHandlers[NodeType.EnoughAttr] = {
onEnter(node: EnoughAttrNode, context: ExecuteContext) : void {
let com = context.world.getComponent(context.entity, node.com);
if(!com) return ;
node.state = NodeState.Executing;
},
onUpdate(node: EnoughAttrNode, context: ExecuteContext) : void {
let com = context.world.getComponent(context.entity, node.com);
if(!com) return ;
node.state = com[node.attr] >= node.value ? NodeState.Success : NodeState.Fail;
}
};
/** GoDeath node */
NodeHandlers[NodeType.GoDeath] = {
onEnter(node: GoDeathNode, context: ExecuteContext) : void {
node.countDown = node.waitSeconds;
node.state = NodeState.Executing;
},
onUpdate(node: GoDeathNode, context: ExecuteContext) : void {
if(node.countDown <= 0) {
node.state = NodeState.Success;
}
}
};
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "6cd218b5-3651-451d-90b5-5765b038f824",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,297 @@
export class LoadProgress {
public url: string;
public completedCount: number;
public totalCount: number;
public item: any;
public cb?: Function;
}
/** 一些cocos api 的封装, promise函数统一加上sync后缀 */
export default class CocosHelper {
public static async callInNextTick() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(true);
}, 0);
})
}
/** 加载进度 */
public static loadProgress = new LoadProgress();
/** 等待时间, 秒为单位 */
public static sleepSync = function(dur: number): Promise<boolean> {
return new Promise((resolve, reject) => {
cc.Canvas.instance.scheduleOnce(() => {
resolve(true);
}, dur);
});
}
/**
*
* @param target
* @param repeat -1
* @param tweens
*/
public static async runRepeatTweenSync(target: any, repeat: number, ...tweens: cc.Tween[]) {
return new Promise((resolve, reject) => {
let selfTween = cc.tween(target);
for(const tmpTween of tweens) {
selfTween = selfTween.then(tmpTween);
}
if(repeat < 0) {
cc.tween(target).repeatForever(selfTween).start();
}else {
cc.tween(target).repeat(repeat, selfTween).call(() => {
resolve(true);
}).start();
}
});
}
/** 同步的tween */
public static async runTweenSync(target: any, ...tweens: cc.Tween[]): Promise<void> {
return new Promise((resolve, reject) => {
let selfTween = cc.tween(target);
for(const tmpTween of tweens) {
selfTween = selfTween.then(tmpTween);
}
selfTween.call(() => {
resolve();
}).start();
});
}
/** 停止tween */
public stopTween(target: any) {
cc.Tween.stopAllByTarget(target);
}
public stopTweenByTag(tag: number) {
cc.Tween.stopAllByTag(tag);
}
/** 同步的动作, 在2.4.x action已经被废弃了, 不建议使用 */
public static async runActionSync(node: cc.Node, ...actions: cc.FiniteTimeAction[]) {
if(!actions || actions.length <= 0) return ;
return new Promise((resolve, reject) => {
actions.push(cc.callFunc(() => {
resolve(true);
}));
node.runAction(cc.sequence(actions));
});
}
/** 同步的动画 */
public static async runAnimSync(node: cc.Node, animName?: string | number) {
let anim = node.getComponent(cc.Animation);
if(!anim) return ;
let clip: cc.AnimationClip = null;
if(!animName) clip = anim.defaultClip;
else {
let clips = anim.getClips();
if(typeof(animName) === "number") {
clip = clips[animName];
}else if(typeof(animName) === "string") {
for(let i=0; i<clips.length; i++) {
if(clips[i].name === animName) {
clip = clips[i];
break;
}
}
}
}
if(!clip) return ;
await CocosHelper.sleepSync(clip.duration);
}
/** 加载资源异常时抛出错误 */
public static loadResThrowErrorSync<T>(url: string, type: typeof cc.Asset, onProgress?: (completedCount: number, totalCount: number, item: any) => void): Promise<T> {
return null;
}
private static _loadingMap: {[key: string]: Function[]} = {};
public static loadRes<T>(url: string, type: typeof cc.Asset, callback: Function ) {
if(this._loadingMap[url]) {
this._loadingMap[url].push(callback);
return ;
}
this._loadingMap[url] = [callback];
this.loadResSync<T>(url, type).then((data: any) => {
let arr = this._loadingMap[url];
for(const func of arr) {
func(data);
}
this._loadingMap[url] = null;
delete this._loadingMap[url];
});
}
/** 加载资源 */
public static loadResSync<T>(url: string, type: typeof cc.Asset, onProgress?: (completedCount: number, totalCount: number, item: any) => void): Promise<T>{
return new Promise((resolve, reject) => {
if(!onProgress) onProgress = this._onProgress;
cc.resources.load(url, type, onProgress, (err, asset: any) => {
if (err) {
cc.error(`${url} [资源加载] 错误 ${err}`);
resolve(null);
}else {
resolve(asset);
}
});
});
}
/**
*
* cb方法 loader方法的progress
*/
private static _onProgress(completedCount: number, totalCount: number, item: any) {
CocosHelper.loadProgress.completedCount = completedCount;
CocosHelper.loadProgress.totalCount = totalCount;
CocosHelper.loadProgress.item = item;
CocosHelper.loadProgress.cb && CocosHelper.loadProgress.cb(completedCount, totalCount, item);
}
/**
*
*/
public static findChildInNode(nodeName: string, rootNode: cc.Node): cc.Node {
if(rootNode.name == nodeName) {
return rootNode;
}
for(let i=0; i<rootNode.childrenCount; i++) {
let node = this.findChildInNode(nodeName, rootNode.children[i]);
if(node) {
return node;
}
}
return null;
}
/** 获得Component的类名 */
public static getComponentName(com: Function) {
let arr = com.name.match(/<.*>$/);
if(arr && arr.length > 0) {
return arr[0].slice(1, -1);
}
return com.name;
}
/** 加载bundle */
public static loadBundleSync(url: string, options: any): Promise<cc.AssetManager.Bundle> {
return new Promise((resolve, reject) => {
cc.assetManager.loadBundle(url, options, (err: Error, bundle: cc.AssetManager.Bundle) => {
if(!err) {
cc.error(`加载bundle失败, url: ${url}, err:${err}`);
resolve(null);
}else {
resolve(bundle);
}
});
});
}
/** 路径是相对分包文件夹路径的相对路径 */
public static loadAssetFromBundleSync(bundleName: string, url: string) {
let bundle = cc.assetManager.getBundle(bundleName);
if(!bundle) {
cc.error(`加载bundle中的资源失败, 未找到bundle, bundleUrl:${bundleName}`);
return null;
}
return new Promise((resolve, reject) => {
bundle.load(url, (err, asset: cc.Asset | cc.Asset[]) => {
if(err) {
cc.error(`加载bundle中的资源失败, 未找到asset, url:${url}, err:${err}`);
resolve(null);
}else {
resolve(asset);
}
});
});
}
/** 通过路径加载资源, 如果这个资源在bundle内, 会先加载bundle, 在解开bundle获得对应的资源 */
public static loadAssetSync(url: string) {
return new Promise((resolve, reject) => {
cc.resources.load(url, (err, assets: cc.Asset | cc.Asset[]) => {
if(!err) {
cc.error(`加载asset失败, url:${url}, err: ${err}`);
resolve(null);
}else {
this.addRef(assets);
resolve(assets);
}
});
});
}
/** 释放资源 */
public static releaseAsset(assets: cc.Asset | cc.Asset[]) {
this.decRes(assets);
}
/** 增加引用计数 */
private static addRef(assets: cc.Asset | cc.Asset[]) {
if(assets instanceof Array) {
for(const a of assets) {
a.addRef();
}
}else {
assets.addRef();
}
}
/** 减少引用计数, 当引用计数减少到0时,会自动销毁 */
private static decRes(assets: cc.Asset | cc.Asset[]) {
if(assets instanceof Array) {
for(const a of assets) {
a.decRef();
}
}else {
assets.decRef();
}
}
/** 截图 */
public static captureScreen(camera: cc.Camera, prop?: cc.Node | cc.Rect) {
let newTexture = new cc.RenderTexture();
let oldTexture = camera.targetTexture;
let rect: cc.Rect = cc.rect(0, 0, cc.visibleRect.width, cc.visibleRect.height);
if(prop) {
if(prop instanceof cc.Node) {
rect = prop.getBoundingBoxToWorld();
}else {
rect = prop;
}
}
newTexture.initWithSize(cc.visibleRect.width, cc.visibleRect.height, cc.game['_renderContext'].STENCIL_INDEX8);
camera.targetTexture = newTexture;
camera.render();
camera.targetTexture = oldTexture;
let buffer = new ArrayBuffer(rect.width * rect.height * 4);
let data = new Uint8Array(buffer);
newTexture.readPixels(data, rect.x, rect.y, rect.width, rect.height);
return data;
}
public static tweenFloat(from: number, to: number, duration: number, onUpdate: (t: number) => void, onComplete?: Function, autoStart: boolean = true) {
let o: Record<string, number> = { _value: from };
Object.defineProperty(o, 'value', {
get: () => o._value,
set: (v: number) => { o._value = v; onUpdate && onUpdate(o._value); },
});
let tween = cc.tween(o).to(duration, { value: to }).call(onComplete);
if (autoStart) {
tween.start();
}
return tween;
}
public static tweenVec2(from: cc.Vec2, to: cc.Vec2, duration: number, onUpdate: (t: cc.Vec2) => void, onComplete?: Function, autoStart: boolean = true) {
let o: Record<string, cc.Vec2> = {_value: from};
Object.defineProperty(o, 'value', {
get: () => o._value,
set: (v: cc.Vec2) => { o._value = v; onUpdate && onUpdate(o._value); },
});
let tween = cc.tween(o).to(duration, { value: to }).call(onComplete);
if (autoStart) {
tween.start();
}
return tween;
}
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "dc730152-9513-4cfe-b6f9-8d6cfdfc0e23",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,77 @@
const {ccclass, property} = cc._decorator;
@ccclass('FrameConfig')
class FrameConfig {
@property(cc.Integer) frames: number = 0; //
@property(cc.Integer) frameInterval = 1; // 帧数
offsetFrame = 0; // 偏移量
}
@ccclass
export default class FrameAnimation extends cc.Component {
@property(cc.Sprite) sprite: cc.Sprite = null;
@property(cc.SpriteAtlas) spriteAtlas: cc.SpriteAtlas = null;
@property(FrameConfig) frameConfigs: FrameConfig[] = [];
@property(cc.Boolean) playOnLoad = false;
@property(cc.Boolean) loop = false;
@property(cc.Integer) defaultConfig = 0;
private _passInterval = 0;
private _currFrame = 0;
private _currFrameConfig: FrameConfig = null;
private _playing = false;
private _loop = false;
private _callback: Function;
start () {
if(!this.sprite) this.sprite = this.getComponent(cc.Sprite);
let offset = 0;
for(const config of this.frameConfigs) {
config.offsetFrame += offset;
offset += config.frames;
}
this._loop = this.loop;
if(this.playOnLoad) {
this._currFrameConfig = this.frameConfigs[this.defaultConfig];
this._playing = true;
}
}
play(configIdx: number, loop: boolean, callback?: Function) {
this._currFrameConfig = this.frameConfigs[configIdx];
this._loop = loop;
this._currFrame = 0;
this._playing = true;
this._callback = callback;
}
stop() {
this._playing = false;
}
update (dt: number) {
if(!this._playing) return ;
this._passInterval ++;
if(this._passInterval < this._currFrameConfig.frameInterval) return ;
this._passInterval = 0;
this.sprite.spriteFrame = this.spriteAtlas.getSpriteFrames()[this._currFrameConfig.offsetFrame + this._currFrame];
this._currFrame ++;
if(this._currFrame < this._currFrameConfig.frames) return ;
if(this._loop) this._currFrame = 0;
else {
this._playing = false;
this._callback && this._callback();
}
}
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "60f21b10-95a9-4650-83ae-ff5522171c04",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

13
assets/Script/Core.meta Normal file
View File

@ -0,0 +1,13 @@
{
"ver": "1.1.3",
"uuid": "5e76ac49-9cf7-4eba-9b00-da0cbbdf8778",
"importer": "folder",
"isBundle": false,
"bundleName": "",
"priority": 1,
"compressionType": {},
"optimizeHotUpdate": {},
"inlineSpriteFrames": {},
"isRemoteBundle": {},
"subMetas": {}
}

View File

@ -0,0 +1,79 @@
import { BT } from "../Common/BehaviorTree";
import { ComAttackable } from "../ECS/components/ComAttackable";
import { ComBeAttacked } from "../ECS/components/ComBeAttacked";
import { ComBehaviorTree } from "../ECS/components/ComBehaviorTree";
import { ComCocosNode } from "../ECS/components/ComCocosNode";
import { ComMonitor } from "../ECS/components/ComMonitor";
import { ComMovable } from "../ECS/components/ComMovable";
import { ComNodeConfig } from "../ECS/components/ComNodeConfig";
import { ComRoleConfig } from "../ECS/components/ComRoleConfig";
import { ComTransform } from "../ECS/components/ComTransform";
import { ECSWorld } from "../ECS/lib/ECSWorld";
export class ECSController<T extends ECSWorld> {
public world: T;
public createRoleEntity(name: string) {
let entity = this.world.createEntity();
// 添加nodeconfig
let comMap = this.world.addComponent(entity, ComNodeConfig);
comMap.id = 1;
comMap.layer = 0;
comMap.prefabUrl = `${name}/${name}`;
// 添加transform
let comTrans = this.world.addComponent(entity, ComTransform);
comTrans.x = 0;
comTrans.y = 0;
// 添加behavior tree
let comBehavior = this.world.addComponent(entity, ComBehaviorTree);
let view = cc.view.getVisibleSize();
let patrol = new BT.SequenceNode([
new BT.WaitNode(2),
new BT.WalkToRandomPosNode(100, cc.size(view.width - 200, view.height - 200)),
]);
let follow = new BT.SequenceNode([
new BT.WalkToTargetNode(250),
new BT.AttackNode(1.2)
]);
let mainBehavior = new BT.SelectorNode([
new BT.ParallelNode([
new BT.InverterNode(new BT.MonitorNode()),
patrol
], true),
follow
]);
let root = new BT.RepeaterNode(mainBehavior, 9999);
comBehavior.root = root;
let comMovable = this.world.addComponent(entity, ComMovable);
comMovable.pointIdx = -1;
comMovable.running = false;
let comMonitor = this.world.addComponent(entity, ComMonitor);
comMonitor.lookLen = 350;
comMonitor.lookSize = 300;
comMonitor.aroundLen = 100;
let comRoleConfig = this.world.addComponent(entity, ComRoleConfig);
comRoleConfig.maxHP = 100;
comRoleConfig.lastHP = 100;
comRoleConfig.nowHP = 100;
comRoleConfig.attack = 10;
comRoleConfig.team = name == 'Biker' ? 1 : 2;
let comAttackable = this.world.addComponent(entity, ComAttackable);
comAttackable.dirty = false;
comAttackable.countDown = 0;
let comBeAttack = this.world.addComponent(entity, ComBeAttacked);
return entity;
}
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "f4905a62-50f2-4508-8932-c2446dc9ee16",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,21 @@
import { EventBase } from "../Struct/NodeEvent";
const {ccclass, property} = cc._decorator;
@ccclass
export class EventProcess extends cc.Component {
public onAttach(): void {
}
public onDetach(): void {
}
public processEvent(event: EventBase): void {
}
public sync(x: number, y: number, dir: cc.Vec2) {
}
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "421fe0f3-34ce-4d96-8ede-370f051cdb61",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,78 @@
import CocosHelper from "../Common/CocosHelper";
import FrameAnimation from "../Common/FrameAnimation";
import { EventBase, EventDeath, EventHPChange, EventType } from "../Struct/NodeEvent";
import { EventProcess } from "./EventProcess";
const {ccclass, property} = cc._decorator;
@ccclass
export default class RoleEventProcess extends EventProcess {
@property(cc.Animation) anim: cc.Animation = null;
start () {
}
onAttach(): void {
}
onDetach(): void {
}
processEvent(event: EventBase): void {
let _reset = (name: string) => {
this.anim.on(cc.Animation.EventType.FINISHED, () => {
this.anim.play(name);
}, this);
}
switch(event.type) {
case EventType.Stand:
this.anim.play('stand');
break;
case EventType.Run:
this.anim.play('run');
break;
case EventType.Attack:
this.anim.play('punch');
_reset('stand');
break;
case EventType.Hurt:
this.anim.play('hurt');
_reset('stand');
break;
case EventType.Death:
this.anim.play('death');
this.anim.on(cc.Animation.EventType.FINISHED, () => {
(event as EventDeath).callback();
}, this);
break;
case EventType.HPChange:
this._changeHP(event as EventHPChange);
break;
}
}
private _changeHP(event: EventHPChange) {
let progressBar = this.node.getChildByName("HP").getComponent(cc.ProgressBar);
let from = event.lastHP / event.maxHP;
let to = event.nowHP / event.maxHP;
CocosHelper.tweenFloat(from, to, 0.2, (v) => {
progressBar.progress = v;
});
}
public sync(x: number, y: number, dir: cc.Vec2) {
this.node.x = x;
this.node.y = y;
this.node.getChildByName('sp').scaleX = dir.x >= 0 ? 3 : -3;
this.node.getChildByName('HP').x = dir.x >= 0 ? -30 : 30;
}
// update (dt) {}
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "d38f8101-a4d4-422a-95ce-643759bfe803",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

13
assets/Script/ECS.meta Normal file
View File

@ -0,0 +1,13 @@
{
"ver": "1.1.3",
"uuid": "bf25ebfe-4ae5-4c18-8033-bb2bc7293a03",
"importer": "folder",
"isBundle": false,
"bundleName": "",
"priority": 1,
"compressionType": {},
"optimizeHotUpdate": {},
"inlineSpriteFrames": {},
"isRemoteBundle": {},
"subMetas": {}
}

View File

@ -0,0 +1,13 @@
{
"ver": "1.1.3",
"uuid": "3878b852-fe53-405d-a5bf-3978aedd9ce7",
"importer": "folder",
"isBundle": false,
"bundleName": "",
"priority": 1,
"compressionType": {},
"optimizeHotUpdate": {},
"inlineSpriteFrames": {},
"isRemoteBundle": {},
"subMetas": {}
}

View File

@ -0,0 +1,15 @@
import { ComType, EntityIndex } from "../lib/Const";
import { ECSComponent } from "../lib/ECSComponent";
@ECSComponent(ComType.ComAttackable)
export class ComAttackable {
public duration: number; // 攻击持续时间
public countDown: number; // 攻击剩余时间
public hurtFrame: number; // 攻击帧
public mustAttackFrame: number;
public hurted: boolean;
public dirty: boolean; //
public attack: number; // 攻击力
public hurtArea: cc.Vec2; // 攻击区域
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "7ee555b4-eed0-4207-81f5-41b51bb3bd73",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,7 @@
import { ComType } from "../lib/Const";
import { ECSComponent } from "../lib/ECSComponent";
@ECSComponent(ComType.ComBeAttacked)
export class ComBeAttacked {
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "c3d913ea-e619-4f5e-bb18-3fe947505dda",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,9 @@
import { BT } from "../../Common/BehaviorTree";
import { ComType } from "../lib/Const";
import { ECSComponent } from "../lib/ECSComponent";
@ECSComponent(ComType.ComBehaviorTree)
export class ComBehaviorTree {
public root: BT.NodeBase = null;
public bb: BT.BlackBoard = new BT.BlackBoard();
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "a529fff3-0126-4e53-a4c7-e5ddb6d76976",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,10 @@
import { EventBase } from "../../Struct/NodeEvent";
import { ComType } from "../lib/Const";
import { ECSComponent } from "../lib/ECSComponent";
@ECSComponent(ComType.ComCocosNode)
export class ComCocosNode {
public node: cc.Node = null;
public loaded = false;
public events: EventBase[] = [];
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "e75ab1ce-3260-45cb-975a-9c4c1ab72f94",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,10 @@
import { ComType, EntityIndex } from "../lib/Const";
import { ECSComponent } from "../lib/ECSComponent";
@ECSComponent(ComType.ComMonitor)
export class ComMonitor {
public lookLen = 0;
public lookSize = 0;
public aroundLen = 0;
public others: EntityIndex[] = [];
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "274fabed-397f-4950-b691-2c6b6b894c9f",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,12 @@
import { ComType } from "../lib/Const";
import { ECSComponent } from "../lib/ECSComponent";
@ECSComponent(ComType.ComMovable)
export class ComMovable {
public running = false;
public speed = 0;
public points: cc.Vec2[] = [];
public pointIdx = 0;
public speedDirty = false;
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "773fd91d-89d6-499e-9488-3d85f11938a1",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,9 @@
import { ComType } from "../lib/Const";
import { ECSComponent } from "../lib/ECSComponent";
@ECSComponent(ComType.ComNodeConfig)
export class ComNodeConfig {
id = 0; // 唯一标识
prefabUrl = ''
layer = 0; // 层级
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "ef2ff8f3-8ffa-49f6-8e55-b8ba0284a095",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,16 @@
import { ComType } from "../lib/Const";
import { ECSComponent } from "../lib/ECSComponent";
@ECSComponent(ComType.ComRoleConfig)
export class ComRoleConfig {
public team: number;
public maxHP: number;
public lastHP: number;
public nowHP: number;
public HPDirty: boolean;
public attack: number;
public moveSpeed: number;
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "a0036aea-32a4-4b9b-a587-5262ea4cedf7",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,11 @@
import { ComType } from "../lib/Const";
import { ECSComponent } from "../lib/ECSComponent";
@ECSComponent(ComType.ComTransform)
export class ComTransform {
public dir = cc.v2(1, 0); //方向向量
public x = 0;
public y = 0;
public width = 0;
public height = 0;
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "0bb2991a-84c5-4b22-ade6-59d2e7e97252",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

13
assets/Script/ECS/lib.meta Executable file
View File

@ -0,0 +1,13 @@
{
"ver": "1.1.3",
"uuid": "fa6ac7d5-2bad-42ee-90ba-44147985428d",
"importer": "folder",
"isBundle": false,
"bundleName": "",
"priority": 1,
"compressionType": {},
"optimizeHotUpdate": {},
"inlineSpriteFrames": {},
"isRemoteBundle": {},
"subMetas": {}
}

View File

@ -0,0 +1,16 @@
export type EntityIndex = number;
export type ComPoolIndex = number;
export enum ComType {
ComCocosNode = 0,
ComMovable = 1,
ComNodeConfig = 2,
ComBehaviorTree = 3,
ComTransform = 4,
ComMonitor = 5,
ComRoleConfig = 6,
ComAttackable = 7,
ComBeAttacked = 8
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "ce38ec2f-50f5-4f7a-ad24-48a576032322",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,36 @@
import { ComType, EntityIndex } from "./Const";
/** 构造函数 */
export interface ECSComConstructor extends Function {
new(): any;
}
export interface ECSTypedComConstructor<T> extends ECSComConstructor {
new():T;
}
/** 通过type存取 构造函数 */
const ComConsMap: {[key: number]: ECSComConstructor} = cc.js.createMap();
function RegistComConstructor(comType: ComType, func: ECSComConstructor) {
ComConsMap[comType] = func;
}
export function GetComConstructor(comType: ComType) {
return ComConsMap[comType];
}
/** 通过构造函数存取 type */
function SetComConstructorType(comCons: ECSComConstructor, type: ComType) {
comCons['__type__'] = type;
}
export function GetComConstructorType<T>(comCons: {prototype: T}): ComType {
return comCons['__type__'];
}
/** ECSComponent */
export function ECSComponent(type: ComType) {
return function(func: ECSComConstructor) {
SetComConstructorType(func, type);
RegistComConstructor(type, func);
};
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "58b062eb-ce1e-49dd-8520-15713b92a51f",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,35 @@
import { ComPoolIndex } from "./Const";
import { ECSTypedComConstructor } from "./ECSComponent";
/**
*
*/
export class ECSComponentPool<T> {
private _componentConstructor: ECSTypedComConstructor<T>;
public constructor(comCons: ECSTypedComConstructor<T>) {
this._componentConstructor = comCons;
}
private _components: T[] = []; // components
private _reservedIdxs: ComPoolIndex[] = []; // 缓存的component idx
public get(idx: ComPoolIndex): T {
return this._components[idx];
}
public alloc(): ComPoolIndex {
if(this._reservedIdxs.length > 0) {
let ret = this._reservedIdxs.pop();
this._componentConstructor.apply(this._components[ret]); // 重置对象
return ret;
}
let newInstance = new this._componentConstructor();
this._components.push(newInstance);
return this._components.length - 1;
}
public free(idx: ComPoolIndex) {
this._reservedIdxs.push(idx);
}
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "a40c60fb-7834-4de4-b1e9-6a346dd68e60",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,67 @@
import {ECSWorld} from "./ECSWorld"
import {ECSComConstructor, GetComConstructorType } from "./ECSComponent";
import { ComPoolIndex, ComType } from "./Const";
import { ECSComponentPool } from "./ECSComponentPool";
/** 实体 */
export class Entity {
public id: number; // 唯一标识
public index: number; //
public dead: boolean; //
// 实体上的组件, 存放的是ComponentPool的index
private _components: Array<ComPoolIndex> = new Array<ComPoolIndex>(Object.keys(ComType).length/2).fill(-1);
private _world: ECSWorld = null;
public get world(): ECSWorld {
return this._world;
}
public set world(world: ECSWorld) {
this._world = world;
}
/** 获取实体上的组件 */
public getComponent<T>(typeOrFunc: ComType | {prototype: T}): ComPoolIndex {
let type = typeof typeOrFunc == 'number' ? typeOrFunc : GetComConstructorType(typeOrFunc);
let comPoolIdx = this._components[type];
if(comPoolIdx == -1) return -1;
return comPoolIdx;
}
/** 添加组件 */
public addComponent<T>(func: {prototype: T}): ComPoolIndex {
let type = GetComConstructorType(func);
if(this._components[type] !== -1) {
return this._components[type];
}
let comPoolIdx = this._components[type] = this._world.getComponentPool(func).alloc();
this._world.setEntityDirty(this);
return comPoolIdx;
}
/** 移除组件 */
public removeComponent<T extends ECSComConstructor>(func: ECSComConstructor, dirty = true) {
let comPoolIdx = this._components[GetComConstructorType(func)];
if(comPoolIdx == -1) {
console.error(`[ECSEntity]: removeComponent error, type: ${GetComConstructorType(func)}`);
return false;
}
this._components[GetComConstructorType(func)] = -1;
this._world.getComponentPool<ECSComponentPool<T>>(func).free(comPoolIdx);
dirty && this._world.setEntityDirty(this);
return true;
}
/** 移除所有组件 */
public removeAllComponents(dirty: boolean) {
for(let type = 0; type < this._components.length; type++) {
let comPoolIdx = this._components[type];
if(comPoolIdx == -1) continue;
this._world.getComponentPool<ECSComponentPool<any>>(type).free(comPoolIdx);
}
this._components.fill(-1);
if(dirty) {
this._world.setEntityDirty(this);
}
}
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "f6eafcbc-0cee-4a3d-90a7-d1d89d934869",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,67 @@
import { ComType, EntityIndex } from "./Const";
import { Entity } from "./ECSEntity";
import { ECSWorld } from "./ECSWorld";
export class ECSFillter {
private _world: ECSWorld = null;
private _entitiesMap = new Map<EntityIndex, boolean>();
private _acceptComTypes: ComType[] = []; // 接收的组件类型
private _rejectComTypes: ComType[] = []; // 拒绝的组件类型
public constructor(world: ECSWorld, accepts?: ComType[], rejects?: ComType[]) {
this._world = world;
this._acceptComTypes = accepts && accepts.length > 0 ? accepts : this._acceptComTypes;
this._rejectComTypes = rejects && rejects.length > 0 ? rejects : this._rejectComTypes;
}
public get entities() {
return this._entitiesMap;
}
public onEntityEnter(entity: EntityIndex) {
if(this._entitiesMap.has(entity)) {
console.warn(`[ECSFillter]: addEntity entity is had ${entity}`);
return true;
}
this._entitiesMap.set(entity, true);
return true;
}
public onEntityLeave(entity: EntityIndex) {
if(!this._entitiesMap.has(entity)) {
console.warn(`[ECSFillter]: removeEntity entity not had ${entity}`);
return true;
}
this._entitiesMap.delete(entity);
}
public walk(callback?: (entity: number) => boolean) {
this._entitiesMap.forEach((value, entity) => {
callback(entity);
});
}
public isAccept(entity: Entity) {
for(let i = 0; i < this._acceptComTypes.length; i++) {
if(entity.getComponent(this._acceptComTypes[i]) == -1) {
return false;
}
}
for(let i = 0; i < this._rejectComTypes.length; i++) {
if(entity.getComponent(this._rejectComTypes[i]) !== -1) {
return false;
}
}
return true;
}
public isContains(entity: number) {
return this._entitiesMap.has(entity);
}
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "a2265ecd-3751-461b-9d2f-755ccc56c3da",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,14 @@
import { ECSWorld } from "./ECSWorld";
export abstract class ECSSystem {
/** 连接 */
public abstract onAdd(world: ECSWorld): void;
/** 断开连接 */
public abstract onRemove(world: ECSWorld): void;
/** 添加实体 */
public abstract onEntityEnter(world: ECSWorld, entity: number): void;
/** */
public abstract onEntityLeave(world: ECSWorld, entity: number): void;
/** 更新 */
public abstract onUpdate(world: ECSWorld, dt: number): void;
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "925b6e67-1234-4933-a867-339843b9a663",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

203
assets/Script/ECS/lib/ECSWorld.ts Executable file
View File

@ -0,0 +1,203 @@
import { Entity } from "./ECSEntity"
import { ECSFillter } from "./ECSFillter"
import { ECSComConstructor, GetComConstructor as GetComConstructor, GetComConstructorType } from "./ECSComponent";
import { ECSSystem } from "./ECSSystem";
import { ComPoolIndex, ComType, EntityIndex } from "./Const";
import { ECSComponentPool } from "./ECSComponentPool";
/**
*
*/
export class ECSWorld {
private _systems: ECSSystem[] = []; // world内所有的system
private _entities: Entity[] = []; // world内所有的entity
private _reservedIds: number[] = []; // 缓存
private _componentPools: ECSComponentPool<any>[] = [];
private _fillters = new Map<string, ECSFillter>();
private _entitiesToDelete: number[] = [];
private _entityIdSeed: number = 0;
/** 获取ComponentPool */
public getComponentPool<T>(typeOrFunc: ComType | {prototype: T}): ECSComponentPool<T> {
let type = typeof typeOrFunc == "number" ? typeOrFunc : GetComConstructorType(typeOrFunc);
if(!this._componentPools[type]) {
this._componentPools[type] = new ECSComponentPool<T>(GetComConstructor(type));
}
return this._componentPools[type] as any;
}
/** 添加system */
public addSystem(system: ECSSystem) {
this._systems.push(system);
system.onAdd(this);
for(let i = 0; i < this._entities.length; i++) {
if(this._entities[i].id !== -1) {
system.onEntityEnter(this, i);
}
}
}
/** 移除system */
public removeSystem(system: ECSSystem) {
system.onRemove(this);
for(let i = 0; i < this._entities.length; i++) {
if(this._entities[i].id !== -1) {
system.onEntityLeave(this, i);
}
}
for(let i = this._systems.length - 1; i >= 0; i--) {
if(this._systems[i] == system) {
this._systems.splice(i, 1);
}
}
}
/** 创建实体 */
public createEntity(): number {
let entity: Entity = null;
let index = -1;
if(this._reservedIds.length > 0) {
index = this._reservedIds.pop();
entity = this._entities[index];
}else {
entity = new Entity();
index = this._entities.length;
this._entities.push(entity);
}
entity.id = this._entityIdSeed++;
entity.world = this;
entity.index = index;
entity.dead = false;
for(let system of this._systems) {
system.onEntityEnter(this, entity.index);
}
return entity.index;
}
/** 移除实体 */
public removeEntity(entity: EntityIndex): boolean {
if(entity <= 0) return false;
if(!this._entities[entity] || this._entities[entity].dead) {
console.warn(`[ECSWorld] removeEntity entity is removed`);
return false;
}
this._entities[entity].dead = true;
this._entitiesToDelete.push(entity);
this._fillters.forEach((fillter, key) => {
fillter.isContains(entity) && fillter.onEntityLeave(entity);
});
for(let system of this._systems) {
system.onEntityLeave(this, entity);
}
return true;
}
public getComponent<T>(entity: EntityIndex, com: {prototype: T}) {
if(!this._entities[entity]) return null;
let comPoolIdx = this._entities[entity].getComponent(com);
return this.getComponentPool<T>(com).get(comPoolIdx);
}
public removeComponent(entity: EntityIndex, com: ECSComConstructor) {
if(!this._entities[entity]) return ;
this._entities[entity].removeComponent(com);
}
public addComponent<T>(entity: EntityIndex, com: {prototype: T}) {
if(!this._entities[entity]) return null;
let comPoolIdx = this._entities[entity].addComponent(com);
return this.getComponentPool<T>(com).get(comPoolIdx)
}
public getSingletonComponent<T>(com: {prototype: T}): T {
let entity = this._entities[0];
let comPoolIdx = entity.getComponent(<ECSComConstructor>com);
let pool = this.getComponentPool<T>(com);
if(comPoolIdx >= 0) return pool.get(comPoolIdx);
return pool.get(entity.addComponent(com));
}
public setEntityDirty(entity: Entity): void {
this._fillters.forEach((fillter, key) => {
let accept = !entity.dead && fillter.isAccept(entity);
if(accept != fillter.isContains(entity.index)) {
accept ? fillter.onEntityEnter(entity.index) : fillter.onEntityLeave(entity.index);
}
});
}
public getEntityId(entity: EntityIndex) : number {
return this._entities[entity].id;
}
public getFilter(fillterKey: string): ECSFillter {
if(this._fillters.has(fillterKey)) {
return this._fillters.get(fillterKey);
}
let [acceptStr, rejectStr] = fillterKey.split("-");
let accept = acceptStr && acceptStr.length > 0 ? acceptStr.split(',').map(Number) : null;
let reject = rejectStr && rejectStr.length > 0 ? rejectStr.split(',').map(Number) : null;
let fillter = new ECSFillter(this, accept, reject);
this._fillters.set(fillterKey, fillter);
// 将当期的entity放入fillter
for(let i=1; i<this._entities.length; i++) {
const entity = this._entities[i];
if(fillter.isAccept(entity)) {
fillter.onEntityEnter(entity.index);
}
}
return fillter;
}
public update(dt:number) {
for(let system of this._systems) {
system.onUpdate(this, dt);
}
if(this._entitiesToDelete.length > 0) {
this._realRemoveEntity();
}
}
private _realRemoveEntity() {
for(let entityIdx of this._entitiesToDelete) {
this._entities[entityIdx].removeAllComponents(false);
this._entities[entityIdx].id = -1;
this._reservedIds.push(entityIdx);
}
this._entitiesToDelete.length = 0;
}
}
export function GenFillterKey(accepts: ECSComConstructor[], rejects?: ECSComConstructor[]) {
let acceptTypes: ComType[] = [];
let rejectTypes: ComType[] = [];
if(accepts && accepts.length > 0) {
for(let i = 0; i < accepts.length; i++) {
acceptTypes[i] = GetComConstructorType(accepts[i]);
}
}
if(rejects && rejects.length > 0) {
for(let i = 0; i < rejects.length; i++) {
rejectTypes[i] = GetComConstructorType(rejects[i]);
}
}
if(acceptTypes.length < 0) {
console.error(`[ECSWorld]: GenFillterKey 必须要有accpters`);
return "";
}
acceptTypes.sort();
rejectTypes.sort();
let key = Array.prototype.join.call(acceptTypes, ",");
if(!rejectTypes || rejectTypes.length <= 0) return key;
key += '-';
key += Array.prototype.join.call(rejectTypes, ",");
return key;
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "decf2eee-3ef7-456d-bd8c-8d4eaa63e617",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,13 @@
{
"ver": "1.1.3",
"uuid": "db93b255-0e1f-4c8a-adc8-d3d2fca077cc",
"importer": "folder",
"isBundle": false,
"bundleName": "",
"priority": 1,
"compressionType": {},
"optimizeHotUpdate": {},
"inlineSpriteFrames": {},
"isRemoteBundle": {},
"subMetas": {}
}

View File

@ -0,0 +1,71 @@
import { ComAttackable } from "../components/ComAttackable";
import { ComBeAttacked } from "../components/ComBeAttacked";
import { ComRoleConfig } from "../components/ComRoleConfig";
import { ComTransform } from "../components/ComTransform";
import { ECSSystem } from "../lib/ECSSystem";
import { ECSWorld, GenFillterKey } from "../lib/ECSWorld";
const FILTER_ATTACKABLE = GenFillterKey([ComAttackable]);
const FILTER_BEATTACKED = GenFillterKey([ComBeAttacked]);
export class SysAttack extends ECSSystem {
/** 连接 */
public onAdd(world: ECSWorld): void {
}
/** 断开连接 */
public onRemove(world: ECSWorld): void {
}
/** 添加实体 */
public onEntityEnter(world: ECSWorld, entity: number): void {
}
/** */
public onEntityLeave(world: ECSWorld, entity: number): void {
}
/** 更新 */
public onUpdate(world: ECSWorld, dt: number): void {
let filter = world.getFilter(FILTER_ATTACKABLE);
filter.walk((entity: number) => {
let comTransSelf = world.getComponent(entity, ComTransform);
let comAttackable = world.getComponent(entity, ComAttackable);
let comRoleConfigSelf = world.getComponent(entity, ComRoleConfig);
if(comAttackable.countDown <= 0) return ;
comAttackable.countDown -= dt;
if(comAttackable.mustAttackFrame)
if(comAttackable.dirty && comAttackable.countDown <= comAttackable.hurtFrame) {
comAttackable.dirty = false;
world.getFilter(FILTER_BEATTACKED).walk((entityOther: number) => {
let comRoleConfigOther = world.getComponent(entityOther, ComRoleConfig);
let comTransOther = world.getComponent(entityOther, ComTransform);
if(!comRoleConfigOther || comRoleConfigOther.team == comRoleConfigSelf.team) return ;
let xDiff = comTransOther.x - comTransSelf.x;
if(xDiff * Math.sign(xDiff) >= comAttackable.hurtArea.x || Math.abs(comTransOther.y - comTransSelf.y) >= comAttackable.hurtArea.y) {
return ;
}
// 扣血
if(!comRoleConfigOther || comRoleConfigOther.nowHP <= 0) return ;
comRoleConfigOther.lastHP = comRoleConfigOther.nowHP;
comRoleConfigOther.nowHP -= comAttackable.attack;
comRoleConfigOther.HPDirty = true;
// 打断对方的攻击动作
let comAttackableOther = world.getComponent(entityOther, ComAttackable);
if(!comAttackableOther || comAttackableOther.countDown <= 0) return ;
if(comAttackableOther.countDown >= comAttackableOther.mustAttackFrame) {
comAttackableOther.dirty = false;
}
comAttackable.countDown = 0.25;
return false;
});
}
return false;
});
}
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "780c245c-45f2-47ae-a01a-b61dcfb3ab3e",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,65 @@
import { BT } from "../../Common/BehaviorTree";
import { ComBehaviorTree } from "../components/ComBehaviorTree";
import { ECSSystem } from "../lib/ECSSystem";
import { ECSWorld, GenFillterKey } from "../lib/ECSWorld";
const FILTER_BEHAVIORTREE = GenFillterKey([ComBehaviorTree]);
const Context = new BT.ExecuteContext();
export class SysBehaviorTree extends ECSSystem {
/** 连接 */
public onAdd(world: ECSWorld): void{
Context.init(this, world);
}
/** 断开连接 */
public onRemove(world: ECSWorld): void {
}
/** 添加实体 */
public onEntityEnter(world: ECSWorld, entity: number): void {
}
/** */
public onEntityLeave(world: ECSWorld, entity: number): void {
}
/** 更新 */
public onUpdate(world: ECSWorld, dt: number): void {
Context.executor = this;
Context.dt = dt;
Context.world = world;
world.getFilter(FILTER_BEHAVIORTREE).walk((entity: number) => {
let comBehavior = world.getComponent(entity, ComBehaviorTree);
Context.set(entity, dt, comBehavior.bb);
if(comBehavior.root.state !== BT.NodeState.Executing) {
this.onEnterBTNode(comBehavior.root, Context);
}else {
this.updateBTNode(comBehavior.root, Context);
}
return false;
});
}
/** 进入节点 */
public onEnterBTNode(node: BT.NodeBase, context: BT.ExecuteContext) {
let handler = BT.NodeHandlers[node.type];
handler.onEnter(node, context);
}
/** 更新节点状态 */
public updateBTNode(node: BT.NodeBase, context: BT.ExecuteContext) {
let handler = BT.NodeHandlers[node.type];
handler.onUpdate(node, context);
}
public canExecuteBTNode(node: BT.NodeBase, context: BT.ExecuteContext) : boolean {
return true;
}
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "1b6bd6b5-cb8e-46e8-8052-8ed53e75a2e5",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,96 @@
import CocosHelper from "../../Common/CocosHelper";
import { ComNodeConfig } from "../components/ComNodeConfig";
import { ComCocosNode } from "../components/ComCocosNode";
import { ECSSystem } from "../lib/ECSSystem";
import { ECSWorld, GenFillterKey } from "../lib/ECSWorld";
import { ComTransform } from "../components/ComTransform";
import { EventProcess } from "../../Core/EventProcess";
import { ComRoleConfig } from "../components/ComRoleConfig";
export interface ITouchProcessor {
onTouchStart(worldPos: cc.Vec2, world: ECSWorld): void;
onTouchMove?(worldPos: cc.Vec2, world: ECSWorld): void;
onTouchEnd?(worldPos: cc.Vec2, world: ECSWorld): void;
onTouchCancel?(worldPos: cc.Vec2, world: ECSWorld): void;
}
const FILTER_COCOS_NODE = GenFillterKey([ComNodeConfig], [ComCocosNode]);
const FILTER_NODE_EVENT = GenFillterKey([ComCocosNode, ComTransform]);
export class SysCocosView extends ECSSystem implements ITouchProcessor {
onTouchStart(worldPos: cc.Vec2, world: ECSWorld): boolean {
return false;
}
onAdd(world: ECSWorld) {
}
onRemove(world: ECSWorld) {
}
onEntityEnter(world: ECSWorld, entity: number) {
}
onEntityLeave(world: ECSWorld, entity: number) {
}
onUpdate(world:ECSWorld, dt:number) {
world.getFilter(FILTER_COCOS_NODE).walk((entity: number) => {
let comNodeConfig = world.getComponent(entity, ComNodeConfig);
let comView = world.addComponent(entity, ComCocosNode);
let comRoleConfig = world.getComponent(entity, ComRoleConfig);
this._loadView(world, entity, comNodeConfig).then((node: cc.Node) => {
console.log('load view success');
});
return false;
});
world.getFilter(FILTER_NODE_EVENT).walk((entity: number) => {
let comCocosNode = world.getComponent(entity, ComCocosNode);
if(!comCocosNode.loaded) return ;
let eventProcess = comCocosNode.node.getComponent(EventProcess);
if(!eventProcess) return ;
let comTrans = world.getComponent(entity, ComTransform);
eventProcess.sync(comTrans.x, comTrans.y, comTrans.dir);
while(comCocosNode.events.length) {
let event = comCocosNode.events.shift();
eventProcess.processEvent(event);
}
return true;
});
}
private async _loadView(world: ECSWorld, entity: number, nodeConfig: ComNodeConfig) {
let prefab = await CocosHelper.loadResSync<cc.Prefab>(nodeConfig.prefabUrl, cc.Prefab);
if(!prefab) {
cc.warn(`加载失败: ${nodeConfig.prefabUrl}`);
return;
}
let comView = world.getComponent(entity, ComCocosNode);
if(comView.node) { // 销毁当前node
this.destoryView(comView.node);
}
let layers = cc.find('Canvas/Layers');
if(!layers) return ;
let node = cc.instantiate(prefab);
node.parent = layers.getChildByName(`${nodeConfig.layer}`);
comView.node = node;
comView.loaded = true;
return node;
}
private destoryView(node: cc.Node) {
node.removeFromParent();
node.destroy();
}
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "e4f95950-b571-48dd-8c95-7808eef0c336",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,82 @@
import { ComMonitor } from "../components/ComMonitor";
import { ComRoleConfig } from "../components/ComRoleConfig";
import { ComTransform } from "../components/ComTransform";
import { ECSSystem } from "../lib/ECSSystem";
import { ECSWorld, GenFillterKey } from "../lib/ECSWorld";
const FILTER_MONITOR = GenFillterKey([ComRoleConfig, ComTransform, ComMonitor]);
export class SysMonitor extends ECSSystem {
/** 连接 */
public onAdd(world: ECSWorld): void {
}
/** 断开连接 */
public onRemove(world: ECSWorld): void {
}
/** 添加实体 */
public onEntityEnter(world: ECSWorld, entity: number): void {
}
/** */
public onEntityLeave(world: ECSWorld, entity: number): void {
let filter = world.getFilter(FILTER_MONITOR);
// 判断当前monitor是否
filter.entities.forEach((value: boolean, otherEntity: number) => {
let comMonitor = world.getComponent(otherEntity, ComMonitor);
if(!comMonitor) return ;
for(let i=comMonitor.others.length-1; i>=0; i--) {
if(comMonitor.others[i] == entity) {
comMonitor.others.splice(i);
}
}
});
}
/** 更新 */
public onUpdate(world: ECSWorld, dt: number): void {
let filter = world.getFilter(FILTER_MONITOR);
filter.walk((entity: number) => {
let comMonitor = world.getComponent(entity, ComMonitor);
let comTrans = world.getComponent(entity, ComTransform);
let comRoleConfig = world.getComponent(entity, ComRoleConfig);
// 判断当前monitor是否
filter.entities.forEach((value: boolean, otherEntity: number) => {
let comTransOther = world.getComponent(otherEntity, ComTransform);
let comRoleConfigOther = world.getComponent(otherEntity, ComRoleConfig);
if(entity == otherEntity || !comRoleConfigOther || comRoleConfigOther.team == comRoleConfig.team) return ;
let a = cc.v2(comTrans.x, comTrans.y);
let centerPoint = a.add(comTrans.dir.mul(comMonitor.lookLen));
let b = centerPoint.add(cc.v2(comTrans.dir.y, -comTrans.dir.x).mul(comMonitor.lookSize));
let c = centerPoint.add(cc.v2(-comTrans.dir.y, comTrans.dir.x).mul(comMonitor.lookSize));
let _check = (com: ComTransform) => {
return (a.sub(cc.v2(com.x, com.y)).len() < comMonitor.aroundLen || isInTriangle(cc.v2(com.x, com.y), a, b, c))
}
for(let i=comMonitor.others.length-1; i>=0; i--) {
const com = world.getComponent(comMonitor.others[i], ComTransform);
if(!com || !_check(com)) {
comMonitor.others.splice(i, 1);
}
}
if(comMonitor.others.indexOf(otherEntity) == -1 && _check(comTransOther)) {
comMonitor.others.push(otherEntity);
}
});
return false;
});
}
}
// 判断一个点是否在三角形内
function isInTriangle(point: cc.Vec2, triA: cc.Vec2, triB: cc.Vec2, triC: cc.Vec2) {
let AB = triB.sub(triA), AC = triC.sub(triA), BC = triC.sub(triB), AD = point.sub(triA), BD = point.sub(triB);
//@ts-ignore
return (AB.cross(AC) >= 0 ^ AB.cross(AD) < 0) && (AB.cross(AC) >= 0 ^ AC.cross(AD) >= 0) && (BC.cross(AB) > 0 ^ BC.cross(BD) >= 0);
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "0b207deb-ff30-4fbe-aca7-2f77f299569d",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,70 @@
import { ComCocosNode as ComCocosNode } from "../components/ComCocosNode";
import { ComMovable } from "../components/ComMovable";
import { ComTransform } from "../components/ComTransform";
import { ECSSystem } from "../lib/ECSSystem";
import { ECSWorld, GenFillterKey as GenFilterKey } from "../lib/ECSWorld";
const FILTER_MOVE = GenFilterKey([ComMovable, ComTransform, ComCocosNode]);
export class SysMovable extends ECSSystem {
/** 连接 */
public onAdd(world: ECSWorld): void{
}
/** 断开连接 */
public onRemove(world: ECSWorld): void {
}
/** 添加实体 */
public onEntityEnter(world: ECSWorld, entity: number): void {
}
/** */
public onEntityLeave(world: ECSWorld, entity:number): void {
}
/** 更新 */
public onUpdate(world: ECSWorld, dt:number): void {
world.getFilter(FILTER_MOVE).walk((entity: number) => {
let comMovable = world.getComponent(entity, ComMovable);
let comTrans = world.getComponent(entity, ComTransform);
if(comMovable.speed <= 0 || comMovable.pointIdx >= comMovable.points.length) {
return ;
}
if(!comMovable.running) {
comMovable.running = true;
}
let moveLen = comMovable.speed * dt;
while(moveLen > 0 && comMovable.pointIdx < comMovable.points.length) {
let nextPoint = comMovable.points[comMovable.pointIdx];
let offsetX = nextPoint.x - comTrans.x;
let offsetY = nextPoint.y - comTrans.y;
let offsetLen = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
if(offsetLen <= moveLen) {
moveLen -= offsetLen;
comTrans.x = nextPoint.x;
comTrans.y = nextPoint.y;
comMovable.pointIdx ++;
continue;
}
comTrans.dir.x = offsetX / offsetLen;
comTrans.dir.y = offsetY / offsetLen;
comTrans.x += moveLen * comTrans.dir.x;
comTrans.y += moveLen * comTrans.dir.y;
moveLen = -1;
}
if(comMovable.pointIdx >= comMovable.points.length) {
comMovable.speed = 0;
comMovable.speedDirty = true;
}
return false;
});
}
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "6f024564-9b8c-47ad-95eb-eca4afd96720",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,69 @@
import { EventDeath, EventHPChange, EventHurt, EventRun, EventStand } from "../../Struct/NodeEvent";
import { ComBehaviorTree } from "../components/ComBehaviorTree";
import { ComCocosNode } from "../components/ComCocosNode";
import { ComMonitor } from "../components/ComMonitor";
import { ComMovable } from "../components/ComMovable";
import { ComNodeConfig } from "../components/ComNodeConfig";
import { ComRoleConfig } from "../components/ComRoleConfig";
import { ECSSystem } from "../lib/ECSSystem";
import { ECSWorld, GenFillterKey } from "../lib/ECSWorld";
const FILTER_ROLE_NODE = GenFillterKey([ComCocosNode, ComRoleConfig]);
export class SysRoleState extends ECSSystem {
/** 连接 */
public onAdd(world: ECSWorld): void {
}
/** 断开连接 */
public onRemove(world: ECSWorld): void {
}
/** 添加实体 */
public onEntityEnter(world: ECSWorld, entity: number): void {
}
/** */
public onEntityLeave(world: ECSWorld, entity: number): void {
}
/** 更新 */
public onUpdate(world: ECSWorld, dt: number): void {
world.getFilter(FILTER_ROLE_NODE).walk((entity: number) => {
let comCocosNode = world.getComponent(entity, ComCocosNode);
if(!comCocosNode.loaded) return ;
let comRoleConfig = world.getComponent(entity, ComRoleConfig);
let comMovable = world.getComponent(entity, ComMovable);
if(comMovable && comMovable.speedDirty) {
comMovable.speedDirty = false;
if(comMovable.speed > 0) {
comCocosNode.events.push(new EventRun());
}else {
comCocosNode.events.push(new EventStand());
}
}
if(comRoleConfig && comRoleConfig.HPDirty) {
comCocosNode.events.push(new EventHPChange(comRoleConfig.maxHP, comRoleConfig.lastHP, comRoleConfig.nowHP));
if(comRoleConfig.lastHP > comRoleConfig.nowHP) {
comCocosNode.events.push(new EventHurt());
}
if(comRoleConfig.nowHP <= 0) {
comCocosNode.events.push(new EventDeath(() => {
world.removeComponent(entity, ComNodeConfig);
world.removeComponent(entity, ComCocosNode);
world.removeEntity(entity);
comCocosNode.node.destroy();
}));
world.removeComponent(entity, ComBehaviorTree);
world.removeComponent(entity, ComMonitor);
world.removeComponent(entity, ComMovable);
world.removeComponent(entity, ComRoleConfig);
}
comRoleConfig.HPDirty = false;
}
return false;
});
}
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "b679be6f-54ac-4790-99ff-41e3993bf130",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,13 @@
{
"ver": "1.1.3",
"uuid": "00fbfe49-b0ae-426b-ae03-f11fba4ce919",
"importer": "folder",
"isBundle": false,
"bundleName": "",
"priority": 1,
"compressionType": {},
"optimizeHotUpdate": {},
"inlineSpriteFrames": {},
"isRemoteBundle": {},
"subMetas": {}
}

View File

@ -0,0 +1,5 @@
import { ECSWorld } from "../lib/ECSWorld";
export class WorldCocosView extends ECSWorld {
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "2bbeb931-7f40-48e4-937d-13006b7f016b",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

81
assets/Script/Main.ts Normal file
View File

@ -0,0 +1,81 @@
import { ECSController } from "./Core/ECSController";
import { ECSWorld } from "./ECS/lib/ECSWorld";
import { SysAttack } from "./ECS/systems/SysAttack";
import { SysBehaviorTree } from "./ECS/systems/SysBehaviorTree";
import { ITouchProcessor, SysCocosView } from "./ECS/systems/SysCocosView";
import { SysMonitor } from "./ECS/systems/SysMonitor";
import { SysMovable } from "./ECS/systems/SysMovable";
import { SysRoleState } from "./ECS/systems/SysRoleState";
import { WorldCocosView } from "./ECS/worlds/WorldCocosView";
const {ccclass, property} = cc._decorator;
@ccclass
export default class Main extends cc.Component {
private _world: ECSWorld = null;
private _touchHandler: ITouchProcessor[] = [];
private ecsController = new ECSController();
start () {
this.ecsController.world = this._world = new WorldCocosView();
this._world.createEntity(); // 创建0号实体
this._world.addSystem(new SysBehaviorTree()); // 行为树
this._world.addSystem(new SysMovable()); // 移动
this._world.addSystem(new SysMonitor()); // 监视
this._world.addSystem(new SysAttack()); // 攻击系统
this._world.addSystem(new SysRoleState()); // role state
this._world.addSystem(new SysCocosView()); // cocos view
this.regiestTouchEvent();
}
onClick1() {
this.ecsController.createRoleEntity("Biker");
}
onClick2() {
this.ecsController.createRoleEntity("Cyborg");
}
protected update(dt: number): void {
if(this._world) this._world.update(dt);
}
private regiestTouchEvent() {
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._onTouchCancel, this);
}
private _onTouchStart(e: cc.Event.EventTouch) {
for(let i = 0; i < this._touchHandler.length; i++) {
this._touchHandler[i].onTouchStart(e.getLocation(), this._world);
}
}
private _onTouchMove() {
}
private _onTouchEnd() {
}
private _onTouchCancel() {
}
public regiestTouchHandler(handler: ITouchProcessor) {
this._touchHandler.push(handler);
}
public unRegiestTouchHandler(handler:ITouchProcessor) {
for(let i = this._touchHandler.length - 1; i >= 0; i--) {
if(this._touchHandler[i] == handler) {
this._touchHandler.splice(i, 1);
}
}
}
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "e1b90feb-a217-4493-849d-9a611900d683",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

13
assets/Script/Struct.meta Normal file
View File

@ -0,0 +1,13 @@
{
"ver": "1.1.3",
"uuid": "e2f8f29f-d6d0-4172-802f-80e64a869ccf",
"importer": "folder",
"isBundle": false,
"bundleName": "",
"priority": 1,
"compressionType": {},
"optimizeHotUpdate": {},
"inlineSpriteFrames": {},
"isRemoteBundle": {},
"subMetas": {}
}

View File

@ -0,0 +1,6 @@
export enum Direction {
Up,
Down,
Left,
Right
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "436daff0-c505-45e0-b0ea-d804b6ef7fac",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -0,0 +1,59 @@
export enum EventType {
Stand,
Run,
Attack,
Hurt,
HPChange,
Death
}
export class EventBase {
type: EventType;
constructor(type:number) {
this.type = type;
}
}
export class EventStand extends EventBase {
constructor() {
super(EventType.Stand);
}
}
export class EventRun extends EventBase {
constructor() {
super(EventType.Run);
}
}
export class EventAttack extends EventBase {
constructor() {
super(EventType.Attack);
}
}
export class EventHurt extends EventBase {
constructor() {
super(EventType.Hurt);
}
}
export class EventDeath extends EventBase {
callback: Function;
constructor(cb: Function) {
super(EventType.Death);
this.callback = cb;
}
}
export class EventHPChange extends EventBase {
public lastHP: number;
public nowHP: number;
public maxHP: number;
constructor(maxHP: number, lastHP: number, nowHP: number) {
super(EventType.HPChange);
this.maxHP = maxHP;
this.lastHP = lastHP;
this.nowHP = nowHP;
}
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "8bdaf2f1-e01a-449c-88ab-e35aa4741b9c",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

13
assets/Texture.meta Executable file
View File

@ -0,0 +1,13 @@
{
"ver": "1.1.3",
"uuid": "7b81d4e8-ec84-4716-968d-500ac1d78a54",
"importer": "folder",
"isBundle": false,
"bundleName": "",
"priority": 1,
"compressionType": {},
"optimizeHotUpdate": {},
"inlineSpriteFrames": {},
"isRemoteBundle": {},
"subMetas": {}
}

BIN
assets/Texture/HelloWorld.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@ -0,0 +1,38 @@
{
"ver": "2.3.7",
"uuid": "6aa0aa6a-ebee-4155-a088-a687a6aadec4",
"importer": "texture",
"type": "sprite",
"wrapMode": "clamp",
"filterMode": "bilinear",
"premultiplyAlpha": false,
"genMipmaps": false,
"packable": true,
"width": 195,
"height": 270,
"platformSettings": {},
"subMetas": {
"HelloWorld": {
"ver": "1.0.6",
"uuid": "31bc895a-c003-4566-a9f3-2e54ae1c17dc",
"importer": "sprite-frame",
"rawTextureUuid": "6aa0aa6a-ebee-4155-a088-a687a6aadec4",
"trimType": "none",
"trimThreshold": 1,
"rotated": false,
"offsetX": 0,
"offsetY": 0,
"trimX": 0,
"trimY": 0,
"width": 195,
"height": 270,
"rawWidth": 195,
"rawHeight": 270,
"borderTop": 0,
"borderBottom": 0,
"borderLeft": 0,
"borderRight": 0,
"subMetas": {}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

View File

@ -0,0 +1,38 @@
{
"ver": "2.3.7",
"uuid": "a8027877-d8d6-4645-97a0-52d4a0123dba",
"importer": "texture",
"type": "sprite",
"wrapMode": "clamp",
"filterMode": "bilinear",
"premultiplyAlpha": false,
"genMipmaps": false,
"packable": true,
"width": 2,
"height": 2,
"platformSettings": {},
"subMetas": {
"singleColor": {
"ver": "1.0.6",
"uuid": "410fb916-8721-4663-bab8-34397391ace7",
"importer": "sprite-frame",
"rawTextureUuid": "a8027877-d8d6-4645-97a0-52d4a0123dba",
"trimType": "none",
"trimThreshold": 1,
"rotated": false,
"offsetX": 0,
"offsetY": 0,
"trimX": 0,
"trimY": 0,
"width": 2,
"height": 2,
"rawWidth": 2,
"rawHeight": 2,
"borderTop": 0,
"borderBottom": 0,
"borderLeft": 0,
"borderRight": 0,
"subMetas": {}
}
}
}

13
assets/resources.meta Normal file
View File

@ -0,0 +1,13 @@
{
"ver": "1.1.3",
"uuid": "95da9891-fc56-4ff7-a3a8-7193d87b5173",
"importer": "folder",
"isBundle": true,
"bundleName": "resources",
"priority": 8,
"compressionType": {},
"optimizeHotUpdate": {},
"inlineSpriteFrames": {},
"isRemoteBundle": {},
"subMetas": {}
}

View File

@ -0,0 +1,13 @@
{
"ver": "1.1.3",
"uuid": "a2761368-1fe6-41a6-a672-089f0f06e26f",
"importer": "folder",
"isBundle": false,
"bundleName": "",
"priority": 1,
"compressionType": {},
"optimizeHotUpdate": {},
"inlineSpriteFrames": {},
"isRemoteBundle": {},
"subMetas": {}
}

View File

@ -0,0 +1,492 @@
[
{
"__type__": "cc.Prefab",
"_name": "",
"_objFlags": 0,
"_native": "",
"data": {
"__id__": 1
},
"optimizationPolicy": 0,
"asyncLoadAssets": false,
"readonly": false
},
{
"__type__": "cc.Node",
"_name": "Biker",
"_objFlags": 0,
"_parent": null,
"_children": [
{
"__id__": 2
},
{
"__id__": 6
}
],
"_active": true,
"_components": [
{
"__id__": 13
}
],
"_prefab": {
"__id__": 14
},
"_opacity": 255,
"_color": {
"__type__": "cc.Color",
"r": 255,
"g": 255,
"b": 255,
"a": 255
},
"_contentSize": {
"__type__": "cc.Size",
"width": 0,
"height": 0
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_trs": {
"__type__": "TypedArray",
"ctor": "Float64Array",
"array": [
-447.33,
-180.474,
0,
0,
0,
0,
1,
1,
1,
1
]
},
"_eulerAngles": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_skewX": 0,
"_skewY": 0,
"_is3DNode": false,
"_groupIndex": 0,
"groupIndex": 0,
"_id": ""
},
{
"__type__": "cc.Node",
"_name": "sp",
"_objFlags": 0,
"_parent": {
"__id__": 1
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 3
},
{
"__id__": 4
}
],
"_prefab": {
"__id__": 5
},
"_opacity": 255,
"_color": {
"__type__": "cc.Color",
"r": 255,
"g": 255,
"b": 255,
"a": 255
},
"_contentSize": {
"__type__": "cc.Size",
"width": 48,
"height": 48
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_trs": {
"__type__": "TypedArray",
"ctor": "Float64Array",
"array": [
0,
0,
0,
0,
0,
0,
1,
3,
3,
1
]
},
"_eulerAngles": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_skewX": 0,
"_skewY": 0,
"_is3DNode": false,
"_groupIndex": 0,
"groupIndex": 0,
"_id": ""
},
{
"__type__": "cc.Sprite",
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 2
},
"_enabled": true,
"_materials": [
{
"__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432"
}
],
"_srcBlendFactor": 770,
"_dstBlendFactor": 771,
"_spriteFrame": {
"__uuid__": "6a868846-77bb-4cd0-befb-52a3dfd16822"
},
"_type": 0,
"_sizeMode": 1,
"_fillType": 0,
"_fillCenter": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_fillStart": 0,
"_fillRange": 0,
"_isTrimmedMode": true,
"_atlas": null,
"_id": ""
},
{
"__type__": "cc.Animation",
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 2
},
"_enabled": true,
"_defaultClip": {
"__uuid__": "da671d3b-78c7-475c-9896-f0fe061c1840"
},
"_clips": [
{
"__uuid__": "6ee5314b-888f-409e-902e-26749cad0015"
},
{
"__uuid__": "41ff0c6a-d2e8-4c9b-b789-bc4d370f7306"
},
{
"__uuid__": "61a5270f-7761-498d-bbec-b994abd2c8a5"
},
{
"__uuid__": "1cac62f0-3a28-49cd-928f-3411415323b9"
},
{
"__uuid__": "62e0fbf7-19ce-44b3-a6b6-d24a24f0bdbd"
},
{
"__uuid__": "da671d3b-78c7-475c-9896-f0fe061c1840"
},
{
"__uuid__": "28fe88ff-4bc8-4075-8e33-036bc8590ed4"
}
],
"playOnLoad": true,
"_id": ""
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "32/npSD/5H0La6j9teJEMf",
"sync": false
},
{
"__type__": "cc.Node",
"_name": "HP",
"_objFlags": 0,
"_parent": {
"__id__": 1
},
"_children": [
{
"__id__": 7
}
],
"_active": true,
"_components": [
{
"__id__": 10
},
{
"__id__": 11
}
],
"_prefab": {
"__id__": 12
},
"_opacity": 255,
"_color": {
"__type__": "cc.Color",
"r": 255,
"g": 255,
"b": 255,
"a": 255
},
"_contentSize": {
"__type__": "cc.Size",
"width": 50,
"height": 10
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_trs": {
"__type__": "TypedArray",
"ctor": "Float64Array",
"array": [
-30,
43.706,
0,
0,
0,
0,
1,
1,
1,
1
]
},
"_eulerAngles": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_skewX": 0,
"_skewY": 0,
"_is3DNode": false,
"_groupIndex": 0,
"groupIndex": 0,
"_id": ""
},
{
"__type__": "cc.Node",
"_name": "value",
"_objFlags": 512,
"_parent": {
"__id__": 6
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 8
}
],
"_prefab": {
"__id__": 9
},
"_opacity": 255,
"_color": {
"__type__": "cc.Color",
"r": 255,
"g": 0,
"b": 0,
"a": 255
},
"_contentSize": {
"__type__": "cc.Size",
"width": 50,
"height": 10
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0.5
},
"_trs": {
"__type__": "TypedArray",
"ctor": "Float64Array",
"array": [
-25,
0,
0,
0,
0,
0,
1,
1,
1,
1
]
},
"_eulerAngles": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_skewX": 0,
"_skewY": 0,
"_is3DNode": false,
"_groupIndex": 0,
"groupIndex": 0,
"_id": ""
},
{
"__type__": "cc.Sprite",
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 7
},
"_enabled": true,
"_materials": [
{
"__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432"
}
],
"_srcBlendFactor": 770,
"_dstBlendFactor": 771,
"_spriteFrame": {
"__uuid__": "67e68bc9-dad5-4ad9-a2d8-7e03d458e32f"
},
"_type": 1,
"_sizeMode": 0,
"_fillType": 0,
"_fillCenter": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_fillStart": 0,
"_fillRange": 0,
"_isTrimmedMode": true,
"_atlas": null,
"_id": ""
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "89xzFFRJFFJJux2bcfP/16",
"sync": false
},
{
"__type__": "cc.Sprite",
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 6
},
"_enabled": true,
"_materials": [
{
"__uuid__": "eca5d2f2-8ef6-41c2-bbe6-f9c79d09c432"
}
],
"_srcBlendFactor": 770,
"_dstBlendFactor": 771,
"_spriteFrame": {
"__uuid__": "88e79fd5-96b4-4a77-a1f4-312467171014"
},
"_type": 1,
"_sizeMode": 0,
"_fillType": 0,
"_fillCenter": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_fillStart": 0,
"_fillRange": 0,
"_isTrimmedMode": true,
"_atlas": null,
"_id": ""
},
{
"__type__": "cc.ProgressBar",
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 6
},
"_enabled": true,
"_N$totalLength": 50,
"_N$barSprite": {
"__id__": 8
},
"_N$mode": 0,
"_N$progress": 1,
"_N$reverse": false,
"_id": ""
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "6f4u0eKrBLg6uBBF8XiS5j",
"sync": false
},
{
"__type__": "d38f8EBpNRCKpXOZDdZv+gD",
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 1
},
"_enabled": true,
"anim": {
"__id__": 4
},
"_id": ""
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "",
"sync": false
}
]

View File

@ -0,0 +1,9 @@
{
"ver": "1.3.2",
"uuid": "e3f2d00f-b307-4c62-93ae-b968945b1b9f",
"importer": "prefab",
"optimizationPolicy": "AUTO",
"asyncLoadAssets": false,
"readonly": false,
"subMetas": {}
}

View File

@ -0,0 +1,13 @@
{
"ver": "1.1.3",
"uuid": "6dabbe66-aa6b-4573-9fa1-3da50292f5a1",
"importer": "folder",
"isBundle": false,
"bundleName": "",
"priority": 1,
"compressionType": {},
"optimizeHotUpdate": {},
"inlineSpriteFrames": {},
"isRemoteBundle": {},
"subMetas": {}
}

View File

@ -0,0 +1,61 @@
{
"__type__": "cc.AnimationClip",
"_name": "attack",
"_objFlags": 0,
"_native": "",
"_duration": 1.0166666666666666,
"sample": 60,
"speed": 1,
"wrapMode": 1,
"curveData": {
"comps": {
"cc.Sprite": {
"spriteFrame": [
{
"frame": 0,
"value": {
"__uuid__": "36974693-ddd3-43de-994b-2f87d650afab"
}
},
{
"frame": 0.16666666666666666,
"value": {
"__uuid__": "e5b7949e-ab1a-485a-8652-81db128e6d2d"
}
},
{
"frame": 0.3333333333333333,
"value": {
"__uuid__": "70703165-d081-4711-9156-ec570fb12db9"
}
},
{
"frame": 0.5,
"value": {
"__uuid__": "4ccbf914-5142-4ba7-80f6-46a3503c4afe"
}
},
{
"frame": 0.6666666666666666,
"value": {
"__uuid__": "e7d042b9-f49a-4495-bb62-fcae6fa39cb3"
}
},
{
"frame": 0.8333333333333334,
"value": {
"__uuid__": "24dc980d-2426-41b9-91a7-9a664bd2f6b0"
}
},
{
"frame": 1,
"value": {
"__uuid__": "36974693-ddd3-43de-994b-2f87d650afab"
}
}
]
}
}
},
"events": []
}

View File

@ -0,0 +1,6 @@
{
"ver": "2.1.2",
"uuid": "6ee5314b-888f-409e-902e-26749cad0015",
"importer": "animation-clip",
"subMetas": {}
}

View File

@ -0,0 +1,55 @@
{
"__type__": "cc.AnimationClip",
"_name": "death",
"_objFlags": 0,
"_native": "",
"_duration": 0.85,
"sample": 60,
"speed": 1,
"wrapMode": 1,
"curveData": {
"comps": {
"cc.Sprite": {
"spriteFrame": [
{
"frame": 0,
"value": {
"__uuid__": "f5476f9b-3eac-4003-a7d9-868fbeb659fa"
}
},
{
"frame": 0.16666666666666666,
"value": {
"__uuid__": "0d4a7d9d-533a-4b37-a48d-bc4b4b5d3409"
}
},
{
"frame": 0.3333333333333333,
"value": {
"__uuid__": "1051eefe-7216-45c1-8f10-673f9dae689f"
}
},
{
"frame": 0.5,
"value": {
"__uuid__": "dde16e39-67b3-427e-8c49-53c00fb59dc7"
}
},
{
"frame": 0.6666666666666666,
"value": {
"__uuid__": "555da2d0-cb33-416b-95fc-4e21b3979e7b"
}
},
{
"frame": 0.8333333333333334,
"value": {
"__uuid__": "297a317b-d3f4-4495-a907-2d120eb45f93"
}
}
]
}
}
},
"events": []
}

View File

@ -0,0 +1,6 @@
{
"ver": "2.1.2",
"uuid": "41ff0c6a-d2e8-4c9b-b789-bc4d370f7306",
"importer": "animation-clip",
"subMetas": {}
}

View File

@ -0,0 +1,37 @@
{
"__type__": "cc.AnimationClip",
"_name": "hurt",
"_objFlags": 0,
"_native": "",
"_duration": 0.18333333333333332,
"sample": 60,
"speed": 1,
"wrapMode": 1,
"curveData": {
"comps": {
"cc.Sprite": {
"spriteFrame": [
{
"frame": 0,
"value": {
"__uuid__": "4d3d9c32-a3e4-462c-9bd6-0e23c769bdb0"
}
},
{
"frame": 0.08333333333333333,
"value": {
"__uuid__": "877bb390-9e8f-4426-ae29-630095004e6d"
}
},
{
"frame": 0.16666666666666666,
"value": {
"__uuid__": "4d3d9c32-a3e4-462c-9bd6-0e23c769bdb0"
}
}
]
}
}
},
"events": []
}

View File

@ -0,0 +1,6 @@
{
"ver": "2.1.2",
"uuid": "28fe88ff-4bc8-4075-8e33-036bc8590ed4",
"importer": "animation-clip",
"subMetas": {}
}

View File

@ -0,0 +1,61 @@
{
"__type__": "cc.AnimationClip",
"_name": "punch",
"_objFlags": 0,
"_native": "",
"_duration": 1.0166666666666666,
"sample": 60,
"speed": 1,
"wrapMode": 1,
"curveData": {
"comps": {
"cc.Sprite": {
"spriteFrame": [
{
"frame": 0,
"value": {
"__uuid__": "f180d226-0504-4b60-ae35-40d47deac85a"
}
},
{
"frame": 0.16666666666666666,
"value": {
"__uuid__": "4328e236-573e-49e1-9922-523be4bbcffd"
}
},
{
"frame": 0.3333333333333333,
"value": {
"__uuid__": "443d0757-6fe2-4834-b162-26d77092ee5c"
}
},
{
"frame": 0.5,
"value": {
"__uuid__": "5b9ea29e-14bd-4376-be56-7641d93cdd45"
}
},
{
"frame": 0.6666666666666666,
"value": {
"__uuid__": "bf94064d-b1ae-42e6-8dce-c1bedea66e07"
}
},
{
"frame": 0.8333333333333334,
"value": {
"__uuid__": "7501f2da-6a8b-4a10-9908-ec67aff429f4"
}
},
{
"frame": 1,
"value": {
"__uuid__": "f180d226-0504-4b60-ae35-40d47deac85a"
}
}
]
}
}
},
"events": []
}

View File

@ -0,0 +1,6 @@
{
"ver": "2.1.2",
"uuid": "61a5270f-7761-498d-bbec-b994abd2c8a5",
"importer": "animation-clip",
"subMetas": {}
}

View File

@ -0,0 +1,61 @@
{
"__type__": "cc.AnimationClip",
"_name": "run",
"_objFlags": 0,
"_native": "",
"_duration": 1.0166666666666666,
"sample": 60,
"speed": 1,
"wrapMode": 2,
"curveData": {
"comps": {
"cc.Sprite": {
"spriteFrame": [
{
"frame": 0,
"value": {
"__uuid__": "541e4c96-93f5-4ccd-8d1b-b9e386d143c1"
}
},
{
"frame": 0.16666666666666666,
"value": {
"__uuid__": "e61ec58b-4144-437a-b053-090890f3fb81"
}
},
{
"frame": 0.3333333333333333,
"value": {
"__uuid__": "e7ae4439-ebc8-49eb-9ff6-8d255c822619"
}
},
{
"frame": 0.5,
"value": {
"__uuid__": "15102378-3270-4f81-a975-9fde0a542fdb"
}
},
{
"frame": 0.6666666666666666,
"value": {
"__uuid__": "a6252eac-25e4-4b98-8b85-05c4ba8e7641"
}
},
{
"frame": 0.8333333333333334,
"value": {
"__uuid__": "24a57346-5b93-4131-832b-ed6fdc48ec16"
}
},
{
"frame": 1,
"value": {
"__uuid__": "541e4c96-93f5-4ccd-8d1b-b9e386d143c1"
}
}
]
}
}
},
"events": []
}

View File

@ -0,0 +1,6 @@
{
"ver": "2.1.2",
"uuid": "1cac62f0-3a28-49cd-928f-3411415323b9",
"importer": "animation-clip",
"subMetas": {}
}

View File

@ -0,0 +1,61 @@
{
"__type__": "cc.AnimationClip",
"_name": "run_attack",
"_objFlags": 0,
"_native": "",
"_duration": 1.0166666666666666,
"sample": 60,
"speed": 1,
"wrapMode": 1,
"curveData": {
"comps": {
"cc.Sprite": {
"spriteFrame": [
{
"frame": 0,
"value": {
"__uuid__": "6589b771-3f5a-491e-8bc2-881a7462e3f7"
}
},
{
"frame": 0.16666666666666666,
"value": {
"__uuid__": "9a7da8fb-e686-4cec-baf9-4fbfd24cb6a0"
}
},
{
"frame": 0.3333333333333333,
"value": {
"__uuid__": "b06d453f-86ed-4630-bc36-73c0a97a2767"
}
},
{
"frame": 0.5,
"value": {
"__uuid__": "5a2a0b48-ed3b-4d7e-9896-6085d0a48464"
}
},
{
"frame": 0.6666666666666666,
"value": {
"__uuid__": "bc238bda-b094-4257-8a60-c8b54d69cb9a"
}
},
{
"frame": 0.8333333333333334,
"value": {
"__uuid__": "5e70746a-2039-42d2-b514-53c793c70ba7"
}
},
{
"frame": 1,
"value": {
"__uuid__": "6589b771-3f5a-491e-8bc2-881a7462e3f7"
}
}
]
}
}
},
"events": []
}

View File

@ -0,0 +1,6 @@
{
"ver": "2.1.2",
"uuid": "62e0fbf7-19ce-44b3-a6b6-d24a24f0bdbd",
"importer": "animation-clip",
"subMetas": {}
}

Some files were not shown because too many files have changed in this diff Show More