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

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

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

Сообщение lawsonilka 29 янв 2015, 18:24

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

Загрузка

В отличие от системы сохранения способностей и их компонентов, которая была разбита на две части из за большого объема информации,
система загрузки будет намного меньше и проще т к мы все максимально подготовили для удобства использования системы способностей.
pic38.png

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

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

 [Header("Put object abilities into this list")]
 public List<AbilityObject> Abilities = new List<AbilityObject>();

}

Все просто. Теперь при старте будет достаточно их добавить объекту с помощью метода AddAbility
Синтаксис:
Используется csharp
[DisallowMultipleComponent]
public class SpellManager : MonoBehaviour {

 [Header("Put object abilities into this list")]
 public List<AbilityObject> Abilities = new List<AbilityObject>();

 protected virtual void Start() {
  foreach(AbilityObject ability in this.Abilities) {
   if (ability != null) this.AddAbility(ability);
  }
 }

}

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

 [Header("Put object abilities into this list")]
 public List<AbilityObject> Abilities = new List<AbilityObject>();

 protected virtual void Start() {
  foreach(AbilityObject ability in this.Abilities) {
   if (ability != null) this.AddAbility(ability);
  }
 }

 public static AbilityObject LoadAbility(string name) {
  string path = AbilitySettings.SPELL_RESOURCES_FOLDER + name + "/" + name;
  AbilityObject ability = Resources.Load(path, typeof(AbilityObject)) as AbilityObject;
  return ability;
 }

}

Здесь все тоже очень просто. В метод LoadAbility передаю имя способности, и создаю путь к этой способности, потом просто загружаю ее
через метод Resources.Load и возвращаю способность.
Дальше, я для удобства использования менеджера способностей, добавил еще один метод AddAbility только теперь этот метод будет брать
параметр "имя" способности, выглядит это так.
Синтаксис:
Используется csharp
[DisallowMultipleComponent]
public class SpellManager : MonoBehaviour {

 [Header("Put object abilities into this list")]
 public List<AbilityObject> Abilities = new List<AbilityObject>();

 protected virtual void Start() {
  foreach(AbilityObject ability in this.Abilities) {
   if (ability != null) this.AddAbility(ability);
  }
 }

 public static AbilityObject LoadAbility(string name) {
  string path = AbilitySettings.SPELL_RESOURCES_FOLDER + name + "/" + name;
  AbilityObject ability = Resources.Load(path, typeof(AbilityObject)) as AbilityObject;
  return ability;
 }

 public void AddAbility(string name) {
  AbilityObject ability = LoadAbility(name);
  if (ability != null) AddAbility(ability);
 }

}

Здесь тоже все просто. Я загружаю способность через статический метод LoadAbility и если способность была загружена - просто добавляю ее в
менеджер способностей.

Вот в общем то и все что я хотел сказать о системе загрузки способностей.
Теперь я думаю вам стало более понятен выбор в пользу скриптовых объектов в отличие от сериализации, архивирования и других способов
сохранения\загрузки способностей которые нам бы пришлось использовать.

Активные способности и их обертки.
Видео
На этом видео я показал как создавать и как работают АКТИВНЫЕ способности.

Обычно когда я рассказывал о том или ином принципе работы способностей, я обычно брался объяснять это на пассивных способностях - это
очевидно т к их легче контроллировать. В этот же раз мы закончим всю логику активных способностей. Мы уже разобрали их активацию и как
они работают в связке с временным компонентом, теперь мы займемся временем их перезагрузки(cooldown).

Давайте вспомним про обертки способностей, а именно об классе ActiveAbilityData который содержит в себе только АКТИВНЫЕ способности.
Этот класс также будет отвечать за время перезагрузки способности, для этого нам понадобится одна переменная и пара свойств, а также
некоторые знания арифметики :)

Для начала объявим в этом классе метод StartCooldown который будет вызываться когда способность активируется(используется), и одно
свойство.
Синтаксис:
Используется csharp
public sealed class ActiveAbilityData : AbilityData {

 private float startTime = 0f;
 
 public void StartCooldown() {
  this.startTime = Time.time;
 }

 public ActiveAbilityData(AbilityObject ability) : base(ability) {}

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

Когда мы вызовим метод StartCooldown в свойство startTime запишется время(Time.time) вызова этого метода, это время даст понять когда произошла активация способности, а дальше мы уже сможем исходя из этого определиться сколько нужно будет ждать пока способность перезагрузится.
Для определения всех этих условий нам нужно будет добавить три свойства(по сути методов).
timeLeft - свойство будет показывать сколько времени осталось ждать до завершения перезагрузки способности
complete - процентное свойство, также будет показывать на сколько процентов выполнилась перезагрузка.
ready - будет показывать готова ли способность для повторного использования.
Первым разберем свойство timeLeft
Синтаксис:
Используется csharp
public sealed class ActiveAbilityData : AbilityData {

 private float startTime = 0f;
 
 public void StartCooldown() {
  this.startTime = Time.time;
 }

 public ActiveAbilityData(AbilityObject ability) : base(ability) {}

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

