角色视图 数据逻辑 依ecs 框架进行重构

This commit is contained in:
2025-10-30 08:56:37 +08:00
parent edb7f23918
commit a79cb9f35d
10 changed files with 1274 additions and 602 deletions

View File

@@ -0,0 +1,90 @@
import { _decorator, EventTarget } from 'cc';
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
const { ccclass } = _decorator;
/** 事件总线组件 - 简化版,仅提供基本的发布订阅功能 */
@ecs.register('EBusComp', false)
export class EBusComp extends CCComp {
/** 内部事件目标 */
private _eventTarget: EventTarget;
/** 获取事件目标 */
private get eventTarget(): EventTarget {
if (!this._eventTarget) {
this._eventTarget = new EventTarget();
}
return this._eventTarget;
}
start() {
// 组件启动时的初始化逻辑
}
/**
* 发布事件
* @param event 事件名称
* @param data 事件数据
*/
public emit(event: string, data?: any): void {
this.eventTarget.emit(event, data);
}
/**
* 订阅事件
* @param event 事件名称
* @param callback 回调函数
* @param target 回调函数的this指向
*/
public on(event: string, listener: (...args: any[]) => void, object?: any): void {
this.eventTarget.on(event, listener, object);
}
/**
* 取消订阅事件
* @param event 事件名称
* @param callback 回调函数
* @param target 回调函数的this指向
*/
public off(event?: string, callback?: (data?: any) => void, target?: any): void {
if (arguments.length === 0) {
// 无参调用:清理本节点所有监听
this.eventTarget.targetOff(this);
} else if (arguments.length === 1) {
// 仅提供 event清理该事件名下所有回调
this.eventTarget.targetOff(event);
} else {
// 完整参数:精确移除指定回调
this.eventTarget.off(event!, callback!, target);
}
}
/**
* 订阅一次性事件
* @param event 事件名称
* @param callback 回调函数
* @param target 回调函数的this指向
*/
public once(event: string, callback: (data?: any) => void, target?: any): void {
this.eventTarget.once(event, callback, target);
}
/**
* 取消所有事件监听
*/
public targetOff(target: any): void {
this.eventTarget.targetOff(target);
}
reset() {
// 清理所有事件监听
if (this._eventTarget) {
this._eventTarget.targetOff(this);
}
// 组件删除时触发自定义释放逻辑
this.node.destroy();
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "b44b7a75-de7d-4aa1-ad20-78e6690926c0",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -12,18 +12,20 @@ import { SkillSet } from "../common/config/SkillSet";
import { time } from "console"; import { time } from "console";
import { getNeAttrs, getAttrs ,Attrs} from "../common/config/HeroAttrs"; import { getNeAttrs, getAttrs ,Attrs} from "../common/config/HeroAttrs";
import { TalComp } from "./TalComp"; import { TalComp } from "./TalComp";
import { EBusComp } from "./EBusComp";
/** 角色实体 */ /** 角色实体 */
@ecs.register(`Hero`) @ecs.register(`Hero`)
export class Hero extends ecs.Entity { export class Hero extends ecs.Entity {
HeroModel!: HeroModelComp; HeroModel!: HeroModelComp;
HeroView!: HeroViewComp; View!: HeroViewComp;
BattleMove!: BattleMoveComp; BattleMove!: BattleMoveComp;
protected init() { protected init() {
this.addComponents<ecs.Comp>( this.addComponents<ecs.Comp>(
BattleMoveComp, BattleMoveComp,
HeroModelComp, HeroModelComp,
TalComp TalComp,
EBusComp,
); );
} }
@@ -31,6 +33,7 @@ export class Hero extends ecs.Entity {
this.remove(HeroViewComp); this.remove(HeroViewComp);
this.remove(HeroModelComp); this.remove(HeroModelComp);
this.remove(TalComp); this.remove(TalComp);
this.remove(EBusComp);
super.destroy(); super.destroy();
} }
@@ -49,7 +52,56 @@ export class Hero extends ecs.Entity {
node.parent = scene.entityLayer!.node! node.parent = scene.entityLayer!.node!
node.setPosition(pos) node.setPosition(pos)
// console.log("hero load",pos) // console.log("hero load",pos)
var hv = this.hero_init(uuid,node) var hv = node.getComponent(HeroViewComp)!;
const model = this.get(HeroModelComp);
let hero = HeroInfo[uuid]; // 共用英雄数据
// 设置 View 层属性(表现相关)
hv.scale = 1;
hv.box_group = BoxSet.HERO;
// 设置 Model 层属性(数据相关)
model.hero_uuid = uuid;
model.hero_name = hero.name;
model.lv = hero.lv ? hero.lv : 1;
model.type = hero.type;
model.fac = FacSet.HERO;
model.is_master = true;
// 设置技能
for(let i=0; i<hero.skills.length; i++){
let skill = {
uuid: SkillSet[hero.skills[i]].uuid,
cd_max: SkillSet[hero.skills[i]].cd,
cost: SkillSet[hero.skills[i]].cost,
cd: 0
};
model.skills.push(skill);
}
// 设置基础属性
model.base_ap = hero.ap;
model.base_map = hero.mp;
model.base_def = hero.def;
model.base_hp = hero.hp;
model.base_mp = hero.mp;
model.base_dis = hero.dis;
model.base_speed = hero.speed;
// 初始化属性数组
model.Attrs = getAttrs();
model.NeAttrs = getNeAttrs();
model.hp = model.Attrs[Attrs.HP_MAX] = model.base_hp;
model.mp = model.Attrs[Attrs.MP_MAX] = model.base_mp;
model.Attrs[Attrs.DEF] = model.base_def;
model.Attrs[Attrs.AP] = model.base_ap;
model.Attrs[Attrs.MAP] = model.base_map;
model.Attrs[Attrs.SPEED] = hero.speed;
model.Attrs[Attrs.DIS] = hero.dis;
// 初始化 buff/debuff 系统
model.initAttrs();
this.add(hv); this.add(hv);
oops.message.dispatchEvent(GameEvent.MasterCalled,{uuid:uuid}) oops.message.dispatchEvent(GameEvent.MasterCalled,{uuid:uuid})
const move = this.get(BattleMoveComp); const move = this.get(BattleMoveComp);
@@ -63,40 +115,28 @@ export class Hero extends ecs.Entity {
} }
smc.vmdata.mission_data.hero_num++ smc.vmdata.mission_data.hero_num++
} }
hero_init(uuid:number=1001,node:Node) {
var hv = node.getComponent(HeroViewComp)!; reset() {
let hero= HeroInfo[uuid] // 共用英雄数据 // 注: 自定义释放逻辑,视图层实现 ecs.IComp 接口的 ecs 组件需要手动释放
hv.scale = 1; super.destroy();
hv.is_master=true; }
hv.lv=hero.lv?hero.lv:1
hv.fac = FacSet.HERO;
hv.type = hero.type;
hv.box_group = BoxSet.HERO;
hv.hero_uuid= uuid;
hv.hero_name= hero.name;
for(let i=0;i<hero.skills.length;i++){
let skill={ uuid:SkillSet[hero.skills[i]].uuid, cd_max:SkillSet[hero.skills[i]].cd,cost:SkillSet[hero.skills[i]].cost,cd:0 }
hv.skills.push(skill)
}
hv.base_ap=hero.ap
hv.base_map=hero.mp
hv.base_def=hero.def
hv.base_hp=hero.hp
hv.base_mp=hero.mp
hv.base_dis=hero.dis
hv.base_speed=hero.speed
hv.Attrs=getAttrs()
hv.NeAttrs=getNeAttrs()
hv.hp=hv.Attrs[Attrs.HP_MAX]=hv.base_hp
hv.mp=hv.Attrs[Attrs.MP_MAX]=hv.base_mp
hv.Attrs[Attrs.DEF]=hv.base_def
hv.Attrs[Attrs.AP]=hv.base_ap
hv.Attrs[Attrs.MAP]=hv.base_map
hv.Attrs[Attrs.SPEED]=hero.speed
hv.Attrs[Attrs.DIS]=hero.dis
// 初始化 buff/debuff 系统 }
hv.initAttrs();
return hv export class HeroLifecycleSystem extends ecs.ComblockSystem
implements ecs.IEntityEnterSystem, ecs.IEntityRemoveSystem {
filter() {
return ecs.allOf(HeroModelComp);
}
entityEnter(e: ecs.Entity): void {
// 英雄实体创建时的特殊处理
console.log(`英雄进入世界: ${e.get(HeroModelComp).hero_name}`);
}
entityRemove(e: ecs.Entity): void {
// 英雄实体销毁时的清理工作
console.log(`英雄离开世界: ${e.get(HeroModelComp).hero_name}`);
} }
} }

View File

