Как создать меню для игры на Unity
Без меню игроку не изменить настройки, не сохраниться или не загрузить прошлый прогресс. Объясняем, как сделать меню в Unity.
Если вы уже немного понимаете, как работать в Unity, и попробовали что-то создать, пора научиться верстать игровые меню. Если нет, прочтите для начала статью о создании игры.
Здесь будут описаны общие моменты, с полной версией проекта можно ознакомиться, скачав его из репозитория на GitHub.
Для создания интерфейсов, в том числе и меню, в Unity используются
UI-объекты. К ним относятся:
- кнопки;
- изображения;
- списки;
- слайдеры;
- чекбоксы;
- выпадающие списки и другие элементы.
Чтобы работать с ними, нужно создать объект Canvas и дать ему понятное название. Например MenuCanvas. Добавьте в него объект Panel и задайте какое-нибудь фоновое изображение или цвет.
После этого можно начинать верстать меню. Создайте внутри MenuCanvasобъект типа Empty и назовите его MainMenu. Внутрь него можно добавить элементы типа Text и Button.
Менять надпись на кнопке можно с помощью вложенного объекта Text. Добавьте их столько, сколько вам необходимо. Затем разместите их на холсте так, чтобы получить что-то вроде этого:
Ваше главное меню готово. Нужно ещё добавить отдельные подменю для настроек, сохранения и загрузки.
Как добавить несколько меню в Unity
Чтобы создать несколько экранов меню, добавьте ещё несколько объектов типа Empty и поместите новые элементы в них. Например, в этом проекте будут созданы SaveMenu, LoadMenu и SettingsMenu.
При добавлении объекта он становится активным, поэтому все кнопки и слайды будут просто налезать друг на друга. Чтобы отключить какое-нибудь меню, нажмите на его объект и в Inspector возле его названия уберите галочку.
Этому действию соответствует метод SetActive(), который можно использовать, чтобы переключать меню при нажатии кнопки. Для этого выберите кнопку и в окне Inspector найдите поле On Click ().
В нём описаны действия, которые будут выполняться при клике на кнопку.
На скриншоте показано отключение основного меню и включение меню с сохранениями при нажатии кнопки SaveButton. Сделано всё это без использования кода.
Как создать меню настроек в Unity
Отдельно рассмотрим создание настроек игры. Чтобы реализовать их, нужно сначала сверстать меню с помощью объектов Toggle, Dropbox и Slider:
Дальше создайте скрипт Menu.cs и прикрепите его к MenuCanvas: он будет отвечать за работу со всеми настройками.
В него нужно добавить следующие библиотеки:using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; //Работа с интерфейсами
using UnityEngine.SceneManagement; //Работа со сценами
using UnityEngine.Audio; //Работа с аудио
Затем добавьте эти поля:public bool isOpened = false; //Открыто ли меню
public float volume = 0; //Громкость
public int quality = 0; //Качество
public bool isFullscreen = false; //Полноэкранный режим
public AudioMixer audioMixer; //Регулятор громкости
public Dropdown resolutionDropdown; //Список с разрешениями для игры
private Resolution[] resolutions; //Список доступных разрешений
private int currResolutionIndex = 0; //Текущее разрешение
Не забудьте добавить в скрипт выпадающий список и регулятор громкости:
Теперь можно заняться основным функционалом. В первую очередь нужно создать метод, который будет открывать меню в игре.public void ShowHideMenu()
{
isOpened = !isOpened;
GetComponent<Canvas> ().enabled = isOpened; //Включение или отключение Canvas. Ещё тут можно использовать метод SetActive()
}
Вызываться этот метод будет при нажатии на кнопку Esc:void Update()
{
if(Input.GetKey(KeyCode.Escape))
{
ShowHideMenu();
}
}
Дальше нужно создать методы, которые будут вызываться при изменении настроек в меню:public void ChangeVolume(float val) //Изменение звука
{
volume = val;
}
public void ChangeResolution(int index) //Изменение разрешения
{
currResolutionIndex = index;
}
public void ChangeFullscreenMode(bool val) //Включение или отключение полноэкранного режима
{
isFullscreen = val;
}
public void ChangeQuality(int index) //Изменение качества
{
quality = index;
}
Выберите необходимый объект, например, список с уровнями качества, и добавьте обработчик события On Value Changed.
Для этого перетащите объект MenuCanvas и выберите Menu.ChangeQuality. Обратите внимание, что метод указан без скобок. В таком случае значение будет передано ему автоматически. Здесь это индекс выбранного пункта.
Чтобы настройки вступили в силу, нужен ещё один метод:public void SaveSettings()
{
audioMixer.SetFloat("MasterVolume", volume); //Изменение уровня громкости
QualitySettings.SetQualityLevel(quality); //Изменение качества
Screen.fullScreen = isFullscreen; //Включение или отключение полноэкранного режима
Screen.SetResolution(Screen.resolutions[currResolutionIndex].width, Screen.resolutions[currResolutionIndex].height, isFullscreen); //Изменения разрешения
}
Добавьте вызов этого метода при нажатии на кнопку сохранения настроек.
Меню можно улучшить, если сделать автоматическое добавление всех доступных разрешений:resolutionDropdown.ClearOptions(); //Удаление старых пунктов
resolutions = Screen.resolutions; //Получение доступных разрешений
List<string> options = new List<string> (); //Создание списка со строковыми значениями
for(int i = 0; i < resolutions.Length; i++) //Поочерёдная работа с каждым разрешением
{
string option = resolutions [i].width + " x " + resolutions [i].height; //Создание строки для списка
options.Add(option); //Добавление строки в список
if(resolutions[i].Equals(Screen.currentResolution)) //Если текущее разрешение равно проверяемому
{
currResolutionIndex = i; //То получается его индекс
}
}
resolutionDropdown.AddOptions(options); //Добавление элементов в выпадающий список
resolutionDropdown.value = currResolutionIndex; //Выделение пункта с текущим разрешением
resolutionDropdown.RefreshShownValue(); //Обновление отображаемого значения
Также сюда стоит добавить возможность выхода из игры:public void QuitGame()
{
Application.Quit(); //Закрытие игры. В редакторе, кончено, она закрыта не будет, поэтому для проверки можно использовать Debug.Log();
}
Или перехода на другую сцену:public void GoToMain()
{
SceneManager.LoadScene("Menu"); //Переход на сцену с названием Menu
}
Теперь остается только функция сохранения.
Как создать сохранение в Unity
Сохранения представляют собой файлы, в которых хранится информация о текущем состоянии игровых объектов:
- позиции игрока;
- уровне;
- мане;
- здоровье;
- опыте и так далее.
Чтобы можно было всё это удобно преобразовать в файл, используется сериализация — специальный инструмент, который позволяет сохранить объект в формате JSON или XML. Лучше сохранять всё в виде бинарных файлов, потому что так игроки не смогут изменить характеристики своего персонажа.
Чтобы сохранить данные (координаты, наличие предметов в инвентаре, здоровье), создается класс SaveData:
[System.Serializable] //Обязательно нужно указать, что класс должен сериализоваться public class SaveData { //Создание полей с игровыми параметрами public float currHP; public float HP; public float currMP; public float MP; public float currXP; public float XP; public int level; public float[] position; //В Unity позиция игрока записана с помощью класса Vector3, но его нельзя сериализовать. Чтобы обойти эту проблему, данные о позиции будут помещены в массив типа float. public SaveData(Character character) //Конструктор класса { //Получение данных, которые нужно сохранить HP = character.HP; currHP = character.currHP; MP = character.MP; currMP = character.currMP; XP = character.XP; currXP = character.currXP; level = character.level; position = new float[3] //Получение позиции { character.transform.position.x, character.transform.position.y, character.transform.position.z }; } }
Теперь понадобится дополнительный класс, который будет отвечать за сохранение и загрузку данных:
using UnityEngine; using System.IO; //Библиотек для работы с файлами using System.Runtime.Serialization.Formatters.Binary; //Библиотека для работы бинарной сериализацией public static class SaveLoad //Создание статичного класса позволит использовать методы без объявления его экземпляров { private static string path = Application.persistentDataPath + "/gamesave.skillbox"; //Путь к сохранению. Вы можете использовать любое расширение private static BinaryFormatter formatter = new BinaryFormatter(); //Создание сериализатора public static void SaveGame(Character character) //Метод для сохранения { FileStream fs = new FileStream (path, FileMode.Create); //Создание файлового потока SaveData data = new SaveData(character); //Получение данных formatter.Serialize(fs, data); //Сериализация данных fs.Close(); //Закрытие потока } public static SaveData LoadGame() //Метод загрузки { if(File.Exists(path)) { //Проверка существования файла сохранения FileStream fs = new FileStream(path, FileMode.Open); //Открытие потока SaveData data = formatter.Deserialize(fs) as SaveData; //Получение данных fs.Close(); //Закрытие потока return data; //Возвращение данных } else { return null; //Если файл не существует, будет возвращено null } } }
Созданные методы будут вызываться из класса Character:
public void LoadCharacter() { SaveData data = SaveLoad.LoadGame(); //Получение данных if(!data.Equals(null)) //Если данные есть { HP = data.HP; currHP = data.currHP; MP = data.MP; currMP = data.currMP; XP = data.XP; currXP = data.currXP; level = data.level; currHP = data.currHP; transform.position = new Vector3(data.position[0], data.position[1], data.position[2]); } }
Можно проверять:
Получился достаточно компактный код. Но чем больше нужно сохранить данных, тем объемнее он будет. Например, информацию о пользователе, настройках и других объектах можно добавить в один большой файл, а можно создать несколько отдельных файлов для них.
Заключение
Полученных из статьи знаний хватит, чтобы создавать другие интерфейсы в игре:
- полосы здоровья;
- уровень персонажа;
- меню паузы.