技能主要更新,修复卡顿并加入动画,以及各种其他更新。
This commit is contained in:
@@ -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 时,技能仅在判定为 Miss(quality == 0)时触发
|
||||
// ������Ϊ Miss ʱ�����ܽ����ж�Ϊ Miss��quality == 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user