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

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

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

Сообщение lawsonilka 24 дек 2014, 18:20

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

Менеджер способностей. Обработка данных.

В прошлой части я закончил на объявлении класса менеджера и абстрактного класса AbilityBihaviour.
В этой части я расскажу каким образом будут храниться и обрабатываться способности.
Но сначала вернусь к менеджеру и дополню его парой методов: AddAbility который будет добавлять способность, RemoveAbility
который будет удалять ненужную способность и UseAbility который будет использовать указанную способность - вроде все просто.
Синтаксис:
Используется csharp
[DisallowMultipleComponent]
public class SpellManager : MonoBehaviour {

 public AbilityBihaviour objectSpellBihaviour { get; private set;}

 protected virtual void Awake() {
  this.objectSpellBihaviour = this.GetComponent<AbilityBihaviour>();
 }

 protected virtual void Start() {

 }

 public void AddAbility(AbilityObject ability) {

 }

 public void RemoveAbility(AbilityObject ability) {

 }

 public void UseAbility(AbilityObject ability) {

 }

 protected void SendAbilityEvent(SpellManagerData.AbilitySendEvent e, AbilityObject ability) {
  if (this.objectSpellBihaviour == null) {
   this.SendMessage(e.ToString(), ability, SendMessageOptions.DontRequireReceiver);
  } else {
   switch(e) {
    case SpellManagerData.AbilitySendEvent.OnAbilityAdded: this.objectSpellBihaviour.OnAbilityAdded(ability);
    break;
    case SpellManagerData.AbilitySendEvent.OnAbilityRemoved: this.objectSpellBihaviour.OnAbilityRemoved(ability);
    break;
    case SpellManagerData.AbilitySendEvent.OnAbilityUsed: this.objectSpellBihaviour.OnAbilityUsed(ability);
    break;
   }
  }
 }

}

Готово. Пока что эти методы будут пустыми пока мы не разберемся какие действия мы хотим в них добавить.

Вернемся опять к теории, и начнем с способностей.

В моем приложении у объектов могут быть разные способности но также они могут и обладать идентичными способностями, тоесть
допустим я создал способность и раздал всем объектам ее ссылку, теперь получается все объекты имеют ссылку на одну и туже
способность, а значит если хоть один объект решит что то исправить в этой способности - то эти исправления каснутся всех
других объектов, следовательно мне нужно как то обезопасить способность от изменений со стороны объектов что их хранит. И тогда
я решил написать некую обертку, эта обертка будет хранить в себе способность, точнее ссылку на нее, как абсолютное свойство
(readonly) это свойство нельзя будет изменить - оно будет предназначено только для чтения. Но т к существует всего два вида
способностей и они довольно таки сильно отличаются мне придется также создать две отдельные обертки для каждого вида.
И я начал с объявления базового класса - обертки, теперь перейдем к програмной части, создадим класс AbilityData, с, пока
что, одним свойством и защищенным конструктором.
Синтаксис:
Используется csharp
public class AbilityData {

 public readonly AbilityObject ability = null;
               
 protected AbilityData(AbilityObject ability) {
  this.ability = ability;
 }

}

Теперь нужно унаследовать два типа класса - обертки для каждого типа способности.
Я назвал эти классы ActiveAbilityData и PassiveAbilityData
Синтаксис:
Используется csharp
public sealed class PassiveAbilityData : AbilityData {
               
 public PassiveAbilityData(PassiveAbility ability) : base(ability) {}
               
 public new PassiveAbility ability {
  get { return base.ability as PassiveAbility;}
 }
               
}

public sealed class ActiveAbilityData : AbilityData {
 
 public ActiveAbilityData(ActiveAbility ability) : base(ability) {}

 public new ActiveAbility ability {
  get { return base.ability as ActiveAbility;}
 }
 
}

Как видите я сделал эти классы запечатанными(sealed) и также объявил новое(new) свойство ability, т к у базового класса уже
есть свойство ability оно вернет только общий тип способности AbilityObject, а унаследовавщиеся классы уже могут вернуть
мне способность определенного типа что я и сделал в этих новых свойствах.

Готово. Теперь у нас есть обертки которые будут хранить неизменяемую переменную способности. Мы еще вернемся к этим обертками
т к их нужно будет наполнить еще несколькими свойствами, в особенности ту что хранит активную способность.

Теперь можно возвращаться опять к менеджеру и создать в нем список(массив) этих оберток который будет хранить в себе
способности.

По ходу работы, класс менеджера будет увеличивать и увеличиваться в размерах, поэтому я создал отдельный класс как локальную
базу данных где я мог бы хранить именно данные(способности, списки и тд), а обрабатывать эти данные уже в самом менеджере.
Поэтому давайте создадим такой класс который бы хранил данные менеджера, я назвал такой класс AbilitiesManagerData
Синтаксис:
Используется csharp
public sealed class AbilitiesManagerData {

}

