Привет! За базу возьму предыдущий урок, где мы написали скрипт, который центрировал книгу между двумя точками. Главной проблемой было то, что объект двигал я сам, да ещё и двигал через редактор, а не в режиме игры. Надо это исправить, и для этого мы используем сразу два решения: физика и управление с клавиатуры. В Unity есть компонент, которые называется Rigidbody. У него есть 2D версия, которую мы и будем использовать, т.к. она соответствует проекту/запросу.
Как нам подскажет документация Unity: Rigidbody (твёрдые тела) позволяют вашим игровым объектам взаимодействовать с помощью физики. Для реалистичного перемещения твёрдых тел, на последние воздействуют сила вращения и другие силы. Любой игровой объект должен содержать в себе твёрдое тело, чтобы быть подверженным гравитации, действовать согласно назначенным путём скриптинга силам, или взаимодействовать с другими объектами через физический движок NVIDIA PhysX.
Т.е. этот компонент делает объект физическим телом, на которое будут действовать различные силы (в т.ч. гравитация).
Но сам по себе Rigidbody не даёт представления о физических размерах объекта. Для этого существую различные коллайдеры:
Как понятно из названий, 2D версии подойдут 2D проектам, тогда как в 3D играх вы будете использовать объемные коллайдеры.
И от теории перейдём к практике.
Добавляем Rigidbody
Итак. Чтобы добавить физики выбранному объекту, нам нужно в инспекторе (при помощи Add Component) добавить Rigidbody 2D. Я хочу сделать физической точку Finish:
Новый компонент имеет такой вид:
Нам предложены поля для редактирования массы тела, выбор физического материала, сила (величина) гравитации, сопротивление при перемещении и т.д. Русская документация устарела, но английская в норме. Body Type может быть Dynamic (дефолтное), Kinematic или Static. В зависимости от выбранного типа следующие параметры могут отличаться. Но нас сейчас интересует именно Dynamic, который и предоставляет необходимую физику для тела.
Посмотрим, что из этого вышло?
Но куда интереснее будет сделать так, чтобы наша точка не падала в бездну. Добавим на сцену пустой спрайт. А в него: Box Collider 2D.
В Finish добавим Circle Collider 2D, который и определит размеры физического тела.
Box Collider 2D это буквально коробка коллайдер, который и поможет нам сделать препятствие для падающего диска. А Circle Collider это более сложный круглый коллайдер. Как вы понимаете, круглый коллайдер имеет несколько больше точек взаимодействия, так что по возможности лучше использовать более простые (тот же box).
Меняем размер платформы при помощи Transform, а заодно меняем и её местоположение, чтобы словить падающий диск. Коллайдер внутри должен автоматически соответствовать новому размеру.
Пробуем запускать.
Вот теперь всё работает отлично!
Можно ради интереса добавить других стеночек, да ещё и с разными углами наклона.
Физика работает, как и планировалось, теперь пора докинуть элементы правлены.
Немного о работе с Visual Studio
И отвлечемся для важного момента пользователей VS. Если мы просто открываем в студии скрипты, то их можно отредактировать и сохранить, но полноценным это использование назвать сложно. Например, подсказки будут минимальными и не будут касаться API Unity. Чтобы в полной мере насладиться написанием кода в IDE нам нужно открывать не скрипты отдельно, а создать VS проект. Сделать это можно прямо в Unity.
Должна открыться папка с файлом проекта .sln. Он то нам и нужен, запускаем и получаем свой проект с доступом к другим скриптам и подсказкам.
Кстати, новые скрипты могут быть не включены в проект, для этого нужно в Обозревателе решений отобразить все файлы и включить новые скрипты в проект:
Управляем движением объекта при помощи скрипта
Чтобы реализовать управление, нам нужно будет создать ещё один скрипт. Сделаем это. Пускай это будет какой-то UserControlScript. Управление физическим объектом может быть реализовано при помощи передаваемых направленных импульсов (Force) компоненту Rigidbody. Это можно делать при помощи векторов, умноженных на некоторую силу.
Но для начала нам нужно получить сам компонент Rigidbody 2D. Как вы уже знаете, можно создать публичную переменную, а уже в редакторе просто перетянуть на неё компонент (мы перетягивали объекты в прошлый раз, но с компонентами система та же).
Но такое решение мне не особо нравится. Так как и скрипт, и Rigidbody 2D находятся внутри одного объекта, а значит мы можем получить ссылку на объект Rigidbody при помощи функции GetComponent.
1 2 3 4 5 6 |
private Rigidbody2D rigidbody2D; void Start() { rigidbody2D = GetComponent<Rigidbody2D>(); } |
Если такой компонент есть, то функция его вернет. Если такого нет, то функция вернет null. Это стоит учитывать.
Также надо добавить пару публичных float переменных для определения величины силы прыжка, jumpForce, и силы для при движении в левую/правую стороны, это будет просто force.
И давайте проверим что у нас получилось, добавив в функцию Update такую строку:
1 2 3 4 |
void Update() { rigidbody2D.AddForce(Vector2.up * jumpForce); } |
Вешаем скрипт на объект Finish и указываем силу 2.5, например.
Запускаем и видим, что теперь наш объект, поборов гравитацию, улетает за пределы видимости (да, с камерой мы пока не работали).
Значит всё работает! Теперь добавим управление с клавиатуры.
Перемещаем объект с клавиатуры
И сразу задам важный вопрос. Помните ли вы описание функции Update? Обновление каждый кадр. Это значит, что если у нас FPS 60, то объект получал 60 импульсов в секунду, а если FPS 5? Или 180? Тут и кроется главная проблема этой функции. Скорость движения объекта стаёт зависимой от FPS. Такое решение нам не подходит.
Что же использовать тогда? В Unity есть подобная функция, которая обновляется через фиксированные промежутки времени без привязки к FPS, она так и называется: FixedUpdate.
В этой функции мы должны прописывать любое продолжительное движение, тогда как импульсы, вроде прыжка, можно оставить в обычной Update, и я далее объясню почему.
А пока скопируем код Update из прошлого абзаца и немного перепишем его:
1 2 3 4 5 6 7 8 9 10 11 |
void FixedUpdate() { if (Input.GetKey(KeyCode.A)) { rigidbody2D.AddForce(Vector2.left * force); } if (Input.GetKey(KeyCode.D)) { rigidbody2D.AddForce(Vector2.right * force); } } |
Тут вы видите ещё и новое условие if. Это проверка на нажатие клавиш A и D. Таким образом при выборе разных констант KeyCode мы можем проверить большую часть возможных клавиш на клавиатуре. И если они нажаты – выполнить действие.
Тут есть ещё один момент. Теперь сила будет применена без задержки FPS, но с периодичностью выполнения FixedUpdate. Чтобы получить силу, которая не будет зависеть от времени выполнения FixedUpdate, нам нужно умножить силу на Time.fixedDeltaTime.
1 2 3 4 5 6 7 8 9 10 11 |
void FixedUpdate() { if (Input.GetKey(KeyCode.A)) { rigidbody2D.AddForce(Vector2.left * force * Time.fixedDeltaTime); } if (Input.GetKey(KeyCode.D)) { rigidbody2D.AddForce(Vector2.right * force * Time.fixedDeltaTime); } } |
Такая конструкция будет давать одинаковую скорость перемещения объекта как на слабом железе, с низким FPS, так и не более мощном с высоким. При этом если вы решите изменить частоту выполнения FixedUpdate, то это тоже не скажется на перемещении объекта.
Проверим, работает ли? Для теста я указал силу равную 500, но тут уж подбирайте как вам понравится.
И прыжок
И добавим прыжок. Только GetKey срабатывает всё время, пока нажата кнопка. Это будет странный прыжок. Но чтобы это исправить — пишем не Input.GetKey(KeyCode.Space), а Input.GetKeyDown(KeyCode.Space). Так функция будет выполнена только один раз, при нажатии пробела.
Но есть проблема. GetKeyDown не будет нормально работать в FixedUpdate. Всё дело в реализации функции, которая привязана к кадрам, от которых отвязана FixedUpdate. Из-за этого очень часто прыжок может просто не срабатывать. Потому-то мы и пропишем это условие в обычном Update. Заодно укажем, что это будет импульс.
1 2 3 4 5 6 |
void Update() { if (Input.GetKeyDown(KeyCode.Space)) { rigidbody2D.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse); } } |
Упомянутая выше проблема скорости и FPS не будет касаться прыжка, так как это не постоянное продолжающееся движение. Это разовый импульс, который сработает одинаково не смотря на FPS или что-либо.
Проверяем что у нас получилось:
Отлично. Итоговый код скрипта для перемещения вот:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public class UserControlScript : MonoBehaviour { private Rigidbody2D rigidbody2D; public float force; public float jumpForce; void Start() { rigidbody2D = GetComponent<Rigidbody2D>(); } void FixedUpdate() { if (Input.GetKey(KeyCode.A)) { rigidbody2D.AddForce(Vector2.left * force * Time.fixedDeltaTime); } if (Input.GetKey(KeyCode.D)) { rigidbody2D.AddForce(Vector2.right * force * Time.fixedDeltaTime); } } void Update() { if (Input.GetKeyDown(KeyCode.Space)) { rigidbody2D.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse); } } } |
Спасибо за внимание и до скорого!
Пингбэк: Уроки по Unity – GeekStand