Unity3D + C#, или как переводить скрипты

На Unity3D я натолкнулся сравнительно недавно, до этого работал со своими самописными движками для мобильных телефонов на Java2ME, в остальное время я — дотнетчик.
При переходе на новую платформу я в первую очередь выискивал для себя уже готовую технологическую платформу, и основными критериями для меня были цена (доступная/оправданная) и по возможности — мультиплатформенность, чтобы единожды написанный код можно было использовать вновь и вновь без конвертаций. Почти сразу я наткнулся на Unity.
Про Unity в целом тут уже писали, так что повторяться не буду, отмечу главное — разрабатываемую игру можно (а на мой взгляд — и нужно) скриптовать на C# (спасибо Mono). Единственное ограничение — следует писать в пределах .NET Framework 1.1 — только он поддерживается на iPhone. [upd: пока я писал этот пост, вышла новая версия Unity3D для iPhone, поддерживающий .NET 2.1]
Итак, ключевые плюсы для меня от использования Unity вообще и C# в частности:
- Использование .NET Framework и C# (лично для меня это очень удобно)
- Возможность сделать сборку сразу на iPhone и для веб-плеера (standalone сборки меня не прельщают по ряду причин, выходящих за рамки поста)
- Уже готовые сценарии поведения на C# можно будет повторно использовать, например, при создании порта игры на XBox при помощи XNA (с изменениями, но все же)
- Удобный интерфейс и не слишком прожорливые редакторы
- Приемлимая цена
Минусы конечно, также имеются, но речь не за них, ибо плюсы в конечном счете перевесили.
Для скриптования используется прикрученный редактор UniSciTE на базе Scintilla, что лично мне пришлось не по душе, а потому я сразу захотел воспользоваться старой доброй Visual Studio. Вот о том, как скриптовать на C# будет речь ниже:
[upd: перенесено в Game Development]
Текущая версия Unity — 2.6.1, и в ней с интеграцией с Visual Studio проблем никаких нет — достаточно в окне обозревателя проекта сделать правый клик и выбрать соответствующий пункт меню. После этого Unity создаст солюшн, а если он уже создан — обновит. В проекте уже будут указаны ссылки на используемые библиотеки UnityEditor и UnityEngine, а все файлы со скриптами (независимо от языка) — добавлены в проект.

Что надо иметь ввиду:
- Каждый отдельный скрипт — это класс, описывающий поведение игровой сущности. Например, то что сущность «мокрая» при касании повышает уровень влажности на 3. Потом в игровом редакторе мы прикручиваем эту сущность к конкретной луже — и при попадании в нее уровень влажности будет повышаться. Каждый такой класс должен быть отнаследован от класса UnityEngine.MonoBehaviour, и если в скриптах на js это подразумевается, то в C# надо указывать явно.
- Unity не переваривает namespace'ы. В будущем обещают исправить, но пока так.
- Имя класса и имя файла скрипта должны совпадать. Если вы создадите класс через Visual Studio, то не забудтье брать namespace И отнаследовать класс от MonoBehaviour (using тоже не забудьте). Если вы создадите класс через Unity, то по умолчанию он будет называться NewBehaviourscript, не забудьте переименовать как файл скрипта, так и имя класса.
Для описания других тонкостей приеду пример простого скрипта, который идет в комплекте с движком и позволяет перемещаться как в FPS, то есть wasd + пробел для прыжка + мышь. Чтобы игровой аватар пользователя не провалился ниже пола, его надо обернуть в простой коллайдер — CharacterController, каковой должен налепиться на игровой объект автоматически при применении к нему сущности FPSWalker.
//параметры нашего хождения
var speed = 6.0;
var jumpSpeed = 8.0;
var gravity = 20.0;
//внутренние переменные
private var moveDirection = Vector3.zero;
private var grounded : boolean = false;
//обновление по таймеру
function FixedUpdate() {
//мы на земле
if (grounded) {
//строим вертор перемещения
moveDirection = new Vector3(Input.GetAxis("Horizontal"), , Input.GetAxis("Vertical"));
//переносим в глобальные координаты
moveDirection = transform.TransformDirection(moveDirection);
//увеличиваем пропорционально скорости
moveDirection *= speed;
//если велено прыгать - прыгаем
if (Input.GetButton ("Jump")) {
moveDirection.y = jumpSpeed;
}
}
//вычитаем падение
moveDirection.y -= gravity * Time.deltaTime;
//берем товарища
var controller : CharacterController = GetComponent(CharacterController);
//перемещаем
var flags = controller.Move(moveDirection * Time.deltaTime);
//и проверям, на земле ли он
grounded = (flags & CollisionFlags.CollidedBelow) != 0;
}
//ах да, если этот скрипт применят к игровому объекту, надо затребовать от объекта наличие компоненты CharacterController
@script RequireComponent(CharacterController)
Итак, при переводе этого скрипта на C# помимо вышесказанного следует учесть:
- вы хотите, чтобы значения параметров можно было менять в игровом редакторе? Тогда делайте их public'ами.

- Не путайте класс CharacterController и его тип typeof(CharacterController)
- инструкции типа "@script RequireComponent(CharacterController)" превращаются в аттрибуты класса — [RequireComponent(typeof(CharacterController))]
- Плавающая запятая предсавлена float'ом, на double даже вектор не помножить =)
- В js не надо было явно приводить типы. C# — не такой!
- И если вам нужно что-то сложное инициализировать, воспользуйтесь готовыми функциями Awake() и Start(), перегружать конструктор MonoBehaviour не рекомендуется
После применения всех этих нехитрых правил, наш класс выглядит следующим образом:
using UnityEngine;
//тот самый аттрибут класса
[RequireComponent(typeof(CharacterController))]
//не забыли переименовать класс и файл и указать наследование
public class FPSWalkerInCS : MonoBehaviour {
//это надо показать в редакторе, обращаем внимание на float
public float speed = 6.0f;
public float jumpSpeed = 8.0f;
public float gravity = 20.0f;
//а это - не надо
private Vector3 moveDirection = Vector3.zero;
private bool grounded = false;
//сюда класть инициализцию
void Start () {
}
void FixedUpdate() {
if (grounded) {
moveDirection = new Vector3(Input.GetAxis("Horizontal"), , Input.GetAxis("Vertical"));
moveDirection = transform.TransformDirection(moveDirection);
moveDirection *= speed;
if (Input.GetButton ("Jump")) {
moveDirection.y = jumpSpeed;
}
}
moveDirection.y -= gravity * Time.deltaTime;
//не забываем приведение типов
CharacterController controller = (CharacterController)GetComponent(typeof(CharacterController));
CollisionFlags flags = controller.Move(moveDirection * Time.deltaTime);
grounded = (flags & CollisionFlags.CollidedBelow) != 0;
}
}
Все, класс готов.



комментарии (15)