Теперь в нем давайте создадим наш список в котором будем хранить способности.
Синтаксис:
Используется csharp
public sealed class AbilitiesManagerData {

 private readonly Dictionary<AbilitySettings.AbilityType, List<AbilityData>> abilities;

}

Как видите это не простой список - это список типа Dictionary который хранит данные в виде <ключ, данные>, я использовал
именно такого вида список потому что им легче оперировать имея разные ключи. Ключами у нас будут выступать типы способностей
- их всего два: активная и пассивная, а значениями будут списки где уже будут храниться способности.
Теперь объявим в этом классе конструктор
Синтаксис:
Используется csharp
public sealed class AbilitiesManagerData {

 private readonly Dictionary<AbilitySettings.AbilityType, List<AbilityData>> abilities;

 public AbilitiesManagerData() {
  this.abilities = new Dictionary<AbilitySettings.AbilityType, List<AbilityData>>();
  this.abilities.Add(AbilitySettings.AbilityType.Active, new List<AbilityData>());
  this.abilities.Add(AbilitySettings.AbilityType.Passive, new List<AbilityData>());
 }

}

В конструкторе я сначала инициализирую список, потом под каждый ключ создаю список способностей.
Готово теперь объявим переменную этого класса в самом менеджере.
Синтаксис:
Используется csharp
[DisallowMultipleComponent]
public class SpellManager : MonoBehaviour {

 public AbilityBihaviour objectSpellBihaviour { get; private set;}

 protected AbilitiesManagerData data { get; private set;}

 protected virtual void Awake() {
  this.objectSpellBihaviour = this.GetComponent<AbilityBihaviour>();
  this.data = new AbilitiesManagerData();
 }

 protected virtual void Start() {

 }

 public void AddAbility(AbilityObject ability) {

 }

 public void RemoveAbility(AbilityObject ability) {

 }

 public void UseAbility(AbilityObject ability) {

 }

 protected void SendAbilityEvent(SpellManagerData.AbilitySendEvent e, AbilityObject ability) {
  if (this.objectSpellBihaviour == null) {
   this.SendMessage(e.ToString(), ability, SendMessageOptions.DontRequireReceiver);
  } else {
   switch(e) {
    case SpellManagerData.AbilitySendEvent.OnAbilityAdded: this.objectSpellBihaviour.OnAbilityAdded(ability);
    break;
    case SpellManagerData.AbilitySendEvent.OnAbilityRemoved: this.objectSpellBihaviour.OnAbilityRemoved(ability);
    break;
    case SpellManagerData.AbilitySendEvent.OnAbilityUsed: this.objectSpellBihaviour.OnAbilityUsed(ability);
    break;
   }
  }
 }

}

Я сделал свойство data защищенного типа т к ни в коем случае НЕЛЬЗЯ допустить чтобы объект мог получить доступ к способности
вне менеджера, иначе отсюда начнут расти проблемы с нечестным использованием системы способностей.

Как вы видите я сделал список в классе AbilitiesManagerData приватным чтобы опять же не допустить вывод данных вне области
работ менеджера, но все же прочитать эти данные возможность будет.

Допустим если я хочу узнать сколько вообще способностей у объекта или есть ли у него определенная способность или нет, или
вообще как я смогу рисовать иконку способности если это свойство закрыто. Для этого мне нужно вернуть список способностей.
Но все способности закрыты под ключом, и мало того они еще все находятся в виде базового класса AbilityData, и выходом из
этой ситуации стал НУМЕРОВАННЫЙ список точнее список типа IEnumerable который будет возвращать мне список объектов того вида
которого я захочу - опять же здесь мы имеем дело с преобразованием.
И так как это будет выглядеть в классе AbilitiesManagerData
Синтаксис:
Используется csharp
public sealed class AbilitiesManagerData {

 private readonly Dictionary<AbilitySettings.AbilityType, List<AbilityData>> abilities;

 public IEnumerable<ActiveAbilityData> activeAbilities {
  get {
   foreach(AbilityData data in this.abilities[AbilitySettings.AbilityType.Active])
    yield return data as ActiveAbilityData;
  }
 }
                 
 public IEnumerable<PassiveAbilityData> passiveAbilities {
  get {
   foreach(AbilityData data in this.abilities[AbilitySettings.AbilityType.Passive])
    yield return data as PassiveAbilityData;
  }
 }

 public AbilitiesManagerData() {
  this.abilities = new Dictionary<AbilitySettings.AbilityType, List<AbilityData>>();
  this.abilities.Add(AbilitySettings.AbilityType.Active, new List<AbilityData>());
  this.abilities.Add(AbilitySettings.AbilityType.Passive, new List<AbilityData>());
 }

}

