[Статья]Тэги и взаимодействие между объектами. ч1

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

[Статья]Тэги и взаимодействие между объектами. ч1

Сообщение ilkalawson 03 апр 2015, 15:58

Статья: Тэги и взаимодействие между объектами.

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

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

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

Начинающие unity разработчики, которые еще плохо знакомы с ООП(Объектно ориентированным программированием), обычно видят все объекты на сцене простыми GameObject'ми которые лишь могут иметь некоторые компоненты в себе, между которыми нужно настраивать связь.
Эти разработчики обычно делятся на две группы в соотношении 75% к 25%.
1) В первую группу часто попадают те разработчики которые пытаются на каждый случай жизни писать каждый раз новый компонент который будет
выполнять нужную им работу. Но когда дело доходит до взаимосвязей между этими компонентами то все выглядит как в басне "лебедь, рак и щука".
2) Во вторую группу часто попадают те разработчики которые в отличие от первых пытаются все контролировать в одном большом скрипте, который
отвечает за все свойства объектов, теряясь и запутываясь в собственном же написании.

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

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

Начинающие разработчики используют тэги для разделения объектов, так как для них все GameObject'ы на сцене похожи друг на друга.
Вот есть необходимость чтобы в начале игры игроку давалось определенной кол во золота, мы ищем игрока по тэгу, находим его и выполняем поиск
нужного скрипта, что отвечает за кол во золота у игрока, выдираем этот скрипт и с помощью нужного метода добавляем золото игроку. Все работает
как часы - ни каких противоречий, и последствий.
Вот также другой эксперимент: допустим при входе в определенный триггер игрок должен будет умирать - и здесь будет все просто. При входе в триггер мы сравниваем тэг объекта, что вошел в триггер и если это игрок - то убиваем его, эта система также будет полноценно работать. Этими двумя экспериментами я подтверждаю простоту и практичность использования тэгов.

Но тогда почему же более опытные разработчики редко их используют!?

Для того чтобы объяснить это я расскажу вам об одном проекте в котором я участвовал как программист.
Это была игра с видом сверху. Что то типа файтинга, где главный герой все свои проблемы решал с помощью кулаков: когда нужно было отдубасить
врага, или разбить стену, или подвинуть тяжелый ящик для того чтобы пройти дальше по уровню.

В игре было три типа взаимодействия:
1) Первый тип взаимодействия это взаимодействие с врагом: при этом взаимодействии врагу просто наносится урон при ударе и отнимается уровень
его здоровья.
2) Второй тип взаимодействия это взаимодействие с ящиком: при этом взаимодействии ящик не разлетается на куски при ударе, а просто отлетает
в сторону удара согласно силе удара.
3) Третий тип взаимодействия, взаимодействие со стеной: в этом случае стена, также как и враг, будет иметь уровень здоровья, только здесь он
заменен на уровень прочности в процентном соотношении от 0% до 100%.

Как будет главный герой взаимодействовать с объектами и будет вашим показателем ваших же знаний и опыта программирования!
Сначала я рассмотрю очевидный вариант решения этой проблемы с помощью сравнения тэгов.
И так для начала создадим все три скрипта для каждого типа объекта и наделим их необходимыми свойства.
Первым будет скрипт врага, я назвал его Enemy и он будет наследоваться от MonoBehavior.
Синтаксис:
Используется csharp
public class Enemy : MonoBehaviour {
 
}

Теперь добавим в этот скрипт переменную уровня здоровья врага.
Синтаксис:
Используется csharp
public class Enemy : MonoBehaviour {
 
 public float health;

}

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

Дальше переходим к скрипту ящика. Я назвал этот скрипт Box и также унаследовал его от MonoBehaviour.
Синтаксис:
Используется csharp
public class Box : MonoBehaviour {
 
}

И т к ящик будет отлетать при ударе, он должен будет иметь компонент Rigidbody, поэтому добавим этот компонент.
Синтаксис:
Используется csharp
public class Box : MonoBehaviour {
 
 public Rigidbody body = null;

 private void Start() {
  this.body = this.GetComponent<Rigidbody>() ?? this.gameObject.AddComponent<Rigidbody>();
 }

}

При старте свойству body передастся компонент Rigidbody объекта Box, и при ударе мы будет просто двигать коробку с помощью этого компонента.

И закончим скриптом стены, я назвал его Wall и также унаследовал от MonoBehaviour.
Синтаксис:
Используется csharp
public class Wall : MonoBehaviour {
 
}

И сначала запишем в этот скрипт свойство прочности стены.
Синтаксис:
Используется csharp
public class Wall : MonoBehaviour {
 
 public int prochnost = 100;

}

Теперь при ударе у объекта Wall будет отниматься ее прочность. Но так как стены бывают разными: кирпичными, металлическими - нам придется
также уменьшать прочность стены в зависимости от ее типа. Для этого я завел перечисление которое будет показывать какого типа именно эта
стена и исходя от этого уменьшать урон наносимый стене. Добавим это перечисление.
Синтаксис:
Используется csharp
public class Wall : MonoBehaviour {
 
