Как сделать плавное перемещение объекта unity

Обновлено: 07.07.2024

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

Каковы плюсы/минусы каждого из этих методов и какие предпочтительны для RPG от первого лица?

Что-то, что я заметил, - то, что приложенная сила кажется мимным колесным транспортным средствам, в то время как положение изменяет мимическую ходьбу/бег.

Твердые тела и скорости/Физика

Единственный раз, когда я лично использовал систему hardbodys, был при реализации моих собственных boids (поведение флокирования), так как вам нужно вычислить несколько отдельных векторов и применить их все к модулю.

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

Эта конкретная функция является оберткой вокруг Rigidbody.AddForce я верю.

    Хорошо, если вы хотите реалистичные физические реакции

Немного громоздко использовать, если все, чего вы пытаетесь достичь, это переместить объект из точки А в точку Б.

    Иногда неправильная настройка, установленная где-то слишком высоко (например: Масса> 10000000), может привести к действительно странным ошибкам в поведении, которые могут быть довольно трудной задачей, чтобы ограничить и смягчить.

Примечания: твердые тела при столкновении с другим твердым телом будут отскакивать друг от друга в зависимости от настроек физики.

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

И Rigidbody.AddForce в основном такой же, как указано выше, за исключением того, что вы сами вычисляете вектор.

Так, например, чтобы получить вектор к цели, которую вы бы сделали

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

Ведь гораздо проще заставить вещи вести себя так, как вы хотите, если, конечно, вы не стремитесь к физическому реализму!

Интерполяция позиции единиц

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

Вы можете использовать эту технику, скажем, в игре Pokemon, вы не останавливаетесь в Pokemon и ждете, когда пепел прекратит занос или ударит стену и неудержимо подпрыгнет назад.

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

Во время создания игры, вы должны разместить много различных объектов в вашем игровом мире.

Перемещение, вращение и масштабирование

Используйте инструменты трансформаций в панели инструментов для перемещения, вращения и масштабирования отдельных объектов. Каждый инструмент имеет соответствующее гизмо, которое появляется вокруг выделенного игрового объекта в окне Scene. Вы можете использовать мышь и манипулировать любой осью гизмо для изменения компонента Transform игрового объекта, или Вы можете ввести значения непосредственно в числовые поля компонента в инспекторе. Каждый из трех режимов может быть выбран горячими клавишами - W для перемещения, E для вращения и R для масштабирования.


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

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

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

Обратите внимание, что в режиме 2D, ось Z не может быть изменена в сцене с помощью гизмо. Тем не менее, бывает полезно использовать ось Z в некоторых методах скриптов для других целей; в таких случаях вы все еще можете установить ось Z в инспекторе.

Для получения дополнительной информации о трансформировании игровых объектов, см. страницу компонента Transform.

Переключатели отображения гизмо

Переключатели отображения гизмо( Gizmo Display Toggles ) используются для определения положения любого из них.

Переключатели отображения гизмо

  • Center установит гизмо в центре отображаемых границ объекта.
  • Pivot установит гизмо в позицию пивота Mesh’а.
  • Local установит гизмо для вращения объекта в локальных осях.
  • Global установит ориентацию гизмо по мировым осям.

Привязка к сетке

При перетаскивании любой оси гизмо инструмента перемещения, Вы можете зажать клавишу Control ( Command в Mac) для изменения значения с использованием привязки, определенной в Snap Settings .

Вы можете изменить шаг, который используется для привязки, выбрав в меню Edit->Snap Settings…

Настройки привязки к сетке в окна Scene.

Настройки привязки к сетке в окна Scene.

Привязка к поверхности

При перетаскивании, с использованием центра инструмента перемещения, вы можете зажать Shift и Control ( Command в Mac) для привязки объекта к пересечению с любым коллайдером . Это позволяет очень быстро и точно расставлять объекты.

Вращение взгляда

При использовании инструмента вращения, вы можете зажать клавиши Shift и Control ( Command в Mac) для направления взгляда объекта на точку поверхности любого коллайдера . Это позволяет легко ориентировать объекты относительно друг друга.

Вершинная привязка

