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

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

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

Сообщение lawson 06 дек 2014, 12:34

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

Способности

Теперь наконец закончив основную часть работы с компонентами(основную потому что мы еще к ним не раз вернемся), я перехожу
к самому главному - написанию самих способностей.

И как всегда начну немного с теории.
Ну начну конечно со способностей в таких играх как WarCraft и StarCraft, как я говорил раньше есть два типа способностей:
пассивная и активная, которые будут разными по реализации но они должны будут иметь какието общие черты - тоесть
наследоваться от одной главной способности, как и в случае компонента, эта главная способность будет содержать в себе
основополагающие свойства для каждого типа способности. Выглядеть будет примерно так для начала.
pic2.png

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

Теперь нужно написать эту базовую способность, я назвал этот класс AbilityObject
Синтаксис:
Используется csharp
public class AbilityObject {

}

Есть, теперь нужно решить какими БАЗОВЫМИ свойствами будет обладать этот объект чтобы потом передать эти свойства в
наследующиеся способности. Вы сами можете решить какими свойствами будет обладать каждая способность и объявит их в классе AbilityObject.
Сразу скажу что в моем приложении способностям не нужны были такие свойства как "уровень", или "колво маны", "апгрейды\улучшения", НО если вы разберетесь в этой части статьи вы сможете сами легко добавить эти свойства, я же использовал
упрощенный вариант способностей с минимальным набором параметров.

И так какие свойства я использовал:
name - имя способности
type - тип способности(перечисление): пассивная или активная
icon - иконка способности, т к независимо от типа любая способность должна иметь иконку.
abilityDescription - описание самой способности - краткое содержание.
Сначала создадим к статическом классе AbilitySettings перечисление AbilityType
Синтаксис:
Используется csharp
public enum AbilityType {
 Active,
 Passive
}

Я думаю не нужно пояснять что это за перечисление.

Теперь запишем все перечисленные мной свойства в класс AbilityObject
Синтаксис:
Используется csharp
public class AbilityObject {

 public readonly AbilitySettings.AbilityType type;

 public string name = "";
 public Texture2D icon = null;
 public string abilityDescription = "";

}

Как видите я использовал readonly тип для свойства type чтобы нельзя было после создания способности изменить ее тип на
другой.
И так, раз это класс AbilityObject базовый тогда мне нельзя будет создавать его экземпляры т к это будет происходить через
наследующиеся способности, поэтому я использовал здесь защищенный конструктор в который буду передавать нужные мне параметры.
Вот как будет выглядеть первоначальный вариант базовой способности
Синтаксис:
Используется csharp
public class AbilityObject {

 public readonly AbilitySettings.AbilityType type;

 public string name = "";
 public Texture2D icon = null;
 public string abilityDescription = "";

 protected AbilityObject(AbilitySettings.AbilityType type, string name) {
  this.type = type;
  this.name = name;
  this.abilityDescription = "New " + this.type.ToString() + " ability";
 }

}

Готово теперь у меня есть ГЛАВНАЯ способность и ее базовые свойства.
Можно переходит к создание отдельных типов способностей. Сразу замечу что в этой части статьи я рассмотрю только создание
ПАССИВНОЙ способности т к если я буду рассматривать оба типа способностей то объем этой части статьи очень сильно вырастет,
поэтому АКТИВНУЮ способность я рассмотрю в будущей части.

И так теперь будем продолжать иерархию способностей как было изображено на картинке выше - создадим пассивную способность.
Я назвал ее PassiveAbility и она будет наследоваться от AbilityObject
Синтаксис:
Используется csharp
public class PassiveAbility : AbilityObject {
 
 public PassiveAbility(string name) : base(AbilitySettings.AbilityType.Passive, name) {}
 
}

И... это все! Это вся пассивная способность, больше она не будет содержать ни каких свойств и методов и сейчас я поясню
почему.

