Trong lập trình nói chung và lập trình game nói riêng thì việc tối ưu phần cứng luôn được lập trình viên chúng ta quan tâm hàng đầu, đảm bảo người dùng trải nghiệm tốt nhất có thể. Đặc biệt trong bài này mình sẽ giới thiệu các bạn FlyWeight Pattern, một pattern sinh ra để optimize RAM và ứng dụng trong Unity thế nào cho hợp lý?
Vấn đề đặt ra?
Khi trong game của bạn yêu cầu tạo ra một lượng lớn object, mà mỗi object chứa quá nhiều tài nguyên -> Tổ chức không tốt sẽ khiến cho RAM phải cache lại rất nhiều thứ, đây là điều thực sự không tốt, phải được optimize
Ví dụ bạn có một class chứa 3 thuộc tính
public class MyObject { int currentHeal; int maxHealh; float moveSpeed; }
ta sẽ tạo 3 instance của class này, lúc này trên RAM sẽ cache lại như sau
![]() |
| Source: Sqrly Code |
Trong ảnh trên, 3 màu đại diện cho mỗi instance của class đó, vậy thì lưu như vậy có hợp lý không?
Rõ ràng là không, bạn để ý có những thuộc tính hoàn toàn chỉ cần 1 vùng địa chỉ để lưu nó, cụ thể là maxHP, nhưng khi tạo ra từng instance thì mỗi instance sẽ tạo ra 1 copy của class và cache nó vào trong RAM như trong ảnh => cực kì tốn vùng nhớ khi cần khởi tạo một lượng lớn đối tượng
FlyWeight là gì?
FlyWeigh pattern đơn giản chỉ là cách mà ta tối ưu bộ nhớ bằng cách chia sẽ các tài nguyên có thể tái sử dụng lại hoặc dùng chung giữa nhiều object khác nhau.
Đơn giản là vậy, vậy thì để áp dụng FlyWeight ta chỉ cần phải trả lời câu hỏi sau
Thuộc tính nào sẽ dùng chung cho mọi instance?
Từ câu hỏi này, mình rút ra được 2 nguyên tắc cho FlyWeight như sau
- Mỗi instance chỉ nên lưu biến mà giá trị của nó sẽ khác biệt hoặc sẽ thay đổi so với các instance còn lại
- Biến dùng chung cho mỗi instance không nên được sao chép bởi các instance khác
Oke, tiếp tục với ví dụ trên, sau khi ta cache lại các thuộc tính dùng chung như maxHP và movSpeed, thì RAM sẽ có dạng như này
Làm sao để áp dụng FlyWeigh
Khi ta làm việc với các texture hay material thì bạn để ý trong unity đã support chúng ta sẵn FlyWeight build-in, khi ta có thể tái sử dụng 1 material cho nhiều object khác nhau. Nhưng để tự implement FlyWeigh thì ta có các cách sau đây
Const - Static varibles
Yup!, chúng ta sẽ nghĩ đến ngay const và static vì các biến này chỉ được tạo ra duy nhất 1 lần. Tuy nhiên đào sâu hơn một tí, khi dùng const hay static nó thực sự chỉ có lợi khi team bạn không có Game Designer, vì khi dùng các biến này, chúng ta không thể control chúng trong inspector, đổi lại điều này rất tiện dụng khi team bạn toàn Dev vì có thể nhanh trong truy cập vào file script config để sửa lại thông số. Ez game
Nhưng chúng ta sẽ ưu tiên việc chúng ta sẽ hoạt động cùng Game Designer, nếu xài các này thì việc control các stats trong game cực kì khó khắn đối với 1 GD, hầu hết thời gian của họ không phải mở script lên sửa như Dev chúng ta
ScriptableObject
Sức mạnh của ScriptableObject là đây, cực kì mạnh mẽ khi áp dụng FlyWeight pattern, dễ teamwork với GD, mình đã có ghi rõ trong ScriptableObject không chỉ để lưu data
Sự khác nhau giữa FlyWeight và Object Pooling?
Thật sự rất rất nhiều người không thể phân biệt nổi 2 pattern này với nhau, nhưng rất đơn giản khi chúng ta đào sâu vào gốc rễ của 2 khái niệm, ta dễ dàng phân biệt được
- FlyWeight: Giảm tải cho RAM bằng cách tách các data dùng chung sang một object FlyWeight
- Pooling: Giảm thiểu việc tính toán trên CPU khi tránh việc Instantiate và Destroy liên tục. Tuy nhiên sẽ tốn nhiều RAM hơn để cache object
Tóm lại đơn giản thì FlyWeight tối ưu RAM, còn Pooling thì tối ưu CPU vậy thôi, khỏi bàn cải tranh luận gì nữa, kết bài, đi ngủ.
Tổng kết
Đùa thôi, đấy là góc nhìn từ phía mình, nếu có gì thắc mắc hay góp ý cho bài viết của mình, đừng ngại comment cho mình biết nhóe, thansk for reading

