Как сделать уровень в игре

Добавил пользователь Валентин П.
Обновлено: 19.09.2024

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

С тех пор как разработка игр стала легко доступной для инди разработчиков, появилось не мало игр, где дизайну уделялось не такое большое внимание, а все силы уходили именно на геймплей. Позже разработчики пришли к тому, что стали доверять машинам и алгоритмам генерации. Самый яркий пример таких игр с генерируемыми мирами, это конечно же – Minecraft , где весь ландшафт состоит из блоков, хоть это и является иллюзией – ведь хранить даже обозримое кол-во блоков в оперативной памяти невероятно затратно, и все что мы видим в игре на самом деле это один большой “ супермеш ”.

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

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

Геймплей

Сначала разберем то, что будет из себя представлять будущая игра, точнее, нужно придумать примерно абстрактную модель генерируемого мира в игре. Пусть это будет простая 2D игра, где персонажу необходимо попасть из точки А в точку Б в случайно сгенерируемом уровне.

Сцена состоит из 2D блоков, которые будут случайным образом сгенерированы перед прохождением.


Процесс игры разобьем на три этапа:

  • Генерация уровня. Сперва необходимо будет сгенерировать уровень, состоящий из блоков.
  • Прохождение уровня. Помещаем персонажа на сцену и проходим уровень.
  • Завершение игры. Уничтожаем старый уровень и переходим к созданию нового.

Генерация уровня

Уровень будет состоять из блоков, разного типа:

  • Стартовый блок. Это именно то место, откуда игрок будет начинать свое путешествие.
  • Конечный блок. При попадании игрока на последний блок уровня, игра считается пройденной.


  • Промежуточный блок. Из этих блоков будет состоять большая часть уровня.


Именно кол-во промежуточных блоков будет влиять на дизайн и длину уровня.

Программная часть генерации блоков

Создадим скрипт Control , где укажем три переменных спрайтовых ( Sprite ) переменных для каждого типа блока.

  1. publicclass Control : MonoBehaviour
  2. public Sprite startBlock ;
  3. public Sprite midBlock ;
  4. public Sprite endBloc ;
  5. >

В эти три переменные занесем каждый спрайт блока по отдельности: стартовый, промежуточный и конечный. Дополним скрипт Control стартовым методом Start .

  1. publicclass Control : MonoBehaviour
  2. public Sprite startBlock ;
  3. public Sprite midBlock ;
  4. public Sprite endBloc ;
  5. publicvoid Start () <>
  6. >

В методе Start мы будем запускать генерацию уровня во время старта игры.

  1. publicclass Control : MonoBehaviour
  2. public Sprite startBlock ;
  3. public Sprite midBlock ;
  4. public Sprite endBloc ;
  5. publicvoid Start () <>
  6. private IEnumerator OnGeneratingRoutine () <>
  7. >

В методе OnGeneratingRoutine , будем выполнять сам процесс генерации уровня. Так как уровни у нас могут быть как большими, так и маленькими и генерироваться разное количество времени, процесс генерации мы поместим в корутину, чтобы игра не “ зависала ” во время работы “ генератора ”. Далее добавим одну числовую переменную completeLevels, с помощью которой будем указывать количество пройденных уровней.

  1. publicclass Control : MonoBehaviour
  2. public Sprite startBlock ;
  3. public Sprite midBlock ;
  4. public Sprite endBloc ;
  5. privateint completeLevels = 0 ;
  6. publicvoid Start () <>
  7. private IEnumerator OnGeneratingRoutine () <>
  8. publicvoid CompleteLevel ()
  9. this . completeLevels += 1 ;
  10. >
  11. >

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

