Giới thiệu
Sau bài viết trước, component cơ sở CBase
và Entity
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 trongm_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::COMPONENT
và getTypeID
. 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:
Đâ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
- CBP-0: Giới Thiệu về Component Base Development
- CBP-1: Tổng Quan về Project Ví Dụ
- CBP-2: Khai Báo Component Cơ Sở và Entity Cơ Bản
- CBP-3: Khai Báo Component Cơ Bản, Tích Hợp Component vào Entity
- CBP-4: Giao Tiếp với Entity – Hệ Thống Chỉ Lệnh
- CBP-5: Truyền và Lấy Thông Số Từ Component
- CBP-6: Hàng Đợi Chỉ Lệnh
- CBP-7: Cài Đặt Phản Ứng cho Entity
- CBP-8: Component Điều Khiển và AI – Component Ra Lệnh
- CBP-9: Bộ Khởi Tạo Entity – Factory và Hệ Thống ID
- CBP-10: Hệ Thống Quản Lý Tập Trung Các Component Đặc Thù