Jak ustrukturyzować stany gry w systemie opartym na jednostce / komponencie


11

Tworzę grę zaprojektowaną według paradygmatu encja-komponent, który wykorzystuje systemy do komunikacji między komponentami, jak wyjaśniono tutaj . Osiągnąłem punkt w moim rozwoju, że muszę dodawać stany gry (takie jak pauza, gra, rozpoczęcie poziomu, rozpoczęcie rundy, zakończenie gry itp.), Ale nie jestem pewien, jak to zrobić w moim środowisku. Przejrzałem ten przykład kodu dotyczący stanów gry, do którego wszyscy się odnoszą, ale nie sądzę, żeby pasował do mojego frameworka. Wygląda na to, że każdy stan obsługuje własny rysunek i aktualizację. Mój framework ma SystemManager, który obsługuje wszystkie aktualizacje za pomocą systemów. Na przykład oto moja klasa RenderingSystem:

public class RenderingSystem extends GameSystem {

    private GameView gameView_;

    /**
     * Constructor
     * Creates a new RenderingSystem.
     * @param gameManager The game manager. Used to get the game components.
     */
    public RenderingSystem(GameManager gameManager) {
        super(gameManager);
    }

    /**
     * Method: registerGameView
     * Registers gameView into the RenderingSystem.
     * @param gameView The game view registered.
     */
    public void registerGameView(GameView gameView) {
        gameView_ = gameView;
    }

    /**
     * Method: triggerRender
     * Adds a repaint call to the event queue for the dirty rectangle.
     */
    public void triggerRender() {
        Rectangle dirtyRect = new Rectangle();

        for (GameObject object : getRenderableObjects()) {
            GraphicsComponent graphicsComponent =
                    object.getComponent(GraphicsComponent.class);
            dirtyRect.add(graphicsComponent.getDirtyRect());
        }

        gameView_.repaint(dirtyRect);
    }

    /**
     * Method: renderGameView
     * Renders the game objects onto the game view.
     * @param g The graphics object that draws the game objects.
     */
    public void renderGameView(Graphics g) {
        for (GameObject object : getRenderableObjects()) {
            GraphicsComponent graphicsComponent =
                    object.getComponent(GraphicsComponent.class);
            if (!graphicsComponent.isVisible()) continue;

            GraphicsComponent.Shape shape = graphicsComponent.getShape();
            BoundsComponent boundsComponent =
                    object.getComponent(BoundsComponent.class);
            Rectangle bounds = boundsComponent.getBounds();

            g.setColor(graphicsComponent.getColor());

            if (shape == GraphicsComponent.Shape.RECTANGULAR) {
                g.fill3DRect(bounds.x, bounds.y, bounds.width, bounds.height,
                        true);
            } else if (shape == GraphicsComponent.Shape.CIRCULAR) {
                g.fillOval(bounds.x, bounds.y, bounds.width, bounds.height);
            }
        }
    }

    /**
     * Method: getRenderableObjects
     * @return The renderable game objects.
     */
    private HashSet<GameObject> getRenderableObjects() {
        return gameManager.getGameObjectManager().getRelevantObjects(
                getClass());
    }

}

Również wszystkie aktualizacje w mojej grze są oparte na zdarzeniach. Nie mam takiej pętli, która po prostu aktualizuje wszystko w tym samym czasie.

Podoba mi się mój framework, ponieważ ułatwia dodawanie nowych GameObjects, ale nie ma problemów z niektórymi projektami opartymi na komponentach podczas komunikacji między komponentami. Nie chciałbym tego wyrzucać, żeby zrobić sobie przerwę w pracy. Czy istnieje sposób dodawania stanów gry do mojej gry bez usuwania projektu elementu-elementu? Czy przykład stanu gry faktycznie pasuje do mojego frameworka i po prostu czegoś brakuje?

EDYCJA: Być może nie dość dobrze wyjaśniłem swoje ramy. Moje komponenty to tylko dane. Gdybym pisał w C ++, prawdopodobnie byłyby to struktury. Oto przykład jednego z nich:

public class BoundsComponent implements GameComponent {

    /**
     * The position of the game object.
     */
    private Point pos_;

    /**
     * The size of the game object.
     */
    private Dimension size_;

    /**
     * Constructor
     * Creates a new BoundsComponent for a game object with initial position
     * initialPos and initial size initialSize. The position and size combine
     * to make up the bounds.
     * @param initialPos The initial position of the game object.
     * @param initialSize The initial size of the game object.
     */
    public BoundsComponent(Point initialPos, Dimension initialSize) {
        pos_ = initialPos;
        size_ = initialSize;
    }