Хоть это
Синтаксис:
Используется csharp
public IEnumerable<PassiveAbilityData> passiveAbilities {
 get {
  foreach(AbilityData data in this.abilities[AbilitySettings.AbilityType.Passive])
   yield return data as PassiveAbilityData;
 }
}

может и показаться сложным для понимая новичкам на самом делел здесь заложен простой принцип.
Ведь на самом деле такая запись практически равносильна записи такого вида
Синтаксис:
Используется csharp
public PassiveAbilityData[] passiveAbilities {
 get {
  return this.abilities[AbilitySettings.AbilityType.Passive].Select(d => d as PassiveAbilityData).ToArray();
 }
}

или более понятной
Синтаксис:
Используется csharp
public List<PassiveAbilityData> passiveAbilities {
 get {
  List<PassiveAbilityData> list = new List<PassiveAbilityData>();
  foreach(AbilityData data in this.abilities[AbilitySettings.AbilityType.Passive])
   list.Add(data as PassiveAbilityData);
  return list;
 }
}

Все эти три записи по функционалу похожи, но в первом варианте главную задачу выполняет yeild - инструкция.
Дело в том что когда вы используете цикл типа foreach обычно мы заранее уже имеем список(массив) по которому будет проходить
цикл - тоесть список(массив) уже заранее определен, в случае же с yeild мы также получаем массив на выходе но пока цикл
перебирает элемент за елементом мы можем выполнять какието действия с этим елементом не в самом цикле а сразу в массиве, это
очень полезная вещь на самом деле. И если хотите с ней лучше разобраться то рекомендую вот это видео из youtube.
Видео

Ну а мы движемся дальше.
Как вы помните активная способность имеет кнопку на которую нажав мы можем ее активировать через всем известный Input, а
значит нам нужно подключить метод Update в менеджере где мы будет обрабатывать нажатия на клавииши.
Синтаксис:
Используется csharp
[DisallowMultipleComponent]
public class SpellManager : MonoBehaviour {

 public AbilityBihaviour objectSpellBihaviour { get; private set;}

 protected AbilitiesManagerData data { get; private set;}

 protected virtual void Awake() {
  this.objectSpellBihaviour = this.GetComponent<AbilityBihaviour>();
  this.data = new AbilitiesManagerData();
 }

 protected virtual void Start() {

 }

 protected virtual void Update() {

 }

 public void AddAbility(AbilityObject ability) {

 }

 public void RemoveAbility(AbilityObject ability) {

 }

 public void UseAbility(AbilityObject ability) {

 }

 protected void SendAbilityEvent(SpellManagerData.AbilitySendEvent e, AbilityObject ability) {
  if (this.objectSpellBihaviour == null) {
   this.SendMessage(e.ToString(), ability, SendMessageOptions.DontRequireReceiver);
  } else {
   switch(e) {
    case SpellManagerData.AbilitySendEvent.OnAbilityAdded: this.objectSpellBihaviour.OnAbilityAdded(ability);
    break;
    case SpellManagerData.AbilitySendEvent.OnAbilityRemoved: this.objectSpellBihaviour.OnAbilityRemoved(ability);
    break;
    case SpellManagerData.AbilitySendEvent.OnAbilityUsed: this.objectSpellBihaviour.OnAbilityUsed(ability);
    break;
   }
  }
 }

}

Я сделал метод Update тоже виртуальным чтобы будущие наследующиеся классы могли перегрузить его.
Теперь пройдемся циклом по списку активных способностей чтобы знать какую способность мы активировали.
Синтаксис:
Используется csharp
protected virtual void Update() {
 foreach(ActiveAbilityData data in this.data.activeAbilities) {
  if (Input.GetKeyUp(data.ability.castKey)) {
   Debug.Log("User used: " + data.ability.name);
   return;
  }
 }
}

Готово теперь при нажатии определенной кнопки мы будет знать какая способность сработала.

Теперь давайте рассмотрим работу метода AddAbility который должен добавлять новые способности в менеджер.
Как я упоминал в прошлой части при добавлении разного типа способностей мы имеем не много разную реализацию метода AddAbility
Давайте наполним этот метод некоторыми действиями
Синтаксис:
Используется csharp
public void AddAbility(AbilityObject ability) {
 AbilityData data = null;
 if (ability.type == AbilitySettings.AbilityType.Active)
  data = new ActiveAbilityData(ability as ActiveAbility);
 else
  data = new PassiveAbilityData(ability as PassiveAbility);
 this.data.AddAbilityData(data);
 SendAbilityEvent(SpellManagerData.AbilitySendEvent.OnAbilityAdded, ability);
}

Рассмотрим поближе: мы вызываем метод AddAbility и передаем в его параметры способность которую хотим добавить в менеджер, но
сначала нам нужно создать обертку для этой способности, поэтому я объявляю переменную стандартной обертки AbilityData а
дальше в зависимости от того какой тип способности присваиваю этой переменной определенный тип обертки, дальше я добавляю
через метод AddAbilityData класса AbilitiesManagerData обертку со способностью, и вызываю событие что способность была
добавлена. Вроде все просто. Теперь нужно в классе AbilitiesManagerData объявить метод AddAbilityData.
Синтаксис:
Используется csharp
public sealed class AbilitiesManagerData {

