Продолжим разбирать практические задания с unity3dstudent.com. На очереди последняя на данный момент задачка. Статья слегка задержалась, но, надеюсь, будет кому-то полезна.
Вот ссылка на оригинальное задание: www.unity3dstudent.com/2010/07/challenge-c03-beginner

Суть: игрок должен уметь перемещаться вправо/влево и стрелять по трём мишеням. Мишени при попадании должны падать, а игроку за каждую сбитую мишень начисляется очко. По достижении трёх очков показывается экран окончания игры.
Первая задача.
Вторая задача.
Добавим на сцену плоскость – пол, на который будут падать кубы-мишени, её размер пока изменять не будем. Предположим, что игрок будет стрелять из точки положения камеры в том же направлении, куда она смотрит. Для простоты предположим, что стрелять будем вдоль оси OX (напоминаю, направления осей показано в правом верхнем углу вкладки Scene).
Необходимо в соответствии с этим расположить камеру. Выберем на сцене плоскость. Если нажать на ось OX (на отвечающий ей конус-кнопку на первом скриншоте ниже), то мы «посмотрим» на выбранный объект (то есть плоскость) в направлении, параллельном оси OX (см. второй скриншот).

Скриншот 1

Скриншот 2
Не удивляйтесь, что перестала быть видна сама плоскость, так и должно быть. Теперь поставим камеру в ту же точку, из которой смотрим: выбираем в панели иерархии объект Main Camera, нажимаем GameObject -> Align With View (или нажмите Ctrl+Shift+F). Теперь из камеры видно плоскость в нужном ракурсе, но далековато, да и камера слишком низко расположена (при выбранном объекте камеры в правом нижнем углу вкладки Scene – предпросмотр вида камеры). На обведённое кругом пока не обращайте внимания.

Вернёмся к «нормальному» виду на сцену: для этого нужно выделить на сцене плоскость и нажать на кубик между стрелками осей в верхнем правом углу (сейчас он виден как квадрат) – он обведён красным кругом на скриншоте выше. Получим что-то вроде этого (может быть нужно будет удалиться от плоскости, покрутив колёсико мыши, чтобы камера попала в поле зрения):

На скриншоте выделена камера.
Теперь поднимем камеру повыше и пододвинем к плоскости (можно даже так, что ближний край плоскости станет не виден):

С камерой пока всё. Добавим в сцену освещение – точечный источник света (можно и любой другой, здесь цель добавления освещения – просто сделать сцену «посветлее», не более того; для красивого освещения, возможно, придётся использовать другие источники света) и приподнимем его:

Замечу, что у точечных источников света есть несколько редактируемых параметров. В частности, нас интересуют Intensity и Range. Первый – интенсивность света – отвечает за то, как ярко светит источник. Второй – дальность действия. То есть источник света может светить очень ярко, но лишь в небольшом объёме. И сколько бы мы не меняли параметр Intensity, дальше заданного радиуса свет от источника распространяться не будет.
По умолчанию выставляются параметры Range = 10 и Intensity = 1. Если выделить объект источника света, на сцене вокруг него обрисовывается шар – это и есть объём, в котором «светит» источник. В моём случае плоскость лежала внутри этого объема, так что Range можно не менять, но плоскость была как-то тускловато освещена, поэтому я увеличил Intensity до 4 (на скриншоте выше это уже сделано).
Создадим теперь префаб для мишени в панели Project, назовём его target (можно, конечно, обойтись и тремя отдельными объектами, но это неудобно, да и вдруг понадобится не три, а сто таких мишеней?). Добавим в сцену куб. Слегка разукрасим его, добавив текстуру. В прошлом разборе мы создавали материал с некоторой текстурой, почем бы не использовать его ещё раз? Если не помните, как создать материал – загляните в разбор предыдущей задачки.

Теперь из панели иерархии перетащим этот куб на префаб в панели Project, а сам куб удалим. Создадим три копии (или «образца»; по-английски, насколько я понимаю, это называется instance) префаба. Разместим их все примерно на той же высоте, что и камера:

