refactor(map/hero): 重构英雄位置管理逻辑,移除lane相关字段
重构了英雄分路排位的旧实现,改用硬编码的点位数组管理英雄站位,移除了HeroAttrsComp中的lane和lane_index字段,简化了英雄位置分配、UI面板绑定的逻辑,提升代码可维护性。
This commit is contained in:
@@ -39,6 +39,7 @@ import { FacSet, FightSet, BoxSet } from "../common/config/GameSet";
|
||||
import { oneCom } from "../skill/oncend";
|
||||
import { HeroViewComp } from "../hero/HeroViewComp";
|
||||
import { FieldSkillSet, FieldSkillType } from "../common/config/SkillSet";
|
||||
import { MoveComp } from "../hero/MoveComp";
|
||||
const { ccclass } = _decorator;
|
||||
|
||||
/**
|
||||
@@ -52,18 +53,18 @@ const { ccclass } = _decorator;
|
||||
export class MissionHeroCompComp extends CCComp {
|
||||
// ======================== 常量 ========================
|
||||
|
||||
/** 硬编码的6个英雄占位点 */
|
||||
public static readonly HERO_POSITIONS: Vec3[] = [
|
||||
v3(-200, BoxSet.GAME_LINE + 100, 0), // index 0 (node_index 1): Top Front
|
||||
v3(-200, BoxSet.GAME_LINE, 0), // index 1 (node_index 2): Mid Front
|
||||
v3(-200, BoxSet.GAME_LINE - 100, 0), // index 2 (node_index 3): Bot Front
|
||||
v3(-300, BoxSet.GAME_LINE + 100, 0), // index 3 (node_index 4): Top Back
|
||||
v3(-300, BoxSet.GAME_LINE, 0), // index 4 (node_index 5): Mid Back
|
||||
v3(-300, BoxSet.GAME_LINE - 100, 0), // index 5 (node_index 6): Bot Back
|
||||
];
|
||||
|
||||
/** 英雄出生时的掉落高度(从空中落到地面的像素差) */
|
||||
private static readonly HERO_DROP_HEIGHT = 260
|
||||
/** 近战英雄起始出生 X 坐标 */
|
||||
private static readonly HERO_SPAWN_START_MELEE_X = -320
|
||||
/** 远程(含中程)英雄起始出生 X 坐标 */
|
||||
private static readonly HERO_SPAWN_START_RANGED_X = -320
|
||||
/** 三路高度偏移(上路, 中路, 下路) */
|
||||
private static readonly HERO_LANE_Y_OFFSETS = [ BoxSet.GAME_LINE+90, BoxSet.GAME_LINE, BoxSet.GAME_LINE-90]
|
||||
/** 每路前排容量 */
|
||||
private static readonly HERO_LANE_CAP = 2
|
||||
/** 同路内 X 间距 */
|
||||
private static readonly HERO_GAP_X = 100
|
||||
|
||||
// ======================== 运行时属性 ========================
|
||||
|
||||
@@ -131,10 +132,7 @@ export class MissionHeroCompComp extends CCComp {
|
||||
if (model && view) {
|
||||
if (model.is_dead) {
|
||||
view.alive();
|
||||
const { lane, indexInLane } = this.pickLaneForHero(model.hero_uuid, [hero.eid]);
|
||||
model.lane = lane;
|
||||
model.lane_index = indexInLane;
|
||||
const landingPos = this.resolveHeroLandingPos(model.hero_uuid, lane, indexInLane);
|
||||
const landingPos = this.pickPositionForHero([hero.eid]);
|
||||
// 不再直接设置位置,而是播放下落入场动画
|
||||
// 计算出出生点(空中)
|
||||
const spawnPos: Vec3 = v3(landingPos.x, landingPos.y + MissionHeroCompComp.HERO_DROP_HEIGHT, 0);
|
||||
@@ -170,43 +168,39 @@ export class MissionHeroCompComp extends CCComp {
|
||||
// ======================== 英雄生成 ========================
|
||||
|
||||
/**
|
||||
* 动态分配英雄上场的路和排位(优先中路 -> 上路 -> 下路)
|
||||
* 标记英雄的6个登录点
|
||||
* @param uuid 英雄 UUID
|
||||
* 动态分配英雄上场的位置
|
||||
* @param excludeEids 排除计算的实体ID数组(避免复活或合成时把自己算成占据的位置)
|
||||
*/
|
||||
private pickLaneForHero(uuid: number, excludeEids: number[] = []): { lane: number; indexInLane: number } {
|
||||
private pickPositionForHero(excludeEids: number[] = []): Vec3 {
|
||||
const heroes = this.getAllHeroes().filter(h => {
|
||||
const m = h.get(HeroAttrsComp);
|
||||
return m && !m.is_dead && !excludeEids.includes(h.eid);
|
||||
});
|
||||
|
||||
// 记录6个位置点的占用情况 [lane][indexInLane]
|
||||
const occupied = [
|
||||
[false, false], // 上路 0
|
||||
[false, false], // 中路 1
|
||||
[false, false] // 下路 2
|
||||
];
|
||||
|
||||
const occupied = new Set<number>();
|
||||
for (const h of heroes) {
|
||||
const m = h.get(HeroAttrsComp);
|
||||
if (m && m.lane >= 0 && m.lane <= 2 && m.lane_index >= 0 && m.lane_index <= 1) {
|
||||
occupied[m.lane][m.lane_index] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 优先中路(1) -> 上路(0) -> 下路(2)
|
||||
const priority = [1, 0, 2];
|
||||
for (let indexInLane = 0; indexInLane < MissionHeroCompComp.HERO_LANE_CAP; indexInLane++) {
|
||||
for (const lane of priority) {
|
||||
if (!occupied[lane][indexInLane]) {
|
||||
return { lane, indexInLane };
|
||||
const move = h.get(MoveComp); // MoveComp 记录了英雄当前的目标位置
|
||||
if (move) {
|
||||
for (let i = 0; i < MissionHeroCompComp.HERO_POSITIONS.length; i++) {
|
||||
const pos = MissionHeroCompComp.HERO_POSITIONS[i];
|
||||
if (Math.abs(move.targetX - pos.x) < 2 && Math.abs(move.baseY - pos.y) < 2) {
|
||||
occupied.add(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 溢出:仍放中路,沿 X 继续排
|
||||
return { lane: 1, indexInLane: 2 };
|
||||
// 优先中前(1) -> 上前(0) -> 下前(2) -> 中后(4) -> 上后(3) -> 下后(5)
|
||||
const slotPriority = [1, 0, 2, 4, 3, 5];
|
||||
for (const idx of slotPriority) {
|
||||
if (!occupied.has(idx)) {
|
||||
return MissionHeroCompComp.HERO_POSITIONS[idx];
|
||||
}
|
||||
}
|
||||
|
||||
// 溢出:默认中前
|
||||
return MissionHeroCompComp.HERO_POSITIONS[1];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,16 +217,13 @@ export class MissionHeroCompComp extends CCComp {
|
||||
console.log("addHero uuid:",uuid)
|
||||
let hero = ecs.getEntity<Hero>(Hero);
|
||||
let scale = 1
|
||||
const { lane, indexInLane } = this.pickLaneForHero(uuid);
|
||||
const landingPos = this.resolveHeroLandingPos(uuid, lane, indexInLane);
|
||||
const landingPos = this.pickPositionForHero();
|
||||
let spawnPos:Vec3 = v3(landingPos.x, landingPos.y + MissionHeroCompComp.HERO_DROP_HEIGHT, 0);
|
||||
hero.load(spawnPos,scale,uuid,landingPos.y,hero_lv,pool_lv);
|
||||
|
||||
// 召唤完成后,派发事件以更新英雄面板
|
||||
const model = hero.get(HeroAttrsComp);
|
||||
if (model) {
|
||||
model.lane = lane;
|
||||
model.lane_index = indexInLane;
|
||||
oops.message.dispatchEvent(GameEvent.MasterCalled, {
|
||||
eid: hero.eid,
|
||||
model: model
|
||||
@@ -242,33 +233,7 @@ export class MissionHeroCompComp extends CCComp {
|
||||
return hero;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算英雄落点位置。
|
||||
* Y 坐标来自 HeroPos 配置,X 坐标根据英雄类型(近战/远程)决定。
|
||||
*
|
||||
* @param uuid 英雄 UUID
|
||||
* @param lane 分配到的路 (0: 上, 1: 中, 2: 下)
|
||||
* @param indexInLane 该路排位
|
||||
* @returns 落点 Vec3
|
||||
*/
|
||||
private resolveHeroLandingPos(uuid: number, lane: number, indexInLane: number): Vec3 {
|
||||
const hero_pos = 0;
|
||||
const baseY = HeroPos[hero_pos].pos.y + MissionHeroCompComp.HERO_LANE_Y_OFFSETS[lane];
|
||||
const startX = this.resolveSpawnStartX(uuid);
|
||||
return v3(startX + indexInLane * MissionHeroCompComp.HERO_GAP_X, baseY, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据英雄类型决定出生 X 坐标。
|
||||
* @param uuid 英雄 UUID
|
||||
* @returns 近战 or 远程的起始 X
|
||||
*/
|
||||
private resolveSpawnStartX(uuid: number): number {
|
||||
const heroType = HeroInfo[uuid]?.type;
|
||||
return heroType === HType.Melee
|
||||
? MissionHeroCompComp.HERO_SPAWN_START_MELEE_X
|
||||
: MissionHeroCompComp.HERO_SPAWN_START_RANGED_X;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成合成后的高级英雄,并覆盖为聚合后的属性。
|
||||
@@ -278,33 +243,21 @@ export class MissionHeroCompComp extends CCComp {
|
||||
* @param pool_lv 卡池等级
|
||||
* @param ap 聚合后攻击力
|
||||
* @param hp_max 聚合后最大生命值
|
||||
* @param targetLane 指定生成路
|
||||
* @param targetIndex 指定该路排位
|
||||
* @param targetPos 指定生成位置
|
||||
* @returns 实际生成的英雄等级
|
||||
*/
|
||||
private addMergedHero(uuid:number, hero_lv:number, pool_lv:number, ap:number, hp_max:number, targetLane?: number, targetIndex?: number): number {
|
||||
private addMergedHero(uuid:number, hero_lv:number, pool_lv:number, ap:number, hp_max:number, targetPos?: Vec3): number {
|
||||
console.log("addMergedHero uuid:",uuid)
|
||||
let hero = ecs.getEntity<Hero>(Hero);
|
||||
let scale = 1
|
||||
|
||||
// 如果未指定路,则按普通添加英雄处理
|
||||
let lane = targetLane;
|
||||
let indexInLane = targetIndex;
|
||||
if (lane === undefined || indexInLane === undefined) {
|
||||
const res = this.pickLaneForHero(uuid);
|
||||
lane = res.lane;
|
||||
indexInLane = res.indexInLane;
|
||||
}
|
||||
|
||||
const landingPos = this.resolveHeroLandingPos(uuid, lane, indexInLane);
|
||||
const landingPos = targetPos || this.pickPositionForHero();
|
||||
let spawnPos:Vec3 = v3(landingPos.x, landingPos.y + MissionHeroCompComp.HERO_DROP_HEIGHT, 0);
|
||||
hero.load(spawnPos,scale,uuid,landingPos.y,hero_lv,pool_lv);
|
||||
|
||||
// 召唤完成后,派发事件以更新英雄面板
|
||||
const model = hero.get(HeroAttrsComp);
|
||||
if (model) {
|
||||
model.lane = lane;
|
||||
model.lane_index = indexInLane;
|
||||
model.ap = Math.max(0, ap);
|
||||
model.hp_max = Math.max(1, hp_max);
|
||||
model.hp = model.hp_max;
|
||||
@@ -540,14 +493,13 @@ export class MissionHeroCompComp extends CCComp {
|
||||
}
|
||||
|
||||
// 计算目标出生点(提前排除素材英雄所占的位置)
|
||||
const { lane, indexInLane } = this.pickLaneForHero(uuid, mergeEids);
|
||||
const landingPos = this.resolveHeroLandingPos(uuid, lane, indexInLane);
|
||||
const landingPos = this.pickPositionForHero(mergeEids);
|
||||
const spawnPos:Vec3 = v3(landingPos.x, landingPos.y + MissionHeroCompComp.HERO_DROP_HEIGHT, 0);
|
||||
|
||||
// 汇聚 → 特效 → 生成
|
||||
await this.mergeDestroyAtBirth(mergeHeroes, spawnPos);
|
||||
await this.playMergeBoomFx(spawnPos);
|
||||
return this.addMergedHero(uuid, Math.min(this.merge_max_lv, hero_lv + 1), pool_lv, sumAp, sumHpMax, lane, indexInLane);
|
||||
return this.addMergedHero(uuid, Math.min(this.merge_max_lv, hero_lv + 1), pool_lv, sumAp, sumHpMax, landingPos);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user