 private readonly Dictionary<AbilitySettings.AbilityType, List<AbilityData>> abilities;

 public void AddAbilityData(AbilityData data) {
  if (this.abilities[data.ability.type].Contains(data) == false) {
   this.abilities[data.ability.type].Add(data);
  }  
 }

 public IEnumerable<ActiveAbilityData> activeAbilities {
  get {
   foreach(AbilityData data in this.abilities[AbilitySettings.AbilityType.Active])
    yield return data as ActiveAbilityData;
  }
 }
                 
 public IEnumerable<PassiveAbilityData> passiveAbilities {
  get {
   foreach(AbilityData data in this.abilities[AbilitySettings.AbilityType.Passive])
    yield return data as PassiveAbilityData;
  }
 }

 public AbilitiesManagerData() {
  this.abilities = new Dictionary<AbilitySettings.AbilityType, List<AbilityData>>();
  this.abilities.Add(AbilitySettings.AbilityType.Active, new List<AbilityData>());
  this.abilities.Add(AbilitySettings.AbilityType.Passive, new List<AbilityData>());
 }

}

В этом методе(AddAbilityData) я сначала проверяю: есть ли по данному ключу уже такая обертка и если их нет то добавляю новую.
Но раз у нас есть метод который добавляет данные нужен тогда и метод который будет удалять эти данные.
Поэтому объявим еще метод RemoveAbilityData который будет удалять данные из списка.
Синтаксис:
Используется csharp
public sealed class AbilitiesManagerData {

 private readonly Dictionary<AbilitySettings.AbilityType, List<AbilityData>> abilities;

 public void AddAbilityData(AbilityData data) {
  if (this.abilities[data.ability.type].Contains(data) == false) {
   this.abilities[data.ability.type].Add(data);
  }  
 }

 public void RemoveAbilityData(AbilityData data) {
  for(int index = 0; index < this.abilities[data.ability.type].Count; index++) {
   if (this.abilities[data.ability.type][index] == data) {
    this.abilities[data.ability.type].RemoveAt(index);
    return;
   }
  }
 }

 public IEnumerable<ActiveAbilityData> activeAbilities {
  get {
   foreach(AbilityData data in this.abilities[AbilitySettings.AbilityType.Active])
    yield return data as ActiveAbilityData;
  }
 }
                 
 public IEnumerable<PassiveAbilityData> passiveAbilities {
  get {
   foreach(AbilityData data in this.abilities[AbilitySettings.AbilityType.Passive])
    yield return data as PassiveAbilityData;
  }
 }

 public AbilitiesManagerData() {
  this.abilities = new Dictionary<AbilitySettings.AbilityType, List<AbilityData>>();
  this.abilities.Add(AbilitySettings.AbilityType.Active, new List<AbilityData>());
  this.abilities.Add(AbilitySettings.AbilityType.Passive, new List<AbilityData>());
 }

}

Теперь мы можем также и удалить данные, данные к которым нет доступа - ага, значит нужно этот доступ както получить. Раз у
меня всегда есть ссылка на способность то я смогу и найти по этой ссылке данные(обертку) в которых эта способность находится.
Сделал я это через метод FindAbilityData
Синтаксис:
Используется csharp
public sealed class AbilitiesManagerData {

 private readonly Dictionary<AbilitySettings.AbilityType, List<AbilityData>> abilities;

 public AbilityData FindAbilityData(AbilityObject ability) {
  return this.abilities[ability.type].FirstOrDefault(d => d.ability == ability);
 }

 public void AddAbilityData(AbilityData data) {
  if (this.abilities[data.ability.type].Contains(data) == false) {
   this.abilities[data.ability.type].Add(data);
  }  
 }

 public void RemoveAbilityData(AbilityData data) {
  for(int index = 0; index < this.abilities[data.ability.type].Count; index++) {
   if (this.abilities[data.ability.type][index] == data) {
    this.abilities[data.ability.type].RemoveAt(index);
    return;
   }
  }
 }

 public IEnumerable<ActiveAbilityData> activeAbilities {
  get {
   foreach(AbilityData data in this.abilities[AbilitySettings.AbilityType.Active])
    yield return data as ActiveAbilityData;
  }
 }
                 
 public IEnumerable<PassiveAbilityData> passiveAbilities {
  get {
   foreach(AbilityData data in this.abilities[AbilitySettings.AbilityType.Passive])
    yield return data as PassiveAbilityData;
  }
 }