Вы можете строить миры еще быстрее с возможностью, которая называется “вершинная привязка”( Vertex snapping ). Эта возможность очень проста, но является очень мощным инструментом в Unity. Она позволяет вам взять один меш за любую вершину и с помощью мышки расположить его в такое же положение вершины другого меша, который вы выберите.

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

Использовать вершинную привязку в Unity достаточно просто. Просто следуйте этим шагам:

  1. Rigidbody.AddForce(15 * Time.deltaTime, 0, 0, ForceMode.VelocityChange);

Это будет заставить объект ускоряться, чтобы он не двигался с постоянной скоростью (это из-за второго закона Ньютона, сила = масса * ускорение). Также, если у вас есть другая сила, движущаяся в противоположном направлении, эта сила может быть отменена, и объект вообще не будет двигаться.

  1. Transform.transform.position = playerTransform.position + cameraOffset;

Это телепортирует объект. Без плавного перехода, без взаимодействия с какими-либо силами, уже находящимися в игре, просто мгновенное изменение позиции.

RigidBodies и Velocities / Physics

Единственный раз, когда я лично использовал систему hardbodys, был при реализации моих собственных boids (поведение флокирования), как вам нужно вычислить несколько отдельных векторов и применить их все к единице.

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

Эта конкретная функция является оберткой вокруг Rigidbody.AddForce Я считаю.

  • Хорошо, если вам нужны реалистичные физические реакции

Немного неудобно использовать, если все, что вы пытаетесь достичь, это перемещение объекта из точки А в точку Б.

  • Иногда неправильная настройка, установленная где-то слишком высоко (например, Масса> 10000000), может привести к действительно странным ошибкам в поведении, которые могут быть довольно трудной задачей, чтобы ограничить и смягчить.

Примечания: Твердые тела при столкновении с другим твердым телом будут отскакивать друг от друга в зависимости от настроек физики.

Они также подвержены влиянию гравитации. По сути, они пытаются имитировать объекты реальной жизни, но иногда бывает трудно приручить объекты и заставить их делать именно то, что вы хотите.

И Rigidbody.AddForce в основном то же, что и выше, за исключением того, что вы сами вычисляете вектор.

Так, например, чтобы получить вектор к цели, вы бы сделали

Если вы не планируете использовать какую-либо основную физическую механику в своей игре, я бы предложил двигаться, интерполируя положение объектов. . [+1122]

Поскольку гораздо проще заставить вещи вести себя так, как вы хотите, если, конечно, вы не стремитесь к физическому реализму!

Интерполяция позиции юнитов

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

Вы будете использовать эту технику, скажем, в игре Pokemon, вы не останавливаетесь в Pokemon и не ждете, пока пепел прекратит скользить или ударит стену и неудержимо подпрыгнет назад.

Хоть я здесь и разбираю в т.ч. нефизическое движение, я настоятельно рекомендую его НЕ использовать. И наоборот, я настоятельно рекомендую использовать ФИЗИЧЕСКОЕ движение.

Так делать - хороший тон. Удобнее менять все настройки клавиш из одного внешнего места, а не из многих документов с кодом.

Я буду использовать здесь 2 термина: "телепортация" и "плавное движение". В моем понимании:

Плавное движение - перерасчет позиции обьекта в рамках физики или паралельно физике на вызове FixedUpdate() .

  • Плавное движение - исключительно физическое движение
  • Телепортация - изменение позиции вручную или использование .Translate() метода.

Двигать обьекты в игровых движках можно следующими способами:

используя физический движок (движение обусловленное физической моделью игрового движка)

движение НЕфизическое. Неправильный подход -- подход телепортации на каждом кадре. (в Update() )

движение НЕфизическое. Правильный подход -- плавное передвижение обьекта между кадрами (паралельно каждому просчету физики)

На практике метод передвижения подбирается под конкретного персонажа[персонажа - не буквально. Это может быть и автомобиль]. В одном случае лучше будет физическое перемещение. В другом - нефизическое. В третьем случае будет лучше всего CharacterController. Понимание что лучше в каком случае прийдет с практикой.

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

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

