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 स्पेस कॉन्टेक्स्ट के तौर पर सेट अप करें और CSS 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 और ::after एलिमेंट दिख रहे हैं.

ये एलिमेंट, तय किए गए 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 से भाग दिया है. ऐसा करने का कोई बेहतर तरीका हो सकता है. अगर आपको याद है कि हमने clamp() फ़ंक्शन के बीच में --x और --y प्रॉप डाले थे, तो इससे माउस की पोज़िशन, कार्ड को ज़्यादा घुमाकर उसे पढ़ने लायक नहीं बना पाती.

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 स्टाइल उपलब्ध नहीं होता.

नतीजा

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

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

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

अभी यहां देखने के लिए कुछ नहीं है!