Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Unity_в_действии_Джозеф_Хокинг_Рус.pdf
Скачиваний:
83
Добавлен:
21.06.2022
Размер:
26.33 Mб
Скачать

5.3. Отображение различных карт      123

присутствующий в сцене объект card_back на переменную этого сценария на панели Inspector, в процессе игры карта начнет исчезать после щелчка на ней. Исчезновение рубашки откроет лицевую сторону карты; как видите, мы решили еще одну важную для построения игры Memory задачу! Но пока на стол выложена всего одна карта, давайте исправим этот недостаток.

5.3. Отображение различных карт

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

5.3.1. Программная загрузка изображений

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

Чтобы посмотреть на процесс программной загрузки изображений, напишем простой тестовый код (который позже будет заменен). Сначала добавьте код следующего листинга в сценарий MemoryCard.

Листинг 5.3. Тестовый код, демонстрирующий изменение картинки спрайта

...

[SerializeField] private Sprite image; ¬ Ссылка на загружаемый ресурс Sprite void Start() {

GetComponent<SpriteRenderer>().sprite = image; ¬ Сопоставляем этот спрайт компоненту SpriteRenderer.

}

...

После сохранения этого сценария на панели Inspector появилась новая переменная, так как она была указана как сериализованная. Перетащите какой-либо спрайт со вкладки Project (выберите одно из лицевых изображений, кроме того, которое уже есть в сцене) и перетащите на ячейку Image. Затем выполните воспроизведение сцены, и вы увидите на карте новое изображение.

Ключом к пониманию этого кода являются сведения о компоненте SpriteRenderer. Обратите внимание, что на рис. 5.7 у рубашки карты всего два компонента: стандартный компонент Transform, присутствующий у всех объектов сцены, и новый компонент SpriteRenderer. Именно он превращает объект в спрайт и определяет, какой ресурс будет на нем отображаться. Обратите внимание, что первое свойство этого компонента называется Sprite и связано с одним из спрайтов на вкладке Project; этим свойством можно управлять в коде, чем, собственно, и занимается наш сценарий.

124      Глава 5. Игра Memory на основе новой 2D-функциональности

Р Sprite, а а

а Sprite

Ц , а • • Sprite ( а • , • а)

Рис. 5.7. Компонент SpriteRenderer, присоединенный к объекту-спрайту в сцене

Как вы видели в предыдущих главах на примере компонента CharacterController и написанных нами сценариев, метод GetComponent() возвращает другие компоненты для того же самого объекта, поэтому мы воспользуемся им для ссылки на объект SpriteRenderer. Свойству Sprite объекта SpriteRenderer можно присвоить любой ресурс, поэтому данный код присваивает указанному свойству объявленную в верхней части переменную Sprite (которую мы в редакторе заполнили одним из спрайтов).

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

5.3.2. Выбор изображения в невидимом компоненте

SceneController

Вспомните, как в главе 3 мы создавали в сцене невидимый объект для управления процессом генерации объектов. Мы снова воспользуемся этим приемом, но на этот раз невидимый объект будет контролировать более абстрактные вещи, не связанные ни с одним из объектов сцены. Для начала создайте пустой объект GameObject (напоминаю, что нужно выбрать в меню GameObject команду Create Empty). Затем создайте на вкладке Project сценарий SceneController.cs и перетащите этот ресурс на контроллер GameObject. Перед тем как писать код нового сценария, добавьте содержимое следующего листинга в сценарий MemoryCard вместо того, что было в листинге 5.3.

Листинг 5.4. Новые открытые методы в сценарии MemoryCard.cs

...

[SerializeField] private SceneController controller;