 public AbilitiesManagerData() {
  this.abilities = new Dictionary<AbilitySettings.AbilityType, List<AbilityData>>();
  this.abilities.Add(AbilitySettings.AbilityType.Active, new List<AbilityData>());
  this.abilities.Add(AbilitySettings.AbilityType.Passive, new List<AbilityData>());
 }

}

Этот метод(FindAbilityData) ищет данные по способности.
Также мне тогда придется добавить метод через который я бы мог убедиться через менеджер что у него уже есть такая
способность. Я сделал это через метод AbilityExists
Синтаксис:
Используется csharp
public sealed class AbilitiesManagerData {

 private readonly Dictionary<AbilitySettings.AbilityType, List<AbilityData>> abilities;

 public bool AbilityExists(AbilityObject ability) {
  return this.abilities[ability.type].Any(d => d.ability == ability);
 }

 public AbilityData FindAbilityData(AbilityObject ability) {
  return this.abilities[ability.type].FirstOrDefault(d => d.ability == ability);
 }

 public void AddAbilityData(AbilityData data) {
  if (this.abilities[data.ability.type].Contains(data) == false) {
   this.abilities[data.ability.type].Add(data);
  }  
 }

 public void RemoveAbilityData(AbilityData data) {
  for(int index = 0; index < this.abilities[data.ability.type].Count; index++) {
   if (this.abilities[data.ability.type][index] == data) {
    this.abilities[data.ability.type].RemoveAt(index);
    return;
   }
  }
 }

 public IEnumerable<ActiveAbilityData> activeAbilities {
  get {
   foreach(AbilityData data in this.abilities[AbilitySettings.AbilityType.Active])
    yield return data as ActiveAbilityData;
  }
 }
                 
 public IEnumerable<PassiveAbilityData> passiveAbilities {
  get {
   foreach(AbilityData data in this.abilities[AbilitySettings.AbilityType.Passive])
    yield return data as PassiveAbilityData;
  }
 }

 public AbilitiesManagerData() {
  this.abilities = new Dictionary<AbilitySettings.AbilityType, List<AbilityData>>();
  this.abilities.Add(AbilitySettings.AbilityType.Active, new List<AbilityData>());
  this.abilities.Add(AbilitySettings.AbilityType.Passive, new List<AbilityData>());
 }

}

Ну вот вроде весь функционал и готов для работы с данными способностей.
Вот конечный вариант класса AbilitiesManagerData
Синтаксис:
Используется csharp
public sealed class AbilitiesManagerData {

 private readonly Dictionary<AbilitySettings.AbilityType, List<AbilityData>> abilities;

 public bool AbilityExists(AbilityObject ability) {
  return this.abilities[ability.type].Any(d => d.ability == ability);
 }

 public AbilityData FindAbilityData(AbilityObject ability) {
  return this.abilities[ability.type].FirstOrDefault(d => d.ability == ability);
 }

 public bool AbilityExists(string name) {
  return FindAbility(name) != null;
 }
               
 public AbilityObject FindAbility(string name) {
  AbilityData data = this.abilities[AbilitySettings.AbilityType.Active].FirstOrDefault(d => d.ability.name == name);
  if (data == null)
   data = this.abilities[AbilitySettings.AbilityType.Passive].FirstOrDefault(d => d.ability.name == name);
  return data.ability;
 }

 public void AddAbilityData(AbilityData data) {
  if (this.abilities[data.ability.type].Contains(data) == false) {
   this.abilities[data.ability.type].Add(data);
  }  
 }

 public void RemoveAbility(string name) {
  this.RemoveAbility(FindAbility(name));
 }
               
 public void RemoveAbility(AbilityObject ability) {
  this.RemoveAbilityData(this.abilities[ability.type].FirstOrDefault(d => d.ability == ability));
 }

 public void RemoveAbilityData(AbilityData data) {
  for(int index = 0; index < this.abilities[data.ability.type].Count; index++) {
   if (this.abilities[data.ability.type][index] == data) {
    this.abilities[data.ability.type].RemoveAt(index);
    return;
   }
  }
 }

 public IEnumerable<ActiveAbilityData> activeAbilities {
  get {
   foreach(AbilityData data in this.abilities[AbilitySettings.AbilityType.Active])
    yield return data as ActiveAbilityData;
  }
 }
                 
 public IEnumerable<PassiveAbilityData> passiveAbilities {
  get {
   foreach(AbilityData data in this.abilities[AbilitySettings.AbilityType.Passive])
    yield return data as PassiveAbilityData;
  }
 }

 public AbilitiesManagerData() {
  this.abilities = new Dictionary<AbilitySettings.AbilityType, List<AbilityData>>();
  this.abilities.Add(AbilitySettings.AbilityType.Active, new List<AbilityData>());
  this.abilities.Add(AbilitySettings.AbilityType.Passive, new List<AbilityData>());
 }

}

Теперь можно полностью посветить остальное время менеджеру и способностям.

