3D गेम मेन्यू कॉम्पोनेंट बनाना

रिस्पॉन्सिव, अडैप्टिव, और ऐक्सेस किए जा सकने वाले 3D गेम मेन्यू बनाने के तरीके के बारे में बुनियादी जानकारी.

इस पोस्ट में, हम आपको बताना चाहते हैं कि गेम मेन्यू का 3D कॉम्पोनेंट बनाने के बारे में कैसे सोचें. डेमो आज़माएं.

डेमो

अगर आपको वीडियो देखना है, तो इस पोस्ट का YouTube वर्शन यहां देखें:

खास जानकारी

वीडियो गेम में अक्सर उपयोगकर्ताओं को ऐनिमेट किया गया और 3D स्पेस में एक क्रिएटिव और असामान्य मेन्यू दिया जाता है. यह नए एआर/वीआर गेम में लोकप्रिय है, क्योंकि मेन्यू को स्पेस में तैरता हुआ दिखता है. आज हम इस इफ़ेक्ट की सबसे ज़रूरी चीज़ों को फिर से तैयार करेंगे. हालांकि, हम कम मोशन इफ़ेक्ट को पसंद करने वाले लोगों के लिए, अडैप्टिव कलर स्कीम और ठहरने की कुछ सुविधाएं उपलब्ध कराएंगे.

एचटीएमएल

गेम मेन्यू, बटन की सूची होती है. इसे एचटीएमएल में दिखाने का सबसे अच्छा तरीका यह है:

<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>

स्क्रीन रीडर टेक्नोलॉजी और JavaScript या सीएसएस के बिना काम करने वाले बटन के बारे में, बटन की सूची बेहतर तरीके से बताएगी.

आइटम के रूप में
सामान्य बटन के साथ एक बेहद सामान्य दिखने वाली बुलेट सूची है.

सीएसएस

बटन की सूची का स्टाइल, यहां दिए गए हाई लेवल के चरणों में बंट जाता है:

  1. कस्टम प्रॉपर्टी सेट अप की जा रही हैं.
  2. एक फ़्लेक्सबॉक्स लेआउट.
  3. सजावटी नकली एलिमेंट वाला कस्टम बटन.
  4. एलिमेंट को 3D स्पेस में डालना.

कस्टम प्रॉपर्टी की खास जानकारी

कस्टम प्रॉपर्टी, बिना किसी क्रम वाली दिखने वाली वैल्यू को काम के नाम देकर, वैल्यू को अलग करने में मदद करती हैं. ऐसा करने से, बार-बार कोड डालने और बच्चों के बीच वैल्यू शेयर करने से बचा जाता है.

नीचे सीएसएस वैरिएबल के रूप में सेव की गई मीडिया क्वेरी दी गई हैं. इन्हें कस्टम मीडिया भी कहा जाता है. ये ग्लोबल कोड हैं. इनका इस्तेमाल, अलग-अलग सिलेक्टर में किया जाएगा, ताकि कोड को छोटा और साफ़ तौर पर पढ़ा जा सके. गेम मेन्यू कॉम्पोनेंट, डिसप्ले के लिए मोशन सेटिंग, सिस्टम कलर स्कीम, और रंग की रेंज की क्षमताओं का इस्तेमाल करता है.

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

नीचे दी गई कस्टम प्रॉपर्टी, कलर स्कीम को मैनेज करती हैं और गेम मेन्यू को इंटरैक्टिव बनाने के लिए माउस पोज़िशनल वैल्यू को होल्ड करती हैं. कस्टम प्रॉपर्टी को नाम देने से, कोड को आसानी से पढ़ा जा सकता है, क्योंकि इससे वैल्यू के लिए इस्तेमाल के उदाहरण या वैल्यू के नतीजे के लिए सही नाम का पता चलता है.

.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);
    }
  }
}

हल्के और गहरे रंग वाली थीम वाले बैकग्राउंड कोनिक बैकग्राउंड

हल्के रंग वाली थीम में चमकीले cyan से deeppink शांकव-ग्रेडिएंट है, जबकि गहरे रंग वाली थीम में गहरे रंग का शंकु ग्रेडिएंट है, जो गहरा है. इस बारे में ज़्यादा जानने के लिए कि कोनिक ग्रेडिएंट की मदद से क्या किया जा सकता है, conic.style देखें.

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

  @media (--dark) {
    background: conic-gradient(at -10% 50%, #212529, 50%, #495057, #212529);
  }
}
बैकग्राउंड को हल्के और गहरे रंग के बीच बदलते हुए दिखाया गया है.

