Hướng dẫn sử dụng Singleton để quản lý game trong Unity3D
Giới thiệu Như ở trên bạn đã nhìn thấy quy trình hoạt động cơ bản của một trò chơi .Ta thấy trọng một game có các trạng thái cơ bản như : Start,Play,Pause,Stop,Game over (win or lose). Để quản lý tất cả các trạng thái ấy chúng ta cần có một cơ chế quản lý thích hợp để có thể cho biết chúng ...
Giới thiệu
Như ở trên bạn đã nhìn thấy quy trình hoạt động cơ bản của một trò chơi .Ta thấy trọng một game có các trạng thái cơ bản như : Start,Play,Pause,Stop,Game over (win or lose).
Để quản lý tất cả các trạng thái ấy chúng ta cần có một cơ chế quản lý thích hợp để có thể cho biết chúng ta đang ở trạng thái nào , chuyển trạng thái , cũng như những chức năng khác như quản lý điểm , quản lý vật phẩm mua được trong game ...
Ngoài ra còn cần quản lý các chức năng như chơi nhạc , quản lý giao diện người dùng . Vì vậy chúng ta cần có một cơ chế để đảm bảo các yếu tố sau :
- dễ dàng tái sử dụng code mà không bị lặp .
- dễ dàng để hiểu và tái cấu trúc lại, thay đổi mà không có bất kỳ sự mơ hồ khó hiểu nào .
Một số thứ cơ bản trong game mà ta cần có cơ chế quản lý là : Texture, audio, Configuration ,Input, UI, Game manager và một số thứ khác tuỳ từng game khác nhau .
1. Tại sao nên sử dụng Singleton để quản lý game trong Unity
Trong một game chúng ta cần một đối tượng quản lý có thể được truy cập ở mọi nơi nhưng chỉ được khởi tạo một lần .
Ví dụ :
Để quản lý việc phát âm thanh trong game chúng ta cần một đối tượng Audio Manager duy nhất bởi vì nó là không hay nếu ta có nhiều đối tượng quản lý việc phát những âm thanh khác nhau trong game , sẽ dễ gây nên nhiễu loạn phát âm thanh không đúng không chuẩn .Vì thế trong game đối tượng quản lý nên khởi tạo một lần duy nhất và duy trì nó trong suốt cả game tức là không bị xoá đi khi chuyển đến màn hình khác .
Giải pháp 1: Phương pháp phổ biến nhất để tạo ra một đối tượng truy cập trong cả game và duy nhất là ta sẽ tạo ra một prefab có gắn script manager , ta có thể gắn nhiều script manager cho đối tượng duy nhất này và sẽ sử dụng nó bằng cách add nó vào tất cả các scene của game.
Nhưng singleton là một cách nâng cao tốt hơn vì nó chỉ khởi tạo một lần duy nhất.
Giải pháp 2 : Đó là ta tạo ra một class manager dạng tĩnh static nhưng nó cũng có nhược điểm :
- class tĩnh không thể được khởi tạo .
- bạn không thể vượt qua class tĩnh như một tham số .
- class tĩnh không thể kế thừa lớp khác vì thế nó cũng không thể kế thừa class MonoBehaviour
Trong khi đó lớp singleton có thể kế thừa MonoBehaviour và nó cũng có thể được khởi tạo .
Giải pháp 3 : Đó là tạo ra tất cả các thành phần của class public statis . Nhưng :
- giá trị tĩnh được khởi tạo trước khi instantiation đó là không cần thiết.
- Nếu một lớp kế thừa interface sau đó thực hiện các phương thức của interface tĩnh nó không thể được sử dụng trong các class khác mà không instance, ví dụ. Đối với kịch bản này, chúng ta phải tạo ra các trường hợp mà chúng ta muốn sử dụng những phương pháp tức là nó không thể sử dụng thừa kế của interface hoặc abstract classes.
Singleton cũng đưa ra một giải pháp cho vấn đề loại này.
2 . Giới thiệu về Singleton
Tên Singleton có nghĩa là một điều duy nhất cho tất cả các loại được xem xét .
Singleton pattern cho phép bạn viết một lớp, mà chỉ có thể được khởi tạo một lần. Singleton pattern là rất hữu ích, nhưng nó là một mẫu thiết kế rất đơn giản. Nó hạn chế instantiation bên ngoài class bằng cách tạo ra constructor private.
public class Singleton : MonoBehaviour{ //create an object of Singleton public static Singleton instance = new Singleton(); //make the constructor private so that this class cannot be //instantiated for outside the class. private Singleton(){} }
Lý do lớn nhất singleton được sử dụng là để cho code rõ ràng. Bất kỳ lập trình viên nào mà nhận ra một cái gì đó là một singleton thì nên sử dụng nó. Ngoài hỗ trợ khái niệm một singleton cũng có thể là một phương tiện hiệu quả của việc cho phép truy cập global của một đối tượng. Các mẫu thiết kế Singleton tạo điều kiện truy cập global đến một đối tượng trong khi đảm bảo rằng chỉ có một thể hiện của đối tượng tồn tại một thời điểm. Nếu một thể hiện của singleton không tồn tại khi nó được tham chiếu, nó sẽ được khởi tạo
Khởi tạo singleton khi khởi động
void Awake() { // Save a Reference to the component as our singleton instance Instance = this; } Public static Singleton Instance{get;private set;}
3 . Ví dụ mẫu
Using UnityEngine; Public class AudioManager : MonoBehaviour { Public AudioClip Clip; // Static singleton property. Public static AudioManager Instance { get; private set; } Void Awake() { // Save a reference to the AudioManager component as our //singleton instance. Instance = this; } // Instance method, this method can be accessed through the //singleton instance Public void PlayAudio(AudioClip clip) { audio.clip = clip; audio.Play(); } }
Bây giờ để truy cập chúng ta làm như sau :
AudioManager.Instance.PlayAudio(AudioManager.Instance.Clip);
Đây là instantiation cho phép chúng ta muốn sử dụng nó từ đầu hay không, vì vậy để khởi tạo lười biếng thì đây là giải pháp duy nhất: "Lazy Initialization" Lazy Initialization là một kỹ thuật mà một trì hoãn instantiation của một đối tượng cho đến khi sử dụng lần đầu tiên của nó. Nói cách khác là thể hiện của một class được tạo ra khi nó yêu cầu để được sử dụng lần đầu tiên. Ý tưởng đằng sau là để tránh quá trình không cần thiết và tốn kém của việc tạo ra instance cho đến khi nó không phải là bắt buộc.
public class UIManager : MonoBehaviour { public static UIManager UIManagerInstance ; public static UIManager Instance { // Single Instance assurity get { if (UIManagerInstance == null) { UIManagerInstance = FindObjectOfType (); if (UIManagerInstance == null) { GameObject obj = new GameObject (); UIManagerInstance = obj.AddComponent (); } } return UIManagerInstance; } } void Awake () { GameObject.DontDestroyOnLoad (gameObject); }
Việc thực hiện trên các tìm kiếm đầu tiên cho một thể hiện của các thành phần Singleton trong scene nếu một tham chiếu không tồn tại. Nếu nó không tìm thấy một thành phần Singleton, một GameObject được tạo ra và một thành phần Singleton được gắn vào nó. Dòng tiếp theo khởi tạo các reference tĩnh của Singleton. dòng mã "GameObject.DontDestroyOnLoad (gameObject);" trong hàm Awake () là Phương pháp làm UIManager tồn tại trong tất cả các scene. Nó có thể là quá trình rất dài để sao chép và dán mã này vào mỗi lớp manager. C# hỗ trợ class generic vì vậy chúng ta không những có thể tạo ra class generic mà còn cung cấp cách thức bảo vệ chống lại các race conditions như sau:
public class Singleton : MonoBehaviour where T : MonoBehaviour { private static T _instance; private static object _lock = new object (); public static T Instance { get { if (applicationIsQuitting) { Debug.LogWarning ("[Singleton] Instance '" + typeof(T) + "' already destroyed on application quit." + " Won't create again - returning null."); return null; } lock (_lock) { if (_instance == null) { _instance = (T)FindObjectOfType (typeof(T)); if (FindObjectsOfType (typeof(T)).Length > 1) { Debug.LogError ("[Singleton] Something went really wrong "+ " - there should never be more than 1 singleton!" + " Reopenning the scene might fix it."); return _instance; } if (_instance == null) { GameObject singleton = new GameObject (); _instance = singleton.AddComponent (); singleton.name ="(singleton)" + typeof(T).ToString(); DontDestroyOnLoad (singleton); Debug.Log ("[Singleton] An instance of " + typeof(T)+ " is needed in the scene, so '" + singleton + "' was created with DontDestroyOnLoad."); } else { //Debug.Log("[Singleton] Using instance already created: " + _instance.gameObject.name); } } return _instance; } } } private static bool applicationIsQuitting = false; public void OnDestroy () { applicationIsQuitting = true; } }
4 . Kết luận
Một số game yêu cầu hàng chục kịch bản quản lý do đó nó là cần thiết để giảm số lượng mã trùng lặp và chuẩn hóa trên setting up, referencing, tearing xuống manager. Một lớp cơ sở singleton chung chung là một giải pháp như vậy sẽ phục vụ chúng ta tốt. Mặc dù Singleton là một mẫu thiết kế, nó không phải là giải pháp hoàn hảo. Sử dụng singleton trong một số kịch bản có thể hữu ích trong một số trường hợp, nhưng trong một số khác nó không được khuyến khích. Tuy nhiên Singleton pattern cho ta một cách linh hoạt trong việc quản lý đối tượng theo yêu cầu .