Нужно запомнить всего одно правило: Двигать/поворачивать через присвоение transform.position / transform.rotation нельзя. Это порождает проблемы. Прям в любом случае это вам вылезет боком.

Все для чего нужно это - телепортация в другое место обьекта, но никак не его движение.

( на примере обьекта-шара )

Есть Update() - этот метод вызывается на каждой прорисовке кадра. Time.DeltaTime - это расчетное время между прорисовкой двух кадров. Если FPS проседает на компьютере, то этот параметр возрастает пропорционально проседанию.

Есть FixedUpdate() - это метод который вызывается при перепросчете физики. Time.FixedDeltaTime , как вы уже догадались, это время между вызовами FixedUpdate() . Оно может изменятся вручную через настройки, но упирается в физические возможности машины на которой игра будет запущена.

Если обьект не обладает физическими свойствами (не имеет RigitBody) эти параметры и методы можно использовать для НЕфизического передвижения.

Например поворот камеры.

Или крутящийся куб на небосводе.

Или движущийся изображение поезда где-то далеко, к которому нельзя подойти близко. Физика такому обьекту просто ни к чему -- это просто лишняя трата ресурсов

но, даже, в этом случае предпочтительно использовать Transform.Translate , но про это позже

Мы не получим дергающуюся картинку при проседании кадров если сделаем НЕФИЗИЧЕСКОЕ движение правильно:

ВАЖНО: . ПРИМЕР ВРЕДНОГО КОДА. Не делайте так!

мы присваиваем в новую позицию:

  1. старую позицию
  2. направление движения
  3. скорость передвижения умноженную на Time.deltaTime .

Поэтому, даже, если, у нас было 60 кадров и случилось проседание до 10 кадров -- скорость вращения/движения обьекта не будет изменятся. Ведь, мы ее учитываем вместе с проседанием кадров.

Допустим мы двигаем обьект через rb.Velocity или через AddForce() , то это физическое движение обьекта. То есть она может изменятся во времени сама под действием неких физических законов. Например, мы задумали сделать прыжок персонажа:

Мы разово задаем вектор скачка. Только 1 долю секунды. Но он будет изменятся во времени автоматически равномерно уменьшаясь под силой тяжения. Пока не станет нулевым (верхняя точка прыжка), а потом не пойдет в минус по Y (падение), а потом не упадет на землю и не отскочит от нее (снова плюс по Y ) и так до полной остановки физической скорости обьекта на земле.

Допустим, мы двигаем изменением transform.Positon нашего плеера вперед по нажатию клавиши "пробел". В какой-то момент мы перестаем нажимать кнопку -- движение резко остановится и замрет. Это потому, что наше движение НЕ является физическим. Допустим мы подойдем к стенке и попробуем пройти на нее. Т.к. мы занимаемся телепортацией обьекта, то наш персонаж сначала дойдет до стенки, а потом телепортируется ВНУТРЬ нее, после чего Collider ее вытолкнет из себя. Как глубоко телепортируется внутрь зависит лишь от того, на какое расстояние мы телепортируем нашего персонажа за кадр. То есть это "Bad Practice" так реализовать перемещение персонажа.

Но в то же время есть и допустимое не-физическое перемещение. Это использование метода Transform.Translate() . Это уже НЕ телепортация обьекта. Это - плавное перемещение обьекта (просто без учета физики). Но это не освобождает нас от использования deltaTime/fixedDeltaTime, как в примере оф.документации.

Если девайс с игрой сильно загружен, вызов методов Update() / FixedUpdate() тоже может просесть в скорости. И если в физике это учтено и без нас, то сейчас мы делаем НЕ физическое движение и именно по-этому это нужно учитывать добавлением даного множителя.

Но и без использования даного множителя у нас не появится проблем с провалами сквозь стены. Это просто фикс скорости.

Если в прошлом примере мы двигали шар, то было допустимо его толкать используя физ.модель. То есть мы использовали AddForce() для этих целей.

Допустим у нас персонаж - человек, а не шар. Давай создадим вместо человека его подобие - высокий куб 0.8х1.8х0.3 и попробуем нацепить на него наш скрипт движения шара. Выйдет следующее:

То есть когда мы пытаемся подвигать, наш персонаж падает (мы ж его толкаем, логично!). Когда он упал - он не может двигатся из-за силы трения. Зато мы можем двигать его в прыжке. :)

Давайте актуализируем этот код под даного персонажа. Мы заменим физический толчек обьекта на не-физическое, но ПЛАВНОЕ перемещение обьекта в пространстве:

С этим кодом мы получим такой результат:

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

Да, можно подобное реализовать и на физике.

Наша прошлая версия скрипта имела несколько недостатков. А именно:

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

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

Давайте поместим на наш куб CapsuleCollider (минимальное торможение из-за силы трения) и заблочим в rigitBody rotateX и rotateZ (что б наш персонаж не падал на бок).

А потом нацепим на него вот этот скрипт:

Вы видите эту плавность, как будто человек бежит, останавливается, бежит в другую сторону? Красота!

А теперь вернитесь к прошлой гифке и присмотритесь. Движение совсем не такое :) Там как буд-то рукой двигают шахматную фигуру по доске.

Ну и описанные выше баги поведения были пофикшены с такой реализацией.

Можно добавить еще физический материал нашему персонажу и откоректировать его поведение.

Вообще улучшать реализацию можно до бесконечности. Но, думаю, основные проблемы СПОСОБОВ ПЕРЕДВИЖЕНИЯ с которыми вы столкнетесь, я затронул :)

Такая физика чаще используется для неживих персонажей. Например для автомобилей.

Одним из моих любимейших примеров нестандартной физики движения является игра Ori and the Blind Forest

Такое перемещение/такие прыжки невозможно сделать на основе физики. Это делалось через нефизическое перемещение + костыли для получения нужных эфектов.

Сначала разрабатываются концепты движения. Они делаются в любом видеоредакторе с примитивными фигурами. Вот пример (если станет недоступным искать можно по Ori and the blind forest Enemy Concepts ) :

Обратите внимание на то, то здесь прорисовано не только перемещение обьекта, но и его вытягивания/сжатия. Изменения формы во время любого взаимодействия с внешним миром. В т.ч. выстрелы так же влияют на форму. А так же что указываются радиусы опознавания главного героя каждым отдельным врагом.

Когда вся концепция всех движений готова - делается нефизическое движение обьекта и приближается разного рода костылями к концепту игры.

Костыли для каждого персонажа/врага свои собственные. Это делается что бы каждый из них обладал своей уникальной физикой. Сделать это на общей физике навряд ли возможно.

ДАЖЕ если вы реализовали физическое передвижение вашего персонажа, все равно может случится такое, что просчет CollisionDetect может проходить с ошибками. Это редкость, но такое бывает иногда.

Для таких случаев есть настройки отвечающие за обработку CollisionDetect в настройках самого RigitBody.


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

Перемещение за время в Unity

Очень частый вопрос, который я встречал на форумах – “Как переместить объект в Unity за время?”
Без какого-либо твинера, это можно сделать рассчитывая дельту для интерполяции по времени.
Например, вот так:

Скрипт перемещает объект в центр контейнера за 3 секунды. Легко можно адаптировать его, чтобы поворачивать за время, перемещать объект за время в 3d, и т.д.

Пишет статьи о разработке игр. Не инди, — работает рендеринг-программистом в крупной ААА студии в Санкт-Петербурге. Большой поклонник игр Naughty Dog.

А не проще yield return new WaitForSecond ?

Что значит проще? Двигать нужно каждый фрейм, зачем возвращать WaitForSeconds, если не нужно ждать нисколько?

Вы не правильно задачу, наверное поняли) подправлю в статье. Нужно не через 3 секунды передвинуть объект, а в течении 3 секунд линейно передвигать

если в Update написать это:
rectTransform.anchoredPosition = Vector2.Lerp(startPosition, targetPosition, delta/3);
разве это не даст такой же результат?

Смотря как вы считаете последний параметр, если от времени как я, то сработает, если от позиции движение будет неплавное из-за разных deltaTime. И для Update ещё флажок дополнительный нужен “двигаюсь/не двигаюсь”, хотя можно 2 вектора сравнивать..

Читайте также: