Trudno odpowiedzieć na to pytanie, ponieważ każdy ma własne wyobrażenie o tym, jak powinien wyglądać system komponentu encji. Najlepsze, co mogę zrobić, to podzielić się z Tobą niektórymi rzeczami, które uważam za najbardziej przydatne dla mnie.
Jednostka
Podchodzę do ECS klasami tłuszczowymi, prawdopodobnie dlatego, że uważam, że ekstremalne metody programowania są wysoce nieefektywne (pod względem produktywności człowieka). W tym celu byt jest dla mnie abstrakcyjną klasą, którą odziedziczą bardziej wyspecjalizowane klasy. Jednostka ma wiele wirtualnych właściwości i prostą flagę, która mówi mi, czy ta jednostka powinna istnieć. W związku z tym pytanie dotyczące systemu renderowania Entity
wygląda tak:
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; }
}
składniki
Składniki są „głupie”, ponieważ nic nie robią ani nic nie wiedzą . Nie mają żadnych odniesień do innych komponentów i zazwyczaj nie mają żadnych funkcji (pracuję w języku C #, więc używam właściwości do obsługi funkcji pobierających / ustawiających - jeśli mają funkcje, opierają się na odzyskiwaniu przechowywanych danych).
Systemy
Systemy są mniej „głupie”, ale wciąż są głupimi automatami. Nie mają one kontekstu dla całego systemu, nie mają odniesień do innych systemów i nie przechowują żadnych danych poza kilkoma buforami, które mogą być potrzebne do ich indywidualnego przetwarzania. W zależności od systemu może mieć specjalistyczną metodę Update
lub Draw
metodę, aw niektórych przypadkach obie te metody.
Interfejsy
Interfejsy są kluczową strukturą w moim systemie. Służą do określania, co System
może przetwarzać, a co Entity
potrafi. Interfejsami istotnymi do renderowania są: IRenderable
i IAnimatable
.
Interfejsy po prostu informują system, które komponenty są dostępne. Na przykład system renderujący musi znać obwiednię elementu i rysowany obraz. W moim przypadku byłoby to SpatialComponent
i ImageComponent
. Wygląda to tak:
public interface IRenderable {
SpatialComponent Component { get; }
ImageComponent Image { get; }
}
RenderingSystem
Jak więc system renderujący rysuje byt? To jest naprawdę dość proste, więc pokażę ci tylko uproszczoną klasę, aby dać ci pomysł:
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);
}
}
}
Patrząc na klasę, system renderowania nawet nie wie, co to Entity
jest. Wszystko, o czym wie IRenderable
, to po prostu podano listę do narysowania.
Jak to wszystko działa
Pomoże to również zrozumieć, w jaki sposób tworzę nowe obiekty gry i jak je karmię do systemów.
Tworzenie jednostek
Wszystkie obiekty gry dziedziczą od Entity oraz wszelkie odpowiednie interfejsy opisujące możliwości tego obiektu gry. Prawie wszystko, co jest animowane na ekranie, wygląda następująco:
public class MyAnimatedWidget : Entity, IRenderable, IAnimatable {}
Karmienie systemów
Trzymam listę wszystkich bytów, które istnieją w świecie gry, na jednej liście o nazwie List<Entity> gameObjects
. Następnie każdą ramkę przeglądam tę listę i kopiuję odwołania do obiektów do większej liczby list na podstawie typu interfejsu, takiego jak List<IRenderable> renderableObjects
i List<IAnimatable> animatableObjects
. W ten sposób, jeśli różne systemy muszą przetwarzać ten sam byt, mogą to zrobić. Następnie po prostu przekazuję te listy każdemu z systemów Update
lub Draw
metod i pozwalam systemom wykonywać swoją pracę.
Animacja
Możesz być ciekawy, jak działa system animacji. W moim przypadku możesz zobaczyć interfejs IAnimatable:
public interface IAnimatable {
public AnimationComponent Animation { get; }
public ImageComponent Image { get; set; }
}
Kluczową rzeczą, na którą należy zwrócić uwagę, jest to, że ImageComponent
aspekt IAnimatable
interfejsu nie jest tylko do odczytu; ma setera .
Jak można się domyślać, komponent animacji po prostu przechowuje dane dotyczące animacji; lista ramek (które są składnikami obrazu), bieżąca ramka, liczba ramek na sekundę do narysowania, czas, jaki upłynął od przyrostu ostatniej klatki, oraz inne opcje.
System animacji korzysta z systemu renderowania i relacji komponentu obrazu. Po prostu zmienia komponent obrazu elementu, zwiększając klatkę animacji. W ten sposób animacja jest renderowana pośrednio przez system renderowania.