1

免责声明:我对 JavaFX 和模型-视图-控制器设计原则非常陌生。

这是我的基本情况:

我有一个类似于教程中概述的 ScreenManager。使用屏幕管理器,我使用屏幕按钮上的基本操作处理在多个 FXML 屏幕之间切换。

问题是其中之一是带有地图的游戏屏幕。我将在此游戏屏幕上绘制基于图块的地图,因此我在 FXML 文件中使用了 TilePane(如果有更好的方法将图块绘制到屏幕区域,请告诉我)。

这是我创建要绘制到游戏屏幕的 TilePane 的代码:

public TilePane createRandomMap(boolean centeredRiver)
{

    Image atlas = new Image("resources/textures/tile_atlas.jpg");
    Random rand = new Random();
    int riverPos = 4, 
        tileHeight = (int)atlas.getHeight()/2, 
        tileWidth = (int)atlas.getWidth()/3;
    if(!centeredRiver)
        riverPos = rand.nextInt(10);
    for(int i = 0; i < 5; i++)
    {
        for(int j = 0; j < riverPos; j++)
        {
            int type = rand.nextInt(4);
            Tile tile = new Tile(tileTypes[type], i, j);
            tile.setImage(atlas);
            tile.setViewport(new Rectangle2D((type%3)*tileWidth, //minX
                    tileHeight-(type/3)*tileHeight, //minY
                    tileWidth, tileHeight)); // width, height
            tile.setFitHeight(tilePane.getHeight()/5); //fit to the size of the pane
            tile.setFitWidth(tilePane.getWidth()/9); //fit to the size of the pane
            tile.setPreserveRatio(true);
            tilesToAdd.add(tile); 
        }
        if(i == 2)
        {
            Tile tile = new Tile(tileTypes[5], i, riverPos);
            tile.setImage(atlas);
            tile.setViewport(new Rectangle2D(2*tileWidth, 0, 
                    tileWidth, tileHeight));
            tile.setFitHeight(tilePane.getHeight()/5);
            tile.setFitWidth(tilePane.getWidth()/9);
            tile.setPreserveRatio(true);
            tilesToAdd.add(tile);
        }
        else
        {
            Tile tile = new Tile(tileTypes[4], i, riverPos);
            tile.setImage(atlas);
            tile.setViewport(new Rectangle2D(tileWidth, 0, 
                    tileWidth, tileHeight));
            tile.setFitHeight(tilePane.getHeight()/5);
            tile.setFitWidth(tilePane.getWidth()/9);
            tile.setPreserveRatio(true);
            tilesToAdd.add(tile);
        }
        for(int j = riverPos + 1; j<9; j++)
        {
            int type = rand.nextInt(4);
            Tile tile = new Tile(tileTypes[type], i, j);
            tile.setImage(atlas);
            tile.setViewport(new Rectangle2D((type%3)*tileWidth, //minX
                    tileHeight-(type/3)*tileHeight, //minY
                    tileWidth, tileHeight)); // width, height
            tile.setFitHeight(tilePane.getHeight()/5); //fit to the size of the pane
            tile.setFitWidth(tilePane.getWidth()/9); //fit to the size of the pane
            tile.setPreserveRatio(true);
            tilesToAdd.add(tile); 
        }
    }
    tilePane.getChildren().addAll(tilesToAdd);

    return tilePane;
}

我已经能够在我的控制器类中访问这个 TilePane:

public class GameScreenController implements Initializable, ControlledScreen {

ScreenManager screenManager;

@FXML
TilePane mapPane

/**
 * Initializes the controller class.
 */
@Override
public void initialize(URL url, ResourceBundle rb) {
    TileEngine engine = new TileEngine();
    mapPane = engine.createRandomMap(true);
}    

在上述实例中,我将 FXML 屏幕中定义的 TilePane 设置为我在模型中创建的 TilePane,但出现以下错误:

Can not set javafx.scene.layout.TilePane field
screens.gameScreen.GameScreenController.mapPane to 
javafx.scene.layout.AnchorPane

这是我的 FXML 文件中处理 TilePane 的部分:

<TilePane id="MapPane" fx:id="mapPane" layoutX="0.0" layoutY="0.0" prefColumns="9" prefHeight="560.0" prefTileHeight="112.0" prefTileWidth="112.0" prefWidth="1108.0" visible="true" />

总的来说,我真的很难理解 JavaFX 和游戏设计,但我非常想学习。我很乐意澄清并批评其结构的任何部分,以使其变得更好,即使是与我所问的问题无关的事情。

先感谢您!

4

1 回答 1

3

MVC

严格来说,JavaFX 不支持 MVC(发明者自己宣称它已经死了)而是某种 MVVM(模型-视图-视图模型)

模型和控制器之间的通信

我通常使用依赖注入来做到这一点。如果你想要一个简单的 FrameWork,我可以建议你afterburner.fx或 Google Guice。两个非常轻量级的框架都可以满足您的需求。基本上,他们可以为您处理类的实例化,并确保您的模型只有一个实例处于活动状态。

一些示例代码:

public class Presenter {
    // Injects
    @Inject private StateModel stateModel;
}

不需要声明构造函数或任何东西。如果你愿意,你需要做更多的工作来测试它,但总的来说我不想再次错过这个功能,因为通常你只想要一个StateModel你的所有信息都来自于并且所有视图只是听它们或改变他们。

让我们再举一个例子来扩展发布的代码

@Singleton
public class StateModel {
    // Properties
    private final StringProperty name = new SimpleStringProperty();

    // Property Getter
    public StringProperty nameProperty() { return this.name; }
}

如果我们现在在 Presenter/Controller 中声明了一个标签,我们可以像这样监听模型中的变化

public class Presenter implements Initializable {
    // Nodes
    @FXML private Label nodeLabelName;

    // Injects
    @Inject private StateModel stateModel;

    @Override
    public void initialize(URL location, ResourceBundle resource) {
        this.nodeLabelName.textProperty().bind(this.stateModel.nameProperty());
    }
}

我能给你的另一个建议是Threading。您通常希望您的业务逻辑在另一个线程中执行,否则您的应用程序线程在处理时将开始滞后

于 2013-10-21T12:07:18.950 回答