Tworzenie komponentu menu gry 3D

Podstawowe informacje o tym, jak stworzyć elastyczne, elastyczne i łatwo dostępne menu gier 3D.

W tym poście chcę pokazać, jak stworzyć komponent menu gry 3D. Wypróbuj demo.

Demonstracja
.

Jeśli wolisz film, oto wersja tego posta w YouTube:

Omówienie

Gry wideo często mają kreatywne i nietypowe menu, animowane i w przestrzeni 3D. To popularne rozwiązanie w nowych grach AR/VR, ponieważ menu wygląda jak unosi się w przestrzeni kosmicznej. Dzisiaj omówimy podstawy tego efektu, ale dostosowywane kolorystycznie oraz opcje dostosowania którzy preferują mniej ruchu.

HTML

Menu gry to lista przycisków. Najlepszym sposobem pokazania tego w kodzie HTML jest następujące:

<ul class="threeD-button-set">
  <li><button>New Game</button></li>
  <li><button>Continue</button></li>
  <li><button>Online</button></li>
  <li><button>Settings</button></li>
  <li><button>Quit</button></li>
</ul>

Lista przycisków będzie dobrze widoczna dla technologii czytników ekranu. działa bez JavaScriptu i CSS.

w
bardzo ogólna lista punktowana ze zwykłymi przyciskami jako elementami.

CSS

Styl listy przycisków dzieli się na te ogólne kroki:

  1. Konfiguruję właściwości niestandardowe.
  2. Układ flexbox.
  3. Własny przycisk z ozdobnymi pseudoelementami.
  4. Umieszczanie elementów w przestrzeni 3D.

Omówienie właściwości niestandardowych

Właściwości niestandardowe pomagają identyfikować wartości, nadając im realistyczne znaczenie w innych przypadkach przypadkowych wartościach, unikając powtarzania kodu i udostępniania wartości wśród dzieci.

Poniżej znajdują się zapytania o media zapisane jako zmienne CSS, znane również jako niestandardowe multimediów. Są to wymiary globalne, używane w różnych selektorach, aby kod był zwięzły i czytelny. Komponent menu gry używa ruchu ustawienia, kolor systemu schemat, i zakres kolorów wyświetlacz.

@custom-media --motionOK (prefers-reduced-motion: no-preference);
@custom-media --dark (prefers-color-scheme: dark);
@custom-media --HDcolor (dynamic-range: high);

Poniższe właściwości niestandardowe zarządzają schematem kolorów i przytrzymują kursor wartości pozycjonowania, dzięki którym menu gry będzie interaktywne i najeżdżać kursorem. Niestandardowe nazwy pomaga zachować czytelność kodu, ponieważ ujawnia przypadek użycia dla wartości lub przyjazna nazwa wyniku dla wartości.

.threeD-button-set {
  --y:;
  --x:;
  --distance: 1px;
  --theme: hsl(180 100% 50%);
  --theme-bg: hsl(180 100% 50% / 25%);
  --theme-bg-hover: hsl(180 100% 50% / 40%);
  --theme-text: white;
  --theme-shadow: hsl(180 100% 10% / 25%);

  --_max-rotateY: 10deg;
  --_max-rotateX: 15deg;
  --_btn-bg: var(--theme-bg);
  --_btn-bg-hover: var(--theme-bg-hover);
  --_btn-text: var(--theme-text);
  --_btn-text-shadow: var(--theme-shadow);
  --_bounce-ease: cubic-bezier(.5, 1.75, .75, 1.25);

  @media (--dark) {
    --theme: hsl(255 53% 50%);
    --theme-bg: hsl(255 53% 71% / 25%);
    --theme-bg-hover: hsl(255 53% 50% / 40%);
    --theme-shadow: hsl(255 53% 10% / 25%);
  }

  @media (--HDcolor) {
    @supports (color: color(display-p3 0 0 0)) {
      --theme: color(display-p3 .4 0 .9);
    }
  }
}

Tło w kształcie stożka w jasnym i ciemnym motywie

Jasny motyw ma intensywny stożkowy od cyan do deeppink gradient a ciemny ma ciemny, subtelny stożkowy gradient. Aby dowiedzieć się więcej można użyć gradientów stożkowych, patrz conic.style.