    /**
     * Method: getBounds
     * @return The bounds of the game object.
     */
    public Rectangle getBounds() {
        return new Rectangle(pos_, size_);
    }

    /**
     * Method: setPos
     * Sets the position of the game object to newPos.
     * @param newPos The value to which the position of the game object is
     * set.
     */
    public void setPos(Point newPos) {
        pos_ = newPos;
    }

}

Moje komponenty nie komunikują się ze sobą. Systemy obsługują komunikację między komponentami. Moje systemy również nie komunikują się ze sobą. Mają osobną funkcjonalność i można je łatwo przechowywać osobno. MovementSystem nie musi wiedzieć, co renderuje RenderingSystem, aby poprawnie przenieść obiekty gry; wystarczy ustawić właściwe wartości na komponentach, aby podczas renderowania obiektów gry RenderingSystem miał dokładne dane.

Stan gry nie może być systemem, ponieważ musi współdziałać z systemami, a nie z komponentami. Nie ustawia danych; określa, które funkcje należy wywołać.

GameStateComponent nie miałby sensu, ponieważ wszystkie obiekty gry mają ten sam stan gry. Elementy składają się na obiekty i każdy z nich jest inny dla każdego innego obiektu. Na przykład obiekty gry nie mogą mieć takich samych granic. Mogą mieć nakładające się granice, ale jeśli współużytkują BoundsComponent, to tak naprawdę są tym samym obiektem. Mam nadzieję, że to wyjaśnienie sprawia, że ​​moje ramy są mniej mylące.

Odpowiedzi:


4

Przyznaję, że nie przeczytałem opublikowanego linku. Po dokonaniu edycji i przeczytaniu podanego linku moja pozycja uległa zmianie. Poniżej to odzwierciedla.


Nie wiem, czy trzeba martwić się stanami gry w tradycyjnym tego słowa znaczeniu. Biorąc pod uwagę twoje podejście do rozwoju, każdy system jest tak specyficzny, że w rzeczywistości jest to zarządzanie stanem gry.

W systemie encji komponenty to tylko dane, prawda? Taki jest stan. W najprostszej formie jest to tylko flaga. Jeśli zbudujesz stany w komponenty i pozwolisz, aby twoje systemy wykorzystały dane tych komponentów i zareagowały na zawarte w nich stany (flagi), będziesz budował zarządzanie stanami w każdym systemie osobno.

Wygląda na to, że systemy zarządzania, takie jak przykład AppHub, nie odnoszą się zbyt dobrze do paradygmatu programistycznego. Wydaje się, że stworzenie super-systemu, który obudowuje inne systemy, nie pozwala na oddzielenie logiki od danych.

To może pomóc ci zrozumieć, co mam na myśli, mówiąc o tym, że nie musisz jawnie obsługiwać stanów gry:

http://paulgestwicki.blogspot.com/2012/03/components-and-systems-of-morgans-raid.html


Proszę zobaczyć moją edycję. Przepraszam, jeśli się myliłem.
Eva

Zaktualizowano, aby odzwierciedlić nowe odkrycia i zmiany. Mam nadzieję, że ktoś z większym doświadczeniem w budowaniu systemów bytów wkroczy, ponieważ nie jest to obszar, w którym mam duże doświadczenie.
Cypher

Co powiesz na usuwanie i dodawanie systemów, gdy zmienia się stan gry? Na przykład, gdy zatrzymasz grę, być może Twój MovementSystem lub CollisionSystem nie są potrzebne, ale nadal chcesz, aby Twój RenderSystem rysował rzeczy na ekranie. Czy aktywne systemy mogą reprezentować stan gry?
argenkiwi

0

Stan to wartość, która dotyczy obiektu. Stan gry, jak sama nazwa wskazuje, byłby stanem obiektu „Gra”. Ten obiekt gry - lub, co bardziej prawdopodobne, określony komponent na nim - śledziłby bieżący stan i tworzył lub niszczył wszelkie obiekty niezbędne do ułatwienia bieżącego stanu. Ponieważ komponenty są tylko danymi, do obsługi tego potrzebny będzie nowy system, nawet jeśli może istnieć tylko jedna instancja powiązanego komponentu.

Trudno jest wypowiedzieć się na temat tego, jak zaimplementować pauzę, gdy nie jest jasne, jak zaimplementować aktualizację. Proces emitujący zdarzenia aktualizacji może tego nie robić, jeśli obiekt gry mówi, że gra jest wstrzymana. Sposób, w jaki obiekt gry komunikuje się z procesem aktualizacji, zależy od ciebie; być może twoje getRelevantObjectswywołanie powinno pozwolić aktualizatorowi znaleźć obiekt gry lub odwrotnie.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.