Наверх

Посты по тегу Челленджи)

Фильтр и сортировка

Собака бывает кусачей

Пёс Кошачий

Немножко про свг-геймс

Всем ку

Уже больше полугода прошло с момента выхода уникального в плане пользования механик тест challenge? от Etern. Более того, он (надеюсь, Etern идентифицирует себя мужчиной, тем не менее) выпустил пост-гайд по таким челленджам, который мне кажется немножко сумбурным и требующим каких-то предварительных знаний, но тем не менее, для какой-то базы по кода можете почитать, пояснять в этом посте я этого не буду.

Еще полгода назад я грезил возможностями, но уперся в ограничения редактора, однако за последние две недели я очень сильно продвинулся в сием мероприятии, потому поделюсь своими открытиями с вами. 

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

Немножко про свг-геймс

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

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

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

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

Теперь нужно вообще понять, как работает закраска на пикуко. Итак, в svg имеются объекты - по сути те самые кляксы, которые вы рисуете. Сервер пикуко берет эти элементы и присваивает им CSS-правило, по которому какой-то элемент будет выглядеть - в котором с пометкой !important прописан конкретный серый цвет. При вводе правильного ответа идет обращение к серверу, который находит то, какой объект был привязан к данному ответу и какой цвет нужно присвоить, и просто накидывает поверх серого правила новое правило (дальше буду называть стилем для простоты) с уже нужным цветом. 

В данном случае нам важно запомнить две вещи - обработка изображения идет таким образом, что заданный сайтом стиль имеет строгий приоритет над тем, что задают стили свг. Также важно - цвет прописан именно в стиле и с пометкой !important (эта пометка значит, что правило, прописанное под ним важнее всего, что мы пытается сделать дальше, и он никак не поменяется). И наконец - правило стиля нашего объекта не меняется, а просто создает новое на странице вашего браузера. 

Также уточню еще один важный момент - описанное выше актуально не для всего. Данный стиль не применяется к объектам ellipse (эллипс), line (линия), polyline (ломаная) и, внезапно, image (встроенное изображение). Другими словами, эти объекты сохранят тот же цвет, что вы задали в свг. Дополнительно отмечу, что обводке прямо указано сохранять цвет, так что в теории можно обойтись с помощью нее, но по моему опыту это работает кривовато. Можно исхищряться и делать все через эллипсы, если вам нужно будет, например, понизить вес картинки и сохранить цвета (хотя на практике проверено - пнгшки более чем успешно сохраняются в свгшках на пикуко)

Немножко про свг-геймс

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

Самое первое, что нас беспокоит - сложность покраски. Поскольку суть челленджей заключена в покраске свг-карты, все правила, связанные с цветами заданы очень строго, потому придется исхищряться (хотя никто не запрещает вам все-таки передать покраску на пользователей). Тем не менее, есть сразу несколько способов задать цвета насильно (уточняю - через фотошоп не проверял, смотрел в фигме):
1) Описал еще Etern в своем посте и я же упоминал выше - нарисовать все нужные элементы с помощью ellipse, line и polyline. Для меня это выглядит как какая-то лютая дрочь, но тем не менее метод более, чем рабочий. Но тоже важно учитывать - если вы выгрузите идеальный круг (ширина и высота одинаковые), то при экспорте данная фигура будет помечена как circle, а на него правило покраски идет как на обычные объекты. Поэтому делайте ширину или высоту хотя бы на 1 десятую пикселя больше (фигма поддерживает дробные значения).

2) Использовать пнг-картинку. Для этого можно просто закинуть пнгшку в какую-нибудь фигму и сделать экспорт в svg. На самом деле, для всех сложных неинтерактивных элементов это лучший вариант. НО! Очень важный момент. Картинку лучше вставить в первородной форме, т.е. не делать заливку изображением, а именно засунуть само изображение. Заливку пикуко съест.
Из-за этого есть 2 проблемы. Во-первых, у нас все еще есть ограничение в 512КБ, а пнгшки довольно жирные по памяти, так что придется ужиматься (к слову, при добавлении в свг пнг еще и жирнее станет примерно на треть). Вторая проблема - к сожалению, графические редакторы вроде фигмы делают именно заливку, так что придется поработать ручками с кодом свг. Дальше задроченное описание, можете скипнуть:

Открываете файл, например, через блокнот. В начале файла у вас прописываются все объекты, которые если в вашем файле с помощью, в нашем случае интересует <rect> (если мы встраиваем изображение, чаще всего оно вписывается в прямоугольник). При выгрузке из фигмы он будет ссылаться на какой-то pattern, сможете найти дальше по файлу. Проверяет, что данный паттерн (запись начинается с <pattern id= *ваш айди*) ссылается на изображение (должно быть <use xlink:href= и какой-то #image)
Когда это нашли, удаляем строку <rect, ссылающуюся на наше изображение. После удаляем и все, начиная с соответствующего <pattern>, заканчивая </pattern>. В конце берем наше изображение - все, что начинается с <image id= и вставляем на то место, где стоял rect. Таким образом мы меняем логику изображения - вместо того, чтобы делать прямоугольник, у которого добавлена заливка изображением, мы вставляем само изображение. Также изображение будет окружено строками <defs> и </defs>. Они нам не нужны, так что можно их удалить, но в целом, если вы их оставите, ничего не поменяется - картинка все так же отобразится, важно вынести само изображение из этих строк


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

3) Для каждого объекта вручную прописать пометку !important. Опять же, для этого нужно открывать код, но зато вариант дает полный контроль над свгшкой - находите ваши объекты (например, rect) и ищете свойство style ="fill: . Дальше будет прописан цвет - либо цветовым кодом, либо просто словом (red). Внутри этих же кавычек сразу после цвета пишете !important и вуаля - все готово (еще нюанс - если style= не был прописан, пропишите его, а fill внесите в кавычки)
Для кода помните правило - объект, написанный ниже, находится выше. По сути первая часть - это ваши слои с обозначением, кто за кем идет, к которым потом добавляются какие-то правила заполнения

Как видите, все три способа дают цвет без необходимости его закраски.

Как видите, все три способа дают цвет без необходимости его закраски.

Второй вопрос, который у нас возникает - как нам взаимодействовать с ответами, введенными на пикуко? Тыкаться в картинку - это, конечно, круто, но хотелось бы, чтобы и сам смысл челленджа был сохранен (как вот в этом моем тесте или в этом моем тесте)

Что ж, есть у меня ответ и на это. Есть в CSS такая функция - :has(). По сути он проверяет наличие какого-то элемента на странице в данный момент. И у нас есть два варианта - в котором нам неважно, в каком порядке введут ответы (как в 100 к 1) и тот, где мы точно знаем порядок (как в тесте про уток)

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

Важно! Если вы используете первый вариант, нужно обязательно, чтобы цвет на пикуко совпадал с цветом в вашем свг-файле. Если цвет будет отличаться хоть на 1 последнюю цифру, все перестанет работать.

Дальше немного по коду, можете пропустить, но если хотите прописывать это самостоятельно или дать пояснения нейронке, можете почитать (или скопировать)

Пример такого кода:
<style>
 .challenge__window:has( - запускаем проверку в рамках окна челленжа на наличие css-правила
  .challenge__answer[style*="border-color: rgb(255, 202, 40)"] span style - в данном случае проверяет появление правила на конкретный цвет. По возможности желательно и hex-код прописать, но и ргб в целом хватает (если что, проверяется не сам цвет, а запись, так что лучше задать явно)
)
#my-pikuco-game .your-object {} - в фигурных скобках прописываем, что должно произойти. Любая анимация, движение, изменение размеров, изменение прозрачности и тд
</style> - закрываем блок.
Второй вариант реализуется примерно так
 .challenge__window:has(
  .challenge__answer:nth-child(
   3 of .challenge__answer[style*="border-color"]
  ) span style и дальше как в прошлом коде - грубо говоря, проверь, чтобы css-правил стало три. По сути, можно применять, если до этого вы итак счетчик сделали

Картинка жабы для разгрузки

Картинка жабы для разгрузки

Следующий возможный вопрос - как нам выполнить запоминание какого-то значения? HTML и JS обрезаются, а CSS и SVG не дают памяти в явном виде. Тут нам на помощь придет такая вещь, как фокус.

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

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


<style>
 #my-pikuco-game #focus-object {
  opacity: 1; - задаем начальное состояние (100% непрозрачно)
  transition: opacity 0.3s; - задаем время анимации (для мгновенного можно 1ms поставить)
 }
 #my-pikuco-game #focus-object:focus { - пишем, что при фокусе должно что-то произойти
  opacity: 0.4; - задаем конечное состояние
 }
</style>
<rect - находим нужный нам объект
 id="focus-object" - в айдишник
 tabindex="0" - получаем фокус при клике
И остальные свойства оставляем как есть


Второй вопрос - что делать, если мы хотим обратить анимацию вспять (как в этом моем тесте). Тут все несколько сложнее, но вполне реализуемо. Значится, помимо времени перехода мы также можем задать и время действия анимации. По сути мы просто создаем два фокуса в одной области - и в каждом запускает анимацию на срок больше, чем идет тест (например, миллиард лет). Настраиваем кнопку переключения режима, которые присваивают определенное значение заданной переменной, которая, в свою очередь, определит, какая из областей будет активна в данный момент. А затем каждой из них задаем анимацию на фокус. Пример кода:

<a href="#flagmode">...</a><a href="#erasemode">...</a> - задаем кнопки преключения режимов
.flaghit, .erasehit { - переменные 
pointer-events: none; - начальное состояние, не взаимодействуем с ними
}
#flagmode:target ~ #main #assembly .flaghit { 
pointer-events: all; - при активации принимаем все действия
}
#erasemode:target ~ #main #assembly .erasehit {
pointer-events: all; - то же самое, но со вторым режимом
}
<a href="#flagmode">...</a>
<a href="#erasemode">...</a> - задаем кнопки преключения режимов
.flaghit, .erasehit { - переменные
 pointer-events: none; - начальное состояние, не взаимодействуем с ними
}
#flagmode:target ~ #main #assembly .flaghit {
 pointer-events: all; - при активации принимаем все действия
}
#erasemode:target ~ #main #assembly .erasehit {
pointer-events: all; - то же самое, но со вторым режимом
}
.flagState {
visibility: hidden; - пишем, что в конце флаг должен оказаться скрыт
transition: visibility 1000000000s step-end; - указываем, что событие должно произойти после конца анимации через миллиард секунд
}


