Если объект движется или вращается - он должен быть твердым телом т.е. иметь компонент Rigidbody. Если объект является твердым телом - двигаться и вращаться он должен через методы твердого тела

  • Tutorial

Мой странный творческий путь занес меня в разработку игр. Благодаря отличной студенческой программе от IT-компании, название которой СостоИт из одной Греческой МАленькой буквы, сотрудничающей с нашим университетом, удалось собрать команду, родить документацию и наладить Agile разработку игры под присмотром высококлассного QA-инженера (здравствуйте, Анна!)

Без особо долгих размышлений, в качестве движка был выбран Unity. Это замечательный движок, на котором действительно быстро и легко можно сделать очень плохую игру, в которую, в здравом уме, никто и никогда не будет играть. Чтобы создать хорошую игру, все же придется перелопатить документацию, вникнуть в некоторые особенности и набраться опыта разработки.

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

Игра

Гифка с игрой


Пару слов о том, как она сделана.
Сделана с помощью Blender и пары скриптов на питоне. На время съемки, в углу экрана находились 16 квадратиков, цвет которых кодировал 32 бита числа с плавающей запятой - вращение телефона в данный момент времени. R, G - данные, B - четность. 0 - 0, 255 - 1. Снятое на компьютере видео разбивалось на кадры с помощью ffmpeg, каждому кадру рендера в соответствие ставился расшифрованный угол. Такой формат позволил пережить любое сжатие в процессе съемки и поборол тот факт, что все программы имеют несколько разные представления о течении времени. В реальности игра играется так же как и на рендере.


Самолетик летит по бесконечной и непредсказуемой пещере, в которой есть бонусы, всякие монетки и враги, в которых можно стрелять самонаводящимися ракетами. Врезался в стену - сразу проиграл.
Отличительная особенность игры в том, что уровень прибит к горизонту и управление в ней гироскопическое, причем, абсолютное. Наклонил телефон на 45 градусов - самолетик полетел под углом 45 градусов. Нужно сделать мертвую петлю - придется крутить планшет. Никакой чувствительности нет, только хардкор.
Выделим две основные и очевидные проблемы для разработчика:
Проблема 1: Бесконечность
Unity хранит и обрабатывает координаты объектов в виде обычных 32-битных float, имеющих точность где-то до 6 знака после запятой. Проблема в том, что игра у нас бесконечная и, если мы достаточно долго будем лететь, начнутся различного рода безумные баги, вплоть до телепортации сквозь стены. Есть несколько подходов к решению этой проблемы:
  1. Игнорирование. В Minecraft, например, ошибки округления лишь сделали игру интереснее, породив феномен «Далеких Земель» .
  2. Телепортация в (0;0;0) при слишком сильном удалении самолетика от начала координат.
  3. Смена точки отсчета. Движется не самолет, а уровень вокруг него.
В нашем случае, единственный допустимый вариант - третий, который и был реализован. О реализации - чуть позже.
Первый - игнорирование - абсолютно недопустим. Создание робота, который сможет вечно играть в нашу игру - интересная (и весьма простая) задача, которую кто-нибудь решит. Да и обычных корейских игроков недооценивать не стоит - самолетик быстрый, уровень генерируется непредсказуемо. И если до прохождений сквозь стены лететь и лететь, то куда более точная стрельба начнет очевидно подглючивать уже через 5 минут полета.
Второй - телепортация игрока и всего мира - ставит мобильные устройства на колени, в некоторых случаях - где-то на полсекунды. Это очень заметно, а потому - недопустимо. Но это вполне приемлемый вариант для простеньких бесконечных игр для ПК.
Проблема 2: Генерация уровня

Есть несколько основных подходов к строительству endless runner"ов:

  1. Использование готовых сегментов уровня, которые стыкуются случайным образом. Так сделано, например, в Subway Surfers. Это просто реализовать, но игрок к этому быстро привыкает и знает, к чему готовиться, что скучно.
  2. Уровень - просто прямая, на которой случайным образом расставляются препятствия. Так сделано в Joypack Joyride и Temple Run. В нашем случае, это сильно ограничило бы количество маневров.
  3. Все генерируется случайным образом. Самый сложный, непредсказуемый и интересный для игрока вариант.