Теперь можно переходить к методу OnGeneratingRoutine , где мы начнем описывать сам алгоритм генерации уровня.

  1. publicclass Control : MonoBehaviour
  2. public Sprite startBlock ;
  3. public Sprite midBlock ;
  4. public Sprite endBloc ;
  5. privateint completeLevels = 0 ;
  6. /*…остальной код…*/
  7. private IEnumerator OnGeneratingRoutine ()
  8. Vector2 size = new Vector2 ( 1 , 1 );
  9. Vector2 position = new Vector2 ( 0 , 0 );
  10. yieldreturn new WaitForEndOfFrame ();
  11. >
  12. >

Для начала в методе OnGeneratingRoutine объявим две векторные переменные: size , где укажем размер блоков по длине и высоте и position, где укажем точку, откуда будет начинать строится уровень. Теперь можно построить стартовый блок.

  1. publicclass Control : MonoBehaviour
  2. public Sprite startBlock ;
  3. public Sprite midBlock ;
  4. public Sprite endBloc ;
  5. privateint completeLevels = 0 ;
  6. /*…остальной код…*/
  7. private IEnumerator OnGeneratingRoutine ()
  8. Vector2 size = new Vector2 ( 1 , 1 );
  9. Vector2 position = new Vector2 ( 0 , 0 );
  10. GameObject newBlock = new GameObject ( “ Start block” );
  11. yieldreturn new WaitForEndOfFrame ();
  12. >
  13. >

Создаем новый GameObject newBlock на сцене.

  1. private IEnumerator OnGeneratingRoutine ()
  2. Vector2 size = new Vector2 ( 1 , 1 );
  3. Vector2 position = new Vector2 ( 0 , 0 );
  4. GameObject newBlock = new GameObject ( “ Start block” );
  5. newBlock . transform . position = position ;
  6. newBlock . transform . localScale = size ;
  7. SpriteRendere renderer = newBlock . AddComponent SpriteRenderer >();
  8. renderer . sprite = this . startBlock ;
  9. yieldreturn new WaitForEndOfFrame ();
  10. >

После создания нового блока, устанавливаем ему позицию и размер через его transform , добавляем блоку компонент SpriteRenderer, чтобы отобразить на сцене и указываем, какой именно спрайт ему отобразить, в нашем случае это будет стартовый спрайт первого блока startBlock .

Теперь запустим корутину OnGeneratingRoutine в методе Start и проверим ее выполнение.

Переходим к созданию промежуточных блоков. Для этого в корутине OnGeneratingRoutine добавим еще одну переменную count .

  1. private IEnumerator OnGeneratingRoutine ()
  2. Vector2 size = new Vector2 ( 1 , 1 );
  3. Vector2 position = new Vector2 ( 0 , 0 );
  4. GameObject newBlock = new GameObject ( “ Start block” );
  5. newBlock . transform . position = position ;
  6. newBlock . transform . localScale = size ;
  7. SpriteRendere renderer = newBlock . AddComponent SpriteRenderer >();
  8. renderer . sprite = this . startBlock ;
  9. int count = this . completeLevels + 5 ;
  10. yieldreturn new WaitForEndOfFrame ();
  11. >

Числовая переменная count будет указывать какое кол-во промежуточных блоков необходимо построить, это число будет зависеть от количества пройденных уровней и, чтобы их изначально не было слишком мало на первых уровнях, еще пяти (5) дополнительных блоков. Строить промежуточные блоки будем через цикл for .

  1. private IEnumerator OnGeneratingRoutine ()
  2. Vector2 size = new Vector2 ( 1 , 1 );
  3. Vector2 position = new Vector2 ( 0 , 0 );
  4. GameObject newBlock = new GameObject ( “ Start block” );
  5. newBlock . transform . position = position ;
  6. newBlock . transform . localScale = size ;
  7. SpriteRendere renderer = newBlock . AddComponent SpriteRenderer >();
  8. renderer . sprite = this . startBlock ;
  9. int count = this . completeLevels + 5 ;
  10. for ( int i = 0 ; i count ; i ++)
  11. newBlock = new GameObject ( “ Middle block” );
  12. renderer = newBlock . AddComponent SpriteRenderer >();
  13. renderer . sprite = this . midBlock ;
  14. >
  15. yieldreturn new WaitForEndOfFrame ();
  16. >