3D दृश्य चालू करना

एलिमेंट को किसी वेब पेज की 3D जगह में रखने के लिए, perspective वाले व्यूपोर्ट को शुरू करना ज़रूरी है. मैंने body एलिमेंट में ऐंगल शामिल किया और अपनी पसंद की स्टाइल बनाने के लिए, व्यूपोर्ट यूनिट का इस्तेमाल किया.

body {
  perspective: 40vw;
}

लोगों पर इस तरह का असर पड़ सकता है.

<ul> बटन की सूची का लुक तय किया जा रहा है

इस एलिमेंट की मदद से, बटन की सूची का पूरा मैक्रो लेआउट बनाया जा सकता है. साथ ही, एक इंटरैक्टिव और 3D फ़्लोटिंग कार्ड भी बनाया जा सकता है. इसे हासिल करने का तरीका यहां बताया गया है.

बटन ग्रुप का लेआउट

Flexbox, कंटेनर लेआउट को मैनेज कर सकता है. flex-direction की मदद से, फ़्लेक्सिबल की डिफ़ॉल्ट दिशा को पंक्तियों से कॉलम में बदलें. साथ ही, align-items के लिए stretch से start को बदलकर पक्का करें कि हर आइटम, कॉन्टेंट के साइज़ के मुताबिक हो.

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

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

इसके बाद, कंटेनर को 3D कॉन्टेक्स्ट के तौर पर सेट करें और सीएसएस clamp() फ़ंक्शन सेट अप करें. इससे यह पक्का किया जा सकेगा कि कार्ड, पढ़ने लायक रोटेशन से बाहर न आए. ध्यान दें कि क्लैंप के लिए बीच की वैल्यू एक कस्टम प्रॉपर्टी है. --x और --y की ये वैल्यू, बाद में माउस के इंटरैक्ट करने पर JavaScript से सेट हो जाएंगी.

.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)
      )
    )
  ;
}

इसके बाद, अगर वेबसाइट पर आने वाले उपयोगकर्ता के हिसाब से मोशन ठीक है, तो ब्राउज़र को बताएं कि will-change से, इस आइटम का ट्रांसफ़ॉर्म लगातार बदलता रहेगा. इसके अलावा, ट्रांसफ़ॉर्म पर transition सेट करके इंटरपोलेशन चालू करें. यह ट्रांज़िशन तब होगा, जब माउस, कार्ड से इंटरैक्ट करेगा. इससे, रोटेशन में बदलाव करने में आसानी होगी. यह ऐनिमेशन लगातार चलने वाला ऐनिमेशन है, जो कार्ड के अंदर मौजूद 3D स्पेस को दिखाता है. भले ही, माउस कॉम्पोनेंट के साथ इंटरैक्ट न कर पा रहा हो या नहीं कर पा रहा हो.

@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;
  }
}

rotate-y ऐनिमेशन सिर्फ़ बीच की कीफ़्रेम को 50% पर सेट करता है, क्योंकि ब्राउज़र 0% और 100% को एलिमेंट की डिफ़ॉल्ट स्टाइल पर डिफ़ॉल्ट रूप से सेट करता है. यह उन ऐनिमेशन के लिए शॉर्टहैंड है जो वैकल्पिक होते हैं, जिनकी शुरुआत और अंत एक ही क्रम में होनी चाहिए. यह अलग-अलग ऐनिमेशन वाले शब्दों को लिखने का एक बेहतरीन तरीका है.

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

<li> एलिमेंट का लुक तय करना

सूची के हर आइटम (<li>) में बटन और उसके बॉर्डर एलिमेंट होते हैं. display स्टाइल में बदलाव किया गया है, इसलिए आइटम ::marker को नहीं दिखाता है. position स्टाइल को relative पर सेट किया गया है, ताकि आगे आने वाले बटन pseudo-elements, उन्हें उस जगह पर सेट कर सकें जहां उन्हें बटन इस्तेमाल कर रहा है.

.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;
}

नज़रिया दिखाने के लिए, सूची का स्क्रीनशॉट 3D स्पेस में घुमाया गया. सूची के हर आइटम में अब बुलेट नहीं है.

<button> एलिमेंट का लुक तय करना