Опять в теорию.
И так. Я начиная с первой части статьи говорю о том чтобы предоставить полную свободу пользователю при создании
способностей, и поэтому ввел компонентную модель в эту систему - где пользователь сам решает какими свойствами будет
обладать та или иная способность. Но а что если способность вообще не должна ничего делать а просто указывать на то что она
есть и все!? - допустим случай: персонаж может только перемещаться влево и вправо, далее персонаж приобретает способность
прыгать, но эта способность не имеет больше ни каких свойств или компонентов типа "высота прыжка" или какого временного
параметра, эта способность просто указывает на то что персонаж просто может прыгать, эта способность представляет собой
ПУСТЫШКУ, которая ни чего не делает т к не имеет КОНКРЕТНОЙ реализации. Из этого следует что мне нужно ввести еще один
подвид ПАССИВНОЙ способности - такой способности которую уже можно будет наполнить свойствами(компонентами).
Как видно на картинке, я продолжил иерархию ПАССИВНОЙ способности - пустышки добавив еще один объект ПАССИВНОЙ способности
уже с конкретной реализацией, которая может содержать в себе компоненты.
pic3.png

Теперь понятно что нужно создать еще одну ПАССИВНУЮ способность которая будет наследоваться от PassiveAbility, я назвал ее
PassiveCustomAbility и добавил одни публичный конструктор.
Синтаксис:
Используется csharp
public class PassiveCustomAbility : PassiveAbility {

 public PassiveCustomAbility(string name) : base(name) {}

}

Но теперь у меня уже есть два вида ПАССИВНОЙ способности, а также два вида АКТИВНОЙ способности в будущем, теперь когда я
буду создавать редактор способностей мне нужно будет както различать с каким подвидом способности именно я работаю, ведь я
не могу в способность ПУСТЫШКУ напихать компонентов. Поэтому я решил добавить необходимый инструментарий для работы со
способностями КОНКРЕТНОЙ реализации - добавить интерфейс с названием IAbilityComponentable.
pic4.png

На изображении видно что подвиды способности с КОНКРЕТНОЙ реализацией я объединяю этим интерфейсом IAbilityComponentable
чтобы удостовериться в том что я работаю с способностями которые могут содержать в себе компоненты.

Создадим теперь этот интерфейс и наполним его нужными инструментами для работы с компонентами
Синтаксис:
Используется csharp
public interface IAbilityComponentable {
 AbilityComponent[] components { get;}
 AbilityComponent AddComponent(AbilityComponent component);
 void RemoveComponents<T>() where T : AbilityComponent;
 void RemoveComponent(AbilityComponent component);
}

И так что же он содержит:
components - это массив всех компонентов что содержит способность
AddComponent - метод который будет добавлять компоненты.
RemoveComponents - метод который будет стирать все компоненты определенного типа AbilityComponent.
RemoveComponent - метод который просто будет стирать не нужный пользователю компонент
Ну а теперь надо наследовать класс PassiveCustomAbility от этого интерфейса
Синтаксис:
Используется csharp
public class PassiveCustomAbility : PassiveAbility, IAbilityComponentable {

 public PassiveCustomAbility(string name) : base(name) {}

}

Далее интерфейс потребует реализовать в классе PassiveCustomAbility все его инструменты.
Синтаксис:
Используется csharp
public class PassiveCustomAbility : PassiveAbility, IAbilityComponentable {
 
 private List<AbilityComponent> abilityComponents = new List<AbilityComponent>();
 private AbilityComponent[] abilityArrayComponents;
               
 public AbilityComponent AddComponent(AbilityComponent component) {

 }
               
 public void RemoveComponents<T>() where T : AbilityComponent {
                 
 }
               
 public void RemoveComponent(AbilityComponent component) {

 }
               
 public AbilityComponent[] components {
  get { return this.abilityArrayComponents;}
 }

 public PassiveCustomAbility(string name) : base(name) {
  this.abilityArrayComponents = this.abilityComponents.ToArray();
 }

}

Теперь заполним каждый метод интерфейса действиями, сначала метод AddComponent
Этот метод будет добавлять компонент в способность.
Синтаксис:
Используется csharp
public AbilityComponent AddComponent(AbilityComponent component) {
 this.abilityComponents.Add(component);
 this.abilityArrayComponents = this.abilityComponents.ToArray();
 return component;
}

Теперь метод RemoveComponent, этот метод будет удалять ненужный компонент у способности.
Синтаксис:
Используется csharp
public void RemoveComponent(AbilityComponent component) {
 for(int index = 0; index < this.abilityComponents.Count; index++) {
  if (this.abilityComponents[index] == component) {
   this.abilityComponents.RemoveAt(index);
   this.abilityArrayComponents = this.abilityComponents.ToArray();
   return;
  }
 }
}

