Co z silnikiem opartym na komponentach ?
Miałbyś główną klasę o nazwie Engine
, która prowadziłaby listę GameScreens
, która sama posiadałaby listę Components
.
Silnik posiada Update
oraz Draw
metody i zarówno zadzwonić do GameScreen
„s Update
i Draw
metod, które same przejść przez każdego komponentu i rozmowy Update
i Draw
.
Tak przedstawiony, zgadzam się, że brzmi jak kiepski i powtarzalny projekt. Ale uwierz mi, mój kod stał się znacznie czystszy dzięki zastosowaniu podejścia opartego na komponentach niż w przypadku wszystkich moich starych klas menedżerów .
Utrzymanie takiego kodu również jest o wiele prostsze, ponieważ przechodzisz właśnie przez dużą hierarchię klas i nie musisz przeszukiwać BackgroundManager
wszystkich różnych środowisk. Po prostu mieć ScrollingBackground
, ParallaxBackground
, StaticBackground
, itd., Które wywodzą się z Background
klasy.
W końcu zbudujesz całkiem solidny silnik, który możesz ponownie wykorzystać we wszystkich swoich projektach z wieloma często używanymi komponentami i metodami pomocniczymi (np. FrameRateDisplayer
Jako narzędzie do debugowania,Sprite
klasa jako podstawowy duszek z metodami tekstury i rozszerzeń dla wektorów i generowanie liczb losowych).
Nie miałbyś już BackgroundManager
klasy, ale Background
klasę, która sama sobie poradziłaby.
Po rozpoczęciu gry wszystko, co musisz zrobić, to w zasadzie:
// when declaring variables:
Engine engine;
// when initializing:
engine = new Engine();
engine.Initialize();
engine.LoadContent();
engine.AddGameScreen(new MainMenuScreen());
// when updating:
engine.Update();
// when drawing:
engine.Draw();
I to wszystko w przypadku kodu początkowego gry.
Następnie na ekranie menu głównego:
class MainMenuScreen : MenuScreen // where MenuScreen derives from the GameScreen class
{
const int ENEMY_COUNT = 10;
StaticBackground background;
Player player;
List<Enemy> enemies;
public override void Initialize()
{
background = new StaticBackground();
player = new Player();
enemies = new List<Enemy>();
base.AddComponent(background); // defined within the GameScreen class
base.AddComponent(player);
for (int i = 0; i < ENEMY_COUNT; ++i)
{
Enemy newEnemy = new Enemy();
enemies.Add(newEnemy);
base.AddComponent(newEnemy);
}
}
}
Masz ogólny pomysł.
Zachowałbyś również odniesienie do Engine
wszystkich swoich GameScreen
klas, aby móc dodawać nowe ekrany nawet w obrębie GameScreen
klasy (np. Kiedy użytkownik kliknie przycisk StartGame w twojej klasie MainMenuScreen
, możesz przejść do GameplayScreen
).
To samo dotyczy Component
klasy: powinna zawierać referencję swojego rodzica GameScreen
, aby mieć zarówno dostęp do Engine
klasy, jak i jej rodzica, GameScreen
aby dodać nowe komponenty (np. Możesz stworzyć klasę związaną z interfejsem HUD o nazwie, DrawableButton
która zawiera
DrawableText
komponent i StaticBackground
komponent).
Następnie możesz zastosować inne wzorce projektowe, takie jak „wzorzec projektowy usługi” (nie jestem pewien dokładnej nazwy), w którym możesz przechowywać różne przydatne usługi w swojej Engine
klasie (po prostu przechowujesz listę IService
s, a inne klasy mogą same dodawać usługi ). np. zachowałbym Camera2D
komponent w całym moim projekcie jako usługę, aby zastosować jego transformację podczas rysowania innych komponentów. Pozwala to uniknąć konieczności przekazywania go jako parametru wszędzie.
Podsumowując, z pewnością mogą istnieć inne lepsze konstrukcje silnika, ale uznałem, że silnik zaproponowany przez to łącze jest bardzo elegancki, niezwykle łatwy w konserwacji i wielokrotnego użytku. Osobiście poleciłbym przynajmniej spróbować.