Как я говорил в прошлой части, пассивная способность не имеет какой либо активации через кнопки или еще как, он активируется
сразу как только добавилась в менеджер. Но активная способность активируется уже с помощью нажатия на клавиши.
Я уже рассказывал что из себя представляет активация и какие действия она выполняет.
pic14.png

Как видно на картинке, активация в менеджере будет представлять собой метод который будет раскладывать способность на
компоненты и отсылать эти компоненты в компонент AbilityBihaviour и там уже пользователь дальше решит что с ними делать.
Но в компоненте AbilityBihaviour нет методов которые будут принимать эти компоненты, значит надо их объявить.
Как и в случае с посыланием сообщений о добавлении\удалении\использовании способности, мы также будем использовать
перечисление событий которые хотим отослать в AbilityBihaviour.
Я создал в статическом классе перечисление ComponentSendEvent
Синтаксис:
Используется csharp
public static class SpellManagerData {
 
 public static SpellManager SpellManager(this GameObject gameObject) {
  return gameObject.GetComponent<SpellManager>();
 }
 
 public enum AbilitySendEvent {
  OnAbilityAdded,
  OnAbilityUsed,
  OnAbilityRemoved
 }
 
 public enum ComponentSendEvent {
  OnComponentExpansion
 }
 
}

Пока что там будет только одно перечисление OnComponentExpansion которое будет отправляться в случае когда менеджер будет
отправлять компонент при разложении.
Теперь нужно добавить метод SendComponentEvent в менеджер который будет отправлять сообщения вместе с компонентом.
Синтаксис:
Используется csharp
protected void SendComponentEvent(SpellManagerData.ComponentSendEvent e, AbilityComponent component) {
 if (this.objectSpellBihaviour == null) {
  this.SendMessage(e.ToString(), component, SendMessageOptions.DontRequireReceiver);
 } else {
  switch(e) {
   case SpellManagerData.ComponentSendEvent.OnComponentExpansion: this.objectSpellBihaviour.OnComponentExpansion(component);
   break;
  }
 }
}

Опять же я проверяю здесь если менеджер имеет ссылку на AbilityBihaviour значит отослать сообщение на прямую иначе
использовать SendMessage. Теперь нужно объявить метод OnComponentExpansion в классе AbilityBihaviour.
Синтаксис:
Используется csharp
public abstract class AbilityBihaviour : MonoBehaviour {
 
 private SpellManager objectManager = null;
 
 public SpellManager spellManager {
  get {
   if (this.objectManager == null) {
    this.objectManager = this.gameObject.GetComponent<SpellManager>();
    if (this.objectManager == null) this.objectManager = this.gameObject.AddComponent<SpellManager>();
   }
   return this.objectManager;
  }
 }
 
 public virtual void OnAbilityAdded(AbilityObject ability) {
  Debug.Log("Character: " + this.name + " got new ability: " + ability.name);
 }
               
 public virtual void OnAbilityUsed(AbilityObject ability) {
  Debug.Log("Character: " + this.name + " used ability: " + ability.name);
 }
               
 public virtual void OnAbilityRemoved(AbilityObject ability) {
  Debug.Log("Character: " + this.name + " has removed ability: " + ability.name);
 }
               
 public abstract void OnComponentExpansion(AbilityComponent component);
 
}

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

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

Вернемся к менеджеру.

Активация будет представлять собой корутину InitializeAbility в которой будет происходить активация способности.
Давайте создадим ее пустой для начала.
Синтаксис:
Используется csharp
private IEnumerator InitializeAbility(AbilityData data) {
 yeild break;
}

Как видите она берет параметр AbilityData - тоесть не способность на прямую а обертку в которой она находится.
Теперь когда мы добавим пассивную способность в методе AddAbility мы сразу же передадим ее на активацию.
Синтаксис:
Используется csharp
public void AddAbility(AbilityObject ability) {
 if (this.data.AbilityExists(ability)) return;
 AbilityData data = null;
 if (ability.type == AbilitySettings.AbilityType.Active)
  data = new ActiveAbilityData(ability as ActiveAbility);
 else
  data = new PassiveAbilityData(ability as PassiveAbility);
 this.data.AddAbilityData(data);
 SendAbilityEvent(SpellManagerData.AbilitySendEvent.OnAbilityAdded, ability);
 if (ability.type == AbilitySettings.AbilityType.Passive)
  UseAbility(data);
}

Как видите в самом конце метода AddAbility я передаю данные(обертку) способности в метод UseAbility который будет отсылать
сообщения о том что мы использовали способность и запустит корутину активации.
Синтаксис:
Используется csharp
private void UseAbility(AbilityData data) {
 SendAbilityEvent(SpellManagerData.AbilitySendEvent.OnAbilityUsed, data.ability);
 this.StartCoroutine(InitializeAbility(data));
}

