Написать пост

Пишем арканоид на Unity. Поведение блоков, префабы и дизайн уровней

Аватар Типичный программист

В этой статье мы продолжаем серию уроков о написании простой игры на Unity — классического арканоида. Использовать будем только 2D инструменты, предоставляемые нам движком. В каждой из статей мы затронем один из аспектов написания игры, а в этой настроим поведение игровых блоков и создадим префабы.

Где мы остановились?

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

Прилагаем список всех уроков:

  1. Настройка проекта.
  2. Механика мяча и платформы.
  3. Поведение блоков, префабы и дизайн уровней.
  4. Добавление звуков и новых уровней.

Превью результата

Ну и по традиции — посмотрим на игру, которая должна получиться у нас по итогам всей серии.

А вот игра, которую мы напишем в этой статье.

Скрипт для блока

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

  • об общем количестве ударов, которое может выдержать блок;
  • об очках, которые получит игрок при уничтожении блока.

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

  1. Переменная для хранения количества выдерживаемых ударов.
  2. Переменная для хранения количества очков, которые заработает игрок при уничтожении этого блока.
  3. Переменная для хранения количества уже принятых на себя ударов.

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

Итак, создадим новый скрипт под название BlockScript, объявим наши переменные и проинициализируем нулем переменную, отвечающую за количество принятых ударов:

			public class BlockScript : MonoBehaviour {
 
    public int hitsToKill:
    public int points;
    private int numberOfHits;
 
    // используйте этот метод для инициализации
    void Start () {
        numberOfHits = 0;
    }
     
    // Update вызывается при отрисовке каждого кадра игры
    void Update () {
     
    }
}
		

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

Для того, чтобы воспользоваться этой функцией, мы должны пометить шар специальным тегом. В редакторе Unity на вкладке Hierarchy выберите мяч, перейдите в окно Inspector и найдите поле Tag. В данный момент объект не имеет тега, а потому в этом поле значится Untagged. Кликните по нему и выберите Add Tag.

Пишем арканоид на Unity. Поведение блоков, префабы и дизайн уровней 1

После этого откроется новый интерфейс:

Пишем арканоид на Unity. Поведение блоков, префабы и дизайн уровней 2

В этой статье мы сосредоточимся на свойстве Element 0, которое определят название тега. Свойству Size задайте 2.

Пишем арканоид на Unity. Поведение блоков, префабы и дизайн уровней 3

Отлично. Теперь заново в окне Inspector кликните по полю Tag и в списке кликните на добавленный нами тег Ball.

Пишем арканоид на Unity. Поведение блоков, префабы и дизайн уровней 4

Откройте ранее созданный нами скрипт BlockScript. Переопределим метод OnCollisionEnter2D, в котором будем проверять тег объекта, с которым столкнулся наш блок.

			void OnCollisionEnter2D(Collision2D collision){
    if (collision.gameObject.tag == "Ball"){
 
    }
}
		

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

			void OnCollisionEnter2D(Collision2D collision){
    if (collision.gameObject.tag == "Ball"){
        numberOfHits++;
 
        if (numberOfHits == hitsToKill){
            // уничтожаем объект
            Destroy(this.gameObject);
        }
    }
}
		

А теперь добавьте этот скрипт к объекту Block (Hierarchy -> Inspector -> Add Component):

Пишем арканоид на Unity. Поведение блоков, префабы и дизайн уровней 5

Прежде, чем идти дальше, давайте проверим, все ли работает должным образом. В редакторе Unity измените параметр Hits To Kill на 1 и запустите игру. После первого же попадания по блоку, последний должен исчезнуть.

Создание префабов

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

Из официальной документации:

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

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

Давайте рассмотрим это на примере. Переименуйте объект Block в Blue Block. Затем создайте новую папку внутри Assets под названием Prefabs. Перетащите Blue Block в новую папку.

Пишем арканоид на Unity. Поведение блоков, префабы и дизайн уровней 6

Как вы могли заметить, иконка кубика в верхней части вкладки Inspector теперь стала голубенькой, также как и название блока во вкладке Hierarchy. Это значит, что был создан префаб. Довольно просто, не так ли? Отныне, выбрав этот префаб и изменив у него какое-либо значение, обновятся параметры у всех его экземпляров.

Давайте создадим еще один экземпляр префаба: перетащите Blue Block из папки Prefabs во вкладку Hierarchy. Теперь у нас два синих блока, но оба находятся на одной и той же позиции.

Пишем арканоид на Unity. Поведение блоков, префабы и дизайн уровней 7

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

Пишем арканоид на Unity. Поведение блоков, префабы и дизайн уровней 8