@@ -1,12 +1,724 @@
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { Attrs, AttrsType, BType, NeAttrs } from "../common/config/HeroAttrs";
import { BuffConf, SkillSet } from "../common/config/SkillSet";
import { HeroInfo, AttrSet, HeroUpSet } from "../common/config/heroSet";
import { MonModelComp } from "./MonModelComp";
import { FacSet } from "../common/config/BoxSet";
import { FightSet } from "../common/config/Mission";
/**
* 角色属性数据
*/
@ecs.register('HeroModel') @ecs.register('HeroModel')
export class HeroModelComp extends ecs.Comp { export class HeroModelComp extends ecs.Comp {
// ==================== 角色基础信息 ====================
hero_uuid: number = 1001;
hero_name: string = "hero";
lv: number = 1;
type: number = 0; // 0近战 1远程 2辅助
fac: number = 0; // 0:hero 1:monster
// ==================== 基础属性(有初始值) ====================
base_ap: number = 0; // 基础攻击
base_map: number = 0; // 基础魔法攻击
base_def: number = 5; // 基础防御
base_hp: number = 100; // 基础血量
base_mp: number = 100; // 基础魔法值
base_speed: number = 100; // 基础移动速度
base_dis: number = 100; // 基础距离
// ==================== 动态属性值 ====================
hp: number = 100; // 当前血量
mp: number = 100; // 当前魔法值
shield: number = 0; // 当前护盾
Attrs: any = []; // 最终属性数组经过Buff计算后
NeAttrs: any = []; // 负面状态数组
// ==================== Buff/Debuff 系统 ====================
/** 持久型buff数组 - 不会自动过期 */
BUFFS: Record<number, Array<{value: number, BType: BType}>> = {};
/** 临时型buff数组 - 按时间自动过期 */
BUFFS_TEMP: Record<number, Array<{value: number, BType: BType, remainTime: number}>> = {};
// ==================== 标记状态 ====================
is_dead: boolean = false;
is_count_dead: boolean = false;
is_boss: boolean = false;
is_big_boss: boolean = false;
is_master: boolean = false;
is_friend: boolean = false;
is_kalami: boolean = false;
// ==================== 计数统计 ====================
atk_count: number = 0; // 攻击次数
atked_count: number = 0; // 被攻击次数
// ==================== 技能配置 ====================
skills: any = [];
// ==================== BUFF 系统初始化 ====================
/**
* 初始化角色的 buff debuff
* 从 HeroInfo 读取初始配置,建立属性系统
*/
initAttrs() {
// 清空现有 buff/debuff
this.BUFFS = {};
this.BUFFS_TEMP = {};
// 获取英雄配置
const heroInfo = HeroInfo[this.hero_uuid];
if (!heroInfo) return;
// 1. 重置为基础值
this.Attrs[Attrs.HP_MAX] = this.base_hp;
this.Attrs[Attrs.MP_MAX] = this.base_mp;
this.Attrs[Attrs.DEF] = this.base_def;
this.Attrs[Attrs.AP] = this.base_ap;
this.Attrs[Attrs.MAP] = this.base_map;
this.Attrs[Attrs.SPEED] = this.base_speed;
this.Attrs[Attrs.DIS] = this.base_dis;
// 2. 初始化其他属性(无初始值的)
for (const attrKey in this.Attrs) {
const attrIndex = parseInt(attrKey);
if (
attrIndex !== Attrs.HP_MAX &&
attrIndex !== Attrs.MP_MAX &&
attrIndex !== Attrs.DEF &&
attrIndex !== Attrs.AP &&
attrIndex !== Attrs.MAP &&
attrIndex !== Attrs.SPEED &&
attrIndex !== Attrs.DIS
) {
this.Attrs[attrIndex] = 0;
}
}
// 加载初始 buff
if (heroInfo.buff && heroInfo.buff.length > 0) {
for (const buffConf of heroInfo.buff) {
this.addBuff(buffConf);
}
}
}
// ==================== BUFF 管理 ====================
/**
* 添加 buff 效果(支持多次叠加)
* @param buffConf buff 配置 (来自 SkillSet.BuffConf heroSet.buff)
*/
addBuff(buffConf: BuffConf) {
const isPermanent = buffConf.time === 0;
const attrIndex = buffConf.buff;
if (isPermanent) {
// 添加持久buff到BUFFS - 直接追加到数组
if (!this.BUFFS[attrIndex]) {
this.BUFFS[attrIndex] = [];
}
this.BUFFS[attrIndex].push({ value: buffConf.value, BType: buffConf.BType });
} else {
// 添加临时buff到BUFFS_TEMP - 直接追加到数组
if (!this.BUFFS_TEMP[attrIndex]) {
this.BUFFS_TEMP[attrIndex] = [];
}
this.BUFFS_TEMP[attrIndex].push({
value: buffConf.value,
BType: buffConf.BType,
remainTime: buffConf.time
});
}
// 重新计算受影响的属性
this.recalculateSingleAttr(attrIndex);
}
// ==================== 属性计算系统 ====================
/**
* 重新计算单个属性
* @param attrIndex 属性索引
*/
recalculateSingleAttr(attrIndex: number) {
// 1. 获取基础值
const baseValues: Record<number, number> = {
[Attrs.HP_MAX]: this.base_hp,
[Attrs.MP_MAX]: this.base_mp,
[Attrs.DEF]: this.base_def,
[Attrs.AP]: this.base_ap,
[Attrs.MAP]: this.base_map,
[Attrs.SPEED]: this.base_speed,
[Attrs.SHIELD_MAX]: 0
};
const baseVal = baseValues[attrIndex] !== undefined ? baseValues[attrIndex] : 0;
// 2. 收集所有数值型 buff/debuff
let totalValue = baseVal;
// 遍历持久buff数组
if (this.BUFFS[attrIndex] && this.BUFFS[attrIndex].length > 0) {
for (const buff of this.BUFFS[attrIndex]) {
if (buff.BType === BType.VALUE) {
totalValue += buff.value;
}
}
}
// 遍历临时buff数组
if (this.BUFFS_TEMP[attrIndex] && this.BUFFS_TEMP[attrIndex].length > 0) {
for (const buff of this.BUFFS_TEMP[attrIndex]) {
if (buff.BType === BType.VALUE) {
totalValue += buff.value;
}
}
}
// 3. 收集所有百分比型 buff/debuff
let totalRatio = 0;
// 遍历持久buff数组
if (this.BUFFS[attrIndex] && this.BUFFS[attrIndex].length > 0) {
for (const buff of this.BUFFS[attrIndex]) {
if (buff.BType === BType.RATIO) {
totalRatio += buff.value;
}
}
}
// 遍历临时buff数组
if (this.BUFFS_TEMP[attrIndex] && this.BUFFS_TEMP[attrIndex].length > 0) {
for (const buff of this.BUFFS_TEMP[attrIndex]) {
if (buff.BType === BType.RATIO) {
totalRatio += buff.value;
}
}
}
// 4. 根据属性类型计算最终值
const attrType = AttrsType[attrIndex];
const isRatioAttr = attrType === BType.RATIO;
if (isRatioAttr) {
// 百分比型属性:直接加减
this.Attrs[attrIndex] = totalValue + totalRatio;
} else {
// 数值型属性:(基础值+数值) × (1 + 百分比/100)
this.Attrs[attrIndex] = Math.floor(totalValue * (1 + totalRatio / 100));
}
// 5. 确保属性值合理
this.clampSingleAttr(attrIndex);
}
/**
* 确保单个属性值合理
*/
private clampSingleAttr(attrIndex: number) {
switch(attrIndex) {
case Attrs.HP_MAX:
case Attrs.MP_MAX:
this.Attrs[attrIndex] = Math.max(1, this.Attrs[attrIndex]);
break;
case Attrs.DEF:
case Attrs.AP:
case Attrs.MAP:
this.Attrs[attrIndex] = Math.max(1, this.Attrs[attrIndex]);
break;
case Attrs.CRITICAL:
case Attrs.DODGE:
case Attrs.HIT:
this.Attrs[attrIndex] = Math.max(0, Math.min(AttrSet.ATTR_MAX, this.Attrs[attrIndex]));
break;
}
}
// ==================== 临时 BUFF/DEBUFF 更新 ====================
/**
* 更新临时 buff/debuff 的剩余时间
* @param dt 时间增量
*/
updateTemporaryBuffsDebuffs(dt: number) {
const affectedAttrs = new Set<number>();
// 更新临时型buff
for (const attrIndex in this.BUFFS_TEMP) {
const buffs = this.BUFFS_TEMP[attrIndex];
buffs.forEach(buff => {
buff.remainTime -= dt;
if (buff.remainTime <= 0) {
const index = buffs.indexOf(buff);
if (index > -1) {
buffs.splice(index, 1);
}
}
});
if (buffs.length === 0) {
delete this.BUFFS_TEMP[attrIndex];
affectedAttrs.add(parseInt(attrIndex));
}
}
// 负面状态更新
for (const key in this.NeAttrs) {
const debuff = this.NeAttrs[key];
debuff.remainTime -= dt;
if (debuff.remainTime <= 0) {
debuff.remainTime = 0;
}
}
// 只重新计算受影响的属性
affectedAttrs.forEach(attrIndex => {
this.recalculateSingleAttr(attrIndex);
});
}
// ==================== BUFF 辅助方法 ====================
/**
* 清空buff
* @param attrIndex 属性索引如果为空则清理所有buff
* @param isBuff true时清理value>0的增益bufffalse时清理value<0的减益buff
*/
clearBuffs(attrIndex?: number, isBuff: boolean = true): void {
if (attrIndex === undefined) {
for (const attrIndex in this.BUFFS_TEMP) {
this.clearBuffsForAttr(parseInt(attrIndex), isBuff);
}
} else {
this.clearBuffsForAttr(attrIndex, isBuff);
}
}
/**
* 清理指定属性的buff
* @param attrIndex 属性索引
* @param isBuff true清理增益bufffalse清理减益buff
*/
private clearBuffsForAttr(attrIndex: number, isBuff: boolean): void {
const buffContainer = this.BUFFS_TEMP;
if (!buffContainer[attrIndex]) return;
buffContainer[attrIndex] = buffContainer[attrIndex].filter(buff => {
const shouldClear = isBuff ? buff.value > 0 : buff.value < 0;
return !shouldClear;
});
if (buffContainer[attrIndex].length === 0) {
delete buffContainer[attrIndex];
}
this.recalculateSingleAttr(attrIndex);
}
// ==================== NeAttrs负面状态管理 ====================
/**
* 清理单个NeAttr负面状态
*/
clearNeAttr(neAttrIndex: number): void {
if (this.NeAttrs[neAttrIndex]) {
this.NeAttrs[neAttrIndex].value = 0;
this.NeAttrs[neAttrIndex].time = 0;
}
}
/**
* 清理所有NeAttrs负面状态
*/
clearAllNeAttrs(): void {
for (const key in this.NeAttrs) {
this.NeAttrs[key].value = 0;
this.NeAttrs[key].time = 0;
}
}
/**
* 检查是否处于眩晕状态
*/
public isStun(): boolean {
return this.NeAttrs[NeAttrs.IN_STUN]?.time > 0;
}
/**
* 检查是否处于冰冻状态
*/
public isFrost(): boolean {
return this.NeAttrs[NeAttrs.IN_FROST]?.time > 0;
}
reset() { reset() {
// 重置为初始状态
this.hero_uuid = 1001;
this.hero_name = "hero";
this.lv = 1;
this.type = 0;
this.fac = 0;
this.base_ap = 0;
this.base_map = 0;
this.base_def = 5;
this.base_hp = 100;
this.base_mp = 100;
this.base_speed = 100;
this.base_dis = 100;
this.hp = 100;
this.mp = 100;
this.shield = 0;
this.Attrs = [];
this.NeAttrs = [];
this.BUFFS = {};
this.BUFFS_TEMP = {};
this.is_dead = false;
this.is_count_dead = false;
this.is_boss = false;
this.is_big_boss = false;
this.is_master = false;
this.is_friend = false;
this.is_kalami = false;
this.atk_count = 0;
this.atked_count = 0;
this.skills = [];
} }
} }
/**
* ==================== 英雄属性更新系统 ====================
*
* 按照 ECS 设计理念:
* - ComponentHeroModelComp存储数据
* - SystemHeroAttrSystem处理业务逻辑
*
* 系统职责:
* 1. 每帧更新临时 Buff时间递减过期移除
* 2. 每帧更新 HP/MP 自然回复
* 3. 限制属性值在合理范围内
*
* 使用方式:
* 在 RootSystem 中注册此系统,它会自动每帧更新所有拥有 HeroModelComp 的实体
*/
export class HeroAttrSystem extends ecs.ComblockSystem
implements ecs.ISystemUpdate, ecs.IEntityEnterSystem, ecs.ISystemFirstUpdate {
// ==================== 调试统计(可选)====================
private entityCount: number = 0; // 本帧处理的实体数
private frameCount: number = 0; // 总帧数
private debugMode: boolean = false; // 是否启用调试模式
/**
* 过滤器:只处理拥有 HeroModelComp 的实体
*/
filter(): ecs.IMatcher {
return ecs.allOf(HeroModelComp);
}
/**
* 实体首次进入系统时调用(每个实体只调用一次)
*/
entityEnter(e: ecs.Entity): void {
const model = e.get(HeroModelComp);
if (!model) return;
console.log(`[HeroAttrSystem] 英雄进入系统: ${model.hero_name} (uuid: ${model.hero_uuid})`);
}
/**
* 系统首次更新前调用(整个系统只调用一次)
*/
firstUpdate(): void {
console.log("[HeroAttrSystem] 系统首次更新");
}
/**
* 每帧更新(为每个英雄调用一次)
*
* ⭐ 关键理解:
* - 如果有 3 个英雄,这个方法每帧会被调用 3 次
* - 每次调用处理不同的实体 e
* - 这是正确的设计,不是 bug
*/
update(e: ecs.Entity): void {
const model = e.get(HeroModelComp);
if (!model || model.is_dead) return;
// 统计:记录本帧处理的实体数
this.entityCount++;
// 调试日志(可选,调试时启用)
if (this.debugMode) {
console.log(` [${this.entityCount}] 更新英雄: ${model.hero_name}, HP: ${model.hp.toFixed(2)}`);
}
// 1. 更新临时 Buff/Debuff时间递减过期自动移除
model.updateTemporaryBuffsDebuffs(this.dt);
// 2. HP/MP 自然回复(业务规则)
model.mp += HeroUpSet.MP * this.dt;
model.hp += HeroUpSet.HP * this.dt;
// 3. 限制属性值在合理范围内
if (model.mp > model.Attrs[Attrs.MP_MAX]) {
model.mp = model.Attrs[Attrs.MP_MAX];
}
if (model.hp > model.Attrs[Attrs.HP_MAX]) {
model.hp = model.Attrs[Attrs.HP_MAX];
}
// 每 60 帧输出一次统计
this.frameCount++;
if (this.frameCount % 60 === 0 && this.entityCount === 1) {
console.log(`[HeroAttrSystem] 第 ${this.frameCount} 帧,处理 ${this.entityCount} 个英雄`);
}
// 注意:显示更新由 HeroViewComp 负责,这里只处理数据
}
/**
* 启用调试模式(调试时使用)
*/
enableDebug() {
this.debugMode = true;
}
/**
* 禁用调试模式(正式运行)
*/
disableDebug() {
this.debugMode = false;
}
}
/**
* ==================== 英雄战斗系统 ====================
*
* 按照 ECS 设计理念:
* - ComponentHeroModelComp存储数据
* - SystemHeroBattleSystem处理战斗业务逻辑
*
* 系统职责:
* 1. 处理角色被攻击逻辑(伤害计算、暴击判定、闪避判定)
* 2. 处理护盾吸收逻辑
* 3. 处理角色死亡逻辑
* 4. 处理战斗状态管理
*
* 使用方式:
* 在 RootSystem 中注册此系统,它会自动处理所有拥有 HeroModelComp 的实体的战斗逻辑
*/
export class HeroBattleSystem extends ecs.ComblockSystem
implements ecs.ISystemUpdate, ecs.IEntityEnterSystem {
private debugMode: boolean = false; // 是否启用调试模式
/**
* 过滤器:只处理拥有 HeroModelComp 的实体
*/
filter(): ecs.IMatcher {
return ecs.allOf(HeroModelComp);
}
/**
* 实体首次进入系统时调用(每个实体只调用一次)
*/
entityEnter(e: ecs.Entity): void {
const model = e.get(HeroModelComp);
if (!model) return;
console.log(`[HeroBattleSystem] 英雄进入战斗系统: ${model.hero_name} (uuid: ${model.hero_uuid})`);
}
/**
* 处理角色被攻击
* @param target 被攻击的目标实体
* @param remainingDamage 基础伤害值
* @param attackerAttrs 攻击者的属性
* @param skillId 技能ID
* @returns 实际造成的伤害
*/
public doAttack(target: ecs.Entity, remainingDamage: number, attackerAttrs: any, skillId: number): number {
const targetModel = target.get(HeroModelComp);
if (!targetModel || targetModel.is_dead) return 0;
// 获取技能配置
const skillConf = SkillSet[skillId];
// 触发被攻击事件
this.onAttacked(target);
// 闪避判定
if (this.checkDodge(targetModel)) {
return 0;
}
// 暴击判定
const isCrit = this.checkCrit(attackerAttrs[Attrs.CRITICAL]);
let damage = remainingDamage;
if (isCrit) {
damage = Math.floor(damage * (1 + (FightSet.CRIT_DAMAGE + attackerAttrs[Attrs.CRITICAL_DMG]) / 100));
}
// 伤害计算考虑易伤等debuff
damage = this.calculateDamage(targetModel, damage);
// 护盾吸收
damage = this.absorbShield(targetModel, damage);
if (damage <= 0) return 0;
// 应用伤害
targetModel.hp -= damage;
targetModel.atked_count++;
// 检查死亡
if (targetModel.hp <= 0) {
this.doDead(target);
}
if (this.debugMode) {
console.log(`[HeroBattleSystem] ${targetModel.hero_name} 受到 ${damage} 点伤害 (暴击: ${isCrit})`);
}
return damage;
}
/**
* 处理角色死亡
*/
private doDead(entity: ecs.Entity): void {
const model = entity.get(HeroModelComp);
if (!model || model.is_dead) return;
model.is_dead = true;
// 触发死亡事件
this.onDeath(entity);
if (this.debugMode) {
console.log(`[HeroBattleSystem] ${model.hero_name} 死亡`);
}
}
/**
* 闪避判定
*/
private checkDodge(model: HeroModelComp): boolean {
if (model.Attrs[Attrs.DODGE] > 0) {
const random = Math.random() * 100;
if (random < model.Attrs[Attrs.DODGE]) {
if (this.debugMode) {
console.log(`[HeroBattleSystem] ${model.hero_name} 闪避了攻击`);
}
return true;
}
}
return false;
}
/**
* 暴击判定
*/
private checkCrit(critRate: number): boolean {
if (critRate > 0) {
const random = Math.random() * 100;
return random < critRate;
}
return false;
}
/**
* 伤害计算考虑易伤等debuff
*/
private calculateDamage(model: HeroModelComp, baseDamage: number): number {
// 这里可以添加易伤等debuff的计算逻辑
// 例如如果目标有易伤buff增加受到的伤害
return baseDamage;
}
/**
* 护盾吸收伤害
*/
private absorbShield(model: HeroModelComp, damage: number): number {
if (model.shield <= 0) return damage;
if (model.shield >= damage) {
model.shield -= damage;
if (model.shield <= 0) {
model.shield = 0;
model.Attrs[Attrs.SHIELD_MAX] = 0;
}
return 0;
} else {
const remainingDamage = damage - model.shield;
model.shield = 0;
model.Attrs[Attrs.SHIELD_MAX] = 0;
return remainingDamage;
}
}
/**
* 被攻击时触发的事件
*/
private onAttacked(entity: ecs.Entity): void {
const model = entity.get(HeroModelComp);
if (!model || model.is_dead) return;
// 这里可以添加被攻击时的特殊处理逻辑
if (model.fac === FacSet.MON) return;
// 例如:触发某些天赋效果、反击逻辑等
}
/**
* 死亡时触发的事件
*/
private onDeath(entity: ecs.Entity): void {
const model = entity.get(HeroModelComp);
if (!model) return;
if (model.fac === FacSet.MON) {
// 怪物死亡处理
this.scheduleDrop(entity);
} else if (model.fac === FacSet.HERO) {
// 英雄死亡处理
this.scheduleHeroDeath(entity);
}
}
/**
* 延迟执行掉落逻辑
*/
private scheduleDrop(entity: ecs.Entity): void {
// 这里可以添加掉落逻辑
// 例如:延迟一段时间后生成掉落物品
}
/**
* 延迟执行英雄死亡逻辑
*/
private scheduleHeroDeath(entity: ecs.Entity): void {
// 这里可以添加英雄死亡的特殊处理
// 例如:触发游戏结束、复活机制等
}
/**
* 启用调试模式
*/
enableDebug() {
this.debugMode = true;
}
/**
* 禁用调试模式
*/
disableDebug() {
this.debugMode = false;
}
/**
* 系统更新(每帧调用)
* 可以在这里添加需要每帧处理的战斗逻辑
*/
update(e: ecs.Entity): void {
// 这里可以添加需要每帧处理的战斗逻辑
// 例如:持续伤害、战斗状态检查等
}
}

