Friday, April 16, 2021
Home Lập trình Game với Unity Hướng dẫn về Endless Runner trong Unity 3D

Hướng dẫn về Endless Runner trong Unity 3D

Trong trò chơi điện tử, dù thế giới có rộng lớn đến đâu thì nó cũng luôn có hồi kết. Nhưng có một số trò chơi cố gắng mô phỏng thế giới vô hạn, những trò chơi như vậy thuộc danh mục gọi là Endless Runner .

Endless Runners chủ yếu phổ biến trên nền tảng di động và có đặc điểm là người chơi liên tục di chuyển về phía trước trong khi cố gắng tránh các chướng ngại vật. Ví dụ về các trò chơi đó là: Temple Run (Mobile), Temple Run 2 (Mobile), Subway Surfers (Mobile).

Gameplay của Subway Surfers

Tuy nhiên, xét đến việc ngay cả máy tính / thiết bị chơi game hiện đại cũng có khả năng xử lý hạn chế, thì việc tạo ra một thế giới vô hạn thực sự là điều không thể. Vậy làm thế nào để những trò chơi trên tạo ra ảo giác về một Thế giới vô tận? Câu trả lời là sử dụng lại các khối xây dựng (hay còn gọi là Object Pooling).

Về cơ bản, ngay khi khối phía sau chế độ xem Máy ảnh, nó sẽ được di chuyển ra phía trước, v.v.

Trong hướng dẫn này, chúng tôi sẽ tập trung vào những điều sau:

  • Tạo một nền tảng với nhiều biến thể chướng ngại vật sẽ được tái sử dụng và lặp lại một cách liền mạch
  • Tạo bộ điều khiển trình phát đơn giản với khả năng phát hiện va chạm chướng ngại vật
  • Logic trò chơi đơn giản

Vì vậy, chúng ta hãy bắt đầu!

Phần 1: Tạo nền tảng

Đầu tiên, chúng tôi bắt đầu bằng cách tạo một nhà lắp ghép nền tảng lát gạch:

  • Tạo GameObject mới và gọi nó là “TilePrefab”
  • Tạo khối mới (GameObject -> 3D Object -> Cube)
  • Di chuyển Khối lập phương bên trong đối tượng “TilePrefab”, thay đổi vị trí của nó thành (0, 0, 0) và chia tỷ lệ thành (8, 0,4, 20)
  • Theo tùy chọn, bạn có thể thêm Rails vào các bên bằng cách tạo các Hình khối bổ sung, như sau:

Bây giờ đối với chướng ngại vật, tôi sẽ có 3 biến thể chướng ngại vật, nhưng bạn có thể thực hiện bao nhiêu tùy chọn nếu cần:

  • Tạo 3 GameObjects bên trong đối tượng “TilePrefab” và đặt tên cho chúng là “Chướng ngại vật1”, “Chướng ngại vật2” và “Chướng ngại vật3”
  • Đối với chướng ngại vật đầu tiên, hãy tạo Khối lập phương mới và di chuyển nó vào bên trong đối tượng “Chướng ngại vật1”
  • Chia tỷ lệ Khối lập phương mới có chiều rộng bằng chiều rộng của nền tảng và điều chỉnh chiều cao của nó xuống (Người chơi sẽ cần phải nhảy để tránh chướng ngại vật này)
  • Tạo Vật liệu mới, đặt tên là “RedMaterial” và thay đổi màu của nó thành Đỏ, sau đó gán nó cho Khối lập phương (đây chỉ là để vật cản được phân biệt với nền tảng chính)
  • Đối với “Chướng ngại vật 2”, tạo một vài khối lập phương và đặt chúng thành hình tam giác, để lại một khoảng trống ở phía dưới (Người chơi sẽ phải cúi người để tránh chướng ngại vật này)
  • Và cuối cùng, “Chướng ngại vật 3” sẽ là một bản sao của “Chướng ngại vật1” và “Chướng ngại vật 2”, kết hợp với nhau
  • Bây giờ chọn tất cả các Đối tượng bên trong chướng ngại vật và thay đổi thẻ của chúng thành “Hoàn thành”, điều này sẽ cần thiết sau này để phát hiện va chạm giữa Người chơi và chướng ngại vật.

