/** @format */ import {ECSSystem} from '../lib/ECSSystem' import {GenFilterKey} from '../lib/ECSComponent' import {FightWorld} from '../worlds/FightWorld' import {ComDie} from '../components/ComDie' import {ComFindEnemy} from '../components/ComFindEnemy' import {ComRole} from '../components/ComRole' import {ComTransform} from '../components/ComTransform' import {cCollider} from '../../collision/Collider' import {ShapeType} from '../../collision/Shape' import {EventType} from '../core/NodeEvent' import {ComCocosNode} from '../components/ComCocosNode' import {ComAttackable} from '../components/ComAttackable' import {Data} from '../../GameControl' import {ComFriend} from '../components/ComFriend' import {ComEnemy} from '../components/ComEnemy' import {cObject} from '../../collision/Object' import {ROLE_TYPE, SKILL_TYPE} from '../../enums/Enum' import {ComSkillAbel} from '../components/ComSkillAbel' import {ComBehaviorTree} from '../components/ComBehaviorTree' export const FILTER_FIND_ENEMY = GenFilterKey( [ComFindEnemy, ComRole, ComAttackable, ComTransform, ComCocosNode, ComBehaviorTree], [ComDie], ) export const FILTER_CAN_ATTACK_FRIEND = GenFilterKey( [ComRole, ComTransform, ComCocosNode, ComFriend, ComBehaviorTree], [ComDie], ) export const FILTER_CAN_ATTACK_ENEMY = GenFilterKey( [ComRole, ComTransform, ComCocosNode, ComEnemy, ComBehaviorTree], [ComDie], ) export class SysFindEnemy extends ECSSystem { /** 连接 */ public onAdd(world: FightWorld): void {} /** 断开连接 */ public onRemove(world: FightWorld): void {} /** 添加实体 */ public onEntityEnter(world: FightWorld, entity: number): void {} /** */ public onEntityLeave(world: FightWorld, entity: number): void {} /** 更新 */ public onUpdate(world: FightWorld, dt: number): void { cCollider.inst.update(dt) let filter = world.getFilter(FILTER_FIND_ENEMY) let canAttackFriendFilter = world.getFilter(FILTER_CAN_ATTACK_FRIEND) let canAttackEnemyFilter = world.getFilter(FILTER_CAN_ATTACK_ENEMY) filter.walk((entity: number) => { let comRole: ComRole = world.getComponent(entity, ComRole) let comFindEnemy: ComFindEnemy = world.getComponent(entity, ComFindEnemy) let comAttackable: ComAttackable = world.getComponent(entity, ComAttackable) let comTransform: ComTransform = world.getComponent(entity, ComTransform) let comSkillAbel = world.getComponent(entity, ComSkillAbel) //目标角色死亡 if (world.isDie(comFindEnemy.enemy)) { //console.log('SysFindEnemy 目标角色已死亡-------->', comFindEnemy.enemy) comFindEnemy.enemy = 0 } //顶盾的角色不寻敌 if (comSkillAbel?.isInShield) { return } //自己打的人死了,换目标 if (comAttackable.isMelee) { for (let i = comAttackable.beAttacks.length - 1; i >= 0; i--) { let attack = comAttackable.beAttacks[i] let comBeAttackTransform = world.getComponent(attack, ComTransform) let dir = Infinity if (comBeAttackTransform) { dir = cc.Vec2.distance( cc.v2(comTransform.x, comTransform.y), cc.v2(comBeAttackTransform.x, comBeAttackTransform.y), ) } //去掉死亡包围角色 和 攻击范围外的包围 if (world.isDie(attack) || dir > Data.game.meleeRange + 10) { if (dir > Data.game.meleeRange + 10 && !world.isDie(attack)) { cc.warn('SysFindEnemy 攻击范围外的包围角色-------->', attack) comAttackable.curAttack = 0 } comAttackable.beAttacks.splice(i, 1) } } if (comAttackable.curAttack && world.isDie(comAttackable.curAttack)) { let nextAttack = 0 // 自己被包围优先攻击自己的正面,让刺客发挥效果 comAttackable.beAttacks.sort((a, b) => { let aComTransform = world.getComponent(a, ComTransform) let bComTransform = world.getComponent(b, ComTransform) if (!aComTransform || !bComTransform) return 0 return ( Math.abs(bComTransform.dir.x - comTransform.dir.x) - Math.abs(aComTransform.dir.x - comTransform.dir.x) ) }) if (comAttackable.beAttacks.length > 0) nextAttack = comAttackable.beAttacks[0] comAttackable.curAttack = nextAttack } if (world.isDie(comFindEnemy.willAttackMelee)) { comFindEnemy.willAttackMelee = 0 } if (world.isDie(comFindEnemy.willBeAttackMelee)) { comFindEnemy.willBeAttackMelee = 0 } //去掉Y轴探测的人 if (comFindEnemy.cObjectMeleeX.containsBody.size > 0) { comFindEnemy.cObjectMeleeX.containsBody.forEach((body, otherEntity) => { let otherComAttackable: ComAttackable = world.getComponent(otherEntity, ComAttackable) if ( comFindEnemy.cObject.containsBody.get(otherEntity) || !otherComAttackable || !otherComAttackable.isMelee ) { comFindEnemy.cObjectMeleeX.containsBody.delete(otherEntity) } }) } } let findCloseEnemy = () => { //寻找最近的存活目标并且加载到场景的敌方单位 let minDistance = Infinity let minDistanceY = Infinity let minDistanceMeleeY = Infinity let canAttackFilter = comRole.group == Data.game.gFriend ? canAttackEnemyFilter : canAttackFriendFilter let minDistanceFunc = (otherEntity: number) => { let otherComTransform: ComTransform = world.getComponent(otherEntity, ComTransform) let otherComCocosNode: ComCocosNode = world.getComponent(otherEntity, ComCocosNode) let otherComRole: ComRole = world.getComponent(otherEntity, ComRole) if (!otherComCocosNode?.loaded) return //正在背刺的刺客不能被寻敌 let comEnemySkillAble = world.getComponent(otherEntity, ComSkillAbel) if ( comEnemySkillAble && comEnemySkillAble.skillConfig && comEnemySkillAble.skillConfig.type == SKILL_TYPE.moveBack && comSkillAbel.dirty ) { return } let dx = comTransform.x - otherComTransform.x let dy = comTransform.y - otherComTransform.y let distance = dx * dx + dy * dy if (world.isBase(otherComRole)) { // 强制修改基地的距离,让优先级低于近战攻击范围内的敌人 //distance = Data.game.meleeRange * Data.game.meleeRange + 10 distance = Infinity } let distanceY = Math.abs(dy) if (distance <= minDistance) { minDistance = distance comFindEnemy.enemy = otherEntity } //行为树的修改导致这个打近战去掉了 // if (comAttackable.isMelee) { // let otherComAttackable = world.getComponent(otherEntity, ComAttackable) // if (distanceY < minDistanceY && minDistanceMeleeY == Infinity) { // minDistanceY = distanceY // comFindEnemy.enemy = otherEntity // } // // 近战优先攻击近战 // if (otherComAttackable && otherComAttackable.isMelee && distanceY < minDistanceMeleeY) { // minDistanceMeleeY = distanceY // comFindEnemy.enemy = otherEntity // } // } return false } if (comFindEnemy.cObjectMeleeX && comFindEnemy.cObjectMeleeX.containsBody.size > 0) { comFindEnemy.cObjectMeleeX.containsBody.forEach((body, otherEntity) => { minDistanceFunc(otherEntity) }) } else { canAttackFilter.walk(minDistanceFunc) } } //远程永远攻击最近的目标 //近战角色在攻击范围内没有敌人并且没有被近战锁定(一直更新最近的目标),或者自己的敌人死亡 if ( !comAttackable.isMelee || (comAttackable.isMelee && !comAttackable.curAttack && !comFindEnemy.willAttackMelee && !comFindEnemy.willBeAttackMelee) ) { findCloseEnemy() } // //先以X轴探测到的敌人为首选目标 // if ( // comAttackable.isMelee && // !comAttackable.curAttack && // !comFindEnemy.willAttackMelee && // !comFindEnemy.willBeAttackMelee && // comFindEnemy.cObjectMeleeX.containsBody.size > 0 // ) { // let minDistanceX = Infinity // comFindEnemy.cObjectMeleeX.containsBody.forEach((body, otherEntity) => { // let otherComTransform: ComTransform = world.getComponent(otherEntity, ComTransform) // let otherComCocosNode: ComCocosNode = world.getComponent(otherEntity, ComCocosNode) // if (!otherComCocosNode?.loaded) return // let dx = comTransform.x - otherComTransform.x // let distanceX = dx * dx // if (distanceX <= minDistanceX) { // comFindEnemy.enemy = otherEntity // } // }) // } //不在攻击中,最近的敌人刚好在攻击范围可以准备攻击了 if ( comAttackable.isMelee && !comAttackable.curAttack && !comFindEnemy.willAttackMelee && !comFindEnemy.willBeAttackMelee && comFindEnemy.attackCObject.containsBody.has(comFindEnemy.enemy) ) { //如果敌军没有其他人即将攻击他,让敌军静止等待 let enemyComRole = world.getComponent(comFindEnemy.enemy, ComRole) let enemyComFindEnemy = world.getComponent(comFindEnemy.enemy, ComFindEnemy) let enemyComAttackable = world.getComponent(comFindEnemy.enemy, ComAttackable) comFindEnemy.willAttackMelee = comFindEnemy.enemy if ( !enemyComAttackable.curAttack && !enemyComFindEnemy.willBeAttackMelee && !enemyComFindEnemy.willAttackMelee ) { enemyComFindEnemy.willBeAttackMelee = entity //敌人准备去攻击X轴的敌人,被Y轴更近的敌人拦截,这里要把自己的寻敌置空 enemyComFindEnemy.enemy = 0 } comFindEnemy.enemy = 0 } //绘制攻击范围碰撞体 //world.drawDebug(entity, comFindEnemy.cObjectMeleeX) //world.drawDebug(entity, comFindEnemy.cObject) return false }) } }