Нажмите кнопку Play и проверьте работу игры.

Пересоздание префабов

Давайте создадим префабы и для других блоков: зеленых, желтых и красных. Вот, по какому плану мы должны пойти:

  1. Создаем новый спрайт.
  2. Добавляем соответствующее изображение.
  3. Добавляем компонент Box Collider 2D.
  4. Добавляем BlockScript.
  5. Переименовываем объект.
  6. Создаем префаб.

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

Пишем арканоид на Unity. Поведение блоков, префабы и дизайн уровней 9

Для того, чтобы игра была более интересной, измените количество ударов для уничтожения блока (параметр Hits To Kill) таким образом:

  1. Синий: 1 удар.
  2. Зеленый: 2 удара.
  3. Желтый: 3 удара.
  4. Красный: 4 удара.

Добавьте несколько различных блоков на сцену и запустите игру.

Дизайн уровней

Итак, давайте заполним наш уровень. Используя префабы, добавьте требуемое количество различных блоков. Чтобы не засорять проект, создайте во вкладке Hierarchy пустой объект Blocks, а внутри него еще четыре пустых объекта — по одному на каждый цвет. Отсортируйте все существующие блоки по цветам таким образом:

Пишем арканоид на Unity. Поведение блоков, префабы и дизайн уровней 10

На том этапе игра практически готова, вся игровая механика написана. У вас должно получиться что-то вроде такого:

Пишем арканоид на Unity. Поведение блоков, префабы и дизайн уровней 11

Набор очков и жизни

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

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

			private int playerLives;
private int playerPoints;

// используйте этот метод для инициализации
void Start () {
	// получим начальную позицию платформы
	playerPosition = gameObject.transform.position;

	playerLives = 3;
	playerPoints = 0;

}
		

У нас должен быть метод, который будет добавлять необходимое количество очков:

			void addPoints(int points){
    playerPoints += points;
}
		

Этот метод получает в качестве параметра некоторое количество очков. Но откуда мы будем брать его?

Лучшим способом является передача сообщения от блока платформе. Как это сделать? Для начала пометим платформу тегом Player (Hierarchy -> Inspector -> Tag):

Пишем арканоид на Unity. Поведение блоков, префабы и дизайн уровней 12

Теперь, когда платформа имеет свой тег, перейдем к сценарию блока BlockScript. Давайте изменим метод OnCollisionEnter2D, в котором найдем нашу платформу при помощи метода FindGameObjectsWithTag и вызовем метод SendMessage, который позволяет выполнить функцию из другого скрипта. Выполнять будем метод addPoints.

Обратим внимание на то, что метод FindGameObjectsWithTag возвращает массив игровых объектов, а т.к. у нас ровно один объект с тегом Player, то 0-ой элемент и будет нашей платформой.

			void OnCollisionEnter2D(Collision2D collision){
 
    if (collision.gameObject.tag == "Ball"){
        numberOfHits++;
 
        if (numberOfHits == hitsToKill){
            // получаем ссылку на платформу
            GameObject player = GameObject.FindGameObjectsWithTag("Player")[0];
 
            // выполняем метод из другого скрипта
            player.SendMessage("addPoints", points);
 
            // уничтожаем блок
            Destroy(this.gameObject);
        }
    }
}
		

Измените количество очков (параметр Points) в каждом префабе. Например, по такому принципу:

  1. Синий: 10 очков.
  2. Зеленый: 20 очков.
  3. Желтый: 35 очков.
  4. Красный: 50 очков.
Пишем арканоид на Unity. Поведение блоков, префабы и дизайн уровней 13

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

			void OnGUI(){
    GUI.Label (new Rect(5.0f,3.0f,200.0f,200.0f),"Live's: " + playerLives + "  Score: " + playerPoints);
}
		

Вернемся в PlayerScript и добавим метод, который будет отнимать количество жизней:

			void TakeLife(){
    playerLives--;
}
		

И, наконец, перейдем в BallScript и в момент, когда мячик падает за пределы игрового поля, вызовем метод TakeLife:

			// проверка падения шара
if (ballIsActive && transform.position.y < -6) {
	ballIsActive = !ballIsActive;
	ballPosition.x = playerObject.transform.position.x;
	ballPosition.y = -4.2f;
	transform.position = ballPosition;

	rigidbody2D.isKinematic = true;
	
	//добавили вызов метода
	playerObject.SendMessage("TakeLife");
}
		

На этом все! Запустите игру и проверьте, что все работает правильно.

В следующей части

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

Перевод статьи «Build Arkanoid With Unity: Block Mechanics, Prefabs, and Level Design»

Разработка игр
Unity
13466