Пользователь
0,0
рейтинг
19 сентября 2011 в 17:59

Разработка → Погружение в скрипты игрового движка Unity3d, ч.1 из песочницы

Доброго времени суток, уважаемый читатель! На Хабре неоднократно публиковались статьи о разработке игр с использованием замечательного движка Unity3d. Большинство этих статей были посвящены вполне определенным задачам, я же хотел сделать общий экскурс в данный движок. Данная часть будет посвящена наиболее часто используемым скриптовым методам и объектам, которые используются мной в процессе разработки на данном движке. Примеры я буду приводить на JavaScript, как на наиболее близком мне языке.

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


Создание нового игрового объекта с именем MyObject.
var myObject = new GameObject("MyObject");
Созданный объект будет доступен по ссылку myObject.

Поиск объекта по его имени.
var myObject = GameObject.Find("MyObject");


Теги можно использовать для помечания группы объектов со сходными свойствами, либо использующиеся в единой сцене.
Поиск объекта по тегу, возвращает единственный объект:
var myObject = GameObject.FindWithTag("MyTag");


Возвращает список всех объектов с указанным тегом:
var objectList = GameObject.FindGameObjectsWithTag("MyTag");


Проверка на наличие у объекта требуемого тега. Возвращает true, если у указанного объекта имеется тег MyTag:
var isCompare = GameObject.CompareTag("MyTag");


Уничтожение объекта:
Destroy(myObject);


Уничтожение объекта через минуту, после его создания:
Destroy(myObject, 60);


Возвращает компонент component, привязанный к объекту GameObject, либо null, если объект не содержит данного компонента. Может использоваться, например, для доступа к другим скриптам, привязанным к объекту.
var objectComponent = GameObject.GetComponent(component);


Возвращает все имеющиеся у объекта компоненты типа componentType.
var objectComponents = GameObject.GetComponents(componentType);


Привязать компонент myComponent к объекту GameObject и получить ссылку на него.
var component = GameObject.AddComponent(myComponent);


Положение игрового объекта



Свойство transform объекта GameObject содержит в себе данные о положении объекта в игровом мире.

Возвращает глобальные координаты объекта в игровом мире. Возвращаемая величина имеет тип Vector3, который представляет из себя список из 3 координат — x, y и z:

var position = GameObject.transform.position; 
var x = position.x;


Переместить объект в точку 0, 10, 0 игрового мира.
GameObject.transform.position = Vector3(0, 10, 0);


Тоже самое, что и в случае глобальных координат, но с локальными. Локальные координаты расситываются относительно родительского объекта. В случае отсутствия родительского объекта локальные координаты совпадают с глобальными:
var localPosition = GameObject.transform.localPosition; 
var x = localPosition.x;


Поворот объекта в углах Эйлера. Метод также возвращает координаты в виде объекта Vector3:
var eulerAngle = GameObject.transform.eulerAngles;


Тоже самое, что и предыдущий пример, но поворот объекта рассчитывается относительно родительского объекта:
var localEulerAngle = GameObject.transform.localEulerAngles;


Текущий угол поворота объекта, основанный на кватернионах. Возвращает объект типа Quaternion.
var quaternionAngle = GameObject.transform.rotation;


Текущий поворот объекта, основанный на кватернионах, но относительно родительского объекта:
var localQuaternionAngle = GameObject.transform.localRotation;


Сброс угла поворота объекта:
GameObject.transform.rotation = Quaternion.identity;
GameObject.transform.localRotation = Quaternion.identity;


Вращаем наш объект в указанную сторону со скоростью 1 градус в секунду. Принимает в качестве координат объект типа Vector3. Метод deltaTime объекта Time содержит время в секундах, затраченное на выполнение предыдущего кадра:
GameObject.transform.Rotate(Vector3.left * Time.deltaTime);


Тоже самое, что и предыдущий пример, но вращение объекта относительно координат родителя:
GameObject.transform.localRotate(Vector3.left * Time.deltaTime);


Перемещаем наш объект в указанном направлении со скоростью 1 юнит в секунду. Также принимает в качестве координат объект класса Vector3:
GameObject.transform.Translate(Vector3.up * Time.deltaTime);


Физические свойства игрового объекта


Метод rigidbody объекта GameObject хранит в себе его физические свойства. Прежде, чем использовать метод rigidbody, его необходимо добавить к игровому объекту.