Готово. Теперь опять вернемся к методу активации, и рассмотрим ее действия.
Синтаксис:
Используется csharp
private IEnumerator InitializeAbility(AbilityData data) {
 IAbilityComponentable compSys = data.ability as IAbilityComponentable;
 if (compSys == null) yield break;
 foreach(AbilityComponent component in compSys.components) {
  SendComponentEvent(SpellManagerData.ComponentSendEvent.OnComponentExpansion, component);
  yield return new WaitForFixedUpdate();
 }
}

Сначала мне нужно узнать есть ли вообще у этой способности компоненты, это я делаю с помощью интерфейса
IAbilityComponentable, далее проверяю если у способности нет компонентов то завершаю корутина на этом этапе т к слать нечего.
Иначе я с помощью цикла перебираю все компоненты в способности и с помощью метода SendComponentEvent отсылаю ее дальше
объектам.

Готово. Теперь у нас есть базовые представления о том как менеджер будет справляться со способностями: как их сохранять
\удалять и активировать. Думаю для этой части хватит. Вот как выглядит класс менеджера в этой части статьи.
Синтаксис:
Используется csharp
[DisallowMultipleComponent]
public class SpellManager : MonoBehaviour {

 public AbilityBihaviour objectSpellBihaviour { get; private set;}

 protected AbilitiesManagerData data { get; private set;}

 protected virtual void Awake() {
  this.objectSpellBihaviour = this.GetComponent<AbilityBihaviour>();
  this.data = new AbilitiesManagerData();
 }

 protected virtual void Start() {

 }

 protected virtual void Update() {
  foreach(ActiveAbilityData data in this.data.activeAbilities) {
   if (Input.GetKeyUp(data.ability.castKey)) {
    Debug.Log("User used: " + data.ability.name);
    return;
   }
  }
 }

 public void AddAbility(AbilityObject ability) {
  if (this.data.AbilityExists(ability)) return;
  AbilityData data = null;
  if (ability.type == AbilitySettings.AbilityType.Active)
    data = new ActiveAbilityData(ability as ActiveAbility);
  else
   data = new PassiveAbilityData(ability as PassiveAbility);
  this.data.AddAbilityData(data);
  SendAbilityEvent(SpellManagerData.AbilitySendEvent.OnAbilityAdded, ability);
  if (ability.type == AbilitySettings.AbilityType.Passive)
   UseAbility(data);
 }

 public void RemoveAbility(AbilityObject ability) {

 }

 public void UseAbility(AbilityObject ability) {

 }

 private void UseAbility(AbilityData data) {
  SendAbilityEvent(SpellManagerData.AbilitySendEvent.OnAbilityUsed, data.ability);
  this.StartCoroutine(InitializeAbility(data));
 }

 private IEnumerator InitializeAbility(AbilityData data) {
  IAbilityComponentable compSys = data.ability as IAbilityComponentable;
  if (compSys == null) yield break;
  foreach(AbilityComponent component in compSys.components) {
   SendComponentEvent(SpellManagerData.ComponentSendEvent.OnComponentExpansion, component);
   yield return new WaitForFixedUpdate();
  }
 }

 protected void SendAbilityEvent(SpellManagerData.AbilitySendEvent e, AbilityObject ability) {
  if (this.objectSpellBihaviour == null) {
   this.SendMessage(e.ToString(), ability, SendMessageOptions.DontRequireReceiver);
  } else {
   switch(e) {
    case SpellManagerData.AbilitySendEvent.OnAbilityAdded: this.objectSpellBihaviour.OnAbilityAdded(ability);
    break;
    case SpellManagerData.AbilitySendEvent.OnAbilityRemoved: this.objectSpellBihaviour.OnAbilityRemoved(ability);
    break;
    case SpellManagerData.AbilitySendEvent.OnAbilityUsed: this.objectSpellBihaviour.OnAbilityUsed(ability);
    break;
   }
  }
 }

 protected void SendComponentEvent(SpellManagerData.ComponentSendEvent e, AbilityComponent component) {
  if (this.objectSpellBihaviour == null) {
   this.SendMessage(e.ToString(), component, SendMessageOptions.DontRequireReceiver);
  } else {
   switch(e) {
    case SpellManagerData.ComponentSendEvent.OnComponentExpansion: this.objectSpellBihaviour.OnComponentExpansion(component);
    break;
   }
  }
 }

}


Заключение: теперь у нас есть рабочие методы менеджера через которые мы сможем в будущем добавлять\удалять и использовать
способности, конечно пока они не полностью рабочие т к эту часть я оставил на следующий раз, мы продолжим работать с этими компонентами а также разберемся с эффектами от способностей.

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

автор: этот хрен, он же llka, он же lawsonilka
У вас нет доступа для просмотра вложений в этом сообщении.
Последний раз редактировалось lawsonilka 27 дек 2014, 17:56, всего редактировалось 3 раз(а).
lawsonilka
UNIверсал
 
