Kako sem naredil ugankarsko igro Pure CSS PlatoBlockchain Data Intelligence. Navpično iskanje. Ai.

Kako sem naredil puzzle igro Pure CSS

Nedavno sem odkril veselje do ustvarjanja iger samo s CSS. Vedno je fascinantno, kako sta HTML in CSS sposobna obvladati logiko celotne spletne igre, zato sem moral poskusiti! Takšne igre se običajno zanašajo na stari vdor v polje Checkbox, kjer združimo označeno/neoznačeno stanje vnosa HTML z :checked psevdorazred v CSS. S to kombinacijo lahko naredimo veliko čarovnije!

Pravzaprav sem se izzval, da zgradim celotno igro brez potrditvenega polja. Nisem bil prepričan, ali bo to mogoče, a vsekakor je, in pokazal vam bom, kako.

Poleg uganke, ki jo bomo preučevali v tem članku, sem naredil zbirka čistih iger CSS, večina brez vdora v polje Checkbox. (Na voljo so tudi na CodePen.)

Se želite igrati, preden začnemo?

Osebno raje igram igro v celozaslonskem načinu, vendar jo lahko igrate spodaj oz odpri ga tukaj.

Kul kajne? Vem, da to ni najboljša ugankarska igra, kar ste jih kdaj videli™, a tudi sploh ni slaba za nekaj, kar uporablja samo CSS in nekaj vrstic HTML. Z lahkoto lahko prilagodite velikost mreže, spremenite število celic za nadzor težavnostne stopnje in uporabite katero koli sliko, ki jo želite!

Skupaj bomo predelali ta demo, nato pa mu bomo na koncu dodali malo več iskrice za nekaj zagona.

Funkcionalnost povleci in spusti

Medtem ko je struktura sestavljanke dokaj enostavna s CSS Grid, je možnost vlečenja in spuščanja kosov sestavljanke nekoliko težavnejša. Moral sem se zanašati na kombinacijo prehodov, učinkov lebdenja in sorodnih izbirnikov, da sem to dosegel.

Če miškin kazalec premaknete nad prazno polje v tej predstavitvi, se slika premakne znotraj njega in tam ostane, tudi če premaknete kazalec iz polja. Trik je v tem, da dodate veliko trajanje prehoda in zakasnitev — tako veliko, da slika potrebuje veliko časa, da se vrne v začetni položaj.

img {
  transform: translate(200%);
  transition: 999s 999s; /* very slow move on mouseout */
}
.box:hover img {
  transform: translate(0);
  transition: 0s; /* instant move on hover */
}

Z navedbo samo transition-delay je dovolj, vendar uporaba velikih vrednosti za zakasnitev in trajanje zmanjša možnost, da igralec kdaj vidi, da se slika premakne nazaj. Če počakate na 999s + 999s — kar je približno 30 minut — potem boste videli, da se slika premika. Ampak ne boš, kajne? Mislim, nihče ne bo potreboval toliko časa med potezami, razen če ne odide iz igre. Torej se mi zdi to dober trik za preklapljanje med dvema stanjema.

Ste opazili, da se spremembe sprožijo tudi z lebdenjem na sliko? To je zato, ker je slika del elementa škatle, kar ni dobro za nas. To lahko popravimo z dodajanjem pointer-events: none na sliko, vendar je pozneje ne bomo mogli povleči.

To pomeni, da moramo vnesti še en element znotraj .box:

Ta dodatek div (uporabljamo razred .a) bo zavzela isto območje kot slika (zahvaljujoč CSS Grid in grid-area: 1 / 1) in bo element, ki sproži učinek lebdenja. In tu nastopi selektor brata in sestre:

.a {
  grid-area: 1 / 1;
}
img {
  grid-area: 1 / 1;
  transform: translate(200%);
  transition: 999s 999s;
}
.a:hover + img {
  transform: translate(0);
  transition: 0s;
}

Lebdenje na .a element premakne sliko, in ker zavzame ves prostor znotraj polja, je kot da lebdimo nad poljem! Lebdenje slike ni več problem!

Povleci in spusti našo sliko v polje in si oglej rezultat:

Ali si videl to? Najprej zgrabite sliko in jo premaknete v polje, nič posebnega. Ko pa sliko sprostite, sprožite učinek lebdenja, ki premakne sliko, nato pa simuliramo funkcijo povleci in spusti. Če izpustite miško izven polja, se ne zgodi nič.

Hmm, vaša simulacija ni popolna, ker lahko tudi premaknemo polje in dosežemo enak učinek.

