Wawa Sensei logo

Physics

Starter pack

भौतिकी आपके 3D प्रोजेक्ट्स के लिए संभावनाओं की एक पूरी नई दुनिया खोलती है। आप यथार्थवादी यूनिवर्स, उपयोगकर्ता इंटरैक्शन, और यहां तक कि गेम्स भी बना सकते हैं।

इस पाठ में, हम एक साधारण गेम बनाते समय आवश्यक अवधारणाओं को जानेंगे।

चिंता न करें, कोई पूर्व ज्ञान आवश्यक नहीं है, हम बिलकुल शुरुआत से शुरू करेंगे। (आपको बता दूं कि स्कूल में मैं भौतिकी में बहुत ही बुरा छात्र था, तो अगर मैं कर सकता हूँ, तो आप भी कर सकते हैं!)

Physics engines

हमारे 3D प्रोजेक्ट्स में भौतिकी जोड़ने के लिए, हम एक physics engine का उपयोग करेंगे। एक physics engine एक लाइब्रेरी है जो हमारे लिए सभी जटिल गणित को संभालेगा, जैसे कि गुरुत्वाकर्षण, टकराव, बल, आदि।

जावास्क्रिप्ट इकोसिस्टम में, कई physics engines उपलब्ध हैं।

दो बहुत ही लोकप्रिय हैं Cannon.js और Rapier.js

Poimandres (फिर से) ने इन engines को React Three Fiber के साथ उपयोग करने के लिए दो शानदार लाइब्रेरी बनाई हैं: react-three-rapier और use-cannon

इस पाठ में, हम react-three-rapier का उपयोग करेंगे, लेकिन वे दोनों काफी समान हैं और यहाँ हम जो अवधारणाएं सीखेंगे वे दोनों पर लागू होती हैं।

इसे इंस्टॉल करने के लिए, निम्नलिखित चलाएं:

yarn add @react-three/rapier

अब हम शुरू करने के लिए तैयार हैं!

भौतिकी विश्व

खेल बनाने से पहले, आवश्यक अवधारणाओं को कवर करना होगा।

पहले, हमें एक भौतिकी विश्व बनाना होगा। यह विश्व हमारे सीन के सभी भौतिकी ऑब्जेक्ट्स को समाहित करेगा। react-three-rapier के साथ, हमें सिर्फ अपने सभी ऑब्जेक्ट्स को एक <Physics> कंपोनेंट के साथ रैप करना है:

// ...
import { Physics } from "@react-three/rapier";

function App() {
  return (
    <>
      <Canvas camera={{ position: [0, 6, 6], fov: 60 }} shadows>
        <color attach="background" args={["#171720"]} />
        <Physics>
          <Experience />
        </Physics>
      </Canvas>
    </>
  );
}

export default App;

अब हमारा विश्व तैयार है लेकिन कुछ भी नहीं हो रहा है! ऐसा इसलिए है क्योंकि हमारे पास अभी तक कोई भौतिकी ऑब्जेक्ट्स नहीं हैं।

Rigidbody

किसी ऑब्जेक्ट में भौतिकी जोड़ने के लिए, हमें एक rigidbody जोड़ने की आवश्यकता होती है। एक rigidbody एक घटक होता है जो हमारे ऑब्जेक्ट को भौतिकी दुनिया में चलाता है।

किससे ऑब्जेक्ट का मूवमेंट ट्रिगर हो सकता है? Forces, जैसे gravity, collisions, या user interactions

आइए अपने भौतिकी दुनिया को बताएं कि Player.jsx में स्थित हमारा क्यूब एक physics object है, इसके लिए उसमें rigidbody जोड़ते हैं:

import { RigidBody } from "@react-three/rapier";

export const Player = () => {
  return <RigidBody>{/* ... */}</RigidBody>;
};

अब हमारा क्यूब गुरुत्वाकर्षण के प्रति प्रतिक्रिया कर रहा है और गिर रहा है। लेकिन यह हमेशा के लिए गिर रहा है!

हमें जमीन को भी एक physics object बनाना होगा ताकि क्यूब उसके साथ टकरा सके और गिरना बंद कर सके।

आइए ज़मीन में Experience.jsx में एक rigidbody जोड़ें, लेकिन क्योंकि हम नहीं चाहते कि यह क्यूब की तरह मूव और गिर सके, हम type="fixed" prop जोड़ेंगे:

// ...
import { RigidBody } from "@react-three/rapier";

export const Experience = () => {
  return (
    <>
      {/* ... */}
      <RigidBody type="fixed">
        <mesh position-y={-0.251} receiveShadow>
          <boxGeometry args={[20, 0.5, 20]} />
          <meshStandardMaterial color="mediumpurple" />
        </mesh>
      </RigidBody>
      {/* ... */}
    </>
  );
};