Сообщения: 390
Зарегистрирован: 21 окт 2014, 14:48

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

Сообщение bwolf88 25 дек 2014, 15:24

Он же Lawson, спалил свой второй ник :D.
Вы бы видео или рисунки вставляли как и что получается если добавить это или прибавить то, а то много текста без визуализации очень тяжело читать. (это рекомендации)
Сюда периодически чего нибудь выкладываю https://github.com/LuchunPen
Аватара пользователя
bwolf88
Адепт
 
Сообщения: 2184
Зарегистрирован: 30 апр 2014, 06:40
Skype: bwolf331

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

Сообщение lwe 25 дек 2014, 15:53

bwolf88 писал(а):Он же Lawson, спалил свой второй ник :D.
Вы бы видео или рисунки вставляли как и что получается если добавить это или прибавить то, а то много текста без визуализации очень тяжело читать. (это рекомендации)

Ясно, спасибо, но как я могу продемонстрировать как работает код пока он еще не закончен?
По плану это должно было быть в следующей части статьи с примерами и картинками.
Если можете ответьте: какие моменты вызывают сложность в понимании?
lwe
UNITрон
 
Сообщения: 261
Зарегистрирован: 24 авг 2014, 14:20
Skype: lawsonilka

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

Сообщение bwolf88 25 дек 2014, 16:32

Сложность вызывает делать то, незнаю что. Вы написали, что не можете показать как работает код, если он не закончен. Допустим я решил воспользоваться Вашим уроком, но ведь код не закончен и у меня так же не будет это дело работать.
Вот поэтому мне больше нравятся видеоуроки, потому что в них даже незаконченную логику можно показать. Вот пример: https://www.youtube.com/user/craigp223. У него есть цикл из 60 уроков по майнкрафту и в каждом уроке он по несколько раз запускает Play и проверяет, а чего же получилось. Все наглядно и понятен каждый шаг. Это к тому, что без картинок тяжело воспринимается. Вы очень полезную инфу пишите, но лучше если все это будет с картинками, а еще лучше с видео, пусть без слов, просто чтобы видеть результат. (Хотя у Вас текст уже полностью оформлен, остается только читать на видео).

Вообще по скриптингу у меня вопросов уже давно нет, я в состоянии написать практически любую логику самостоятельно, разве что из за незнания API иногда трудности возникают :).
Сюда периодически чего нибудь выкладываю https://github.com/LuchunPen
Аватара пользователя
bwolf88
Адепт
 
Сообщения: 2184
Зарегистрирован: 30 апр 2014, 06:40
Skype: bwolf331

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

Сообщение lwe 25 дек 2014, 16:48

Сложность вызывает делать то, незнаю что

Вы имеете ввиду что я не доступно или не правильно предоставляю информацию? Я даже не знаю куда проще, не браться же мне с самых основ!? Я еще в первой части написал что данная статья не расчитана на новичков и я надеюсь у вас будут хоть какие то представления о том как работать с объектами.
Вы написали, что не можете показать как работает код, если он не закончен. Допустим я решил воспользоваться Вашим уроком, но ведь код не закончен и у меня так же не будет это дело работать.
Придется тогда запастись терпением пока выйдет следующая часть, я не могу тоже писать все от начала до конца мне даже бы видео уроки не помогли бы в этом.
Вот поэтому мне больше нравятся видеоуроки,

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

Опять же как я писал еще в первой части: я не хочу чтобы вы повторяли точь в точь мои шаги, я хочу научить, хочу чтобы человек понял по какому принципу работают подобные системы, поэтому я пишу так много текста - я пытаюсь объяснить и видимо пока это получается у меня не очень. Я бы мог просто закомментить текст программы и кинуть ее сюда - типа сами разберетесь, но кому от этого станет легче!?
Скрытый текст:
Я сам не люблю читать, я не прочитал ни одной книги по программированию, хоть я этим и не горжусь, я недавно не поступил в университет на программиста хоть и мог там себя прекрасно чувствовать. Мне не нравятся занудные статьи или видео уроки где автор только пытается рассказать уже тысячу раз рассказанное, мне больше нравится объяснять словами т к сам не разбираюсь в терминах. К примеру те же интерфейсы - я не уверен что понимаю стандартное определение на том же мсдн, но это мне не мешает их использовать, а все благодаря статье в которой доступными словами объяснили принцип их работы.
Последний раз редактировалось lwe 25 дек 2014, 23:00, всего редактировалось 2 раз(а).
lwe
UNITрон
 
Сообщения: 261
Зарегистрирован: 24 авг 2014, 14:20
Skype: lawsonilka

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

Сообщение llka 25 дек 2014, 22:50

bwolf88 писал(а):я в состоянии написать практически любую логику самостоятельно

Вот бы мне так научиться.
llka
UNIверсал
 
Сообщения: 359
Зарегистрирован: 08 янв 2014, 05:00


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

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

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