Res je in to bomo popravili. Učinek lebdenja moramo onemogočiti in ga dovoliti le, če sprostimo sliko znotraj polja. Igrali se bomo z razsežnostjo našega .a element, da se to zgodi.

Zdaj lebdenje na polje ne naredi nič. Če pa začnete vleči sliko, bo .a element se prikaže in ko ga sprostimo znotraj polja, lahko sprožimo učinek lebdenja in premaknemo sliko.

Razčlenimo kodo:

.a {
  width: 0%;
  transition: 0s .2s; /* add a small delay to make sure we catch the hover effect */
}
.box:active .a { /* on :active increase the width */
  width: 100%;
  transition: 0s; /* instant change */
}
img {
  transform: translate(200%);
  transition: 999s 999s;
}
.a:hover + img {
  transform: translate(0);
  transition: 0s;
}

S klikom na sliko se sproži :active psevdorazred, ki naredi .a element polne širine (na začetku je enak 0). Aktivno stanje bo ostalo aktivna dokler ne izdamo slike. Če sprostimo sliko znotraj škatle, se .a element se vrne v width: 0, vendar bomo sprožili učinek lebdenja, preden se to zgodi in bo slika padla v okvir! Če ga izpustite izven škatle, se ne zgodi nič.

Obstaja majhna posebnost: klik na prazno polje tudi premakne sliko in prekine našo funkcijo. trenutno, :active je povezan z .box element, tako da ga bo aktiviral klik nanj ali katerega koli od njegovih podrejenih elementov; in s tem na koncu pokažemo .a element in sproži učinek lebdenja.

To lahko popravimo tako, da se igramo z pointer-events. Omogoča nam, da onemogočimo kakršno koli interakcijo z .box ob ohranjanju interakcij z otroškimi elementi.

.box {
  pointer-events: none;
}
.box * {
  pointer-events: initial;
}

zdaj naša funkcija povleci in spusti je popolna. Razen če ne najdete, kako jo vdreti, je edini način, da premaknete sliko, da jo povlečete in spustite v okvir.

Sestavljanje mreže uganke

Sestavljanje sestavljanke se bo v primerjavi s tem, kar smo pravkar naredili za funkcijo povleci in spusti, zdelo enostavno. Pri ustvarjanju sestavljanke se bomo zanašali na trike mreže CSS in ozadja.

Tukaj je naša mreža, napisana v jeziku Pug zaradi priročnosti:

- let n = 4; /* number of columns/rows */
- let image = "https://picsum.photos/id/1015/800/800";

g(style=`--i:url(${image})`)
  - for(let i = 0; i < n*n; i++)
    z
      a
      b(draggable="true") 

Koda je morda videti čudna, vendar se prevede v navaden HTML:

<g style="--i: url(https://picsum.photos/id/1015/800/800)">
 <z>
   <a></a>
   <b draggable="true"></b>
 </z>
 <z>
   <a></a>
   <b draggable="true"></b>
 </z>
 <z>
   <a></a>
   <b draggable="true"></b>
 </z>
  <!-- etc. -->
</g>

Stavim, da se sprašujete, kaj je s temi oznakami. Nobeden od teh elementov nima posebnega pomena - ugotovil sem le, da je kodo veliko lažje napisati z uporabo <z> kot kup <div class="z"> ali karkoli že.

Takole sem jih preslikal:

  • <g> je naš mrežni vsebnik, ki vsebuje N*N <z> elemente.
  • <z> predstavlja naše elemente mreže. Igra vlogo .box element, ki smo ga videli v prejšnjem razdelku.
  • <a> sproži učinek lebdenja.
  • <b> predstavlja delček naše podobe. Uporabljamo draggable nanj, ker ga privzeto ni mogoče povleči.

V redu, registrirajmo naš mrežni vsebnik <g>. To je v Sass namesto v CSS:

$n : 4; /* number of columns/rows */

g {
  --s: 300px; /* size of the puzzle */

  display: grid;
  max-width: var(--s);
  border: 1px solid;
  margin: auto;
  grid-template-columns: repeat($n, 1fr);
}

Pravzaprav bomo svoje mrežne otroke naredili – <z> elementi — tudi mreže in imajo oboje <a> in <b> znotraj istega mrežnega območja:

z {
  aspect-ratio: 1;
  display: grid;
  outline: 1px dashed;
}
a {
  grid-area: 1/1;
}
b {
  grid-area: 1/1;
}

