Как сделать чтобы объект двигался к другому объекту в unity

Обновлено: 07.07.2024

В функции FixedUpdate () мы работаем с физикой, то есть с основной частью скрипта. Нам понадобится сила толкания, то есть работа с физикой. А как мы знаем, что базовая физика движка — Rigidbody. Её подтип AddForce дает сил толкания на объект в нужном векторе. Теперь давайте посчитаем наш вектор движения тела. Отнимем позицию объекта, к которому движемся позицию игрока, нормализуем вектор и умножим на скорость:

Спасибо за внимание, с вами был Максим Епихин!

7 комментариев

Как я понимаю таким образом можно создать силу тяготения к сферическому объекту, чтобы воссоздать силу тяготения к земному шару?

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

Как я могу плавно перемещать объекты в их целевую позицию? Я пробовал возиться с Lerp и Smoothdamp , но оба они, кажется, дают странные результаты (билеты ходят повсюду. Вот соответствующий код, с которым я работаю:

DestroyedIndex - это индекс объекта, который был уничтожен (так что мы перемещаем объекты только после него в списке) Up - это флаг, который активирует SlideRail (), когда для него установлено значение false, метод завершается, а следующий метод продолжается.

Затем в методе Update () вызывается SlideRail:

Я чувствую, что смогу сделать это с помощью Translate , но мне что-то не хватает.

Ответы 3

При таком движении обычно следует включать Time.deltaTime (как фактор при трансляции), чтобы привязать движение к частоте кадров. [Обновлено: переведите это предложение на независимое от частоты кадров, это я имел в виду.]

Теперь, что касается фактической точки, похоже, что вы выполняете движение всего за один кадр и, таким образом, получаете этот прыжок (вы всегда устанавливаете up в значение false в конце SlideRail ).

Это лучшее предложение, которое я когда-либо делал, хотя я все еще не уверен, как его реализовать. Чтобы проверить, я сделал случайный квадратный префаб и поместил предложенный вами код в функцию обновления, чтобы посмотреть, будет ли он работать. Конечно же, так оно и было. Это потому, что, как я полагаю, Update () проверяет условие if каждый кадр. Но SlideRail () вызывается во время обновления, но условия не проверяются в каждом кадре таким же образом. Правильно ли я так думаю? И если да, то как я могу проверить это условие if для каждого элемента в списке? Я поместил условие в цикл while, но это привело к сбою Unity.

you should usually include Time.deltaTime (as a factor in translate) to bind the movement to the frame rate. На самом деле отвяжите его. Вы хотите, чтобы движение было независимая от частоты кадров, поэтому хорошо использовать deltaTime.

Draco18s no longer trusts SE

@ Draco18s Да, я это имел в виду.

@Matt Ну, Update уже является циклом, поэтому вам не нужен дополнительный цикл (за исключением, конечно, выполнения его для всех элементов). В Update вы проверяете if(up == true) , теперь, в конце SlideRail , вы устанавливаете для него значение false без каких-либо условий, поэтому с предоставленным кодом вы войдете в SlideRail только один раз. Вместо этого вы хотите поместить up = false в условие, которое спрашивает, достиг ли объект (или потенциально все, для этого нужен какой-то счетчик или что-то подобное), и просто установите up = false тогда.

Другой вариант - использовать сопрограмму вместо обновления, как в ответе ATHellboy. Этому нужен цикл while, чтобы делать то же самое, что и update.

Используя iTween, его можно упростить до однострочного вызова:

Кажется, это только для 3D-объектов? Если да, есть ли способ использовать это расширение / плагин для 2D-игры с использованием 2D-игровых объектов?


Предположу, что теперь вы знаете о корутинах достаточно много, поэтому перейдем к примерам их использования. Будут рассмотрены примеры:

  • движение к цели;
  • простой вариант искусственного интеллекта;
  • инициализация процедуры;
  • реализация игрового цикла.

Пример 1. Движение к цели

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

Нужно пояснить тут несколько вещей.

  1. мы используем логическую переменную _moved. Когда запускается корутина, мы устанавливаем ей значение true. Это значение каждый раз проверяется при срабатывании триггера. В противном случае, корутина будет запускаться при каждом входе персонажа в триггерную зону.
  2. мы не использовали WaitForSeconds в этом примере, потому что наш объект _mover должен перемещаться без каких-либо пауз, пока не достигнет своей цели. Вместо этого мы используем yield return null. Если в вашей корутине есть раздел, который вам не нужен, применяйте yield return null.
  3. мы используем цикл while с условием. Условие срабатывает, пока _mover находится достаточно далеко от цели (_target), чтобы продолжать двигаться. Как только _mover окажется близко, сопрограмма завершится. Мы реализуем перемещение объекта с помощью метода Vector3.MoveTowards.

Пример 2. Простой вариант ИИ

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

public class SimpleAI : MonoBehaviour
[SerializeField] private Transform[] _waypoints;
[SerializeField] private float _minWaitTime = 1f;
[SerializeField] private float _maxWaitTime = 5f;
[SerializeField] private float _speed = 2f;
private IEnumerator Start()
while(true)
yield return StartCoroutine(Move());
yield return StartCoroutine(Wait());
>
>
private IEnumerator Move()
Vector3 destination = _waypoints[Random.Range(0, _waypoints.Length)].position;
while(Vector3.Distance(transform.position, destination) > float.Epsilon)
Transform.postion = Vector3.MoveTowards(transform.position, destination, _speed * Time.deltaTime);
yield return null;
>
>
private IEnumerator Wait()
yield return new WaitForSeconds(Random.Range(_minWaitTime, _maxWaitTime);
>
>

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

В методе start() мы реализуем бесконечный цикл. Также обратите внимание, что в данном случае мы ждем завершения сопрограмм. Я не буду обсуждать, почему бесконечный цикл работает с IEnumerators, но, возможно, стоит подумать о методе update. Он тоже будет постоянно повторять то, что прописано в его коде.

Теперь по пунктам объясню, что здесь происходит.

  • Unity запускает сопрограмму со стартом сцены.
  • создается бесконечный цикл while(true). Это позволяет повторяться нашим сопрограммам вечно.
  • вызывается функция yield return StartCoroutine (Move ()). Сопрограмма будет выполнять этот метод до тех пор, пока объект не прибудет в путевую точку, случайно выбранную из массива _waypoints.
  • yield return StartCoroutine (Wait ()) вызывается сразу после завершения перемещения. ИИ будет просто оставаться в точке маршрута в течение случайного количества времени.
  • после того, как ожидание закончилось, мы повторяем нашу сопрограмму, начиная с Move снова. Это будет продолжаться вечно. Еще раз, причина этого повторения цикл while(true).

Пример 3. Инициализация процедуры

Одна вещь, которая хороша в циклических сопрограммах, заключается в том, что вы можете инициализировать их перед входом в цикл. Это здорово, потому что вы можете сохранить весь код, содержащийся внутри одного IEnumerator и легко работать с ним.
Давайте возьмем наш предыдущий пример т будем воспроизводить разную анимацию в зависимости от того, что делает ИИ.

Здесь вы можете видеть, что мы добавили ссылку на аниматор. Когда наш ИИ изменяет состояние, он теперь также меняет анимацию в зависимости от того, движется ли он или ждет.
Обратите внимание, что в методе Move мы меняем анимацию перед циклом while, чтобы она выполнялась только один раз за изменение состояния. Затем мы меняем анимацию на ожидание, бездействие, когда ИИ изменяется на Wait IEnumerator.

Пример 4 Игровой Цикл

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

  • Инициализация – настройка игровой сцены. Это может быть спаунинг всех объектов в нужных местах и все остальное, что требуется для запуска.
  • Игровой процесс - это будет просто игра, работающая нормально. Игра здесь обычно приостанавливается и проверяет условие на окончание, чтобы очистить сцену.
  • Конец игры – что происходит, когда игра заканчивается. Это может быть отключение управления персонажами или объектами и показ меню или еще что-то.

public class GameLoop : MonoBehaviour
[SerializeField] private Player[] _players;
[SerializeField] private GameObject _gameOverMenu;
private IEnumerator Start()
Initialize();
yield return StartCoroutine(GameOver());
>
private void Initialize()
Foreach(Player player in _players)
player spawn = Instantiate(player);
float x = Random.Range(0, 1000f);
float y = Random.Range(0, 1000f);
float z = Random.Range(0, 1000f);
spawn.transform.position = new Vector3(x, y, z);
>
>
private IEnumerator GameOver()
while(true)
Foreach(Player player in _players)
If(player.IsDead)
_gameOverMenu.setActive(true);
yield break;
>
> yield return null;
>
>
>

В этом примере мы создаем игроков, а затем просто перебираем их всех, пока один из них не умрет. Как только это произойдет, мы покажем меню game over.
Мы выходим из сопрограммы через break, если один из игроков умер. Пока этого не происходит, наша корутина продолжает цикл.
Этот пример является базовым, но он показывает, что вы можете легко управлять потоком игры с помощью нескольких сопрограмм.

По книге Lucas Faustino “Unity5 Coroutines”.
Другие части статьи
1 часть. Что такое корутины.
2 часть. Coroutine vs Update.
3 часть. Примеры использования сопрограмм.
4 часть. Финал.


Управление персонажем в 3D

  • Plane — это территория, по которой будет перемещаться наш персонаж.
  • Sphere — объект персонажа.



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

  • movementSpeed — скорость передвижения.
  • turningSpeed — скорость поворота.
  • vertical — движение по вертикали.
  • horizontal — движение по горизонтали.



Управление персонажем в 2D

Для работы потребуется два спрайта — для персонажа и для земли. Перемещаем персонажа и землю на сцену. С помощью клавиш CTRL+D дублируем землю.



Создаем пустой объект. И в него перемещаем все объекты земли.



На пустой объект добавляем коллайдер. Add Component - Physics2D - BoxCollider. И с помощью инструмента Edit Collider настраиваем размеры.



Персонажу добавляем физику и коллайдер и так же настраиваем границы коллайдера.

  • maxSpeed — скорость персонажа
  • flipRight — направление спрайта персонажа.

Теперь отражаем наш спрайт в зависимости от того, в какую сторону движется наш персонаж. Условием If проверяем. Если нажали клавишу для перемещения вправо, а персонаж направлен влево, то поворачиваем спрайт вправо. С else if — обратная ситуация.

И теперь сама функция Flip. В ней мы отражаем спрайт по оси X и задаем размеры, чтобы из-за отражения не исказились размеры спрайта.

В школе “Пиксель” мы ведем полный курс обучения Unity 3D для детей и подростков. Изучая наши уроки Си Шарп, вы быстро освоите азы программирования и научитесь создавать свои игры.

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