Pull to refresh

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

Reading time 7 min
Views 75K
image

На 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, а все файлы со скриптами (независимо от языка) — добавлены в проект.
VisualStudio

Что надо иметь ввиду:

  • Каждый отдельный скрипт — это класс, описывающий поведение игровой сущности. Например, то что сущность «мокрая» при касании повышает уровень влажности на 3. Потом в игровом редакторе мы прикручиваем эту сущность к конкретной луже — и при попадании в нее уровень влажности будет повышаться. Каждый такой класс должен быть отнаследован от класса UnityEngine.MonoBehaviour, и если в скриптах на js это подразумевается, то в C# надо указывать явно.
  • Unity не переваривает namespace'ы. В будущем обещают исправить, но пока так.
  • Имя класса и имя файла скрипта должны совпадать. Если вы создадите класс через Visual Studio, то не забудтье брать namespace И отнаследовать класс от MonoBehaviour (using тоже не забудьте). Если вы создадите класс через Unity, то по умолчанию он будет называться NewBehaviourScript, не забудьте переименовать как файл скрипта, так и имя класса.

Для описания других тонкостей приеду пример простого скрипта, который идет в комплекте с движком и позволяет перемещаться как в FPS, то есть wasd + пробел для прыжка + мышь. Чтобы игровой аватар пользователя не провалился ниже пола, его надо обернуть в простой коллайдер — CharacterController, каковой должен налепиться на игровой объект автоматически при применении к нему сущности FPSWalker.

//параметры нашего хождения<br/>
var speed = 6.0;<br/>
var jumpSpeed = 8.0;<br/>
var gravity = 20.0;<br/>
 <br/>
//внутренние переменные<br/>
private var moveDirection = Vector3.zero;<br/>
private var grounded : boolean = false;<br/>
 <br/>
//обновление по таймеру<br/>
function FixedUpdate() {<br/>
   //мы на земле<br/>
   if (grounded) {<br/>
      //строим вертор перемещения<br/>
      moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));<br/>
      //переносим в глобальные координаты<br/>
      moveDirection = transform.TransformDirection(moveDirection);<br/>
      //увеличиваем пропорционально скорости<br/>
      moveDirection *= speed; <br/>
 <br/>
      //если велено прыгать - прыгаем<br/>
      if (Input.GetButton ("Jump")) {<br/>
         moveDirection.y = jumpSpeed;<br/>
      }<br/>
   }<br/>
 <br/>
   //вычитаем падение<br/>
   moveDirection.y -= gravity * Time.deltaTime;<br/>
 <br/>
   //берем товарища<br/>
   var controller : CharacterController = GetComponent(CharacterController);<br/>
   //перемещаем<br/>
   var flags = controller.Move(moveDirection * Time.deltaTime);<br/>
   //и проверям, на земле ли он<br/>
   grounded = (flags & CollisionFlags.CollidedBelow) != 0;<br/>
}<br/>
//ах да, если этот скрипт применят к игровому объекту, надо затребовать от объекта наличие компоненты CharacterController<br/>
@script RequireComponent(CharacterController)


Итак, при переводе этого скрипта на C# помимо вышесказанного следует учесть:
  • вы хотите, чтобы значения параметров можно было менять в игровом редакторе? Тогда делайте их public'ами.
    editor
  • Не путайте класс CharacterController и его тип typeof(CharacterController)
  • инструкции типа "@script RequireComponent(CharacterController)" превращаются в аттрибуты класса — [RequireComponent(typeof(CharacterController))]
  • Плавающая запятая предсавлена float'ом, на double даже вектор не помножить =)
  • В js не надо было явно приводить типы. C# — не такой!
  • И если вам нужно что-то сложное инициализировать, воспользуйтесь готовыми функциями Awake() и Start(), перегружать конструктор MonoBehaviour не рекомендуется

После применения всех этих нехитрых правил, наш класс выглядит следующим образом:

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


Все, класс готов.
Tags:
Hubs:
+19
Comments 15
Comments Comments 15

Articles