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