战斗管理系统基础
This commit is contained in:
@@ -1,96 +0,0 @@
|
||||
import { _decorator, NodePool, Node, Label, Color, tween, Vec3, instantiate, Prefab, resources } from "cc";
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { DamageResult } from "./DamageComp";
|
||||
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
|
||||
import { DamageText } from "./DamageText";
|
||||
|
||||
@ecs.register('DamageShowSystem')
|
||||
export class DamageShowSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate{
|
||||
private _pool: NodePool = new NodePool();
|
||||
private _timers: Set<number> = new Set();
|
||||
private _prefab: Prefab | null = null;
|
||||
|
||||
init() {
|
||||
// 使用原生资源加载
|
||||
resources.load("game/skills/damageText", Prefab, (err, prefab) => {
|
||||
if (err) {
|
||||
console.error("预制体加载失败:", err);
|
||||
return;
|
||||
}
|
||||
this._prefab = prefab;
|
||||
console.log("预制体加载成功:", this._prefab);
|
||||
// 初始化对象池
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const node = instantiate(this._prefab!);
|
||||
this._pool.put(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
filter(): ecs.IMatcher {
|
||||
return ecs.allOf(DamageResult);
|
||||
}
|
||||
|
||||
update(e: ecs.Entity) {
|
||||
const res = e.get(DamageResult);
|
||||
this.scheduleShow(res);
|
||||
e.remove(DamageResult);
|
||||
}
|
||||
|
||||
private scheduleShow(res: DamageResult) {
|
||||
const timer = setTimeout(() => {
|
||||
let node = this._pool.get();
|
||||
if (!node || !node.isValid) {
|
||||
node = instantiate(this._prefab);
|
||||
}
|
||||
|
||||
const damageText = node.getComponent(DamageText);
|
||||
|
||||
if (!damageText) {
|
||||
console.error("Damage text prefab must have DamageText component!");
|
||||
return;
|
||||
}
|
||||
|
||||
this.setupText(node, res);
|
||||
this.playAnimation(node, res.position);
|
||||
|
||||
this._timers.delete(timer);
|
||||
}, res.delay * 1000);
|
||||
|
||||
this._timers.add(timer);
|
||||
}
|
||||
|
||||
private setupText(node: Node, res: DamageResult) {
|
||||
const damageText = node.getComponent(DamageText)!;
|
||||
const label = damageText.label;
|
||||
|
||||
if (res.isDodged) {
|
||||
label.string = "Miss";
|
||||
label.color = Color.GRAY;
|
||||
return;
|
||||
}
|
||||
|
||||
label.string = Math.round(res.value).toString();
|
||||
label.color = res.isCrit ? Color.RED : Color.WHITE;
|
||||
damageText.playEffect(res.isCrit);
|
||||
}
|
||||
|
||||
private playAnimation(node: Node, position: Vec3) {
|
||||
node.setPosition(position);
|
||||
tween(node)
|
||||
.by(0.5, { position: new Vec3(0, 100, 0) })
|
||||
.call(() => {
|
||||
if (this._pool.size() < 20) { // 控制最大缓存数量
|
||||
this._pool.put(node);
|
||||
} else {
|
||||
node.destroy();
|
||||
}
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
this._timers.forEach(clearTimeout);
|
||||
this._pool.clear();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "9c86e3d7-f8da-4e74-b180-76fe3f1fda90",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -23,8 +23,13 @@ export class DamageSystem extends ecs.ComblockSystem implements ecs.ISystemUpdat
|
||||
const sourceView = req.source.get(HeroViewComp);
|
||||
let final = req.baseValue;
|
||||
|
||||
// 伤害浮动(±10%)
|
||||
const damageFloat = 0.9 + Math.random() * 0.2; // 0.9~1.1
|
||||
final *= damageFloat;
|
||||
final = Math.round(final);
|
||||
|
||||
// 闪避判定
|
||||
if (Math.random() < targetView.dodge) {
|
||||
if (Math.random()*100 < targetView.dodge) {
|
||||
const result = new DamageResult();
|
||||
result.isDodged = true;
|
||||
return result;
|
||||
@@ -32,21 +37,24 @@ export class DamageSystem extends ecs.ComblockSystem implements ecs.ISystemUpdat
|
||||
|
||||
// 护甲减伤
|
||||
if (!req.ignoreDefense) {
|
||||
final -= targetView.def * 0.5;
|
||||
const effectiveArmor = Math.min(targetView.def, 300); // 最大减伤75%
|
||||
const damageReduction = effectiveArmor / (effectiveArmor + 100);
|
||||
final *= (1 - damageReduction);
|
||||
final = Math.round(final); // 四舍五入取整
|
||||
}
|
||||
|
||||
// 暴击判定
|
||||
let isCrit = false;
|
||||
if (req.canCrit) {
|
||||
const critRate = sourceView.crit;
|
||||
if (Math.random() < critRate) {
|
||||
if (Math.random() * 100 < critRate) {
|
||||
final *= 1.5;
|
||||
isCrit = true;
|
||||
}
|
||||
}
|
||||
|
||||
const result = new DamageResult();
|
||||
result.value = Math.max(0, final);
|
||||
result.value = Math.max(1, final); // 确保最小伤害为1
|
||||
result.isCrit = isCrit;
|
||||
result.position = req.target.get(HeroViewComp).node.position;
|
||||
result.delay = req.delay;
|
||||
@@ -55,20 +63,52 @@ export class DamageSystem extends ecs.ComblockSystem implements ecs.ISystemUpdat
|
||||
|
||||
private applyDamage(target: ecs.Entity, result: DamageResult) {
|
||||
const view = target.get(HeroViewComp);
|
||||
view.hp -= result.value;
|
||||
// 添加伤害结果组件
|
||||
const comp = target.add(DamageResult);
|
||||
comp.value = result.value;
|
||||
comp.isCrit = result.isCrit;
|
||||
comp.position = result.position;
|
||||
comp.delay = result.delay;
|
||||
comp.isDodged = result.isDodged;
|
||||
if (!view.ent.has(HeroViewComp)) return;
|
||||
this.scheduleOnce(()=>{
|
||||
// 护盾优先吸收伤害
|
||||
let remainingDamage = result.value;
|
||||
if(result.isDodged){
|
||||
view.BUFFCOMP.tooltip(5,"*闪避*");
|
||||
return;
|
||||
}
|
||||
if (view.shield > 0) {
|
||||
const shieldAbsorb = Math.min(view.shield, remainingDamage);
|
||||
view.shield -= shieldAbsorb;
|
||||
remainingDamage -= shieldAbsorb;
|
||||
|
||||
if (view.shield <= 0) {
|
||||
view.BUFFCOMP.show_shield(false);
|
||||
}
|
||||
}
|
||||
|
||||
// 剩余伤害扣除血量
|
||||
if (remainingDamage > 0) {
|
||||
view.hp -= remainingDamage;
|
||||
view.showDamage(result.value, result.isCrit);
|
||||
}else{
|
||||
view.BUFFCOMP.tooltip(5,"*吸收*");
|
||||
}
|
||||
|
||||
|
||||
// 触发伤害事件
|
||||
}, result.delay)
|
||||
|
||||
// 直接触发事件
|
||||
oops.message.dispatchEvent("OnDamage", {
|
||||
target,
|
||||
damage: result.value,
|
||||
isCrit: result.isCrit
|
||||
});
|
||||
}
|
||||
|
||||
private scheduleOnce(callback: () => void, delay: number) {
|
||||
const timer = setTimeout(() => {
|
||||
callback();
|
||||
delete this._timers[timer];
|
||||
}, delay * 1000);
|
||||
this._timers[timer] = timer;
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
Object.values(this._timers).forEach(clearTimeout);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { _decorator, Component, Label, ParticleSystem } from "cc";
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('DamageText')
|
||||
export class DamageText extends Component {
|
||||
@property(Label)
|
||||
label: Label = null!;
|
||||
|
||||
@property(ParticleSystem)
|
||||
critEffect: ParticleSystem = null!;
|
||||
|
||||
playEffect(isCrit: boolean) {
|
||||
if (isCrit && this.critEffect) {
|
||||
this.critEffect.play();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "96d1637a-2dfb-4c34-b60c-44144656c616",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
Reference in New Issue
Block a user