Search…

CBP-2: Khai Báo Component Cơ Sở và Entity Cơ Bản

14/09/20206 min read
Tư tưởng của hệ thống Component - Entity trong Component Base Programming (CBP).

Giới thiệu

Giới thiệu tư tưởng của hệ thống Component - Entity mà tôi đang hướng dẫn. Tôi là một con người có thiên hướng về lập trình game, nên những bài viết này được viết bằng ngôn từ và tư duy của một người lập trình game.

Các thành phần cơ bản của hệ thống

Hệ thống gồm các thành phần sau:

  • Component
  • Entity
  • Command

Component

Component là những lớp được định nghĩa để thực hiện một công việc chuyên biệt, chẳng hạn:

  • Component hiển thị hình ảnh.
  • Component di chuyển.
  • Component vật lý.
  • ...

Các quy tắc đối với một Component:

  • Chỉ thực hiện duy nhất một nhiệm vụ.
  • Không trực tiếp liên kết đến các Component khác.
  • Các Component không thuộc nhóm Component ra lệnh không được thực hiện gửi Command cho Entity chủ hay Component khác cùng thuộc Entity chủ.
  • Component dựa vào Command để thực thi những tác vụ tương ứng, một số Component đặc thù dựa vào những phương thức riêng của nó để thực hiện tác vụ. (chẳng hạn Component quản lý hình ảnh sẽ có phương thức draw).

Entity

Entity là đơn vị thực thể trong thế giới game. Bản thân Entity không có ý nghĩa gì cả, thậm chí không có hình ảnh, tọa độ, phản ứng…

Entity chỉ có nghĩa khi sở hữu một hoặc một số component. Dựa vào số lượng, chủng loại và cách thiết đặt của các component được thêm vào mà Entity có những đặc tính khác nhau trong thế giới game.

Các quy tắc đối với một Entity:

  • Không gửi Command cho chính nó.
  • Không sở hữu bất kỳ component nào nằm ngoài danh sách component của nó.
  • Không chứa 2 hoặc nhiều Component cùng loại.

Command

Command là hệ thống chỉ lệnh dùng để giao tiếp giữa Entity và Component. Entity dựa vào hệ thống command để điều khiển các component mà nó sở hữu.

Có 2 loại Command:

  • Entity Command: là loại command mà các Component ra lệnh gửi cho Entity.
  • Component Command: là loại command mà Entity gửi cho các Component.

Các quy tắc đối với Command:

  • Không gửi Entity Command cho Component.
  • Không gửi Command của Component này cho các Component khác, mỗi Component có một danh sách Command riêng.

Khai báo cơ sở và các vấn đề cơ bản

Vấn đề 1: Entity chứa một số lượng không xác định các component?

Đây là một vấn đề đơn giản, có thể sử dụng mảng tĩnh, mảng động hoặc danh sách liên kết, tùy người lập trình định nghĩa, để thuận tiện có thể sử dụng vector.

Vấn đề phát sinh là các Component lúc này phải có cùng "kiểu dữ liệu" để lưu trữ vào mảng hoặc vector. Cần khai báo một lớp làm cơ sở và cho các Component kế thừa từ đó, gọi là CBase - tức Component-Base, sau này các lớp Component tôi sẽ dùng tiền tố C để đánh dấu.

Tóm tắt lại, khai báo thuộc tính lưu trữ component của Entity như sau:

vector<CBase*> m_components;

Vấn đề 2: Đối với một Entity như vậy làm sao để update?

Bản thân Entity không là gì cả, bản thân của component cũng không là gì cả, chỉ khi được kết hợp với nhau chúng mới có thể là một đối tượng trong game.

Đối với CBP, thao tác update một Entity bao gồm 2 bước:

  1. Dựa vào những Entity Command nhận được, Entity tiến hành gửi các Command tương ứng cho component tương ứng.
  2. Cho update lần lượt các Component trong danh sách mà Entity sở hữu, các Component lúc này ngoài những thao tác update mặc định được xác định cho mỗi component, chúng còn dựa vào các Component Command nhận được để có phản ứng phù hợp.

Giả sử có Entity bao gồm:

  • CAnimation: component quản lý animation (hoạt ảnh mà nhân vật hiển thị trên màn hình).
  • CMove: component điều khiển di chuyển của nhân vật.

Khi điều khiển nhân vật di chuyển sang trái, Entity sẽ nhận được một Command, tạm gọi là COMMAND_MOVE_LEFT. Trong bước update, Entity này sẽ lần lượt thao tác như sau:

  1. Gửi COMMAND_ANIMATION_MOVE cho CAnimation: để chuyển hoạt ảnh của nhân vật từ đứng yên sang di chuyển.
  2. Gửi COMMAND_ANIMATION_FACE_LEFT cho CAnimation: để chuyển hoạt ảnh của nhân vật từ nhìn sang phải sang nhìn sang trái.
  3. Gửi COMMAND_MOVE_TO_LEFT cho CMove: để dịch chuyển tọa độ nhân vật sang trái.

Tổng kết vấn đề, thao tác update của Entity chỉ bao gồm gửi Component Command cho các Component, còn xử lý về logic cho game là bước update ngay sau đó của các Component.

Vấn đề 3: Làm sao để vẽ một Entity không chứa gì như vậy?

Đây là một vấn đề phức tạp, vì ngoài việc “không có hình ảnh để vẽ” còn có vấn đề "tọa độ vẽ", trong khi giải quyết vấn đề này còn phải đảm bảo không phá vỡ kiến trúc hệ thống, tách biệt bước updatedraw.

Trước mắt tạm tạo một hàm vẽ trong Entity, cách giải quyết hoàn thiện sẽ được giới thiệu trong bài cuối cùng - bài 10.

Tổng kết

Sau bài viết này, có thể tổng kết được 2 khai báo của Entity và CBase tạm có dạng như sau:

class CBase
{
public:
	virtual int update(float deltaTime);
	virtual int commandProcess(int command);
};

Phương thức updatecommandProcess lúc này không làm gì cả, nội dung chỉ là return 0. Do những Component kế thừa CBase có thể có hoặc không có những phương thức này, ngoài ra, đây cũng là một bước chuẩn bị để ta hiện thực Command Queue ở CBP-6.

class Entity
{
public:
	Entity();
	~Entity();

	int update(float delta);
	void draw();

private:
	vector<CBase*> m_components;
};

Độc giả có thể thắc mắc tại sao tôi lại đặt kiểu trả về là int cho phương thức updatecommandProcess. Đây thật ra là một vấn đề về kỹ thuật, đôi khi có một sự kiện đặc biệt xảy ra, có thể thông qua giá trị trả về để thông báo cho Entity biết, chẳng hạn đối với CAnimation, khi hoạt ảnh đã hoàn tất, trả về một sự kiện EVENT_ANIMATION_END từ hàm update, Entity có thể căn cứ vào sự kiện này để có một số phản ứng chẳng hạn như khi kết thúc hoạt ảnh chuẩn bị nhảy thì gửi sự kiện nhảy đến CMove.

Bài chung series

IO Stream

IO Stream Co., Ltd

30 Trinh Dinh Thao, Hoa Thanh ward, Tan Phu district, Ho Chi Minh city, Vietnam
+84 28 22 00 11 12
developer@iostream.co

383/1 Quang Trung, ward 10, Go Vap district, Ho Chi Minh city
Business license number: 0311563559 issued by the Department of Planning and Investment of Ho Chi Minh City on February 23, 2012

©IO Stream, 2013 - 2024