我正在编写Checkers游戏,正如标题所暗示的那样,是人类玩家和AI之间的游戏。人类玩家的每个检查器都设置了拖动检测。 OnDragDetected收集检查器当前位置的坐标。 OnDragDropped收集玩家想要放弃检查器的位置的坐标。一旦完成此操作,就会发生一系列其他功能以确保移动有效。如果移动有效,则轮到AI。在人工智能移动之后,它就是人类玩家,等等。
我遇到的问题是,我必须以某种方式延迟播放器拖动检查器后发生的所有功能,因为我需要首先收集有关坐标的信息。
我尝试过很多东西但没有成功。我非常感谢任何指针,因为这是我使用JavaFX的第一个程序。
谢谢。
答案 0 :(得分:0)
我不确定我知道你的意思"延迟"这里。您只需要使用事件处理程序管理所有内容,并跟踪文件的移动位置和移动位置。然后在用户尝试完成移动时检查有效性(通常通过释放拖动)。
这是一个非常基本的轮廓(一个玩家只是移动棋子)。在现实生活中,方形状态(occupied
)会更复杂(两个玩家等),移动验证也是如此。但它应该给你一个想法。
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.css.PseudoClass;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class CheckersExample extends Application {
private static final int BOARD_SIZE = 10 ;
private static final int SQUARE_SIZE = 60 ;
private static final int PIECE_RADIUS = 25 ;
@Override
public void start(Stage primaryStage) {
// Use a GridPane for the board:
GridPane board = new GridPane();
// track where move is made from and to
ObjectProperty<Square> moveFrom = new SimpleObjectProperty<>();
ObjectProperty<Square> moveTo = new SimpleObjectProperty<>();
// Create the squares and add views of them to the grid pane:
for (int y=0; y<BOARD_SIZE; y++) {
for (int x=0; x<BOARD_SIZE; x++) {
Square square = new Square(x, y);
board.add(square.getView(), x, y);
// alternate squares in last 3 rows are occupied:
if (y >= BOARD_SIZE-3 && (x + y) % 2 == 1) {
square.setOccupied(true);
}
// add event handlers for dragging to enable moving pieces:
setUpDraggingForMove(square, moveFrom, moveTo);
}
}
BorderPane root = new BorderPane(board);
Scene scene = new Scene(root);
scene.getStylesheets().add("checkers.css");
primaryStage.setScene(scene);
primaryStage.show();
}
private void setUpDraggingForMove(Square square, ObjectProperty<Square> moveFrom,
ObjectProperty<Square> moveTo) {
// CSS pseudoclass for highlighting squares before moving to them:
PseudoClass highlight = PseudoClass.getPseudoClass("highlight");
// when starting drag, if square has a piece, set the moveFrom:
square.getView().setOnDragDetected(event -> {
if (square.isOccupied()) {
moveFrom.set(square) ;
// this enables drag events to other nodes (squares):
square.getView().startFullDrag();
}
});
// if dragging onto a square that's a valid move, highlight it and set the moveTo:
square.getView().setOnMouseDragEntered(event -> {
if (moveValid(moveFrom.get(), square)) {
square.getView().pseudoClassStateChanged(highlight, true);
moveTo.set(square);
}
});
// when dragging off a square, un-highlight it and unset the moveTo:
square.getView().setOnMouseDragExited(event -> {
square.getView().pseudoClassStateChanged(highlight, false);
moveTo.set(null);
});
// when releasing, if the move is valid, set the moveFrom to be unoccupied and the
// moveTo to be occupied:
square.getView().setOnMouseReleased(event -> {
if (moveValid(moveFrom.get(), moveTo.get())) {
moveFrom.get().setOccupied(false);
moveTo.get().setOccupied(true);
}
moveFrom.set(null);
moveTo.set(null);
});
}
// check validity of move:
private boolean moveValid(Square from, Square to) {
if (from == null || // no from
to == null || // no to
!from.isOccupied() || // from occupied
to.isOccupied() || // to occupied
from.getY() - to.getY() != 1 || // not moving forward
Math.abs(from.getX() - to.getX()) != 1) // not moving sideways
{
return false ;
} else {
return true ;
}
}
// represents a square on the board. Encapsulates its position (x and y),
// whether or not it contains a piece (occupied)
// and exposes a UI component representing it (view):
private static class Square {
private final int x ;
private final int y ;
private final BooleanProperty occupied ;
private final StackPane view ;
public Square(int x, int y) {
this.x = x ;
this.y = y ;
this.occupied = new SimpleBooleanProperty(this, "occupied", false);
this.view = createView();
}
private StackPane createView() {
// use a StackPane as the base view:
StackPane view = new StackPane();
view.getStyleClass().add("square");
// pseudoclass to enable alternate color on alternate squares:
PseudoClass oddClass = PseudoClass.getPseudoClass("odd");
// fill GridPane cell:
GridPane.setFillHeight(view, true);
GridPane.setFillWidth(view, true);
// fix size:
view.setMinSize(SQUARE_SIZE, SQUARE_SIZE);
view.setMaxSize(SQUARE_SIZE, SQUARE_SIZE);
// set pseudoclass state for "odd" squares:
if ((x + y) % 2 == 0) {
view.pseudoClassStateChanged(oddClass, true);
}
// add piece to stack pane...
Circle piece = new Circle(SQUARE_SIZE/2, SQUARE_SIZE/2, PIECE_RADIUS);
piece.getStyleClass().add("piece");
view.getChildren().add(piece);
// .. but only actually display it if the square is occupied:
piece.visibleProperty().bind(occupied);
return view ;
}
public Node getView() {
return view ;
}
public int getX() {
return x ;
}
public int getY() {
return y ;
}
public final BooleanProperty occupiedProperty() {
return this.occupied;
}
public final boolean isOccupied() {
return this.occupiedProperty().get();
}
public final void setOccupied(final boolean occupied) {
this.occupiedProperty().set(occupied);
}
}
public static void main(String[] args) {
launch(args);
}
}
checkers.css:
.root {
/* and.. breathe... */
-fx-padding: 40px ;
}
.square {
/* use a looked-up color for the main background of a square,
so it's easy to change */
-square-background: antiquewhite ;
/* nest some backgrounds to give the effect of a raised border: */
-fx-background-color: white, lightgray, darkgray, black, -square-background ;
-fx-background-insets: 0px, 1px 1px 0px 0px, 2px 2px 1px 1px, 4px 4px 2px 2px, 5px 5px 3px 3px ;
}
.square:odd {
/* change the main background on "odd" squares: */
-square-background: cornflowerblue ;
}
.square:highlight {
/* and change it again for highlighted squares: */
-square-background: lightgoldenrodyellow ;
}
.piece {
/* color for pieces: */
-fx-fill: steelblue ;
}