Cube on top of the ground

शुरुआती बिंदु पर वापस, हमारे पास जमीन के शीर्ष पर एक स्थिर क्यूब है। लेकिन, आंतरिक रूप से, हमारे पास एक cube है जो gravity के प्रति प्रतिक्रिया कर रहा है और ground के साथ टकराकर रुक गया है।

बल

अब जब हमारे पास एक भौतिक दुनिया और भौतिक वस्तुएं हैं, तो हम बलों के साथ खेलना शुरू कर सकते हैं।

हम कीबोर्ड के तीरों से क्यूब को हिलाएंगे। ऐसा करने के लिए चलिए KeyboardControls का उपयोग करते हैं, जिसे हमने इवेंट्स पाठ में खोजा था:

// ...
import { KeyboardControls } from "@react-three/drei";
import { useMemo } from "react";

export const Controls = {
  forward: "forward",
  back: "back",
  left: "left",
  right: "right",
  jump: "jump",
};

function App() {
  const map = useMemo(
    () => [
      { name: Controls.forward, keys: ["ArrowUp", "KeyW"] },
      { name: Controls.back, keys: ["ArrowDown", "KeyS"] },
      { name: Controls.left, keys: ["ArrowLeft", "KeyA"] },
      { name: Controls.right, keys: ["ArrowRight", "KeyD"] },
      { name: Controls.jump, keys: ["Space"] },
    ],
    []
  );
  return <KeyboardControls map={map}>{/* ... */}</KeyboardControls>;
}

export default App;

अब हम अपने Player.jsx कंपोनेंट में दबाए गए कुंजियों को प्राप्त कर सकते हैं:

// ...
import { Controls } from "../App";
import { useKeyboardControls } from "@react-three/drei";
import { useFrame } from "@react-three/fiber";

export const Player = () => {
  const [, get] = useKeyboardControls();

  useFrame(() => {
    if (get()[Controls.forward]) {
    }
    if (get()[Controls.back]) {
    }
    if (get()[Controls.left]) {
    }
    if (get()[Controls.right]) {
    }
    if (get()[Controls.jump]) {
    }
  });
  // ...
};

get() KeyboardControls कंपोनेंट के साथ दबाए गए कुंजियों को प्राप्त करने का एक वैकल्पिक तरीका है।

अब जब हमारे पास दबाए गए कुंजी हैं, तो हम क्यूब पर बल लागू कर सकते हैं। हम दो विधियों का उपयोग कर सकते हैं:

  • applyImpulse: वस्तु पर एक तात्कालिक बल लागू करें
  • setLinVel: वस्तु की रैखिक वेग सेट करें

चलो दोनों का पता लगाते हैं।

RigidBody को useRef जोड़ें, और उसे सही दिशा में क्यूब को हिलाने के लिए एक इम्पल्स लागू करें:

import { useRef } from "react";
import { Vector3 } from "three";
const MOVEMENT_SPEED = 0.5;

export const Player = () => {
  const rb = useRef();
  const [, get] = useKeyboardControls();
  const impulse = new Vector3();
  useFrame(() => {
    impulse.x = 0;
    impulse.y = 0;
    impulse.z = 0;
    if (get()[Controls.forward]) {
      impulse.z -= MOVEMENT_SPEED;
    }
    if (get()[Controls.back]) {
      impulse.z += MOVEMENT_SPEED;
    }
    if (get()[Controls.left]) {
      impulse.x -= MOVEMENT_SPEED;
    }
    if (get()[Controls.right]) {
      impulse.x += MOVEMENT_SPEED;
    }
    if (get()[Controls.jump]) {
    }
    rb.current.applyImpulse(impulse, true);
  });
  return <RigidBody ref={rb}>{/* ... */}</RigidBody>;
};

ref को RigidBody पर असाइन करने का ध्यान रखें, न कि mesh पर।

यह काम करता है, लेकिन बहुत तेजी से त्वरण कर रहा है और जमीन पर फिसल रहा है। इसे ठीक करने के लिए हम जमीन पर अधिक घर्षण जोड़ सकते हैं:

// ...

export const Experience = () => {
  // ...
  return (
    <>
      {/* ... */}
      <RigidBody type="fixed" friction={5}>
        {/* ... */}
      </RigidBody>
      {/* ... */}
    </>
  );
};

घर्षण के कारण क्यूब घूमने लगता है क्योंकि यह जमीन से चिपक जाता है। इसे ठीक करने के लिए हम क्यूब की रोटेशन लॉक कर सकते हैं:

// ...
export const Player = () => {
  // ...
  return (
    <RigidBody ref={rb} lockRotations>
      {/* ... */}
    </RigidBody>
  );
};

