首页 > 编程学习 > [翻译] 使用FXGL创建一个非常基本的游戏

[翻译] 使用FXGL创建一个非常基本的游戏

发布时间:2022/11/17 10:39:47

游戏要求

首先,让我们为我们的简单游戏定义一些要求:

  1. 一个600x600的窗口。
  2. 屏幕上的玩家,由蓝色矩形表示。
  3. 可以通过按键盘上的W、S、A或D来移动玩家。
  4. UI由一行文本表示。
  5. 当玩家移动时,UI文本会更新以显示玩家在其生命周期内移动了多少像素。

在本教程的最后,你可以获得这样的一个游戏窗口 (可能略有不同):

虽然它可能看起来不像游戏,但它将帮助你了解FXGL的基本功能。完成本教程后,你可以构建各种简单的游戏。

准备工作

既然我们对游戏的期望有一个大致的概念,我们可以回到集成开发环境,为我们的游戏创建一个包。

注意: 目录结构类似于Maven目录结构,但是,如果你不知道这是什么,请不要担心。我们将在稍后阶段介绍结构。此时,将 src作为主源目录就足够了。

我要用tutorial作为包名称。

  1. 在你的IDE中创建包 tutorial .

  2. 在package中,使用BasicGameApp名称创建Java类.

通常,在你的main()所在的类上附加 "App"。这可以让其他开发者轻松识别你的游戏的主要入口在哪里。为了使你接下来的步骤更容易,我建议你打开你的BasicGameApp类并添加这些导入。

import com.almasb.fxgl.app.GameApplication;
import com.almasb.fxgl.app.GameSettings;
import com.almasb.fxgl.dsl.FXGL;
import com.almasb.fxgl.entity.Entity;
import com.almasb.fxgl.input.Input;
import com.almasb.fxgl.input.UserAction;
import javafx.scene.input.KeyCode;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import java.util.Map;
复制代码

我们现在可以开始编写我们的代码了

编码阶段

为了使用FXGL,你的App 类需要继承GameApplication 并重写initSettings()方法:

public class BasicGameApp extends GameApplication {

    @Override
    protected void initSettings(GameSettings settings) {}
}
复制代码

一旦继承GameApplication ,大多数IDEs将自动生成重写方法。现在我们希望能够开始游戏。为此,只需添加以下内容:

public static void main(String[] args) {
    launch(args);
}
复制代码

如果你以前使用过JavaFX,那么你会注意到,它与我们用来启动JavaFX应用程序的方法完全相同。简而言之,FXGL是一个具有游戏开发功能的JavaFX应用程序,仅此而已。

需求1 (窗口)

在这一点上,你应该已经能够运行你的游戏,但是首先让我们调整一些设置。

@Override
protected void initSettings(GameSettings settings) {
    settings.setWidth(600);
    settings.setHeight(600);
    settings.setTitle("Basic Game App");
    settings.setVersion("0.1");
}
复制代码

正如你所看到的,所有的设置都是在initSettings()中配置的。一旦设置好了,在运行时就不能改变设置。现在你可以在你的IDE中点击 "run",它应该以600x600的窗口和 "Basic Game App "为标题启动游戏。

我们现在达到了我们的需求1。很简单,对吧?

需求 2 (Player)

下一步是在屏幕上添加一个玩家。我们将在initGame()中完成这一工作。简而言之,这是你设置游戏开始前需要准备的所有东西的地方。

private Entity player;

@Override
protected void initGame() {
    player = FXGL.entityBuilder()
            .at(300, 300)
            .view(new Rectangle(25, 25, Color.BLUE))
            .buildAndAttach();
}
复制代码

(注意:对于保存/加载系统来说,我们不在声明时初始化实例级字段,而是在'initGame()'中进行初始化,这一点很重要。)

如果你不熟悉函数式 API,那么上面的代码是很难一下子接受的。所以我们要慢慢开始。

