Практическое использование движка 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; }
}