Универсальная система способностей(спеллов) часть V

Научился сам? Помоги начинающему.

Универсальная система способностей(спеллов) часть V

Сообщение lawson 10 дек 2014, 23:04

Создание системы универсальных способностей(спеллов) на unity3D Часть V

Данные компонентов.

И так в этой части статьи я хочу вам рассказать как же собирать данные из компонентов, в основном там будет очень малая
программная часть, но достаточно теории т к я, еще раз скажу, хочу чтобы вы поняли как и почему это работает. Также вы
поймете почему базовый класс способности AbilityObject мы не объявили абстрактным как базовый
класс компонента AbilityComponent.

Как бы очевидно это не было, но данные будут собираться не через свойство "value" у компонента AbilityComponentValue, и
сейчас я на примере объясню почему.

Давайте создадим одну пассивную способность и наполним ее компонентами, просто для теста.
Я создал один маленький скриптик TestWorkOut и кинул его на камеру чтобы тестировать систему.
Синтаксис:
Используется csharp
public class TestWorkOut : MonoBehaviour {
 
 private void Start() {
   
 }

 private void Update() {

 }

}

Теперь давайте объявим переменную пассивной способности в старте.
Синтаксис:
Используется csharp
public class TestWorkOut : MonoBehaviour {

 private PassiveCustomAbility ability;

 private void Start() {
  this.ability = new PassiveCustomAbility("New ability");              
 }

}

Теперь после того как я создал способность я хочу добавить в нее несколько компонентов.
Я могу это делать разными способами, могу через CreateComponentForTarget сразу создавать компонент с целью, или использовать
любой шаблон, или использовать УНИВЕРСАЛЬНЫЙ шаблон AbilityComponentValue с любым значением - как всегда это решает
пользователь.
Я использовал все три варианта
Синтаксис:
Используется csharp
private void Start() {
 this.ability = new PassiveCustomAbility("New ability");
               
 AbilityComponent component = new AbilityFloatComponent("Float component", AbilitySettings.ComponentValueTarget.Armor);
 this.ability.AddComponent(component);
               
 component = new AbilityComponentValue<int>("Int component", AbilitySettings.ComponentValueTarget.Health);
 this.ability.AddComponent(component);
               
 component = AbilitySettings.CreateComponentForTarget(AbilitySettings.ComponentValueTarget.Special, "Special component");
 this.ability.AddComponent(component);
}

Так как видите я создал один компонент из шаблонов, один как универасальный с типом "int" и один специальный.
Теперь способность имеет все три компонента.
Давайте теперь проверим какие это компоненты - что у них за цель.
Для этого я создал вот такой цикл, и вывел в дебаг свойство targetValue у каждого компонента.
Синтаксис:
Используется csharp
foreach(AbilityComponent abilityComponent in this.ability.components) {
 Debug.Log("Component name: " + abilityComponent.name + ", component target: " + abilityComponent.targetValue);
}

Вот как теперь выглядит метод Start
Синтаксис:
Используется csharp
private void Start() {
 this.ability = new PassiveCustomAbility("New ability");
               
 AbilityComponent component = new AbilityFloatComponent("Float component", AbilitySettings.ComponentValueTarget.Armor);
 this.ability.AddComponent(component);
               
 component = new AbilityComponentValue<int>("Int component", AbilitySettings.ComponentValueTarget.Health);
 this.ability.AddComponent(component);
               
 component = AbilitySettings.CreateComponentForTarget(AbilitySettings.ComponentValueTarget.Special, "Special component");
 this.ability.AddComponent(component);

 foreach(AbilityComponent abilityComponent in this.ability.components) {
  Debug.Log("Component name: " + abilityComponent.name + ", component target: " + abilityComponent.targetValue);
 }

}
Теперь нажмите "Старт" и посмотрите что выведет Debug в консоль.
У меня вышло это:
pic6.png

Как видно на изображении, вывело все компоненты и их цели - какие и были добавлены.
Этот пример послужил нам ПРИМЕРНЫМ действием когда нам придется уже в игре добавлять способности к объектам, и когда нам
придется разбирать каждый компонент в способности чтобы применить его значения в ту или иную область согласно "цели".
Но теперь идем дальше: как видно из примера для любого нового созданного компонента я использовал тип компонента как
AbilityComponent что является базовым классом любого компонента, и способность содержит компоненты только в виде объекта AbilityComponent в свойстве components, но теперь когда мы хотим разобрать нашу способность на компоненты мы уже не будем
знать какой тип РЕАЛЬНОГО компонента содержит способность ведь они все становятся типом AbilityComponent, допустим шаблон
AbilityFloatComponent в способности становится типом AbilityComponent и мы уже не можем получить доступ к свойству "value" у
компонента т к объект AbilityComponent не имеет такого свойства - этот класс имеет только свойства name и targetValue,
свойство "value" имеет его наследующийся класс AbilityComponentValue.

Допустим если я сделаю так
Синтаксис:
Используется csharp
AbilityComponent component = new AbilityFloatComponent("Float component", AbilitySettings.ComponentValueTarget.Armor);

То я не смогу получить данные свойства "value" у объекта component
Синтаксис:
Используется csharp
component.value = 0f; //ОШИБКА