Kot lahko vidite, nič posebnega – ustvarili smo mrežo določene velikosti. Preostali del CSS-ja, ki ga potrebujemo, je za funkcijo povleci in spusti, ki od nas zahteva, da naključno postavimo kose po plošči. Za to se bom obrnil na Sass, spet zaradi udobja, da se lahko premikam po vseh delih sestavljanke in jih oblikujem s funkcijo:

b {
  background: var(--i) 0/var(--s) var(--s);
}

@for $i from 1 to ($n * $n + 1) {
  $r: (random(180));
  $x: (($i - 1)%$n);
  $y: floor(($i - 0.001) / $n);
  z:nth-of-type(#{$i}) b{
    background-position: ($x / ($n - 1)) * 100% ($y / ($n - 1)) * 100%;
    transform: 
      translate((($n - 1) / 2 - $x) * 100%, (($n - 1)/2 - $y) * 100%) 
      rotate($r * 1deg) 
      translate((random(100)*1% + ($n - 1) * 100%)) 
      rotate((random(20) - 10 - $r) * 1deg)
   }
}

Morda ste opazili, da uporabljam Sass random() funkcijo. Tako dobimo naključne položaje za koščke sestavljanke. Ne pozabite, da bomo onemogočiti ta položaj, ko lebdite nad <a> element po vlečenju in spuščanju njegovega ustreznega <b> element znotraj mrežne celice.

z a:hover ~ b {
  transform: translate(0);
  transition: 0s;
}

V tej isti zanki definiram tudi konfiguracijo ozadja za vsak kos sestavljanke. Vsi bodo logično delili isto sliko kot ozadje, njena velikost pa mora biti enaka velikosti celotne mreže (definirano z --s spremenljivka). Uporaba istega background-image in nekaj matematike, posodobimo background-position za prikaz le delčka slike.

To je to! Naša uganka samo za CSS je tehnično pripravljena!

Ampak vedno smo lahko boljši, kajne? pokazal sem ti kako narediti mrežo oblik kosov sestavljanke v drugem članku. Vzemimo isto idejo in jo uporabimo tukaj, kajne?

Oblike koščkov sestavljanke

Tukaj je naša nova ugankarska igra. Enaka funkcionalnost, vendar z bolj realističnimi oblikami!

To je ilustracija oblik na mreži:

Kako sem naredil puzzle igro Pure CSS

Če pogledate natančno, boste opazili, da imamo devet različnih oblik sestavljank: štirje vogalije štiri robovein ena za vse ostalo.

Mreža kosov sestavljanke, ki sem jo naredil v drugem članku, ki sem ga omenil, je nekoliko bolj preprosta:

Uporabimo lahko isto tehniko, ki združuje maske CSS in prelive, da ustvarimo različne oblike. V primeru, da ne poznate mask in prelivi toplo priporočam, da preverite tisti poenostavljeni primer da bolje razumete tehniko, preden preidete na naslednji del.

Najprej moramo uporabiti posebne izbirnike za ciljanje vsake skupine elementov, ki imajo enako obliko. Imamo devet skupin, zato bomo uporabili osem izbirnikov in privzeti izbirnik, ki izbere vse.

z  /* 0 */

z:first-child  /* 1 */

z:nth-child(-n + 4):not(:first-child) /* 2 */

z:nth-child(5) /* 3 */

z:nth-child(5n + 1):not(:first-child):not(:nth-last-child(5)) /* 4 */

z:nth-last-child(5)  /* 5 */

z:nth-child(5n):not(:nth-child(5)):not(:last-child) /* 6 */

z:last-child /* 7 */

z:nth-last-child(-n + 4):not(:last-child) /* 8 */

Tukaj je slika, ki prikazuje, kako se to preslika v našo mrežo:

Kako sem naredil ugankarsko igro Pure CSS PlatoBlockchain Data Intelligence. Navpično iskanje. Ai.
Kako sem naredil puzzle igro Pure CSS

Zdaj pa se lotimo oblik. Osredotočimo se na učenje le ene ali dveh oblik, ker vse uporabljajo isto tehniko – in tako imate nekaj domače naloge, ki se jo morate učiti!

Za dele sestavljanke na sredini mreže, 0:

mask: 
  radial-gradient(var(--r) at calc(50% - var(--r) / 2) 0, #0000 98%, #000) var(--r)  
    0 / 100% var(--r) no-repeat,
  radial-gradient(var(--r) at calc(100% - var(--r)) calc(50% - var(--r) / 2), #0000 98%, #000) 
    var(--r) 50% / 100% calc(100% - 2 * var(--r)) no-repeat,
  radial-gradient(var(--r) at var(--r) calc(50% - var(--r) / 2), #000 98%, #0000),
  radial-gradient(var(--r) at calc(50% + var(--r) / 2) calc(100% - var(--r)), #000 98%, #0000);

Koda je morda videti zapletena, vendar se osredotočimo na en gradient naenkrat, da vidimo, kaj se dogaja:

Dva preliva ustvarita dva kroga (v predstavitvi označena z zeleno in vijolično), druga dva preliva pa ustvarita reže, s katerimi se povezujejo drugi kosi (tisti, ki je označen z modro, zapolni večino oblike, medtem ko tisti, ki je označen z rdečo, zapolni zgornji del). spremenljivka CSS, --r, nastavi polmer krožnih oblik.

Kako sem naredil ugankarsko igro Pure CSS PlatoBlockchain Data Intelligence. Navpično iskanje. Ai.
Kako sem naredil puzzle igro Pure CSS

Oblika kosov sestavljanke v sredini (označeno 0 na sliki) je najtežje narediti, saj uporablja štiri prelive in ima štiri ukrivljenosti. Vsi drugi deli žonglirajo z manj prelivi.

Na primer, koščki sestavljanke vzdolž zgornjega roba sestavljanke (označeni 2 na sliki) uporablja tri prelive namesto štirih:

mask: 
  radial-gradient(var(--r) at calc(100% - var(--r)) calc(50% + var(--r) / 2), #0000 98%, #000) var(--r) calc(-1 * var(--r)) no-repeat,
  radial-gradient(var(--r) at var(--r) calc(50% - var(--r) / 2), #000 98%, #0000),
  radial-gradient(var(--r) at calc(50% + var(--r) / 2) calc(100% - var(--r)), #000 98%, #0000);

Odstranili smo prvi (zgornji) gradient in prilagodili vrednosti drugega gradienta tako, da pokriva prostor, ki ostane zadaj. Če primerjate oba primera, ne boste opazili velike razlike v kodi. Upoštevati je treba, da lahko najdemo različne konfiguracije ozadja, da ustvarimo isto obliko. Če se začnete igrati s prelivi, boste zagotovo našli nekaj drugačnega od tega, kar sem jaz. Lahko celo napišete kaj bolj jedrnatega - če je tako, to delite v komentarjih!

Poleg ustvarjanja oblik boste ugotovili tudi, da povečujem širino in/ali višino elementov, kot je prikazano spodaj:

height: calc(100% + var(--r));
width: calc(100% + var(--r));

Kosi sestavljanke morajo preseči mrežno celico, da se povežejo.

Kako sem naredil ugankarsko igro Pure CSS PlatoBlockchain Data Intelligence. Navpično iskanje. Ai.
Kako sem naredil puzzle igro Pure CSS

Končni demo

Tukaj je spet celoten demo. Če jo primerjate s prvo različico, boste videli isto strukturo kode za ustvarjanje mreže in funkcijo povleci in spusti ter kodo za ustvarjanje oblik.

Možne izboljšave

Članek se tukaj konča, vendar bi lahko še naprej izboljševali našo uganko s še več funkcijami! Kaj pa časovnik? Ali morda nekakšna čestitka, ko igralec dokonča sestavljanko?

Vse te funkcije bom morda upošteval v prihodnji različici, torej bodite pozorni na moj GitHub repo.

Zavijanje

in CSS ni programski jezik, pravijo. ha!

S tem ne poskušam sprožiti #HotDrama. To pravim, ker smo naredili nekaj res zapletenih logičnih stvari in ob tem zajeli veliko lastnosti in tehnik CSS. Igrali smo se z mrežo CSS, prehodi, maskiranjem, prelivi, izbirniki in lastnostmi ozadja. Da ne omenjam nekaj trikov Sass, ki smo jih uporabili za enostavno prilagajanje kode.

Cilj ni bil sestaviti igro, ampak raziskati CSS in odkriti nove lastnosti in trike, ki jih lahko uporabite v drugih projektih. Ustvarjanje spletne igre v CSS je izziv, ki vas prisili, da zelo podrobno raziščete funkcije CSS in se naučite, kako jih uporabljati. Poleg tega je zelo zabavno, da dobimo nekaj za igranje, ko je vse povedano in storjeno.

Ne glede na to, ali je CSS programski jezik ali ne, ne spremeni dejstva, da se vedno učimo z gradnjo in ustvarjanjem inovativnih stvari.

Časovni žig:

Več od Triki CSS