 public int prochnost = 100;

 public enum WallType {
  Brick,
  Metal,
  Glass
 }

}

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

 public WallType wallType = WallType.Glass;
 public int prochnost = 100;

 public enum WallType {
  Brick,
  Metal,
  Glass
 }

}

Готово. Теперь все объекты имеют нужные компоненты для взаимодействия.
Нужно только раздать им тэги, запомните их!
Для врага будет тэг - "Враг".
Для ящика будет тэг - "Ящик".
Для стены будет тэг - "Стена".


Теперь когда объекты готовы и каждый из них имеет свой тэг, можно перейти к скрипту игрока который будет наносить удар.
Я назвал этот скрипт Player и унаследовал его от MonoBehaviour.
Синтаксис:
Используется csharp
public class Player : MonoBehaviour {
 
}

У игрока будет только два свойства: урон который наноситься при ударе, и направление куда смотрит главный герой.
Синтаксис:
Используется csharp
public class Player : MonoBehaviour {
 
 public float damage;
 public Vector2 direction;

}

Так как игра было с видом сверху, то и направление игрока будет определяться двумя осями - отсюда я и использовал Vector2.

Дальше удар будет совершаться по очень простой схеме. Представьте что на каждый кулак главного героя будет надет коллайдер, когда объект
будет входить в коллайдер кулака - значит нужно будет нанести объекту урон. Поэтому в скрипте Player напишем метод OnTriggerEnter который будет вызываться когда игрок совершает удар по объекту.
Синтаксис:
Используется csharp
public class Player : MonoBehaviour {
 
 public float damage;
 public Vector2 direction;

 private void OnTriggerEnter(Collider collider) {

 }

}

Теперь когда сработает метод OnTriggerEnter нужно будет узнать по тэгу что за объект вошел в триггер и выполнить с объектом нужные действия
в зависимости от того что это за объект.
Первым объектом взаимодействия рассмотрим врага, у которого при ударе должно будет отниматься здоровье.
Синтаксис:
Используется csharp
public class Player : MonoBehaviour {
 
 public float damage;
 public Vector2 direction;

 private void OnTriggerEnter(Collider collider) {
  if (collider.gameObject.tag == "Враг") {

  }
 }

}

И так после того как мы определили тэг объекта нужно найти нужный скрипт где храниться свойство уровня здоровья.
Синтаксис:
Используется csharp
public class Player : MonoBehaviour {
 
 public float damage;
 public Vector2 direction;

 private void OnTriggerEnter(Collider collider) {
  if (collider.gameObject.tag == "Враг") {
   Enemy enemy = collider.gameObject.GetComponent<Enemy>();
  }
 }

}

После того как нашли скрипт, просто отнимем уровень здоровья врага согласно нанесенному урону.
Синтаксис:
Используется csharp
public class Player : MonoBehaviour {
 
 public float damage;
 public Vector2 direction;

 private void OnTriggerEnter(Collider collider) {
  if (collider.gameObject.tag == "Враг") {
   Enemy enemy = collider.gameObject.GetComponent<Enemy>();
   enemy.health -= this.damage;
  }
 }

}

Готово. Теперь когда игрок ударит врага у него отнимется уровень здоровья. Первое взаимодействие готово.

Переходим к следующему объект - ящику, у которого тэг "Ящик".
Синтаксис:
Используется csharp
public class Player : MonoBehaviour {
 
 public float damage;
 public Vector2 direction;

 private void OnTriggerEnter(Collider collider) {
  if (collider.gameObject.tag == "Враг") {
   Enemy enemy = collider.gameObject.GetComponent<Enemy>();
   enemy.health -= this.damage;
  } esle if (collider.gameObject.tag == "Ящик") {
   
  }
 }

}

И также как при первом взаимодействии ищем нужный скрипт с которым мы будем взаимодействовать, у ящика этот скрипт - Box.
Синтаксис:
Используется csharp
public class Player : MonoBehaviour {
 
 public float damage;
 public Vector2 direction;

 private void OnTriggerEnter(Collider collider) {
  if (collider.gameObject.tag == "Враг") {
   Enemy enemy = collider.gameObject.GetComponent<Enemy>();
   enemy.health -= this.damage;
  } esle if (collider.gameObject.tag == "Ящик") {
   Box box = collider.gameObject.GetComponent<Box>();
  }
 }

}

И когда мы найдем нужным скрипт ящика - Box , то просто передвинем его через компонент Rigidbody по траектории удара согласно нанесенному урону.
Синтаксис:
Используется csharp
public class Player : MonoBehaviour {
 
 public float damage;
 public Vector2 direction;