Положение мишеней можно (и, может быть, придётся) подкорректировать и уже после того, как игрок научится стрелять (если невозможно будет попасть по мишеням).
Можно было заметить, что мишени у нас пока что без компонента Rigidbody, они же не должны падать до того, как в них попали. А как понадобится – мы сразу добавим им этот компонент и они упадут (в уроках с того же www.unity3dstudent.com неявно предлагается именно такой вариант, о другом способе – ближе к концу статьи, в третьей части).
Основная сцена готова, экран завершения игры опять же будет позже.
Сначала определимся, какие скрипты нам нужны.
Во-первых, нужно из точки расположения камеры стрелять по мишеням, в том же скрипте (он будет «повешен» на саму камеру) будем двигать её вправо/влево. Этот скрипт назовём Playerscript.
Ещё к префабу мишени нужно прикрепить скрипт, который будет при соударении добавлять компонент Rigidbody и засчитывать игроку очко. Это будет Rigidbodytizer.
Но кто будет вести счёт? Можно было бы делать это в скрипте для камеры, но как-то некрасиво получается. Добавим в сцену пустой объект (GameObject->Create Empty) и назовём его GameManager – он будет считать набранные очки и при нужном количестве переходить на другую сцену – экран окончания игры. Этому объекту тоже нужен свой скрипт, пусть называется так же: GameManager.
Сначала разберёмся с тем, что нужно в первую очередь: скрипт GameManager. По сути нам нужны только метод Update для проверки счёта и пара переменных для хранения текущего счёта и количества очков, которые нужно набрать для победы (можно, конечно, обойтись использованием числа 3, но если захочется модифицировать игру, всплывут некоторые неудобства).
Переменная score описана как public static, так как в этом случае к ней можно будет обращаться из любого скрипта как GameManager.score. Конечно, не очень хорошо, что переменная, в которой хранится значение счёта, является public-членом, но для упрощения скриптовой логики оставим так. Не забудьте прикрепить только что написанный скрипт к объекту GameManager.
Теперь займёмся скриптом Rigidbodytizer. Методы Update или Start нам не понадобятся, добавим метод OnCollisionEnter():
Условие проверяет, добавлен ли уже компонент Rigidbody к объекту, и если нет, добавляет его и увеличивает число засчитанных очков. Обратите внимание, увеличение числа очков – тоже внутри условия! Иначе очко засчитается даже за соударение мишени с полом. Теперь прикрепим Rigidbodytizer к префабу target.
Осталось добавить стрелка – скрипт Playerscript. Но для начала нужен префаб «пули» — им и будем стрелять. Добавляем в проект префпаб “bullet”, а в сцену – сферу. Последнюю сожмём до размеров, например, 0.4 по всем измерениям: в Inspector у компонента Transform изменим все значения поля Scale на 0.4.
Можно добавить материал и сфере, это опционально. Перетащим сферу из панели Hierarchy на префаб bullet в панели Project и удалим сферу со сцены.
Как мы будем двигать камеру? Скорее, вопрос не «как» (поменять transform.Position – не проблема), а «в соответствии с чем?». У класса Input есть замечательный статический метода GetAxis, который по названию оси выдаёт некое float-значение. В том числе для строк “Horizontal” и “Vertical” мы получим значение от -1 до 1, показывающее степень отклонения от, соответственно, вертикальной и горизонтальной осей. Это сработает для стрелок и WASD на клавиатуре и для джойстика. При этом если для джойстика фраза про «отклонение от оси» имеет смыл, то для клавиатуры, как я понял, большую часть времени при нажатой клавише возвращается +1 или -1 (в зависимости от направления), хотя и есть некоторая инертность при нажатии и отпускании клавиши (значение, возвращаемое методом Input.GetAxis, проходит через промежуточное значение). Более подробно про этот метод – здесь.
С помощью полученной информации заставим камеру двигаться:
Public переменная speed отвечает за максимальную скорость движения камеры. Значение, возвращаемое Input.GetAxis умножаем на Time.deltaTime для того, чтобы камера двигалась не быстрее speed метров в секунду, а не «метров за кадр». А bulletPrefab – префеб пули, которым будем стрелять. force – модуль силы, с которой будем эту самую пулю «кидать».
Прикрепим скрипт к камере, запустим сцену и попробуем нажимать стрелки вправо/влево. Всё отлично, пора и пострелять!
Добавим выстрел при нажатии на клавишу прыжка (пробел по умолчанию) в метод Update:
Не забудем выбрать объект MainCamera и в панели Inspector в поле Bullet Prefab компонента Palyerscript (Script) перетащить префаб bullet из панели Project.
Можно запустить сцену – игра почти готова. Осталось сделать переход на другую сцену по окончании игры. Для этого нужно сначала создать новую сцену и обязательно включить её в список сцен для сборки.
Добавляем в проект новую сцену, сразу сохраним её под именем “win”. Выберем File -> Build Settings. Потом нужно нажать кнопку Add Current в появившемся окне (можно и перетащить сцену из панели Project в список Scenes In Build). Поменяем цвет фона у объекта Main Camera на какой-нибудь более оптимистичный:

