Почему не работаю условия из virtual метода в override методе
Всем доброго времени суток! Объясните пожалуйста, почему класс наследник переопределяет метод и выполняет весь функционал, за исключением условий?
У меня есть главный базовый класс "Skill" с protected virtual void Use() {}
Этот класс наследует другой базовый класс "PlayerSkill", который содержит функционал обозначенный ниже. Так вот, когда я наследую любым другим классом класс "PlayerSkill", то у меня в переопределенном методе выполняется весь функционал из Skill и PlayerSkill, но почему-то в открытую игнорируются условия прописанные ниже. С чем это может быть связано?
Иерархия классов такова: Skill -> PlayerSkill -> *Какой-то класс реализующий необходимую логику"
Класс Skill
public class Skill : MonoBehaviour
{
[SerializeField] private bool CanMoveAfterCasting;
private MoveHandler Move;
private Mana Mana;
[SerializeField] protected int ManaCost;
[SerializeField] protected float MinDistanceForCastingSpell;
[SerializeField] protected float CurrentCooldownTime;
[SerializeField] protected float CooldownTime;
[SerializeField] protected bool IsCooldown
{
get
{
if (CurrentCooldownTime <= 0)
return false;
else
return true;
}
}
protected Transform MyTransform;
protected void Awake()
{
MyTransform = transform;
Move = transform.root.GetComponent<MoveHandler>();
Mana = transform.root.GetComponent<Mana>();
}
protected virtual void Use()
{
if (CanMoveAfterCasting == false)
Move.Direction = Vector2.zero;
Mana.Restore(-ManaCost);
StartCoroutine(Cooldown());
}
private IEnumerator Cooldown()
{
CurrentCooldownTime = CooldownTime;
while (CurrentCooldownTime > 0)
{
CurrentCooldownTime -= 1f;
yield return new WaitForSeconds(1f);
}
}
}
Класс PlayerSkill
public class PlayerSkill : Skill
{
[SerializeField] protected UIDocument Document;
protected IDeathInspector TargetHandlerInspector;
private ParticleSystem CastingSpellEffect;
protected Button SkillButton;
private Label ManaCostText;
protected string SkillButtonName;
protected TargetHandler TargetDealer;
protected Transform Root;
[Inject]
private void Construct(Document Document)
{
this.Document = Document.Skill;
}
protected virtual void Start()
{
SkillButton.clicked += Use;
}
protected override void Use()
{
if (IsCooldown)
return;
if (TargetDealer.TargetTransform == null)
return;
float distance = (TargetDealer.TargetTransform.position - Root.position).sqrMagnitude;
if (distance > MinDistanceForCastingSpell)
return;
CastingSpellEffect.Play();
base.Use();
}
Для примера один из нескольких классов наследующих PlayerSkill
public class Rush : PlayerSkill
{
[SerializeField] private TrailRenderer Effect;
[SerializeField] private float RushSpeed;
[SerializeField] private float StunDuration;
[SerializeField] private float MinDistanceForRush;
private Transform Target;
protected override void Start()
{
SkillButtonName = "Rush";
base.Start();
}
protected override void Use()
{
Effect.emitting = true;
Target = TargetDealer.TargetTransform;
Target.GetComponent<IStun>().Stun(StunDuration);
base.Use();
}
}
Ответы (1 шт):
Мог ли бы сами провести простенькую отладку расставив Debug.Log и посмотрев результаты.
protected virtual void Use()
{
Debug.Log("Skill.Use");
...
}
protected override void Use()
{
Debug.Log("PlayerSkill.Use");
...
base.Use();
}
protected override void Use()
{
Debug.Log("Rush.Use");
...
base.Use();
}
Но вообще все очень плохо...
Skillне проверяетIsCooldownхотя запускает корутину, но проверяетPlayerSkillSkillпочему-то связан сMove, хотя это не его ответственностьPlayerSkillпочему-то связан с какими-то лейблы, баттоны, причем тут интерфейс?- в
PlayerSkillпочему-то присутствуют эффекты для визуализации - в
PlayerSkillпочему-то фигурируют трансформы кастера и цели - почему это все MonoBehaviour?
- почему вообще существуют
Skill,PlayerSkillиRush? В любой rpg разработчик по желанию может дать любому, любую способность.
Информация о способности это одна история типа
public class AbilityInfo : ScriptableObject
{
public Sprite Icon;
public string Name;
public string Discription;
public AbilityType Type;
public float Damage;
public float Range;
public float Cooldown;
public float ManaCost;
public float CastingDuration;
public AbilityShell Shell;
...
}
Объект имеющий визуализацию другая история
public class AbilityShell : MonoBehaviour
{
[SerializeField] private GameObject _spawnEffect;
[SerializeField] private GameObject _hitEffect;
private AbilityInfo _info;
private Damage _damage;
private Object _sorce;
public void Execute (AbilityInfo info, Damage damage, Object sorce)
{
_info = info;
_damage = damage;
_sorce = sorce;
}
...
}
Способность в перечне способностей исполнителя третья и ответственность у нее мало, контейнер для Info и изменчивые состояния типа статус перезарядки, уровня прокачки, число оставшихся использований. Ничего она не проверяет и ни о ком не знает. Весь пул способностей должен храниться в другом более высокоуровневом классе персонажа UnitAbilitys, который знает о персонаже, может проверить CanUse (int index) на ману, дистанцию, заспавнить где нужно согласно описанию, из руки, под ногами или на земле задав аргументы Execute, поставить флаг или состояние персонажа с "Move"/"Stay" на "CastingSpell", все что связано в контексте персонажа.
public class UnitAbility
{
public Action Used;
public readonly AbilityInfo Info;
private float _reload;
public UnitAbility (AbilityInfo info)
{
Info = info;
}
public bool IsCooldown => _reload > 0;
public float CooldownProgress => _reload/Info.Cooldown;
public void Use ()
{
_reload = Info.Cooldown;
Used?.Invoke();
}
public void Update (float delta)
{
if (_reload > 0)
_reload -= delta;
}
...
}
А интерфейс соответственно тоже отдельно, скармливаешь ему UnitAbility, он сам заглявывает в Info, посмотреть на иконку, имя и описание, отриагирует на использование и отобразит перезарядку. По нажатию сам спросит UnitAbilitys.CanUse(targetAbility) и даст команду UnitAbilitys.Use(targetAbility), либо через конструкцию bool TryUse().