Также как мы строили стартовый блок, также строим и промежуточные: создаем новый GameObject , добавляем ему компонент SpriteRenderer , указываем спрайт для отображения на сцене и задаем размер и позицию.

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

  1. private IEnumerator OnGeneratingRoutine ()
  2. Vector2 size = new Vector2 ( 1 , 1 );
  3. Vector2 position = new Vector2 ( 0 , 0 );
  4. /*…остальной код…*/
  5. int count = this . completeLevels + 5 ;
  6. for ( int i = 0 ; i count ; i ++)
  7. newBlock = new GameObject ( “ Middle block” );
  8. renderer = newBlock . AddComponent SpriteRenderer >();
  9. renderer . sprite = this . midBlock ;
  10. newBlock . transform . localScale = size ;
  11. position . x += size . x ;
  12. newBlock . transform . position = position ;
  13. >
  14. yieldreturn new WaitForEndOfFrame ();
  15. >

Чтобы сдвинуть позицию блока вверх или вниз, воспользуемся случайной генерацией чисел через Random .

  1. private IEnumerator OnGeneratingRoutine ()
  2. Vector2 size = new Vector2 ( 1 , 1 );
  3. Vector2 position = new Vector2 ( 0 , 0 );
  4. /*…остальной код…*/
  5. int count = this . completeLevels + 5 ;
  6. for ( int i = 0 ; i count ; i ++)
  7. newBlock = new GameObject ( “ Middle block” );
  8. renderer = newBlock . AddComponent SpriteRenderer >();
  9. renderer . sprite = this . midBlock ;
  10. newBlock . transform . localScale = size ;
  11. position . x += size . x ;
  12. position . y += size . y * Random . Range (- 1 , 2 );
  13. newBlock . transform . position = position ;
  14. >
  15. yieldreturn new WaitForEndOfFrame ();
  16. >

Высота блока по Y в переменной position также смещается вверх, либо вниз, в зависимости от размера блока, умноженного на случайное число от -1 до 1. Метод Random.Range генерирует ЦЕЛЫЕ числа от минимального до максимально (ИСКЛЮЧИТЕЛЬНО), это значит, что максимальное указанное число никогда достигнуто не будет. Завершаем цикл постройки промежуточных блоков новым WaitForEndOfFrame .

  1. private IEnumerator OnGeneratingRoutine ()
  2. Vector2 size = new Vector2 ( 1 , 1 );
  3. Vector2 position = new Vector2 ( 0 , 0 );
  4. /*…остальной код…*/
  5. int count = this . completeLevels + 5 ;
  6. for ( int i = 0 ; i count ; i ++)
  7. newBlock = new GameObject ( “ Middle block” );
  8. renderer = newBlock . AddComponent SpriteRenderer >();
  9. renderer . sprite = this . midBlock ;
  10. newBlock . transform . localScale = size ;
  11. position . x += size . x ;
  12. position . y += size . y * Random . Range (- 1 , 2 );
  13. newBlock . transform . position = position ;
  14. yieldreturn new WaitForEndOfFrame ();
  15. >
  16. yieldreturn new WaitForEndOfFrame ();
  17. >

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

  1. private IEnumerator OnGeneratingRoutine ()
  2. Vector2 size = new Vector2 ( 1 , 1 );
  3. Vector2 position = new Vector2 ( 0 , 0 );
  4. /*…остальной код…*/
  5. newBlock = new GameObject ( “ End block” );
  6. renderer = newBlock . AddComponent SpriteRenderer >();
  7. renderer . sprite = this . endBlock ;
  8. position . x += size . x ;
  9. position . y += size . y * Random . Range (- 1 , 2 );
  10. newBlock . transform . position = position ;
  11. newBlock . transform . localScale = size ;
  12. yieldreturn new WaitForEndOfFrame ();
  13. >