बटनों को शैली देना कठिन हो सकता है, बहुत सी स्थितियों और इंटरैक्शन का ध्यान रखना होता है. छद्म-एलिमेंट, ऐनिमेशन, और इंटरैक्शन के संतुलन की वजह से ये बटन काफ़ी जल्दी जटिल हो जाते हैं.

शुरुआती <button> स्टाइल

यहां कुछ बुनियादी स्टाइल दिए गए हैं, जो दूसरे राज्यों के लिए भी मददगार होंगे.

.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;
}

बटन की सूची का 3D व्यू का स्क्रीनशॉट, जिसमें अब स्टाइल वाले बटन हैं.

बटन के नकली एलिमेंट

बटन के बॉर्डर, पारंपरिक बॉर्डर नहीं हैं. ये बॉर्डर वाले पूरी तरह से एकदम अलग पोज़िशन में होते हैं.

Chrome Devtools के एलिमेंट पैनल का स्क्रीनशॉट, जिसमें एक बटन दिख रहा है. इसमें
::before और ::उपयोगकर्ता के बाद एलिमेंट शामिल हैं.

ये तत्व स्थापित किए गए 3D दृष्टिकोण को दिखाने के लिए ज़रूरी हैं. इनमें से एक नकली एलिमेंट को बटन से दूर धकेल दिया जाएगा और एक एलिमेंट को उपयोगकर्ता के पास ले जाया जाएगा. यह इफ़ेक्ट सबसे ऊपर और सबसे नीचे बटन पर नज़र आता है.

.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);
    }
  }
}

3D ट्रांसफ़ॉर्म स्टाइल

नीचे transform-style को preserve-3d पर सेट किया गया है, ताकि बच्चे z ऐक्सिस पर खुद को स्पेस में रख सकें. transform को --distance कस्टम प्रॉपर्टी पर सेट किया गया है. इसकी वैल्यू को होवर और फ़ोकस करने पर बढ़ाया जाएगा.

.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));
  }
}

कंडिशनल ऐनिमेशन स्टाइल

अगर उपयोगकर्ता को कार्रवाई करने में कोई समस्या नहीं है, तो इस बटन से ब्राउज़र को यह संकेत मिलता है कि ट्रांसफ़ॉर्म प्रॉपर्टी में बदलाव किया जा सकता है. साथ ही, transform और background-color प्रॉपर्टी के लिए ट्रांज़िशन सेट किया गया है. अवधि में अंतर पर ध्यान दें, मुझे लगा कि इसे बहुत कम स्टेज किए गए इफ़ेक्ट के लिए बनाया गया है.

.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 }
  }
}

इंटरैक्शन स्टाइल पर कर्सर घुमाएं और उन पर फ़ोकस करें

इंटरैक्शन ऐनिमेशन का मकसद, फ़्लैट दिखने वाले बटन बनाने वाली लेयर को फैलाना है. --distance वैरिएबल को शुरू में 1px पर सेट करके, इसे पूरा करें. कोड के नीचे दिए गए उदाहरण में दिखाया गया सिलेक्टर यह जांच करता है कि क्या बटन को किसी ऐसे डिवाइस पर घुमाया जा रहा है या उस पर फ़ोकस किया जा रहा है जिसे फ़ोकस इंडिकेटर दिखना चाहिए और चालू नहीं होना चाहिए. अगर ऐसा है, तो यह इन कामों को करने के लिए सीएसएस लागू करता है:

  • होवर बैकग्राउंड का रंग लागू करें.
  • दूरी बढ़ाएं .
  • बाउंस ईज़िंग इफ़ेक्ट जोड़ें.
  • स्यूडो एलिमेंट ट्रांज़िशन को अडजस्ट करें.
.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 }
    }
  }
}

reduced की मोशन प्राथमिकता के लिए, 3D व्यू सटीक था. ऊपर और नीचे के एलिमेंट, इफ़ेक्ट को अच्छे से दिखाते हैं.

JavaScript की मदद से छोटे-छोटे सुधार

इस इंटरफ़ेस का इस्तेमाल कीबोर्ड, स्क्रीन रीडर, गेमपैड, टच, और माउस से किया जा सकता है. हालांकि, कुछ स्थितियों को आसान बनाने के लिए, हम JavaScript की कुछ आसान सुविधाएं जोड़ सकते हैं.

तीर के निशान वाली कुंजियां

