refactor(battle): 重构战斗目标查找与位置管理逻辑
新增全局位置网格系统,用于按索引存储敌我单位实体ID: - 在SingletonModuleComp添加heroGrid与monGrid数组 - 为HeroAttrsComp新增posIndex字段记录位置索引并初始化 优化战斗核心流程: - 重构MissionHeroComp的位置选择逻辑,拆分方法返回位置索引而非直接坐标,优化位置占用检测 - 重构SCastSystem的目标查找与收集逻辑,改用网格遍历替代全量实体查询,大幅提升性能 - 统一三路单位的查找优先级,简化代码提升可维护性 - 完善Hero与Monster的创建销毁流程,同步更新网格的单位注册与注销信息
This commit is contained in:
@@ -45,6 +45,8 @@ export class SingletonModuleComp extends ecs.Comp {
|
|||||||
in_select: false,
|
in_select: false,
|
||||||
in_fight: false,
|
in_fight: false,
|
||||||
stop_mon_action: false,
|
stop_mon_action: false,
|
||||||
|
heroGrid: [-1, -1, -1, -1, -1, -1],
|
||||||
|
monGrid: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
|
||||||
};
|
};
|
||||||
finish_guides: number[] = [0]
|
finish_guides: number[] = [0]
|
||||||
data: any = {
|
data: any = {
|
||||||
|
|||||||
@@ -42,6 +42,13 @@ export class Hero extends ecs.Entity {
|
|||||||
view.node.destroy();
|
view.node.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const model = this.get(HeroAttrsComp);
|
||||||
|
if (model && model.posIndex >= 0) {
|
||||||
|
if (smc.mission.heroGrid[model.posIndex] === this.eid) {
|
||||||
|
smc.mission.heroGrid[model.posIndex] = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 手动移除组件,确保 ecs 侧引用及时释放
|
// 手动移除组件,确保 ecs 侧引用及时释放
|
||||||
this.remove(HeroViewComp);
|
this.remove(HeroViewComp);
|
||||||
this.remove(HeroAttrsComp);
|
this.remove(HeroAttrsComp);
|
||||||
@@ -66,7 +73,7 @@ export class Hero extends ecs.Entity {
|
|||||||
* 2) 初始化表现与属性数据
|
* 2) 初始化表现与属性数据
|
||||||
* 3) 播放下落入场并在落地后启用碰撞与移动
|
* 3) 播放下落入场并在落地后启用碰撞与移动
|
||||||
*/
|
*/
|
||||||
load(pos: Vec3 = Vec3.ZERO,scale:number = 1,uuid:number=1001, dropToY:number = pos.y,hero_lv:number=1, pool_lv:number=1) {
|
load(pos: Vec3 = Vec3.ZERO,scale:number = 1,uuid:number=1001, dropToY:number = pos.y,hero_lv:number=1, pool_lv:number=1, posIndex: number = -1) {
|
||||||
// 英雄始终朝右,表现缩放固定为正向
|
// 英雄始终朝右,表现缩放固定为正向
|
||||||
scale = 1
|
scale = 1
|
||||||
// 英雄等级在当前规则下上限为 3,避免超配表范围
|
// 英雄等级在当前规则下上限为 3,避免超配表范围
|
||||||
@@ -114,6 +121,10 @@ export class Hero extends ecs.Entity {
|
|||||||
model.type = hero.type;
|
model.type = hero.type;
|
||||||
model.fac = FacSet.HERO;
|
model.fac = FacSet.HERO;
|
||||||
model.dis = hero.dis ?? 720;
|
model.dis = hero.dis ?? 720;
|
||||||
|
model.posIndex = posIndex;
|
||||||
|
if (posIndex >= 0) {
|
||||||
|
smc.mission.heroGrid[posIndex] = this.eid;
|
||||||
|
}
|
||||||
|
|
||||||
// 复制触发技能配置
|
// 复制触发技能配置
|
||||||
model.call = hero.call;
|
model.call = hero.call;
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ export class HeroAttrsComp extends ecs.Comp {
|
|||||||
minSkillDistance: number = 0; // 最近技能攻击距离(缓存,不受MP影响,用于停止位置判断)
|
minSkillDistance: number = 0; // 最近技能攻击距离(缓存,不受MP影响,用于停止位置判断)
|
||||||
|
|
||||||
// ==================== 阵型位置 ====================
|
// ==================== 阵型位置 ====================
|
||||||
|
posIndex: number = -1;
|
||||||
|
|
||||||
// ==================== 标记状态 ====================
|
// ==================== 标记状态 ====================
|
||||||
is_dead: boolean = false;
|
is_dead: boolean = false;
|
||||||
@@ -408,6 +409,7 @@ export class HeroAttrsComp extends ecs.Comp {
|
|||||||
this.maxSkillDistance = 0;
|
this.maxSkillDistance = 0;
|
||||||
this.minSkillDistance = 0;
|
this.minSkillDistance = 0;
|
||||||
|
|
||||||
|
this.posIndex = -1;
|
||||||
|
|
||||||
this.is_dead = false;
|
this.is_dead = false;
|
||||||
this.is_count_dead = false;
|
this.is_count_dead = false;
|
||||||
|
|||||||
@@ -113,6 +113,12 @@ export class Monster extends ecs.Entity {
|
|||||||
Monster.putToPool(path, view.node);
|
Monster.putToPool(path, view.node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (model && model.posIndex >= 0) {
|
||||||
|
if (smc.mission.monGrid[model.posIndex] === this.eid) {
|
||||||
|
smc.mission.monGrid[model.posIndex] = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 手动移除组件,避免 ecs 引用滞留
|
// 手动移除组件,避免 ecs 引用滞留
|
||||||
this.remove(HeroViewComp);
|
this.remove(HeroViewComp);
|
||||||
this.remove(HeroAttrsComp);
|
this.remove(HeroAttrsComp);
|
||||||
@@ -170,6 +176,10 @@ export class Monster extends ecs.Entity {
|
|||||||
model.type = hero.type;
|
model.type = hero.type;
|
||||||
model.fac = FacSet.MON;
|
model.fac = FacSet.MON;
|
||||||
model.dis = hero.dis ?? 720;
|
model.dis = hero.dis ?? 720;
|
||||||
|
model.posIndex = laneIndex;
|
||||||
|
if (laneIndex >= 0) {
|
||||||
|
smc.mission.monGrid[laneIndex] = this.eid;
|
||||||
|
}
|
||||||
|
|
||||||
// 复制触发技能配置
|
// 复制触发技能配置
|
||||||
model.call = hero.call;
|
model.call = hero.call;
|
||||||
|
|||||||
@@ -100,12 +100,17 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
|||||||
let highestAp = baseAp;
|
let highestAp = baseAp;
|
||||||
|
|
||||||
// 2. 获取场上最高攻击力的英雄,保证后期奶量/增益绝对够用
|
// 2. 获取场上最高攻击力的英雄,保证后期奶量/增益绝对够用
|
||||||
ecs.query(ecs.allOf(HeroAttrsComp)).forEach(e => {
|
for (const eid of smc.mission.heroGrid) {
|
||||||
const attr = e.get(HeroAttrsComp);
|
if (eid >= 0) {
|
||||||
if (attr && attr.fac === FacSet.HERO && !attr.is_dead && attr.ap > highestAp) {
|
const entity = ecs.getEntityByEid(eid);
|
||||||
highestAp = attr.ap;
|
if (entity) {
|
||||||
|
const attr = entity.get(HeroAttrsComp);
|
||||||
|
if (attr && !attr.is_dead && attr.ap > highestAp) {
|
||||||
|
highestAp = attr.ap;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
mockAttrs.ap = highestAp;
|
mockAttrs.ap = highestAp;
|
||||||
mockAttrs.critical = 0;
|
mockAttrs.critical = 0;
|
||||||
@@ -562,16 +567,21 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
|||||||
*/
|
*/
|
||||||
private collectFriendlyTargetEids(fac: number, selfEid: number | undefined, includeSelf: boolean): number[] {
|
private collectFriendlyTargetEids(fac: number, selfEid: number | undefined, includeSelf: boolean): number[] {
|
||||||
const eids: number[] = [];
|
const eids: number[] = [];
|
||||||
ecs.query(this.getHeroMatcher()).forEach(entity => {
|
const grid = fac === FacSet.HERO ? smc.mission.heroGrid : smc.mission.monGrid;
|
||||||
const model = entity.get(HeroAttrsComp);
|
|
||||||
const view = entity.get(HeroViewComp);
|
for (const eid of grid) {
|
||||||
if (!model || !view?.node || !view.ent) return;
|
if (eid >= 0) {
|
||||||
if (model.fac !== fac) return;
|
if (!includeSelf && typeof selfEid === "number" && eid === selfEid) continue;
|
||||||
if (model.is_dead || model.is_reviving) return;
|
|
||||||
const eid = view.ent.eid;
|
const entity = ecs.getEntityByEid(eid);
|
||||||
if (!includeSelf && typeof selfEid === "number" && eid === selfEid) return;
|
if (entity) {
|
||||||
eids.push(eid);
|
const model = entity.get(HeroAttrsComp);
|
||||||
});
|
if (model && !model.is_dead && !model.is_reviving) {
|
||||||
|
eids.push(eid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return eids;
|
return eids;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -592,119 +602,85 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
|||||||
return group === TGroup.Self;
|
return group === TGroup.Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据网格快速查找最近的敌人
|
||||||
|
* 英雄查找怪物:按列从前往后(列0 -> 列3),列内优先同排
|
||||||
|
* 怪物查找英雄:按列从前往后(列0 -> 列1),列内优先中路或同排
|
||||||
|
*/
|
||||||
|
private findNearestEnemyByGrid(heroAttrs: HeroAttrsComp, heroView: HeroViewComp, maxRange: number): HeroViewComp | null {
|
||||||
|
if (!heroView.node) return null;
|
||||||
|
const isHero = heroAttrs.fac === FacSet.HERO;
|
||||||
|
const myPosIndex = heroAttrs.posIndex;
|
||||||
|
let myRow = myPosIndex >= 0 ? myPosIndex % 3 : 1; // 默认中路
|
||||||
|
|
||||||
|
const currentX = heroView.node.position.x;
|
||||||
|
let targetView: HeroViewComp | null = null;
|
||||||
|
let minCol = -1;
|
||||||
|
|
||||||
|
if (isHero) {
|
||||||
|
// 英雄找怪物
|
||||||
|
for (let col = 0; col < 4; col++) {
|
||||||
|
// 列内顺序:优先同排,其次中路,再次其他
|
||||||
|
const rowOrder = myRow === 1 ? [1, 0, 2] : [myRow, 1, myRow === 0 ? 2 : 0];
|
||||||
|
for (const row of rowOrder) {
|
||||||
|
const idx = col * 3 + row;
|
||||||
|
const eid = smc.mission.monGrid[idx];
|
||||||
|
if (eid >= 0) {
|
||||||
|
const target = ecs.getEntityByEid(eid);
|
||||||
|
if (target) {
|
||||||
|
const tModel = target.get(HeroAttrsComp);
|
||||||
|
const tView = target.get(HeroViewComp);
|
||||||
|
if (tModel && !tModel.is_dead && !tModel.is_reviving && tView && tView.node) {
|
||||||
|
const dist = Math.abs(currentX - tView.node.position.x);
|
||||||
|
if (dist <= maxRange) {
|
||||||
|
return tView; // 找到列内最优且在射程内的目标,直接返回
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 怪物找英雄
|
||||||
|
for (let col = 0; col < 2; col++) {
|
||||||
|
// 列内顺序:怪物配置优先中路
|
||||||
|
const rowOrder = [1, myRow, myRow === 0 ? 2 : 0];
|
||||||
|
for (const row of rowOrder) {
|
||||||
|
const idx = col * 3 + row;
|
||||||
|
const eid = smc.mission.heroGrid[idx];
|
||||||
|
if (eid >= 0) {
|
||||||
|
const target = ecs.getEntityByEid(eid);
|
||||||
|
if (target) {
|
||||||
|
const tModel = target.get(HeroAttrsComp);
|
||||||
|
const tView = target.get(HeroViewComp);
|
||||||
|
if (tModel && !tModel.is_dead && !tModel.is_reviving && tView && tView.node) {
|
||||||
|
const dist = Math.abs(currentX - tView.node.position.x);
|
||||||
|
if (dist <= maxRange) {
|
||||||
|
return tView;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在施法距离内查找最近敌人。
|
* 在施法距离内查找最近敌人。
|
||||||
* 用于单体技能与基础目标参考。
|
* 替换为网格化查找,大幅提升性能并解决同排优先问题。
|
||||||
* 考虑三路设计:同路(Y差较小)优先,如果同路没有目标再考虑跨路
|
|
||||||
*/
|
*/
|
||||||
private findNearestEnemyInRange(heroAttrs: HeroAttrsComp, heroView: HeroViewComp, maxRange: number): HeroViewComp | null {
|
private findNearestEnemyInRange(heroAttrs: HeroAttrsComp, heroView: HeroViewComp, maxRange: number): HeroViewComp | null {
|
||||||
if (!heroView.node) return null;
|
return this.findNearestEnemyByGrid(heroAttrs, heroView, maxRange);
|
||||||
const currentX = heroView.node.position.x;
|
|
||||||
const currentY = heroView.node.position.y;
|
|
||||||
let nearest: HeroViewComp | null = null;
|
|
||||||
let minDist = Infinity;
|
|
||||||
let foundPreferredLane = false;
|
|
||||||
const isHero = heroAttrs.fac === FacSet.HERO;
|
|
||||||
|
|
||||||
ecs.query(this.getHeroMatcher()).forEach(entity => {
|
|
||||||
const attrs = entity.get(HeroAttrsComp);
|
|
||||||
const view = entity.get(HeroViewComp);
|
|
||||||
if (!attrs || !view?.node) return;
|
|
||||||
if (attrs.fac === heroAttrs.fac) return;
|
|
||||||
if (attrs.is_dead || attrs.is_reviving) return;
|
|
||||||
if (this.isOutOfBattleBounds(view.node.position.x)) return;
|
|
||||||
|
|
||||||
const distX = Math.abs(currentX - view.node.position.x);
|
|
||||||
if (distX > maxRange) return;
|
|
||||||
|
|
||||||
if (isHero) {
|
|
||||||
// 英雄:单纯找X轴最近,无视路线
|
|
||||||
if (distX < minDist) {
|
|
||||||
minDist = distX;
|
|
||||||
nearest = view;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 怪物:优先找中路目标
|
|
||||||
const isMidLane = Math.abs(view.node.position.y - BoxSet.GAME_LINE) < 30; // BoxSet.GAME_LINE(100) 是中路
|
|
||||||
|
|
||||||
if (foundPreferredLane && !isMidLane) return;
|
|
||||||
|
|
||||||
if (isMidLane && !foundPreferredLane) {
|
|
||||||
foundPreferredLane = true;
|
|
||||||
minDist = distX;
|
|
||||||
nearest = view;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (distX >= minDist) return;
|
|
||||||
minDist = distX;
|
|
||||||
nearest = view;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return nearest;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在施法距离内查找“最前排”敌人。
|
* 在施法距离内查找“最前排”敌人。
|
||||||
* 依据施法者面向方向选择 x 轴上更前的目标。
|
* 依据网格排布,列0天然是最前排,因此直接复用网格查找即可。
|
||||||
* 考虑三路设计:英雄找最前排,怪物优先找中路最前排
|
|
||||||
*/
|
*/
|
||||||
private findFrontEnemyInRange(heroAttrs: HeroAttrsComp, heroView: HeroViewComp, maxRange: number, nearestEnemy: HeroViewComp): HeroViewComp | null {
|
private findFrontEnemyInRange(heroAttrs: HeroAttrsComp, heroView: HeroViewComp, maxRange: number, nearestEnemy: HeroViewComp): HeroViewComp | null {
|
||||||
if (!heroView.node || !nearestEnemy.node) return null;
|
return this.findNearestEnemyByGrid(heroAttrs, heroView, maxRange);
|
||||||
const currentX = heroView.node.position.x;
|
|
||||||
const currentY = heroView.node.position.y;
|
|
||||||
const direction = nearestEnemy.node.position.x >= currentX ? 1 : -1;
|
|
||||||
let frontEnemy: HeroViewComp | null = null;
|
|
||||||
let edgeX = direction > 0 ? Infinity : -Infinity;
|
|
||||||
let foundPreferredLane = false;
|
|
||||||
const isHero = heroAttrs.fac === FacSet.HERO;
|
|
||||||
|
|
||||||
ecs.query(this.getHeroMatcher()).forEach(entity => {
|
|
||||||
const attrs = entity.get(HeroAttrsComp);
|
|
||||||
const view = entity.get(HeroViewComp);
|
|
||||||
if (!attrs || !view?.node) return;
|
|
||||||
if (attrs.fac === heroAttrs.fac) return;
|
|
||||||
if (attrs.is_dead || attrs.is_reviving) return;
|
|
||||||
const enemyX = view.node.position.x;
|
|
||||||
if (this.isOutOfBattleBounds(enemyX)) return;
|
|
||||||
|
|
||||||
const dist = Math.abs(currentX - enemyX);
|
|
||||||
if (dist > maxRange) return;
|
|
||||||
|
|
||||||
if (isHero) {
|
|
||||||
// 英雄:无视路线,找X轴最前的
|
|
||||||
if (direction > 0) {
|
|
||||||
if (enemyX >= edgeX) return;
|
|
||||||
edgeX = enemyX;
|
|
||||||
} else {
|
|
||||||
if (enemyX <= edgeX) return;
|
|
||||||
edgeX = enemyX;
|
|
||||||
}
|
|
||||||
frontEnemy = view;
|
|
||||||
} else {
|
|
||||||
// 怪物:优先找中路最前排的
|
|
||||||
const isMidLane = Math.abs(view.node.position.y - BoxSet.GAME_LINE) < 30; // BoxSet.GAME_LINE(100) 是中路
|
|
||||||
|
|
||||||
if (foundPreferredLane && !isMidLane) return;
|
|
||||||
|
|
||||||
if (isMidLane && !foundPreferredLane) {
|
|
||||||
foundPreferredLane = true;
|
|
||||||
edgeX = enemyX;
|
|
||||||
frontEnemy = view;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (direction > 0) {
|
|
||||||
if (enemyX >= edgeX) return;
|
|
||||||
edgeX = enemyX;
|
|
||||||
} else {
|
|
||||||
if (enemyX <= edgeX) return;
|
|
||||||
edgeX = enemyX;
|
|
||||||
}
|
|
||||||
frontEnemy = view;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return frontEnemy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -132,11 +132,14 @@ export class MissionHeroComp extends CCComp {
|
|||||||
if (model && view) {
|
if (model && view) {
|
||||||
if (model.is_dead) {
|
if (model.is_dead) {
|
||||||
view.alive();
|
view.alive();
|
||||||
const landingPos = this.pickPositionForHero([hero.eid]);
|
const posIndex = this.pickPositionIndexForHero([hero.eid]);
|
||||||
|
const landingPos = MissionHeroComp.HERO_POSITIONS[posIndex];
|
||||||
// 不再直接设置位置,而是播放下落入场动画
|
// 不再直接设置位置,而是播放下落入场动画
|
||||||
// 计算出出生点(空中)
|
// 计算出出生点(空中)
|
||||||
const spawnPos: Vec3 = v3(landingPos.x, landingPos.y + MissionHeroComp.HERO_DROP_HEIGHT, 0);
|
const spawnPos: Vec3 = v3(landingPos.x, landingPos.y + MissionHeroComp.HERO_DROP_HEIGHT, 0);
|
||||||
view.node.setPosition(spawnPos);
|
view.node.setPosition(spawnPos);
|
||||||
|
model.posIndex = posIndex;
|
||||||
|
if (posIndex >= 0) smc.mission.heroGrid[posIndex] = hero.eid;
|
||||||
hero.playDropAnim(spawnPos, landingPos.y);
|
hero.playDropAnim(spawnPos, landingPos.y);
|
||||||
}
|
}
|
||||||
model.dirty_hp = true;
|
model.dirty_hp = true;
|
||||||
@@ -171,7 +174,7 @@ export class MissionHeroComp extends CCComp {
|
|||||||
* 动态分配英雄上场的位置
|
* 动态分配英雄上场的位置
|
||||||
* @param excludeEids 排除计算的实体ID数组(避免复活或合成时把自己算成占据的位置)
|
* @param excludeEids 排除计算的实体ID数组(避免复活或合成时把自己算成占据的位置)
|
||||||
*/
|
*/
|
||||||
private pickPositionForHero(excludeEids: number[] = []): Vec3 {
|
private pickPositionIndexForHero(excludeEids: number[] = []): number {
|
||||||
const heroes = this.getAllHeroes().filter(h => {
|
const heroes = this.getAllHeroes().filter(h => {
|
||||||
const m = h.get(HeroAttrsComp);
|
const m = h.get(HeroAttrsComp);
|
||||||
return m && !m.is_dead && !excludeEids.includes(h.eid);
|
return m && !m.is_dead && !excludeEids.includes(h.eid);
|
||||||
@@ -179,28 +182,20 @@ export class MissionHeroComp extends CCComp {
|
|||||||
|
|
||||||
const occupied = new Set<number>();
|
const occupied = new Set<number>();
|
||||||
for (const h of heroes) {
|
for (const h of heroes) {
|
||||||
const move = h.get(MoveComp); // MoveComp 记录了英雄当前的目标位置
|
const m = h.get(HeroAttrsComp);
|
||||||
if (move) {
|
if (m && m.posIndex >= 0) occupied.add(m.posIndex);
|
||||||
for (let i = 0; i < MissionHeroComp.HERO_POSITIONS.length; i++) {
|
|
||||||
const pos = MissionHeroComp.HERO_POSITIONS[i];
|
|
||||||
if (Math.abs(move.targetX - pos.x) < 2 && Math.abs(move.baseY - pos.y) < 2) {
|
|
||||||
occupied.add(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 优先中前(1) -> 上前(0) -> 下前(2) -> 中后(4) -> 上后(3) -> 下后(5)
|
// 优先中前(1) -> 上前(0) -> 下前(2) -> 中后(4) -> 上后(3) -> 下后(5)
|
||||||
const slotPriority = [1, 0, 2, 4, 3, 5];
|
const slotPriority = [1, 0, 2, 4, 3, 5];
|
||||||
for (const idx of slotPriority) {
|
for (const idx of slotPriority) {
|
||||||
if (!occupied.has(idx)) {
|
if (!occupied.has(idx)) {
|
||||||
return MissionHeroComp.HERO_POSITIONS[idx];
|
return idx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 溢出:默认中前
|
// 溢出:默认中前
|
||||||
return MissionHeroComp.HERO_POSITIONS[1];
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -217,9 +212,10 @@ export class MissionHeroComp extends CCComp {
|
|||||||
console.log("addHero uuid:",uuid)
|
console.log("addHero uuid:",uuid)
|
||||||
let hero = ecs.getEntity<Hero>(Hero);
|
let hero = ecs.getEntity<Hero>(Hero);
|
||||||
let scale = 1
|
let scale = 1
|
||||||
const landingPos = this.pickPositionForHero();
|
const posIndex = this.pickPositionIndexForHero();
|
||||||
|
const landingPos = MissionHeroComp.HERO_POSITIONS[posIndex];
|
||||||
let spawnPos:Vec3 = v3(landingPos.x, landingPos.y + MissionHeroComp.HERO_DROP_HEIGHT, 0);
|
let spawnPos:Vec3 = v3(landingPos.x, landingPos.y + MissionHeroComp.HERO_DROP_HEIGHT, 0);
|
||||||
hero.load(spawnPos,scale,uuid,landingPos.y,hero_lv,pool_lv);
|
hero.load(spawnPos,scale,uuid,landingPos.y,hero_lv,pool_lv,posIndex);
|
||||||
|
|
||||||
// 召唤完成后,派发事件以更新英雄面板
|
// 召唤完成后,派发事件以更新英雄面板
|
||||||
const model = hero.get(HeroAttrsComp);
|
const model = hero.get(HeroAttrsComp);
|
||||||
@@ -246,14 +242,19 @@ export class MissionHeroComp extends CCComp {
|
|||||||
* @param targetPos 指定生成位置
|
* @param targetPos 指定生成位置
|
||||||
* @returns 实际生成的英雄等级
|
* @returns 实际生成的英雄等级
|
||||||
*/
|
*/
|
||||||
private addMergedHero(uuid:number, hero_lv:number, pool_lv:number, ap:number, hp_max:number, targetPos?: Vec3): number {
|
private addMergedHero(uuid:number, hero_lv:number, pool_lv:number, ap:number, hp_max:number, targetPosIndex?: number, targetPos?: Vec3): number {
|
||||||
console.log("addMergedHero uuid:",uuid)
|
console.log("addMergedHero uuid:",uuid)
|
||||||
let hero = ecs.getEntity<Hero>(Hero);
|
let hero = ecs.getEntity<Hero>(Hero);
|
||||||
let scale = 1
|
let scale = 1
|
||||||
|
|
||||||
const landingPos = targetPos || this.pickPositionForHero();
|
let posIndex = targetPosIndex;
|
||||||
|
let landingPos = targetPos;
|
||||||
|
if (posIndex === undefined || posIndex < 0 || !landingPos) {
|
||||||
|
posIndex = this.pickPositionIndexForHero();
|
||||||
|
landingPos = MissionHeroComp.HERO_POSITIONS[posIndex];
|
||||||
|
}
|
||||||
let spawnPos:Vec3 = v3(landingPos.x, landingPos.y + MissionHeroComp.HERO_DROP_HEIGHT, 0);
|
let spawnPos:Vec3 = v3(landingPos.x, landingPos.y + MissionHeroComp.HERO_DROP_HEIGHT, 0);
|
||||||
hero.load(spawnPos,scale,uuid,landingPos.y,hero_lv,pool_lv);
|
hero.load(spawnPos,scale,uuid,landingPos.y,hero_lv,pool_lv,posIndex);
|
||||||
|
|
||||||
// 召唤完成后,派发事件以更新英雄面板
|
// 召唤完成后,派发事件以更新英雄面板
|
||||||
const model = hero.get(HeroAttrsComp);
|
const model = hero.get(HeroAttrsComp);
|
||||||
@@ -481,14 +482,14 @@ export class MissionHeroComp extends CCComp {
|
|||||||
sumHpMax += model.hp_max;
|
sumHpMax += model.hp_max;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算目标出生点(提前排除素材英雄所占的位置)
|
const posIndex = this.pickPositionIndexForHero(mergeEids);
|
||||||
const landingPos = this.pickPositionForHero(mergeEids);
|
const landingPos = MissionHeroComp.HERO_POSITIONS[posIndex];
|
||||||
const spawnPos:Vec3 = v3(landingPos.x, landingPos.y + MissionHeroComp.HERO_DROP_HEIGHT, 0);
|
const spawnPos:Vec3 = v3(landingPos.x, landingPos.y + MissionHeroComp.HERO_DROP_HEIGHT, 0);
|
||||||
|
|
||||||
// 汇聚 → 特效 → 生成
|
// 汇聚 → 特效 → 生成
|
||||||
await this.mergeDestroyAtBirth(mergeHeroes, spawnPos);
|
await this.mergeDestroyAtBirth(mergeHeroes, spawnPos);
|
||||||
await this.playMergeBoomFx(spawnPos);
|
await this.playMergeBoomFx(spawnPos);
|
||||||
return this.addMergedHero(uuid, Math.min(this.merge_max_lv, hero_lv + 1), pool_lv, sumAp, sumHpMax, landingPos);
|
return this.addMergedHero(uuid, Math.min(this.merge_max_lv, hero_lv + 1), pool_lv, sumAp, sumHpMax, posIndex, landingPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -544,7 +544,7 @@ export const InfiniteModeConfig = {
|
|||||||
*/
|
*/
|
||||||
export const TestModeConfig = {
|
export const TestModeConfig = {
|
||||||
/** 是否开启单挑测试模式 */
|
/** 是否开启单挑测试模式 */
|
||||||
enable: true,
|
enable: false,
|
||||||
/** 测试模式中生成怪物的基础生命值 (对应 1级 英雄) */
|
/** 测试模式中生成怪物的基础生命值 (对应 1级 英雄) */
|
||||||
baseHp: 150,
|
baseHp: 150,
|
||||||
/** 测试模式中生成怪物的基础攻击力 (对应 1级 英雄) */
|
/** 测试模式中生成怪物的基础攻击力 (对应 1级 英雄) */
|
||||||
|
|||||||
Reference in New Issue
Block a user