Đây là một câu hỏi khó trả lời bởi vì mọi người đều có ý tưởng riêng về cách cấu trúc một hệ thống thành phần thực thể. Điều tốt nhất tôi có thể làm là chia sẻ với bạn một số điều tôi thấy hữu ích nhất đối với tôi.
Thực thể
Tôi áp dụng cách tiếp cận lớp chất béo vào ECS, có lẽ bởi vì tôi thấy các phương pháp lập trình cực đoan rất kém hiệu quả (về năng suất của con người). Cuối cùng, một thực thể đối với tôi là một lớp trừu tượng được kế thừa bởi các lớp chuyên biệt hơn. Thực thể có một số thuộc tính ảo và một cờ đơn giản cho tôi biết thực thể này có tồn tại hay không. Vì vậy, liên quan đến câu hỏi của bạn về một hệ thống kết xuất, đây là giao Entity
diện:
public abstract class Entity {
public bool IsAlive = true;
public virtual SpatialComponent Spatial { get; set; }
public virtual ImageComponent Image { get; set; }
public virtual AnimationComponent Animation { get; set; }
public virtual InputComponent Input { get; set; }
}
Các thành phần
Các thành phần "ngu ngốc" ở chỗ chúng không làm hoặc không biết gì. Chúng không có tham chiếu đến các thành phần khác và chúng thường không có chức năng (Tôi làm việc trong C #, vì vậy tôi sử dụng các thuộc tính để xử lý getters / setters - nếu chúng có chức năng, chúng dựa trên việc truy xuất dữ liệu mà chúng giữ).
Hệ thống
Các hệ thống ít "ngu ngốc" hơn, nhưng vẫn là những máy tự động câm. Chúng không có ngữ cảnh của toàn bộ hệ thống, không có tham chiếu đến các hệ thống khác và không có dữ liệu ngoại trừ một vài bộ đệm mà chúng có thể cần thực hiện xử lý riêng lẻ. Tùy thuộc vào hệ thống, nó có thể có một chuyên ngành Update
, hoặc Draw
phương pháp, hoặc trong một số trường hợp, cả hai.
Giao diện
Giao diện là một cấu trúc quan trọng trong hệ thống của tôi. Chúng được sử dụng để xác định những gì System
có thể xử lý và những gì Entity
có khả năng. Các Giao diện có liên quan để kết xuất là: IRenderable
và IAnimatable
.
Các giao diện chỉ đơn giản cho hệ thống biết các thành phần có sẵn. Ví dụ, hệ thống kết xuất cần biết hộp giới hạn của thực thể và hình ảnh cần vẽ. Trong trường hợp của tôi, đó sẽ là SpatialComponent
và ImageComponent
. Vì vậy, nó trông như thế này:
public interface IRenderable {
SpatialComponent Component { get; }
ImageComponent Image { get; }
}
Hệ thống kết xuất
Vậy làm thế nào để hệ thống kết xuất vẽ một thực thể? Nó thực sự khá đơn giản, vì vậy tôi sẽ chỉ cho bạn thấy lớp bị loại bỏ để cho bạn một ý tưởng:
public class RenderSystem {
private SpriteBatch batch;
public RenderSystem(SpriteBatch batch) {
this.batch = batch;
}
public void Draw(List<IRenderable> list) {
foreach(IRenderable obj in list) {
this.batch.draw(
obj.Image.Texture,
obj.Spatial.Position,
obj.Image.Source,
Color.White);
}
}
}
Nhìn vào lớp, hệ thống kết xuất thậm chí không biết đó là gì Entity
. Tất cả những gì nó biết là IRenderable
và nó chỉ đơn giản là đưa ra một danh sách chúng để vẽ.
Tất cả hoạt động như thế nào
Nó cũng có thể giúp tôi hiểu cách tôi tạo các đối tượng trò chơi mới và cách tôi cung cấp chúng cho các hệ thống.
Tạo các thực thể
Tất cả các đối tượng trò chơi được kế thừa từ Thực thể và bất kỳ giao diện áp dụng nào mô tả những gì đối tượng trò chơi đó có thể làm. Tất cả mọi thứ hoạt hình trên màn hình đều trông như thế này:
public class MyAnimatedWidget : Entity, IRenderable, IAnimatable {}
Nuôi dưỡng hệ thống
Tôi giữ một danh sách tất cả các thực thể tồn tại trong thế giới trò chơi trong một danh sách duy nhất được gọi List<Entity> gameObjects
. Mỗi khung hình, sau đó tôi chọn lọc danh sách đó và sao chép các tham chiếu đối tượng vào nhiều danh sách hơn dựa trên loại giao diện, chẳng hạn như List<IRenderable> renderableObjects
, và List<IAnimatable> animatableObjects
. Bằng cách này, nếu các hệ thống khác nhau cần xử lý cùng một thực thể, chúng có thể. Sau đó, tôi chỉ cần đưa các danh sách đó cho từng hệ thống Update
hoặc Draw
phương thức và để các hệ thống thực hiện công việc của chúng.
Hoạt hình
Bạn có thể tò mò làm thế nào hệ thống hoạt hình. Trong trường hợp của tôi, bạn có thể muốn xem giao diện IAnimizable:
public interface IAnimatable {
public AnimationComponent Animation { get; }
public ImageComponent Image { get; set; }
}
Điều quan trọng cần chú ý ở đây là ImageComponent
khía cạnh của IAnimatable
giao diện không chỉ đọc; nó có một setter .
Như bạn có thể đoán, thành phần hoạt hình chỉ chứa dữ liệu về hoạt hình; một danh sách các khung (là các thành phần hình ảnh), khung hiện tại, số khung hình mỗi giây sẽ được vẽ, thời gian trôi qua kể từ lần tăng khung hình cuối cùng và các tùy chọn khác.
Hệ thống hoạt hình tận dụng hệ thống kết xuất và mối quan hệ thành phần hình ảnh. Nó chỉ đơn giản là thay đổi thành phần hình ảnh của thực thể khi nó tăng khung hình của hình ảnh động. Bằng cách đó, hình ảnh động được kết xuất gián tiếp bởi hệ thống kết xuất.