Search…

CBP-3: Khai Báo Component Cơ Bản, Tích Hợp Component vào Entity

15/09/20206 min read
Khai báo component CAnimation để thêm hình ảnh hiển thị cho Entity, đây là một Component tiêu biểu và có liên quan đến một số Component khác nên tôi sẽ hiện thực để độc giả có được cái nhìn tốt nhất về hệ thống Component Base do tôi định nghĩa.

Giới thiệu

Sau bài viết trước, component cơ sở CBaseEntity cơ bản đã được giới thiệu và khai báo. Bài viết này tiến hành nâng cấp CBase để định nghĩa Component tiêu biểu nhất và thêm vào Entity để kiểm tra.

Tiến hành các bước cụ thể đối với CAnimation - Component quản lý hoạt ảnh của Entity.

Tải project mẫu

Đây là project ví dụ dùng trong bài viết: CBP-3-2019.zip.

Project này có thể mở bằng Visual Studio 2019, project này chứa các file mã C++ của CBP, project có thể không tương thích với nhiều môi trường Visual Studio khác nhau nhưng có thể dành để tham khảo.

Phân tích component quản lý hoạt ảnh

Phân tích chức năng

Component quản lý hoạt ảnh của Entity có nhiệm vụ quản lý hình ảnh hiển thị, 1 Entity có thể bao gồm nhiều loại hoạt ảnh khác nhau như đứng, ngồi, di chuyển, nhảy, ...

Cần đảm bảo rằng Entity sẽ không có hình ảnh khi thiếu CAnimation vì nó nắm giữ thao tác draw của đối tượng game.

CAnimation không quản lý những hình ảnh không phải là hình ảnh của đối tượng mà nó định nghĩa. Ví dụ: 1 đối tượng quái vật Slime sẽ có CAnimation chứa các hình ảnh về đứng, di chuyển, bị tấn công hay tấn công, ... chứ không chứa các hình ảnh như bụi bay sau bước nhảy, hiệu ứng ánh sáng lấp lánh quanh Slime, hiệu ứng nổ khi bị tấn công, ... các hình ảnh này thuộc về những Entity hiệu ứng sẽ được thêm vào sau này.

Phân tích chức năng đặc thù

CAnimation nắm giữ hoạt ảnh của Entity nên cần có phương thức hiển thị hình ảnh của Entity lên màn hình, đặt tên nó là phương thức draw.

Khai báo cơ bản của CAnimation

class CAnimation : public CBase
{
public: 
	static CAnimation* addComponentTo(Entity* object, const char* resourcePath);

	virtual int update(float deltaTime);
	virtual int commandProcess(int command);

	int draw();

private:
	CAnimation();
	~CAnimation();

private:
	vector<FTexture*> m_animate;
};

Kế thừa từ CBase

Như đã đề cập ở CBP-2, tất cả các lớp định nghĩa Component phải được kế thừa từ CBase nhằm thuận tiện cho công tác quản lý ở Entity.

Phương thức addComponentTo

Đây là 1 quy tắc riêng về hệ thống Component. Mỗi lớp Component phải có 1 phương thức addComponentTo để khởi tạo Component và thêm vào đối tượng được truyền vào. Do số lượng đối số cần để khởi tạo mỗi loại Component là khác nhau nên không thể khai báo phương thức này thuần ảo ở lớp CBase được.

Phương thức update, commandProcess

Đây là 2 phương thức cơ bản cho mỗi Component.

  • Phương thức update tập trung xử lý logic cho component (Animation sẽ tính toán thời gian chuyển frame, Physic sẽ tính toán va chạm, trọng lực, ...).
  • Phương thức commandProcess xử lý những Component Command nhận được từ Entity và đưa ra những phản ứng phù hợp với từng Command.

Phương thức draw

Đây là phương thức đặc trưng của CAnimation, phương thức này chịu trách nhiệm vẽ hình ảnh của Entity lên cửa sổ hiển thị.

Constructor, Destructor

Phương thức tạo và hủy đối tượng đặt trong tầm vực private nhằm ngăn developer khởi tạo mới Component 1 cách tự do. Ở đây, developer muốn tạo Component mới thì bắt buộc phải thông qua phương thức addComponentTo và cũng trong phương thức này, Component được sinh ra sẽ được thêm vào Entity được truyền vào, đảm bảo không có Component nào vô chủ.

