Máy Trạng Thái Hữu Hạn (FSM) - Triển Khai Mẫu Trạng Thái Trong Unity và C#

Interface Trạng Thái

Mỗi lớp trạng thái cần implement interface State như GameConnectStart, GameConnectIng, GameConnectERROR,... Triển khai các phương thức của interface và lưu trữ từng trạng thái trong GameStateMachine để tiện quản lý.

public interface ITrangThai // Định nghĩa interface trạng thái
{
    void KhoiTao(); // Phương thức khởi tạo

    int LayIdTrangThaiHienTai(); // Trả về ID của trạng thái hiện tại

    void SuKienVao(ITrangThai trangThai); // Sự kiện khi vào trạng thái

    void SuKienRa(ITrangThai trangThai); // Sự kiện khi rời trạng thái
}

Interface Máy Trạng Thái

Địn nghĩa interface cho máy trạng thái với các phương thức cần thiết.

public interface IMayTrangThai<T> // Định nghĩa interface máy trạng thái
{
    T LayTrangThaiHienTai(); // Trả về trạng thái hiện tại

    T LayTrangThaiTruocDo(); // Trả về trạng thái trước đó

    void DangKy(T t); // Đăng ký trạng thái

    void XoaTrangThai(T t); // Xóa trạng thái

    ChuyenTrangThai(T t); // Chuyển đổi trạng thái
    
}

Máy Trạng Thái Game

public class MayTrangThaiGame : IMayTrangThai<ITrangThai>
{
    public ITrangThai TrangThaiHienTai = null; // Trạng thái hiện tại
    public ITrangThai TrangThaiTruocDo = null; // Trạng thái trước đó

    private Dictionary _danhSachTrangThai; // Lưu trữ các trạng thái

    public MayTrangThaiGame()
    {
        _danhSachTrangThai = new Dictionary();
    }

    public ITrangThai LayTrangThaiHienTai()
    {
        return TrangThaiHienTai; // Trả về trạng thái hiện tại
    }

    public ITrangThai LayTrangThaiTruocDo()
    {
        return TrangThaiTruocDo; // Trả về trạng thái trước đó
    }

    public void DangKy(ITrangThai t)
    {
        if (_danhSachTrangThai.ContainsKey(t.LayIdTrangThaiHienTai())) // Kiểm tra trạng thái đã được đăng ký chưa
            return;

        _danhSachTrangThai.Add(t.LayIdTrangThaiHienTai(), t); // Thêm trạng thái vào danh sách
    }

    public void XoaTrangThai(ITrangThai t)
    {
        if (!_danhSachTrangThai.ContainsKey(t.LayIdTrangThaiHienTai())) // Kiểm tra trạng thái đã được đăng ký chưa
            return;
        
        _danhSachTrangThai.Remove(t.LayIdTrangThaiHienTai()); // Xóa trạng thái khỏi danh sách
    }

    public void ChuyenTrangThai(ITrangThai t)
    {
        if (!_danhSachTrangThai.ContainsKey(t.LayIdTrangThaiHienTai())) // Kiểm tra trạng thái đã được đăng ký chưa
            return;

        if (TrangThaiHienTai !=null) // Kiểm tra có phải lần đầu tiên chuyển trạng thái không
        {
            if (TrangThaiHienTai.LayIdTrangThaiHienTai() == t.LayIdTrangThaiHienTai()) // Kiểm tra trạng thái mới có giống trạng thái hiện tại không
                return;

            TrangThaiHienTai.SuKienRa(t); // Thực hiện sự kiện khi rời trạng thái hiện tại
            TrangThaiTruocDo = TrangThaiHienTai; // Gán trạng thái hiện tại làm trạng thái trước đó
        }
        t.KhoiTao(); // Thực hiện khởi tạo trạng thái mới
        TrangThaiHienTai = t; // Gán trạng thái mới là trạng thái hiện tại
        TrangThaiHienTai.SuKienVao(t); // Thực hiện sự kiện khi vào trạng thái mới
    }

    public void ChuyenTrangThai(int id)
    {
        if (!_danhSachTrangThai.ContainsKey(id)) // Kiểm tra trạng thái đã được đăng ký chưa
            return;

        ITrangThai t = _danhSachTrangThai[id]; // Tìm trạng thái theo ID

        if (TrangThaiHienTai != null)
        {
            if (TrangThaiHienTai.LayIdTrangThaiHienTai() == id)
                return;
            TrangThaiHienTai.SuKienRa(t);
            TrangThaiTruocDo = TrangThaiHienTai;
        }
        t.KhoiTao(); // Thực hiện khởi tạo trạng thái mới
        TrangThaiHienTai = t;
        TrangThaiHienTai.SuKienVao(t);
    }

}

Enum Trạng Thái Game

public enum TrangThaiGame
{
    TRANG_THAI_KET_NOI,// Bắt đầu
    TRANG_THAI_DANG_KET_NOI,
    TRANG_THAI_KET_NOI_THAT_BAI,
    TRANG_THAI_KET_THUC,
}

Trạng Thái Bắt Đầu Kết Nối

public class TrangThaiBatDauKetNoi : ITrangThai
{
    public void SuKienVao(ITrangThai trangThai)
    {
        Console.WriteLine("Đã vào trạng thái Bắt đầu");
    }

    public int LayIdTrangThaiHienTai()
    {
        return (int)TrangThaiGame.TRANG_THAI_KET_NOI;
    }