接下来讲解一下上述代码:

  • 有一个名为player的实例级字段,其类型为Entity

  • 一个实体基本上就是一个游戏对象。这就是你现在需要知道的一切。

  • FXGL.entityBuilder()是构建实体的首选方式。

  • 通过调用.at(),我们将实体定位到我们想要的位置。在这个例子中,它是x = 300,y = 300。

(注意: 实体在FXGL中的位置是其左上角,就像在JavaFX中一样。)

然后我们告诉构建器,通过使用我们传入的UI节点作为参数来创建实体的视图。这里是一个标准的JavaFX Rectanglewidth=25height =25,颜色为蓝色。

(注意:你可以使用任何基于JavaFX节点的对象,这非常酷。 😄)

最后,我们调用.buildAndAttach()方法。通过调用build,我们可以获得我们正在构建的实体的引用。至于 "attach "部分,它可以方便地将构建的实体直接连接到游戏世界中。如果你运行游戏,你现在应该在屏幕中心附近看到一个蓝色的矩形。

太好了,我们刚刚完成了2号需求!

需求3 (输入)

现在,我们将继续执行与用户输入相关的要求。我们将输入处理代码放入initInput()中。下面的代码显示了用于添加输入操作的所有API。考虑之后,我们将看到如何使用更简单的API。

We will now proceed with the requirement related to user input. We put the input handling code in initInput(). The below code shows the "full" API for adding an input action. After considering it, we will see how to use simpler API.pixels

@Override
protected void initInput() {
    Input input = FXGL.getInput();

    input.addAction(new UserAction("右移") {
        @Override
        protected void onAction() {
            player.translateX(5); // 向右移动5个像素
        }
    }, KeyCode.D);
}
复制代码

让我们逐行浏览这个片段。

首先得到输入对象。正如你所注意到的,要使用大部分的FXGL功能,你需要做的就是调用FXGL.***,你的IDE会显示你可以调用的所有功能。

接下来,我们添加一个动作,然后是一个按键代码。同样,如果你以前使用过JavaFX,那么你就会知道,这些键码与事件处理程序中使用的键码完全相同。我们在说:当'D'被按下时,做我们所创建的动作。现在让我们来看看动作本身。

当我们创建一个动作时,我们也给它一个名字--"右移"。这很重要,因为这个名字会直接反馈给控件和菜单系统,用户可以随时改变它们。所以这个名字必须对用户有意义,而且是唯一的。一旦我们创建了这个动作,我们就覆盖它的一个方法(这次是onAction()),并提供一些代码。该代码将在动作发生时被调用,即当 "D "被按下时。

回顾一下需求,我们想要移动玩家。所以当'D'被按下时,我们想把Player向右移动。我们调用player.translateX(5),将其X坐标平移5像素。

(注意: translate是计算机图形学中使用的术语,意思是移动。)

现在让我们缩短它:

@Override
protected void initInput() {
    FXGL.onKey(KeyCode.D, () -> {
        player.translateX(5); // 向右移动5个像素
    });
}
复制代码

这也导致玩家实体向右移动5个像素,并且 (几乎) 等同于我们之前编写的代码。但是,我认为你会同意此API (称为DSL) 更加简洁。如果导入com.almasb.fxgl.dsl.FXGL.*,这可以进一步缩短,即:

import static com.almasb.fxgl.dsl.FXGL.*;
复制代码

然而,为了避免引入太多的新概念,我们现在还不会这么做。你可能会猜测其余的输入代码会是什么样子,但以防万一,以下是所有的代码

@Override
protected void initInput() {
    FXGL.onKey(KeyCode.D, () -> {
        player.translateX(5); // move right 5 pixels
    });

    FXGL.onKey(KeyCode.A, () -> {
        player.translateX(-5); // move left 5 pixels
    });

    FXGL.onKey(KeyCode.W, () -> {
        player.translateY(-5); // move up 5 pixels
    });

    FXGL.onKey(KeyCode.S, () -> {
        player.translateY(5); // move down 5 pixels
    });
}
复制代码

需求3--完成了,尘埃落定。我们已经完成了一半以上,做得很好!

需求 4 (UI)

我们现在进入下一个位--UI,你或许已经猜到了,initUI()