Thuộc tính m_animate

Đây là thuộc tính đặc trưng của CAnimation, là nơi lưu trữ các hoạt ảnh hiển thị của Entity.

Về kiểu dữ liệu FTexture có thể đọc lại bài CBP-1.

Lưu ý

Trong phạm vi chủ đề, tôi sẽ không giới thiệu sâu về các kỹ thuật khác, độc giả có thể tùy chỉnh Component sao cho phù hợp nhất với kỹ thuật và phương pháp hiện thực của bản thân, vì những khai báo này là không đầy đủ trong các dự án thật.

Tích hợp component vào Entity

File main.cpp, giả định mọi phương thức đã sẵn sàng để sử dụng và tiến hành khai báo như sau:

Tại khu vực global:

Entity* g_entity;

Trong hàm init:

g_entity = new Entity();
CAnimation::addComponentTo(g_entity, "Resource\\logo.bmp");

Trong hàm release:

delete(g_entity);

Trong hàm draw:

g_entity->draw(hdc);

Trong hàm update:

g_entity->update(delta);

Ở tầng developing, chỉ với những thao tác rất đơn giản như vậy là 1 Entity đã sẵn sàng được sử dụng.

Sâu xuống nội dung các phương thức được sử dụng trên đây, lần lượt tiến hành định nghĩa như sau:

CAnimation::addComponentTo

// BASIC INIT FOR ANY TYPE OF COMPONENT
CAnimation* result = new CAnimation();
object->addComponent(result);
result->m_owner = object;

// SPECIAL INIT FOR THIS TYPE OF COMPONENT
result->m_animate.push_back(new FTexture(resourcePath));
result->m_currentAnimationIndex = 0; 

return result;

Tại đây ta thấy nhu cầu sử dụng xuất hiện thêm:

  • m_owner: lưu trữ liên kết đến Entity sử dụng Component này.
  • addComponent: phương thức của Entity nhằm đặt Component này vào danh sách Component mà nó sở hữu.
  • m_currentAnimationIndex: thuộc tính của Component lưu trữ số thứ tự của hoạt ảnh đang sử dụng trong m_animate.

CAnimation::CAnimation

m_typeID = ID::COMPONENT::ANIMATION;
m_animate.clear();

Tại đây, ta cần thêm m_typeID để lưu trữ ID của component, nhằm phân biệt Component này với các loại Component khác trong danh sách của Entity.

CAnimation::draw

m_animate.at(m_currentAnimationIndex)->draw(hdc);

draw là phương thức có sẵn trong lớp FTexture nhằm vẽ ảnh đã load lên cửa sổ, tọa độ vẽ mặc định là 50, 50.

Entity::draw

for (CBase* component : m_components)
{
	if (component->getTypeID() == ID::COMPONENT::ANIMATION)
	{
		((CAnimation*)component)->draw(hdc);
		break;
	}
}

Lưu ý đây chỉ là định nghĩa tạm thời của phương thức này, hiện tại phương thức hoạt động bằng cách tìm Component có kiểu CAnimate sau đó ép kiểu trực tiếp để gọi phương thức đặc trưng draw, điều này hoàn toàn không nên. CBP-10 sẽ giải quyết hoàn thiện vấn đề này.

Tại đây, xuất hiện thêm ID::COMPONENTgetTypeID. Bộ ID và phương thức này dùng để đánh dấu từng loại component trong danh sách Component của Entity, cụ thể, độc giả có thể xem trong ví dụ vì vấn đề này khá đơn giản và dễ hiểu.

Entity::update

for (CBase* component : m_components)
	component->update(delta);

Phương thức này đơn giản là gọi phương thức update của từng Component mà nó sở hữu, delta là thời gian của vòng lặp game vừa rồi.

Tổng kết

Sau bài viết này, chúng ta có 1 kết quả như sau khi chạy chương trình:

Kết quả lập trình game CBP.

Đây là định nghĩa của Component đầu tiên, thêm vào đó các thành phần quan trọng như phản ứng, command vẫn chưa có hoặc chưa hoàn chỉnh, gây khó khăn không nhỏ trong việc diễn đạt ý tưởng của hệ thống, qua những bài viết sau, ý tưởng cốt lõi của hệ thống CBP sẽ được thể hiện ngày càng rõ ràng hơn.

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