Получаем/задаем вектор скорости объекта:
var velocity = GameObject.rigidbody.velocity;
GameObject.rigidbody.velocity = Vector3(0, 1, 0);


Сила противодействия объекта. Может использоваться для замедления скорости, в среде с отсутствующей силой трения. Наиболее часто используется для замедления падающих объектов, например при создании парашюта. Принимает в качестве параметра целое число:
GameObject.rigidbody.drag = 100;


Задание массы объекту. Рекомендуется использовать массу в пределах от 0.1 до 10. Использование слишком больших значений может привести к непредсказуемым результатам при расчете физики:
GameObject.rigidbody.mass = 5;


Влияние на объект гравитации. Принимает в качестве параметра булево значение. Позволяет отключить влияние гравитации на отдельные объекты:
GameObject.rigidbody.useGravity = false;


Влияние физики на игровой объект. Позволяет отключить частично, либо полностью влияние физических законов на объект:
GameObject.rigidbody.isKinematic = true;


Запрет на вращение объекта. Наиболее часто используется, когда необходимо сохранить определенный угол поворота даже после столкновения с другими объектами:
GameObject.rigidbody.freezeRotation = true;


Указание координат точки центра массы объекта. Применяет координаты в виде уже знакомого нам объекта Vector3.
GameObject.rigidbody.centerOfMass = Vector3(1, 0, 0);


Использовать ли для объекта обнаружение столкновений с другими объектами. Можно выключить, тогда ваш объект будет игнорировать любые столкновения:
GameObject.rigidbody.detectCollisions = false;


Режим определения столкновений между объектами. Можно указать несколько разных режимов:
CollisionDetectionMode.ContinuousDynamic для быстро движущихся объектов;
CollisionDetectionMode.Continuous для столкновений с быстро движущимися объектами;
CollisionDetectionMode.Discrete (по умолчанию) для обычных столкновений;
В случае отсутствия проблем с определением столкновений рекомендуется использовать свойство по умолчанию.

Задать плотность объекта:
GameObject.rigidbody.SetDensity(1.5);


Применить импульс к объекту с указанным вектором. В результате применения импульса объект придет в движение пропорционально силе импульса.
GameObject.rigidbody.AddForce(5, 0, 0);


Применить импульс к объекту с вектором в его (объекта) системы координат:
GameObject.rigidbody.AddRelativeForce(0, 0, 5);


Добавить объекту крутящий момент. Применение данного метода заставит объект вращаться вокруг своего центра масс GameObject.rigidbody.centerOfMass.
GameObject.rigidbody.AddTorque(0, 1, 0);


Тоже самое, что и предыдущий пример, но относительно координат объекта:
GameObject.rigidbody.AddRelativeTorque (1, 0, 0);


Применение импульса к объекту из внешней указанной точки. Заставляет объект двигаться и вращаться одновременно. Может использоваться, например, для симуляции попадания в объект пули. Первый параметр указывает вектор направления силы, второй параметр — исходную точку направления силы.
GameObject.rigidbody.AddForceAtPosition(Vector3(0, 5, 7), Bomb.transform.position);


Для полноценной симуляции объемных взрывов в Unity3D есть отдельный метод. Первый параметр метода позволяет указать мощность импульса, второй параметр — точку, из которой исходит импульс, третий параметр — радиус распространения импульса, четвертый параметр — модификатор сжатия сферы распространения силы, пятый, необязательный, параметр указывает тип используемого импульса:
GameObject.rigidbody.AddExplosionForce(power, explosionPos, radius, 2.0);


Заставить объект «уснуть», и запретить дальнейший расчет физических показателей для него:
GameObject.rigidbody.Sleep();


Проверить «заснул» ли объект:
GameObject.rigidbody.IsSleeping();


«Разбудить» объект для возможности дальнейшего применения влияния физики на него:
GameObject.rigidbody.WakeUp();


Трассировка лучей


Один из самых часто используемых в разработке на Unity3D объект, это Ray. Данный объект позволяет выпустить луч из указанной точки, в указанном направлении, и вернуть некоторые свойства объектов, которых он смог достичь.

Создаем объект класса RaycastHit, который содержит информацию об объекте, с которым столкнулся луч:
var hit : RaycastHit;