Конечно же, мы выбрали самый сложный вариант. В его сердце находится весьма сложная машина состояний, которая выполняет по ним случайные переходы. Но в рамках данной статьи интересен не механизм, а процесс генерации уровня и его организация, с учетом выбранной точки отсчета.
Структура уровня

Летим мы в пещере, она имеет пол и потолок - пару блоков, элементарных строительных единиц. Блоки объединяются в сегменты, которые бесшовно стыкуются друг с другом. Сегменты, как единое целое, вращаются вокруг самолета и двигаются по его вектору скорости, создавая иллюзию полета. Если сегмент выходит из поля зрения камеры - он очищается от блоков, пристыковывается к последнему сегменту уровня и заполняется новыми блоками, согласно указаниям генератора. Совокупность таких сегментов - и есть уровень.

Опытные Unity-разработчики могли вполне оправданно поморщиться, прикинув объем работ и все возможные подводные камни. Но на словах все просто, а опыта разработки у меня не было…

Основные Законы Физики в Unity

За месяц разработки, экспериментов и чтения документации, я выделил три основных закона физики в Unity. Их можно нарушать, но плата за нарушение - производительность. Движок никак не будет предупреждать вас о допущенной ошибке, а без профайлера вы можете никогда о них и не узнать. Несоблюдение этих законов может замедлить вашу игру в десятки раз. Как я понял, нарушение любого закона приводит к тому, что физический движок помечает коллайдер-нарушитель как некорректный и пересоздает его на объекте, с последующим пересчетом физики:
1. Коллайдеры не должны двигаться, вращаться, включаться\выключаться и менять размер.
Как только вы добавили коллайдер на объект - забудьте про какое-либо воздействие на него или объекты, в которых он содержится. Обычный коллайдер - исключительно статический объект. Дерево, например, может быть с одним коллайдером. Если дерево может упасть на игрока - дерево будет падать вместе с производительностью. Если это дерево растет из волшебного питательного облака, которое коллайдера не имеет, но может перемещаться - это будет сопровождаться падением производительности.
2. Если объект движется или вращается - он должен быть твердым телом т.е. иметь компонент Rigidbody.
Про это написано в документации, да. Которую не обязательно вдумчиво читать, чтобы начать делать игру, потому Unity очень прост и интуитивно понятен.
Rigidbody меняют отношение физического движка к объекту. На него начинают воздействовать внешние силы, он может иметь линейную и угловую скорости, а самое главное - твердое тело может двигаться и вращаться средствами физического движка, не вызывая полный пересчет физики.
Существует два типа твердых тел - обычные и кинематические. Обычные тела взаимодействуют друг с другом и обычными коллайдерами - одно тело не может пройти сквозь другое. Кинематические тела следуют упрощенным правилам симуляции - на них не воздействуют никакие внешние силы, гравитация - в том числе. Они свободно могут проходить друг через друга и коллайдеры, а вот обычные твердые тела они отталкивают, как будто имея бесконечную массу.
Если объекты не жалко отдать под контроль физического движка - используйте обычные твердые тела. Например, если вам нужно красиво скатить камни со скалы. Если ваши скрипты или аниматоры управляют объектом напрямую - используйте кинематические тела, так вам не придется постоянно бороться с движком и случайными столкновениями объектов. Например, если у вас анимированный персонаж или управляемая ракета, взрывающаяся при контакте с чем-то.
3. Если объект является твердым телом - двигаться и вращаться он должен через методы твердого тела.
Забудьте про прямое обращение к Transform"у объекта сразу же после добавления к нему коллайдера. Отныне и навсегда, Transform - ваш враг и убийца производительности. Перед тем как написать transform.position =… или transform.eulerAngles = ..., произнесите фразу «я сейчас абсолютно четко понимаю, что делаю, меня устраивают те тормоза, которые будут вызваны этой строкой». Не забывайте про иерархические связи: если вы, вдруг, сдвинете объект, содержащий твердые тела - произойдет пересчет физики.
Есть три уровня управления твердым телом:
- Самый высокий и, следовательно, естественный, уровень - через силы. Это методы AddForce и AddTorque. Физический движок учтет массу тела и правильно посчитает результирующую скорость. Все взаимодействия тел происходят на этом уровне.
- Средний уровень - изменение скоростей. Это свойства velocity и angularVelocity. На их основе вычисляются силы, влияющие на тела при их взаимодействии, а также, очевидно, их положения в следующий момент времени. Если у твердого тела очень маленькая скорость - оно «засыпает», для экономии ресурсов.
- Самый низкий уровень - непосредственно координаты объекта и его ориентация в пространстве. Это методы MovePosition и MoveRotation. На следующей итерации вычисления физики (это важно, поскольку каждый последующий вызов метода в рамках одного кадра заменяет вызов предыдущего) они выполняют телепортацию объекта в новое положение, после которой он живет как раньше. В нашей игре используется именно этот уровень, и только он, потому что он предоставляет полный контроль над объектом.