अब यह बहुत बेहतर है, लेकिन क्यूब अभी भी थोड़ा फिसल रहा है। हम इसे क्यूब पर रैखिक डैम्पिंग को समायोजित करके ठीक कर सकते हैं लेकिन हम इस पाठ में इसे नहीं करेंगे।

क्योंकि हमें क्यूब की अधिकतम वेग को भी समायोजित करना पड़ेगा ताकि यह निरंतर सदिश वृद्धि न करे। जब हम क्यूब को घुमाने के लिए बाएँ और दाएँ कुंजियों का उपयोग करेंगे, तो हमें समस्याओं का सामना करना पड़ेगा।

चलो हमारे सिस्टम को applyImpulse की जगह setLinVel का उपयोग करने के लिए बदलते हैं:

// ...
import { useRef } from "react";
import { Vector3 } from "three";

const MOVEMENT_SPEED = 5;

export const Player = () => {
  // ...
  const rb = useRef();
  const vel = new Vector3();
  useFrame(() => {
    vel.x = 0;
    vel.y = 0;
    vel.z = 0;
    if (get()[Controls.forward]) {
      vel.z -= MOVEMENT_SPEED;
    }
    if (get()[Controls.back]) {
      vel.z += MOVEMENT_SPEED;
    }
    if (get()[Controls.left]) {
      vel.x -= MOVEMENT_SPEED;
    }
    if (get()[Controls.right]) {
      vel.x += MOVEMENT_SPEED;
    }
    if (get()[Controls.jump]) {
    }
    rb.current.setLinvel(vel, true);
  });
  return <RigidBody ref={rb}>{/* ... */}</RigidBody>;
};

अब आप जमीन से घर्षण हटा सकते हैं क्योंकि इसकी आवश्यकता नहीं रही।

F2 आपका मित्र है जो जल्दी से वेरिएबल का नाम बदल देता है।

शानदार! अब हमारे पास एक क्यूब है जो कीबोर्ड के तीरों से हिल सकता है।

आइए जब उपयोगकर्ता स्पेस बार दबाए तब एक जंप बल जोड़ते हैं:

// ...
const JUMP_FORCE = 8;

export const Player = () => {
  // ...
  useFrame(() => {
    // ...
    if (get()[Controls.jump]) {
      vel.y += JUMP_FORCE;
    }
    rb.current.setLinvel(vel, true);
  });
  return (
    <RigidBody ref={rb} lockRotations>
      {/* ... */}
    </RigidBody>
  );
};

हमारे पास दो समस्याएं हैं:

  • क्यूब ग्रेविटी के प्रति सही प्रतिक्रिया नहीं दे रहा है (पाठ की शुरुआत में गिरने वाले क्यूब की तुलना में)
  • क्यूब हवा में होते हुए भी जंप कर सकता है

ग्रेविटी की समस्या तब होती है जब हम मैन्युअल रूप से क्यूब के y अक्ष पर वेग सेट करते हैं। हमें इसे केवल तभी बदलना चाहिए जब हम जंप कर रहे हों, और बाकी समय के लिए फिजिक्स इंजन को ग्रेविटी संभालने देना चाहिए:

// ...

export const Player = () => {
  // ...
  useFrame(() => {
    const curVel = rb.current.linvel();
    if (get()[Controls.jump]) {
      vel.y += JUMP_FORCE;
    } else {
      vel.y = curVel.y;
    }
    rb.current.setLinvel(vel, true);
  });
  // ...
};

हम rb.current.linvel() के साथ क्यूब का वर्तमान वेग प्राप्त करते हैं और अगर हम कूद नहीं रहे हैं, तो हम y वेग को वर्तमान वाले पर सेट करते हैं।

क्यूब को हवा में रहते हुए जंप करने से रोकने के लिए, हम यह जांच सकते हैं कि क्यूब ने जमीन को छुआ है या नहीं, इससे पहले कि वह फिर से जंप कर सके:

// ...
export const Player = () => {
  // ...
  const inTheAir = useRef(false);
  useFrame(() => {
    // ...
    if (get()[Controls.jump] && !inTheAir.current) {
      vel.y += JUMP_FORCE;
      inTheAir.current = true;
    } else {
      vel.y = curVel.y;
    }
    rb.current.setLinvel(vel, true);
  });
  // ...
};

अब हम केवल एक बार कूद सकते हैं, और ग्रेविटी काम कर रही है लेकिन थोड़ी धीमी है।

इन्हें ठीक करने से पहले चलिए देखते हैं कि हमारा कोलिजन सिस्टम कैसे काम करता है।

End of lesson preview

To get access to the entire lesson, you need to purchase the course.