Практическое использование движка Unity3d.
Задача 3: Выбор и подсветка объектов в Unity3d
Содержание
- Задача 3: Выбор и подсветка объектов в Unity3d.
 - 1. Выбор объекта
 - 2. Подсветка объекта
 - Замечания
 
Задача 3: Выбор и подсветка объектов в Unity3d.
  В этой статье рассмотрим следующую задачу: необходимо реализовать выбор объектов левой клавишей мыши, причем при выборе объекта необходимо его подсветить. Эта статья является продолжением второй статьи (http://denis-potapenko.blogspot.com/2013/03/2-unity3d.html). Поэтому перед началом чтения этой статьи желательно прочесть вторую часть. Результат, который мы хотим получить представлен на рисунке В:
Рисунок. В. – Результирующая сцена
Выбор объекта
  Для начала создадим переменную – член класса, которая будет хранить выбранный объект. 
// selected GameObject private GameObject mSelectedObject;
  Также создадим свойство на этот объект.
////// Gets or sets selected GameObject /// public GameObject SelectedObject { get { return mSelectedObject; } set { … // assign new game object mSelectedObject = value; … } }
  Работу сеттера этого свойства рассмотрим ниже.
  Теперь создадим метод, который будет выбирать объект. Определим объект, находящийся под мышкой. Алгоритм выбора объекта следующий:
- Перевести позицию мышки из пространства окна (2д координаты) в трехмерные координаты (А);
 - Построить луч, идущий от позиции мышки в 3д координатах в сторону «от камеры» (Б);
 - Найти пересечение луча с любым объектом в сцене;
 - Первый объект, попавшийся на пересечении, является выбранным;
 
  Преобразуем позицию мыши в луч исходящий от камеры (Объединим пункты А и Б) используя метод «ScreenPointToRay».
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
  Теперь найдем пересечение луча с любым объектом в сцене. Для этого используем метод «Physics.Raycast». 
RaycastHit hit; if (Physics.Raycast(ray, out hit, Constants.cMaxRayCastDistance))
  Хочу сразу заметить, что на каждый объект, с которым мы хотим посчитать пересечение, должен быть назначен так называемый «Collider». «Collider» используется в основном для работы с физикой и в частности для определения пересечений. Для того чтоб назначить «Collider» на GameObject, необходимо выделить объект в сцене, и выбрать пункт меню «Component / Physics / Box Collider». В дальнейших статьях мы более подробно изучим эти компоненты.
  В случае если пересечение найдено метод «Physics.Raycast» возвращает «true» и заполняет переменную «hit». Теперь определим GameObject, который соответствует найденному объекту типа «RaycastHit». 
// get game object GameObject rayCastedGO = hit.collider.gameObject;
И наконец, установим найденный объект, как выделенный. 
// select object this.SelectedObject = rayCastedGO;
  А теперь нужно вызвать метод “SelectObjectByMousePos” в методе «Update». Причем вызывать мы его будет, только при нажатии левой клавиши мыши.
// process object selection
if (Input.GetMouseButtonDown(0))
{
    SelectObjectByMousePos();
}
  Теперь давайте подробно рассмотрим, как работает сеттер свойства «SelectedObject».Подсветка объекта
  С начала немного теории. Дело в том, что подсветку объектов можно сделать множеством способов. Я расскажу способ, который можно добиться, не используя никакие дополнения к движку Unity3d. После описания теории и практики я расскажу Вам, на мой взгляд, более совершенный способ реализации подсветки. 
  И так алгоритм реализации подсветки объектов следующий:
- Создать 2 материала: первый обычный материал, второй материал подсветки;
 - Передать эти материалы в скрипт;
 - При выделении объекта, установить «подсвеченный» материал на выделенный объект;
 - На объект, который был выделен ранее, установить обычный материал;
 
Теперь рассмотрим реализацию этого метода. В редакторе Unity3d создадим папку с материалами, и назовём ее «Materials». В этой папке создадим 2 материала – обычный “SimpleMat”, и подсвеченный “HighlightedMat”. Получившая иерархия папок и файлов показана на рисунке 2.1
Рис. 2.1 - Структура файлов и папок с материалами
  Хочу отметить, что у обоих материалов установлен стандартный шейдер “Diffuse”. Материалы настроены идентично, за исключением того что у материала «HighlightedMat» диффузный цвет установлен в красный. На рис. 2.2 показаны настройки материала «HighlightedMat».
Рисунок 2.2 – Настройки материала «HighlightedMat»
  Теперь создадим 2 публичных переменных – члена класса “AppRoot”, которые будут хранить созданные материалы.
// materials for highlight public Material SimpleMat; public Material HighlightedMat;
Теперь присвоим этим переменным значение. Сделаем это прямо в редакторе. Процесс назначения материалов в скрипт показан на рис. 2.3.
Рисунок 2.3 – Назначение материалов в скрипт “AppRoot” 
  Теперь наконец-то реализация сеттера свойства “SelectedObject”. 
  Получим «старый» выбранный объект.
// get old game object GameObject goOld = mSelectedObject;
Установим новый выбранный объект.
// assign new game object mSelectedObject = value;
Если выбранный объект не поменялся, просто выходим с метода.
// if this object is the same - just not process this
if (goOld == mSelectedObject)
{
    return;
}
  Установим материал на старый выбранный объект.
// set material to non-selected object
if (goOld != null)
{
    goOld.renderer.material = SimpleMat;
}
  Установим материал на выбранный объект.
// set material to selected object
if (mSelectedObject != null)
{
    mSelectedObject.renderer.material = HighlightedMat;
}
В результате мы получили в сцену, в которой пользователь, с помощью левой клавиши мыши, может выбирать объекты, и они будут окрашиваться в красный цвет. 
Замечания
  Как я уже писал выше, я не считаю выше описанный алгоритм подсветки объектов самым эффективным. Я считаю, что более эффективный метод будет использовать шейдер с компонентом «Emissive». Этот компонент умножается особым способ на результирующий цвет, таким образом, выделенный объект получает «окраску» например красного цвета, а не полностью закрашивается. Вы вполне можете создать такой шейдер своими руками. В последующих статьях я расскажу как это сделать. Из существующих стандартных шейдеров в Unity3d, я нашел шейдер «Reflective/Diffuse», который в некоторых случаях очень подходит для целей подсветки объектов. Можете попробовать использовать этот шейдер и его поле “Reflection Color” вместе “Emissive”.
Демонстрацию работы можно посмотреть здесь:
https://dl.dropbox.com/u/20023505/Articles/Unity3d/Lesson3/WebPlayer/WebPlayer.html
Исходные коды Вы как всегда сможете бесплатно скачать здесь:
https://dl.dropbox.com/u/20023505/Articles/Unity3d/Lesson3/Sources/
Sources.ziphttps://github.com/den-potapenko/Unity3dArticles/tree/master/Lesson3
Не получается у меня выделить объект от First Person который я создал из fbx.
ReplyDeleteработает при строчке
Ray ray = new Ray(transform.position, transform.forward);
Но тогда надо стоять напротив объекта
А при строчке
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Не находит объект
Что можно сделать?
Пожалуйста дайте ссылку, где можно скачать Ваш проект, и я помогу Вам
ReplyDeleteКак по-мне, то проще воспользоваться событиями наводки/отводки мыши на объект.
ReplyDeletepublic class Target : MonoBehaviour {
Material defaultMat;
Material newMat;
void Start() { defaultMat = transform.renderer.material; }
void OnMouseEnter() { transform.renderer.material = newMat; }
void OnMouseExit() { transform.renderer.material = defaultMat; }
}