Что остается за бортом? Включение\выключение объекта и масштаб. Я не знаю, есть ли способ изменить размер объекта, не смущая движок. Вполне возможно, что нет. Выключение объекта проходит безболезненно, а включение… да, вызывает пересчет физики, в окрестностях включенного объекта. Поэтому старайтесь не включать одновременно слишком много объектов, растяните этот процесс во времени, чтобы пользователь не заметил.

Есть закон, не влияющий на производительность, но влияющий на работоспособность: твердое тело не может быть частью твердого тела. Родительский объект будет доминировать, поэтому ребенок будет или стоять на месте относительно родителя, или вести себя непредсказуемо и неправильно.

Есть еще одна особенность Unity, не относящаяся к физике, но достойная упоминания: динамическое создание и удаление объектов через методы Instantiate/Destroy - БЕЗУМНО медленный процесс. Я боюсь себе даже представить, что там происходит под капотом во время создания объекта. Если вам нужно создавать и удалять что-то динамически - используйте фабрики и заправляйте их нужными объектами во время загрузки игры. Instantiate должен вызываться в крайнем случае - если у фабрики вдруг закончились свободные объекты, а про Destroy забудьте навсегда - все созданное должно использоваться повторно.

Применение законов на практике

(в этом разделе находится ход рассуждений при создании игры и ее особенности)

Уровень, очевидно, должен вращаться и двигаться.
Облегчим себе жизнь навечно, разместив ось вращения уровня - самолетик - в начале координат. Теперь мы сможем вычислять расстояние от точки до него, вычисляя длину вектора координат точки. Мелочь, а приятно.
Совместное движение объектов легко реализуется через иерархию объектов в Unity, потому что дети являются частью родителя. Например, описанная структура уровня логично реализуется следующим образом:
- Ось вращения
- - \ Уровень
- - - \ Сегмент 1
- - - - \ Блок 1 (Collider)
- - - - \ ...
- - - - \ Блок N
- - - \ Сегмент 2 ...
- - - \ Сегмент 3 ...
- - - \ Сегмент 4 ...
(Можно даже обойтись без объекта уровня)

Скрипт на оси получает данные с гироскопа и выставляет ей соответствующий угол… И нарушает сразу множество правил, потому что вращение передастся по иерархии на коллайдеры, что сведет физический движок с ума. Придется делать ось твердым телом и вращать ее через соответствующий метод. Но что с движением уровня? Очевидно, что ось вращения и объект уровня перемещаться не будут, каждый сегмент нужно двигать персонально, иначе мы сталкиваемся с проблемой бесконечности. Значит, твердыми телами должны быть сегменты. Но у нас уже есть твердое тело выше в иерархии и твердое тело не может быть частью твердого тела. Логичная и элегантная иерархия не подходит, все придется делать руками - и вращение, и перемещение, без использования объекта для оси вращения. Будьте готовы к такому, если у вас уникальные геймплейные фичи.