private int _id; public int id {

get {return _id;} ¬ Добавление функции чтения (идиома, распространенная в таких языках, как C# и Java).

}

Открытый метод, которым могут пользоваться

 

public void SetCard(int id, Sprite image) { ¬

другие сценарии для передачи указанному

объекту новых спрайтов.

_id = id;

 

}

GetComponent<SpriteRenderer>().sprite = image; ¬ Точно такая же строка SpriteRenderer, как в удаленном примере кода.

...

5.3. Отображение различных карт      125

Основным отличием от предыдущего листинга является задание изображения спрайта методом SetCard() вместо метода Start(). Так как это открытый метод, принимающий спрайт в качестве параметра, его можно вызывать из других сценариев и устанавливать изображение для данного объекта. Обратите внимание, что метод SetCard() также принимает в качестве параметра значение ID и код его сохраняет. Хотя в настоящий момент этот параметр нам не требуется, скоро мы напишем код, сравнивающий карты, и это сравнение будет производиться именно на основе значения ID.

ПРИМЕЧАНИЕ  Возможно, раньше вы пользовались языком программирования, в котором отсутствовали такие понятия, как метод чтения (getter) и метод задания (setter). Коротко говоря, это функции, которые запускаются, когда вы пытаетесь получить доступ к связанному с ними свойству (например, получить значение card.id). Эти методы применяются для разных целей, но в нашем случае свойство id предназначено только для чтения, поэтому вы можете только прочитать его значение, но не задать его.

Наконец, обратите внимание, что в этом коде присутствует переменная для контроллера; в то время как SceneController начинает клонировать карты для заполнения сцены, картам требуется ссылка на этот контроллер для вызова его открытых методов. Как обычно, когда код ссылается на объекты сцены, перетащите объект-кон- троллер из редактора Unity на ячейку переменной на панели Inspector. Достаточно сделать это для одной карты, а все появившиеся позже копии получат эту ссылку автоматически.

Теперь, когда мы добавили новый код в сценарий MemoryCard, введите код следующего листинга в сценарий SceneController.

Листинг 5.5. Первый проход компонента SceneController в игре Memory

using UnityEngine;

using System.Collections;

public class SceneController : MonoBehaviour {

[SerializeField] private MemoryCard originalCard; ¬ Ссылка для карты в сцене. [SerializeField] private Sprite[] images; ¬ Массив для ссылок на ресурсы-спрайты.

void Start() {

 

int id = Random.Range(0, images.Length);

Вызов открытого метода, который

originalCard.SetCard(id, images[id]); ¬

}

мы добавили в сценарий MemoryCard.

 

}

 

Это короткий фрагмент, демонстрирующий принцип управления картами контроллером SceneController. Большая часть представленного кода уже должна быть вам знакома (например, перетаскивание объекта-карты в редакторе Unity на ячейку переменной на панели Inspector), но с массивом изображений вы раньше не сталкивались. Как показано на рис. 5.8, на панели Inspector можно задать набор элементов. Введите 4 в поле размера массива, а затем перетащите спрайты с изображениями карт на ячейки элементов массива. Эти спрайты станут доступны в массиве, как и все остальные ссылки на объекты.

126      Глава 5. Игра Memory на основе новой 2D-функциональности

З а а

а а

Р-а

а а

а а а

Рис. 5.8. Заполнение массива спрайтов

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

Щелкните на кнопке Play для воспроизведения нового кода. Вы увидите, что при каждом запуске сцены изображение на открываемой карте меняется. Теперь нужно разложить по столу все остальные карты.

5.3.3. Создание экземпляров карт

У компонента SceneController уже есть ссылка на объект-карту, поэтому нам остается воспользоваться методом Instantiate() (см. следующий листинг) для получения набора карт, аналогично тому, как мы генерировали объекты в главе 3.

Листинг 5.6. Восьмикратное копирование карты и выкладывание карт на стол

using UnityEngine;

using System.Collections;

public class SceneController : MonoBehaviour {

public const int gridRows = 2; ¬ Значения, указывающие количество ячеек сетки и их расстояние друг от друга. public const int gridCols = 4;

public const float offsetX = 2f; public const float offsetY = 2.5f;

[SerializeField]

private MemoryCard originalCard;

[SerializeField] private Sprite[] images;

 

void Start() {

 

 

Положение первой карты; положение

 

 

 

Vector3 startPos = originalCard.transform.position; ¬ остальных карт отсчитывается от этой точки.

for (int i =

0; i < gridCols; i++) {

Вложенные циклы, задающие как столбцы,

for (int j

= 0; j < gridRows; j++) {

¬ так и строки нашей сетки.

MemoryCard card; ¬ Ссылка на контейнер для исходной карты или ее копий.

if (i ==

0 && j == 0) {

 

card =

originalCard;

 

}else {

card = Instantiate(originalCard) as MemoryCard;

}

int id = Random.Range(0, images.Length); card.SetCard(id, images[id]);

float posX = (offsetX * i) + startPos.x;