Tutorial Pelari Tak Berujung untuk Unity

Dalam video game, sebesar apa pun dunia ini, selalu ada akhir. Namun beberapa game mencoba meniru dunia tanpa batas, game seperti itu termasuk dalam kategori yang disebut Endless Runner.

Endless Runner adalah jenis permainan yang pemainnya terus bergerak maju sambil mengumpulkan poin dan menghindari rintangan. Tujuan utamanya adalah untuk mencapai akhir level tanpa terjatuh atau bertabrakan dengan rintangan, namun seringkali, level tersebut berulang tanpa batas, secara bertahap meningkatkan kesulitannya, hingga pemain bertabrakan dengan rintangan tersebut.

Gameplay Peselancar Kereta Bawah Tanah

Mengingat bahkan komputer/perangkat game modern pun memiliki kekuatan pemrosesan yang terbatas, mustahil untuk membuat dunia yang benar-benar tak terbatas.

Jadi bagaimana beberapa game menciptakan ilusi dunia tanpa batas? Jawabannya adalah dengan menggunakan kembali blok penyusun (alias pengumpulan objek), dengan kata lain, segera setelah blok berada di belakang atau di luar tampilan Kamera, blok tersebut dipindahkan ke depan.

Untuk membuat game pelari tak berujung di Unity, kita perlu membuat platform dengan rintangan dan pengontrol pemain.

Langkah 1: Buat Platform

Kita mulai dengan membuat platform ubin yang nantinya akan disimpan di Prefab:

  • Buat GameObject baru dan beri nama "TilePrefab"
  • Buat Kubus baru (GameObject -> Objek 3D -> Kubus)
  • Pindahkan Kubus ke dalam objek "TilePrefab", ubah posisinya menjadi (0, 0, 0), dan skalakan menjadi (8, 0.4, 20)

  • Secara opsional, Anda dapat menambahkan Rel ke samping dengan membuat Kubus tambahan, seperti ini:

Untuk rintangannya, saya punya 3 variasi rintangan, namun anda bisa membuatnya sebanyak yang diperlukan:

  • Buat 3 GameObjects di dalam objek "TilePrefab" dan beri nama "Obstacle1", "Obstacle2" dan "Obstacle3"
  • Untuk rintangan pertama, buatlah Kubus baru dan pindahkan ke dalam objek "Obstacle1"
  • Skalakan Kubus baru hingga kira-kira sama lebarnya dengan platform dan turunkan tingginya (pemain harus melompat untuk menghindari rintangan ini)
  • Buat Material baru, beri nama "RedMaterial" dan ubah warnanya menjadi Merah, lalu tugaskan ke Cube (ini hanya agar rintangannya dibedakan dari platform utama)

  • Untuk "Obstacle2" buat beberapa kubus dan letakkan dalam bentuk segitiga, sisakan satu ruang terbuka di bagian bawah (pemain harus berjongkok untuk menghindari rintangan ini)

  • Dan terakhir, "Obstacle3" akan menjadi duplikat dari "Obstacle1" dan "Obstacle2", digabungkan menjadi satu

  • Sekarang pilih semua Objek di dalam Hambatan dan ubah tagnya menjadi "Finish", ini akan diperlukan nanti untuk mendeteksi tabrakan antara Pemain dan Hambatan.

Untuk menghasilkan platform tanpa batas kita memerlukan beberapa skrip yang akan menangani Penggabungan Objek dan aktivasi Kendala:

  • Buat skrip baru, beri nama "SC_PlatformTile" dan tempel kode di bawah ini di dalamnya:

SC_PlatformTile.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);
        }
    }
}
  • Buat skrip baru, beri nama "SC_GroundGenerator" dan tempel kode di bawah ini di dalamnya:

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));
    }
}
  • Lampirkan skrip SC_PlatformTile ke objek "TilePrefab"
  • Tetapkan objek "Obstacle1", "Obstacle2" dan "Obstacle3" ke array Hambatan

Untuk Titik Awal dan Titik Akhir, kita perlu membuat 2 GameObject yang ditempatkan masing-masing di awal dan akhir platform:

  • Tetapkan variabel Titik Awal dan Titik Akhir di SC_PlatformTile

  • Simpan objek "TilePrefab" ke Prefab dan hapus dari Scene
  • Buat GameObject baru dan beri nama "_GroundGenerator"
  • Lampirkan skrip SC_GroundGenerator ke objek "_GroundGenerator"
  • Ubah posisi Kamera Utama menjadi (10, 1, -9) dan ubah putarannya menjadi (0, -55, 0)
  • Buat GameObject baru, beri nama "StartPoint" dan ubah posisinya menjadi (0, -2, -15)
  • Pilih objek "_GroundGenerator" dan di SC_GroundGenerator tetapkan variabel Kamera Utama, Titik Awal, dan Prefab Ubin

Sekarang tekan Mainkan dan amati bagaimana platform bergerak. Segera setelah ubin platform keluar dari tampilan kamera, ubin tersebut dipindahkan kembali ke ujung dengan rintangan acak diaktifkan, menciptakan ilusi level tak terbatas (Lewati ke 0:11).

Kamera harus ditempatkan serupa dengan video, sehingga platform mengarah ke Kamera dan di belakangnya, jika tidak, platform tidak akan terulang.

Sharp Coder Pemutar video

Langkah 2: Buat Pemain

Instance pemain akan menjadi Sphere sederhana yang menggunakan pengontrol dengan kemampuan melompat dan berjongkok.

  • Buat Sphere baru (GameObject -> 3D Object -> Sphere) dan hapus komponen Sphere Collider-nya
  • Tetapkan "RedMaterial" yang dibuat sebelumnya ke dalamnya
  • Buat GameObject baru dan beri nama "Player"
  • Pindahkan Sphere yang ada di dalam objek "Player" dan ubah posisinya menjadi (0, 0, 0)
  • Buat skrip baru, beri nama "SC_IRPlayer" dan tempel kode di bawah ini di dalamnya:

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;
        }
    }
}
  • Lampirkan skrip SC_IRPlayer ke objek "Player" (Anda akan melihat bahwa objek tersebut menambahkan komponen lain yang disebut Rigidbody)
  • Tambahkan komponen BoxCollider ke objek "Player"

  • Tempatkan objek "Player" sedikit di atas objek "StartPoint", tepat di depan Kamera

Tekan Mainkan dan gunakan tombol W untuk melompat dan tombol S untuk berjongkok. Tujuannya adalah untuk menghindari Hambatan merah:

Sharp Coder Pemutar video

Periksa ini Horizon Bending Shader.

Sumber
📁EndlessRunner.unitypackage26.68 KB
Artikel yang Disarankan
Membuat Game Pemecah Bata 2D di Unity
Membuat Game Puzzle Geser dalam Unity
Cara Membuat Game Terinspirasi Flappy Bird di Unity
Tutorial Game Puzzle Mencocokkan-3 di Unity
Mini Game dalam Persatuan | Kubus Flappy
Cara Membuat Game Ular di Unity
Mini Game dalam Persatuan | HINDARI KUBUS