STDIO
Tìm kiếm gần đây

    Nội dung

    Design Pattern: State Pattern

    Hoàng Thái

    06/09/2015
    03/08/2020
    Design Pattern: State Pattern
    Trong quá trình làm game ở trường, tôi gặp phải một vấn đề khó khăn trong việc xử lý các trạng thái của đối tượng khiến công việc trở nên phức tạp và khó hiểu. Nhưng nhờ có một người bạn gợi ý cho tôi về State Pattern để xử lý các trạng lý từ đó công việc trở nên dễ dàng và thuận tiện hơn.

    Giới thiệu

    State Pattern là một trong những design pattern rất phổ biến trong phát triển game cũng như ứng dụng. Bài viết sẽ cung cấp các kiến thức cơ bản về State Pattern và cách thức hoạt động của nó.

    Vấn đề đặt ra

    Trong khi lập trình các hoạt động của đối tượng trong Game, tôi gặp phải một vấn đề phức tạp trong việc xử lý các trạng thái của đối tượng.

    Ban đầu, tôi tưởng chừng mọi thứ có vẻ rất đơn giản khi xử lý đối tượng Object như sau.

    void    Object::update(float delta)
    {
        if (KeyPress(BUTTON_LEFT_ARROW))
        {
            this->velocity = MOVE_LEFT_VELOCITY;
            this->image = MOVE_LEFT_IMAGE;
        }
    }

    Nhưng sau đó, Object của tôi có thêm trạng thái “Jump” và mọi vấn đề nảy sinh khi tôi thêm vào trong code của minh.

    void    Object::update(float delta)
    {
        if (KeyPress(BUTTON_LEFT_ARROW))
        {
            this->velocity    = MOVE_LEFT_VELOCITY;
            this->image        = MOVE_LEFT_IMAGE;
        }
    
        if (KeyPress(BUTTON_SPACE))
        {
            this->velocity    = JUMP_VELOCITY;
            this->image        = JUMP_IMAGE;
        }
    }

    Ở đây có 2 ván đề này sinh đó là: Khi nhấn Space thì Object nhảy liên tục và nhấn Space + Left Arrow để nhảy qua bên trái thì hình ảnh không chính xác. Tôi thêm thuộc tính m_Jumping kiểu bool để kiểm tra Object khi Jump và sửa lại như sau:

    if (KeyPress(BUTTON_LEFT_ARROW))
    {
    	this->velocity    = MOVE_LEFT_VELOCITY;
    	if (!this->m_Jumping)
    		this->image    = MOVE_LEFT_IMAGE;
    }
    
    if (KeyPress(BUTTON_SPACE))
    {
    	if (!m_Jumping)
    	{
    		this->m_Jumping     = true;
    		this->velocity    = JUMP_VELOCITY;
    		this->image        = JUMP_IMAGE;
    	}
    }

    Vấn đề có vẻ được giải quyết, nhưng tôi tưởng tượng khi Object có thêm nhiều trạng thái khác nữa thì tôi phải thêm rất nhiều biến bool và rất nhiều câu điều kiện if … else … nữa. 

    Giải pháp

    Trong một lần tình cờ tôi đề cập vấn đề với bạn của mình và đã được giới thiệu một giải pháp đó là State Pattern.

    Mô hình của State Pattern

    ss_1

    Khái niệm cơ bản

    State là “trạng thái” của đối tượng trong Game.

    Ví dụ: 

    • Object có các state là Stand, Running, Jumpping…
    • Scene có các state là Intro, Play, End,…

    State Pattern là cách tổ chức quản lý các State một cách hợp lý nhất.

    Đặc điểm

    Giảm bớt rắc rối trong quá trình xử lý các sự kiện.

    Dễ dàng chỉnh sửa trong quá trình lập trình.

    Cách hiện thực

    Trong bài viết này, tôi trình bày State Pattern dưới 2 dạng: 

    • Enum kết hợp với Switch-Cases.
    • OOP

    Enum kết hợp với Switch – Cases

    Đầu tiên, các bạn phải xác định được các State của đối tượng và tạo Enum cho các State đó. Ở trong ví dụ dưới đây, Object của tôi có 3 loại State: Stand, Move và Jump nên sẽ có Enum cho các State đó là:

    enum State
    {
        STAND_STATE = 0,
        MOVE_STATE,
        JUMP_STATE
    };

    Sau khi đã có được Enum State của đối tượng, các bạn áp dụng vào trong class đối tượng của mình các tạo State Pattern như sau: 

    Trong khai báo lớp Object

    #ifndef __OBJECT_H__
    #define __OBJECT_H__
    
    #include <stdio.h>
    
    class Object
    {
    public:
        void    update(float delta);
    private:
        State    m_currentState;
    };
    
    #endif // !__OBJECT_H__

    Trong định nghĩa lớp Object

    #include "Object.h"
    
    void    Object::update(float delta)
    {
        switch (this->m_currentState)
        {
        case State::STAND_STATE:
            // Làm bất kỳ các hoạt động nào mà bạn muốn
            // khi đối tượng Object đang ở trong trạng thái Stand.
            break;
        case State::MOVE_STATE:
            // Tương tự Stand State.
            break;
        case State::JUMP_STATE:
            // Tương tự Stand State.
            break;
        default:
            break;
        }
    }

    OOP

    Tương tự như Enum kết hợp với Switch – Cases, các bạn cũng cần phải xác định các State của đối tượng nhưng cách hiện thực State Pattern theo hướng OOP.

    Đầu tiên, các bạn phải tạo lớp State thuần ảo.

    #ifndef __STATE_H__
    #define __STATE_H__
    
    #include <stdio.h>
    
    class State
    {
    public:
        State();
        ~State();
    
        virtual    void enter() = 0;
        virtual    void update(float delta) = 0;
        virtual    void exit() = 0;
    };
    
    #endif // !__STATE_H__

    Ở trong ví dụ này, tôi sẽ cần override lại 3 phương thức đó là: 

    • Enter: Là phương thức được gọi khi chuyển từ State cũ sang State mới. Phương thức này khởi tạo các điều kiện cần thiết để phương thức Update hoạt động một cách hiệu quả.
    • Update: Là phương thức cập nhật của đối tượng khi đang ở trong một State bất kỳ nào đó.
    • Exit: Là phương thức được gọi khi kết thúc State cũ để chuyển sang State mới. Phương thức này có thể kết thúc các điều kiện cần thiết đã khởi tạo từ phương thức Enter trước đó.

    Tạo lớp StandState kế thừa từ State, và overrride lại các phương thức cần thiết.

    #ifndef __STAND_STATE_H__
    #define __STAND_STATE_H__
    
    #include "State.h"
    
    class StandState : public State
    {
    public:
        StandState();
        ~StandState();
    
        void    enter();
        void    update(float delta);
        void    exit();
    };
    
    #endif // !__STAND_STATE_H__

    Sau khi đã có đầy đủ các State của đối tượng, các bạn áp dụng vào trong class của đối tượng như sau:

    Ở trong khai báo lớp Object, các bạn khai báo:

    • Thuộc tính State* m_currentState “chạy” State hiện tại của đối tượng
    • Phương thức  void changeState(State* state) để thay đổi các State của đối tượng.
    #ifndef __OBJECT_H__
    #define __OBJECT_H__
    
    #include <stdio.h>
    #include "State.h"
    
    class Object
    {
    public:
        Object();
        ~Object();
    
        void    update(float delta);
        
        void    changeState(State* state);
    private:
        State*    m_currentState;
    };
    
    #endif // !__OBJECT_H__

    Ở trong định nghĩa lớp Object, các bạn sử dụng thuộc tính và phương thức như sau.

    #include "Object.h"
    
    Object::Object()
    {
        m_currentState = new StandState();
    }
    
    Object::~Object()
    {
        if (m_currentState != nullptr)
        {
            delete m_currentState;
            m_currentState = nullptr;
        }
    }
    
    void    Object::update(float delta)
    {
        m_currentState->update(delta);
    }
    
    void    Object::changeState(State* state)
    {
        if (m_currentState)
            m_currentState->exit();
        m_currentState = state;
        m_currentState->enter();
    }

    Tương tự đối với các State còn lại của đối tượng.

    Lưu ý

    Ở trong Game, các bạn cần phân biệt giữa Game State và Object State:

    • Game State có nghĩa là các trạng thái của Game. Tôi ví dụ các trạng thái của Game như sau: IntroState – là State giới thiệu về game; PlayState – là State “chạy” game; …
    • Object State có nghĩa là các trạng thái của Object. Tôi ví dụ các trạng thái của Object như sau: StandState – là State đứng yên của Object; MoveState – là State di chuyển của Object ….

    Kinh nghiệm

    Khi áp dụng State Pattern vào trong Game của tôi, tôi đã sử dụng Enum cho Object State và OOP cho GameState. Lý do:

    • Object State của mỗi đối tượng là khác nhau nên khi các bạn làm theo hướng OOP sẽ cần phải khởi tạo rất nhiều class khác nhau nên rất khó để tôi có thể quản lý hết.
    • Game State thì các State của nó mang tính duy nhất nên các bạn có thể kết hợp OOP của State Pattern với Singleton Pattern để quản lý Game State một cách đơn giản.

    Tham khảo

    • http://gameprogrammingpatterns.com/state.html - 16/07/2015
    • http://gamedevgeek.com/tutorials/managing-game-states-in-c/ - 16/07/2015

    Thảo luận

    Đăng nhập

    Bài viết liên quan

    Design Pattern: Proxy Pattern

    Design Pattern: Proxy Pattern

    Proxy Pattern là một trong những Design Pattern thường được áp dụng khi phát triển phần mềm cũng như thiết kế game, là một mẫu thiết kế thuộc nhóm Structural patterns. ...

    Software ArchitectureDesign Patterns

    18/10/2017

    Design Pattern: Builder Pattern

    Design Pattern: Builder Pattern

    Builder Pattern là một trong những Design Pattern phổ biến trong thiết kế và phát triển các phần mềm ứng dụng cũng như games. Bài viết sẽ cung cấp các kiến thức ...

    Software ArchitectureDesign Patterns

    05/02/2017

    Design Pattern: Prototype Pattern

    Design Pattern: Prototype Pattern

    Prototype Pattern là một trong những Pattern phổ biến trong lập trình hướng đối tượng, là một Pattern thuộc nhóm Creational Patterns. Ý tưởng này là một design pattern ...

    Software ArchitectureDesign Patterns

    25/03/2017

    Design Pattern: Adapter Pattern

    Design Pattern: Adapter Pattern

    Trong lập trình hướng đối tượng, một khái niệm cực kì quan trọng đó là “giao diện của lớp” . Trong bài viết này sẽ đề cập đến một design pattern được sử dụng rất phổ biến ...

    Software ArchitectureDesign Patterns

    28/11/2014

    Design Pattern: Abstract Factory Pattern

    Design Pattern: Abstract Factory Pattern

    Được phát triển dựa trên kiến thức đa hình trong phương pháp lập trình hướng đối tượng. Abstract Factory là một trong những Design Pattern thông dụng, làm các đoạn mã trở ...

    Software ArchitectureDesign Patterns

    01/06/2015

    Design Pattern: Dependency Injection

    Design Pattern: Dependency Injection

    Khi thực hiện những dự án với độ phức tạp cao ngoài việc thiết kế tính năng cho ứng dụng, tổ chức code luôn luôn là vấn đề được đặt lên hàng đầu. Tổ chức tốt giúp lập ...

    Software ArchitectureDesign Patterns

    28/12/2014

    Design Pattern: Singleton Pattern

    Design Pattern: Singleton Pattern

    Singleton Pattern là một trong những Design Pattern rất phổ biến trong phát triển game cũng như ứng dụng. Trong kỹ thuật phần mềm, Singleton là một mẫu thiết kế nhằm hạn ...

    Software ArchitectureDesign Patterns

    07/09/2015

    Design Pattern: Singleton Pattern

    Design Pattern: Singleton Pattern

    Làm thế nào để hiện thực được một đối tượng tồn tại duy nhất song có thể truy xuất mọi lúc mọi nơi trong chương trình. Để giải bài toán trên, người ta hướng đến một giải ...

    Software ArchitectureDesign Patterns

    17/08/2015

    Design Pattern: Factory Pattern

    Design Pattern: Factory Pattern

    Factory Pattern là một trong những Pattern phổ biến trong lập trình hướng đối tượng. Khi một chương trình có nhiều đối tượng, việc quản lý khởi tạo các đối tượng đó sẽ ...

    Software ArchitectureDesign Patterns

    26/07/2015

    Design Pattern: MVC Pattern

    Design Pattern: MVC Pattern

    Model – View – Controller hay MVC pattern được sử dụng phổ biến trong mảng ứng dụng web ngày nay. MVC được sử dụng lần đầu tiên tại Smalltalk. Hiện tại, có rất nhiều nền ...

    Software ArchitectureDesign Patterns

    04/12/2015

    STDIO
    Trang chính
    Công ty TNHH STDIO

    30, Trịnh Đình Thảo, Hòa Thạnh, Tân Phú, Hồ Chí Minh
    +84 28.36205514 - +84 942.111912
    [email protected]

    383/1 Quang Trung, Phường 10, Quận Gò Vấp, Hồ Chí Minh
    Số giấy phép ĐKKD: 0311563559 do sở Kế hoạch và Đầu Tư TPHCM cấp ngày 23/02/2012

    ©STDIO, 2013 - 2020