---
title: Unity3D
date: 2009-11-16T10:05:00
tags: [ai, go, javascript, php, soft]
---

[Unity3D](http://unity3d.com/) это игровой движок 3D-игр в основном стрелялок и аркад, но при большой сноровке можно реализовать и [RPG](http://krankyboygames.com/), RTS, квесты, 2D-игры, [встраиваемые в браузер](http://www.interstellarmarines.com/game/bullseye/?guest), iPhone и с использованием сети. Движок понимает множество разные форматы файлов

- 3д модели и анимация из Maya, Cinema 4D, 3ds Max, Cheetah3D, Modo, Lightwave, Blender. Форматы 3ds, fbx и dae
- практически все растровые изображения для маск и текстур
- звуковые mp3, wav форматы для эффектов и фона  
    

Профессиональная версия имеет более расширенные возможности по работе с динамическим освещением, сетевыми возможностями. Но даже без наличия блокбастерных игр о которых все бы слышали, unity успешно продвигается в плане комьюнити и «народного движка» и вполне возможно что с бесплатными плагинами можно будет написать игру ничем не уступающую платным.

### IDE

![](../../игры/img/Pasted%20image%2020241020180021.png)

Графическая оболочка разработчика (IDE) состоит из 4 основных панелей

1. Графическое поле, переключающееся / вид игры (Game & Scene)
2. Иерархия объектов (Hierarchy)
3. Инспектор объекта (Inspector)
4. Библиотека доступных ресурсов (Assets)

По используемости самое важное место это Инспектор, потому что именно в нём видны внутренности объекта (**GameObject**)

### Создание карты

Карта (Terrain) создаётся очень просто из меню и по умолчанию уже есть. Курсором как в фотошопе можно изменять высоту рельефа (в том числе используя Shift). Более [продвинутые](http://bmp.thefloatingwidget.net/heightmaps/) господа придумали как импортировать карты из реального мира с помощью google earth, [openTDD](http://www.tt-forums.net/viewtopic.php?f=33&t=33956), делая так называемые heightmaps. Встроенный импорт работает только с raw форматами, надо [использовать хак](http://www.unifycommunity.com/wiki/index.php?title=HeightmapFromTexture). Недавно вышел генератор карт

![](../../игры/img/Pasted%20image%2020241020180040.png)

### Игровой объект Unity  

Игровой объект может состоять из скриптов-компонентов, некоторые из них встроенные, некоторые надо писать самому, тоесть:

- Местоположения ([Transform](http://unity3d.com/support/documentation/ScriptReference/Transform.html))
- Внешнего вида ([Mesh](http://unity3d.com/support/documentation/ScriptReference/Mesh.html))
- Модели столкновения ([Collider](http://unity3d.com/support/documentation/ScriptReference/Collider.html))
- Твёрдое тело ([Rigidbody](http://unity3d.com/support/documentation/ScriptReference/Rigidbody.html))
- Управляемый пользователь ([CharacterController](http://unity3d.com/support/documentation/ScriptReference/CharacterController.html))

Свои скрипты добавляются очень легко перетаскиванием в инспектор и публичные переменные сразу переводятся в названия с пробелами и значением по умолчанию. Для игрового объекта добавленные **компоненты** и с точки зрения ООП являются инкапсулированными объектами, хотя и играют в большинстве своём роль свойств (т.е. прилагательных - движущийся, крутящийся, выбираемый и тп.). По синтаксису скрипты пишутся на Javascript, C# или Boo скриптах со своими прибамбасами - глобальными объектами Unity, некоторыми своими ключевыми словами..

---

### Взаимодействие объектов  

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

Поскольку объекты можно вкладывать друг в друга как dom-блоки, то искать по иерархии объекты можно несколькими путями. Жалко что ребята изобретали велосипед, вместо того что-бы взять существующие аналоги из html/js  

- Сразу писать название компонента скажем myWorkerGuy.someColorParam
- Поиск по иерархии - **transform.Find**("MyWorkerGuy") - аналог html'ного getElementById  
    
- По тєгам, через GameObject.**FindWithTag** ("Worker")  
    

Компонент объекта доступен через - **GetComponent**("Moveable").  

Ещё одно мощное решение - вместо поиска объекта с проверкой наличия метода и затем вызова его, можно просто послать сообщение - gameObject.**SendMessage**("BegForFood",true), причём метод вызывается у всех компонентов.  

Каждый компонент наследует класс [**Monobehaviour**](http://unity3d.com/support/documentation/ScriptReference/MonoBehaviour.html) (в Javascript синтаксисе объявлять не нужно) и именно с ним приходится работать описывая логику и расширяя наследуемые методы которые сами вызываются когда движок решит это нужным, например:  

- Invoke - вызов третьего метода, в том числе периодический, аналог [call_user_method](http://ee.php.net/call_user_method) в php + cron  
    
- Awake - своего рода конструктор, вызывающийся когда все объекты уже существуют.
- Start - конструктор второго рода, вызывается после Awake
- **Update** - вызывается каждый фрейм. Сюда идёт основная логика.
- FixedUpdate - вызывается каждый n-фрейм, полезен для вычислений твёрдых тел
- LateUpdate - вызов после всех Update вызовов, полезен для камеры  
    
- OnGui - сюда идёт показ кнопок, цифр для управления

### Реальные нужды  

После того как вы придумаете историю, надо продумать основные технические моменты. К этому моменту вы знаете что есть объекты и их характеристики (компоненты), но из чего состоит процесс игры?

**Рождение** (Spawn) юнитов и врагов. Объект создаётся с указанием, где и с каким вращательным моментом. Как оказывается нарожать много умных юнитов с поддержкой всех фич (в частности физики) очень нагружает процессор.

`GameObject go = Instantiate (building,hit.point,Quaternion.identity) as GameObject;`

**Управление камерой**. Существуют уже заранее сделанные компоненты-скрипты на всякие случаи жизни, которые учитывают в том числе столкновение камеры со стеной и тп. Вот часть кода для изометрической камеры (добавьте остальные направления сами)

```
if (Input.GetKey("up") && transform.position.z < terrain.terrainData.size.z) {
    transform.position += linearSpeed * Vector3.forward * Time.deltaTime;
}

if(Input.GetAxis("Mouse ScrollWheel") > 0  && transform.position.y > 1){
    transform.position -= linearSpeed * Vector3.up * Time.deltaTime;
}
```

**Выделение юнитов мышкой**. Достаточно сложная задача - в кратце на камеру вешается обработчик нажатий и передвижения курсора. Независимо создаётся singleton-объект для хранения всех юнитов. При нажатии и передвижении курсора происходит проекция прямоугольника с маской ландшафта. Задача не из лёгких, потому что обрабатывать надо и deselect, учитывать клики для передвижения и атаки, рождение юнитов и тп. Возможно singleton не самое хорошее решение и лучше это сделать тэгами.

```
mouseButton1UpPoint = screenPosition;
var hit : RaycastHit;
ray = Camera.main.ScreenPointToRay (screenPosition);         
if ( Physics.Raycast (ray, hit, raycastLength, terrainLayerMask) ){ 
    selectionPointEnd = hit.point;
    UnitManager.GetInstance().SelectUnitsInArea(selectionPointStart, selectionPointEnd);
}    
```

**Поиск пути** (Pathfinding) и передвижение юнита кликами мышки очень алгоритмически увлекательная задача. К счастью существует [отличное решение Арона Гранберга](http://www.arongranberg.com/unity/a-pathfinding/). Алгоритм A* интересно [в теории](http://www.policyalmanac.org/games/aStarTutorial_rus.htm), но в редакторе можно и запутаться. Основная идея в том что юниты имеют дискретную сетку клеток для расчёта передвижения. 

Сетка генерируется из ландшафта и можно задать ограничения по углу наклона, максимальной высоте и тп. В плане кода - очень сложная штука. На практике его решение подразумевает что у каждого передвигаемого объекта есть Seeker компонент которому прицеплена цель куда надо дойти. При установки цели буквально надо выполнить

`GetComponent("Seeker").StartPath (transform.position,destinationPoint);`

Seeker расчитает путь по которому можно добраться до цели (обычно это ломанная кривая из 3х отрезков) и дальше уже своими силами надо реализовывать передвижение по этим точкам.

**Движение юнитов**. Пожалуй самое сложное, поскольку если юниты обладают физикой то это усложняет задачу. Надо просто понимать что нельзя сказать юниту x=x+1 через фиксированные участки времени. По идее надо использовать rigidbody.AddForce и учитывать ситуации OnCollision. Ещё одна проблема - повороты объекта. Они далеко не простые

`transform.rotation = Quaternion.Slerp (transform.rotation, Quaternion.LookRotation(direction), rotationSpeed * Time.deltaTime);`

### Минусы и сложности

Эта статья написана в 2010 году, поэтому вероятно эти минусы уже исчезли, но всё-таки..

К этому времени вы вероятно заметили что спецификация есть, IDE работает, комьюнити и примеров хватает - можно попробовать. Но надо и холодный душ принять. Вот список недочётов что я заметил

Падения. Движок падает в разных случаях - как от неосторожного движения с интерфейсом, пробного Run с косячным скриптом, так и просто при работе. Порой обидно что «не сохранился».

Глюки интерфейса. Это не так критично как падения, но мозги плавятся. Например проблема с зумом колёсиком мышки - камера перелетает килопиксели , не давая плавно подобраться к нужной сцене. Или например если вы импортировали модель с большим числом текстур или mesh-форм, будьте уверены что в следующий раз диалоги с таким содержанием будут забиты ненужными значениями.

Глупый GUI. Во времена когда HTML5 реализуется всеми браузерами, тут приходится писать очень странный код, напоминающий layout в Java Swing (sic!) и порой вычитывать недокументированные особенности на форуме (например что при декларации кнопки его onclick event сразу внутри скобок, а вот как получить onmouseover и другие события или изменить её дизайн - та ещё тайна).

Синтаксис. Согласен - трёхмерный мир это сложно и я даже могу понять почему используются математические термины типа кватерниона вместо "поворота в пространстве", но почему нельзя использовать короткие методы для основных функций, или почему нельзя в JS создавать глобальные объекты.

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

### Расширения

Unity позволяет не только писать скрипты под существующие объекты, но и изменять саму IDE добавляя новые панели управления и меню, то движок можно расширить своими мини-фреймворками  

- [Explosion](http://unity3d.com/support/resources/unity-extensions/explosion-framework)- многопараметрические взрывы (цвет, взрывная волна, дым, размеры)
- [Locomotion](http://unity3d.com/support/resources/unity-extensions/locomotion-ik) - движение людей и зверюшек c вызовом нужной анимации при соответсвующих вводах с клавиатуры
- [Skydome](http://www.pixelstudio.nl/?p=138) - динамическое небо
- [Last Bastion Games](http://lastbastiongames.com/) - диалоги, recorder, прототип RTS и гонок

![](../../игры/img/Pasted%20image%2020241020180055.png)

Обязательно гляньте:  

- [LearnMeSilly](http://learnmesilly.com/index.php?currentPage=unity_lesson1) - набор основных уроков по работе с редактором, объектами  
    
- [Blender](http://www.graphicall.org/builds/builds/showbuild.php?action=show&id=1131) - бесплатный редактор. Можно в нём создать модели и экспортировать в 3ds  
    
- [Turbosquid](http://www.turbosquid.com/) - коллекции 3д моделей (в т.ч. платные)  
    
- [Unity3D Answers](http://answers.unity3d.com/) - своего рода база проблем и решений

## Related

- [Плагины браузеров](/ru/blog/tech/soft/dopolneniya-k-brauzeram-plugins/)
- [Google AI challenge - стратегии ботов](/ru/blog/tech/google-ai-challenge-strategii-botov/)
- [Phonegap приложение](/ru/blog/tech/phonegap-prilozhenie-s-angularjs/)