Готово, алгоритм генерации завершен, запускаем игру для последней проверки.

Заключение

Чтобы генерация уровня происходила каждый раз когда игрок завершает игру, в методе CompleteLevel достаточно просто запустить корутину OnGeneratingRoutine заново.

  1. publicclass Control : MonoBehaviour
  2. public Sprite startBlock ;
  3. public Sprite midBlock ;
  4. public Sprite endBloc ;
  5. privateint completeLevels = 0 ;
  6. publicvoid Start () <>
  7. private IEnumerator OnGeneratingRoutine () <>
  8. publicvoid CompleteLevel ()
  9. this . completeLevels += 1 ;
  10. StartCoroutine ( OnGeneratingRoutine ());
  11. >
  12. >

Сам алгоритм генерации достаточно простой, его можно расширить и дополнить новыми элементами блоков: ловушками, пропастями и тд. Добавить блокам коллайдеры и персонажа, который сможет перемещать по ним.


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

Gardenscapes сегодня

В июне в Gardenscapes было 29 механик на 1300 уровнях.

Год назад, когда мы сделали предварительный запуск игры, у нас было 16 механик на 300 уровнях.

В каждый апдейт мы стараемся добавлять 1-2 уникальных механики.


Создание нового элемента

Этап первый: изучение конкурентов

Перед нами стоит простая задача: сделать новый элемент. С чего стоит начать?

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

Увиденное следует, если речь о хорошей идее, проанализировать и, доработав, применить к своему проекту.

Этап второй: генерация и фильтрация идей

После того, как мы изучили конкурентов, мы начинаем разработку нового элемента.

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

После того, как мы подготовили большой список и отсеяли его, группа экспертов выбирает 2-3 механики с наибольшим потенциалом. Это важный этап, на котором мы концентрируемся на всего 2-3 элементах.

Этап третий: прототипирование

Что получаем по итогу? Готовый, оттестированный на тестовом уровне, на тестовом прототипе элемент.

Этап четвертый: создание образа

После того, как мы подготовили идею, мы переходим к следующей части — это образ элемента.

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

Для начала, чтобы заниматься образом, нужно определить сеттинг игры.


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

Приведу пример блокатора из Gardenscapes. Блокатор, ящик, — тот элемент, который вписан в тайл, не оставляет нигде свободного места, и поэтому игрок, когда видит этот элемент, понимает, что ящик привязан к полю и никуда не денется. Это одно из свойств.

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

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

Нашли референсы, сделали несколько эскизов. Дальше перешли к свойствам элемента.

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


Этап пятый: встраивание в игру

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

Итак, мы подготовили элемент, ввели его в нашу систему ввода элементов. Теперь начинаем делать уровни.

Разработка уровня

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

У нас существует документ, в котором прописано, на каком месте находится тот или иной уровень, какие требования предъявляем к этому уровню. Это сложность, цели, количество элементов, используемых на этом уровне, и так далее. Фактически мы готовим полноценное ТЗ для левел-дизайнера, по которому он начинает делать уровни.

Итак, подготовили документ, передали левел-дизайнеру. Теперь нужно обсудить, какие вещи он должен учитывать при подготовке конкретного уровня.

Требований основных немного – четыре:

а) На уровне должно быть не больше трех-четырех элементов.

б) Сочетаемость элементов.

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

в) У каждого уровня должна быть идея.

Каждый новый элемент создает новые идеи.

Приведу пример: уровень может быть ориентирован на специальную стратегию. Или, наоборот, уровень может быть рандомный, где нужно взрывать много бустов и собирать бонусы.

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

Что мы делаем после того, как подготовили уровень, который соответствует кривой? Мы эти уровни наигрываем.