View File

@@ -14,56 +14,9 @@ import { RandomManager } from "db://oops-framework/core/common/random/RandomMana
import { AttrSet, HeroInfo, HeroUpSet } from "../common/config/heroSet"; import { AttrSet, HeroInfo, HeroUpSet } from "../common/config/heroSet";
import { Attrs, AttrsType, BType, NeAttrs } from "../common/config/HeroAttrs"; import { Attrs, AttrsType, BType, NeAttrs } from "../common/config/HeroAttrs";
import { TalComp } from "./TalComp"; import { TalComp } from "./TalComp";
import { HeroModelComp, HeroBattleSystem } from "./HeroModelComp";
const { ccclass, property } = _decorator; const { ccclass, property } = _decorator;
/**
* ==================== BUFF 系统使用说明 ====================
*
* 1. 系统架构 - 支持多次叠加(简化设计)
* - BUFFS: 持久型buff数组持久存在直到主动移除
* - BUFFS_TEMP: 临时型buff数组持续指定时间时间到自动移除
* - 每个buff实例内包含BType字段区分数值型(VALUE)和百分比型(RATIO)
*
* 2. 叠加规则 - 核心特性
* - 同一属性允许多个buff实例同时存在数值型和百分比型混合
* - 每个buff实例保持独立的数据value、BType、remainTime
* - 所有buff实例在属性计算时都被纳入求和计算
* - 示例:若添加两个+10 AP的buff最终会累加 +20 AP
*
* 3. 数据结构 - 统一数组设计
* - BUFFS: Record<attrIndex: number, Array<{value, BType}>>
* - BUFFS_TEMP: Record<attrIndex: number, Array<{value, BType, remainTime}>>
* - 每个属性对应一个buff数组可混合存储数值型和百分比型buff
*
* 4. 属性计算公式 - 按BType区分
* - 数值型: (基础值 + 所有数值型buff之和) × (1 + 所有百分比buff之和/100)
* - 百分比: 基础值 + 所有数值型buff之和 + 所有百分比buff之和
*
* 5. API 使用示例
* // 添加buff自动根据time判断持久或临时
* const buffConf: BuffConf = {
* buff: Attrs.AP,
* BType: BType.VALUE,
* value: 10,
* time: 5, // 0表示持久>0表示临时
* chance: 100
* };
* hero.addBuff(buffConf);
*
* // 移除特定buff
* hero.removeBuff(Attrs.AP, 10, true); // 移除临时的+10 AP
*
* // 清空属性所有buff
* hero.clearBuffs(Attrs.AP);
*
* // 查询激活的buff数量
* const count = hero.getActiveBuffCount(Attrs.AP);
*
* 6. 临时buff自动更新
* - 每帧update中自动调用updateTemporaryBuffsDebuffs(dt)
* - 递减所有临时buff的remainTime
* - 当remainTime <= 0时自动移除buff并重新计算属性
*/
/** 角色显示组件 */ /** 角色显示组件 */
export interface BuffInfo { export interface BuffInfo {
attr: Attrs; attr: Attrs;
@@ -73,54 +26,20 @@ export interface BuffInfo {
@ccclass('HeroViewComp') // 定义Cocos Creator 组件 @ccclass('HeroViewComp') // 定义Cocos Creator 组件
@ecs.register('HeroView', false) // 定义ECS 组件 @ecs.register('HeroView', false) // 定义ECS 组件
export class HeroViewComp extends CCComp { export class HeroViewComp extends CCComp {
// ==================== View 层属性(表现相关)====================
BUFFCOMP:BuffComp=null! BUFFCOMP:BuffComp=null!
TALCOMP:any=null! TALCOMP:any=null!
as: HeroSpine = null! as: HeroSpine = null!
status:String = "idle" status:String = "idle"
hero_uuid:number = 1001; scale: number = 1; // 显示方向
hero_name : string = "hero"; box_group:number = BoxSet.HERO; // 碰撞组
lv:number =1;
scale: number = 1; /** 角色阵营 1hero -1 :mon */
type: number = 0; /**角色类型 0近战-需要贴1远程-保持距离 2辅助 */
fac:number=0; //阵营 0hero 1monster
box_group:number = BoxSet.HERO;
is_dead:boolean = false; //是否摧毁
is_count_dead:boolean = false; //是否计数死亡
is_stop:boolean = false; is_stop:boolean = false;
is_atking:boolean = false; is_atking:boolean = false;
is_boss:boolean = false; // ==================== 直接访问 HeroModelComp ====================
is_big_boss:boolean = false; private get model() {
is_master:boolean =false; return this.ent.get(HeroModelComp);
is_friend:boolean =false; }
is_kalami:boolean =false;
skills:any=[]
mp: number = 100;
hp: number = 100; /** 血*/
shield:number=0; //当前护甲
/** 基础属有初始值的基础属后续Attrs 属性计算时用到*/
base_ap: number = 0; //基础攻击
base_map: number = 0;
base_def: number = 5;
base_hp: number = 100;
base_mp: number = 100;
base_speed: number = 100; /** 角色移动速度 */
base_dis: number = 100;
Attrs:any=[]
NeAttrs:any=[]
// Buff debuff 统一管理
// 每个buff实例内包含BType用于区分数值型(VALUE)和百分比型(RATIO)
// [attrIndex: number]: Array<BuffInstance> - 允许多个buff实例独立存在
/** 持久型buff数组 - 不会自动过期 */
BUFFS: Record<number, Array<{value: number, BType: BType}>> = {}
/** 临时型buff数组 - 按时间自动过期 */
BUFFS_TEMP: Record<number, Array<{value: number, BType: BType, remainTime: number}>> = {}
atk_count: number = 0;
atked_count: number = 0;
private damageQueue: Array<{ private damageQueue: Array<{
damage: number, damage: number,
@@ -145,7 +64,7 @@ export class HeroViewComp extends CCComp {
start () { start () {
this.as.idle() this.as.idle()
this.BUFFCOMP=this.node.getComponent(BuffComp); this.BUFFCOMP=this.node.getComponent(BuffComp);
this.TALCOMP=this.node.getComponent(TalComp); this.TALCOMP=this.ent.get(TalComp);
// console.log("[HeroViewComp]:heroview"+this.hero_name,this.Attrs) // console.log("[HeroViewComp]:heroview"+this.hero_name,this.Attrs)
/** 方向 */ /** 方向 */
this.node.setScale(this.scale,1); this.node.setScale(this.scale,1);
@@ -158,351 +77,97 @@ export class HeroViewComp extends CCComp {
this.node.getChildByName("top").getChildByName("pow").active = true; this.node.getChildByName("top").getChildByName("pow").active = true;
} }
// ==================== BUFF系统初始==================== // ==================== BUFF 系统方法直接调用 ====================
/** /**
* 初始化角色的 buff debuff * 初始化角色的 buff debuff(直接调用 Model
* HeroInfo 读取初始配置,建立属性系
*/ */
initAttrs() { initAttrs() {
// 清空现有 buff/debuff if (this.model) {
this.BUFFS = { this.model.initAttrs();
};
this.BUFFS_TEMP = {
};
// 获取英雄配置
const heroInfo = HeroInfo[this.hero_uuid];
if (!heroInfo) return;
// 1. 重置为基础
this.Attrs[Attrs.HP_MAX] = this.base_hp;
this.Attrs[Attrs.MP_MAX] = this.base_mp;
this.Attrs[Attrs.DEF] = this.base_def;
this.Attrs[Attrs.AP] = this.base_ap;
this.Attrs[Attrs.MAP] = this.base_map;
this.Attrs[Attrs.SPEED] = this.base_speed;
this.Attrs[Attrs.DIS] = this.base_dis;
// 2. 初始化其他属性(无初始值的
for (const attrKey in this.Attrs) {
const attrIndex = parseInt(attrKey);
if(
attrIndex !== Attrs.HP_MAX &&
attrIndex !== Attrs.MP_MAX&&
attrIndex !== Attrs.DEF &&
attrIndex !== Attrs.AP &&
attrIndex !== Attrs.MAP &&
attrIndex !== Attrs.SPEED &&
attrIndex !== Attrs.DIS
) {
this.Attrs[attrIndex] = 0;
}
}
// 加载初始 buff
if (heroInfo.buff && heroInfo.buff.length > 0) {
for (const buffConf of heroInfo.buff) {
this.addBuff(buffConf);
}
} }
} }
// ==================== BUFF管理 ====================
/** /**
* 添加 buff 效果(支持多次叠加 * 添加 buff 效果(直接调用 Model
* @param buffConf buff 配置 (来自 SkillSet.BuffConf heroSet.buff)
*
* 叠加规则
* 1. 持久型buff直接添加到数组中与现有buff独立存在
* 2. 临时型buff直接添加到数组中保持独立的剩余时间计算
* 3. 所有buff实例在属性计算时都被纳入求和计算
*/ */
addBuff(buffConf: BuffConf) { addBuff(buffConf: BuffConf) {
const isPermanent = buffConf.time === 0; if (this.model) {
const attrIndex = buffConf.buff; this.model.addBuff(buffConf);
if (isPermanent) {
// 添加持久buff到BUFFS - 直接追加到数组
if (!this.BUFFS[attrIndex]) {
this.BUFFS[attrIndex] = [];
}
this.BUFFS[attrIndex].push({ value: buffConf.value, BType: buffConf.BType });
} else {
// 添加临时buff到BUFFS_TEMP - 直接追加到数组
if (!this.BUFFS_TEMP[attrIndex]) {
this.BUFFS_TEMP[attrIndex] = [];
}
this.BUFFS_TEMP[attrIndex].push({
value: buffConf.value,
BType: buffConf.BType,
remainTime: buffConf.time
});
}
// 重新计算受影响的属性
this.recalculateSingleAttr(attrIndex);
}
// ==================== 属性计算系====================
/**
* 重新计算单个属性
* @param attrIndex 属性索引
*
* 计算公式:
* - 数值型属性:最终值 = (基础值 + 所有数值型buff之和 - 所有数值型debuff之和) × (1 + 所有百分比buff之和/100 - 所有百分比debuff之和/100)
* - 百分比型属性:最终值 = 基础值 + 所有数值型buff之和 - 所有数值型debuff之和 + 所有百分比buff之和 - 所有百分比debuff之和
*/
private recalculateSingleAttr(attrIndex: number) {
// 1. 获取基础值
const baseValues: Record<number, number> = {
[Attrs.HP_MAX]: this.base_hp,
[Attrs.MP_MAX]: this.base_mp,
[Attrs.DEF]: this.base_def,
[Attrs.AP]: this.base_ap,
[Attrs.MAP]: this.base_map,
[Attrs.SPEED]: this.base_speed,
[Attrs.SHIELD_MAX]: 0
};
const baseVal = baseValues[attrIndex] !== undefined ? baseValues[attrIndex] : 0;
// 2. 收集所有数值型 buff/debuff - 遍历所有buff数组并按BType筛选求和
let totalValue = baseVal;
// 遍历持久buff数组
if (this.BUFFS[attrIndex] && this.BUFFS[attrIndex].length > 0) {
for (const buff of this.BUFFS[attrIndex]) {
if (buff.BType === BType.VALUE) {
totalValue += buff.value;
}
}
}
// 遍历临时buff数组
if (this.BUFFS_TEMP[attrIndex] && this.BUFFS_TEMP[attrIndex].length > 0) {
for (const buff of this.BUFFS_TEMP[attrIndex]) {
if (buff.BType === BType.VALUE) {
totalValue += buff.value;
}
}
}
// 3. 收集所有百分比型 buff/debuff - 遍历所有buff数组并按BType筛选求和
let totalRatio = 0; // 总百分比(可正可负)
// 遍历持久buff数组
if (this.BUFFS[attrIndex] && this.BUFFS[attrIndex].length > 0) {
for (const buff of this.BUFFS[attrIndex]) {
if (buff.BType === BType.RATIO) {
totalRatio += buff.value;
}
}
}
// 遍历临时buff数组
if (this.BUFFS_TEMP[attrIndex] && this.BUFFS_TEMP[attrIndex].length > 0) {
for (const buff of this.BUFFS_TEMP[attrIndex]) {
if (buff.BType === BType.RATIO) {
totalRatio += buff.value;
}
}
}
// 4. 根据属性类型计算最终值
const attrType = AttrsType[attrIndex];
const isRatioAttr = attrType === BType.RATIO;
if (isRatioAttr) {
// 百分比型属性:直接加减
this.Attrs[attrIndex] = totalValue + totalRatio;
} else {
// 数值型属性:(基础值+数值) × (1 + 百分比/100)
this.Attrs[attrIndex] = Math.floor(totalValue * (1 + totalRatio / 100));
}
// 5. 确保属性值合理
this.clampSingleAttr(attrIndex);
}
/**
* 确保单个属性值合
*/
private clampSingleAttr(attrIndex: number) {
switch(attrIndex) {
case Attrs.HP_MAX:
case Attrs.MP_MAX:
this.Attrs[attrIndex] = Math.max(1, this.Attrs[attrIndex]);
break;
case Attrs.DEF:
case Attrs.AP:
case Attrs.MAP:
this.Attrs[attrIndex] = Math.max(1, this.Attrs[attrIndex]);
break;
case Attrs.CRITICAL:
case Attrs.DODGE:
case Attrs.HIT:
this.Attrs[attrIndex] = Math.max(0, Math.min(AttrSet.ATTR_MAX, this.Attrs[attrIndex])); //AttrSet.ATTR_MAX =85
break;
} }
} }
// ==================== 临时 BUFF/DEBUFF 更新 ====================
// 属性计算方法已迁移到 Model这里不再需要
/** /**
* 更新临时 buff/debuff 的剩余时 * 更新临时 buff/debuff(直接调用 Model
* 应在 update 中定期调
* @param dt 时间
*/ */
updateTemporaryBuffsDebuffs(dt: number) { updateTemporaryBuffsDebuffs(dt: number) {
const affectedAttrs = new Set<number>(); if (this.model) {
// 更新临时型数buff this.model.updateTemporaryBuffsDebuffs(dt);
for (const attrIndex in this.BUFFS_TEMP) {
const buffs = this.BUFFS_TEMP[attrIndex];
buffs.forEach(buff => {
buff.remainTime -= dt;
if (buff.remainTime <= 0) {
// 从数组中移除
const index = buffs.indexOf(buff);
if (index > -1) {
buffs.splice(index, 1);
}
}
});
if (buffs.length === 0) {
delete this.BUFFS_TEMP[attrIndex];
affectedAttrs.add(parseInt(attrIndex));
}
} }
// 更新临时型百分比 buff
for (const attrIndex in this.BUFFS_TEMP) {
const buffs = this.BUFFS_TEMP[attrIndex];
buffs.forEach(buff => {
buff.remainTime -= dt;
if (buff.remainTime <= 0) {
// 从数组中移除
const index = buffs.indexOf(buff);
if (index > -1) {
buffs.splice(index, 1);
}
}
});
if (buffs.length === 0) {
delete this.BUFFS_TEMP[attrIndex];
affectedAttrs.add(parseInt(attrIndex));
}
}
// 负面状态更新
for (const key in this.NeAttrs) {
const debuff = this.NeAttrs[key];
debuff.remainTime -= dt;
if (debuff.remainTime <= 0) {
debuff.remainTime = 0;
}
}
// 只重新计算受影响的属
affectedAttrs.forEach(attrIndex => {
this.recalculateSingleAttr(attrIndex);
});
} }
// ==================== BUFF 辅助方法 ====================
/** /**
* 清空buff * 清空buff(直接调用 Model
* @param attrIndex 属性索引如果为空则清理所有buff包括临时和持久
* @param isBuff true时清理value>0的增益bufffalse时清理value<0的减益buff
*/ */
clearBuffs(attrIndex?: number, isBuff: boolean = true): void { clearBuffs(attrIndex?: number, isBuff: boolean = true): void {
if (attrIndex === undefined) { if (this.model) {
// 清理所有buff根据isBuff过滤 this.model.clearBuffs(attrIndex, isBuff);
for (const attrIndex in this.BUFFS_TEMP) {
this.clearBuffsForAttr(parseInt(attrIndex), isBuff);
}
} else {
// 清理指定属性的buff根据isBuff过滤
this.clearBuffsForAttr(attrIndex, isBuff);
} }
} }
/** /**
* 清理指定属性的buff根据增益/减益类型过滤 * 清理单个NeAttr直接调用 Model
* @param attrIndex 属性索引
* @param isBuff true清理增益bufffalse清理减益buff
*/
private clearBuffsForAttr(attrIndex: number, isBuff: boolean): void {
const buffContainer = this.BUFFS_TEMP;
if (!buffContainer[attrIndex]) return;
// 过滤buff数组保留不符合清理条件的buff
buffContainer[attrIndex] = buffContainer[attrIndex].filter(buff => {
const shouldClear = isBuff ? buff.value > 0 : buff.value < 0;
return !shouldClear;
});
// 如果数组为空,删除该属性条目
if (buffContainer[attrIndex].length === 0) {
delete buffContainer[attrIndex];
}
// 重新计算该属性
this.recalculateSingleAttr(attrIndex);
}
// ==================== NeAttrs负面状态管理 ====================
/**
* 清理单个NeAttr负面状态
* @param neAttrIndex NeAttrs索引如NeAttrs.IN_STUN、NeAttrs.IN_FROST等
* 清理即将该状态的value和time都设为0
*/ */
clearNeAttr(neAttrIndex: number): void { clearNeAttr(neAttrIndex: number): void {
if (this.NeAttrs[neAttrIndex]) { if (this.model) {
this.NeAttrs[neAttrIndex].value = 0; this.model.clearNeAttr(neAttrIndex);
this.NeAttrs[neAttrIndex].time = 0;
} }
} }
/** /**
* 清理所有NeAttrs负面状态 * 清理所有NeAttrs直接调用 Model
* 清理即将所有状态的value和time都设为0
*/ */
clearAllNeAttrs(): void { clearAllNeAttrs(): void {
for (const key in this.NeAttrs) { if (this.model) {
this.NeAttrs[key].value = 0; this.model.clearAllNeAttrs();
this.NeAttrs[key].time = 0;
} }
} }
public isStun() { /**
return this.NeAttrs[NeAttrs.IN_STUN].time > 0; * 检查是否眩晕(直接调用 Model
} */
public isFrost() { public isStun(): boolean {
return this.NeAttrs[NeAttrs.IN_FROST].time > 0; return this.model?.isStun() ?? false;
} }
/**
* 检查是否冰冻(直接调用 Model
*/
public isFrost(): boolean {
return this.model?.isFrost() ?? false;
}
/**
* View 层每帧更新
* 注意:数据更新逻辑已移到 HeroAttrSystem这里只负责显示
*/
update(dt: number){ update(dt: number){
if(!smc.mission.play||smc.mission.pause) return if(!smc.mission.play||smc.mission.pause) return
// if(this.is_dead) {
// this.ent.destroy();
// return
// }
this.BaseUp(dt)
// 更新所有按时间减少的buff和debuff
this.in_stop(dt);
// 处理伤害队列
this.processDamageQueue();
// 更新临时 buff/debuff 时间
this.updateTemporaryBuffsDebuffs(dt);
this.BUFFCOMP.hp_show(this.hp,this.Attrs[Attrs.HP_MAX])
this.BUFFCOMP.mp_show(this.mp,this.Attrs[Attrs.MP_MAX])
this.BUFFCOMP.show_shield(this.shield,this.Attrs[Attrs.SHIELD_MAX])
}
BaseUp(dt:number){
this.mp += HeroUpSet.MP*dt
this.hp += HeroUpSet.HP*dt
if(this.mp > this.Attrs[Attrs.MP_MAX]) this.mp = this.Attrs[Attrs.MP_MAX]
if(this.hp > this.Attrs[Attrs.HP_MAX]) this.hp = this.Attrs[Attrs.HP_MAX]
// ✅ View 层职责:处理表现相关的逻辑
this.in_stop(dt); // 动画状态
this.processDamageQueue(); // 伤害数字显示队列
// ✅ 更新显示(数据由 HeroAttrSystem 更新)
this.BUFFCOMP.hp_show(this.hp, this.Attrs[Attrs.HP_MAX]);
this.BUFFCOMP.mp_show(this.mp, this.Attrs[Attrs.MP_MAX]);
this.BUFFCOMP.show_shield(this.shield, this.Attrs[Attrs.SHIELD_MAX]);
} }
// 注意BaseUp 逻辑已移到 HeroAttrSystem.update()
// 注意updateTemporaryBuffsDebuffs 逻辑已移到 HeroAttrSystem.update()
do_fight_end(){ do_fight_end(){
this.as.do_buff() this.as.do_buff()
} }
@@ -522,34 +187,14 @@ export class HeroViewComp extends CCComp {
} }
} }
add_shield(shield:number){ add_shield(shield:number){
this.shield = this.Attrs[Attrs.SHIELD_MAX] +=shield // 护盾数据更新由 Model 层处理,这里只负责视图表现
if(this.shield>0) this.BUFFCOMP.show_shield(this.shield,this.Attrs[Attrs.SHIELD_MAX]) if(this.shield>0) this.BUFFCOMP.show_shield(this.shield,this.Attrs[Attrs.SHIELD_MAX])
} }
health(hp: number = 0,is_num:boolean=true) { health(hp: number = 0,is_num:boolean=true) {
// 生命值更新由 Model 层处理,这里只负责视图表现
this.BUFFCOMP.heathed(); this.BUFFCOMP.heathed();
let real_hp=0 this.BUFFCOMP.show_heal(hp);
let hp_max=this.Attrs[Attrs.HP_MAX]
let lost_hp=hp_max-this.hp
if(is_num){
if(lost_hp > hp){
real_hp=Math.floor(hp);
}else{
real_hp=lost_hp;
}
}else{
if(lost_hp > hp/100*hp_max){
real_hp=Math.floor(hp/100*hp_max);
}else{
real_hp=lost_hp;
}
}
if(real_hp > 0){
this.hp+=real_hp;
this.BUFFCOMP.tooltip(TooltipTypes.health,real_hp.toFixed(0));
}
this.BUFFCOMP.hp_show(this.hp,this.Attrs[Attrs.HP_MAX])
// this.update_vm
} }
@@ -563,10 +208,11 @@ export class HeroViewComp extends CCComp {
} }
do_dead(){ do_dead(){
this.do_dead_trigger() // 死亡逻辑主要由 HeroBattleSystem 处理
//console.log("[HeroViewComp]:角色死亡",this.hero_uuid) // 这里只保留视图层的表现逻辑
if(this.is_count_dead) return if(this.is_count_dead) return
this.is_count_dead=true this.is_count_dead=true
if(this.fac==FacSet.MON){ if(this.fac==FacSet.MON){
this.scheduleOnce(()=>{ this.scheduleOnce(()=>{
this.do_drop() this.do_drop()
@@ -577,51 +223,26 @@ export class HeroViewComp extends CCComp {
oops.message.dispatchEvent(GameEvent.HeroDead,{hero_uuid:this.hero_uuid}) oops.message.dispatchEvent(GameEvent.HeroDead,{hero_uuid:this.hero_uuid})
},0.1) },0.1)
} }
if(this.fac==FacSet.HERO){
//console.log("[HeroViewComp]:英雄死亡")
// oops.message.dispatchEvent(GameEvent.FightEnd,{victory:false})
}
} }
do_drop(){ do_drop(){
} }
do_atked(remainingDamage:number,CAttrs:any,s_uuid:number){ do_atked(remainingDamage:number,CAttrs:any,s_uuid:number){
// 使用战斗系统处理攻击逻辑
const battleSystem = this.ent.ecs.getSystem(HeroBattleSystem);
if (!battleSystem) {
console.error("[HeroViewComp] HeroBattleSystem 未找到");
return;
}
const damage = battleSystem.doAttack(this.ent, remainingDamage, CAttrs, s_uuid);
if (damage <= 0) return;
// 视图层表现
let SConf=SkillSet[s_uuid] let SConf=SkillSet[s_uuid]
this.do_atked_trigger()
if(this.check_dodge()) return
let is_crit = this.check_crit(CAttrs[Attrs.CRITICAL])
if(this == null) return;
let damage = this.count_damage(remainingDamage)
if(is_crit) {
damage = Math.floor(damage * (1 + (FightSet.CRIT_DAMAGE+CAttrs[Attrs.CRITICAL_DMG])/100))
}
// console.log(this.hero_name+"[HeroViewComp]:heroview :damage|hp|hp_max",damage,this.hp,this.Attrs[BuffAttr.HP_MAX])
damage=this.check_shield(damage)
if(damage <= 0) return
this.hp -= damage;
if(this.hp <= 0) {
if(this == null) return;
this.is_dead=true
if(this.BUFFCOMP){
this.BUFFCOMP.dead()
}
this.do_dead()
//console.log("[HeroViewComp]:dead,fac => "+(this.fac==FacSet.HERO?"hero":"monster"))
if(this.ent == null) return;
if(this.fac ==FacSet.HERO){
this.to_grave()
}else{
this.ent.destroy();
}
}
// this.update_vm
this.back() this.back()
this.showDamage(damage, is_crit,SConf.AtkedName); this.showDamage(damage, false, SConf.AtkedName); // 暴击状态由战斗系统内部处理
} }
//后退 //后退
back(){ back(){
@@ -636,53 +257,7 @@ export class HeroViewComp extends CCComp {
tween(this.node).to(0.1, { position:v3(tx,this.node.position.y,0)}).start() tween(this.node).to(0.1, { position:v3(tx,this.node.position.y,0)}).start()
} }
} }
//伤害计算 debuff 易伤 // 伤害计算和战斗逻辑已迁移到 HeroBattleSystem
count_damage(remainingDamage:number){
return remainingDamage
}
check_shield(damage:number){
if(this.shield <= 0 ) return damage
if(this.shield >= damage){
this.shield -= damage
this.BUFFCOMP.tooltip(TooltipTypes.uskill,"*吸收*");
if(this.shield <= 0){
this.shield=this.Attrs[Attrs.SHIELD_MAX]=0
}
damage = 0
}
if(this.shield < damage){
damage=damage-this.shield
this.shield=0
this.Attrs[Attrs.SHIELD_MAX]=0
}
this.BUFFCOMP.show_shield(this.shield,this.Attrs[Attrs.SHIELD_MAX])
return damage
}
check_dodge(){
if(this.Attrs[Attrs.DODGE] > 0){
let random = Math.random()*100
if(random < this.Attrs[Attrs.DODGE]) {
this.BUFFCOMP.tooltip(TooltipTypes.uskill,"*闪避*");
return true
}
}
return false
}
check_crit(crit:number=0){
if(crit > 0){
let random = Math.random()*100
if(random < crit) {
//console.log("[HeroViewComp]:crit",crit,random)
return true
}
}
//console.log("[HeroViewComp]:crit",crit)
return false
}
do_dead_trigger(){ //死亡特殊处理 do_dead_trigger(){ //死亡特殊处理
if(this.is_dead||this.fac==FacSet.MON) return if(this.is_dead||this.fac==FacSet.MON) return

View File

@@ -2,7 +2,6 @@ import { instantiate, Node, Prefab, Vec3 ,v3,resources,SpriteFrame,Sprite,Sprite
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops"; import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { smc } from "../common/SingletonModuleComp"; import { smc } from "../common/SingletonModuleComp";
import { HeroViewComp } from "./HeroViewComp";
import { BoxSet, FacSet } from "../common/config/BoxSet"; import { BoxSet, FacSet } from "../common/config/BoxSet";
import { HeroInfo } from "../common/config/heroSet"; import { HeroInfo } from "../common/config/heroSet";
import { MonModelComp } from "./MonModelComp"; import { MonModelComp } from "./MonModelComp";
@@ -12,11 +11,13 @@ import { BuffConf, SkillSet } from "../common/config/SkillSet";
import { getNeAttrs, getAttrs ,Attrs} from "../common/config/HeroAttrs"; import { getNeAttrs, getAttrs ,Attrs} from "../common/config/HeroAttrs";
import { TalComp } from "./TalComp"; import { TalComp } from "./TalComp";
import { getMonAttr, MonType } from "../map/RogueConfig"; import { getMonAttr, MonType } from "../map/RogueConfig";
import { EBusComp } from "./EBusComp";
import { MonViewComp } from "./MonViewComp";
/** 角色实体 */ /** 角色实体 */
@ecs.register(`Monster`) @ecs.register(`Monster`)
export class Monster extends ecs.Entity { export class Monster extends ecs.Entity {
HeroModel!: MonModelComp; HeroModel!: MonModelComp;
HeroView!: HeroViewComp; HeroView!: MonViewComp;
BattleMove!: BattleMoveComp; BattleMove!: BattleMoveComp;
protected init() { protected init() {
@@ -24,13 +25,15 @@ export class Monster extends ecs.Entity {
BattleMoveComp, BattleMoveComp,
MonModelComp, MonModelComp,
TalComp, TalComp,
EBusComp,
); );
} }
destroy(): void { destroy(): void {
this.remove(HeroViewComp); this.remove(MonViewComp);
this.remove(MonModelComp); this.remove(MonModelComp);
this.remove(TalComp); this.remove(TalComp);
this.remove(EBusComp);
super.destroy(); super.destroy();
} }
@@ -38,8 +41,6 @@ export class Monster extends ecs.Entity {
load(pos: Vec3 = Vec3.ZERO,scale:number = 1,uuid:number=1001,lv:number=1,monType:MonType=MonType.NORMAL, buffs: BuffConf[] = [],is_call=false) { load(pos: Vec3 = Vec3.ZERO,scale:number = 1,uuid:number=1001,lv:number=1,monType:MonType=MonType.NORMAL, buffs: BuffConf[] = [],is_call=false) {
scale=-1 scale=-1
let box_group=BoxSet.MONSTER let box_group=BoxSet.MONSTER
// console.log("mon load",uuid)
// this.addComponents<ecs.Comp>( MonModelComp, BattleMoveComp);
var scene = smc.map.MapView.scene; var scene = smc.map.MapView.scene;
var path = "game/heros/"+HeroInfo[uuid].path; var path = "game/heros/"+HeroInfo[uuid].path;
var prefab: Prefab = oops.res.get(path, Prefab)!; var prefab: Prefab = oops.res.get(path, Prefab)!;
@@ -49,7 +50,52 @@ export class Monster extends ecs.Entity {
const collider = node.getComponent(BoxCollider2D); const collider = node.getComponent(BoxCollider2D);
if (collider) collider.enabled = false; // 先禁用 // 延迟一帧启用碰撞体 if (collider) collider.enabled = false; // 先禁用 // 延迟一帧启用碰撞体
node.setPosition(pos) node.setPosition(pos)
this.hero_init(uuid,node,scale,box_group,lv,monType, buffs,is_call)
var view = node.getComponent(MonViewComp)!;
const model = this.get(MonModelComp);
let hero = HeroInfo[uuid]; // 共用英雄数据
// 设置 View 层属性(表现相关)
view.scale = scale;
view.box_group = box_group;
// 设置 Model 层属性(数据相关)
model.hero_uuid = uuid;
model.hero_name = hero.name;
model.lv = lv;
model.type = hero.type;
model.fac = FacSet.MON;
model.is_boss = monType == MonType.BOSS;
if(!model.is_boss){
model.is_kalami = true;
}
// 根据等级和类型获取怪物属性
const {hp, mp, ap, map, def, mdef} = getMonAttr(lv, uuid, monType);
// 初始化属性数组
model.Attrs = getAttrs();
model.hp = model.Attrs[Attrs.HP_MAX] = hp;
model.mp = model.Attrs[Attrs.MP_MAX] = mp;
model.Attrs[Attrs.DEF] = def;
model.Attrs[Attrs.MDEF] = mdef;
model.Attrs[Attrs.AP] = ap;
model.Attrs[Attrs.MAP] = map;
model.Attrs[Attrs.SPEED] = hero.speed;
model.Attrs[Attrs.DIS] = hero.dis;
// 初始化师兄
// 设置技能
for(let i=0; i<hero.skills.length; i++){
let skill = {
uuid: SkillSet[hero.skills[i]].uuid,
cd_max: SkillSet[hero.skills[i]].cd,
cost: SkillSet[hero.skills[i]].cost,
cd: 0
};
model.skills.push(skill);
}
this.add(view);
oops.message.dispatchEvent("monster_load",this) oops.message.dispatchEvent("monster_load",this)
// 初始化移动参数 // 初始化移动参数
@@ -57,53 +103,29 @@ export class Monster extends ecs.Entity {
move.direction = -1; // 向左移动 move.direction = -1; // 向左移动
move.targetX = -800; // 左边界 move.targetX = -800; // 左边界
smc.vmdata.mission_data.mon_num++ smc.vmdata.mission_data.mon_num++
// console.log("[Mon] mission_data.mon_num:",smc.vmdata.mission_data.mon_num)
} }
hero_init(uuid:number=1001,node:Node,scale:number=1,box_group=BoxSet.HERO,lv:number=1,monType:MonType=MonType.NORMAL,buffs: BuffConf[] = [],is_call=false) {
var hv = node.getComponent(HeroViewComp)!; reset() {
// 注: 自定义释放逻辑,视图层实现 ecs.IComp 接口的 ecs 组件需要手动释放
super.destroy();
// console.log("hero_init",buff)
let hero= HeroInfo[uuid] // 共用英雄数据
hv.scale = scale;
hv.fac = FacSet.MON;
hv.type = hero.type;
hv.is_boss = monType==MonType.BOSS;
if(!hv.is_boss){
hv.is_kalami=true
}
hv.box_group = box_group;
hv.hero_uuid= uuid;
hv.hero_name= hero.name;
const {hp,mp,ap,map,def,mdef}=getMonAttr(lv,uuid,monType)
// 初始化基础属性,并根据强度倍率调整
for(let i=0;i<hero.skills.length;i++){
let skill={ uuid:SkillSet[hero.skills[i]].uuid, cd_max:SkillSet[hero.skills[i]].cd,cost:SkillSet[hero.skills[i]].cost,cd:0 }
hv.skills.push(skill)
}
hv.base_ap=ap
hv.base_map=map
hv.base_def=def
hv.base_hp=hp
hv.base_mp=mp
hv.hp=hv.base_hp
hv.mp=hv.base_mp
hv.Attrs=getAttrs()
hv.NeAttrs=getNeAttrs()
hv.Attrs[Attrs.HP_MAX]=hv.base_hp
hv.Attrs[Attrs.MP_MAX]=hv.base_mp
hv.Attrs[Attrs.DEF]=hv.base_def
hv.Attrs[Attrs.AP]=hv.base_ap
hv.Attrs[Attrs.MAP]=hv.base_map
hv.Attrs[Attrs.SPEED]=hero.speed
hv.Attrs[Attrs.DIS]=hero.dis
// 初始化 buff/debuff 系统
hv.initAttrs();
this.add(hv);
} }
}
export class MonLifecycleSystem extends ecs.ComblockSystem
implements ecs.IEntityEnterSystem, ecs.IEntityRemoveSystem {
filter() {
return ecs.allOf(MonModelComp);
}
entityEnter(e: ecs.Entity): void {
// 英雄实体创建时的特殊处理
console.log(`怪物进入世界: ${e.get(MonModelComp).hero_name}`);
}
entityRemove(e: ecs.Entity): void {
// 英雄实体销毁时的清理工作
console.log(`怪物离开世界: ${e.get(MonModelComp).hero_name}`);
}
} }

View File

@@ -5,15 +5,200 @@
* @LastEditTime: 2022-08-17 13:43:25 * @LastEditTime: 2022-08-17 13:43:25
*/ */
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { Attrs, AttrsType, BType, NeAttrs } from "../common/config/HeroAttrs";
import { BuffConf } from "../common/config/SkillSet";
import { HeroInfo, AttrSet } from "../common/config/heroSet";
/** /**
* 角色属性数据 * 怪物属性数据模型 - 存储纯数据,不含表现逻辑
* 简化版本只保留临时buff系统移除持久型buff
*
* 注意HeroModelComp 中有详细的 ECS 架构分析,迁移方案见那个文件
*/ */
@ecs.register('MonModel') @ecs.register('MonModel')
export class MonModelComp extends ecs.Comp { export class MonModelComp extends ecs.Comp {
/** 角色编号 */
// ==================== 角色基础信息 ====================
hero_uuid: number = 1001;
hero_name: string = "monster";
lv: number = 1;
type: number = 0; // 0近战 1远程 2辅助
fac: number = 1; // 0:hero 1:monster默认为怪物
// ==================== 动态属性值 ====================
hp: number = 100; // 当前血量
mp: number = 100; // 当前魔法值
shield: number = 0; // 当前护盾
Attrs: any = []; // 最终属性数组经过Buff计算后
NeAttrs: any = []; // 负面状态数组
// ==================== Buff/Debuff 系统 ====================
/** 临时型buff数组 - 按时间自动过期怪物只使用临时buff */
BUFFS_TEMP: Record<number, Array<{value: number, BType: BType, remainTime: number}>> = {};
// ==================== 标记状态 ====================
is_dead: boolean = false;
is_count_dead: boolean = false;
is_boss: boolean = false;
is_big_boss: boolean = false;
is_master: boolean = false;
is_friend: boolean = false;
is_kalami: boolean = false;
// ==================== 计数统计 ====================
atk_count: number = 0; // 攻击次数
atked_count: number = 0; // 被攻击次数
// ==================== 技能配置 ====================
skills: any = [];
addBuff(buffConf: BuffConf) {
// 怪物只使用临时buff
const attrIndex = buffConf.buff;
if (!this.BUFFS_TEMP[attrIndex]) this.BUFFS_TEMP[attrIndex] = [];
this.BUFFS_TEMP[attrIndex].push({ value: buffConf.value, BType: buffConf.BType, remainTime: buffConf.time });
this.recalculateSingleAttr(attrIndex);
}
recalculateSingleAttr(attrIndex: number) {
const baseValues: Record<number, number> = {
[Attrs.HP_MAX]: this.base_hp, [Attrs.MP_MAX]: this.base_mp, [Attrs.DEF]: this.base_def,
[Attrs.AP]: this.base_ap, [Attrs.MAP]: this.base_map, [Attrs.SPEED]: this.base_speed, [Attrs.SHIELD_MAX]: 0
};
const baseVal = baseValues[attrIndex] !== undefined ? baseValues[attrIndex] : 0;
let totalValue = baseVal, totalRatio = 0;
// 怪物只计算临时buff
if (this.BUFFS_TEMP[attrIndex]) {
for (const buff of this.BUFFS_TEMP[attrIndex]) {
if (buff.BType === BType.VALUE) totalValue += buff.value;
else totalRatio += buff.value;
}
}
const attrType = AttrsType[attrIndex];
this.Attrs[attrIndex] = attrType === BType.RATIO ? totalValue + totalRatio : Math.floor(totalValue * (1 + totalRatio / 100));
this.clampSingleAttr(attrIndex);
}
private clampSingleAttr(attrIndex: number) {
switch(attrIndex) {
case Attrs.HP_MAX: case Attrs.MP_MAX: case Attrs.DEF: case Attrs.AP: case Attrs.MAP:
this.Attrs[attrIndex] = Math.max(1, this.Attrs[attrIndex]); break;
case Attrs.CRITICAL: case Attrs.DODGE: case Attrs.HIT:
this.Attrs[attrIndex] = Math.max(0, Math.min(AttrSet.ATTR_MAX, this.Attrs[attrIndex])); break;
}
}
updateTemporaryBuffsDebuffs(dt: number) {
const affectedAttrs = new Set<number>();
for (const attrIndex in this.BUFFS_TEMP) {
const buffs = this.BUFFS_TEMP[attrIndex];
buffs.forEach(buff => {
buff.remainTime -= dt;
if (buff.remainTime <= 0) buffs.splice(buffs.indexOf(buff), 1);
});
if (buffs.length === 0) {
delete this.BUFFS_TEMP[attrIndex];
affectedAttrs.add(parseInt(attrIndex));
}
}
for (const key in this.NeAttrs) {
this.NeAttrs[key].remainTime -= dt;
if (this.NeAttrs[key].remainTime <= 0) this.NeAttrs[key].remainTime = 0;
}
affectedAttrs.forEach(attrIndex => this.recalculateSingleAttr(attrIndex));
}
clearBuffs(attrIndex?: number, isBuff: boolean = true): void {
if (attrIndex === undefined) {
for (const idx in this.BUFFS_TEMP) this.clearBuffsForAttr(parseInt(idx), isBuff);
} else {
this.clearBuffsForAttr(attrIndex, isBuff);
}
}
private clearBuffsForAttr(attrIndex: number, isBuff: boolean): void {
if (!this.BUFFS_TEMP[attrIndex]) return;
this.BUFFS_TEMP[attrIndex] = this.BUFFS_TEMP[attrIndex].filter(buff => {
const shouldClear = isBuff ? buff.value > 0 : buff.value < 0;
return !shouldClear;
});
if (this.BUFFS_TEMP[attrIndex].length === 0) delete this.BUFFS_TEMP[attrIndex];
this.recalculateSingleAttr(attrIndex);
}
clearNeAttr(neAttrIndex: number): void {
if (this.NeAttrs[neAttrIndex]) {
this.NeAttrs[neAttrIndex].value = 0;
this.NeAttrs[neAttrIndex].time = 0;
}
}
clearAllNeAttrs(): void {
for (const key in this.NeAttrs) {
this.NeAttrs[key].value = 0;
this.NeAttrs[key].time = 0;
}
}
public isStun(): boolean {
return this.NeAttrs[NeAttrs.IN_STUN]?.time > 0;
}
public isFrost(): boolean {
return this.NeAttrs[NeAttrs.IN_FROST]?.time > 0;
}
reset() { reset() {
this.hero_uuid = 1001;
this.hero_name = "monster";
this.lv = 1;
this.type = 0;
this.fac = 1;
this.hp = 100;
this.mp = 100;
this.shield = 0;
this.Attrs = [];
this.NeAttrs = [];
this.BUFFS_TEMP = {}; // 只重置临时buff
this.is_dead = false;
this.is_count_dead = false;
this.is_boss = false;
this.is_big_boss = false;
this.is_master = false;
this.is_friend = false;
this.is_kalami = false;
this.atk_count = 0;
this.atked_count = 0;
this.skills = [];
} }
} }
/**
* ==================== 怪物属性更新系统 ====================
*
* 与 HeroAttrSystem 类似,但针对怪物
* 可以复用相同逻辑,也可以定制不同规则
*/
export class MonAttrSystem extends ecs.ComblockSystem
implements ecs.ISystemUpdate {
filter(): ecs.IMatcher {
return ecs.allOf(MonModelComp);
}
update(e: ecs.Entity): void {
const model = e.get(MonModelComp);
if (!model || model.is_dead) return;
// 怪物的属性更新逻辑(可以与英雄不同)
model.updateTemporaryBuffsDebuffs(this.dt);
// 怪物可能没有自然回复,或者回复速度不同
// model.mp += MonUpSet.MP * this.dt;
// model.hp += MonUpSet.HP * this.dt;
}
}

View File

@@ -0,0 +1,29 @@
import { _decorator } from "cc";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
const { ccclass, property } = _decorator;
/** 视图层对象 */
@ccclass('MonViewComp')
@ecs.register('MonView', false)
export class MonViewComp extends CCComp {
/** 视图层逻辑代码分离演示 */
start() {
// var entity = this.ent as ecs.Entity; // ecs.Entity 可转为当前模块的具体实体对象
// this.on(ModuleEvent.Cmd, this.onHandler, this);
}
/** 全局消息逻辑处理 */
// private onHandler(event: string, args: any) {
// switch (event) {
// case ModuleEvent.Cmd:
// break;
// }
// }
/** 视图对象通过 ecs.Entity.remove(ModuleViewComp) 删除组件是触发组件处理自定义释放逻辑 */
reset() {
this.node.destroy();
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "413afaac-8d90-4810-8036-a5dae3f9eea8",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -4,6 +4,8 @@ import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/modu
import { ItalConf, TalType, TalEType, talConf } from "../common/config/TalSet"; import { ItalConf, TalType, TalEType, talConf } from "../common/config/TalSet";
import { BuffConf, SkillSet } from "../common/config/SkillSet"; import { BuffConf, SkillSet } from "../common/config/SkillSet";
import { HeroInfo } from "../common/config/heroSet"; import { HeroInfo } from "../common/config/heroSet";
import { HeroViewComp } from "./HeroViewComp";
import { SkillConComp } from "./SkillConComp";
const { ccclass } = _decorator; const { ccclass } = _decorator;
@@ -35,7 +37,7 @@ interface TalEffect {
*/ */
@ccclass('TalComp') @ccclass('TalComp')
@ecs.register('TalComp', false) @ecs.register('TalComp', false)
export class TalComp extends CCComp { export class TalComp extends ecs.Comp {
/** 英雄视图组件引用,运行时获取避免循环引用 */ /** 英雄视图组件引用,运行时获取避免循环引用 */
private heroView: any = null; private heroView: any = null;
private skillCon:any=null; private skillCon:any=null;
@@ -57,8 +59,8 @@ export class TalComp extends CCComp {
*/ */
start() { start() {
// 运行时获取组件,避免编译时循环引用 // 运行时获取组件,避免编译时循环引用
this.heroView = this.node.getComponent("HeroViewComp" as any); this.heroView = this.ent.get(HeroViewComp);
this.skillCon = this.node.getComponent("SkillConComp" as any); this.skillCon = this.ent.get(SkillConComp);
if (this.heroView) { if (this.heroView) {
this.heroUuid = this.heroView.hero_uuid; this.heroUuid = this.heroView.hero_uuid;
this.initializeTalents(); this.initializeTalents();
@@ -165,6 +167,5 @@ export class TalComp extends CCComp {
reset() { reset() {
this.isInitialized = false; this.isInitialized = false;
this.node.destroy();
} }
} }