khác

Làm trò chơi phi dao bằng unity(#2 / 2)

Knife Hit Part 2 Featured

Làm trò chơi Knife Hit  bằng Unity (phần # 2/2)

 Trong phần trước của loạt hướng dẫn này, bạn đã học được cách tạo Log xoay và một con dao duy nhất. Điều này có nghĩa là bạn có cơ chế trò chơi cốt lõi đang hoạt động nhưng trò chơi về cơ bản vẫn không thể chơi được.
 Trong phần thứ hai và cuối cùng này, bạn sẽ thêm các hạt gỗ đẹp, tạo thêm dao, tạo giao diện người dùng và trò chơi theo trình tự. Hãy cuộn xuống dưới và tiếp tục theo dõi để biết thêm hướng dẫn như thế này!

Còn  đây là video cho phần #2/2 này nhé:

Làm cho trò chơi trở nên sống động với các thành phần riêng lẻ

Khi nói đến trò chơi, bạn không bao giờ có đầy đủ hết các thành phần riêng lẻ ! Với Unity, việc thêm cácthành phần riêng lẻ là khá dễ dàng.

Chọn đối tượng Knife từ hệ thống phân cấp(Hierarchy) và thêm một thành phần hệ thống System Particle mới.

Bây giờ bạn có nó tại chỗ, tạo một vật liệu mới trong thư mục Assets và gọi nó là WoodParticleMaterial. Sau đó chọn tài liệu này và thiết lập các thuộc tính của nó như sau.

Tiếp theo, bạn sẽ lại chọn Knife từ hệ thống phân cấp và thiết lập rất nhiều thứ trên thành phần Particle System. Hầu hết trong số họ là tự giải thích và tất cả các lĩnh vực bạn cần phải thiết lập được đánh dấu.

Hãy bắt đầu với các thuộc tính chính. Vô hiệu hóa vòng lặp, đặt thời gian bắt đầu và vv. Bạn có thể thấy rằng trên hình ảnh là hai trường cho Thời gian bắt đầu và cũng cho Tốc độ khởi động. Để có được chúng, nhấp vào hình tam giác nhỏ bên cạnh chúng ở bên phải và chọn Random Between Two Constants. Đối với Màu bắt đầu, hãy sử dụng bộ chọn màu và chọn màu nhạt hơn của Nhật ký.

Trong tab Phát xạ, đặt Tốc độ theo Thời gian thành 0. Tạo một cụm mới và Đếm lần nữa là một Ngẫu nhiên giữa Hai Hằng số


Đặt Shape thành một vòng tròn với Arc 160 độ, vị trí Y của vòng quay 1 và Z là 190.

Đối với Kích thước trong suốt thời gian, hãy chọn dòng tuyến tính hướng xuống ở dưới cùng.


Cuối cùng, kéo WoodParticleMaterial mà bạn đã tạo một lúc trước vào trường Material của Renderer.


Đảm bảo rằng tất cả các phần Hệ thống Hạt được đề cập ở trên đều được bật. (Đánh dấu vào ô bên cạnh tên của phần.)

Tạo giao diện người dùng
Trước khi bạn tạo một kịch bản sẽ sinh ra nhiều con dao và kiểm soát logic trò chơi, không phải là một ý tưởng tồi để xây dựng giao diện người dùng sẽ được hiển thị cho người chơi ở các giai đoạn khác nhau của trò chơi.

Để tạo giao diện người dùng, nhấp chuột phải vào cấu trúc phân cấp và chọn Giao diện người dùng -> Canvas.

Trên Canvas này, đặt Chế độ hiển thị thành “Không gian màn hình – Máy ảnh” và kéo Máy ảnh chính từ hệ thống phân cấp sang trường Render Camera. Bạn cũng có thể cập nhật thành phần Canvas Scaler như trong hình. Điều này sẽ đảm bảo Canvas đáp ứng với kích thước màn hình của điện thoại.

Thêm nút khởi động lại
Trước tiên, hãy thêm nút. Nhấp chuột phải vào Canvas trong cấu trúc phân cấp và thêm giao diện người dùng -> Nút. Đặt tên cho nó là ButtonRestart. Bạn sẽ quay lại nó ở cuối hướng dẫn. Bây giờ, di chuyển nó và phong cách nó như sau.

ButtonRestart này có một đối tượng con được gọi là Text. Thay đổi nó theo hình ảnh. Như bạn có thể thấy, màu sắc của nút và văn bản được chọn từ các tông màu nâu nhạt và đậm hơn của Log sprite.


Với mọi thứ được đặt chính xác, nút nên trò chơi sẽ trông như thế này:

Bây giờ chọn ButtonRestart trong hệ thống phân cấp và vô hiệu hóa nó theo mặc định. Nút này sẽ chỉ được kích hoạt nếu người chơi thua trò chơi.


Hiển thị số lượng dao còn lại có biểu tượng
Người chơi phải bằng cách nào đó biết bao nhiêu con dao còn lại. Trong trò chơi Knife Hit ban đầu, điều này được thực hiện thông qua các biểu tượng ở phía bên trái. Hãy tạo điều đó trong bản sao này.

Nhấp chuột phải vào Canvas và tạo giao diện người dùng -> Bảng điều khiển. Gọi nó là PanelKnives. Loại bỏ thành phần hình ảnh khỏi nó (nhấn chuột phải -> Remove Component) và thêm một thành phần mới Vertical Layout Group. Bây giờ thiết lập các lĩnh vực của PanelKnives như trên hình ảnh.


Đảm bảo bảng điều khiển được căn chỉnh về bên trái và trải dọc theo trục Y. Nhóm bố cục dọc sẽ tự động xếp chồng con của bảng điều khiển này lên trên bảng điều khiển khác. Chúng tôi muốn chính xác điều này để hiển thị số lượng dao còn lại. Thay đổi amout của dao trong một mức độ và sau đó hiển thị chúng sẽ đơn giản như thêm / loại bỏ một đứa trẻ của bảng điều khiển này.

Bây giờ kích chuột phải vào Panel Knives và tạo một giao diện người dùng mới -> Image. Đặt tên cho nó là IconKnife. Nó có thể xuất hiện để không bị ảnh hưởng bởi Vertical Layout Group lúc đầu vì vậy chỉ cần tạm thời thay đổi chiều rộng của nó và nó sẽ chụp đúng vị trí. Sau đó, đặt Image Source của thành phần Image thành sp ic_knife từ thư mục Art. Cuối cùng đặt vòng quay Z của IconKnife thành 45.

Tiếp theo, hãy tạo tiền tố từ đối tượng trò chơi này. Đầu tiên, tạo một thư mục mới Prefabs dưới Assets. Sau đó, chỉ cần kéo Icon Knife từ hệ thống phân cấp đến thư mục mới này. Bạn sẽ sử dụng prefab này để tự động thêm nhiều bản sao của nó vào PanelKnives khi trò chơi bắt đầu. Bây giờ bạn có thể xóa IconKnife một cách an toàn khỏi hệ thống phân cấp.

Scripting UI
Với tất cả giao diện người dùng tại chỗ, hãy tạo tập lệnh sẽ kiểm soát nó. Theo Scripts tạo một C # kịch bản mới GameUI. Nó sẽ không làm bất cứ điều gì bởi chính nó, nó là một trừu tượng cho giao diện người dùng. Bạn sẽ sử dụng kịch bản này từ một kịch bản GameController khác mà bạn sẽ tạo ra trong giây lát.

using UnityEngine;
using UnityEngine.UI;
public class GameUI : MonoBehaviour {
    [SerializeField]
    private GameObject restartButton;
    [Header(“Knife Count Display”)] //header for organization purposes
    [SerializeField]
    private GameObject panelKnives;
    [SerializeField]
    //this will be set to the icon prefab
    private GameObject iconKnife;
    [SerializeField]
    private Color usedKnifeIconColor;
    //enable the restartButton game object
    public void ShowRestartButton()
    {
        restartButton.SetActive(true);
    }
    //add a number of iconKnife children to panelKnives
    public void SetInitialDisplayedKnifeCount(int count)
    {
        for (int i = 0; i < count; i++)
            Instantiate(iconKnife, panelKnives.transform);
    }
    //keeping track of the last icon representing an unthrown knife
    private int knifeIconIndexToChange = 0;
    //changing the color of the image to represent a thrown (used) knife
    public void DecrementDisplayedKnifeCount()
    {
        panelKnives.transform.GetChild(knifeIconIndexToChange++)
            .GetComponent<Image>().color = usedKnifeIconColor;
    }
}

Khi bạn có tập lệnh này, hãy tạo một GameController trò chơi đối tượng trống mới ở gốc của cấu trúc phân cấp. Kéo tập lệnh GameUI này lên và thiết lập nó bằng cách kéo con của Canvas vào các trường tương ứng của chúng. IconKnife sẽ là prefab mà bạn đã tạo ra một chút trước đây và màu được sử dụngKnifeIconColor sẽ có màu trắng với độ phân giải là 100.


Tuyệt vời! Bây giờ là lúc cho tập lệnh cuối cùng – GameController.

Kịch bản để kiểm soát tất cả
Trong Assets / Scripts tạo một C # script GameController mới. Nhiều phương thức của GameController sẽ được gọi là các tập lệnh khác (ví dụ: KnifeScript). Kịch bản GameController là một phiên bản đơn giản của một singleton. Giải thích nó nằm ngoài phạm vi của hướng dẫn này NHƯNG Tôi có một hướng dẫn riêng về các bài hát đơn trong Unity nên chắc chắn kiểm tra nó.

using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;
//this script cannot function properly without GameUI
//let Unity know about it with this attribute
[RequireComponent(typeof(GameUI))]
public class GameController : MonoBehaviour {
    //we can get this instance from other scripts very easily
    public static GameController Instance { get; private set; }
    [SerializeField]
    private int knifeCount;
    [Header(“Knife Spawning”)]
    [SerializeField]
    private Vector2 knifeSpawnPosition;
    [SerializeField]
    //this will be a prefab of the knife. You will create the prefab later.
    private GameObject knifeObject;
    //reference to the GameUI on GameController’s game object
    public GameUI GameUI { get; private set; }
    private void Awake()
    {
        //simple kind of a singleton instance (we’re only in 1 scene)
        Instance = this;
        GameUI = GetComponent<GameUI>();
    }
    private void Start()
    {
        //update the UI as soon as the game starts
        GameUI.SetInitialDisplayedKnifeCount(knifeCount);
        //also spawn the first knife
        SpawnKnife();
    }
    //this will be called from KnifeScript
    public void OnSuccessfulKnifeHit()
    {
        if (knifeCount > 0)
        {
            SpawnKnife();
        }
        else
        {
            StartGameOverSequence(true);
        }
    }
    //a pretty self-explanatory method
    private void SpawnKnife()
    {
        knifeCount;
        Instantiate(knifeObject, knifeSpawnPosition, Quaternion.identity);
    }
    //the public method for starting game over
    public void StartGameOverSequence(bool win)
    {
        StartCoroutine(“GameOverSequenceCoroutine”, win);
    }
    //this is a coroutine because we want to wait for a while when the player wins
    private IEnumerator GameOverSequenceCoroutine(bool win)
    {
        if (win)
        {
            //make the player realize it’s game over and he won
            //you can also add a nice animation of the breaking log
            //but this is outside the scope of this tutorial
            yield return new WaitForSecondsRealtime(0.3f);
            //Feel free to set different values for knife count and log’s rotation pattern
            //instead of just restarting. This would make it feel like a new, harder level.
            RestartGame();
        }
        else
        {
            GameUI.ShowRestartButton();
        }
    }
    public void RestartGame()
    {
        //restart the scene by reloading the currently active scene
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex, LoadSceneMode.Single);
    }
}

Cập nhật KnifeScript để gọi các phương thức trên các GameController và Play Particles
Tất cả các dòng mới bạn cần thêm được tô sáng.

using UnityEngine;
public class KnifeScript : MonoBehaviour {
    [SerializeField]
    private Vector2 throwForce;
    //knife shouldn’t be controlled by the player when it’s inactive
    //(i.e. it already hit the log / another knife)
    private bool isActive = true;
    //for controlling physics
    private Rigidbody2D rb;
    //the collider attached to Knife
    private BoxCollider2D knifeCollider;
    private void Awake()
    {
        rb = GetComponent<Rigidbody2D>();
        knifeCollider = GetComponent<BoxCollider2D>();
    }
    void Update ()
{
        //this method of detecting input also works for touch
if (Input.GetMouseButtonDown(0) && isActive)
        {
            //”throwing” the knife
            rb.AddForce(throwForce, ForceMode2D.Impulse);
            //once the knife isn’t stationary, we can apply gravity (it will not automatically fall down)
            rb.gravityScale = 1;
            //Decrement number of available knives
            GameController.Instance.GameUI.DecrementDisplayedKnifeCount();
        }
}
    private void OnCollisionEnter2D(Collision2D collision)
    {
        //we don’t even want to detect collisions when the knife isn’t active
        if (!isActive)
            return;
        //if the knife happens to be active (1st collision), deactivate it
        isActive = false;
        //collision with a log
        if (collision.collider.tag == “Log”)
        {
            //play the particle effect on collision,
            //you don’t always have to store the component in a field…
            GetComponent<ParticleSystem>().Play();
            //stop the knife
            rb.velocity = new Vector2(0, 0);
            //this will automatically inherit rotation of the new parent (log)
            rb.bodyType = RigidbodyType2D.Kinematic;
            transform.SetParent(collision.collider.transform);
            //move the collider away from the blade which is stuck in the log
            knifeCollider.offset = new Vector2(knifeCollider.offset.x, 0.4f);
            knifeCollider.size = new Vector2(knifeCollider.size.x, 1.2f);
            //Spawn another knife
            GameController.Instance.OnSuccessfulKnifeHit();
        }
        //collision with another knife
        else if (collision.collider.tag == “Knife”)
        {
            //start rapidly moving downwards
            rb.velocity = new Vector2(rb.velocity.x, 2);
            //Game Over
            GameController.Instance.StartGameOverSequence(false);
        }
    }
}

Làm cho nó hoạt động cùng nhau
Kéo tập lệnh GameController mới được tạo ra vào đối tượng GameController bằng nhau. Cũng tạo ra một prefab ra khỏi đối tượng game Knife – kéo nó vào thư mục Prefabs và xóa Knife từ hệ thống phân cấp. Bây giờ cập nhật các trường trên kịch bản GameController. KnifeObject là prefab.


Kết nối ButtonRestart với phương thức thích hợp. Chọn nó và trong thành phần Button thêm một người nghe OnClick () mới. Kéo đối tượng trò chơi GameController vào trường Object và chọn RestartGame từ kịch bản GameController làm phương thức.

Cuối cùng, chúng ta hãy sửa chữa nhanh chóng cho Nhật ký. Đặt Phát hiện va chạm của Rigidbody thành liên tục. Điều này sẽ phát hiện va chạm Log-Knife chính xác hơn và như là một tiền thưởng nó sẽ thêm một hình ảnh động “trả lại” vào Nhật ký khi một con dao chạm! (Không thực sự là hoạt hình, nội dung nào đó với Unity, nhưng có vẻ thú vị!)

Đó là nó! Chơi trò chơi, thử nghiệm với các giá trị khác nhau cho mọi thứ bạn có thể nghĩ ra, tạo nhiều cấp độ, tạo ngẫu nhiên một số giá trị mới… Trí tưởng tượng của bạn là giới hạn duy nhất.

Phần kết luận
Trong loạt bài hướng dẫn gồm hai phần này, bạn đã học cách tạo ra một bản sao Knife Hit đầy đủ chức năng trong Unity. Tôi chắc rằng bạn đã học được nhiều điều mới mà bạn sẽ sử dụng trong các dự án của mình. Nếu hướng dẫn này giúp bạn, hãy chia sẻ nó với người khác. Cảm ơn bạn đã đọc và gặp bạn trong bài tiếp theo!

About the author

lbtmicr06

Leave a Comment

Translate »