Но для появления реальной картины одного левел-дизайнера недостаточно. У нас существует специальный отдел наигрывания, который постоянно пополняется новыми ребятами. Задача этих ребят — играть в уровни точно так же, как это делали бы настоящие игроки. И результат их статистики идет в основу финальной настройки сложности каждого уровня.

Как мы проводим анализ уровня?

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

Первый анализ уровней мы начинаем спустя 2-3 недели после того, как они попадают в продакшен. Чтобы сделать анализ, нам нужно получить сырые данные игроков, а также те события, которые игра отправляет в процессе игры в нашу аналитическую базу. То есть вы играете, проходите игру, а в этот момент игра отправляет нам информацию о том, что же вы конкретно делаете.

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

2) Несоответствие кривой сложности.

Когда левел-дизайнер делает уровень, он настраивает сложность. Когда отдел наигрывания проходит уровень, он подстраивает эту сложность. И все равно очень часто полученный результат не соответствует тому, что мы видим в мире.

Чтобы грамотно провести анализ кривых сложности, мы сравниваем две кривые. Первая — наша кривая проектной сложности, а вторая — кривая реальных результатов игроков на этих уровнях. В том месте, где эти кривые расходятся, скорее всего, существует какая-то проблема, и мы уделяем этим местам особое внимание.

3) Затяжные серии уровней.

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

А/Б-тестирование

Что делать, если вы провели анализ, нашли проблемные уровни и пытаетесь решить, в какую сторону этот уровень менять? Для этого мы используем систему А/Б-тестирования.

Немного расскажу о метриках уровня, которые мы изучаем.

Сам принцип тестирования выглядит так: разделяем игроков на три группы. Первой группе даем текущий вариант уровня, второй — облегченный вариант уровня, а третьей группе игроков даем совершенно новый вариант уровня, и следим за тем, как игроки в эти уровни играют. После того, как проходит несколько недель и у нас есть реальная картина, мы принимаем решение — оставить уровень, взять облегченный вариант или взять абсолютно новый вариант.

Пример — уровень 77. Это один из первых пейволлов, которые есть в игре. Основная проблема контрольного уровня (который на слайде находится слева) — очень высокие отвалы, 2,29%. Это критично, потому что уровень еще только в начале игры. Если мы здесь теряем много игроков, это влияет на заработок всей игры в дальнейшем.

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

Следующий пример — 93 уровень. Более сложная ситуация. На уровне мы имели высокое количество отвалов, провели А/В-тест и получили следующую картину: у нового уровня (справа) очень хорошее число отвалов, игроки почти не отваливаются. Но метрики монетизации очень сильно просели. Нам все-таки хочется найти какой-то баланс. У среднего варианта, у облегченного, при уменьшении отвалов метрики монетизации уменьшились незначительно. И мы остановились на этом уровне в ходе обсуждения, потому что он продолжает оставлять большее число игроков, а зарабатываем почти столько же, сколько на изначальном уровне.

Величины монетизации и отвалов следуют друг за другом.

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

Выводы

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

Lorem ipsum dolor

Процедурная генерация в Unity

Процедурная генерация в Unity: философия игры

Заключение

Процедурная генерация в Unity собрала вокруг себя много противников. Но, несмотря на это, ее активно применяют как большие студии разработки игр, так и инди-разработчики. Этот алгоритм особо актуален для небольших студий и разработчиков-одиночек, потому что позволяет им создавать масштабные игры небольшими командами. К примеру, Майнкрафт был создан единственным геймдизайнером. И только чуть позже игра обросла командой разработчиков.

Мы будем очень благодарны

если под понравившемся материалом Вы нажмёте одну из кнопок социальных сетей и поделитесь с друзьями.