Картинка жабы для разгрузки

Картинка жабы для разгрузки

Последний вопрос - как сделать счетчик очков. Тут все просто. Мы должны явно прописать все варианты очков на экране, а после проверять, какой из них мы должны показывать в данный момент. Делаем длинную вертикальную ленту со всеми вариантами количества и перемещаем их вниз-вверх в зависимости от количества очков (допустим, для 10 очков сдвигаем ленту вниз на 100 пикселей, на 20 - на 200 пикселей вниз и тд)

Отлично, но как понять, что настало время показывать определенное количество очков? А вот тут интересно. Сначала мы задаем анимацию движения - например, начисляя 10 очков, сдвигаем ленту на 100 пикселей вверх, а на 5 очков - на 50 пикселей вверх. После мы привязываем каждой кнопке соответствующее действие. И дальше главный прикол - мы создаем группу для первой анимации, затем кладем ее внутрь группы второй анимации, и так вкладываем до конца. 

Зачем это делать именно так? Из-за того, что мы пользуемся фокусом, мы по сути просто создаем заранее все возможные варианты действия. Мы не запускаем анимацию одну за другой, а просто запускаем другую анимацию. И тут важный момент - фокус в данном случае переходит с одной кнопки на другую, почему же данные сохраняются? Ответ прост - фокус просто запускает воздействие (анимацию, в данном случае), которая не была зациклена, она постоянно назначена группе с момента, как проигралась, а потеря фокуса просто ставит анимацию на паузу, сохраняя ее в том же финальном состоянии. Это значит, что при потере фокуса все тупо зависнет в финальном состоянии, как и было

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


<style>
 #my-pikuco-game .points10 { - задаем правила работы анимации
  animation: plus10 1ms step-end both paused; } - изначально на паузе, после конца застывает в новом состоянии
 #my-pikuco-game .points5 { - то же самое, но для другого объекта (points5)
  animation: plus5 1ms step-end both paused; }
 @keyframes plus10 { - описываем анимацию plus10, которую задавали для объекта points10 выше
  from { transform: translateY(0); } - изначальное положение - 0 по Y
  to { transform: translateY(-100px); } - новое положение на 100 пикселей выше
}
 @keyframes plus5 { - то же самое для 5 очков
  from { transform: translateY(0); } 
  to { transform: translateY(-50px); }
 }
 #my-pikuco-game #add10:focus ~ #counter .points10 { - прописываем, что переключении фокуса (например, при клике) на кнопку add10 запускаем анимацию из группы points10 внутри группы counter
  animation-play-state: running; }- собственно, описываем, что нужно запустить при фокусе
 #my-pikuco-game #add5:focus ~ #counter .points5 { - то же самое для 5 очков. Прописывать весь путь необязательно, сверху главную группу, затем ту, на которую ссылаемся
  animation-play-state: running; - запускаем анимацию}
</style>

<g id="counter"> - создаем главную группу
 <g class="points10"> - внутрь этой группы вложили группу для 10 очков
  <g class="points5"> - внутрь предыдущей группы вложили группу для 5 очков
  </g>
   <!-- Здесь числовая лента --> просто последовательный набор числе друг под другом на фиксированном рассторянии друг от друга. Например,
   <text x="0" y="8">0</text>
   <text x="0" y="18">1</text>
   <text x="0" y="28">2</text>
   <text x="0" y="38">3</text>
 </g>
</g> - закрываем все 3 группы.
Важно, чтобы у всех групп был одинаковый родитель (в данном случае counter). И еще - такие кнопки одноразовые. То есть дважды нажать кнопку и прибавить еще очки не получится. Вы можете переместить на то же место другую кнопку, но такое ограничение важно учитывать

Немножко про свг-геймс

Мда, жаловаться на то, что пост Etern был непонятным и написать еще более задроченное чтиво - это я умею. Ну, а чего вы еще ожидали? На самом деле, я сомневаюсь, что кто-то будет прописывать это самостоятельно. Это очень большая дрочь, даже когда ты понимаешь, что делаешь, а большинство этого не будут понимать, потому что в их жизни есть что-то кроме программирования. Но 26 год - год нейрослопа, а нейронки очень хорошо умеют повторять задачи. Так что вам достаточно просто прислать нейронке описание действий, и она все пропишет сама. В общем, надеюсь, этот гайд кому-то что-то даст, а я за сим откланиваюсь

Прощальный ку

Теги: PikucoЧелленджи)
0
37
37
10комментариев180просмотров
Вы прочитали всё, отличная работа!