Готово, теперь метод RemoveComponents<T>, этот метод будет стирать компоненты только определенного типа T, я пользовался
LINQ чтобы собрать компоненты типа T
Синтаксис:
Используется csharp
public void RemoveComponents<T>() where T : AbilityComponent {
 foreach(AbilityComponent component in this.abilityArrayComponents.Where(c => c is T))
  RemoveComponent(component);
}

Так теперь я могу удалять и добавлять любые компоненты в способность с помощью интерфейса IAbilityComponentable, но как же
с компонентами "особого" типа, вроде компонента "время" или AbilityTimeComponent который мы пометили особым аттрибутом
который будет давать понять способности что этот компонент может находится в ЕДИНСТВЕННОМ экземпляре у способности.
Мне нужно в методе AddComponent перед тем как добавить компонент проверить есть ли у этого компонента аттрибут
DisableComponentMultiplyUsage. Я сделал проверку так - добавил метод CheckForAttribute<T> в абстрактный класс базовый
AbilityComponent чтобы он давал мне знать есть ли аттрибут у компонента или нет.
Вот как он будет выглядеть в абстрактном классе AbilityComponent
Синтаксис:
Используется csharp
public abstract class AbilityComponent {
         
 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;
 }
         
}

Этот метод ищет аттрибут типа Т и когда находи нужный просто возвращает true значение - что значит что искомый аттрибут
имеется в наличии у компонента.

Так теперь у компонента есть метод проверки аттрибута и теперь надо добавить проверку на аттрибут
DisableComponentMultiplyUsage к компонента в методе AddComponent перед добавлением компонента в способность.
Я добавил несколько новый действий в метод AddComponent класса PassiveCustomAbility, а именно пару условий.
Синтаксис:
Используется csharp
public AbilityComponent AddComponent(AbilityComponent component) {
 if (this.abilityComponents.Any(c => c.GetType() == component.GetType())) {
  if (component.CheckForAttribute<DisableComponentMultiplyUsage>()) return null;
 }
 this.abilityComponents.Add(component);
 this.abilityArrayComponents = this.abilityComponents.ToArray();
 return component;
}

Здесь в начале метода перед добавлением компонента я делаю проверку есть ли у способности уже такой компонент и если есть -
то проверить есть ли аттрибут DisableComponentMultiplyUsage у компонента ведь если он присутствует то это значит что
нельзя добавлять ЕЩЕ один такой же компонент в способность и сразу же завершим метод AddComponent на этом месте.

И так вот как будет выглядеть окончательный вариант класса PassiveCustomAbility.
Синтаксис:
Используется csharp
public class PassiveCustomAbility : PassiveAbility, IAbilityComponentable {
 
 private List<AbilityComponent> abilityComponents = new List<AbilityComponent>();
 private AbilityComponent[] abilityArrayComponents;
               
 public AbilityComponent AddComponent(AbilityComponent component) {
  if (this.abilityComponents.Any(c => c.GetType() == component.GetType())) {
   if (component.CheckForAttribute<DisableComponentMultiplyUsage>()) return null;
  }
  this.abilityComponents.Add(component);
  this.abilityArrayComponents = this.abilityComponents.ToArray();
  return component;
 }
               
 public void RemoveComponents<T>() where T : AbilityComponent {
  foreach(AbilityComponent component in this.abilityArrayComponents.Where(c => c is T))
   RemoveComponent(component);           
 }
               
 public void RemoveComponent(AbilityComponent component) {
  for(int index = 0; index < this.abilityComponents.Count; index++) {
   if (this.abilityComponents[index] == component) {
    this.abilityComponents.RemoveAt(index);
    this.abilityArrayComponents = this.abilityComponents.ToArray();
    return;
   }
  }
 }
               
 public AbilityComponent[] components {
  get { return this.abilityArrayComponents;}
 }

 public PassiveCustomAbility(string name) : base(name) {
  this.abilityArrayComponents = this.abilityComponents.ToArray();
 }

}

Ну вот и готов ПАССИВНАЯ способность с КОНКРЕТНОЙ реализацией.

Заключение: Теперь когда я буду создавать способность в редакторе я смогу определить с каким именно видом способности я имею
дело с ПУСТЫШКОЙ или с КОНКРЕТНОЙ реализацией и от этого будет зависеть смогу ли я добавить какие либо свойства(компоненты)
в способность.

На этом я закону третью часть статьи, в следующей части я займусь АКТИВНОЙ способностью и вернемся немного к компонентам перед написанием редактора способностей.

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

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

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

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

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