Hoạt hình vẫn có thể được phân chia hoàn hảo giữa logic và kết xuất. Trạng thái dữ liệu trừu tượng của hình ảnh động sẽ là thông tin cần thiết cho API đồ họa của bạn để hiển thị hình ảnh động.
Ví dụ, trong các trò chơi 2D, đó có thể là một khu vực hình chữ nhật đánh dấu khu vực hiển thị phần hiện tại của trang sprite của bạn cần được vẽ (khi bạn có một trang bao gồm 30 hình vẽ 80x80 chứa các bước khác nhau của nhân vật nhảy, ngồi xuống, di chuyển, vv). Nó cũng có thể là bất kỳ loại dữ liệu nào bạn không cần để kết xuất, nhưng có thể để tự quản lý trạng thái hoạt hình, như thời gian còn lại cho đến khi bước hoạt hình hiện tại hết hạn hoặc tên của hoạt hình ("đi bộ", "đứng" vv) Tất cả điều đó có thể được đại diện theo bất kỳ cách nào bạn muốn. Đó là phần logic.
Trong phần kết xuất, bạn chỉ cần làm như bình thường, lấy hình chữ nhật đó từ mô hình của bạn và sử dụng trình kết xuất của bạn để thực hiện các cuộc gọi đến API đồ họa.
Trong mã (sử dụng cú pháp C ++ tại đây):
class Sprite //Model
{
private:
Rectangle subrect;
Vector2f position;
//etc.
public:
Rectangle GetSubrect()
{
return subrect;
}
//etc.
};
class AnimatedSprite : public Sprite, public Updatable //arbitrary interface for classes that need to change their state on a regular basis
{
AnimationController animation_controller;
//etc.
public:
void Update()
{
animation_controller.Update(); //Good OOP design ;) It will take control of changing animations in time etc. for you
this.SetSubrect(animation_controller.GetCurrentAnimation().GetRect());
}
//etc.
};
Đó là dữ liệu. Trình kết xuất của bạn sẽ lấy dữ liệu đó và vẽ nó. Vì cả Sprites bình thường và hoạt hình đều được vẽ theo cùng một cách, bạn có thể sử dụng đa hình ở đây!
class Renderer
{
//etc.
public:
void Draw(const Sprite &spr)
{
graphics_api_pointer->Draw(spr.GetAllTheDataThatINeed());
}
};
TMV:
Tôi đã đưa ra một ví dụ khác. Giả sử bạn có một game nhập vai. Ví dụ, mô hình của bạn đại diện cho bản đồ thế giới có thể sẽ cần lưu trữ vị trí của nhân vật trên thế giới dưới dạng tọa độ ô trên bản đồ. Tuy nhiên, khi bạn di chuyển nhân vật, họ đi bộ một vài pixel tại một hình vuông tiếp theo. Bạn có lưu trữ vị trí "giữa các ô" này trong một đối tượng hoạt hình không? Làm thế nào để bạn cập nhật mô hình khi nhân vật cuối cùng đã "đến" tại tọa độ ô tiếp theo trên bản đồ?
Bản đồ thế giới không biết trực tiếp về vị trí người chơi (nó không có Vector2f hoặc thứ gì đó tương tự lưu trữ vị trí người chơi =, thay vào đó, nó có tham chiếu trực tiếp đến chính đối tượng người chơi, từ đó xuất phát từ AnimatedSprite để bạn có thể chuyển nó đến trình kết xuất dễ dàng và lấy tất cả dữ liệu cần thiết từ nó.
Tuy nhiên, nói chung, tilemap của bạn không thể thực hiện mọi thứ - Tôi có một lớp "Bản đồ", đảm nhiệm việc quản lý tất cả các ô và có thể nó cũng phát hiện va chạm giữa các đối tượng mà tôi giao cho nó và các viên gạch trên bản đồ. Sau đó, tôi có một lớp "RPGMap" khác, hoặc tuy nhiên, bạn muốn gọi nó, nó có cả tham chiếu đến tilemap của bạn và tham chiếu đến người chơi và thực hiện các cuộc gọi Update () thực tế cho người chơi của bạn và tới sơ đồ.
Cách bạn muốn cập nhật mô hình khi người chơi di chuyển phụ thuộc vào những gì bạn muốn làm.
Người chơi của bạn có được phép di chuyển giữa các ô độc lập (kiểu Zelda) không? Đơn giản chỉ cần xử lý đầu vào và di chuyển người chơi phù hợp với mọi khung hình. Hay bạn muốn người chơi nhấn "phải" và nhân vật của bạn tự động di chuyển một ô sang bên phải? Hãy để lớp RPGMap của bạn nội suy vị trí người chơi cho đến khi anh ta đến đích và khóa tất cả các thao tác nhập liệu phím di chuyển.
Dù bằng cách nào, nếu bạn muốn làm cho bản thân dễ dàng hơn, tất cả các mô hình của bạn sẽ có các phương thức Update () nếu chúng thực sự cần một số logic để tự cập nhật (thay vì chỉ thay đổi giá trị của các biến) - Bạn không từ bỏ bộ điều khiển trong mô hình MVC theo cách đó, bạn chỉ cần di chuyển mã từ "một bước ở trên" (bộ điều khiển) xuống mô hình và tất cả các bộ điều khiển thực hiện đều gọi phương thức Update () này của mô hình (Bộ điều khiển trong trường hợp của chúng tôi sẽ là Bản đồ RPG). Bạn vẫn có thể dễ dàng trao đổi mã logic - bạn có thể thay đổi trực tiếp mã của lớp hoặc nếu bạn cần hành vi hoàn toàn khác, bạn chỉ có thể xuất phát từ lớp mô hình của mình và chỉ ghi đè phương thức Update ().
Cách tiếp cận đó làm giảm các cuộc gọi phương thức và những thứ tương tự như vậy - vốn là một trong những nhược điểm chính của mẫu MVC thuần túy (cuối cùng bạn gọi Get This () GetThat () rất thường xuyên) - nó làm cho mã dài hơn và dài hơn nhỏ hơn một chút để đọc và cũng chậm hơn - mặc dù điều đó có thể được chăm sóc bởi trình biên dịch của bạn, người tối ưu hóa rất nhiều thứ như thế.