Để tạo một nền tảng vô hạn, chúng tôi sẽ cần một vài tập lệnh sẽ xử lý việc kích hoạt Nhóm đối tượng và Vật cản:

  • Tạo tập lệnh mới, gọi nó là “SC_PlatformTile” và dán mã bên dưới vào bên trong nó:

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);
        }
    }
}
  • Tạo tập lệnh mới, gọi nó là “SC_GroundGenerator” và dán mã bên dưới vào bên trong nó:

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));
    }
}
  • Đính kèm tập lệnh SC_PlatformTile vào đối tượng “TilePrefab”
  • Gán đối tượng “chướng ngại vật1”, “chướng ngại vật 2” và “chướng ngại vật 3” cho mảng chướng ngại vật

Đối với Điểm bắt đầu và Điểm kết thúc, chúng ta cần tạo 2 GameObjects sẽ được đặt tương ứng ở đầu và cuối nền tảng:

  • Gán các biến Điểm bắt đầu và Điểm kết thúc trong SC_PlatformTile
  • Lưu đối tượng “TilePrefab” vào Prefab và xóa nó khỏi Cảnh
  • Tạo GameObject mới và gọi nó là “_GroundGenerator”
  • Đính kèm tập lệnh SC_GroundGenerator vào đối tượng “_GroundGenerator”
  • Thay đổi vị trí của Camera chính thành (10, 1, -9) và thay đổi cách xoay của nó thành (0, -55, 0)
  • Tạo GameObject mới, gọi nó là “StartPoint” và thay đổi vị trí của nó thành (0, -2, -15)
  • Chọn đối tượng “_GroundGenerator” và trong SC_GroundGenerator chỉ định các biến Camera chính, Điểm bắt đầu và Tile Prefab

Bây giờ nhấn Play và quan sát cách nền tảng di chuyển. Ngay sau khi ô nền đi ra khỏi chế độ xem camera, nó sẽ được di chuyển trở lại phần cuối cùng với một chướng ngại vật ngẫu nhiên được kích hoạt, tạo ra ảo ảnh về cấp độ vô hạn (Chuyển đến 0:11).

The Camera must be placed similarly to the video, so the platforms go towards the Camera and behind it, otherwise the platforms won't repeat.

Phần 2: Tạo phiên bản trình phát

Player Instance sẽ là một Sphere đơn giản sử dụng bộ điều khiển có khả năng nhảy và cúi người.

  • Tạo Sphere mới (GameObject -> 3D Object -> Sphere) và xóa thành phần Sphere Collider của nó
  • Gán “RedMaterial” đã tạo trước đó cho nó
  • Tạo GameObject mới và gọi nó là “Player”
  • Di chuyển Sphere bên trong đối tượng “Player” và thay đổi vị trí của nó thành (0, 0, 0)
  • Tạo tập lệnh mới, gọi nó là “SC_IRPlayer” và dán mã bên dưới vào bên trong nó:

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;
        }
    }
}
  • Đính kèm tập lệnh SC_IRPlayer vào đối tượng “Player” (Bạn sẽ nhận thấy rằng nó đã thêm một thành phần khác được gọi là Rigidbody)
  • Thêm thành phần BoxCollider vào đối tượng “Player”
  • Cuối cùng, đặt đối tượng “Trình phát” phía trên đối tượng “StartPoint” một chút, ngay trước Máy ảnh

Nhấn Play và sử dụng phím W để nhảy và phím S để cúi người. Mục tiêu là để tránh các chướng ngại vật màu đỏ:

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Bức ảnh một người phụ nữ Brazil 85 tuổi nhận được cái ôm đầu tiên sau 5 tháng từ một y tá qua “tấm...

 Bức ảnh một người phụ nữ Brazil 85 tuổi nhận được cái ôm đầu tiên sau 5 tháng từ một y tá...

Top 15 gian hàng có hiệu suất kinh doanh tốt nhất trên Lazada

Top 15 gian hàngja có hiệu suất kinh doanh cao nhất trên Lazada https://c.lazada.vn/t/c.ZCNRB1 https://c.lazada.vn/t/c.ZCLbRP

Tìm hiểu về điện trở nhiệt

Điện trở nhiệt Điện trở nhiệt hay nhiệt điện trở hay thermistor là loại điện trở có trở kháng của nó...

Đây là cách Bentley tạo ra động cơ mạnh mẽ ở Anh

Đây là cách Bentley tạo ra động cơ mạnh mẽ ở Anh