    public void KhoiTao()
    {
        Console.WriteLine("Khởi tạo trạng thái Bắt đầu");
    }

    public void SuKienRa(ITrangThai trangThai)
    {
        Console.WriteLine("Thoát trạng thái Bắt đầu, chuyển sang trạng thái " + (TrangThaiGame)trangThai.LayIdTrangThaiHienTai());
    }
}

Trạng Thái Đang Kết Nối

public class TrangThaiDangKetNoi : ITrangThai
{
    public void SuKienVao(ITrangThai trangThai)
    {
        Console.WriteLine("Đã vào trạng thái Đang kết nối");
    }

    public int LayIdTrangThaiHienTai()
    {
        return (int)TrangThaiGame.TRANG_THAI_DANG_KET_NOI;
    }

    public void KhoiTao()
    {
        Console.WriteLine("Khởi tạo trạng thái Đang kết nối");
    }

    public void SuKienRa(ITrangThai trangThai)
    {
        Console.WriteLine("Thoát trạng thái Đang kết nối, chuyển sang trạng thái " + (TrangThaiGame)trangThai.LayIdTrangThaiHienTai());
    }
}

Trạng Thái Kết Nối Thất Bại

public class TrangThaiKetNoiThatBai : ITrangThai
{
    public void SuKienVao(ITrangThai trangThai)
    {
        Console.WriteLine("Đã vào trạng thái Kết nối thất bại, chuẩn bị chuyển sang trạng thái Kết thúc");
        QuanLyDonThe.Instance.ChuyenTrangThai(TrangThaiGame.TRANG_THAI_KET_THUC); // Chuyển sang trạng thái kết thúc
    }

    public int LayIdTrangThaiHienTai()
    {
        return (int)TrangThaiGame.TRANG_THAI_KET_NOI_THAT_BAI;
    }

    public void KhoiTao()
    {
        Console.WriteLine("Khởi tạo trạng thái Kết nối thất bại, báo lỗi");
    }

    public void SuKienRa(ITrangThai trangThai)
    {
        Console.WriteLine("Thoát trạng thái Kết nối thất bại, chuyển sang trạng thái " + (TrangThaiGame)trangThai.LayIdTrangThaiHienTai());
    }
}

Trạng Thái Kết Thúc

public class TrangThaiKetThuc : ITrangThai
{
    public void SuKienVao(ITrangThai trangThai)
    {
        Console.WriteLine("Đã vào trạng thái Kết thúc");
    }

    public int LayIdTrangThaiHienTai()
    {
        return (int)TrangThaiGame.TRANG_THAI_KET_THUC;
    }

    public void KhoiTao()
    {
        Console.WriteLine("Khởi tạo trạng thái Kết thúc");
    }

    public void SuKienRa(ITrangThai trangThai)
    {
        Console.WriteLine("Thoát trạng thái Kết thúc, chuyển sang trạng thái " + (TrangThaiGame)trangThai.LayIdTrangThaiHienTai() + "/n");
    }
}

Tạo Đối Tượng Đơn L Để Quản Lý Máy Trạng Thái

(Có thể triển khai theo nhiều cách khác nhau, tùy theo nhu cầu dự án)

QuanLyDonThe (Đơn Thể)

public class QuanLyDonThe
{
    static QuanLyDonThe _instance;

    private MayTrangThaiGame _mayTrangThaiGame = new MayTrangThaiGame(); // Máy trạng thái

    // Bốn trạng thái
    private TrangThaiBatDauKetNoi _trangThaiBatDau = new TrangThaiBatDauKetNoi();
    private TrangThaiDangKetNoi _trangThaiDangKetNoi = new TrangThaiDangKetNoi();
    private TrangThaiKetNoiThatBai _trangThaiThatBai = new TrangThaiKetNoiThatBai();
    private TrangThaiKetThuc _trangThaiKetThuc = new TrangThaiKetThuc();

    // Đơn thể
    public static QuanLyDonThe Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new QuanLyDonThe();
            }
            return _instance;
        }
    }

    /// <summary>
    /// Đăng ký trạng thái
    /// </summary>
    public void KhoiTao()
    {
        _mayTrangThaiGame.DangKy(_trangThaiBatDau);
        _mayTrangThaiGame.DangKy(_trangThaiDangKetNoi);
        _mayTrangThaiGame.DangKy(_trangThaiThatBai);
        _mayTrangThaiGame.DangKy(_trangThaiKetThuc);
    }

    /// <summary>
    /// Chuyển đổi trạng thái
    /// </summary>
    /// <param name="state"> Loại trạng thái </param>
    public void ChuyenTrangThai(TrangThaiGame state)
    {
        _mayTrangThaiGame.ChuyenTrangThai((int)state);
    }
}

Chạy Trong Chương Trình Chính

static void Main(string[] args)
    {
        QuanLyDonThe.Instance.KhoiTao(); // Đăng ký trạng thái

        QuanLyDonThe.Instance.ChuyenTrangThai(0);
        QuanLyDonThe.Instance.ChuyenTrangThai((TrangThaiGame)1);
        QuanLyDonThe.Instance.ChuyenTrangThai((TrangThaiGame)2);

        Console.ReadKey();
    }

Thẻ: Unity C# FSM StatePattern DesignPattern

Đăng vào ngày 7 tháng 7 lúc 12:20