Tab बटन, मेन्यू को नेविगेट करने का एक अच्छा तरीका है. हालांकि, दिशा-निर्देशों पैड या जॉयस्टिक से गेमपैड पर फ़ोकस किया जा सकता है. roving-ux लाइब्रेरी का इस्तेमाल अक्सर जीयूआई चैलेंज इंटरफ़ेस के लिए किया जाता है. इसमें तीर के निशान वाली कुंजियां काम करती हैं. नीचे दिया गया कोड, लाइब्रेरी को .threeD-button-set में फ़ोकस ट्रैप करने और बटन चिल्ड्रेन पर फ़ोकस करने के बारे में बताता है.

import {rovingIndex} from 'roving-ux'

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

माउस पैरालक्स इंटरैक्शन

माउस को ट्रैक करने और मेन्यू को झुकाने का मकसद एआर और वीआर वीडियो गेम इंटरफ़ेस की नकल करना है, जहां माउस के बजाय आपको वर्चुअल पॉइंटर हो सकता है. यह मज़ेदार हो सकता है, जब एलिमेंट को पॉइंटर के बारे में पूरी जानकारी हो.

यह एक छोटी सी सुविधा है, इसलिए हम इंटरैक्शन को उपयोगकर्ता की मोशन प्राथमिकता की क्वेरी के पीछे रखेंगे. साथ ही, सेटअप के तहत, बटन सूची कॉम्पोनेंट को querySelector के साथ मेमोरी में सेव करें और एलिमेंट की बाउंड को menuRect में कैश करें. माउस की स्थिति के आधार पर कार्ड पर लागू किए गए घुमाव ऑफ़सेट को तय करने के लिए इन सीमाओं का इस्तेमाल करें.

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

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

इसके बाद, हमें एक ऐसे फ़ंक्शन की ज़रूरत होगी जो माउस के x और y पोज़िशन को स्वीकार करे और ऐसा मान मिले जिसका इस्तेमाल हम कार्ड को घुमाने के लिए कर सकें. यह फ़ंक्शन बॉक्स के अंदर और उसकी जगह का पता लगाने के लिए, माउस की स्थिति का इस्तेमाल करता है. डेल्टा को फ़ंक्शन से वापस आता है.

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}
}

आखिर में, माउस की मूवमेंट को देखें, हमारे getAngles() फ़ंक्शन की पोज़िशन पास करें, और डेल्टा वैल्यू का इस्तेमाल कस्टम प्रॉपर्टी स्टाइल के तौर पर करें. मैंने डेल्टा को पैड करने के लिए 20 से भाग दिया और गड़बड़ी कम की, ऐसा करने का एक बेहतर तरीका हो सकता है. अगर आपको शुरू से याद है, तो हम --x और --y प्रॉप को clamp() फ़ंक्शन के बीच में रखते हैं. इससे माउस की पोज़िशन, कार्ड को बड़ा नहीं कर पाएगी. इसे पढ़ा नहीं जा सकता.

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`)
  })
}

अनुवाद और दिशा-निर्देश

दूसरे राइटिंग मोड और भाषाओं में गेम मेन्यू की जांच करने के दौरान, एक अच्छी समस्या सामने आई.

उपयोगकर्ता एजेंट स्टाइलशीट में, <button> एलिमेंट में writing-mode के लिए !important स्टाइल है. इसका मतलब था कि गेम मेन्यू के एचटीएमएल को मनमुताबिक डिज़ाइन के हिसाब से बदलने की ज़रूरत थी. बटन की सूची को लिंक की सूची में बदलने पर, लॉजिकल प्रॉपर्टी से मेन्यू की दिशा बदलने में मदद मिलती है. इसकी वजह यह है कि <a> एलिमेंट में ब्राउज़र से !important स्टाइल उपलब्ध नहीं होता.

नतीजा

अब आपको पता है कि मैंने इसे कैसे किया, तो आप कैसे करेंगे‽ 🙂 क्या आपके पास मेन्यू में एक्सलरोमीटर इंटरैक्शन जोड़ने का विकल्प है, जिससे आपके फ़ोन की टाइलिंग मेन्यू को घुमाती है? क्या हम 'बिना मोशन' के अनुभव को बेहतर बना सकते हैं?

आइए, हम अलग-अलग तरह के काम करते हैं और वेब पर काम करने के सभी तरीके सीखते हैं. एक डेमो बनाएं, मुझे ट्वीट करें लिंक, और नीचे दिए कम्यूनिटी रीमिक्स सेक्शन में जोड़ दिया जाएगा!

कम्यूनिटी रीमिक्स

फ़िलहाल, यहां कोई कॉन्टेंट मौजूद नहीं है!