 private void OnTriggerEnter(Collider collider) {
  if (collider.gameObject.tag == "Враг") {
   Enemy enemy = collider.gameObject.GetComponent<Enemy>();
   enemy.health -= this.damage;
  } esle if (collider.gameObject.tag == "Ящик") {
   Box box = collider.gameObject.GetComponent<Box>();
   Vector2 force = this.direction * this.damage;
   box.body.AddForce(force);
  }
 }

}

Готово. Теперь при ударе об ящик он просто сдвинется по направлению удара с силой удара.

И далее закончим с третьим взаимодействием со стеной и ее тэг "Стена".
Синтаксис:
Используется csharp
public class Player : MonoBehaviour {
 
 public float damage;
 public Vector2 direction;

 private void OnTriggerEnter(Collider collider) {
  if (collider.gameObject.tag == "Враг") {
   Enemy enemy = collider.gameObject.GetComponent<Enemy>();
   enemy.health -= this.damage;
  } esle if (collider.gameObject.tag == "Ящик") {
   Box box = collider.gameObject.GetComponent<Box>();
   Vector2 force = this.direction * this.damage;
   box.body.AddForce(force);
  } else if (collider.gameObject.tag == "Стена") {
   
  }
 }

}

Как и раньше найдем нужный нам скрипт управления стеной - Wall.
Синтаксис:
Используется csharp
public class Player : MonoBehaviour {
 
 public float damage;
 public Vector2 direction;

 private void OnTriggerEnter(Collider collider) {
  if (collider.gameObject.tag == "Враг") {
   Enemy enemy = collider.gameObject.GetComponent<Enemy>();
   enemy.health -= this.damage;
  } esle if (collider.gameObject.tag == "Ящик") {
   Box box = collider.gameObject.GetComponent<Box>();
   Vector2 force = this.direction * this.damage;
   box.body.AddForce(force);
  } else if (collider.gameObject.tag == "Стена") {
   Wall wall = collider.gameObject.GetComponent<Wall>();
  }
 }

}

Теперь согласно тому какой тип стены мы ударяем нужно будет вычислить сколько урона нужно нанести стене.
Синтаксис:
Используется csharp
public class Player : MonoBehaviour {
 
 public float damage;
 public Vector2 direction;

 private void OnTriggerEnter(Collider collider) {
  if (collider.gameObject.tag == "Враг") {
   Enemy enemy = collider.gameObject.GetComponent<Enemy>();
   enemy.health -= this.damage;
  } esle if (collider.gameObject.tag == "Ящик") {
   Box box = collider.gameObject.GetComponent<Box>();
   Vector2 force = this.direction * this.damage;
   box.body.AddForce(force);
  } else if (collider.gameObject.tag == "Стена") {
   Wall wall = collider.gameObject.GetComponent<Wall>();
   if (wall.wallType == Wall.WallType.Glass) {
    wall.prochnost -= this.damage;
   } else if (wall.wallType == Wall.WallType.Brick) {
    wall.prochnost -= this.damage / 2f;
   } else if (wall.wallType == Wall.WallType.Metall) {
    wall.prochnost -= this.damage / 3f;
   }
  }
 }

}

Вот так выглядит третье взаимодействие.

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

Но, если бы я хотел привести только такой тип взаимодействия между объектами, то и название статьи было бы не много другим.
Любой более опытный разработчик знакомый с ООП сразу бы уличил вас в вашей некомпетентности относительно вопроса взаимодействия между
объектами. Ведь тэги отлично разделяет объекты друг от друга, да что там, он буквально РУБИТ их на части, и отделяет по сути одни и те же
объекты друг от друга.

В следующей части я покажу вам как эти ужасные 17 строк метода OnTriggerEnter превратятся в элегантные две строки.

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

Автор: этот хрен.
Последний раз редактировалось ilkalawson 05 апр 2015, 03:07, всего редактировалось 7 раз(а).
ilkalawson
UNIверсал
 
Сообщения: 412
Зарегистрирован: 19 янв 2015, 20:38
Skype: lawsonunity

Re: [Статья]Тэги и взаимодействие между объектами.

Сообщение DbIMok 03 апр 2015, 16:13

ilkalawson писал(а):наследоваться от MonoBihavior

что это?
правильный вопрос - половина ответа. учитесь формулировать вопросы понятно.
Новости > _Telegram чат @unity3d_ru (11.6k/4.8k online) > _Telegram канал @unity_news (4.7k подписчиков) > Телеграм тема > "Спасибо"
Аватара пользователя
DbIMok
Адепт
 
Сообщения: 6372
Зарегистрирован: 31 июл 2009, 14:05

Re: [Статья]Тэги и взаимодействие между объектами.

Сообщение ilkalawson 03 апр 2015, 20:19

DbIMok писал(а):
ilkalawson писал(а):наследоваться от MonoBihavior

что это?

Это класс MonoBehaviour написанный с ошибкой, т к код писался на планшете.
Исправил.
ilkalawson
UNIверсал
 
Сообщения: 412
Зарегистрирован: 19 янв 2015, 20:38
Skype: lawsonunity


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

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

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