feat(技能): 为友方技能添加飞行特效并优化目标选择逻辑
- 新增友方技能释放时的飞行特效,包括抛物线动画和缩放旋转效果 - 重构 applyFriendlySkillEffects 方法,将特效播放与实际效果应用分离 - 调整 buff.prefab 的缩放比例从 0.2 增大到 0.5 以适配新特效 - 优化友方技能目标选择逻辑,确保特效从施法位置正确飞向目标
This commit is contained in:
@@ -99,8 +99,8 @@
|
|||||||
},
|
},
|
||||||
"_lscale": {
|
"_lscale": {
|
||||||
"__type__": "cc.Vec3",
|
"__type__": "cc.Vec3",
|
||||||
"x": 0.2,
|
"x": 0.5,
|
||||||
"y": 0.2,
|
"y": 0.5,
|
||||||
"z": 1
|
"z": 1
|
||||||
},
|
},
|
||||||
"_mobility": 0,
|
"_mobility": 0,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||||
import { Vec3 } from "cc";
|
import { Vec3, Prefab, instantiate, tween, Node } from "cc";
|
||||||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||||
import { HeroViewComp } from "./HeroViewComp";
|
import { HeroViewComp } from "./HeroViewComp";
|
||||||
import { DTType, RType, SkillConfig, SkillKind, SkillSet, SkillUpList, TGroup } from "../common/config/SkillSet";
|
import { DTType, RType, SkillConfig, SkillKind, SkillSet, SkillUpList, TGroup } from "../common/config/SkillSet";
|
||||||
@@ -113,7 +113,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
|||||||
if (isFriendly) {
|
if (isFriendly) {
|
||||||
const friendlyTargets = this.resolveFriendlyTargets(targetEids, FacSet.HERO);
|
const friendlyTargets = this.resolveFriendlyTargets(targetEids, FacSet.HERO);
|
||||||
if (friendlyTargets.length === 0) continue;
|
if (friendlyTargets.length === 0) continue;
|
||||||
this.applyFriendlySkillEffects(s_uuid, cardLv, config, null as any, mockAttrs, friendlyTargets, null);
|
this.applyFriendlySkillEffects(s_uuid, cardLv, config, null as any, mockAttrs, friendlyTargets, spawnPos);
|
||||||
} else {
|
} else {
|
||||||
const enemyTargetPos = this.resolveRepeatCastTargetPos(new Vec3(spawnPos.x + 300, spawnPos.y, spawnPos.z), i);
|
const enemyTargetPos = this.resolveRepeatCastTargetPos(new Vec3(spawnPos.x + 300, spawnPos.y, spawnPos.z), i);
|
||||||
this.createSkillEntityForCard(s_uuid, cardLv, mockAttrs, spawnPos, enemyTargetPos, i);
|
this.createSkillEntityForCard(s_uuid, cardLv, mockAttrs, spawnPos, enemyTargetPos, i);
|
||||||
@@ -384,37 +384,114 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
|||||||
const applyTargets = kind === SkillKind.Heal
|
const applyTargets = kind === SkillKind.Heal
|
||||||
? this.pickHealTargetsByMostMissingHp(targets, sHit)
|
? this.pickHealTargetsByMostMissingHp(targets, sHit)
|
||||||
: this.pickRandomFriendlyTargets(targets, sHit);
|
: this.pickRandomFriendlyTargets(targets, sHit);
|
||||||
|
|
||||||
for (const target of applyTargets) {
|
for (const target of applyTargets) {
|
||||||
if (!target.ent) continue;
|
const startPos = _heroView?.node?.position || _targetPos;
|
||||||
const model = target.ent.get(HeroAttrsComp);
|
if (startPos) {
|
||||||
if (!model || model.is_dead) continue;
|
this.playFriendlyCastEffect(startPos, target, () => {
|
||||||
if (kind === SkillKind.Heal && sAp !== 0) {
|
this.applyActualFriendlyEffect(target, kind, sAp, _cAttrsComp, config, sUp);
|
||||||
const addHp = Math.floor(sAp*_cAttrsComp.ap/100);//技能的ap是百分值 需要/100
|
});
|
||||||
model.add_hp(addHp);
|
} else {
|
||||||
target.health(addHp);
|
this.applyActualFriendlyEffect(target, kind, sAp, _cAttrsComp, config, sUp);
|
||||||
} else if (kind === SkillKind.Shield && sAp !== 0) {
|
|
||||||
const addShield = Math.max(0, Math.floor(sAp));
|
|
||||||
model.add_shield(addShield);
|
|
||||||
}
|
|
||||||
if (!config.buffs || config.buffs.length === 0) continue;
|
|
||||||
for (const buffConf of config.buffs) {
|
|
||||||
if (!buffConf) continue;
|
|
||||||
const sBuffAp=buffConf.value+sUp.buff_ap
|
|
||||||
const sBuffHp=buffConf.value+sUp.buff_hp
|
|
||||||
switch (buffConf.buff){
|
|
||||||
case Attrs.ap:
|
|
||||||
model.add_ap(sBuffAp)
|
|
||||||
//加工动画
|
|
||||||
break
|
|
||||||
case Attrs.hp_max:
|
|
||||||
model.add_hp_max(sBuffHp)
|
|
||||||
//加最大生命值动画
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private playFriendlyCastEffect(startPos: Vec3, target: HeroViewComp, callback: Function) {
|
||||||
|
if (!target.node || !target.node.isValid) {
|
||||||
|
callback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prefabPath = "game/skill/buff/buff";
|
||||||
|
const prefab = oops.res.get(prefabPath, Prefab);
|
||||||
|
if (!prefab) {
|
||||||
|
oops.res.load(prefabPath, Prefab, (err, res) => {
|
||||||
|
if (err) {
|
||||||
|
callback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.doPlayFriendlyCastEffect(startPos.clone(), target, res as Prefab, callback);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.doPlayFriendlyCastEffect(startPos.clone(), target, prefab as Prefab, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private doPlayFriendlyCastEffect(startPos: Vec3, target: HeroViewComp, prefab: Prefab, callback: Function) {
|
||||||
|
if (!target.node || !target.node.isValid) {
|
||||||
|
callback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const scene = smc.map?.MapView?.scene;
|
||||||
|
const parent = scene?.entityLayer?.node?.getChildByName("SKILL") || target.node.parent;
|
||||||
|
if (!parent) {
|
||||||
|
callback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const node = instantiate(prefab);
|
||||||
|
node.parent = parent;
|
||||||
|
node.setPosition(startPos);
|
||||||
|
|
||||||
|
const targetPos = target.node.position.clone();
|
||||||
|
targetPos.y += 50;
|
||||||
|
|
||||||
|
const midX = (startPos.x + targetPos.x) / 2;
|
||||||
|
const midY = Math.max(startPos.y, targetPos.y) + 200;
|
||||||
|
|
||||||
|
const dist = Vec3.distance(startPos, targetPos);
|
||||||
|
const duration = Math.min(0.6, Math.max(0.3, dist / 800));
|
||||||
|
|
||||||
|
const proxy = { ratio: 0 };
|
||||||
|
tween(proxy)
|
||||||
|
.to(duration, { ratio: 1 }, {
|
||||||
|
easing: 'sineOut',
|
||||||
|
onUpdate: () => {
|
||||||
|
if (!node.isValid) return;
|
||||||
|
const r = proxy.ratio;
|
||||||
|
const x = (1 - r) * (1 - r) * startPos.x + 2 * r * (1 - r) * midX + r * r * targetPos.x;
|
||||||
|
const y = (1 - r) * (1 - r) * startPos.y + 2 * r * (1 - r) * midY + r * r * targetPos.y;
|
||||||
|
node.setPosition(new Vec3(x, y, startPos.z));
|
||||||
|
node.setScale(new Vec3(1 - r * 0.3, 1 - r * 0.3, 1));
|
||||||
|
node.angle = -r * 720;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.call(() => {
|
||||||
|
if (node.isValid) node.destroy();
|
||||||
|
callback();
|
||||||
|
})
|
||||||
|
.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private applyActualFriendlyEffect(target: HeroViewComp, kind: SkillKind, sAp: number, _cAttrsComp: HeroAttrsComp, config: SkillConfig, sUp: any) {
|
||||||
|
if (!target.ent) return;
|
||||||
|
const model = target.ent.get(HeroAttrsComp);
|
||||||
|
if (!model || model.is_dead) return;
|
||||||
|
if (kind === SkillKind.Heal && sAp !== 0) {
|
||||||
|
const addHp = Math.floor(sAp*_cAttrsComp.ap/100);
|
||||||
|
model.add_hp(addHp);
|
||||||
|
target.health(addHp);
|
||||||
|
} else if (kind === SkillKind.Shield && sAp !== 0) {
|
||||||
|
const addShield = Math.max(0, Math.floor(sAp));
|
||||||
|
model.add_shield(addShield);
|
||||||
|
}
|
||||||
|
if (!config.buffs || config.buffs.length === 0) return;
|
||||||
|
for (const buffConf of config.buffs) {
|
||||||
|
if (!buffConf) continue;
|
||||||
|
const sBuffAp=buffConf.value+sUp.buff_ap
|
||||||
|
const sBuffHp=buffConf.value+sUp.buff_hp
|
||||||
|
switch (buffConf.buff){
|
||||||
|
case Attrs.ap:
|
||||||
|
model.add_ap(sBuffAp)
|
||||||
|
break
|
||||||
|
case Attrs.hp_max:
|
||||||
|
model.add_hp_max(sBuffHp)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private pickRandomFriendlyTargets(targets: HeroViewComp[], hitCount: number): HeroViewComp[] {
|
private pickRandomFriendlyTargets(targets: HeroViewComp[], hitCount: number): HeroViewComp[] {
|
||||||
if (!targets || targets.length === 0) return [];
|
if (!targets || targets.length === 0) return [];
|
||||||
const validHitCount = Math.max(1, Math.floor(hitCount));
|
const validHitCount = Math.max(1, Math.floor(hitCount));
|
||||||
|
|||||||
Reference in New Issue
Block a user