Привет, Пикабу. Я очень люблю делать и играть в игры, особенно 2D. Не знаю почему, но, мне кажется, эта страсть пошла у меня именно со времен Dendy & Sega. Еще в то время я восхищался эстетикой тех игр, задними фонами и красочными уровнями. Я всегда на это обращаю внимание и могу минутами смотреть на дизайн первого уровня ни нажав ни клавиши. Ну да ладно, я бы хотел сегодня немножко поговорить о дизайне уровней для твоих игр. Если ты так же пишешь игры и не делаешь их пачками за пару дней, дабы жадно нажиться лишним мелким рублем плевал на их качество, то тебе это может быть интересно. И да, с тобой как всегда Флатинго (Flatingo) и я являюсь украинским разработчиком игр.

Как я создаю дизайн 2D уровня в Unity5 для своей игры Игры, Компьютерные игры, Gamedev, Создание игр, Unity5, Флатинго, Длинновидео, Видео, Длиннопост

1. Цвета. Первое, что тебе нужно для создания своего уровня, это определится с цветовой гаммой. Советую выбирать, изначально, 3-5 цветов похожей градации, но не яркие и ядовитые. Если это зимний уровень, то, для примера, выбери белый (снег), синий (небо), серый (камни и т.д.), темно-зеленый (ели). Всегда учитывай цвета главного героя, что бы не случился каламбур цветов.

2. Расстановка. Изначально продумай уровень. Не делай его однообразным и очень коротким. Однообразные и ровные платформы утомят игрока. Ты должен определить то время, когда игроку станет скучно и не интересно играть на нем. Это еще в книге Джесси Шелла было упомянуто, что игрок должен находится в потоке удовольствия и заинтересованности, не выходя из него. С одной стороны потока находится скука уровня, его однообразность, а с другой ненависть из-за сложности, поэтому заставь игрока находится в золотой середине.

3. Детали. Когда ты определился с цветом и уровнем, тебе надо расставить игровые объекты, да так, что бы они не повторялись и их было интересно рассматривать. И мы сейчас говорим именно о статических объектах, к примеру: камни, трава, бревна, машины, здания и т.д. Не надо тулить одно и тоже здание даже отзеркаленное в ряд и думать, что твой город уникален, если ты еще вставишь пару фонарей. Твой уровень должен быть насыщен до мелочей. Не забывай о задних и передних фонах. Лично я привык вставлять по 3 паралаксовых задних фона и заблюренный передний фон, но это зависит уже от времени и самого проекта. Если тебе трудно представить тот же город или сельскую местность, то возьми примерные арты или фото и вдохновись ими. Если у тебя в задумке выдуманный мир и ты не знаешь, как его наполнить из-за недостатка воображения, то возьми пару примеров, которыми ты вдохновлялся и попробуй их совместить. Все равно человек не может взять и что-то придумать новое, чего еще ни разу не видел. Все изобретения и придумки - это слияния двух и более идей, просто мы того иногда не замечаем. Когда-то случайно познакомился в пабе с парнем, с которым с процессе беседы заговорили о дизайне, и он сказал вполне годную вещь: "Молоток - это не что-то новое и уникальное. Это смесь палки и камня. Просто нас заставили принять его необходимость". И он в чем-то даже прав.

4. Декорации. Оживи свой уровень анимацией, помимо статических объектов. Ведь приятно смотреть, как игрок идет по полю, а где-то далеко вертится мельница, плавно плывут облака по небу, летают птицы и слышно их чириканье. Это ведь прекрасно и эстетично. Ах да, звуки. Музыка и звуки - это примерно 60% эстетики. Ты можешь наслаждаться прекрасным видом в игре, но музыка. музыка это то, что ты будешь прокручивать в голове постоянно и что всегда будет тебе навевать эту ностальгию (всем известный случай с Robocop 3 для Dendy).

Ну вот, наверное я на этом и закончу. Просто хотел поделится с вами чем-то из своего опыта. Если тебе этого мало, то внизу, как пример, ты увидишь видео c моего канала, как я создаю свой дизайн 2D уровня (speed level design) для своей игры в Unity 5. Присоединяйся. Удачи с будущими проектами. Чао.

Читайте также: