Files
heros/assets/script/game/hero/HeroViewComp.ts
panw b73d756106 refactor(hero): 移除怒气值相关属性和逻辑,修改hp显示逻辑
移除英雄属性组件中的怒气值(pow)属性及相关配置
删除英雄视图组件中怒气值显示和使用的逻辑
简化资源管理,移除不再使用的代码
2025-11-25 16:35:35 +08:00

471 lines
17 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Vec3, _decorator , v3,Collider2D,Contact2DType,Label ,Node,Prefab,instantiate,ProgressBar, Component, Material, Sprite, math, clamp, Game, tween, Color, BoxCollider2D, UITransform} from "cc";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
import { HeroSpine } from "./HeroSpine";
import { BoxSet, FacSet } from "../common/config/GameSet";
import { smc } from "../common/SingletonModuleComp";
import { EAnmConf, SkillSet,} from "../common/config/SkillSet";
import { oops } from "db://oops-framework/core/Oops";
import { GameEvent } from "../common/config/GameEvent";
import { TooltipTypes } from "../common/config/GameSet";
import { Attrs, } from "../common/config/HeroAttrs";
import { HeroAttrsComp } from "./HeroAttrsComp";
import { Tooltip } from "../skill/Tooltip";
import { timedCom } from "../skill/timedCom";
import { HeroInfo, HType } from "../common/config/heroSet";
import { Timer } from "db://oops-framework/core/common/timer/Timer";
const { ccclass, property } = _decorator;
/** 角色显示组件 */
export interface BuffInfo {
value: number;
remainTime?: number;
}
@ccclass('HeroViewComp') // 定义Cocos Creator 组件
@ecs.register('HeroView', false) // 定义ECS 组件
export class HeroViewComp extends CCComp {
// ==================== View 层属性(表现相关)====================
as: HeroSpine = null!
status:String = "idle"
scale: number = 1; // 显示方向
box_group:number = BoxSet.HERO; // 碰撞组
realDeadTime:number=10
deadCD:number=0
// 血条显示相关
hpBarShowTime:number = 5; // 血条显示持续时间(秒)
hpBarShowCD:number = 0; // 血条显示计时器
// ==================== UI 节点引用 ====================
private top_node: Node = null!;
// ==================== 直接访问 HeroAttrsComp ====================
get model() {
// 🔥 修复添加安全检查防止ent为null时的访问异常
if (!this.ent) {
console.warn("[HeroViewComp] ent is null, returning null for model");
return null;
}
return this.ent.get(HeroAttrsComp);
}
private damageQueue: Array<{
damage: number,
isCrit: boolean,
delay: number,
anm:string,
}> = [];
private isProcessingDamage: boolean = false;
private damageInterval: number = 0.01; // 伤害数字显示间隔
onLoad() {
this.as = this.getComponent(HeroSpine);
//console.log("[HeroViewComp]:hero view comp ",this.FIGHTCON)
this.on(GameEvent.FightEnd,this.do_fight_end,this)
const collider = this.node.getComponent(BoxCollider2D);
this.scheduleOnce(()=>{
if (collider) {
collider.enabled = true; // 先禁
collider.group = this.box_group; // 设置为英雄组
}
},0.1)
// let anm = this.node.getChildByName("anm")
// anm.setScale(anm.scale.x*0.8,anm.scale.y*0.8);
}
/** 视图层逻辑代码分离演示 */
start () {
this.as.idle()
// 初始化 UI 节点
this.initUINodes();
/** 方向 */
this.node.setScale(this.scale*this.node.scale.x,1*this.node.scale.y);
this.top_node.setScale(this.scale*this.top_node.scale.x,1*this.top_node.scale.y);
// if(this.model && this.model.is_boss){
// this.top_node.position=v3(this.node.position.x,this.node.position.y+70,0)
// }
/* 显示角色血*/
this.top_node.getChildByName("hp").active = true;
this.top_node.getChildByName("mp").active = true;
// 初始隐藏血条(被攻击后才显示)
this.top_node.active = false;
}
/** 初始化 UI 节点引用 */
private initUINodes() {
this.top_node = this.node.getChildByName("top");
let hp_y = this.node.getComponent(UITransform).height+10;
this.top_node.setPosition(0, hp_y, 0);
}
/**
* View 层每帧更新
* 注意:数据更新逻辑已移到 HeroAttrSystem这里只负责显示
*/
update(dt: number){
if(!smc.mission.play || smc.mission.pause) return;
// 🔥 修复添加安全检查防止在实体销毁过程中访问null的model
if(!this.ent) return;
if (!this.model) return;
if(this.model.is_dead){
this.deadCD+=dt
if(this.deadCD>=this.realDeadTime){
this.deadCD=0
this.realDead()
}
return
} ;
// 处理血条显示计时
if (this.hpBarShowCD > 0) {
this.hpBarShowCD -= dt;
if (this.hpBarShowCD <= 0) {
// 时间到,隐藏血条
this.top_node.active = false;
this.hpBarShowCD = 0;
}
}
// ✅ View 层职责:处理表现相关的逻辑
this.processDamageQueue(); // 伤害数字显示队列
// ✅ 更新 UI 显示(数据由 HeroAttrSystem 更新)
// 移除了每帧调用的 hp_show改为仅在需要时调用
this.hp_show(this.model.hp, this.model.Attrs[Attrs.HP_MAX]);
this.mp_show(this.model.mp, this.model.Attrs[Attrs.MP_MAX]);
this.show_shield(this.model.shield, this.model.Attrs[Attrs.SHIELD_MAX]);
}
/** 显示护盾 */
private show_shield(shield: number = 0, shield_max: number = 0) {
if(!this.top_node.active) return
let shield_progress = shield / shield_max;
this.node.getChildByName("shielded").active = shield > 0;
this.top_node.getChildByName("shield").active = shield > 0;
this.top_node.getChildByName("shield").getComponent(ProgressBar).progress = shield_progress;
this.scheduleOnce(() => {
this.top_node.getChildByName("shield").getChildByName("pb").getComponent(ProgressBar).progress = shield_progress;
}, 0.15);
}
/** 显示血量 */
private hp_show(hp: number, hp_max: number) {
// 不再基于血量是否满来决定显示状态,只更新进度条
if(!this.top_node.active) return;
let hp_progress = hp / hp_max;
this.top_node.getChildByName("hp").getComponent(ProgressBar).progress = hp_progress;
this.scheduleOnce(() => {
this.top_node.getChildByName("hp").getChildByName("hpb").getComponent(ProgressBar).progress = hp_progress;
}, 0.15);
}
/** 显示魔法值 */
private mp_show(mp: number, mp_max: number) {
if(!this.top_node.active) return
this.top_node.getChildByName("mp").getComponent(ProgressBar).progress = mp / mp_max;
this.scheduleOnce(() => {
this.top_node.getChildByName("mp").getChildByName("mpb").getComponent(ProgressBar).progress = mp / mp_max;
}, 0.15);
}
private pow_show(pow: number, pow_max: number) {
if(!this.top_node.active) return
this.top_node.getChildByName("pow").getComponent(ProgressBar).progress = pow / pow_max;
this.scheduleOnce(() => {
this.top_node.getChildByName("pow").getChildByName("mpb").getComponent(ProgressBar).progress = pow / pow_max;
}, 0.15);
}
/** 升级特效 */
private lv_up() {
var path = "game/skill/buff/buff_lvup";
var prefab: Prefab = oops.res.get(path, Prefab)!;
var node = instantiate(prefab);
node.parent = this.node;
}
/** 攻击力提升特效 */
private ap_up() {
var path = "game/skill/buff/buff_apup";
var prefab: Prefab = oops.res.get(path, Prefab)!;
var node = instantiate(prefab);
node.parent = this.node;
}
/** 显示 Buff 特效 */
private show_do_buff(name: string) {
var path = "game/skill/buff/" + name;
var prefab: Prefab = oops.res.get(path, Prefab)!;
var node = instantiate(prefab);
let pos = v3(this.node.position.x, this.node.position.y + 20, this.node.position.z);
node.parent = this.node.parent;
node.setPosition(pos);
}
/** 受击特效 */
private in_atked(anm: string = "atked", scale: number = 1) {
this.as.do_atked()
// var path = "game/skill/end/" + anm;
// var prefab: Prefab = oops.res.get(path, Prefab)!;
// var node = instantiate(prefab);
// node.setScale(node.scale.x * scale, node.scale.y);
// node.setPosition(this.node.position.x, this.node.position.y+50, this.node.position.z);
// node.parent = this.node.parent;
}
/** 冰冻特效 */
private in_iced(t: number = 1, ap: number = 0) {
var path = "game/skill/buff/buff_iced";
var prefab: Prefab = oops.res.get(path, Prefab)!;
var node = instantiate(prefab);
node.getComponent(timedCom).time = t;
node.getComponent(timedCom).ap = ap;
node.parent = this.node;
}
/** 眩晕特效 */
private in_yun(t: number = 1, ap: number = 0) {
var path = "game/skill/buff/buff_yun";
var prefab: Prefab = oops.res.get(path, Prefab)!;
var node = instantiate(prefab);
let height = this.node.getComponent(UITransform).height;
node.setPosition(v3(0, height));
node.getComponent(timedCom).time = t;
node.getComponent(timedCom).ap = ap;
node.parent = this.node;
}
/** 技能提示 */
private tooltip(type: number = 1, value: string = "", s_uuid: number = 1001, y: number = 120) {
let tip = ecs.getEntity<Tooltip>(Tooltip);
let pos = v3(0, 60);
pos.y = pos.y + y;
tip.load(pos, type, value, s_uuid, this.node);
}
/** 血量提示(伤害数字) */
private hp_tip(type: number = 1, value: string = "", s_uuid: number = 1001, y: number = 120) {
let tip = ecs.getEntity<Tooltip>(Tooltip);
let x = this.node.position.x;
let ny = this.node.getComponent(UITransform).height + y;
let pos = v3(x, ny, 0);
tip.load(pos, type, value, s_uuid, this.node.parent);
}
/** 治疗特效 */
private heathed() {
var path = "game/skill/buff/heathed";
var prefab: Prefab = oops.res.get(path, Prefab)!;
var node = instantiate(prefab);
node.parent = this.node;
}
// 注意BaseUp 逻辑已移到 HeroAttrSystem.update()
// 注意updateTemporaryBuffsDebuffs 逻辑已移到 HeroAttrSystem.update()
do_fight_end(){
this.as.do_buff()
}
get isActive() {
return this.ent.has(HeroViewComp) && this.node?.isValid;
}
/** 状态切换(动画) */
status_change(type:string){
this.status = type;
if(this.model.is_dead) return
if(type === "idle"){
this.as.idle();
} else if(type === "move"){
this.as.move();
}
}
add_shield(shield:number){
// 护盾数据更新由 Model 层处理,这里只负责视图表现
if(this.model && this.model.shield>0) this.show_shield(this.model.shield, this.model.Attrs[Attrs.SHIELD_MAX]);
}
health(hp: number = 0, is_num:boolean=true) {
// 生命值更新由 Model 层处理,这里只负责视图表现
this.heathed();
this.hp_tip(TooltipTypes.health, hp.toFixed(0));
this.top_node.active=true
this.hp_show(this.model.hp, this.model.Attrs[Attrs.HP_MAX]);
}
alive(){
this.model.is_dead=false
this.as.do_buff();
this.status_change("idle");
this.model.hp =this.model.Attrs[Attrs.HP_MAX]*50/100;
this.top_node.active=false
this.hpBarShowCD=0
}
/**
* 死亡视图表现
* 由 HeroAtkSystem 调用,只负责视觉效果和事件通知
*/
do_dead(){
// 添加安全检查
if (!this.model) return;
// 防止重复触发
if(this.model.is_count_dead) return;
this.model.is_count_dead = true; // 防止重复触发,必须存在防止重复调用
this.top_node.active=false
// 播放死亡特效
this.as.dead();
}
realDead(){
// 🔥 修复添加model安全检查防止实体销毁过程中的空指针异常
if (!this.model) {
console.warn("[HeroViewComp] realDead called but model is null, skipping");
return;
}
if(this.model.fac === FacSet.HERO){
// 英雄死亡:延迟触发死亡事件
oops.message.dispatchEvent(GameEvent.HeroDead, {
hero_uuid: this.model.hero_uuid
});
}
// 根据阵营触发不同事件
if(this.model.fac === FacSet.MON){
oops.message.dispatchEvent(GameEvent.MonDead, {
hero_uuid: this.model.hero_uuid,
position: this.node.position
});
}
this.ent.destroy();
}
do_atked(damage:number,isCrit:boolean,s_uuid:number,isBack:boolean=false){
// 受到攻击时显示血条并设置显示时间即使伤害为0也显示
this.top_node.active = true;
this.hpBarShowCD = this.hpBarShowTime;
if (damage <= 0) return;
// 视图层表现
let SConf=SkillSet[s_uuid]
if (isBack) this.back()
this.showDamage(damage, isCrit, SConf.DAnm);
}
private isBackingUp: boolean = false; // 🔥 添加后退状态标记
//后退
back(){
// 🔥 防止重复调用后退动画
if (this.isBackingUp) return;
if(this.model.fac==FacSet.MON) {
this.isBackingUp = true; // 🔥 设置后退状态
let tx=this.node.position.x+30
if(tx > 320) tx=320
tween(this.node)
.to(0.1, { position:v3(tx,this.node.position.y,0)})
.call(() => {
this.isBackingUp = false; // 🔥 动画完成后重置状态
})
.start()
}
//英雄不再后退
// if(this.model.fac==FacSet.HERO) {
// let tx=this.node.position.x-30
// if(tx < -320) tx=-320
// tween(this.node).to(0.1, { position:v3(tx,this.node.position.y,0)}).start()
// }
}
// 伤害计算和战斗逻辑已迁移到 HeroBattleSystem
/** 调试日志(已禁用) */
to_console(value:any, value2:any=null, value3:any=null){
// 调试用,生产环境已禁用
}
playSkillEffect(skill_id:number) {
let skill = SkillSet[skill_id]
switch(skill.act){
case "max":
this.as.max()
this.tooltip(TooltipTypes.skill, skill.name)
break
case "atk":
this.as.atk()
break
}
}
/** 显示伤害数字 */
showDamage(damage: number, isCrit: boolean,DAnm:number) {
let anm=EAnmConf[DAnm].path // DAnm和EAnm共用设定数组
this.damageQueue.push({
damage,
isCrit,
delay: this.damageInterval,
anm
});
}
/** 处理伤害队列 */
private processDamageQueue() {
if (this.isProcessingDamage || this.damageQueue.length === 0) return;
this.isProcessingDamage = true;
const damageInfo = this.damageQueue.shift()!;
this.showDamageImmediate(damageInfo.damage, damageInfo.isCrit,damageInfo.anm);
// 设置延时处理下一个伤
this.scheduleOnce(() => {
this.isProcessingDamage = false;
}, this.damageInterval);
}
/** 立即显示伤害效果 */
private showDamageImmediate(damage: number, isCrit: boolean, anm:string="atked") {
if (!this.model) return;
this.hp_show(this.model.hp, this.model.Attrs[Attrs.HP_MAX]);
this.in_atked(anm, this.model.fac==FacSet.HERO?1:-1);
if (isCrit) {
this.hp_tip(TooltipTypes.crit, damage.toFixed(0));
} else {
this.hp_tip(TooltipTypes.life, damage.toFixed(0));
}
}
reset() {
// 清理碰撞器事件监听
const collider = this.getComponent(Collider2D);
if (collider) {
collider.off(Contact2DType.BEGIN_CONTACT);
}
this.deadCD=0
this.hpBarShowCD=0
// 清理伤害队列
this.damageQueue.length = 0;
this.isProcessingDamage = false;
// 延迟销毁节点
this.scheduleOnce(() => {
this.node.destroy();
}, 0.1);
}
}