html {
  background: conic-gradient(at -10% 50%, deeppink, cyan);

  @media (--dark) {
    background: conic-gradient(at -10% 50%, #212529, 50%, #495057, #212529);
  }
}
.
, Prezentacja zmiany tła z preferowanego koloru jasnego na ciemny lub odwrotnie.

Włączanie perspektywy 3D

Aby elementy istniały w przestrzeni 3D strony internetowej, widoczny obszar z perspektywy wymaga zainicjowania. Postawiłem perspektywę na element body i użyliśmy jednostek widocznego obszaru, aby stworzyć styl, który mi się podoba.

body {
  perspective: 40vw;
}

Właśnie taki wpływ może wywrzeć ta perspektywa.

Określanie stylu listy przycisków <ul>

Ten element odpowiada za ogólny układ makra listy przycisków oraz w postaci interaktywnej i pływającej karty 3D. Oto sposób, aby to osiągnąć.

Układ grupy przycisków

Flexbox może zarządzać układem kontenera. Zmień domyślny kierunek Flex od wierszy do kolumn z wartościami flex-direction i upewnij się, że rozmiar każdego elementu wynosi jego zawartość zmienia się z stretch na start dla align-items.

.threeD-button-set {
  /* remove <ul> margins */
  margin: 0;

  /* vertical rag-right layout */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2.5vh;
}

Następnie ustaw kontener jako kontekst przestrzeni 3D i skonfiguruj CSS clamp() by zapobiec obróceniu karty poza czytelne obroty. Uwaga że środkowa wartość ograniczenia jest właściwością niestandardową, te --x i --y wartości zostaną ustawione w JavaScripcie po najechaniu na nie kursorem myszy interakcji później.

.threeD-button-set {
  

  /* create 3D space context */
  transform-style: preserve-3d;

  /* clamped menu rotation to not be too extreme */
  transform:
    rotateY(
      clamp(
        calc(var(--_max-rotateY) * -1),
        var(--y),
        var(--_max-rotateY)
      )
    )
    rotateX(
      clamp(
        calc(var(--_max-rotateX) * -1),
        var(--x),
        var(--_max-rotateX)
      )
    )
  ;
}

Następnie, jeśli ruch jest prawidłowy u gościa, dodaj w przeglądarce wskazówkę, przekształcenie tego elementu będzie się stale zmieniać wraz z will-change Aby włączyć interpolację, ustaw też transition na przekształceniach. Ten przejście następuje wtedy, gdy mysz wejdzie w interakcję z kartą, umożliwiając płynne przejścia do zmian rotacji. Jest to animacja wyświetlana przez cały czas. pokazującą trójwymiarową przestrzeń, w której znajduje się karta, nawet jeśli mysz nie jest w stanie, nie wchodzi w interakcję z komponentem.

@media (--motionOK) {
  .threeD-button-set {
    /* browser hint so it can be prepared and optimized */
    will-change: transform;

    /* transition transform style changes and run an infinite animation */
    transition: transform .1s ease;
    animation: rotate-y 5s ease-in-out infinite;
  }
}

Animacja rotate-y ustawia środkową klatkę kluczową tylko na 50%, ponieważ Przeglądarka 0% i 100% domyślnie zastosuje domyślny styl elementu. Ten jest skrótem od animacji naprzemiennych, które muszą zaczynać się i kończyć pozycji. To świetny sposób na przedstawienie nieskończonych naprzemiennych animacji.

@keyframes rotate-y {
  50% {
    transform: rotateY(15deg) rotateX(-6deg);
  }
}

Określanie stylu elementów <li>

Każdy element listy (<li>) zawiera przycisk i jego elementy obramowania. Styl elementu display został zmieniony i w elemencie nie wyświetla się ::marker Styl position jest ustawiona na relative, więc kolejne pseudoelementy przycisków mogą na całej powierzchni obszaru wokół przycisku.

.threeD-button-set > li {
  /* change display type from list-item */
  display: inline-flex;

  /* create context for button pseudos */
  position: relative;

  /* create 3D space context */
  transform-style: preserve-3d;
}

zrzut ekranu z listą obróconą w przestrzeni 3D w celu pokazania perspektywy;
każdy element listy nie ma już punktora.

Określanie stylu elementów <button>

Stylizowanie przycisków może być pracochłonne. Występuje wiele stanów i typów interakcji które trzeba uwzględnić. Przyciski te szybko się komplikują ze względu na pseudoelementów, animacji i interakcji.

Początkowe style (<button>)

Poniżej znajduje się lista stylów podstawowych, które będą obsługiwane w innych stanach.

.threeD-button-set button {
  /* strip out default button styles */
  appearance: none;
  outline: none;
  border: none;

  /* bring in brand styles via props */
  background-color: var(--_btn-bg);
  color: var(--_btn-text);
  text-shadow: 0 1px 1px var(--_btn-text-shadow);

  /* large text rounded corner and padded*/
  font-size: 5vmin;
  font-family: Audiowide;
  padding-block: .75ch;
  padding-inline: 2ch;
  border-radius: 5px 20px;
}

Zrzut ekranu przedstawiający listę przycisków w perspektywie 3D, tym razem ze stylem
przyciskami.

Pseudoelementy przycisku

Krawędzie przycisku to nie tradycyjne obramowanie, tylko pozycja bezwzględna pseudoelementów z obramowaniem.

Zrzut ekranu przedstawiający panel Elements Chrome Devtools z widocznym przyciskiem
::before i ::after.

Te elementy mają kluczowe znaczenie w prezentowaniu perspektywy 3D . Jeden z tych pseudoelementów zostanie odsunięty od przycisku, a jeden z nich będzie bliżej użytkownika. Efekt jest najbardziej zauważalny górnych i dolnych przycisków.

.threeD-button button {
  

  &::after,
  &::before {
    /* create empty element */
    content: '';
    opacity: .8;

    /* cover the parent (button) */
    position: absolute;
    inset: 0;

    /* style the element for border accents */
    border: 1px solid var(--theme);
    border-radius: 5px 20px;
  }

  /* exceptions for one of the pseudo elements */
  /* this will be pushed back (3x) and have a thicker border */
  &::before {
    border-width: 3px;

    /* in dark mode, it glows! */
    @media (--dark) {
      box-shadow:
        0 0 25px var(--theme),
        inset 0 0 25px var(--theme);
    }
  }
}

Style przekształceń 3D

Opcja poniżej transform-style jest ustawiona na preserve-3d, aby dzieci mogły przebywać w pokoju się na osi z. transform ma wartość --distance niestandardową, która zostanie zwiększona po najechaniu kursorem i zaznacz.

.threeD-button-set button {
  

  transform: translateZ(var(--distance));
  transform-style: preserve-3d;

  &::after {
    /* pull forward in Z space with a 3x multiplier */
    transform: translateZ(calc(var(--distance) / 3));
  }

  &::before {
    /* push back in Z space with a 3x multiplier */
    transform: translateZ(calc(var(--distance) / 3 * -1));
  }
}

Style animacji warunkowych

Jeśli użytkownik wyraża zgodę na ruch, przycisk informuje przeglądarkę, że właściwość przekształcenia powinna być gotowa do zmiany, a przejście jest ustawione dla Usługi transform i background-color. Zwróć uwagę na różnicę w wydawało mi się, że efekt jest delikatny, rozłożony.

.threeD-button-set button {
  

  @media (--motionOK) {
    will-change: transform;
    transition:
      transform .2s ease,
      background-color .5s ease
    ;

    &::before,
    &::after {
      transition: transform .1s ease-out;
    }

    &::after    { transition-duration: .5s }
    &::before { transition-duration: .3s }
  }
}

Style interakcji z najeżdżaniem kursorem i zaznaczeniem

Celem animacji interakcji jest rozłożenie warstw, z których składa się płaskiego przycisku. W tym celu ustaw zmienną --distance. początkowo do użytkownika 1px. Selektor widoczny w przykładowym kodzie poniżej sprawdza, czy urządzenie, które powinno zobaczyć komunikat, wskaźnik ostrości. Nie została aktywowana. Jeśli tak, korzysta z CSS, aby wykonać :

  • Zastosuj kolor tła po najechaniu kursorem.
  • Zwiększ dystans .
  • Dodaj efekt łatwego odbijania odbijania.
  • Rozłóż przejścia pseudoelementów.
.
.threeD-button-set button {
  

  &:is(:hover, :focus-visible):not(:active) {
    /* subtle distance plus bg color change on hover/focus */
    --distance: 15px;
    background-color: var(--_btn-bg-hover);

    /* if motion is OK, setup transitions and increase distance */
    @media (--motionOK) {
      --distance: 3vmax;

      transition-timing-function: var(--_bounce-ease);
      transition-duration: .4s;

      &::after  { transition-duration: .5s }
      &::before { transition-duration: .3s }
    }
  }
}

Perspektywa 3D nadal świetnie wyglądała w przypadku preferencji ruchu w usłudze reduced. Elementy górne i dolne prezentują efekt w subtelny sposób.

Małe ulepszenia w języku JavaScript

Interfejs obsługuje klawiatury, czytniki ekranu, pady do gier, ale możemy dodać kilka drobnych akcentów w JavaScripcie, aby ułatwić dostępnych scenariuszy.

Dodatkowe klawisze strzałek

Klawisz Tab to dobry sposób na poruszanie się po menu, ale tryb kierunkowy pada lub joysticka, aby przenieść zaznaczenie na pad do gier. Biblioteka roving-ux często używana w GUI. Interfejsy wyzwania będą za nas obsługiwać klawisze strzałek. Poniższy kod informuje zatrzymuje ostrość w obrębie .threeD-button-set i przekierowuje go na elementów podrzędnych.

import {rovingIndex} from 'roving-ux'

rovingIndex({
  element: document.querySelector('.threeD-button-set'),
  target: 'button',
})

Interakcja z paralaksą kursora myszy

Śledzenie myszy i przechylanie menu ma na celu naśladowanie AR i VR. w interfejsach gier wideo, gdzie zamiast myszy może znajdować się wirtualny wskaźnik. Może być zabawnie, gdy elementy są zbyt świadome wskaźnika.

Ponieważ jest to niewielka, dodatkowa funkcja, za pomocą zapytania zgodnie z preferencjami użytkownika dotyczącymi ruchu. Zapisuj też listę przycisków w ramach konfiguracji w pamięci za pomocą funkcji querySelector i zapisują granice elementu w pamięci podręcznej menuRect Na podstawie tych granic możesz określić przesunięcie obrotu zastosowane do karty na podstawie pozycji myszy.

const menu = document.querySelector('.threeD-button-set')
const menuRect = menu.getBoundingClientRect()

const { matches:motionOK } = window.matchMedia(
  '(prefers-reduced-motion: no-preference)'
)

Następnie potrzebujemy funkcji, która akceptuje pozycje x i y myszy oraz zwraca wartość, której możemy użyć do rotacji karty. Poniższa funkcja używa myszy aby ustalić, w której części pudełka i jak bardzo się znajduje. delta jest zwracana z funkcji.

const getAngles = (clientX, clientY) => {
  const { x, y, width, height } = menuRect

  const dx = clientX - (x + 0.5 * width)
  const dy = clientY - (y + 0.5 * height)

  return {dx,dy}
}

Na koniec obserwuj ruch myszy, przekaż informację o pozycji do funkcji getAngles() i używać wartości delta jako stylów właściwości niestandardowych. dzielę przez 20, aby wypełnić delta i ograniczania drgań, może być lepsze rozwiązanie. Jeśli pamiętaj o tym, że rekwizyty --x i --y znajdują się w środku clamp(), zapobiega to nadmiernemu obracaniu się kursora myszy w nieczytelne miejsce.

if (motionOK) {
  window.addEventListener('mousemove', ({target, clientX, clientY}) => {
    const {dx,dy} = getAngles(clientX, clientY)

    menu.attributeStyleMap.set('--x', `${dy / 20}deg`)
    menu.attributeStyleMap.set('--y', `${dx / 20}deg`)
  })
}

Tłumaczenia i wskazówki dojazdu

Podczas testowania menu gry w innych trybach pisania wystąpił błąd. języki.

Elementy <button> mają styl !important dla elementu writing-mode w ustawieniach użytkownika arkusza stylów agenta. Oznaczało to, że kod HTML menu gry trzeba było zmienić, aby dostosować zgodnie z oczekiwaniami. Zmiana listy przycisków na listę linków włącza funkcję logiczną właściwości, które zmieniają kierunek menu, ponieważ w elementach <a> nie ma przeglądarki podano styl !important.

Podsumowanie

Wiesz już, jak to robiłem. Jak to zrobisz‽ 🙂 Czy możesz dodać akcelerometr? interakcji z menu, więc ułożenie kafelków w telefonie spowoduje obrót menu? Czy możemy coś poprawić bez ruchu?

Stosujmy różne podejścia i poznajmy sposoby budowania obecności w internecie. Utwórz wersję demonstracyjną, a potem dodaj linki do funkcji tweetuj mi. znajdziesz poniżej w sekcji z remiksami na karcie Społeczność.

Remiksy utworzone przez społeczność

Na razie nic tu nie ma.