Мгновенное окончание игры со сложными поворотами
Завершение пошаговой игры, допускающей одно действие за ход, довольно тривиально - вы можете просто обновить логическое значение при выполнении различных условий выигрыша или проигрыша и проверять значение логического значения каждый раз, когда вы проходите через ход, чтобы выяснить, когда игра заканчивается.
Однако игра, которую я пишу, включает в себя более сложные повороты, когда игрок совершает несколько действий на одного персонажа с несколькими персонажами, которые могут привести к победе или проигрышу, и несколько обновлений, выполняемых компьютером, которые происходят между поворотами характера, которые могут привести к потере. Очевидно, что необходимо прервать ход, когда достигнуто условие выигрыша.Варианты, о которых я подумал:
-
Просто продолжайте проверять завершение один раз за цикл. Эта опция на самом деле не работает - вы должны были бы закончить все действия для хода, даже если вы выиграли (что может быть даже невозможно), и вам пришлось бы включить специальные обработчики, чтобы убедиться, что одно условие завершения не перезаписывается другим в игре. тот же поворот.
-
Бросьте исключение через стек, пока вы не вернетесь в основной метод, затем поймайте исключение, проанализируйте его и предоставьте сообщения о выигрыше/проигрыше. Невероятно грубая реализация, и не совсем то, для чего существуют исключения.
-
Используя модели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 ответа:
Если я вас правильно понял, вы пытаетесь реализовать какую-то модель состояния для вашей игры. Вместо вызова
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) {} }