एकता के लिए अंतहीन धावक ट्यूटोरियल

वीडियो गेम में, चाहे दुनिया कितनी भी बड़ी क्यों न हो, उसका अंत हमेशा होता है। लेकिन कुछ गेम अनंत दुनिया का अनुकरण करने का प्रयास करते हैं, ऐसे गेम एंडलेस रनर नामक श्रेणी में आते हैं।

एंडलेस रनर एक प्रकार का गेम है जहां खिलाड़ी लगातार अंक इकट्ठा करते हुए और बाधाओं से बचते हुए आगे बढ़ता रहता है। मुख्य उद्देश्य बाधाओं में गिरे बिना या उनसे टकराए बिना स्तर के अंत तक पहुंचना है, लेकिन कई बार, स्तर खुद को असीमित रूप से दोहराता है, धीरे-धीरे कठिनाई बढ़ाता है, जब तक कि खिलाड़ी बाधा से टकरा न जाए।

सबवे सर्फर्स गेमप्ले

यह ध्यान में रखते हुए कि आधुनिक कंप्यूटर/गेमिंग उपकरणों में भी सीमित प्रसंस्करण शक्ति है, वास्तव में एक अनंत दुनिया बनाना असंभव है।

तो कुछ गेम अनंत दुनिया का भ्रम कैसे पैदा करते हैं? इसका उत्तर बिल्डिंग ब्लॉक्स (उर्फ ऑब्जेक्ट पूलिंग) का पुन: उपयोग करना है, दूसरे शब्दों में, जैसे ही ब्लॉक कैमरा दृश्य के पीछे या बाहर जाता है, यह सामने की ओर चला जाता है।

Unity में एक अंतहीन धावक गेम बनाने के लिए, हमें बाधाओं वाला एक प्लेटफ़ॉर्म और एक प्लेयर कंट्रोलर बनाने की आवश्यकता होगी।

चरण 1: प्लेटफ़ॉर्म बनाएं

हम एक टाइलयुक्त प्लेटफ़ॉर्म बनाकर शुरुआत करते हैं जिसे बाद में Prefab में संग्रहित किया जाएगा:

  • एक नया गेमऑब्जेक्ट बनाएं और उसे कॉल करें "TilePrefab"
  • नया क्यूब बनाएं (गेमऑब्जेक्ट -> 3डी ऑब्जेक्ट -> क्यूब)
  • क्यूब को "TilePrefab" ऑब्जेक्ट के अंदर ले जाएं, इसकी स्थिति को (0, 0, 0) में बदलें, और स्केल को (8, 0.4, 20) में बदलें।

  • वैकल्पिक रूप से आप अतिरिक्त क्यूब्स बनाकर किनारों पर रेल्स जोड़ सकते हैं, जैसे:

बाधाओं के लिए, मेरे पास 3 बाधा विविधताएँ होंगी, लेकिन आप जितनी आवश्यकता हो उतनी बना सकते हैं:

  • "TilePrefab" ऑब्जेक्ट के अंदर 3 गेमऑब्जेक्ट बनाएं और उन्हें "Obstacle1", "Obstacle2" और नाम दें "Obstacle3"
  • पहली बाधा के लिए, एक नया क्यूब बनाएं और इसे "Obstacle1" ऑब्जेक्ट के अंदर ले जाएं
  • नए क्यूब को प्लेटफ़ॉर्म के समान चौड़ाई में स्केल करें और इसकी ऊंचाई को नीचे स्केल करें (खिलाड़ी को इस बाधा से बचने के लिए कूदने की आवश्यकता होगी)
  • एक नई सामग्री बनाएं, इसे "RedMaterial" नाम दें और इसका रंग लाल में बदलें, फिर इसे क्यूब पर असाइन करें (यह सिर्फ इसलिए है ताकि बाधा मुख्य प्लेटफ़ॉर्म से अलग हो)

  • "Obstacle2" के लिए कुछ घन बनाएं और उन्हें त्रिकोणीय आकार में रखें, नीचे एक खाली जगह छोड़ें (खिलाड़ी को इस बाधा से बचने के लिए झुकना होगा)

  • और अंत में, "Obstacle3" एक साथ मिलकर "Obstacle1" और "Obstacle2" का डुप्लिकेट बनने जा रहा है

  • अब बाधाओं के अंदर सभी ऑब्जेक्ट का चयन करें और उनके टैग को "Finish" में बदलें, बाद में प्लेयर और बाधा के बीच टकराव का पता लगाने के लिए इसकी आवश्यकता होगी।