Если двигать непосредственно сегменты пришлось бы и так, то вращать их придется вынужденно. Основная сложность в том, что в физическом движке Unity нет метода «вращать объект вокруг произвольной точки» (он есть у Transform, но не искушайтесь). Есть только «вращать вокруг своего центра». Это логично, потому что вращение вокруг произвольной оси - одновременно и вращение, и движение, а это две разные операции. Но его можно имитировать. Сначала вращаем сегмент вокруг своей оси, потом вращаем координаты «своей оси» вокруг самолета. Благодаря тому, что самолет у нас в начале координат, не придется вспоминать даже школьную геометрию и лезть в википедию, в Unity уже все есть. Достаточно перевести угол поворота в кватернион и умножить его на координаты точки. Кстати, узнал я об этом прямо во время написания статьи, до этого использовалась матрица поворота.

У нас есть враги, которые отталкивают самолет в стену, надеясь убить. Есть щит, который отталкивает самолет от стен, помогая выжить. Реализовано это тривиально - есть вектор смещения, который каждый кадр прибавляется к координатам каждого сегмента и сбрасывается после этого. Любой желающий пнуть самолетик, через специальный метод, может оставить вектор своего пинка, который прибавится к этому вектору смещения.

В конечном итоге, настоящие координаты сегмента, каждый кадр, вычисляются центром управления движением уровня как-то так:
Vector3 position = segment.CachedRigidbody.position; Vector3 deltaPos = Time.deltaTime * Vector3.left * settings.Speed; segment.truePosition = Quaternion.Euler(0, 0, deltaAngle) * (position + deltaPos + movementOffset);
После всех вычислений и костылей, необходимых для работы точной стыковки сегментов при регенерации, segment.truePosition отправляется в метод MovePosition твердого тела сегмента.

Выводы

Насколько все это быстро работает? На старых флагманах - Nexus 5 и LG G2 - игра летает на 60 FPS, с еле заметной просадкой во время включения новых коллайдеров во время генерации сегмента (это неизбежно и никак не обходится) и выдвигания червяков из земли (можно нагородить какой-то ад, чтобы это обойти, но сейчас там осознанное нарушение третьего закона). 40 стабильных FPS выдает любое устройство с гироскопом, которое нам попадалось. Без знания и учета всех законов, производительность была, мягко сказать, неудовлетворительной и телефоны перегревались. Настолько, что я думал написать свой простенький специализированный движок для 2д-физики. К счастью, физика в Unity оказалось достаточно гибкой, чтобы все проблемы можно было обойти и создать уникальную игру, достаточно было лишь пары недель экспериментов.

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

Читайте документацию и экспериментируйте, даже если пользуетесь простыми и интуитивно понятными инструментами.

Rigidbodies enable your GameObjects to act under the control of physics. The Rigidbody can receive forces and torque to make your objects move in a realistic way. Any GameObject must contain a Rigidbody to be influenced by gravity, act under added forces via scripting, or interact with other objects through the NVIDIA PhysX physics engine.

Свойства