Отправляем луч длиной в 50 юнитов из позиции rayPosition в направлении rayVector, и заносим объект, с которым столкнулся луч в переменную hit:
Physics.Raycast(rayPosition, rayVector.forward, hit, 50);


Получаем дистанцию до объекта, с которым столкнулся луч. Дистанция не может быть больше, чем протяженность луча:
var distance = hit.distance;


Иногда бывает необходимо получить имя объекта, с которым произошло столкновение луча. Наиболее простой способ это сделать:
var objectName = hit.collider.gameObject.name;


Для получения тега объекта используем следующий способ:
var Tag = hit.collider.tag;


Unity3D содержит еще множество различных методов и объектов, полезных и не очень. К сожалению полный их обзор увеличил бы и без того объемную статью, поэтому я постараюсь рассказать об остальном более подробно в будущем, если мне представится такая возможность. Я бы хотел пожелать опытным разработчикам побольше интересных проектов, а начинающим — успехов и интересных открытий. Спасибо, что уделили внимание данной статье.

UPD. вторая часть (скриптовые события).
Абдуллин Дамир @damirazo
карма
49,7
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (34)

  • 0
    Неплохая выборка для тех кто не стал читать документацию. В основном новичкам будет полезно.
  • +1
    Отдельное спасибо за объяснение про лучи, как раз думал, как определить, на какой объект пользователь клацнул мышью.
  • НЛО прилетело и опубликовало эту надпись здесь
    • +1
      ИМХО — от статьи больше вреда чем пользы. Например, за Find(name) можно увольнять, если оно больше чем раз.
      • +4
        Спасибо. Статья в первую очередь позиционируется как справочник, а не самоучитель. Просто хотел, чтобы человек, впервые взявшийся за данный движок, знал о подобных возможностях. Если у Вас есть предложения, то я всегда готов к конструктивной критике.
      • 0
        объясните, пожалуйста, подробнее. Я, к примеру, реализовываю синглтон вот так:
        public class FileDownloader : MonoBehaviour
        {
        private static FileDownloader instance = null;

        public static FileDownloader current {
        get { return instance; }
        }

        // Use this for initialization
        void Awake ()
        {
        if (FileDownloader.instance == null) {
        FileDownloader.instance = FindObjectOfType (typeof(FileDownloader)) as FileDownloader;
        }
        queue = new Queue ();
        isReady = false;
        }


        Это плохо? Чем?
        • 0
          блин, не до конца скопипастил. Но вы поняли идею, да?
          • 0
            ну, последнюю неделю, я провел работая над тем что бы игра закажчика на айфоне 3гс выдавала больше 20 фпс. Чесно признаться — лучше вообще ничего не искать. Совсем. Сделайте себе референс, перетащите в редакторе. Тем более я сказал, «больше чем раз». У вас оно раз, не каждый кадр, а при старте/возвращении. Но я все еще не понял зачем. У вас же и так статический FileDownloader, зачем его еще искать?
            • 0
              в смысле куда логичнее было бы сделать инициализацию лениво в самом файл даунлоадере, не?
              public class FileDownloader
              {
              private static FileDownloader _instance = null;
              public static FileDownloader instance
              {get { return _instance??(_instance = new FileDownloader()); } }
              }

        • +2
          Тема оптимизации вообще заслуживает отдельной статьи. В данной замечательной статье рассматриваются некоторые вопросы оптимизации, в одноименной главе. В частности, цитирую:
          2. Без необходимости лучше не использовать Find\GetComponent и похожие методы — по мере возможности лучше сохранить ссылку на компонент один раз при запуске скрипта.
    • +1
      unity3d.com/support/documentation/ScriptReference/Input-mousePosition.html
      там есть пример, который делает то что вы хотите
      if (Input.GetButtonDown("Fire1")) {
      Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
      if (Physics.Raycast(ray))
      Instantiate(particle, transform.position, transform.rotation) as GameObject;

      }
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          нет, не Вам =)
    • +1
      Я руководствовался оффициальной документацией, в частности этим описанием. В принципе на практике я никогда не испытывал проблем с указанием больших значений, однако раз это указано в Script Reference, я решил упомянуть об этом и в статье.
  • 0
    возможно оффтопик, но хотел бы спросить.
    можно ли модифицировать один обьект другим обьектом с использованием булевой опреации?

    грубо говоря есть квадрат и окружность (пересекающаяся), надо получить обьект иллиюстрирующий булевую операцию «И»
    тоесть оставить только ту область где два обьекта пересекаются?

  • +1
    Ничего нового для себя не нашёл, но новичкам правда может пригодится.

    Автору всё равно спасибо
  • +1
    Ну вот, а я как раз подобную статью пишу в данный момент. Даже не знаю, стоит ли постить теперь…

    По статье — сухо, ну какой мне толк от того, что я знаю, как получить кватернион объекта, если я не понимаю, что это такое и как они работают (статья на вики не одержит ответа на этот вопрос). В Scripting Refefence и то подробнее все расписано.
    • +2
      Лично мое мнение — полезной информации никогда не бывает много. Поэтому писать статью Вам определенно стоит.
      Возможно описано сухо, но я постарался аггрегировать наиболее употребляемые мной лично методы в работе, надеюсь кому-то пригодится как справочник. Изначально была идея сделать «живые» примеры использования для большинства описанных методов, но статья выходила слишком объемный. Решил оставить в таком виде, как вводной экскурс. Если получится, то в ближайшее время планировал сделать более подробные статьи по каждому разделу с примерами и описанием.
      • 0
        Да просто обсасывать одну и ту же тему, ИМХО, моветон. Тем более, с отрывом в пару дней :)

        Ну, если как справочник — тогда все окей, начинающим конечно пригодится, просто я больше именно туториалы и хау-ту читаю, поэтому первая реакция такая была.

        Про объем можете и не говорить, я в январе этого года написал тут коротенький туториал для новичков, а когда второй стал делать быстро сдулся, потому что пытался объяснить в одном уроке от и до каждый аспект… А сейчас вот решил продолжить это дело, только уже постепенно матчасть объяснять, а не сразу практические задачи решать…
  • НЛО прилетело и опубликовало эту надпись здесь
    • +1
      Просто на JavaScript пишу несколько дольше, да и изначально писать под Unity3d начал на нем. Конечно переучиваться никогда не поздно, но на данный момент меня все устраивает.
  • +2
    Очень познавательно для новичков и для тех кто не читает документацию, но я бы на вашем месте раскрыл все таки нюансы производительности при использовании той или иной функции например Find(name) как писали выше, категорически нельзя пихать в Update(), или например:

    Добавить объекту крутящий момент. Применение данного метода заставит объект вращаться вокруг своей оси в указанную сторону:

    GameObject.rigidbody.AddTorque(0, 1, 0);


    Маленькая поправка: он будет вращаться всетаки вокруг своего центра тяжести rigidbody.centerOfMass.
    • 0
      Спасибо, поправил.
  • 0
    У меня была одна проблема, которую до сих пор не решил:
    Есть объект не правильной формы, скажем в виде буквы «П», для инспектора в Юнити он представляется как правильный прямоугольный объект, как программно определить все его реальные стороны, то есть внутренние стороны «П», с внешними все понятно, это не сложно, а вот внутренние, или скажем изначально объект в Максе был повернут и сохранен, в Юнити его видно как прямоугольный, но у него уже есть угол наклона, тоже определить его реальные стороны так и не вышло.

    Некто не сталкивался с такой проблемой и не знает как ее решить?
    • 0
      Я не совсем понял о чем речь (точнее совсем не понял),
      скриншот помог бы!
      • 0
        Давайте перефразирую, есть фигура в виде ромба, для инспектора он прямоугольник, как реально найти стороны этой фигуры?
        • 0
          Это такой очень тонкий троллинг? )

          1. Что значит фраза «для инспектора в юнити он представляется»?
          2. Что значит фраза «как программно определить все его реальные сторон»/«как реально найти его стороны»?

          Нужен скриншот, или лучше чуточку более внятное описание и можно простой проект-пример.

          Спасибо!
        • 0
          Я конечно могу ошибаться, но мне кажется, что у Вас при импорте задействуется дефолтный боксовый коллайдер. То есть после импорта модели Вам следует повесить на нее коллайдер с той же геометрией, что и сама модель. То есть навесить в качестве коллайдера эту же модель. Немного субурно, конечно, но думаю смысл ясен :)
  • +1
    Неплохо было бы добавить, что обращение к rigidbody объекта требует, чтобы у этого объекта был компонент Rigidbody (добавить либо через редактор, либо через код), а то будет возникать NullReferenceException
    • 0
      Спасибо, обновил.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.