एक अनंत प्लेटफ़ॉर्म तैयार करने के लिए हमें कुछ स्क्रिप्ट्स की आवश्यकता होगी जो ऑब्जेक्ट पूलिंग और बाधा सक्रियण को संभालेंगी:

  • एक नई स्क्रिप्ट बनाएं, इसे "SC_PlatformTile" कहें और इसके अंदर नीचे दिया गया कोड पेस्ट करें:

SC_प्लेटफ़ॉर्मटाइल.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SC_PlatformTile : MonoBehaviour
{
    public Transform startPoint;
    public Transform endPoint;
    public GameObject[] obstacles; //Objects that contains different obstacle types which will be randomly activated

    public void ActivateRandomObstacle()
    {
        DeactivateAllObstacles();

        System.Random random = new System.Random();
        int randomNumber = random.Next(0, obstacles.Length);
        obstacles[randomNumber].SetActive(true);
    }

    public void DeactivateAllObstacles()
    {
        for (int i = 0; i < obstacles.Length; i++)
        {
            obstacles[i].SetActive(false);
        }
    }
}
  • एक नई स्क्रिप्ट बनाएं, इसे "SC_GroundGenerator" कहें और इसके अंदर नीचे दिया गया कोड पेस्ट करें:

SC_GroundGenerator.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class SC_GroundGenerator : MonoBehaviour
{
    public Camera mainCamera;
    public Transform startPoint; //Point from where ground tiles will start
    public SC_PlatformTile tilePrefab;
    public float movingSpeed = 12;
    public int tilesToPreSpawn = 15; //How many tiles should be pre-spawned
    public int tilesWithoutObstacles = 3; //How many tiles at the beginning should not have obstacles, good for warm-up

    List<SC_PlatformTile> spawnedTiles = new List<SC_PlatformTile>();
    int nextTileToActivate = -1;
    [HideInInspector]
    public bool gameOver = false;
    static bool gameStarted = false;
    float score = 0;

    public static SC_GroundGenerator instance;

    // Start is called before the first frame update
    void Start()
    {
        instance = this;

        Vector3 spawnPosition = startPoint.position;
        int tilesWithNoObstaclesTmp = tilesWithoutObstacles;
        for (int i = 0; i < tilesToPreSpawn; i++)
        {
            spawnPosition -= tilePrefab.startPoint.localPosition;
            SC_PlatformTile spawnedTile = Instantiate(tilePrefab, spawnPosition, Quaternion.identity) as SC_PlatformTile;
            if(tilesWithNoObstaclesTmp > 0)
            {
                spawnedTile.DeactivateAllObstacles();
                tilesWithNoObstaclesTmp--;
            }
            else
            {
                spawnedTile.ActivateRandomObstacle();
            }
            
            spawnPosition = spawnedTile.endPoint.position;
            spawnedTile.transform.SetParent(transform);
            spawnedTiles.Add(spawnedTile);
        }
    }

    // Update is called once per frame
    void Update()
    {
        // Move the object upward in world space x unit/second.
        //Increase speed the higher score we get
        if (!gameOver && gameStarted)
        {
            transform.Translate(-spawnedTiles[0].transform.forward * Time.deltaTime * (movingSpeed + (score/500)), Space.World);
            score += Time.deltaTime * movingSpeed;
        }

        if (mainCamera.WorldToViewportPoint(spawnedTiles[0].endPoint.position).z < 0)
        {
            //Move the tile to the front if it's behind the Camera
            SC_PlatformTile tileTmp = spawnedTiles[0];
            spawnedTiles.RemoveAt(0);
            tileTmp.transform.position = spawnedTiles[spawnedTiles.Count - 1].endPoint.position - tileTmp.startPoint.localPosition;
            tileTmp.ActivateRandomObstacle();
            spawnedTiles.Add(tileTmp);
        }

        if (gameOver || !gameStarted)
        {
            if (Input.GetKeyDown(KeyCode.Space))
            {
                if (gameOver)
                {
                    //Restart current scene
                    Scene scene = SceneManager.GetActiveScene();
                    SceneManager.LoadScene(scene.name);
                }
                else
                {
                    //Start the game
                    gameStarted = true;
                }
            }
        }
    }

    void OnGUI()
    {
        if (gameOver)
        {
            GUI.color = Color.red;
            GUI.Label(new Rect(Screen.width / 2 - 100, Screen.height / 2 - 100, 200, 200), "Game Over\nYour score is: " + ((int)score) + "\nPress 'Space' to restart");
        }
        else
        {
            if (!gameStarted)
            {
                GUI.color = Color.red;
                GUI.Label(new Rect(Screen.width / 2 - 100, Screen.height / 2 - 100, 200, 200), "Press 'Space' to start");
            }
        }


        GUI.color = Color.green;
        GUI.Label(new Rect(5, 5, 200, 25), "Score: " + ((int)score));
    }
}
  • SC_PlatformTile स्क्रिप्ट को "TilePrefab" ऑब्जेक्ट से जोड़ें
  • बाधा सरणी में "Obstacle1", "Obstacle2" और "Obstacle3" ऑब्जेक्ट असाइन करें

