技能主要更新,修复卡顿并加入动画,以及各种其他更新。

This commit is contained in:
FloatGaming
2026-02-07 21:05:17 +08:00
parent abeca51be5
commit 1ac3cd0104
1349 changed files with 1526749 additions and 24850 deletions
+57 -39
View File
@@ -6,96 +6,100 @@ using System.Globalization;
[CreateAssetMenu(fileName = "NewSkillDefinition", menuName = "Combat/SkillDefinition")]
public class SkillDefinition : ScriptableObject
{
// Cache compiled RPN for formulas to avoid re-tokenizing every trigger.
private static readonly Dictionary<string, List<Token>> s_rpnCache = new Dictionary<string, List<Token>>();
private static readonly HashSet<string> s_badFormulaCache = new HashSet<string>();
public enum SkillTrigger
{
None,
OnGameStart,
OnNoteHit,
OnManaFull,
OnEnemyRevive, // 新增:当敌人登场时触发
OnEnemyDead, // 新增:当敌人死亡时触发
// 新增:当所有敌人被击败时触发
OnAttackZero,
OnEnemyRevive, // ˵dzʱ
OnEnemyDead, // ʱ
// е˱ʱ
OnAllEnemiesDefeated,
// 新增:当单位恢复生命值时触发(治疗)
// λֵָʱƣ
OnHPHealed,
// 新增:当单位失去生命值时触发(受伤/被伤害)
// λʧȥֵʱ/˺
OnHPLost,
// 新增:当单位获得法力时触发
// λ÷ʱ
OnManaGained,
// 新增:当单位失去法力时触发
// λʧȥʱ
OnManaLost,
// 新增:当生命值高于某阈值时触发(按百分比,0-1
// ֵijֵʱٷֱȣ0-1
OnHPAbovePercent,
// 新增:当生命值低于某阈值时触发(按百分比,0-1
// ֵijֵʱٷֱȣ0-1
OnHPBelowPercent
}
[Header("标识 (Identity)")]
[Tooltip("技能的内部唯一 ID,用于代码中识别此技能")]
[Header("ʶ (Identity)")]
[Tooltip("ܵڲΨһ IDڴʶ˼")]
public string skillId;
[Tooltip("技能在 UI 中显示的名称")]
[Tooltip(" UI ʾ")]
public string displayName;
[Tooltip("简短注释/摘要,仅供设计时参考,不影响运行")]
[Tooltip("ע/ժҪʱοӰ")]
[TextArea(1,2)]
public string summaryInfo;
[Header("效果 (Effect)")]
[Tooltip("技能的效果类型,例如即时伤害、持续伤害、治疗、增益/减益等")]
[Header("Ч (Effect)")]
[Tooltip("ܵЧͣ缴ʱ˺˺ơ/")]
public EffectType effectType = EffectType.DamageSingleEnemy;
[Tooltip("默认目标选择器:决定技能默认作用的目标范围(自身/所有友军/敌方等)")]
[Tooltip("ĬĿѡĬõĿ귶Χ/Ѿ/зȣ")]
public Selector defaultSelector = Selector.CurrentEnemies;
[Tooltip("当选择 FromFormula 时使用的表达式字符串。支持使用 AllyHero_SO 中同名数值字段作为变量,例如: attack, maxHP, maxMana, damageResistance, scoreEfficiency, ally_currentEXP, slot。示例: 'attack * 1.5 + maxHP * 0.1 + 20'。如果只填写一个数字(例如 '120'),则等同于指定固定数值。")]
[Tooltip("ѡ FromFormula ʱʹõıʽַ֧ʹñattack, maxHP, currentHP, maxMana, currentMana, damageResistance, scoreEfficiency, ally_currentEXP, currentScore( idolScore), slot, level/currentLevel/levelIDʾ: 'attack * 1.5 + maxHP * 0.1 + 20'ֻдһ֣ '120'̶ֵָͬ")]
[TextArea(2,4)]
public string formula = "";
[Tooltip("默认持续时间(秒),用于持续效果或 Buff 的持续时长。若为 0 则视为一次性/即时技能(不会按 tick 分发)")]
[Tooltip("ĬϳʱڳЧ Buff ijʱΪ 0 Ϊһ/ʱܣ tick ַ")]
public float defaultDuration = 0f;
[Tooltip("默认打点间隔(秒),用于持续效果的每次触发间隔(例如每秒扣血)。若为 0 则视为一次性/即时技能")]
[Tooltip("ĬϴڳЧÿδÿѪΪ 0 Ϊһ/ʱ")]
public float defaultTickInterval = 1f;
[Header("标志 (Flags)")]
[Tooltip("是否必须指定单体目标(true 表示此技能通常需要传入 specificTarget")]
[Header("־ (Flags)")]
[Tooltip("ǷָĿ꣨true ʾ˼ͨҪ specificTarget")]
public bool requiresSpecificTarget = false; // if true, skill usually targets a specific GameObject
[Tooltip("是否直接作用于目标的数据(true 表示 SkillBuilder 会直接调用 ICombatant/AllyCombatant,而非通过 EffectSystem 分发)")]
[Tooltip("ǷֱĿݣtrue ʾ SkillBuilder ֱӵ ICombatant/AllyCombatantͨ EffectSystem ַ")]
public bool operateDirectly = false; // if true, SkillBuilder will operate on ICombatant/AllyCombatant fields directly and update UI, instead of calling EffectSystem
[Tooltip("技能的描述或备注,便于设计时填写说明")]
[Tooltip("ܵעʱд˵")]
[TextArea(8,4)]
public string description;
[Header("触发条件 (Trigger)")]
[Tooltip("技能触发条件:进入游戏时、击打音符时、法力值满时等")]
[Header(" (Trigger)")]
[Tooltip("ܴϷʱʱֵʱ")]
public SkillTrigger triggerCondition = SkillTrigger.None;
[Tooltip("仅当 triggerCondition == OnNoteHit 时生效:达到该判定或更高时触发(可选 Miss/Good/Great/Perfect)。特别说明:选择 Miss 表示“仅在判定为 Miss 时触发”,其它判定(Good/Great/Perfect)不会触发。)")]
[Tooltip(" triggerCondition == OnNoteHit ʱЧжʱѡ Miss/Good/Great/Perfectر˵ѡ Miss ʾжΪ Miss ʱжGood/Great/Perfect")]
public NoteTriggerThreshold onNoteHitMinThreshold = NoteTriggerThreshold.Good;
[Tooltip("仅当 triggerCondition == OnNoteHit 时生效:指定此技能由 Tap(点击)/Hold(长按尾段)或 Either(任意)触发。若选择 Hold,则只有长音符尾段会触发;若选择 Tap,则仅短按触发;Either 则两者均可(但 Hold 仍可被显式禁用)。")]
[Tooltip(" triggerCondition == OnNoteHit ʱЧָ˼ Tap/HoldβΣ Eitherѡ Holdֻгβλᴥѡ Tap̰Either ߾ɣ Hold Կɱʽã")]
public NoteTypeTrigger noteTriggerType = NoteTypeTrigger.Either;
[Tooltip("仅当 triggerCondition == OnNoteHit 时生效:触发后的冷却时间(秒),在冷却时间内再次满足判定不会触发技能")]
[Tooltip(" triggerCondition == OnNoteHit ʱЧȴʱȴʱٴж")]
public float onNoteHitCooldown = 0f;
[Tooltip("仅当 triggerCondition HP 百分比相关时生效:生命值阈值(0-1),例如 0.3 表示 30%")]
[Tooltip(" triggerCondition Ϊ HP ٷֱʱЧֵֵ0-1 0.3 ʾ 30%")]
public float hpTriggerPercent = 0.5f;
// Minimum judge quality required to trigger on note hit
public enum NoteTriggerThreshold
{
// 当设置为 Miss 时,技能仅在判定为 Missquality == 0)时触发
// Ϊ Miss ʱܽжΪ Missquality == 0ʱ
Miss = 0,
Good = 1,
Great = 2,
Perfect = 3
}
// 新增:指定技能应由 Tap/ Hold/ Either 触发
// ָӦ Tap/ Hold/ Either
public enum NoteTypeTrigger
{
Tap,
@@ -103,10 +107,10 @@ public class SkillDefinition : ScriptableObject
Either
}
// 运行时辅助属性:当 defaultDuration defaultTickInterval 0 时,视作一次性即时技能
// ʱԣ defaultDuration defaultTickInterval Ϊ 0 ʱһԼʱ
public bool IsSingleInstance => defaultDuration <= 0f || defaultTickInterval <= 0f;
// 返回实际用于分发打点的间隔(若为一次性技能则返回 0)
// ʵڷַļΪһԼ򷵻 0
public float GetEffectiveTickInterval()
{
return IsSingleInstance ? 0f : Mathf.Max(0.0001f, defaultTickInterval);
@@ -114,7 +118,7 @@ public class SkillDefinition : ScriptableObject
private void OnValidate()
{
// 保证数值为非负
// ֵ֤ΪǸ
if (defaultDuration < 0f) defaultDuration = 0f;
if (defaultTickInterval < 0f) defaultTickInterval = 0f;
if (onNoteHitCooldown < 0f) onNoteHitCooldown = 0f;
@@ -131,17 +135,30 @@ public class SkillDefinition : ScriptableObject
if (string.IsNullOrWhiteSpace(expr)) return false;
try
{
var tokens = Tokenize(expr);
var rpn = ToRPN(tokens);
if (s_badFormulaCache.Contains(expr))
{
return false;
}
if (!s_rpnCache.TryGetValue(expr, out var rpn) || rpn == null)
{
var tokens = Tokenize(expr);
rpn = ToRPN(tokens);
s_rpnCache[expr] = rpn;
}
result = EvalRPN(rpn, variables);
return true;
}
catch (Exception e)
{
Debug.LogWarning($"SkillDefinition: failed to evaluate formula '{expr}': {e.Message}");
if (!s_badFormulaCache.Contains(expr))
{
s_badFormulaCache.Add(expr);
Debug.LogWarning($"SkillDefinition: failed to evaluate formula '{expr}': {e.Message}");
}
return false;
}
}
}}
// tokenization
private enum TokType { Number, Ident, Op, LParen, RParen }
@@ -283,3 +300,4 @@ public class SkillDefinition : ScriptableObject
return st.Pop();
}
}