Я не первый раз задаю этот вопрос, но теперь я гораздо лучше разбираюсь в проблеме. Итак, я хочу сделать настольную игру. В нем есть стопка плиток, и за каждый ход игрок берет одну плитку из стопки и добавляет ее на доску, затем наступает очередь следующего игрока и так далее.
Плитки добавляются с помощью мыши и отображаются в области сетки, которая находится внутри области прокрутки, которая находится внутри области границ (потому что у нас будет больше материала в боковых и, возможно, в верхней части).
Давайте проверим код:
public final class GUI extends Application {
private Game _game;
private GridPane _visualBoard;
private Stage _primaryStage;
@Override
public void start(final Stage primaryStage) {
//stuff
play();
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
public void addTile(myTile r) {
double x, y;
myVisualTile vT = new myVisualTile(r)
_visualBoard.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent e) {
double mouseX = e.getSceneX();
double mouseY = e.getSceneY();
_visualBoard.add(vT, x, y);
}
});
}
public void play() {
Player j;
int i = 0;
while (!_tileStack.empty()) {
if (i == _players.size()) i = 0;
j = _players.get(i);
i++;
turn(j);
}
System.out.println("Final");
}
private void turn(Player j) {
myTile r=_tileStack.poll();
addTile(r);
}
Но на самом деле это не работает так, как должно. Почему? Потому что КАК-ТО я должен «слушать», чтобы произошло событие addTile(), поэтому я прочитал документацию об этом, но не получил четкого ответа.
Кстати, позвольте мне отметить, что я знаю, что плитки не будут добавлены в правильную строку сетки, столбец, поскольку я не знаю, как преобразовать пиксели в правильную позицию сетки, но это не главный вопрос.
Должен ли я использовать EventListener или EventFilter? В чем разница между ними? Как их использовать?




Во-первых, я предлагаю прочитать этот учебник, чтобы узнать больше об обработке событий в JavaFX.
Судя по этому вопросу и двум другим, концептуальная проблема, с которой вы столкнулись, заключается в том, как получить «петлю без петли».
Идея в том, что у вас должен быть класс, представляющий состояние игры. «Состояние» включает в себя такие вещи, как чей сейчас ход, какие плитки были размещены, какие плитки все еще доступны, какие ходы действительны, сколько очков у каждого человека, была ли игра выиграна и так далее.
public class Game {
private final Deque<Tile> tileStack = ...;
private final Tile[][] board = ...; // where "null" would be open (might want a better data structure)
private final List<Player> players = ...;
private final Map<Player, Integer> points = ...;
private int currentPlayerIndex;
private Tile currentTile;
// getters/setters (if necessary)...
}
Я полагаю, что при использовании шаблона проектирования, такого как MVVM, выше будет ViewModel. И поскольку вы используете JavaFX, вы можете захотеть представить эти свойства как фактические экземпляры JavaFX [ReadOnly]Property. Таким образом, вы можете использовать привязку данных между View и ViewModel. Такие вещи, как Player и Tile, будут объектами модели.
Затем вы добавите в этот класс методы для управления состоянием игры.
public void tryPlaceTile(int row, int column) {
if (isValidMove(row, column) {
addPlacedTile(row, column); // might update points
if (tileStack.isEmpty()) {
currentTile = null;
endGame(); // stops the game, determines winner
} else {
currentTile = tileStack.pop();
currentPlayerIndex = currentPlayerIndex == players.size() - 1 ? 0 : currentPlayerIndex + 1;
updateView(); // likely not needed with data binding
}
} else {
notifyUserOfInvalidMove();
}
// let method simply return, it will be called again when
// the (next) player clicks in the appropriate place of
// the view
}
Теперь два приведенных выше фрагмента кода чрезвычайно грубы (особенно потому, что я не очень хорошо знаком с игрой, которую вы кодируете). Такие вещи, как updateView(), не обязательно нужны при использовании привязки данных. isValidMove(int,int) и notifyUserOfInvalidMove() также не обязательно нужны, если представление (на основе состояния игры) не позволяет взаимодействовать с недопустимыми локациями. Но дело в том, что когда пользователь нажимает на местоположение, вы вызываете этот метод. Этот метод обрабатывает ход игрока и обновляет состояние игры. Изменение состояния должно влиять на представление, чтобы визуальные элементы точно отображали состояние1. Как только метод вернется, вы вернетесь к «ожиданию» следующего взаимодействия с пользователем.
node.setOnMouseClicked(event -> {
gameInstance.tryPlaceTile(/* needed information */);
// anything else that needs doing
});
1. If you end up using background threads, remember to only ever update the view (directly or indirectly) on the JavaFX Application Thread.
Я также рекомендую прочитать статьи и учебные пособия о:
И их вариации. Эти архитектуры часто (не всегда) упрощают реализацию программного обеспечения, такого как приложения с графическим интерфейсом. Определите, какой из них лучше всего соответствует вашим потребностям (если таковые имеются), и используйте его.
Извините, но я не вижу никакой разницы между моим кодом и вашими примерами. Я имею в виду; в addTile() у меня есть node.setOnMouseClicked(event -> {, а затем в методе обработки у меня есть обновление игры. Но основной цикл не ждет выполнения действия.
Не иметь петли. Вызываемый обработчик событий будет эквивалентен итерации цикла один.
Затем мне нужен способ узнать, когда игра заканчивается, и в теории это зависит от оставшихся плиток (когда tileStack пуст, игра окончена. Кроме того, мне нужен способ передать ход, и обе вещи выполняются в петля.
Да! Попробую так.
Вот и все! Проблема была в том, что я усвоил урок, а не в приложении javaFX. Теперь нужно знать, как преобразовать щелчок мыши в положение сетки.
Пожалуйста, опубликуйте минимальный воспроизводимый пример