Как сравнивать цвета и сделать из этого казуальную игру?

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Привет, Хабр! В этой статье я хочу рассказать о разработке своей небольшой игры, в основу которой легло сравнение цветов.

Об идеи

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

Пока писал эту статью, нашел скрин той игры:

Та самая игра
Та самая игра

Тогда мне хотелось придумать что-то своё и я подумал, почему бы не сделать аналогичную игру, но где нужно смешивать цвета, чтобы получить нужный?!

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

Реализация

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

где значение 2.3 примерно соответствует минимально различимому для человеческого глаза отличию между цветами (это формула 1976 года, которую я решил использовать в своей игре, ещё есть формулы 1994 и 2000 года, которые тут не упоминаются).

Теперь оставалось только перевести цвета из RGB в Lab пространство.

Для этого для начала придется конвертировать RGB в XYZ, а затем XYZ в Lab :

  1. RGB в XYZ

	//sR, sG and sB (Standard RGB) input range = 0 ÷ 255
	//X, Y and Z output refer to a D65/2° standard illuminant.
	
	var_R = ( sR / 255 )
	var_G = ( sG / 255 )
	var_B = ( sB / 255 )
	
	if ( var_R > 0.04045 ) var_R = ( ( var_R + 0.055 ) / 1.055 ) ^ 2.4
	else                   var_R = var_R / 12.92
	if ( var_G > 0.04045 ) var_G = ( ( var_G + 0.055 ) / 1.055 ) ^ 2.4
	else                   var_G = var_G / 12.92
	if ( var_B > 0.04045 ) var_B = ( ( var_B + 0.055 ) / 1.055 ) ^ 2.4
	else                   var_B = var_B / 12.92
	
	var_R = var_R * 100
	var_G = var_G * 100
	var_B = var_B * 100
	
	X = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805
	Y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722
	Z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505
  1. XYZ в Lab

//Reference-X, Y and Z refer to specific illuminants and observers.
//Common reference values are available below in this same page.

var_X = X / Reference-X
var_Y = Y / Reference-Y
var_Z = Z / Reference-Z

if ( var_X > 0.008856 ) var_X = var_X ^ ( 1/3 )
else                    var_X = ( 7.787 * var_X ) + ( 16 / 116 )
if ( var_Y > 0.008856 ) var_Y = var_Y ^ ( 1/3 )
else                    var_Y = ( 7.787 * var_Y ) + ( 16 / 116 )
if ( var_Z > 0.008856 ) var_Z = var_Z ^ ( 1/3 )
else                    var_Z = ( 7.787 * var_Z ) + ( 16 / 116 )

CIE-L* = ( 116 * var_Y ) - 16
CIE-a* = 500 * ( var_X - var_Y )
CIE-b* = 200 * ( var_Y - var_Z )

Теперь, когда мы научились сравнивать цвета, можно приступать к реализации. В качестве игрового движка был выбран Unity. Я понимаю, что для такой игры его использование - оверхэд, но с Unity я был знаком, а для написания на "чистом" HTML/JS мне пришлось бы подтянуть их до должного уровня, поэтому выбрал остановился на нём.

Геймплей

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

Геймплей
Геймплей

Сложность игры и игровые достижения

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

  • в начале игры цвета нужно подбирать с точностью в 65% и выше, а с каждой следующей 1000 очков точность повышается на 5% до 95%

  • каждые 1000 очков немного увеличивается скорость шариков-целей

  • первые пару тысяч очков генерируются простые цвета, которые можно получить комбинацией 1-2 слайдеров, например, голубой – это G = 255 + B = 255

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

  • после того, как я выбрал площадку для публикации игры, используя методы предоставляемой SDK был добавлен лидерборд для авторизованных пользователей, чтобы подстегнуть соревновательный дух  

Экран Достижений
Экран Достижений

Отладка и баги

Будучи по профессии QA-инженером, было интересно какие ошибки могут возникнуть в таком маленьком проекте. Вот некоторые из них:

  1. В начале первой игры отображается экран с обучением, в этот момент, чтобы остановить время и генерацию целей я использовал свойство Time.timeScale = 0. Оказалось, что в значении 0 останавливается движение объектов, течение времени, но не инициализации префабов(игровых объектов). Поэтому, при клике мимо кнопки закрытия окна с обучением, каждый раз создавался снаряд, что приводило к следующему:

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

Публикация игры

В качестве платформы для сборки я выбрал WebGL, как преемника Flash.

Не знаю как так вышло, но арендовать VPS и отлаживать игру на нем мне оказалось проще, чем отлаживать локально. К тому же это позволило делиться игрой с друзьями, чтобы узнать их мнение. Когда я закончил создании игры, я решил опубликовать её на каких-нибудь площадках, чтобы посмотреть будет ли интересна эта игра кому-то ещё. И тут начались настоящие трудности.
Мне совершенно не приходило в голову, как можно назвать игру? Я даже пытался сгенерировать его через ChatGPT, а когда первое название было придумано, то оказалось, что игра с таким названием уже есть на площадке. В итоге я остановился на незамысловатом «Ловец Цвета».

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

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

Признайтесь, сразу захотелось кликнуть по игре :)
Признайтесь, сразу захотелось кликнуть по игре :)

Итоги

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

Спасибо за внимание! Буду рад ответить на ваши вопросы.

Источник: https://habr.com/ru/articles/787778/


Интересные статьи

Интересные статьи

Nintendo начала закрывать онлайн-сервисы на Wii U и 3DS в декабре, хотя планировала сделать это в апреле 2024 года. Об этом сообщил ресурс Nintendo Life со ссылкой на жалобы владельцев приставок.
В статье поговорим о Dependency Injection. Обсудим что такое чистый DI, коснемся DI контейнеров и их эволюции. В основной части речь пойдет о генераторе исходного кода Pure.DI...
В первой части статьи мы последовательно рассмотрели шаги по созданию и преобразования приложения для Android, необходимыми для реализации тестов, начиная от Unit-тестирования и заканчивая E2E-тестами...
Эта статья — развернутый ответ на вопрос, который нам периодически задают: чем werf отличается от Helm? На первый взгляд можно предположить, что задача у них примерно оди...
О российской часовой мануфактуре Константина Чайкина я впервые узнал несколько лет назад, когда случайно встретил информацию о часах «Луноход», которые показывали лунные фазы. Позже, около го...