Мгновенное окончание игры со сложными поворотами


Завершение пошаговой игры, допускающей одно действие за ход, довольно тривиально - вы можете просто обновить логическое значение при выполнении различных условий выигрыша или проигрыша и проверять значение логического значения каждый раз, когда вы проходите через ход, чтобы выяснить, когда игра заканчивается.

Однако игра, которую я пишу, включает в себя более сложные повороты, когда игрок совершает несколько действий на одного персонажа с несколькими персонажами, которые могут привести к победе или проигрышу, и несколько обновлений, выполняемых компьютером, которые происходят между поворотами характера, которые могут привести к потере. Очевидно, что необходимо прервать ход, когда достигнуто условие выигрыша.

Варианты, о которых я подумал:

  • Просто продолжайте проверять завершение один раз за цикл. Эта опция на самом деле не работает - вы должны были бы закончить все действия для хода, даже если вы выиграли (что может быть даже невозможно), и вам пришлось бы включить специальные обработчики, чтобы убедиться, что одно условие завершения не перезаписывается другим в игре. тот же поворот.

  • Бросьте исключение через стек, пока вы не вернетесь в основной метод, затем поймайте исключение, проанализируйте его и предоставьте сообщения о выигрыше/проигрыше. Невероятно грубая реализация, и не совсем то, для чего существуют исключения.

  • Используя моделиobserver/listener или обработчики событий, чтобы просто бросить другой вызов метода в стек, вместо того, чтобы программа изящно извлекала себя из цикла игры. Кажется, больше для вставки несколько быстрых строк кода или отправка сообщений в другие потоки, не завершая текущий цикл игры.

  • Помещая игровой цикл в свой собственный поток , завершающийся всякий раз, когда условие выигрыша было достигнуто. Основной метод ожидает в отдельном цикле изменения состояния игры и обрабатывает по мере необходимости. Проблема с этим подходом заключается в том, что кажется (в Java, во всяком случае), что реализация Runnable на самом деле не позволяет остановить запущенный поток из другого места (вы должны были бы вернуться из метода run ()), и даже расширение потока (что не должно быть сделано в любом случае)и вызов этого.прерывание () при выполнении условия на самом деле не останавливает выполнение кода игры. В то время как вы можете опросить флаг прерывания потока, чтобы управлять логикой, это просто дает нам ту же самую проблему снова и снова не работать как прерывание.

Некоторый код:

public static void main(String[] args) {        
    Game game = new Game(2, Difficulty.NOVICE);
    game.run();

    while(game.getGameState() == State.INCOMPLETE){
        //Hold while waiting for game to complete.
    }
}

public class Game extends Thread{

    public void checkState(){
    //Let's presume a win condition was thrown:
        state = State.WON;
        this.interrupt();
    }

    public void randomMethod(){
        //This method might contain some code that triggers a win condition, so we immediately call checkState()
        checkState();
    }

    @Override
    public void run() {
        //Lots of different methods called in a single turn, including for example:
        randomMethod();
    }
}

Я уверен, что есть хорошо известный отраслевой стандарт для выполнения такого рода конечно, но я нигде не нашел его прописанным, и я в недоумении, что бы это могло быть.

Решение: модель состояния, на которую ссылаетсяБальдер , по-видимому, является всего лишь билетом. Основная суть этого состоит в том, чтобы отслеживать состояние данного хода и заставить игровой цикл выполнить одно действие, которое может изменить игру, которая динамически изменяется в зависимости от того, в каком состоянии находится игра в данный момент. Есть много способов сделать это-следуя ссылкам в его ответе демонстрирует их было немного. Вот реализация, которую я использовал:

while(gameState_ == GameState.INCOMPLETE){
    turnState_.update();
    checkWin();
}

public void changeTurnState(TurnState state){
    turnState_.exit();
    turnState_ = state;
    turnState_.enter();
}

public abstract class TurnState{
    private Game game;

    public TurnState(Game game){
        this.game = game;
    }

    public void enter(){
    }

    public void exit(){
    }

    public Game getGame(){
         return game;
    }

    public void update(){
    }
}
С помощью этой установки я могу расширить TurnState, чтобы создать любое состояние, которое я хочу, с тем, что должно произойти во время одного действия этого состояния. Например, рассмотрим состояние для затопления одного или нескольких регионов на доске. Помните, что нам нужно, чтобы каждое изменение в игре происходило само по себе, чтобы мы могли проверить условия выигрыша между ними - поэтому это конкретное состояние отслеживает, сколько регионов мы оставили для наводнения, и переходит к следующему. следующее состояние, как только последняя необходимая область будет затоплена.
public class FloodState extends TurnState {

    private int remainingFloods;

    /**
     * @param game
     */
    public FloodState(Game game) {
        super(game);
        remainingFloods = getGame().getFloodRate();
        ForbiddenIsland.logger.info("Flooding {} tiles", remainingFloods);
    }

    public void update(){
        //Draw and resolve a flood card, then decrement remainingFloods
        getGame().flood();
        remainingFloods--;

        //If no more floods remaining, jump to next state
        if(remainingFloods == 0){
            getGame().changeTurnState(new ActionState(getGame()));
        }
    }
}
2 5

2 ответа:

Если я вас правильно понял, вы пытаетесь реализовать какую-то модель состояния для вашей игры. Вместо вызова while(game.getGameState() == State.INCOMPLETE) во время каждого цикла цикла я рекомендую полностью удалить логику состояния игры из основного цикла и вместо этого иметь абстрактный класс состояния:

public abstract class AbstractGameState{
    protected abstract void update(float delta);
}

Тогда у вас может быть другой абстрактный класс состояния для ваших ходов, который только выполняет обновления, если игра еще не выиграна. В противном случае игра закончится.

public abstract class TurnGameState extends AbstractGameState{
    protected final void updateState(float delta){
        if (isWinConditionSatisfied()) {
            // end the game by setting the final state
            TurnBasedGame.currentState = new EndGameState();
        } else{
            update(delta);
        }
    }
    protected abstract void update(float delta);
    private boolean isWinConditionSatisfied() {
        // somehow check if the game is won
        return false;
    }
}

Каждая часть вашего пошаговая логика может представлять собой один TurnGameState, который будет обновляться из основного цикла игры, и у вас есть дополнительный EndGameState, который вводится после того, как игра выиграна:

public class EndGameState extends AbstractGameState{
    @Override
    protected void updateState(float delta) {
        // this simple implementation just ends the game loop
        TurnBasedGame.gameIsRunning = false;
    }
}

Вот очень упрощенный игровой цикл, который использует это понятие:

public class TurnBasedGame {

    private static boolean gameIsRunning;
    private static AbstractGameState currentState;

    public static void main(String[] args) {
        while (gameIsRunning) {
            // very simplified game loop...

            // somehow handle the delta for the game loop...
            float delta;

            // update the game logic
            doGameUpdates(delta);

            // render game graphics here...
            render();
        }
    }

    private static void doGameUpdates(float delta) {
        currentState.update(delta);
    }
}
Конечно, весь код, который я опубликовал, очень упрощен и просто пытается проиллюстрировать общую концепцию. Например, вы, возможно, захотите иметь класс GameStateManager, который обрабатывает добавление и удаление состояний, и у вас, конечно, будет больше сложный основной игровой цикл и т.д.

Для обзора соответствующих концепций, посмотрите на следующие ресурсы:

У меня нет ни малейшего опыта в разработке игр. Поэтому рассматривайте этот ответ как точку зрения новичков. Возможно, это поможет найти правильный подход. Основная идея заключается в моделировании сложного поворота персонажа классом. Игровой цикл не заботится о простых или сложных поворотах. Следовательно, на каждом шаге цикла он вызывает processTurn() для переадресации игры. Метод processTurn() консультируется с CharacterState, который отслеживает состояние сложного поворота и направляет его на один шаг вперед. Если сложный поворот завершен, он возвращает true к покажите, что вам больше нечего делать. Игровой цикл продолжает оценивать новое состояние игры, которое приводит к GameState. Затем он визуализирует игровой мир, чтобы отразить поворот персонажей. Цикл игры проверяет для каждого шага, закончилась ли игра,и если, завершается.

public class Game implements Runnable {

    private GameWorld gameWorld;
    private CharacterState characterState;

    public Game(int value, Difficulty novice) {}

    @Override public void run() {
        while (gameWorld.getGameState() != GameState.COMPLETE) {
            Turn turn = processTurn();
            turn.apply(gameWorld);
            renderGame();
        }
    }

    /**
     * Processes the next step of a complex turn
     */
    private Turn processTurn() {
        Turn turn = characterState.processTurn();
        if (characterState.isDone()) {
            characterState = nextCharactersTurn();
        }
        return turn;
    }

    /**
     * Computes which character has to go next and returns its turn state beginning with the first
     * step of the complex turn.
     * 
     * @return the next characters complex turn
     */
    private CharacterState nextCharactersTurn() {
        throw new UnsupportedOperationException("not yet implemented");
    }

    /**
     * Renders the game world
     */
    private void renderGame() {
        throw new UnsupportedOperationException("not yet implemented");
    }
}

class GameWorld {

    /**
     * Returns the state of the game derived from the state of the world.
     * 
     * @return
     *      the game state
     */
    public GameState getGameState() {
        throw new UnsupportedOperationException("not yet implemented");
    }
}

/**
 * Tracks the complex state of each character turn
 */
class CharacterState {

    /**
     * Processes one step of a complex turn
     * 
     * @return the next turn
     */
    public Turn processTurn() {
        throw new UnsupportedOperationException("not yet implemented");
    }

    /**
     * @return true, if no more turn ar possible for this complex turn, otherwise false.
     */
    public boolean isDone() {
        throw new UnsupportedOperationException("not yet implemented");
    };
}

/**
 * An sequence of commands to be executed atomically.
 * 
 * Each sequence has to be composed of a minimal set of commands representing a transition from one
 * consistent game state to another consistent game state.
 */
class Turn {

    /**
     * Applies all changes encapsulated by this turn to the game world.
     * 
     * Applying the changes may change the game state as a side effect
     * 
     * @param world
     *      to apply the changes at
     */
    public void apply(GameWorld world) {}
}