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 Updateoraz Drawmetody i zarówno zadzwonić do GameScreen„s Updatei Drawmetod, które same przejść przez każdego komponentu i rozmowy Updatei 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ć BackgroundManagerwszystkich różnych środowisk. Po prostu mieć ScrollingBackground, ParallaxBackground, StaticBackground, itd., Które wywodzą się z Backgroundklasy.
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. FrameRateDisplayerJako 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ż BackgroundManagerklasy, ale Backgroundklasę, 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 Enginewszystkich swoich GameScreenklas, aby móc dodawać nowe ekrany nawet w obrębie GameScreenklasy (np. Kiedy użytkownik kliknie przycisk StartGame w twojej klasie MainMenuScreen, możesz przejść do GameplayScreen).
To samo dotyczy Componentklasy: powinna zawierać referencję swojego rodzica GameScreen, aby mieć zarówno dostęp do Engineklasy, jak i jej rodzica, GameScreenaby dodać nowe komponenty (np. Możesz stworzyć klasę związaną z interfejsem HUD o nazwie, DrawableButtonktóra zawiera
DrawableTextkomponent i StaticBackgroundkomponent).
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 Engineklasie (po prostu przechowujesz listę IServices, a inne klasy mogą same dodawać usługi ). np. zachowałbym Camera2Dkomponent 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ć.