प्रारंभ बिंदु और अंतिम बिंदु के लिए, हमें 2 गेमऑब्जेक्ट बनाने की आवश्यकता है जिन्हें क्रमशः प्लेटफ़ॉर्म के प्रारंभ और अंत में रखा जाना चाहिए:

  • SC_PlatformTile में स्टार्ट पॉइंट और एंड पॉइंट वेरिएबल असाइन करें

  • "TilePrefab" ऑब्जेक्ट को प्रीफ़ैब में सहेजें और इसे दृश्य से हटा दें
  • एक नया गेमऑब्जेक्ट बनाएं और उसे कॉल करें "_GroundGenerator"
  • SC_GroundGenerator स्क्रिप्ट को "_GroundGenerator" ऑब्जेक्ट से जोड़ें
  • मुख्य कैमरे की स्थिति को (10, 1, -9) में बदलें और इसके रोटेशन को (0, -55, 0) में बदलें।
  • एक नया गेमऑब्जेक्ट बनाएं, इसे "StartPoint" कहें और इसकी स्थिति को (0, -2, -15) में बदलें
  • "_GroundGenerator" ऑब्जेक्ट का चयन करें और SC_GroundGenerator में मुख्य कैमरा, स्टार्ट पॉइंट और टाइल प्रीफ़ैब वेरिएबल असाइन करें

अब Play दबाएं और देखें कि प्लेटफ़ॉर्म कैसे चलता है। जैसे ही प्लेटफ़ॉर्म टाइल कैमरे के दृश्य से बाहर जाती है, यह एक यादृच्छिक बाधा के सक्रिय होने के साथ अंत तक वापस चली जाती है, जिससे अनंत स्तर का भ्रम पैदा होता है (0:11 पर जाएं)।

कैमरे को वीडियो के समान ही रखा जाना चाहिए, ताकि प्लेटफ़ॉर्म कैमरे की ओर और उसके पीछे चले जाएं, अन्यथा प्लेटफ़ॉर्म दोहराए नहीं जाएंगे।

Sharp Coder वीडियो प्लेयर

चरण 2: प्लेयर बनाएं