 public float timeLeft {
  get {
   float result = (this.startTime > 0f) ? Time.time - this.startTime : this.ability.cooldown;
   return this.ability.cooldown - Mathf.Clamp(result, 0f, this.ability.cooldown);
  }
 }
 
}

Здесь отсчитываю точный интервал времени между активацией способности и ее полной перезагрузкой.
Теперь свойство complete
Синтаксис:
Используется csharp
public sealed class ActiveAbilityData : AbilityData {

 private float startTime = 0f;
 
 public void StartCooldown() {
  this.startTime = Time.time;
 }

 public ActiveAbilityData(AbilityObject ability) : base(ability) {}

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

 public float timeLeft {
  get {
   float result = (this.startTime > 0f) ? Time.time - this.startTime : this.ability.cooldown;
   return this.ability.cooldown - Mathf.Clamp(result, 0f, this.ability.cooldown);
  }
 }

 public int complete {
  get {
   int result = 100 - Mathf.RoundToInt(this.timeLeft / this.ability.cooldown * 100);
   return result;
  }
 }
 
}

здесь тоже все просто. Я нахожу разницу между временем что осталось способности перезагружаться и полным временем перезагрузки
способности.
И закончим свойством ready
Синтаксис:
Используется csharp
public sealed class ActiveAbilityData : AbilityData {

 private float startTime = 0f;
 
 public void StartCooldown() {
  this.startTime = Time.time;
 }

 public ActiveAbilityData(AbilityObject ability) : base(ability) {}

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

 public float timeLeft {
  get {
   float result = (this.startTime > 0f) ? Time.time - this.startTime : this.ability.cooldown;
   return this.ability.cooldown - Mathf.Clamp(result, 0f, this.ability.cooldown);
  }
 }

 public int complete {
  get {
   int result = 100 - Mathf.RoundToInt(this.timeLeft / this.ability.cooldown * 100);
   return result;
  }
 }

 public bool ready {
  get { return this.complete >= 100;}
 }
 
}

Здесь я просто смотрю: если процент готовности способности равен или выше 100%(процентов) - значит способность готова к использованию.
Ну вот и все, теперь у нас есть целых три свойтсва показывающих готовность АКТИВНОЙ способности, осталось только вызвать метод
StartCooldown в методе InitializeAbility класса SpellManager и там же сделать проверку на готовность способности в методе UseAbility.
Синтаксис:
Используется csharp
private IEnumerator InitializeAbility(AbilityData data) {
 if (data.isUsing) {
  this.StartCoroutine(FinalizeAbility(data));
  yield return new WaitForFixedUpdate();
 }
 data.isUsing = true;
 if (data.ability.type == AbilitySettings.AbilityType.Active) {
  ActiveAbilityData aData = data as ActiveAbilityData;
  aData.StartCooldown();
 }
 IAbilityComponentable compSys = data.ability as IAbilityComponentable;
 if (compSys == null) yield break;
 foreach(AbilityComponent component in compSys.components) {
  if (component.CheckForAttribute<DisableComponentMultiplyUsage>())
   SpecialComponentExpansion(component, data);
  else
   SendComponentEvent(SpellManagerData.ComponentSendEvent.OnComponentExpansion, component);
  yield return new WaitForFixedUpdate();
 }
}

Как вы видите, после установления свойства isUsing в значение true, я проверяю: если способность активная - то вызываю метод
StartCooldown для начала перезагрузки способности.
Теперь идем в метод UseAbility где выставим проверку на готовность способности.
Синтаксис:
Используется csharp
private void UseAbility(AbilityData data) {
 if (data.ability.type == AbilitySettings.AbilityType.Active) {
  ActiveAbilityData aData = data as ActiveAbilityData;
  if (aData.ready == false) return;
 }
 SendAbilityEvent(SpellManagerData.AbilitySendEvent.OnAbilityUsed, data.ability);
 this.StartCoroutine(InitializeAbility(data));
}

Здесь я перед началом использования способности проверяю: если способность АКТИВНАЯ значит нужно проверить не перезагружается ли она в
данный момент и если это так то просто прекращаю выполнение метода UseAbility пока способность не перезагрузится.

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

Заключение: ну вот и закончили мы создание системы способностей
Скрытый текст:
Такое ощущение будто написал дипломную работу, хотя мне до нее еще 4 года учиться

Если в общем - со стороны посмотреть на всю эту систему, то она конечно мало чем напоминает такие громоздкие системы тех же способностей
в таких играх как StarCraft и куда уж поскромнее чем в WarCraft. Эта система в основном направлена на роботу с конкретными объектами
которые ими обладают, а также обладают малой интерактивностью. Но как я упоминал еще в первой части статьи, я не старался создать
аналогичную систему на unity я делал это исключительно для этой игры, где не было
необходимости в такой сложной системе. Но я думаю благодаря такому старту, если у вас появится желание, вы сможете добавить или изменить
или даже создать свою систему более универсальную, которая будет отвечать вашим требованиям и желаниям.

Ну вот и все, на этом я хочу закончить знакомство с системой способностей на движке unity3D, ровно за 15 частей - фухх, всего хорошего и удачи в разработке.
Часть о "мелких деталях" выйдет завтра в дополнение к 15 части.

Используемые статьи
Скрытый текст:
Книга о создании RPG на русском(не на unity)
Теоретическая трактовка магических систем на англ


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

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

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

Сейчас этот форум просматривают: Google [Bot] и гости: 3