Свойство: Функция:
Mass The mass of the object (in kilograms by default).
Drag Какое воздушное сопротивление оказывается на объект пока он перемещается под воздействием этих сил. 0 означает отсутствие сопротивления, а бесконечность (infinity) тут же прекращает перемещение объекта.
Angular Drag Какое воздушное сопротивление оказывается на объект пока он вращается под воздействием силы вращения. 0 означает отсутствие сопротивления. Учтите что вы не можете остановить вращение объекта путём установки его углового сопротивления (Angular Drag) в бесконечное (infinity) положение.
Use Gravity При включении на объект действует гравитация.
Is Kinematic If enabled, the object will not be driven by the physics engine, and can only be manipulated by its Transform . This is useful for moving platforms or if you want to animate a Rigidbody that has a HingeJoint attached.
Interpolate Попробуйте одну из опций если вы замечаете тряску в перемещении своего твёрдого тела.
- None Не применено никакой интерполяции.
- Interpolate Сглаживание транформации основано на трансформации из предыдущего кадра.
- Extrapolate Сглаживание трансформации основано на приблизительной трансформации следующего кадра.
Collision Detection Используется для предотвращения проникновения быстро перемещающихся объектов сквозь другие объекты без определения столкновений.
- Discrete На всех остальных коллайдерах сцены используйте осторожное (Discreet) обнаружение столкновений. Другие коллайдеры будут использовать осторожное обнаружение столкновений при проверке на столкновения против них. Используется для нормальных столкновений (стандартное значение).
- Continuous Используйте осторожное обнаружение столкновений против динамических коллайдеров (с твёрдыми телами) и непрерывное обнаружение столкновений против статичных меш коллайдеров (без твёрдых тел). Твёрдые тела установленные как непрерывные динамические (Continuous Dynamic) будут использовать непрерывное обнаружение столкновений при тестировании на столкновения против этого твёрдого тела. Другие твёрдые тела будут использовать осторожное обнаружение столкновений. Используется для объектов, с которыми нужно столкнуться при помощи динамического обнаружения столкновений. Всё это оказывает большое влияние на физическую производительность, поэтому оставьте данное значение установленным в Discrete, если не испытываете проблем со столкновением быстро движущихся объектов.
- Continuous Dynamic Используйте непрерывное обнаружение столкновений против объектов настроенных на непрерывное и непрерывное динамическое столкновение. Также будет использоваться непрерывное обнаружение столкновений против статичных меш коллайдеров (без твёрдых тел). Для всех остальных коллайдеров будет использоваться осторожное обнаружение столкновений. Используется для быстро движущихся объектов.
Constraints Ограничения движения твёрдого тела:-
- Freeze Position Выборочно останавливает перемещение твёрдого тела по осям X, Y и Z.
- Freeze Rotation Выборочно останавливает вращение твёрдого тела по осям X, Y и Z.

Детали

Rigidbodies allow your GameObjects to act under control of the physics engine. This opens the gateway to realistic collisions, varied types of joints, and other very cool behaviors. Manipulating your GameObjects by adding forces to a Rigidbody creates a very different feel and look than adjusting the Transform Component directly. Generally, you shouldn’t manipulate the Rigidbody and the Transform of the same GameObject - only one or the other.

Наибольшее отличие между управлением трансформациями и твёрдыми телами заключается в использовании сил. Твёрдые тела могут управляться силами и вращением, трансформации же не могут. Трансформации можно перемещать и вращать, но это не то же самое, что и использование физики. Вы заметите разницу, когда решите сами испробовать это на деле. Добавление силы/вращения к твёрдому телу позволит изменить позицию и вращение компонента трансформаций (Transform) объекта. Вот почему вам нужно использовать только один из них. Изменение трансформаций при использовании физики может создать проблемы столкновениях и других вычислениях.

Rigidbodies must be explicitly added to your GameObject before they will be affected by the physics engine. You can add a Rigidbody to your selected object from Components->Physics->Rigidbody in the menu. Now your object is physics-ready; it will fall under gravity and can receive forces via scripting, but you may need to add a Collider or a Joint to get it to behave exactly how you want.

Наследование

Когда объект находится под управлением физики, он перемещается частично независимо от своих родителей. Если вы переместите одного из родителей, они потянут за собой Rigidbody потомков. Однако, твёрдые тела также будут падать вниз под воздействием силы тяжести и реагировать на события столкновений.

Скриптинг

To control your Rigidbodies, you will primarily use scripts to add forces or torque. You do this by calling AddForce() and AddTorque() on the object’s Rigidbody. Remember that you shouldn’t be directly altering the object’s Transform when you are using physics.

Анимация