@Override
protected void initUI() {
    Text textPixels = new Text();
    textPixels.setTranslateX(50); // x = 50
    textPixels.setTranslateY(100); // y = 100

    FXGL.getGameScene().addUINode(textPixels); // add to the scene graph
}
复制代码

对于大多数UI对象,我们只是使用JavaFX对象,因为没有必要重新发明轮子。你应该注意到,当我们在世界中添加一个实体时,游戏场景接收到了该实体有一个与之相关的视图这一事实。因此,游戏场景神奇地将该实体添加到场景图中。对于UI对象,我们要负责将其添加到场景图中,我们可以通过调用getGameScene().addUINode()方法来实现。

这就是需求4。继续!

需求5 (Gameplay)

为了完成最后一个要求,我们将使用游戏变量。在FXGL中,可以从游戏的任何部分访问和修改游戏变量。从某种意义上说,它是一个全局变量,其范围与FXGL游戏实例相关联。此外,这些变量可以绑定(类似于JavaFX属性)。我们从创建这样一个变量开始:

@Override
protected void initGameVars(Map<String, Object> vars) {
    vars.put("pixelsMoved", 0);
}
复制代码

然后我们需要在玩家移动时更新变量。我们可以在输入处理部分执行此操作。

FXGL.onKey(KeyCode.D, () -> {
    player.translateX(5); // move right 5 pixels
    FXGL.inc("pixelsMoved", +5);
});
复制代码

我会让你对剩下的动作做同样的事情 (左、上、下)。最后一步 (对于需求和教程) 是将我们的UI文本对象绑定到变量pixelsMoved。在initUI()一旦我们创建了textPixels 对象,我们可以执行以下操作:

textPixels.textProperty().bind(FXGL.getWorldProperties().intProperty("pixelsMoved").asString());
复制代码

之后,UI文本将显示播放器自动移动了多少像素。

你现在有了一个基本的FXGL游戏。希望你玩得开心。下面是本教程的全部源代码,所有FXGL.*调用都是静态导入的。

package tutorial;

import com.almasb.fxgl.app.GameApplication;
import com.almasb.fxgl.app.GameSettings;
import com.almasb.fxgl.dsl.FXGL;
import com.almasb.fxgl.entity.Entity;
import javafx.scene.input.KeyCode;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import java.util.Map;

import static com.almasb.fxgl.dsl.FXGL.*;

public class BasicGameApp extends GameApplication {
    @Override
    protected void initSettings(GameSettings settings) {
        settings.setWidth(600);
        settings.setHeight(600);
        settings.setTitle("Basic Game App");
        settings.setVersion("0.1");
    }

    @Override
    protected void initInput() {
        onKey(KeyCode.D, () -> {
            player.translateX(5); // move right 5 pixels
            inc("pixelsMoved", +5);
        });

        onKey(KeyCode.A, () -> {
            player.translateX(-5); // move left 5 pixels
            inc("pixelsMoved", -5);
        });

        onKey(KeyCode.W, () -> {
            player.translateY(-5); // move up 5 pixels
            inc("pixelsMoved", +5);
        });

        onKey(KeyCode.S, () -> {
            player.translateY(5); // move down 5 pixels
            inc("pixelsMoved", +5);
        });
    }

    @Override
    protected void initGameVars(Map<String, Object> vars) {
        vars.put("pixelsMoved", 0);
    }

    private Entity player;

    @Override
    protected void initGame() {
        player = entityBuilder()
                .at(300, 300)
                .view(new Rectangle(25, 25, Color.BLUE))
                .buildAndAttach();
    }

    @Override
    protected void initUI() {
        Text textPixels = new Text();
        textPixels.setTranslateX(50); // x = 50
        textPixels.setTranslateY(100); // y = 100

        textPixels.textProperty().bind(getWorldProperties().intProperty("pixelsMoved").asString());

        getGameScene().addUINode(textPixels); // add to the scene graph
    }

    public static void main(String[] args) {
        launch(args);
    }

Copyright © 2010-2022 dgrt.cn 版权所有 |关于我们| 联系方式