0 added
0 removed
Original
2026-01-01
Modified
2026-02-21
1
<p><a>#статьи</a></p>
1
<p><a>#статьи</a></p>
2
<ul><li>22 янв 2024</li>
2
<ul><li>22 янв 2024</li>
3
<li>0</li>
3
<li>0</li>
4
</ul><p>Как двигать объекты в Unity с помощью физики и без неё.</p>
4
</ul><p>Как двигать объекты в Unity с помощью физики и без неё.</p>
5
<p>Иллюстрация: Abik Peravan / Axel Ruffini / Anastasiia Ornarin / Vinay Tryambake / Annie для Skillbox Media</p>
5
<p>Иллюстрация: Abik Peravan / Axel Ruffini / Anastasiia Ornarin / Vinay Tryambake / Annie для Skillbox Media</p>
6
<p><a>Делает игры</a>на Unity, пишет статьи про геймдев. Всё ещё ждёт, когда выпустят No One Lives Forever 3.</p>
6
<p><a>Делает игры</a>на Unity, пишет статьи про геймдев. Всё ещё ждёт, когда выпустят No One Lives Forever 3.</p>
7
<p>В основе многих видеоигр лежит перемещение объектов в пространстве. Есть несколько способов, как заставить объект двигаться; каждый из них выгодно применять в одной ситуации и невыгодно - в другой.</p>
7
<p>В основе многих видеоигр лежит перемещение объектов в пространстве. Есть несколько способов, как заставить объект двигаться; каждый из них выгодно применять в одной ситуации и невыгодно - в другой.</p>
8
<p>Условно, передвижение главного героя в шутере с видом от первого лица может быть построено на одном принципе, но в тот момент, когда он подпрыгнет, начнёт действовать другой. Если персонаж бросит гранату во врага, то за полёт гранаты будет отвечать уже третий принцип. А когда он запрыгнет в телепорт и окажется в другой точке карты, то сработает уже четвёртый.</p>
8
<p>Условно, передвижение главного героя в шутере с видом от первого лица может быть построено на одном принципе, но в тот момент, когда он подпрыгнет, начнёт действовать другой. Если персонаж бросит гранату во врага, то за полёт гранаты будет отвечать уже третий принцип. А когда он запрыгнет в телепорт и окажется в другой точке карты, то сработает уже четвёртый.</p>
9
<p>В этом тексте мы рассмотрим основы того, как можно заставить объект переместиться в пространстве в Unity. Предполагается, что вы уже знаете, как установить Unity и разобраться в его интерфейсе. Если нет, то прочитайте материал по ссылке ниже.</p>
9
<p>В этом тексте мы рассмотрим основы того, как можно заставить объект переместиться в пространстве в Unity. Предполагается, что вы уже знаете, как установить Unity и разобраться в его интерфейсе. Если нет, то прочитайте материал по ссылке ниже.</p>
10
<p>У нас есть<strong>телеграм-канал "Чекпоинт"</strong>, где мы собираем полезные материалы для начинающих разработчиков. Там и статьи о геймдизайне, и гайды по софту, и даже интервью с теми, кто уже выпустил собственную игру.<a>Подписывайтесь</a>!</p>
10
<p>У нас есть<strong>телеграм-канал "Чекпоинт"</strong>, где мы собираем полезные материалы для начинающих разработчиков. Там и статьи о геймдизайне, и гайды по софту, и даже интервью с теми, кто уже выпустил собственную игру.<a>Подписывайтесь</a>!</p>
11
<p>Начнём с самого простого способа. Для этого создадим объект (пусть это будет куб), который мы будем передвигать, и добавим к нему скрипт на языке C#, на котором мы и будем писать логику перемещения.</p>
11
<p>Начнём с самого простого способа. Для этого создадим объект (пусть это будет куб), который мы будем передвигать, и добавим к нему скрипт на языке C#, на котором мы и будем писать логику перемещения.</p>
12
<p>Чтобы создать объект, нажимаем правой кнопкой мыши в окне<strong>Inspector</strong>, выбираем пункт<strong>3D</strong><strong>Object</strong>, далее -<strong>Cube</strong>. В окне<strong>Scene</strong>появится белый куб.</p>
12
<p>Чтобы создать объект, нажимаем правой кнопкой мыши в окне<strong>Inspector</strong>, выбираем пункт<strong>3D</strong><strong>Object</strong>, далее -<strong>Cube</strong>. В окне<strong>Scene</strong>появится белый куб.</p>
13
<em>Скриншот: Skillbox Media</em><p>Затем мы нажимаем правой кнопкой мыши на поле в окне<strong>Project</strong>и выбираем пункт<strong>Create</strong>, а следом -<strong>C# Script</strong>. Так мы получим файл, в котором будем писать программу для передвижения нашего куба. Назовём этот файл Moving.</p>
13
<em>Скриншот: Skillbox Media</em><p>Затем мы нажимаем правой кнопкой мыши на поле в окне<strong>Project</strong>и выбираем пункт<strong>Create</strong>, а следом -<strong>C# Script</strong>. Так мы получим файл, в котором будем писать программу для передвижения нашего куба. Назовём этот файл Moving.</p>
14
<em>Скриншот: Skillbox Media</em><p>Теперь выделите куб и перетащите файл Moving в его окно<strong>Inspector</strong>, чтобы всё, что было написано в этом файле, куб принимал на свой счёт и выполнял. Откройте файл Moving.</p>
14
<em>Скриншот: Skillbox Media</em><p>Теперь выделите куб и перетащите файл Moving в его окно<strong>Inspector</strong>, чтобы всё, что было написано в этом файле, куб принимал на свой счёт и выполнял. Откройте файл Moving.</p>
15
<p>Всё. Теперь мы можем написать в нём логику передвижения куба.</p>
15
<p>Всё. Теперь мы можем написать в нём логику передвижения куба.</p>
16
<em>Скриншот: Skillbox Media</em><p>Самое простое, что можно сделать, - это поменять координаты объекта в пространстве. В окне<strong>Inspector</strong>у каждого объекта в сцене есть компонент<strong>Transform</strong>. В этом компоненте параметр<strong>Position</strong>как раз и содержит координаты объекта по осям X, Y, Z. Их-то мы и будем изменять через код.</p>
16
<em>Скриншот: Skillbox Media</em><p>Самое простое, что можно сделать, - это поменять координаты объекта в пространстве. В окне<strong>Inspector</strong>у каждого объекта в сцене есть компонент<strong>Transform</strong>. В этом компоненте параметр<strong>Position</strong>как раз и содержит координаты объекта по осям X, Y, Z. Их-то мы и будем изменять через код.</p>
17
<p>Изначально наш куб находится по координатам (0, 0, 0). Давайте сделаем так, чтобы при запуске игры он сместился по оси X на одну клеточку. Для этого в коде достаточно написать одну строчку, которая задаст новые координаты для объекта:</p>
17
<p>Изначально наш куб находится по координатам (0, 0, 0). Давайте сделаем так, чтобы при запуске игры он сместился по оси X на одну клеточку. Для этого в коде достаточно написать одну строчку, которая задаст новые координаты для объекта:</p>
18
<p><strong>transform.position = new Vector3(1, 0, 0);</strong></p>
18
<p><strong>transform.position = new Vector3(1, 0, 0);</strong></p>
19
<em>Скриншот: Skillbox Media</em><p>Обратите внимание, что эту строчку мы написали внутри фигурных скобок функции<strong>Start</strong>. Всё, что вы напишете в этой части кода,<a>будет выполняться лишь один раз</a> - при запуске игры.</p>
19
<em>Скриншот: Skillbox Media</em><p>Обратите внимание, что эту строчку мы написали внутри фигурных скобок функции<strong>Start</strong>. Всё, что вы напишете в этой части кода,<a>будет выполняться лишь один раз</a> - при запуске игры.</p>
20
<p>Поэтому куб лишь единожды телепортируется по новым координатам, и всё. Больше про эту строчку он не вспомнит.</p>
20
<p>Поэтому куб лишь единожды телепортируется по новым координатам, и всё. Больше про эту строчку он не вспомнит.</p>
21
<p>Помимо<strong>Start</strong>,<strong></strong>есть также функция<strong>Update</strong>. И всё, что вы напишете в ней, будет выполняться в каждом кадре (на будущее:<strong>Start</strong>выполняется до <strong>Update</strong>). Если мы перенесём написанную нами строчку в <strong>Update</strong>, то она каждый кадр будет ставить куб в координаты (1, 0, 0). Но так как после первого же кадра куб уже будет там стоять, то в дальнейшем никакого перемещения мы не увидим.</p>
21
<p>Помимо<strong>Start</strong>,<strong></strong>есть также функция<strong>Update</strong>. И всё, что вы напишете в ней, будет выполняться в каждом кадре (на будущее:<strong>Start</strong>выполняется до <strong>Update</strong>). Если мы перенесём написанную нами строчку в <strong>Update</strong>, то она каждый кадр будет ставить куб в координаты (1, 0, 0). Но так как после первого же кадра куб уже будет там стоять, то в дальнейшем никакого перемещения мы не увидим.</p>
22
<p>А вот если вы попробуете во время игры в окне<strong>Scene</strong>передвинуть куб мышкой вручную, то заметите, что сделать это теперь невозможно, ведь каждое мгновение куб снова и снова возвращается в позицию (1, 0, 0).</p>
22
<p>А вот если вы попробуете во время игры в окне<strong>Scene</strong>передвинуть куб мышкой вручную, то заметите, что сделать это теперь невозможно, ведь каждое мгновение куб снова и снова возвращается в позицию (1, 0, 0).</p>
23
<em>Скриншот: Skillbox Media</em><p>Но мы можем сделать так, что каждый кадр координаты куба будут немного меняться - это создаст эффект движения. Модифицируем нашу строчку так, чтобы в каждом кадре к позиции объекта по оси X прибавлялось значение 0.01. Чем меньше значение, тем медленнее будет двигаться куб:</p>
23
<em>Скриншот: Skillbox Media</em><p>Но мы можем сделать так, что каждый кадр координаты куба будут немного меняться - это создаст эффект движения. Модифицируем нашу строчку так, чтобы в каждом кадре к позиции объекта по оси X прибавлялось значение 0.01. Чем меньше значение, тем медленнее будет двигаться куб:</p>
24
<p><strong>transform.position += new Vector3(0.01f, 0, 0);</strong></p>
24
<p><strong>transform.position += new Vector3(0.01f, 0, 0);</strong></p>
25
<p>Обратите внимание, что здесь у нас не просто один знак "<strong>=</strong>", а целых два знака - "<strong>+=</strong>". Это значит, что значение слева мы увеличиваем на значение справа. Аналогично, если поставить "<strong>-=</strong>", мы будем уменьшать значение слева на значение справа.</p>
25
<p>Обратите внимание, что здесь у нас не просто один знак "<strong>=</strong>", а целых два знака - "<strong>+=</strong>". Это значит, что значение слева мы увеличиваем на значение справа. Аналогично, если поставить "<strong>-=</strong>", мы будем уменьшать значение слева на значение справа.</p>
26
<p>Также вы могли обратить внимание, что рядом с числом 0.01 стоит буква<strong>f</strong>. Так мы даём Unity понять, что используем не целое число, а число с плавающей запятой (тип float). Если<strong>f</strong>не поставить, то Unity будет ругаться, так как не сможет самостоятельно понять, какой же тип данных мы тут хотим использовать.</p>
26
<p>Также вы могли обратить внимание, что рядом с числом 0.01 стоит буква<strong>f</strong>. Так мы даём Unity понять, что используем не целое число, а число с плавающей запятой (тип float). Если<strong>f</strong>не поставить, то Unity будет ругаться, так как не сможет самостоятельно понять, какой же тип данных мы тут хотим использовать.</p>
27
<p>Вот таким образом, прибавляя по 0.01 в каждом кадре, мы получаем следующий результат.</p>
27
<p>Вот таким образом, прибавляя по 0.01 в каждом кадре, мы получаем следующий результат.</p>
28
<p>Посмотрите, как меняется в Inspector значение с положением куба по оси X. Оно постоянно увеличивается на 0.01</p>
28
<p>Посмотрите, как меняется в Inspector значение с положением куба по оси X. Оно постоянно увеличивается на 0.01</p>
29
<p>Теперь можно добавить в написанный нами код условие, что значение по оси X будет увеличиваться, только если удерживать кнопку со стрелкой "Вправо". Но давайте немного увеличим скорость движения куба и сделаем её не 0.01, а 0.1. Выглядеть условие будет так:</p>
29
<p>Теперь можно добавить в написанный нами код условие, что значение по оси X будет увеличиваться, только если удерживать кнопку со стрелкой "Вправо". Но давайте немного увеличим скорость движения куба и сделаем её не 0.01, а 0.1. Выглядеть условие будет так:</p>
30
<p><strong>if (Input.GetKey (KeyCode.RightArrow))</strong></p>
30
<p><strong>if (Input.GetKey (KeyCode.RightArrow))</strong></p>
31
<p><strong>transform.position += new Vector3(0.1f, 0, 0);</strong></p>
31
<p><strong>transform.position += new Vector3(0.1f, 0, 0);</strong></p>
32
<p>Первая строчка - это условие. Вторая - то, что происходит при выполнении условия. Если мы добавим ещё три условия - для кнопок "Влево", "Вверх", "Вниз", - то сможем полноценно управлять передвижением куба. Написать такой код можно по-разному. Вот один из вариантов.</p>
32
<p>Первая строчка - это условие. Вторая - то, что происходит при выполнении условия. Если мы добавим ещё три условия - для кнопок "Влево", "Вверх", "Вниз", - то сможем полноценно управлять передвижением куба. Написать такой код можно по-разному. Вот один из вариантов.</p>
33
Кликните правой кнопкой мыши по картинке, чтобы открыть её в большом размере<em>Скриншот: Skillbox Media</em><p>Это самый простой вариант реализации перемещения объектов в Unity. Он подходит и для 3D, и для 2D. Во втором случае в коде Vector3 надо заменить на Vector2 и менять положение только по осям X и Y.</p>
33
Кликните правой кнопкой мыши по картинке, чтобы открыть её в большом размере<em>Скриншот: Skillbox Media</em><p>Это самый простой вариант реализации перемещения объектов в Unity. Он подходит и для 3D, и для 2D. Во втором случае в коде Vector3 надо заменить на Vector2 и менять положение только по осям X и Y.</p>
34
Код, написанный нами выше, в действии<p>Это не совсем передвижение. При помощи такого способа мы просто каждый кадр телепортируем куб на новую позицию. Но из-за того, что расстояние между двумя позициями ничтожно мало, мы этого не замечаем, и нам кажется, что он плавно сдвигается в нужную сторону. Если бы мы прибавляли большее значение, например 50, то увидели бы, как объект перескакивает с одной точки на другую.</p>
34
Код, написанный нами выше, в действии<p>Это не совсем передвижение. При помощи такого способа мы просто каждый кадр телепортируем куб на новую позицию. Но из-за того, что расстояние между двумя позициями ничтожно мало, мы этого не замечаем, и нам кажется, что он плавно сдвигается в нужную сторону. Если бы мы прибавляли большее значение, например 50, то увидели бы, как объект перескакивает с одной точки на другую.</p>
35
<p><strong>В каких случаях использовать</strong></p>
35
<p><strong>В каких случаях использовать</strong></p>
36
<p>Несмотря на то, что с помощью transform.position тоже можно двигать объект, использовать её для этой цели не совсем правильно. Эта функция лучше подойдёт, когда вам надо в буквальном смысле<strong>телепортировать</strong>объект в другую точку в пространстве.</p>
36
<p>Несмотря на то, что с помощью transform.position тоже можно двигать объект, использовать её для этой цели не совсем правильно. Эта функция лучше подойдёт, когда вам надо в буквальном смысле<strong>телепортировать</strong>объект в другую точку в пространстве.</p>
37
<p>Этот способ похож на разобранный выше. Но запись проще. Чтобы каждый кадр менять позицию куба по оси Z, например на 5, достаточно вот такой строчки в функции<strong>Update</strong>:</p>
37
<p>Этот способ похож на разобранный выше. Но запись проще. Чтобы каждый кадр менять позицию куба по оси Z, например на 5, достаточно вот такой строчки в функции<strong>Update</strong>:</p>
38
<p><strong>transform.Translate (0, 0, 5);</strong></p>
38
<p><strong>transform.Translate (0, 0, 5);</strong></p>
39
<p>Ключевая разница с первым способом в том, что ранее мы просто указывали новые глобальные координаты объекта. Здесь же мы указываем новые локальные координаты. В первом случае, например, как бы вы ни повернули объект, увеличение значения по оси Z будет двигать объект всегда в одну сторону. В случае с <strong>transform.Translate</strong>направление осей, вдоль которых он будет двигаться, при повороте объекта также изменится.</p>
39
<p>Ключевая разница с первым способом в том, что ранее мы просто указывали новые глобальные координаты объекта. Здесь же мы указываем новые локальные координаты. В первом случае, например, как бы вы ни повернули объект, увеличение значения по оси Z будет двигать объект всегда в одну сторону. В случае с <strong>transform.Translate</strong>направление осей, вдоль которых он будет двигаться, при повороте объекта также изменится.</p>
40
<p>Помните, что в методе<strong>Update</strong>строчка кода будет выполняться каждый кадр работы игры. Проблема в том, что на разных компьютерах и устройствах частота кадров может быть разная. А это значит, что на быстрых машинах объект будет перемещаться быстрее, чем на медленных.</p>
40
<p>Помните, что в методе<strong>Update</strong>строчка кода будет выполняться каждый кадр работы игры. Проблема в том, что на разных компьютерах и устройствах частота кадров может быть разная. А это значит, что на быстрых машинах объект будет перемещаться быстрее, чем на медленных.</p>
41
<p>Чтобы избежать этого, надо умножить изменяемое значение на <strong>Time.deltaTime</strong>. Тогда выполнение этого конкретного вычисления будет привязано ко времени. Это обеспечит одинаковую скорость движения объекта при разной частоте кадров.</p>
41
<p>Чтобы избежать этого, надо умножить изменяемое значение на <strong>Time.deltaTime</strong>. Тогда выполнение этого конкретного вычисления будет привязано ко времени. Это обеспечит одинаковую скорость движения объекта при разной частоте кадров.</p>
42
<p>В первом способе это будет выглядеть вот так:</p>
42
<p>В первом способе это будет выглядеть вот так:</p>
43
<p><strong>transform.position += new Vector3(0, 0, 5) * Time.deltaTime;</strong></p>
43
<p><strong>transform.position += new Vector3(0, 0, 5) * Time.deltaTime;</strong></p>
44
<p>Во втором способе так:</p>
44
<p>Во втором способе так:</p>
45
<p><strong>transform.Translate (0, 0, 5 * Time.deltaTime);</strong></p>
45
<p><strong>transform.Translate (0, 0, 5 * Time.deltaTime);</strong></p>
46
<p>Здесь мы умножаем на <strong>Time.deltaTime</strong>только значение по оси Z. Если бы мы меняли значение по осям X и Y, то нам и их пришлось бы отдельно умножать на <strong>Time.deltaTime</strong>. Поэтому лучше подобный код организовать иначе.</p>
46
<p>Здесь мы умножаем на <strong>Time.deltaTime</strong>только значение по оси Z. Если бы мы меняли значение по осям X и Y, то нам и их пришлось бы отдельно умножать на <strong>Time.deltaTime</strong>. Поэтому лучше подобный код организовать иначе.</p>
47
<p>Для этого мы создадим переменную<strong>speed</strong>, в которую через<strong>Inspector</strong>запишем скорость, с которой будет двигаться кубик. Чтобы кубик двигался с этой скоростью по оси Z, мы умножим его на направление<strong>Vector3.forward</strong>.</p>
47
<p>Для этого мы создадим переменную<strong>speed</strong>, в которую через<strong>Inspector</strong>запишем скорость, с которой будет двигаться кубик. Чтобы кубик двигался с этой скоростью по оси Z, мы умножим его на направление<strong>Vector3.forward</strong>.</p>
48
<p><strong>Vector3.forward</strong> - это просто альтернативный способ написать координаты Vector3(0, 0, 1).Аналогично есть короткий способ записать и другие направления:</p>
48
<p><strong>Vector3.forward</strong> - это просто альтернативный способ написать координаты Vector3(0, 0, 1).Аналогично есть короткий способ записать и другие направления:</p>
49
<p>Vector3.back = Vector3(0, 0, -1)</p>
49
<p>Vector3.back = Vector3(0, 0, -1)</p>
50
<p>Vector3.up = Vector3 (0, 1, 0)</p>
50
<p>Vector3.up = Vector3 (0, 1, 0)</p>
51
<p>Vector3.down = Vector3(0, -1, 0)</p>
51
<p>Vector3.down = Vector3(0, -1, 0)</p>
52
<p>Vector3.right = Vector3(1, 0, 0)</p>
52
<p>Vector3.right = Vector3(1, 0, 0)</p>
53
<p>Vector3.left = Vector3(-1, 0, 0)</p>
53
<p>Vector3.left = Vector3(-1, 0, 0)</p>
54
<p>Затем всё это мы умножим на <strong>Time.deltaTime</strong>, чтобы передвижение не зависело от частоты кадров.</p>
54
<p>Затем всё это мы умножим на <strong>Time.deltaTime</strong>, чтобы передвижение не зависело от частоты кадров.</p>
55
Кликните правой кнопкой мыши по картинке, чтобы открыть её в большом размере<em>Скриншот: Skillbox Media</em><p>Обратите внимание, что теперь, задавая в <strong>Inspector</strong>скорость, мы можем двигать объект не только вперёд по его оси Z, но и назад. Для этого достаточно поставить у значения скорости знак "-".</p>
55
Кликните правой кнопкой мыши по картинке, чтобы открыть её в большом размере<em>Скриншот: Skillbox Media</em><p>Обратите внимание, что теперь, задавая в <strong>Inspector</strong>скорость, мы можем двигать объект не только вперёд по его оси Z, но и назад. Для этого достаточно поставить у значения скорости знак "-".</p>
56
<p>Благодаря модификатору<strong>public</strong>мы можем менять значение speed прямо в окне<strong>Inspector</strong></p>
56
<p>Благодаря модификатору<strong>public</strong>мы можем менять значение speed прямо в окне<strong>Inspector</strong></p>
57
<p>Аналогично параметр<strong>speed</strong>можно использовать и для первого способа передвижения кубика. Но чтобы он двигался по оси Z не в глобальных координатах, а в локальных, необходимо вместо<strong>Vector3.forward</strong>использовать<strong>transform.forward</strong>. Строчка кода в этом случае будет выглядеть вот так:</p>
57
<p>Аналогично параметр<strong>speed</strong>можно использовать и для первого способа передвижения кубика. Но чтобы он двигался по оси Z не в глобальных координатах, а в локальных, необходимо вместо<strong>Vector3.forward</strong>использовать<strong>transform.forward</strong>. Строчка кода в этом случае будет выглядеть вот так:</p>
58
<p><strong>transform.position += transform.forward * speed * Time.deltaTime;</strong></p>
58
<p><strong>transform.position += transform.forward * speed * Time.deltaTime;</strong></p>
59
<p>Проблема с <strong>transform.Translate</strong>в том, что объект, который двигается с помощью этого способа, будет плохо считывать прикосновение к другим объектам. Он не будет игнорировать их совсем, но и полноценно отрабатывать не сможет. Ваш персонаж будет проходить сквозь стены и других персонажей. Поэтому такой способ перемещения лучше использовать в случаях, когда нет необходимости в физическом взаимодействии объектов.</p>
59
<p>Проблема с <strong>transform.Translate</strong>в том, что объект, который двигается с помощью этого способа, будет плохо считывать прикосновение к другим объектам. Он не будет игнорировать их совсем, но и полноценно отрабатывать не сможет. Ваш персонаж будет проходить сквозь стены и других персонажей. Поэтому такой способ перемещения лучше использовать в случаях, когда нет необходимости в физическом взаимодействии объектов.</p>
60
Минимум одно столкновение кубиков всё-таки засчиталось. Из-за него и из-за выключенной гравитации они отправились в полёт<p><strong>В каких случаях использовать</strong></p>
60
Минимум одно столкновение кубиков всё-таки засчиталось. Из-за него и из-за выключенной гравитации они отправились в полёт<p><strong>В каких случаях использовать</strong></p>
61
<p><strong>transform.Translate</strong>подойдёт для случаев, когда вам надо, чтобы объект двигался в нужном направлении и при этом неважно, столкнётся ли он с препятствиями на пути и будет ли реалистично с точки зрения физики реагировать на окружающий мир. Представьте себе рогалик в духе Darkest Dungeon или квест наподобие The Cat Lady, где персонажи двигаются либо вправо, либо влево. Для подобного расчёты физики не нужны и можно обойтись<strong>transform.Translate</strong>.</p>
61
<p><strong>transform.Translate</strong>подойдёт для случаев, когда вам надо, чтобы объект двигался в нужном направлении и при этом неважно, столкнётся ли он с препятствиями на пути и будет ли реалистично с точки зрения физики реагировать на окружающий мир. Представьте себе рогалик в духе Darkest Dungeon или квест наподобие The Cat Lady, где персонажи двигаются либо вправо, либо влево. Для подобного расчёты физики не нужны и можно обойтись<strong>transform.Translate</strong>.</p>
62
<p>Чтобы наш кубик можно было двигать при помощи физики, для начала к нему надо добавить компонент<strong>Rigidbody</strong>. Для этого выделите объект в окне<strong>Hierarchy</strong>, затем в окне<strong>Inspector</strong>нажмите<strong>Add Component</strong>и в поисковой строке введите<strong>Rigidbody</strong>(так как у нас 3D-игра, выберите просто Rigidbody, а не Rigidbody 2D).</p>
62
<p>Чтобы наш кубик можно было двигать при помощи физики, для начала к нему надо добавить компонент<strong>Rigidbody</strong>. Для этого выделите объект в окне<strong>Hierarchy</strong>, затем в окне<strong>Inspector</strong>нажмите<strong>Add Component</strong>и в поисковой строке введите<strong>Rigidbody</strong>(так как у нас 3D-игра, выберите просто Rigidbody, а не Rigidbody 2D).</p>
63
<p>Если после этого запустить игру, то вы увидите, что кубик полетел вниз. Это действие гравитации. Если убрать галочку с <strong>Use Gravity</strong>, то падать он не будет.</p>
63
<p>Если после этого запустить игру, то вы увидите, что кубик полетел вниз. Это действие гравитации. Если убрать галочку с <strong>Use Gravity</strong>, то падать он не будет.</p>
64
<em>Скриншот: Skillbox Media</em><p>Чтобы заставить объект двигаться в определённую сторону при помощи физики, можно воспользоваться функцией<strong>AddForce</strong>. Она используется по тому же принципу, что и <strong>transform.Translate</strong>. Вы просто записываете в скобках направление движения объекта по осям X, Y, Z. Чем выше цифры, тем б<strong>о</strong>льшая сила будет применена к нашему объекту.</p>
64
<em>Скриншот: Skillbox Media</em><p>Чтобы заставить объект двигаться в определённую сторону при помощи физики, можно воспользоваться функцией<strong>AddForce</strong>. Она используется по тому же принципу, что и <strong>transform.Translate</strong>. Вы просто записываете в скобках направление движения объекта по осям X, Y, Z. Чем выше цифры, тем б<strong>о</strong>льшая сила будет применена к нашему объекту.</p>
65
Кликните правой кнопкой мыши по картинке, чтобы открыть её в большом размере<em>Скриншот: Skillbox Media</em><p>Обратите внимание, что в этом коде нам надо изначально сделать переменную, которая будет ссылаться на компонент<strong>Rigidbody</strong>. Мы назвали её <strong>rb</strong>. При запуске игры в функции<strong>Start</strong>мы указали, что эта переменная ссылается на компонент<strong>Rigidbody</strong>нашего кубика. Далее функцию<strong>AddForce</strong>мы вызываем уже через неё.</p>
65
Кликните правой кнопкой мыши по картинке, чтобы открыть её в большом размере<em>Скриншот: Skillbox Media</em><p>Обратите внимание, что в этом коде нам надо изначально сделать переменную, которая будет ссылаться на компонент<strong>Rigidbody</strong>. Мы назвали её <strong>rb</strong>. При запуске игры в функции<strong>Start</strong>мы указали, что эта переменная ссылается на компонент<strong>Rigidbody</strong>нашего кубика. Далее функцию<strong>AddForce</strong>мы вызываем уже через неё.</p>
66
При использовании<strong>AddForce</strong>объекты не проходят сквозь друг друга<p>Также обратите внимание, что строчку<strong>rb.AddForce (1, 0, 0)</strong>мы вызываем в методе<a><strong>FixedUpdate</strong></a>. Этот метод подобен<strong>Update</strong>, но используется специально для вычислений, связанных с физикой. Работать наш код будет и в <strong>Update</strong>, но некорректно.</p>
66
При использовании<strong>AddForce</strong>объекты не проходят сквозь друг друга<p>Также обратите внимание, что строчку<strong>rb.AddForce (1, 0, 0)</strong>мы вызываем в методе<a><strong>FixedUpdate</strong></a>. Этот метод подобен<strong>Update</strong>, но используется специально для вычислений, связанных с физикой. Работать наш код будет и в <strong>Update</strong>, но некорректно.</p>
67
<p>В ролике ниже у нас два кубика, реализующих один и тот же код. Но зелёный выполняет его в методе<strong>Update</strong>, а красный - в <strong>FixedUpdate</strong>. Как видите, красный кубик спокойно двигается по экрану, а зелёный уносится далеко вперёд.</p>
67
<p>В ролике ниже у нас два кубика, реализующих один и тот же код. Но зелёный выполняет его в методе<strong>Update</strong>, а красный - в <strong>FixedUpdate</strong>. Как видите, красный кубик спокойно двигается по экрану, а зелёный уносится далеко вперёд.</p>
68
<p>Когда вы добавляете к объекту компонент<strong>Rigidbody</strong>, вы отдаёте его<a>перемещения под контроль физического движка Unity</a>.</p>
68
<p>Когда вы добавляете к объекту компонент<strong>Rigidbody</strong>, вы отдаёте его<a>перемещения под контроль физического движка Unity</a>.</p>
69
<p>Эти вычисления будут проводиться, даже если вы не написали ни строчки кода. Вспомните, что если бы мы не отключили гравитацию, то наш кубик при запуске игры стал бы падать. А если бы он встретил внизу другой такой же объект с <strong>Rigidbody</strong>(и компонентом<strong>Collider</strong>, обозначающим его границы), то они оттолкнулись бы друг от друга.</p>
69
<p>Эти вычисления будут проводиться, даже если вы не написали ни строчки кода. Вспомните, что если бы мы не отключили гравитацию, то наш кубик при запуске игры стал бы падать. А если бы он встретил внизу другой такой же объект с <strong>Rigidbody</strong>(и компонентом<strong>Collider</strong>, обозначающим его границы), то они оттолкнулись бы друг от друга.</p>
70
<p>Физический движок Unity проводит свои вычисления с определёнными временными интервалами. И эти интервалы не совпадают с частотой обновления кадров. Поэтому если вам надо проводить физические расчёты, то вызывать их в методе<strong>Update</strong>будет неправильно, ведь они будут вызываться чаще, чем физический движок будет их обрабатывать.</p>
70
<p>Физический движок Unity проводит свои вычисления с определёнными временными интервалами. И эти интервалы не совпадают с частотой обновления кадров. Поэтому если вам надо проводить физические расчёты, то вызывать их в методе<strong>Update</strong>будет неправильно, ведь они будут вызываться чаще, чем физический движок будет их обрабатывать.</p>
71
<p>Всё, что вы напишете в<strong>FixedUpdate</strong>,<strong></strong>будет вызываться непосредственно перед каждым обновлением физического движка. Поэтому все расчёты будут обработаны физическим движком напрямую и корректно.</p>
71
<p>Всё, что вы напишете в<strong>FixedUpdate</strong>,<strong></strong>будет вызываться непосредственно перед каждым обновлением физического движка. Поэтому все расчёты будут обработаны физическим движком напрямую и корректно.</p>
72
<p><strong>Важно!</strong></p>
72
<p><strong>Важно!</strong></p>
73
<p>Если передвижение с использованием физики надо вызывать в <strong>FixedUpdate</strong>, то проверять, нажал ли игрок кнопку, приводящую объект в движение, лучше всё-таки в <strong>Update</strong>. Иначе может случиться так, что игрок нажмёт кнопку и попадёт в тот самый момент между выполнением<strong>FixedUpdate</strong>. В итоге нажатие не будет засчитано.</p>
73
<p>Если передвижение с использованием физики надо вызывать в <strong>FixedUpdate</strong>, то проверять, нажал ли игрок кнопку, приводящую объект в движение, лучше всё-таки в <strong>Update</strong>. Иначе может случиться так, что игрок нажмёт кнопку и попадёт в тот самый момент между выполнением<strong>FixedUpdate</strong>. В итоге нажатие не будет засчитано.</p>
74
<p>Например, вот так может выглядеть код, в котором нажатие кнопок засчитывается в <strong>Update</strong>, а движение, основанное на физике, выполняется в <strong>FixedUpdate</strong>.</p>
74
<p>Например, вот так может выглядеть код, в котором нажатие кнопок засчитывается в <strong>Update</strong>, а движение, основанное на физике, выполняется в <strong>FixedUpdate</strong>.</p>
75
Кликните правой кнопкой мыши по картинке, чтобы открыть её в большом размере<em>Скриншот: Skillbox Media</em><p>Здесь в <strong>Update</strong>мы <a>используем функцию<strong>GetAxis</strong></a>. Она отслеживает, какие вы используете кнопки из тех, что обычно отвечают за управление персонажем.</p>
75
Кликните правой кнопкой мыши по картинке, чтобы открыть её в большом размере<em>Скриншот: Skillbox Media</em><p>Здесь в <strong>Update</strong>мы <a>используем функцию<strong>GetAxis</strong></a>. Она отслеживает, какие вы используете кнопки из тех, что обычно отвечают за управление персонажем.</p>
76
<p>Если в скобках мы пишем Horizontal, значит, это либо кнопки со стрелками "Вправо" и "Влево", либо A и D. Если в скобках стоит Vertical, значит, это кнопки со стрелками "Вверх" и "Вниз" либо W и S.</p>
76
<p>Если в скобках мы пишем Horizontal, значит, это либо кнопки со стрелками "Вправо" и "Влево", либо A и D. Если в скобках стоит Vertical, значит, это кнопки со стрелками "Вверх" и "Вниз" либо W и S.</p>
77
<p>В обоих случаях результатом становится число от -1 до 1, которое мы сохраняем в переменную. Если мы нажали "Вправо", то в переменной Horizontal сохранится 1. Если нажали "Влево", то сохранится -1. Если мы ничего не нажимаем, то сохраняется 0.</p>
77
<p>В обоих случаях результатом становится число от -1 до 1, которое мы сохраняем в переменную. Если мы нажали "Вправо", то в переменной Horizontal сохранится 1. Если нажали "Влево", то сохранится -1. Если мы ничего не нажимаем, то сохраняется 0.</p>
78
<p>В это же время в <strong>FixedUpdate</strong>выполняется функция<strong>AddForce</strong>. По умолчанию по всем осям в ней стоит 0. Но в осях X и Z мы передаём значения переменных Horizontal и Vertical.</p>
78
<p>В это же время в <strong>FixedUpdate</strong>выполняется функция<strong>AddForce</strong>. По умолчанию по всем осям в ней стоит 0. Но в осях X и Z мы передаём значения переменных Horizontal и Vertical.</p>
79
<p>Соответственно, если мы нажали кнопку "Вправо", то по оси X в <strong>AddForce</strong>будет значение 1, и кубик, подчиняясь законам физики, будет двигаться вправо. Если же мы нажмём "Влево", то в AddForce по оси X уже будет -1, и кубик пойдёт влево. Если мы отпустим кнопки, то по оси X опять будет 0…</p>
79
<p>Соответственно, если мы нажали кнопку "Вправо", то по оси X в <strong>AddForce</strong>будет значение 1, и кубик, подчиняясь законам физики, будет двигаться вправо. Если же мы нажмём "Влево", то в AddForce по оси X уже будет -1, и кубик пойдёт влево. Если мы отпустим кнопки, то по оси X опять будет 0…</p>
80
<p>Правда, кубик не остановится. Он продолжит движение с той скоростью, которую успел набрать, пока сопротивление окружающей среды, гравитация и сила трения не погасят её. Либо пока к нему не будет приложена аналогичная сила с противоположной стороны. На видео ниже видно, что, даже если Horizontal и Vertical равны 0, кубик продолжает двигаться, так как он уже набрал скорость.</p>
80
<p>Правда, кубик не остановится. Он продолжит движение с той скоростью, которую успел набрать, пока сопротивление окружающей среды, гравитация и сила трения не погасят её. Либо пока к нему не будет приложена аналогичная сила с противоположной стороны. На видео ниже видно, что, даже если Horizontal и Vertical равны 0, кубик продолжает двигаться, так как он уже набрал скорость.</p>
81
<p>У функции<strong>AddForce</strong>есть параметр<strong>ForceMode</strong>с четырьмя режимами работы:<strong>Acceleration</strong>,<strong>Force</strong>,<strong>Impulse</strong>,<strong>VelocityChange</strong>. По умолчанию в Unity для функции<strong>AddForce</strong>применяется режим<strong>Force</strong>.</p>
81
<p>У функции<strong>AddForce</strong>есть параметр<strong>ForceMode</strong>с четырьмя режимами работы:<strong>Acceleration</strong>,<strong>Force</strong>,<strong>Impulse</strong>,<strong>VelocityChange</strong>. По умолчанию в Unity для функции<strong>AddForce</strong>применяется режим<strong>Force</strong>.</p>
82
<em>Скриншот: Skillbox Media</em><ul><li><strong>ForceMode.Force</strong> - используется, когда силу надо применять на протяжении какого-то времени. Представьте, что это двигатель, который толкает ракету. При этом учитывается масса объекта. Чем он тяжелее, тем б<strong>о</strong>льшую силу надо применить, чтобы сдвинуть объект.</li>
82
<em>Скриншот: Skillbox Media</em><ul><li><strong>ForceMode.Force</strong> - используется, когда силу надо применять на протяжении какого-то времени. Представьте, что это двигатель, который толкает ракету. При этом учитывается масса объекта. Чем он тяжелее, тем б<strong>о</strong>льшую силу надо применить, чтобы сдвинуть объект.</li>
83
<li><strong>ForceMode.Acceleration</strong> - то же самое, что и <strong>ForceMode.Force</strong>, но масса объекта при этом не учитывается. Он может весить 100 грамм или тонну. Результат будет зависеть только от применяемой силы.</li>
83
<li><strong>ForceMode.Acceleration</strong> - то же самое, что и <strong>ForceMode.Force</strong>, но масса объекта при этом не учитывается. Он может весить 100 грамм или тонну. Результат будет зависеть только от применяемой силы.</li>
84
<li><strong>ForceMode.Impulse</strong> - короткое воздействие силы на объект. Представьте, что это удар по мячу или взрыв гранаты, который разбрасывает всё вокруг. Масса объекта при этом учитывается.</li>
84
<li><strong>ForceMode.Impulse</strong> - короткое воздействие силы на объект. Представьте, что это удар по мячу или взрыв гранаты, который разбрасывает всё вокруг. Масса объекта при этом учитывается.</li>
85
<li><strong>ForceMode.VelocityChange</strong> - то же самое, что и <strong>ForceMode.Impulse</strong>, но масса объекта не учитывается.</li>
85
<li><strong>ForceMode.VelocityChange</strong> - то же самое, что и <strong>ForceMode.Impulse</strong>, но масса объекта не учитывается.</li>
86
</ul><p>В ролике ниже приводится пример отличия<strong>Impulse</strong>от <strong>VelocityChange</strong>. Масса обоих кубиков составляет 5. Нажимая на кубик, мы прикладываем к его оси Y силу, равную 10. В итоге кубик с <strong>ForceMode.Impulse</strong>подпрыгивает едва заметно, а кубик с <strong>ForceMode.VelocityChange</strong>подскакивает высоко.</p>
86
</ul><p>В ролике ниже приводится пример отличия<strong>Impulse</strong>от <strong>VelocityChange</strong>. Масса обоих кубиков составляет 5. Нажимая на кубик, мы прикладываем к его оси Y силу, равную 10. В итоге кубик с <strong>ForceMode.Impulse</strong>подпрыгивает едва заметно, а кубик с <strong>ForceMode.VelocityChange</strong>подскакивает высоко.</p>
87
<p>Чтобы кубик с <strong>Impulse</strong>подпрыгнул выше, нам надо либо уменьшить его массу, либо увеличить прилагаемую силу. При этом высота прыжка кубика с <strong>VelocityChange</strong>зависит только от того, насколько большую силу мы к нему приложим. Масса может быть любой.</p>
87
<p>Чтобы кубик с <strong>Impulse</strong>подпрыгнул выше, нам надо либо уменьшить его массу, либо увеличить прилагаемую силу. При этом высота прыжка кубика с <strong>VelocityChange</strong>зависит только от того, насколько большую силу мы к нему приложим. Масса может быть любой.</p>
88
<p>Отметим также, что функцию<strong>AddForce</strong>в этом ролике мы вызывали в функции<strong>OnMouseDown</strong>, которая выполняется один раз при нажатии курсором на объект с компонентом<strong>Collider</strong>.</p>
88
<p>Отметим также, что функцию<strong>AddForce</strong>в этом ролике мы вызывали в функции<strong>OnMouseDown</strong>, которая выполняется один раз при нажатии курсором на объект с компонентом<strong>Collider</strong>.</p>
89
<p>При использовании компонента<strong>Rigidbody</strong>передвигать объект можно не только с помощью прилагаемой к нему силы. Функция<strong>MovePosition</strong>позволяет реализовать такое же перемещение, как при<strong>transform.Translate</strong>, но при этом объект будет физически более корректно реагировать на все преграды, которые встанут у него на пути. При<strong>transform.Translate</strong>, напомним, объект проходил бы сквозь них.</p>
89
<p>При использовании компонента<strong>Rigidbody</strong>передвигать объект можно не только с помощью прилагаемой к нему силы. Функция<strong>MovePosition</strong>позволяет реализовать такое же перемещение, как при<strong>transform.Translate</strong>, но при этом объект будет физически более корректно реагировать на все преграды, которые встанут у него на пути. При<strong>transform.Translate</strong>, напомним, объект проходил бы сквозь них.</p>
90
<p>Код выше мы выполняем в <strong>FixedUpdate</strong>. Каждый раз, когда физический движок Unity проводит вычисления, мы добавляем к позиции компонента<strong>Rigidbody</strong>направление (0, 0, 1), умноженное на скорость, которую задаём через<strong>Inspector</strong>.</p>
90
<p>Код выше мы выполняем в <strong>FixedUpdate</strong>. Каждый раз, когда физический движок Unity проводит вычисления, мы добавляем к позиции компонента<strong>Rigidbody</strong>направление (0, 0, 1), умноженное на скорость, которую задаём через<strong>Inspector</strong>.</p>
91
<p>Ещё один способ, как привести объект в движение при помощи компонента<strong>Rigidbody</strong>, -<a>менять его параметр</a><strong>velocity</strong>напрямую. В этом случае объект будет двигаться без ускорений и замедлений.</p>
91
<p>Ещё один способ, как привести объект в движение при помощи компонента<strong>Rigidbody</strong>, -<a>менять его параметр</a><strong>velocity</strong>напрямую. В этом случае объект будет двигаться без ускорений и замедлений.</p>
92
Кликните правой кнопкой мыши по картинке, чтобы открыть её в большом размере<em>Скриншот: Skillbox Media</em><p>Правда, в официальной документации Unity указано, что напрямую менять<strong>velocity</strong>не стоит, потому что это может привести к нереалистичному поведению объекта. Допустимая ситуация, при которой можно поменять<strong>velocity</strong>, - если в шутере от первого лица для реализации прыжка надо немедленно изменить скорость и направление движения персонажа.</p>
92
Кликните правой кнопкой мыши по картинке, чтобы открыть её в большом размере<em>Скриншот: Skillbox Media</em><p>Правда, в официальной документации Unity указано, что напрямую менять<strong>velocity</strong>не стоит, потому что это может привести к нереалистичному поведению объекта. Допустимая ситуация, при которой можно поменять<strong>velocity</strong>, - если в шутере от первого лица для реализации прыжка надо немедленно изменить скорость и направление движения персонажа.</p>
93
<p><strong>В каких случаях использовать</strong></p>
93
<p><strong>В каких случаях использовать</strong></p>
94
<p>Когда надо, чтобы объекты в игре вели себя, как предметы из реального мира, у которых есть масса, на которые воздействует гравитация и другие силы. Чтобы предметы, сталкиваясь друг с другом, не просто останавливались, а эффектно разлетались в стороны. Вспомните Angry Birds. Выстрел птицами из рогатки и последующее разрушение крепости поросят реализуются при помощи физики.</p>
94
<p>Когда надо, чтобы объекты в игре вели себя, как предметы из реального мира, у которых есть масса, на которые воздействует гравитация и другие силы. Чтобы предметы, сталкиваясь друг с другом, не просто останавливались, а эффектно разлетались в стороны. Вспомните Angry Birds. Выстрел птицами из рогатки и последующее разрушение крепости поросят реализуются при помощи физики.</p>
95
<p>Объектам в Unity можно задавать не просто направление движения, но и конкретное место в игровом пространстве, куда они должны прибыть. Реализовать такую механику можно разными способами. Самые базовые - при помощи функций<strong>Vector3.MoveTowards</strong>и <strong>Vector3.Lerp</strong>. Разница между ними в том, что первая функция нужна, если вы хотите, чтобы объект двигался к цели с определённой скоростью. А вторая - для ситуаций, когда объект должен достигнуть цели за определённое время: например, чтобы самолёт на глобальной карте прилетел из точки А в точку B строго за три секунды.</p>
95
<p>Объектам в Unity можно задавать не просто направление движения, но и конкретное место в игровом пространстве, куда они должны прибыть. Реализовать такую механику можно разными способами. Самые базовые - при помощи функций<strong>Vector3.MoveTowards</strong>и <strong>Vector3.Lerp</strong>. Разница между ними в том, что первая функция нужна, если вы хотите, чтобы объект двигался к цели с определённой скоростью. А вторая - для ситуаций, когда объект должен достигнуть цели за определённое время: например, чтобы самолёт на глобальной карте прилетел из точки А в точку B строго за три секунды.</p>
96
<p>Рассмотрим первую функцию -<strong>Vector3.MoveTowards</strong>. В окне<strong>Scene</strong>мы видим три объекта: сам кубик и два невидимых объекта, обозначающих точки в пространстве. Координаты первой точки - (0, 0, 0), а второй - (5, 0, -5).</p>
96
<p>Рассмотрим первую функцию -<strong>Vector3.MoveTowards</strong>. В окне<strong>Scene</strong>мы видим три объекта: сам кубик и два невидимых объекта, обозначающих точки в пространстве. Координаты первой точки - (0, 0, 0), а второй - (5, 0, -5).</p>
97
<em>Скриншот: Skillbox Media</em><p>Мы хотим, чтобы при запуске игры куб переместился из позиции, где он стоит (0, 0, 0), к координатам невидимого объекта (5, 0, -5). Причём мы хотим, чтобы сделал он это со скоростью 0.1. Для этого в скрипте Moving мы пишем следующий код:</p>
97
<em>Скриншот: Skillbox Media</em><p>Мы хотим, чтобы при запуске игры куб переместился из позиции, где он стоит (0, 0, 0), к координатам невидимого объекта (5, 0, -5). Причём мы хотим, чтобы сделал он это со скоростью 0.1. Для этого в скрипте Moving мы пишем следующий код:</p>
98
Кликните правой кнопкой мыши по картинке, чтобы открыть её в большом размере<em>Скриншот: Skillbox Media</em><p>Здесь мы в каждом кадре присваиваем позиции нашего кубика координаты, которые вычислила функция<strong>Vector3.MoveTowards</strong>. Вычисления делаются на основе тех данных, которые функция получила в скобках.</p>
98
Кликните правой кнопкой мыши по картинке, чтобы открыть её в большом размере<em>Скриншот: Skillbox Media</em><p>Здесь мы в каждом кадре присваиваем позиции нашего кубика координаты, которые вычислила функция<strong>Vector3.MoveTowards</strong>. Вычисления делаются на основе тех данных, которые функция получила в скобках.</p>
99
<p>Сначала мы указываем текущую позицию кубика<strong>transform.position</strong>. Далее - координаты нашей цели<strong>target.position</strong>. Обратите внимание, что мы объявили эту переменную как<strong>public Transform target</strong>, чтобы можно было просто перетащить этот объект из окна<strong>Hierarchy</strong>в окно<strong>Inspector</strong>нашего кубика в поле с именем<strong>target</strong>. Благодаря этому мы получили его координаты. И теперь, куда бы мы ни передвинули объект<strong>target</strong>, при запуске игры кубик полетит к нему.</p>
99
<p>Сначала мы указываем текущую позицию кубика<strong>transform.position</strong>. Далее - координаты нашей цели<strong>target.position</strong>. Обратите внимание, что мы объявили эту переменную как<strong>public Transform target</strong>, чтобы можно было просто перетащить этот объект из окна<strong>Hierarchy</strong>в окно<strong>Inspector</strong>нашего кубика в поле с именем<strong>target</strong>. Благодаря этому мы получили его координаты. И теперь, куда бы мы ни передвинули объект<strong>target</strong>, при запуске игры кубик полетит к нему.</p>
100
<p>С тем же успехом мы могли бы не ссылаться на какой-то объект, а просто написать в коде координаты цели напрямую, то есть вместо<strong>target.position</strong>мы могли написать new Vector3(5, 0, -5). Эффект был бы тот же.</p>
100
<p>С тем же успехом мы могли бы не ссылаться на какой-то объект, а просто написать в коде координаты цели напрямую, то есть вместо<strong>target.position</strong>мы могли написать new Vector3(5, 0, -5). Эффект был бы тот же.</p>
101
<p>Третий параметр в скобках - это скорость, с которой объект будет двигаться к цели. Мы умножаем её на <strong>Time.deltaTime</strong>, чтобы скорость движения кубика не зависела от частоты кадров.</p>
101
<p>Третий параметр в скобках - это скорость, с которой объект будет двигаться к цели. Мы умножаем её на <strong>Time.deltaTime</strong>, чтобы скорость движения кубика не зависела от частоты кадров.</p>
102
<p>Прелесть функции<strong>Vector3.MoveTowards</strong>в том, что объект всегда будет двигаться с указанной скоростью и в конце он встанет точно в те координаты, которые вы ему указали. Порой это очень важно. Также, если не прописать дополнительных условий, во время игры мы можем поменять позицию объекта-цели, и наш кубик продолжит преследовать его, как в ролике ниже. Самое очевидное применение этому - чтобы монстры постоянно преследовали игрока, куда бы он ни пошёл, как в Vampire Survivors.</p>
102
<p>Прелесть функции<strong>Vector3.MoveTowards</strong>в том, что объект всегда будет двигаться с указанной скоростью и в конце он встанет точно в те координаты, которые вы ему указали. Порой это очень важно. Также, если не прописать дополнительных условий, во время игры мы можем поменять позицию объекта-цели, и наш кубик продолжит преследовать его, как в ролике ниже. Самое очевидное применение этому - чтобы монстры постоянно преследовали игрока, куда бы он ни пошёл, как в Vampire Survivors.</p>
103
<p>Функция<strong>Vector3.Lerp</strong> - это линейная интерполяция. Звучит страшно, но это просто поиск всех промежуточных значений между двумя величинами. В нашем случае эта функция позволит найти все промежуточные координаты между начальной координатой кубика и координатой его цели. Благодаря этим промежуточным координатам и будет построен маршрут. В целом код выглядит почти так же, как и у <strong>Vector3.MoveTowards</strong>. Но есть нюансы.</p>
103
<p>Функция<strong>Vector3.Lerp</strong> - это линейная интерполяция. Звучит страшно, но это просто поиск всех промежуточных значений между двумя величинами. В нашем случае эта функция позволит найти все промежуточные координаты между начальной координатой кубика и координатой его цели. Благодаря этим промежуточным координатам и будет построен маршрут. В целом код выглядит почти так же, как и у <strong>Vector3.MoveTowards</strong>. Но есть нюансы.</p>
104
Кликните правой кнопкой мыши по картинке, чтобы открыть её в большом размере<em>Скриншот: Skillbox Media</em><p>Во-первых, в функции<strong>Vector3.MoveTowards</strong>первым параметром в скобках мы задавали текущую позицию объекта<strong>transform.position</strong>, которая менялась каждый кадр. В <strong>Vector3.Lerp</strong>вместо этого мы указываем изначальную позицию объекта, которая не меняется по ходу его движения. Чтобы сделать это, в функции<strong>Start</strong>при запуске игры мы сохраняем в переменной<strong>startPosition</strong>координаты кубика, которые были у него на момент начала игры.</p>
104
Кликните правой кнопкой мыши по картинке, чтобы открыть её в большом размере<em>Скриншот: Skillbox Media</em><p>Во-первых, в функции<strong>Vector3.MoveTowards</strong>первым параметром в скобках мы задавали текущую позицию объекта<strong>transform.position</strong>, которая менялась каждый кадр. В <strong>Vector3.Lerp</strong>вместо этого мы указываем изначальную позицию объекта, которая не меняется по ходу его движения. Чтобы сделать это, в функции<strong>Start</strong>при запуске игры мы сохраняем в переменной<strong>startPosition</strong>координаты кубика, которые были у него на момент начала игры.</p>
105
<p>Далее мы указываем цель, к которой направляется наш кубик, -<strong>target.position</strong>. Её мы, как и в случае с <strong>Vector3.MoveTowards</strong>, задаём через<strong>Inspector</strong>в объекте кубика.</p>
105
<p>Далее мы указываем цель, к которой направляется наш кубик, -<strong>target.position</strong>. Её мы, как и в случае с <strong>Vector3.MoveTowards</strong>, задаём через<strong>Inspector</strong>в объекте кубика.</p>
106
<p>Ну и самое сложное. В <strong>Vector3.MoveTowards</strong>третьим параметром мы передавали значение скорости движения объекта к цели. А в <strong>Vector3.Lerp</strong>указываем, какая прошла часть времени, выделенного на дорогу. Для этого мы делим время, прошедшее с начала движения<strong>(timeElapsed)</strong>, на время, которое должна занять дорога<strong>(duration)</strong>.</p>
106
<p>Ну и самое сложное. В <strong>Vector3.MoveTowards</strong>третьим параметром мы передавали значение скорости движения объекта к цели. А в <strong>Vector3.Lerp</strong>указываем, какая прошла часть времени, выделенного на дорогу. Для этого мы делим время, прошедшее с начала движения<strong>(timeElapsed)</strong>, на время, которое должна занять дорога<strong>(duration)</strong>.</p>
107
<p>Это третье значение всегда должно находиться в диапазоне от 0 до 1. Просто представьте, что 0 - это 0% от отведённого на дорогу времени. Тот момент, когда кубик находится в самом начале пути. А 1 - это 100% времени, отведённого на дорогу. То есть всё выделенное время прошло, и наш кубик в этот момент уже должен достичь цели.</p>
107
<p>Это третье значение всегда должно находиться в диапазоне от 0 до 1. Просто представьте, что 0 - это 0% от отведённого на дорогу времени. Тот момент, когда кубик находится в самом начале пути. А 1 - это 100% времени, отведённого на дорогу. То есть всё выделенное время прошло, и наш кубик в этот момент уже должен достичь цели.</p>
108
<p><strong>Как это работает</strong></p>
108
<p><strong>Как это работает</strong></p>
109
<p>Допустим, мы выдаём кубику на дорогу три секунды, и полторы секунды уже прошло. Разделим прошедшее время на общее: 1,5/3 = 0,5. То есть за полторы секунды кубик прошёл бы 50% маршрута, на который мы ему выделили три секунды.</p>
109
<p>Допустим, мы выдаём кубику на дорогу три секунды, и полторы секунды уже прошло. Разделим прошедшее время на общее: 1,5/3 = 0,5. То есть за полторы секунды кубик прошёл бы 50% маршрута, на который мы ему выделили три секунды.</p>
110
<p>Допустим, прошло уже две с половиной секунды. Посчитаем: 2,5/3 = 0,86. То есть кубик к этому моменту должен преодолеть уже 86% пути.</p>
110
<p>Допустим, прошло уже две с половиной секунды. Посчитаем: 2,5/3 = 0,86. То есть кубик к этому моменту должен преодолеть уже 86% пути.</p>
111
<p>Ну а если прошло три секунды, то мы разделим 3/3 и получим 1. То есть все 100% пути пройдены.</p>
111
<p>Ну а если прошло три секунды, то мы разделим 3/3 и получим 1. То есть все 100% пути пройдены.</p>
112
<p>Это значение от 0 до 1 меняется с течением времени и используется для вычисления текущей позиции объекта.</p>
112
<p>Это значение от 0 до 1 меняется с течением времени и используется для вычисления текущей позиции объекта.</p>
113
<p>Сам параметр прошедшего времени<strong>timeElapsed</strong>мы повышаем каждый кадр на <strong>Time.deltaTime</strong>после выполнения функции<strong>Vector3.Lerp</strong>, чтобы отсчёт прошедшего времени не зависел от частоты кадров.</p>
113
<p>Сам параметр прошедшего времени<strong>timeElapsed</strong>мы повышаем каждый кадр на <strong>Time.deltaTime</strong>после выполнения функции<strong>Vector3.Lerp</strong>, чтобы отсчёт прошедшего времени не зависел от частоты кадров.</p>
114
<p>Вот пример того, как благодаря функции<strong>Vector3.Lerp</strong>будут вести себя три разных объекта, которым надо пройти одно и то же расстояние, но за разное время.</p>
114
<p>Вот пример того, как благодаря функции<strong>Vector3.Lerp</strong>будут вести себя три разных объекта, которым надо пройти одно и то же расстояние, но за разное время.</p>
115
<p><strong>В каких случаях использовать</strong></p>
115
<p><strong>В каких случаях использовать</strong></p>
116
<p>Самый типичный пример - когда надо, чтобы объект переместился в точку на экране, по которой вы кликнули мышкой. Например, когда вы берёте в инвентаре какой-то предмет, а отпустив его, хотите, чтобы он плавно вернулся в свою клетку.</p>
116
<p>Самый типичный пример - когда надо, чтобы объект переместился в точку на экране, по которой вы кликнули мышкой. Например, когда вы берёте в инвентаре какой-то предмет, а отпустив его, хотите, чтобы он плавно вернулся в свою клетку.</p>
117
<p>* * *</p>
117
<p>* * *</p>
118
<p>Создание эффективной и удобной системы управления персонажем - целая наука. А в этом материале мы разобрали лишь основы того, как привести в движение объекты в Unity. Одна только настройка Rigidbody для физически корректного передвижения требует отдельного внимания. Кроме того, мы не затронули вопрос с вращением объектов, с движением по определённому маршруту и много чего ещё. Но об этом мы расскажем в будущих материалах.</p>
118
<p>Создание эффективной и удобной системы управления персонажем - целая наука. А в этом материале мы разобрали лишь основы того, как привести в движение объекты в Unity. Одна только настройка Rigidbody для физически корректного передвижения требует отдельного внимания. Кроме того, мы не затронули вопрос с вращением объектов, с движением по определённому маршруту и много чего ещё. Но об этом мы расскажем в будущих материалах.</p>
119
<a><b>Кто вы в мире геймдева? Узнайте на бесплатном курсе ➞</b>Вы на практике попробуете 3 профессии: геймдизайнера, 2D-художника и разработчика на Unity. Создадите свою первую игру в стиле Mario. Узнать больше</a>
119
<a><b>Кто вы в мире геймдева? Узнайте на бесплатном курсе ➞</b>Вы на практике попробуете 3 профессии: геймдизайнера, 2D-художника и разработчика на Unity. Создадите свою первую игру в стиле Mario. Узнать больше</a>