For some situations, mainly creating ragdoll effects, it is neccessary to switch control of the object between animations and physics. For this purpose Rigidbodies can be marked isKinematic . While the Rigidbody is marked isKinematic , it will not be affected by collisions, forces, or any other part of the physics system. This means that you will have to control the object by manipulating the Transform component directly. Kinematic Rigidbodies will affect other objects, but they themselves will not be affected by physics. For example, Joints which are attached to Kinematic objects will constrain any other Rigidbodies attached to them and Kinematic Rigidbodies will affect other Rigidbodies through collisions.

Коллайдеры

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


Add a Collider with the Component->Physics menu. View the Component Reference page of any individual Collider for more specific information:

  • Box коллайдер - примитивная форма куба
  • - примитивная форма сферы
  • Капсульный коллайдер - примитивная форма капсулы
  • Меш коллайдер - создаёт коллайдер из меша объекта, не может сталкиваться с другим меш коллайдером
  • Wheel Collider - специально для создания автомобилей и другого транспорта
  • Terrain Collider - handles collision with Unity’s terrain system

Составные коллайдеры

Compound Colliders are combinations of primitive Colliders, collectively acting as a single Collider. They come in handy when you have a model that would be too complex or costly in terms of performance to simulate exactly, and want to simulate the collision of the shape in an optimal way using simple approximations. To create a Compound Collider, create child objects of your colliding object, then add a Collider component to each child object. This allows you to position, rotate, and scale each Collider easily and independently of one another. You can build your compound collider out of a number of primitive colliders and/or convex mesh colliders.


На картинке сверху, игровой объект Gun Model с назначенным твёрдым телом и несколькими примитивными коллайдерами в качестве потомков игрового объекта. При движении Rigidbody родителя под воздействием сил, потомки коллайдеры будут двигаться вместе с ним в том же направлении. Примитивные коллайдеры будут сталкиваться с меш коллайдерами окружающей среды, и родительский Rigidbody изменит способ своего перемещения, основываясь на силах, применённых к нему и том, как его потомки коллайдеры взаимодействуют с другими коллайдерами в сцене.

Mesh Colliders can’t normally collide with each other. If a Mesh Collider is marked as Convex , then it can collide with another Mesh Collider. The typical solution is to use primitive Colliders for any objects that move, and Mesh Colliders for static background objects.

Непрерывное обнаружение столкновений

Continuous collision detection is a feature to prevent fast-moving colliders from passing each other. This may happen when using normal (Discrete ) collision detection, when an object is one side of a collider in one frame, and already passed the collider in the next frame. To solve this, you can enable continuous collision detection on the rigidbody of the fast-moving object. Set the collision detection mode to Continuous to prevent the rigidbody from passing through any static (ie, non-rigidbody) MeshColliders. Set it to Continuous Dynamic to also prevent the rigidbody from passing through any other supported rigidbodies with collision detection mode set to Continuous or Continuous Dynamic . Непрерывное обнаружение столкновений поддерживается для Box-, Sphere- и Capsule коллайдеров. Учтите, что непрерывное обнаружение столкновений служит в качестве системы поддержки для отлова столкновений в тех случаях, когда объекты могли бы пройти сквозь друг друга, но она не предоставляет физически аккуратных результатов столкновений, поэтому вы можете всё ещё счесть нужным уменьшить значение фиксированного временного шага (fixed Time step) в инспекторе менеджера времени (TimeManager), чтобы симуляция прошла более точно если вы испытывали бы проблемы с быстро движущимися объектами.

Используйте правильный размер

Размер вашего игрового объекта играет большую роль по сравнению с массой твёрдого тела. Если вы заметили что ваше твёрдое тело ведёт себя не так, как вы изначально задумывали - медленно двигается, колеблется, или некорректно сталкивается - попробуйте настроить масштаб своего меш ассета. Стандартная единица измерения в Unity 1 юнит = 1 метр, Например, разрушающийся на части небоскрёб выглядит гораздо иначе нежели башня, собранная из игрушечных блоков, поэтому объекты разного размера должны быть смоделированы в масштабе.

If you are modeling a human make sure the model is around 2 meters tall in Unity. To check if your object has the right size compare it to the default cube. You can create a cube using GameObject > 3D Object > Cube . The cube’s height will be exactly 1 meter, so your human should be twice as tall.

If you aren’t able to adjust the mesh itself, you can change the uniform scale of a particular mesh asset by selecting it in Project View and choosing Assets->Import Settings… from the menu. Here, you can change the scale and re-import your mesh.

Если в вашей игре необходимо сделать так, чтобы создавались экземпляры объектов типа GameObject разного размера, будет вполне нормальным изменять значения масштаба осей трансформаций вашего объекта. Плохой стороной этого процесса является то, что физическая симуляция должна брать на себя основную часть работы во время создания экземпляров объекта, а это в свою очередь может сказаться на снижении производительности в вашей игре. Но это не ужасная потеря, и это не так уж эффективно как при использовании для масштабирования ваших объектов двух других опций. Также не забывайте о том, что неравномерное масштабирование может привести к непредсказуемому поведению при использовании наследования. Поэтому, в таких случаях лучше в своём пакете трёхмерного моделирования изначально создавать свои объекты в правильном масштабе.

  • The relative Mass of two Rigidbodies determines how they react when they collide with each other.
  • Making one Rigidbody have greater Mass than another does not make it fall faster in free fall. Use Drag for that.
  • A low Drag value makes an object seem heavy. A high one makes it seem light. Typical values for Drag are between .001 (solid block of metal) and 10 (feather).
  • Если вы напрямую управляете компонентом Transform своего объекта, но хотите воспользоваться физикой, тогда назначьте ему твёрдое тело (Rigidbody) и сделайте его кинематическим (Kinematic).
  • Если вы перемещаете игровой объект через его компонент преобразования (Transform), но хотите получать сообщения о столкновениях/переключениях, вы должны назначить перемещающемуся объекту компонент твёрдое тело (Rigidbody).
  • Вы не можете остановить вращение объекта путём установки его углового сопротивления (Angular Drag) в бесконечное (infinity) положение.

Лабораторная работа № 2

Тема: Основы взаимодействия (столкновения) между объектами. Применение скриптов на языке C #.

Цели работы:

– знакомство с особенностями разработки и внедрения скриптов на языке программирования C#;

– изучение основ взаимодействия трехмерных моделей с использованием функций и применением скриптов Unity3D;

– освоение приемов организации взаимодействия объектов за счет столкновений (collisions) между 3d-объектами.

Практическая часть

1. Запускаем Unity 3 d и открываем проект предыдущего занятия (по умолчанию проект откроется автоматически. Если этого не произошло, выбираем проект в окне File  Open Project, рис. 1).

Рис. 1. Окно выбора проектов для открытия в unity 3 d

2. Повернем объект куб на ребро, относительно плоскости.

(Для взаимодействия объектов между собой, данное действие не обязательно, но для того чтобы лучше увидеть картину данного взаимодействия и возможности трехмерного моделирования среды – необходимо нажать клавишу «Rotate» (вращение). Вокруг выбранного объекта появится своеобразная сфера. Кликнув кнопкой мыши одну из ее сторон и переместив курсор, вы увидите как объект начнет вращаться рис. 2).

Рис. 2. Вращение (поворот) объектов в unity 3 d

3. Добавляем к проекту второй геометрический объект (куб) (GameObject Create Other  Cube, рис. 3):

Рис. 3. Добавление к проекту геометрического объекта «куб» .

4. Изменяем размер и месторасположение вновь созданного «куба» .