Очевидно что мне нужно будет провернуть такую схему чтобы получить значения свойства "value":
Синтаксис:
Используется csharp
AbilityFloatComponent component = new AbilityFloatComponent("Float component", AbilitySettings.ComponentValueTarget.Armor);
component.value = 1f;

Теперь у меня есть доступ к свойству "value" т к я ТОЧНО смог определить какой тип компонента я использую.
В этой ТОЧНОСТИ и есть загвоздка.

Как я сказал ранее все компоненты в способности представлены как AbilityComponent, поэтому у нас нет доступа к свойству
"value" хотя мы и знаем цель этого компонента. Чтобы решить эту проблему нам бы пришлось для КАЖДОГО типа(шаблона) компонента
точно установить его тип перед тем как взять у него значение его свойства, т к язык СиШарп строго типизированный, и что было
бы очень муторно и долго. Поэтому я решил так: в прошлой части статьи я писал что самый элементарный тип объекта в Сишарпе
это System.Object - любой объект можно представит как просто System.Object, поэтому, т к я знаю что любой компонент может
иметь любой тип данных, я буду представлять этот тип данных как System.Object, а дальше следуя цели компонента просто
установлю его свойства. Т к способности содержит компоненты в виде AbilityComponent я принял решение добавить свойство
objectValue которое вернет мне значение компонента как Sytem.Object
в абстрактном классе AbilityComponent, также я решил что это свойство будет также абстрактным - т к это свойство будет
возвращать значение "value" у компонента AbilityComponentValue.
Вот как теперь выглядит абстрактный класс AbilityComponent с свойством objectValue
Синтаксис:
Используется csharp
public abstract class AbilityComponent {
         
 public AbilitySettings.ComponentValueTarget targetValue { get; protected set;}
 public string name = "";
         
 public bool CheckForAttribute<T>() where T : Attribute {
  foreach(Attribute attribute in Attribute.GetCustomAttributes(this.GetType())) {
   if (attribute is T) return true;
  }
  return false;
 }
         
 public abstract System.Object objectValue { get;}
         
}


Теперь т к компонент AbilityComponentValue наследуется от AbilityComponent нужно перегрузить свойство objectValue
Синтаксис:
Используется csharp
public class AbilityComponentValue<T> : AbilityComponent {
               
 public T value;
 public string valueName = "";
               
 public sealed override System.Object objectValue {
  get { return this.value as System.Object;}
 }
               
 public AbilityComponentValue(string name, AbilitySettings.ComponentValueTarget target) {
  this.name = name;
  this.targetValue = target;
  this.valueName = this.targetValue.ToString();
 }
       
}

Готово. Теперь компонент AbilityComponentValue имеет перегруженное свойство objectValue которое будет возвращать его
УНИВЕРСАЛЬНОЕ значение "value" как просто System.Object. Заметьте что я перегрузил свойство objectValue и сразу же его сделал
НЕПЕРЕГРУЖАЕМЫМ для следующих поколений компонентов с помощью sealed чтобы другие компоненты не могли перегрузить это
свойство.

Так а теперь вернемся к примеру со способностью.
Теперь я могу узнать что же за значение содержит каждый компонент с помощью абстрактного свойства objectValue просто не много
расширив цикл перебора компонентов способности.
Синтаксис:
Используется csharp
foreach(AbilityComponent abilityComponent in this.ability.components) {
 Debug.Log("Component's name: " + abilityComponent.name + ", component's target: " + abilityComponent.targetValue + "\n" +
           "Value type: " + abilityComponent.objectValue.GetType() + ", value: " + abilityComponent.objectValue);
}

И давайте посмотрим что же теперь вышло в консоль.
pic7.png

На картинке видно что теперь помимо цели и имени компонента мы также можем узнать какой тип значения содержит компонент а
также само значение. В моем случае т к первые два компонента числа, я вижу что их значение равно "0", а вот последний
компонент содержит в себе "объект" как значение поэтому мы не видим что именно он содержит - строка пуста.

Заключение: на самом деле знать величину значения свойства objectValue нам не нужно при самом переборе компонентов, нам нужно
только знать его ТИП чтобы потом присвоить, согласно цели, нужному свойству объекта, который будет содержать способность с
компонентами. Это был очень важный момент в статье т к видно как же мы будем дальше извлекать значения из компонентов.
И так теперь я думаю понятно почему я не сделал базовый класс AbilityObject абстрактным как и базовый класс AbilityComponent,
т к базовый класс AbilityComponent будет содержать в будущем абстрактные свойства\методы которые в нем самом не будут
содержать какуюто реализацию это будут делать другие компоненты которые наследуются от этого базового класса.

Ну вот и все на этот раз. В следующем части я расскажу как работать со способностями в редакторе, я создам Окно редактора из
прошлой части, и рассмотрю базовые принципы.

Следующая часть

автор: этот хрен, он же llka, он же lawsonilka
У вас нет доступа для просмотра вложений в этом сообщении.
lawson
UNIверсал
 
Сообщения: 481
Зарегистрирован: 14 сен 2012, 21:20

Вернуться в Уроки

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 5