Как я сделал первую игру и загрузил на Яндекс.Игры. Часть 2 (Графика, префабы, сохранение прогресса)​

Дисклеймер

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

Графика

Появилась и сразу прижилась идея о карандаше, который будет прыгать по случайным объектам(насчет случайных объектов, идея появилась после нахождения ассета с такой графикой в стиле дудл).

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

Префабы

Импортируем спрайты и раскидываем их по тематикам (чтобы сделать тематические локации по 3-4 уровня). Также добавляем всем коллайдеры и делаем префабы. Дальше тематик будет больше.

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

Главный герой

Рисовать я не особо умею и решил попросить нарисовать героя у нейросети Midjourney. Запрос изначально был таким: red pencil with face, hyperrealistic, 4k, game character. Понял, что гиппереализм и 4к мешают получить, что нужно:) Дальше лучше, выбрал вариации второй картинки, дальше апскейл первой и немного подправил в фотошопе и вырезал. Идеально!

Слепил 3 уровня и получился такой уровень и геймплей:

Как я сделал первую игру и загрузил на Яндекс.Игры. Часть 2 (Графика, префабы, сохранение прогресса)​

Сохранение прогресса

Делаю еще два уровня для наглядности. Информация о уровне: индекс сцены, спрайт(скрин уровня на кнопке) хранится в ScriptableObject. А на каждой кнопке уровня в меню скрипт LevelDisplay принимает в инспекторе соответствующий ScriptableObject и подгружает через него данные.

public class LevelDisplay : MonoBehaviour { [SerializeField] private Level _level; [SerializeField] private Text _levelName; [SerializeField] private Image _lock; private Button _button; public void Awake() { PlayerPrefs.SetInt("1", 1); // первый всегда открыт } public void Start() { _levelName.text = _level.LevelIndex; _lock.gameObject.SetActive(true); _button = GetComponent<Button>(); _button.enabled = false; if (PlayerPrefs.GetInt(_level.LevelIndex) == 1) //если пройден отрываем доступ кнопке и убираем замок { _button.enabled = true; _lock.gameObject.SetActive(false); } } }
Меню: 1 и 2 открыты, остальные закрыты. Кнопка reset удаляет ключи.
Меню: 1 и 2 открыты, остальные закрыты. Кнопка reset удаляет ключи.

Сохранение уровней идет через PlayerPrefs ключ - "номер уровня" и значение 0 - закрыто, 1 - пройден (открыто).

Финиш открывает уровни

При триггере финиша игроком происходит 2 события:

Как я сделал первую игру и загрузил на Яндекс.Игры. Часть 2 (Графика, префабы, сохранение прогресса)​
  1. Заносится ключ-значение:
private Scene _scene; public void Start() { _scene = SceneManager.GetActiveScene(); } public void UnlockNextLevel() { PlayerPrefs.SetInt((_scene.buildIndex + 1).ToString(), 1); }

2. Открывается окно для перехода в меню или в следующий уровень:

[SerializeField] private Button _toInactive; private Animator _animator; private Player _player; public void Start() { _animator = GetComponent<Animator>(); _player = FindObjectOfType<Player>(); } public void Show() { _animator.SetTrigger("Open"); PauseTime(); _toInactive.enabled = false; } public void PauseTime() { _player.PausePlayer(); }

Кстати, окно окончания уровня весь уровень находится вне поле зрения камеры, она только выезжает при триггере финиша.

Как я сделал первую игру и загрузил на Яндекс.Игры. Часть 2 (Графика, префабы, сохранение прогресса)​

Теперь у нас есть 5 уровней, переход между ними, открытие уровней при прохождении и все это сохраняется между игровыми сессиями в памяти телефона.

Третья часть тут

33
1 комментарий

Кайф !

Ответить