Вновь созданный куб необходимо растянуть в форме «прямоугольника» (он будет представлять собой своеобразную стену (препятствие). Для этого воспользуемся клавишой «Scale»(масштаб). Выбрав геометрический объект, мы увидим три квадратных куба по разным сторонам объекта, позволяющим изменять его размерность в направлении соответсвующему цвету по осям координат. Центральный куб, позволяет изменять размер синхронно по всем координатам.

Рис. 4. Изменение размеров тела в unity 3 d

Изменив размеры куба, разместите объекты примерно nfr , как показано на рисунке ниже:

Рис. 5.

Переключившись со сцены на режим просмотра «Game», вы увидите следующее:

Рис. 6.

5. Переименуем созданные объекты в окне «иерархии»

Нам необходимо отличать объекты по их названию. Давайте переименуем объект «Plane» (Плоскость) в объект «Zemlya», а куб, представляющий стену (препятствие) в объект «Stena». Для этого нам необходимо выбрать объект в окне «иерархии»  «Rename», после чего задать новое имя объекта (в среде Unity3D разрешается ввод русскоязычных символов, однако при работе со скриптами необходимо использовать «транслит»).

Рис. 7. Переименование объектов

6. Создаем скрипт на языке C #

Для того, чтобы заставить взаимодействовать между собой имеющиеся в сцене трехмерные модели, создадим скрипт на языке программирования C# и назовем его «Dialog». Для этого в окне «Project» нажимаем «Create  C# Script».

Рис. 8 .

Двойным щелчком откроем скрипт в редакторе скриптов.

Рис. 9. Встроенный редактор скриптов Unity 3 D – “ MonoDevelop - Unity

При создании нового скрипта в Unity3D автоматически создается каркас для C# скрипта. Удаляем из созданного скрипта “Dialog”все содержимое. Рассмотрим следующий код:

using UnityEngine;

using System.Collections;

public class Dialog: MonoBehaviour {

// Метод-функция, вызываемая при столкновении объектов

void OnCollisionEnter() {

Debug.Log("Hit Something"); // Передаем сообщение в консоль Unity

Здесь метод «OnCollisionEnter» определяет столкновение объекта с другими объектами. А статический метод «Log» класса «Debug» пишет сообщение в консоль Unity.

После сохранения скрипта, добавляем его в качестве компонента для падающего куба.

Для этого необходимо выбрать объект «куб» в окне «иерархии» и перетащить на данный объект (куб) вновь созданный нами скрипт «Dialog». При этом в окне «Inspector» нашего куба, отобразится внизу вновь добавленный скрипт (рис. 10).

Рис . 10. Добавление скриптов к объектам среды Unity 3 D

Теперь если Вы нажмете Play, в момент когда куб коснется «земли» в консоли среды Unity3D появится соответствующее сообщение «Hit Something» (рис. 11). Открыть окно консоли можно комбинацией «ctrl + shift + С» или в окне «Window  Console”. Последнее консольное сообщение отображается в статус в баре проекта (внизу окна).

Рис. 11. Просмотр сообщений консоли

Причем сообщение будет выдаваться при каждом столкновении. Для того, чтобы понять с каким именно объектом столкнулся наш куб, мы будем брать значение параметра класса «Collision», которое будет принимать метод «OnCollisionEnter:».

Открываем редактор скрипта и вставляем в него следующий код.

// Теперь метод принимает объект класса Collision, с которым происходит столкновение

void OnCollisionEnter(Collision myCollision) {

// определение столкновения с двумя разноименными объектами

if (myCollision.gameObject.name == "Zemlya") {

// Обращаемся к имени объекта с которым столкнулись

Debug.Log("Stolknulsya s Zemlei");

else if (myCollision.gameObject.name == "Stena") {

Debug.Log("Stolknulsya s Stenoi ");

После открытия консоли мы увидим, с какими именно объектами столкнулся «куб» (рис. 12).

Рис. 12. Объекты, с которыми столкнулся объект «куб»

Тем самым Unity3D позволяет нам оценить возможности взаимодействия объектов внутри среды.

Контрольное задание: Постарайтесь самостоятельно модернизировать скрипт Dialog на языке C #, так чтобы при столкновении куба со стеной, происходило разрушение стены. Составить план-отчет о проделанной работе (3 балла).