Теперь нужно добавить текст (например, “You win!”). Нажимаем GameObject -> Create Other -> GUI Text. Появится новый объект с текстом по умолчанию “Gui Text”. Изменим поле Text компонента GUIText на нужный, также поменяем размер шрифта (поле Font Size). Но текст оказывается не по центру экрана:

На картинке выделена и причина такого поведения текста. Свойство Anchor показывает, где у текста «центр». Не физический центр, а тот центр, которым определяется положение текста. Сейчас выбрано значение “Upper left”, то есть текст «подвешен» за верхний левый угол. Как можно заметить, этот угол и в самом деле находится в центре экрана. Выберем значение «Middle center», и текст переместится в положенное место по середине экрана.
Почему именно середина экрана, и что делать, если нужно сместить текст? У GUIText есть ещё и группа полей Pixel Offset с полями X и Y. Задавая им нужные значения можно смещать текст на указанное количество пикселей относительно центра экрана (точнее, центра окна с игрой).
Добавим переход на эту сцену в нужный момент времени. Вернёмся на основную сцену и подправим метод Update в скрипте GameManager:
Это загрузит недавно созданную сцену при достижении нужного счёта.
На этом основная часть заканчивается, получилось вполне играбельно. Стоило бы, конечно, добавить надпись с текущим результатом в основную сцену, но пусть это будет небольшим заданием :) Замечу, что досутп к тексте объекта, имеющего компонент GUIText осуществляется так:
И снова ощущение, что в сцене что-то не так! Что сделает порядочный кубик, если в него врежется порядочная пуля (хотя у нас это, скорее, ядро)? Правильно, отлетит! Но почему же не отлетает сейчас? Ответ вполне логичен.
Когда в методе OnCollisionEnter мы добавляем мишени компонент Rigidbody, ядро уже столкнулось с мишенью без Rigidbody. Но нет же метода, вызывающегося, когда «ещё чуть-чуть, и будет столкновение»! Поэтому проблема решается проще.
Что по сути должно происходить? Мишень висит в воздухе. Как такое возможно? Если она не имеет массы? Начнём с того, что так не бывает. А зачем нам отсутствие массы? Чтобы на тело не действовала сила притяжения к земле. Так может это выключается?
Прошлый абзац – от начала и до конца лирическое отступление, на деле обошлось без этих рассуждений. Я просто добавил префабу мишени компонент Rigidbody и случайно заметил там поле Use Gravity. Вот и нашёлся тот самый выключатель силы тяжести.
Кстати, если запустить игру сейчас (проделав только описанное абзацем выше), получится забавный эффект: мишени разлетаются, но не падают вниз под действием силы тяжести.
Пришла пора изменить скрипт Rigidbodytizer (теперь он уже, правда, не оправдывает своего названия).
Ну вот, другое дело. Теперь игра выгладит как-то так:
Второй момент, который хотелось бы затронуть: управление камерой. Движение камеры только вдоль одной прямой накладывает значительные ограничения на положение кубиков (чтобы их ещё и сбить можно было). По аналогии с движением вправо/влево добавьте движение вверх/вниз (придётся использовать
Напоследок отмечу, что если размещать мишени только над плоскостью, логично было бы ограничить движение вправо/влево границами плоскости, внеся небольшие изменения в Playerscript. Добавим парочку переменных и метод Start (пояснения в коде):
Конечно, можно перевычислять ограничения на координату Z каждый кадр, но это неэффективно, а в случае неподвижной плоскости ещё и бессмысленно.
Теперь изменим метод Update: после изменения вектора transform.position добавим ещё строчку:
К сожалению, отдельно поменять координату Z вектора transform.position нельзя. Функция Mathf.Clamp(x, a, b) возвращает значение x, если зажать его между значениями a и b (то есть если, например, x меньше a, то функция вернёт значение a)
Если вы добавили перемещение вверх/вниз, убедитесь схожим образом, что камера не уходит ниже плоскости и не поднимается слишком высоко.
Похожими рассуждениями «а что, если добавить сюда XYZ?» и с помощью идей тестировавших игру друзей я модифицировал игру до примерно вот такого состояния:
Если будет интерес, могу описать,я докатился до жизни такой как превратить набросок игры из этой статьи в набросок игры из видео.
На этом заканчиваются имеющиеся на данный момент задачи, сформулированные здесь, а с ними и этот цикл статей.
До новых встреч.
Вот ссылка на оригинальное задание: www.unity3dstudent.com/2010/07/challenge-c03-beginner

Суть: игрок должен уметь перемещаться вправо/влево и стрелять по трём мишеням. Мишени при попадании должны падать, а игроку за каждую сбитую мишень начисляется очко. По достижении трёх очков показывается экран окончания игры.
Первая задача.
Вторая задача.
Часть 1: Сцена.
Добавим на сцену плоскость – пол, на который будут падать кубы-мишени, её размер пока изменять не будем. Предположим, что игрок будет стрелять из точки положения камеры в том же направлении, куда она смотрит. Для простоты предположим, что стрелять будем вдоль оси OX (напоминаю, направления осей показано в правом верхнем углу вкладки Scene).
Необходимо в соответствии с этим расположить камеру. Выберем на сцене плоскость. Если нажать на ось OX (на отвечающий ей конус-кнопку на первом скриншоте ниже), то мы «посмотрим» на выбранный объект (то есть плоскость) в направлении, параллельном оси OX (см. второй скриншот).

Скриншот 1

Скриншот 2
Не удивляйтесь, что перестала быть видна сама плоскость, так и должно быть. Теперь поставим камеру в ту же точку, из которой смотрим: выбираем в панели иерархии объект Main Camera, нажимаем GameObject -> Align With View (или нажмите Ctrl+Shift+F). Теперь из камеры видно плоскость в нужном ракурсе, но далековато, да и камера слишком низко расположена (при выбранном объекте камеры в правом нижнем углу вкладки Scene – предпросмотр вида камеры). На обведённое кругом пока не обращайте внимания.

Вернёмся к «нормальному» виду на сцену: для этого нужно выделить на сцене плоскость и нажать на кубик между стрелками осей в верхнем правом углу (сейчас он виден как квадрат) – он обведён красным кругом на скриншоте выше. Получим что-то вроде этого (может быть нужно будет удалиться от плоскости, покрутив колёсико мыши, чтобы камера попала в поле зрения):

На скриншоте выделена камера.
Теперь поднимем камеру повыше и пододвинем к плоскости (можно даже так, что ближний край плоскости станет не виден):

С камерой пока всё. Добавим в сцену освещение – точечный источник света (можно и любой другой, здесь цель добавления освещения – просто сделать сцену «посветлее», не более того; для красивого освещения, возможно, придётся использовать другие источники света) и приподнимем его:

Замечу, что у точечных источников света есть несколько редактируемых параметров. В частности, нас интересуют Intensity и Range. Первый – интенсивность света – отвечает за то, как ярко светит источник. Второй – дальность действия. То есть источник света может светить очень ярко, но лишь в небольшом объёме. И сколько бы мы не меняли параметр Intensity, дальше заданного радиуса свет от источника распространяться не будет.
По умолчанию выставляются параметры Range = 10 и Intensity = 1. Если выделить объект источника света, на сцене вокруг него обрисовывается шар – это и есть объём, в котором «светит» источник. В моём случае плоскость лежала внутри этого объема, так что Range можно не менять, но плоскость была как-то тускловато освещена, поэтому я увеличил Intensity до 4 (на скриншоте выше это уже сделано).
Создадим теперь префаб для мишени в панели Project, назовём его target (можно, конечно, обойтись и тремя отдельными объектами, но это неудобно, да и вдруг понадобится не три, а сто таких мишеней?). Добавим в сцену куб. Слегка разукрасим его, добавив текстуру. В прошлом разборе мы создавали материал с некоторой текстурой, почем бы не использовать его ещё раз? Если не помните, как создать материал – загляните в разбор предыдущей задачки.

Теперь из панели иерархии перетащим этот куб на префаб в панели Project, а сам куб удалим. Создадим три копии (или «образца»; по-английски, насколько я понимаю, это называется instance) префаба. Разместим их все примерно на той же высоте, что и камера:

Положение мишеней можно (и, может быть, придётся) подкорректировать и уже после того, как игрок научится стрелять (если невозможно будет попасть по мишеням).
Можно было заметить, что мишени у нас пока что без компонента Rigidbody, они же не должны падать до того, как в них попали. А как понадобится – мы сразу добавим им этот компонент и они упадут (в уроках с того же www.unity3dstudent.com неявно предлагается именно такой вариант, о другом способе – ближе к концу статьи, в третьей части).
Основная сцена готова, экран завершения игры опять же будет позже.
Часть 2: Скрипты.
Сначала определимся, какие скрипты нам нужны.
Во-первых, нужно из точки расположения камеры стрелять по мишеням, в том же скрипте (он будет «повешен» на саму камеру) будем двигать её вправо/влево. Этот скрипт назовём Playerscript.
Ещё к префабу мишени нужно прикрепить скрипт, который будет при соударении добавлять компонент Rigidbody и засчитывать игроку очко. Это будет Rigidbodytizer.
Но кто будет вести счёт? Можно было бы делать это в скрипте для камеры, но как-то некрасиво получается. Добавим в сцену пустой объект (GameObject->Create Empty) и назовём его GameManager – он будет считать набранные очки и при нужном количестве переходить на другую сцену – экран окончания игры. Этому объекту тоже нужен свой скрипт, пусть называется так же: GameManager.
Сначала разберёмся с тем, что нужно в первую очередь: скрипт GameManager. По сути нам нужны только метод Update для проверки счёта и пара переменных для хранения текущего счёта и количества очков, которые нужно набрать для победы (можно, конечно, обойтись использованием числа 3, но если захочется модифицировать игру, всплывут некоторые неудобства).
using UnityEngine;
using System.Collections;
public class GameManager : MonoBehaviour
{
public uint neededScore = 3;
public static uint score = 0;
// Update is called once per frame
void Update ()
{
if (score == neededScore)
{
// будет добавлено позже
}
}
}
Переменная score описана как public static, так как в этом случае к ней можно будет обращаться из любого скрипта как GameManager.score. Конечно, не очень хорошо, что переменная, в которой хранится значение счёта, является public-членом, но для упрощения скриптовой логики оставим так. Не забудьте прикрепить только что написанный скрипт к объекту GameManager.
Теперь займёмся скриптом Rigidbodytizer. Методы Update или Start нам не понадобятся, добавим метод OnCollisionEnter():
void OnCollisionEnter()
{
if (rigidbody == null)
{
gameObject.AddComponent("Rigidbody");
GameManager.score++;
}
}
Условие проверяет, добавлен ли уже компонент Rigidbody к объекту, и если нет, добавляет его и увеличивает число засчитанных очков. Обратите внимание, увеличение числа очков – тоже внутри условия! Иначе очко засчитается даже за соударение мишени с полом. Теперь прикрепим Rigidbodytizer к префабу target.
Осталось добавить стрелка – скрипт Playerscript. Но для начала нужен префаб «пули» — им и будем стрелять. Добавляем в проект префпаб “bullet”, а в сцену – сферу. Последнюю сожмём до размеров, например, 0.4 по всем измерениям: в Inspector у компонента Transform изменим все значения поля Scale на 0.4.
Можно добавить материал и сфере, это опционально. Перетащим сферу из панели Hierarchy на префаб bullet в панели Project и удалим сферу со сцены.
Как мы будем двигать камеру? Скорее, вопрос не «как» (поменять transform.Position – не проблема), а «в соответствии с чем?». У класса Input есть замечательный статический метода GetAxis, который по названию оси выдаёт некое float-значение. В том числе для строк “Horizontal” и “Vertical” мы получим значение от -1 до 1, показывающее степень отклонения от, соответственно, вертикальной и горизонтальной осей. Это сработает для стрелок и WASD на клавиатуре и для джойстика. При этом если для джойстика фраза про «отклонение от оси» имеет смыл, то для клавиатуры, как я понял, большую часть времени при нажатой клавише возвращается +1 или -1 (в зависимости от направления), хотя и есть некоторая инертность при нажатии и отпускании клавиши (значение, возвращаемое методом Input.GetAxis, проходит через промежуточное значение). Более подробно про этот метод – здесь.
С помощью полученной информации заставим камеру двигаться:
public class Playerscript : MonoBehaviour
{
public const float speed = 7;
public GameObject bulletPrefab;
public float force = 1500;
// Update is called once per frame
void Update ()
{
float translation = Input.GetAxis("Horizontal") * Time.deltaTime * speed;
transform.position += new Vector3(0, 0, translation);
}
}
Public переменная speed отвечает за максимальную скорость движения камеры. Значение, возвращаемое Input.GetAxis умножаем на Time.deltaTime для того, чтобы камера двигалась не быстрее speed метров в секунду, а не «метров за кадр». А bulletPrefab – префеб пули, которым будем стрелять. force – модуль силы, с которой будем эту самую пулю «кидать».
Прикрепим скрипт к камере, запустим сцену и попробуем нажимать стрелки вправо/влево. Всё отлично, пора и пострелять!
Добавим выстрел при нажатии на клавишу прыжка (пробел по умолчанию) в метод Update:
if (Input.GetButtonDown("Jump"))
{
GameObject bullet = GameObject.Instantiate(bulletPrefab, transform.position, transform.rotation) as GameObject;
bullet.rigidbody.AddForce(transform.forward * force);
}
Не забудем выбрать объект MainCamera и в панели Inspector в поле Bullet Prefab компонента Palyerscript (Script) перетащить префаб bullet из панели Project.
Можно запустить сцену – игра почти готова. Осталось сделать переход на другую сцену по окончании игры. Для этого нужно сначала создать новую сцену и обязательно включить её в список сцен для сборки.
Добавляем в проект новую сцену, сразу сохраним её под именем “win”. Выберем File -> Build Settings. Потом нужно нажать кнопку Add Current в появившемся окне (можно и перетащить сцену из панели Project в список Scenes In Build). Поменяем цвет фона у объекта Main Camera на какой-нибудь более оптимистичный:

Теперь нужно добавить текст (например, “You win!”). Нажимаем GameObject -> Create Other -> GUI Text. Появится новый объект с текстом по умолчанию “Gui Text”. Изменим поле Text компонента GUIText на нужный, также поменяем размер шрифта (поле Font Size). Но текст оказывается не по центру экрана:

На картинке выделена и причина такого поведения текста. Свойство Anchor показывает, где у текста «центр». Не физический центр, а тот центр, которым определяется положение текста. Сейчас выбрано значение “Upper left”, то есть текст «подвешен» за верхний левый угол. Как можно заметить, этот угол и в самом деле находится в центре экрана. Выберем значение «Middle center», и текст переместится в положенное место по середине экрана.
Почему именно середина экрана, и что делать, если нужно сместить текст? У GUIText есть ещё и группа полей Pixel Offset с полями X и Y. Задавая им нужные значения можно смещать текст на указанное количество пикселей относительно центра экрана (точнее, центра окна с игрой).
Добавим переход на эту сцену в нужный момент времени. Вернёмся на основную сцену и подправим метод Update в скрипте GameManager:
void Update ()
{
if (score == neededScore)
{
Application.LoadLevel("win");
}
}
Это загрузит недавно созданную сцену при достижении нужного счёта.
На этом основная часть заканчивается, получилось вполне играбельно. Стоило бы, конечно, добавить надпись с текущим результатом в основную сцену, но пусть это будет небольшим заданием :) Замечу, что досутп к тексте объекта, имеющего компонент GUIText осуществляется так:
guiText.text = "Hello world!";
Часть 3: Эстетическая.
И снова ощущение, что в сцене что-то не так! Что сделает порядочный кубик, если в него врежется порядочная пуля (хотя у нас это, скорее, ядро)? Правильно, отлетит! Но почему же не отлетает сейчас? Ответ вполне логичен.
Когда в методе OnCollisionEnter мы добавляем мишени компонент Rigidbody, ядро уже столкнулось с мишенью без Rigidbody. Но нет же метода, вызывающегося, когда «ещё чуть-чуть, и будет столкновение»! Поэтому проблема решается проще.
Что по сути должно происходить? Мишень висит в воздухе. Как такое возможно? Если она не имеет массы? Начнём с того, что так не бывает. А зачем нам отсутствие массы? Чтобы на тело не действовала сила притяжения к земле. Так может это выключается?
Прошлый абзац – от начала и до конца лирическое отступление, на деле обошлось без этих рассуждений. Я просто добавил префабу мишени компонент Rigidbody и случайно заметил там поле Use Gravity. Вот и нашёлся тот самый выключатель силы тяжести.
Кстати, если запустить игру сейчас (проделав только описанное абзацем выше), получится забавный эффект: мишени разлетаются, но не падают вниз под действием силы тяжести.
Пришла пора изменить скрипт Rigidbodytizer (теперь он уже, правда, не оправдывает своего названия).
void OnCollisionEnter()
{
if (!rigidbody.useGravity)
{
rigidbody.useGravity = true;
GameManager.score++;
}
}
Ну вот, другое дело. Теперь игра выгладит как-то так:

Второй момент, который хотелось бы затронуть: управление камерой. Движение камеры только вдоль одной прямой накладывает значительные ограничения на положение кубиков (чтобы их ещё и сбить можно было). По аналогии с движением вправо/влево добавьте движение вверх/вниз (придётся использовать
Input.GetAxis("Vertical")
). Тогда можно будет размещать кубики-мишени где угодно в пределах видимости, и в них можно будет попасть.Напоследок отмечу, что если размещать мишени только над плоскостью, логично было бы ограничить движение вправо/влево границами плоскости, внеся небольшие изменения в Playerscript. Добавим парочку переменных и метод Start (пояснения в коде):
float zBoundMin, zBoundMax; // ограничения на перемещение вдоль оси Z
void Start()
{
// ограничивающий параллелепипед объекта с названием "Plane"
Bounds planeBounds = GameObject.Find("Plane").collider.bounds;
zBoundMin = planeBounds.min.z; // минимальное значение Z
zBoundMax = planeBounds.max.z; //максимальное значение Z
}
Конечно, можно перевычислять ограничения на координату Z каждый кадр, но это неэффективно, а в случае неподвижной плоскости ещё и бессмысленно.
Теперь изменим метод Update: после изменения вектора transform.position добавим ещё строчку:
transform.position = new Vector3(transform.position.x,transform.position.y, Mathf.Clamp(transform.position.z, zBoundMin, zBoundMax));
К сожалению, отдельно поменять координату Z вектора transform.position нельзя. Функция Mathf.Clamp(x, a, b) возвращает значение x, если зажать его между значениями a и b (то есть если, например, x меньше a, то функция вернёт значение a)
Если вы добавили перемещение вверх/вниз, убедитесь схожим образом, что камера не уходит ниже плоскости и не поднимается слишком высоко.
Часть 4: Заключительная.
Похожими рассуждениями «а что, если добавить сюда XYZ?» и с помощью идей тестировавших игру друзей я модифицировал игру до примерно вот такого состояния:
Если будет интерес, могу описать,
На этом заканчиваются имеющиеся на данный момент задачи, сформулированные здесь, а с ними и этот цикл статей.
До новых встреч.