Como estruturar estados de jogos em um sistema baseado em entidade / componente

11

Estou criando um jogo projetado com o paradigma de entidade-componente que usa sistemas para se comunicar entre componentes, conforme explicado aqui . Cheguei ao ponto do meu desenvolvimento em que preciso adicionar estados de jogo (como pausa, execução, início de nível, início da rodada, final do jogo etc.), mas não tenho certeza de como fazê-lo com minha estrutura. Eu olhei para este exemplo de código nos estados dos jogos aos quais todos parecem fazer referência, mas não acho que isso se encaixa na minha estrutura. Parece que cada estado lida com seu próprio desenho e atualização. Minha estrutura possui um SystemManager que lida com toda a atualização usando sistemas. Por exemplo, aqui está minha classe 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());
    }

}

Além disso, toda a atualização no meu jogo é orientada a eventos. Eu não tenho um loop como o deles que simplesmente atualiza tudo ao mesmo tempo.

Gosto da minha estrutura porque facilita a adição de novos GameObjects, mas não apresenta os problemas encontrados por alguns designs baseados em componentes ao se comunicar entre componentes. Eu odiaria jogá-lo apenas para fazer uma pausa no trabalho. Existe uma maneira de adicionar estados de jogo ao meu jogo sem remover o design do componente da entidade? O exemplo do estado do jogo realmente se encaixa na minha estrutura, e só estou perdendo alguma coisa?

Edição: Eu não poderia ter explicado o meu quadro o suficiente. Meus componentes são apenas dados. Se eu estivesse codificando em C ++, provavelmente seriam estruturas. Aqui está um exemplo de um:

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;
    }

}

Meus componentes não se comunicam. Os sistemas lidam com a comunicação entre componentes. Meus sistemas também não se comunicam. Eles têm funcionalidade separada e podem ser facilmente mantidos separados. O MovementSystem não precisa saber o que o RenderingSystem está renderizando para mover os objetos do jogo corretamente; ele só precisa definir os valores corretos nos componentes, para que, quando o RenderingSystem renderize os objetos do jogo, ele tenha dados precisos.

O estado do jogo não pode ser um sistema, porque ele precisa interagir com os sistemas e não com os componentes. Não está definindo dados; está determinando quais funções precisam ser chamadas.

Um GameStateComponent não faria sentido porque todos os objetos do jogo compartilham um estado de jogo. Componentes são o que compõem os objetos e cada um é diferente para cada objeto diferente. Por exemplo, os objetos do jogo não podem ter os mesmos limites. Eles podem ter limites sobrepostos, mas se compartilharem um BoundsComponent, serão realmente o mesmo objeto. Felizmente, essa explicação torna minha estrutura menos confusa.

Eva
fonte

Respostas:

4

Admito que não li o link que você postou. Após a sua edição e a leitura do link fornecido, minha posição mudou. O abaixo reflete isso.


Não sei se você precisa se preocupar com os estados dos jogos no sentido tradicional. Considerando sua abordagem ao desenvolvimento, cada sistema é tão específico que, na verdade, é o gerenciamento de estado do jogo.

Em um sistema de entidades, os componentes são apenas dados, certo? Então é um estado. Na sua forma mais simples, é apenas uma bandeira. Se você criar seus estados em componentes e permitir que seus sistemas consumam os dados desses componentes e reagir aos estados (sinalizadores) dentro deles, você estará incorporando seu gerenciamento de estado em cada sistema.

Parece que sistemas de gerenciamento como o exemplo do AppHub não se aplicam muito bem ao seu paradigma de desenvolvimento. Criar um super-sistema que encapsule outros sistemas parece derrotar o objetivo de separar a lógica dos dados.

Isso pode ajudá-lo a entender o que quero dizer sobre não ter que lidar explicitamente com os estados do jogo:

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

Cifra
fonte
Por favor, veja minha edição. Desculpe se eu estava confuso.
Eva
Atualizado para refletir novas descobertas e suas edições. Espero que alguém com mais experiência na criação de sistemas de entidades participe, pois essa não é uma área em que tenho muita experiência.
Cypher
Que tal remover e adicionar sistemas quando o estado do jogo muda? Por exemplo, quando você pausa o jogo, talvez o seu MovementSystem ou CollisionSystem não sejam necessários, mas você ainda deseja que o RenderSystem desenhe coisas na tela. Os sistemas ativos poderiam representar um estado de jogo?
Argenkiwi 8/09/14
0

Estado é um valor que se aplica a um objeto. O estado do jogo, como o nome sugere, seria o estado de um objeto 'Jogo'. Esse objeto Jogo - ou, mais provavelmente, um componente específico - rastreará o estado atual e criará ou destruirá os objetos necessários para facilitar o estado atual. Como seus componentes são apenas dados, você precisará de um novo sistema para lidar com isso, mesmo que possa haver apenas uma instância de seu componente associado.

É difícil comentar sobre como você implementaria a pausa quando não está claro como você implementa a atualização. O processo que emite eventos de atualização pode optar por não fazê-lo, se o objeto do jogo indicar que o jogo está em pausa. A forma como o objeto do jogo se comunica com o processo de atualização depende de você; talvez sua getRelevantObjectsligação permita que o atualizador encontre o objeto Jogo ou vice-versa.

Kylotan
fonte