प्लेयर इंस्टेंस एक सरल क्षेत्र होगा जिसमें कूदने और झुकने की क्षमता वाले नियंत्रक का उपयोग किया जाएगा।

  • एक नया क्षेत्र बनाएं (गेमऑब्जेक्ट -> 3डी ऑब्जेक्ट -> क्षेत्र) और इसके स्फीयर कोलाइडर घटक को हटा दें
  • इसे पहले से निर्मित "RedMaterial" असाइन करें
  • एक नया गेमऑब्जेक्ट बनाएं और उसे कॉल करें "Player"
  • गोले को "Player" ऑब्जेक्ट के अंदर ले जाएँ और उसकी स्थिति को (0, 0, 0) में बदलें
  • एक नई स्क्रिप्ट बनाएं, इसे "SC_IRPlayer" कहें और इसके अंदर नीचे दिया गया कोड पेस्ट करें:

SC_IRPlayer.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(Rigidbody))]

public class SC_IRPlayer : MonoBehaviour
{
    public float gravity = 20.0f;
    public float jumpHeight = 2.5f;

    Rigidbody r;
    bool grounded = false;
    Vector3 defaultScale;
    bool crouch = false;

    // Start is called before the first frame update
    void Start()
    {
        r = GetComponent<Rigidbody>();
        r.constraints = RigidbodyConstraints.FreezePositionX | RigidbodyConstraints.FreezePositionZ;
        r.freezeRotation = true;
        r.useGravity = false;
        defaultScale = transform.localScale;
    }

    void Update()
    {
        // Jump
        if (Input.GetKeyDown(KeyCode.W) && grounded)
        {
            r.velocity = new Vector3(r.velocity.x, CalculateJumpVerticalSpeed(), r.velocity.z);
        }

        //Crouch
        crouch = Input.GetKey(KeyCode.S);
        if (crouch)
        {
            transform.localScale = Vector3.Lerp(transform.localScale, new Vector3(defaultScale.x, defaultScale.y * 0.4f, defaultScale.z), Time.deltaTime * 7);
        }
        else
        {
            transform.localScale = Vector3.Lerp(transform.localScale, defaultScale, Time.deltaTime * 7);
        }
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        // We apply gravity manually for more tuning control
        r.AddForce(new Vector3(0, -gravity * r.mass, 0));

        grounded = false;
    }

    void OnCollisionStay()
    {
        grounded = true;
    }

    float CalculateJumpVerticalSpeed()
    {
        // From the jump height and gravity we deduce the upwards speed 
        // for the character to reach at the apex.
        return Mathf.Sqrt(2 * jumpHeight * gravity);
    }

    void OnCollisionEnter(Collision collision)
    {
        if(collision.gameObject.tag == "Finish")
        {
            //print("GameOver!");
            SC_GroundGenerator.instance.gameOver = true;
        }
    }
}

  • "Player" ऑब्जेक्ट को "StartPoint" ऑब्जेक्ट से थोड़ा ऊपर, कैमरे के ठीक सामने रखें

Play दबाएं और कूदने के लिए W कुंजी और झुकने के लिए S कुंजी का उपयोग करें। इसका उद्देश्य लाल बाधाओं से बचना है:

Sharp Coder वीडियो प्लेयर

इस हॉरिजन बेंडिंग शेडर की जाँच करें।

स्रोत
📁EndlessRunner.unitypackage26.68 KB
सुझाए गए लेख
यूनिटी में मैच-3 पहेली गेम के लिए ट्यूटोरियल
एकता में एक स्लाइडिंग पहेली गेम बनाना
एकता में मिनी गेम | फ़्लैपी क्यूब
एकता में साँप का खेल कैसे बनाएं
यूनिटी में 2डी ब्रिक ब्रेकर गेम बनाना
यूनिटी में फ्लैपी बर्ड-प्रेरित गेम कैसे बनाएं
एकता में मिनी गेम | घनबचें