本篇教程由作者设定未经允许禁止转载。

如何使用图形接口(以下简称GI)打开窗口呢?

导入jar包依赖和配置gradle暂且不提,大家应该都会,所以直接进入正题:

假如我有一个方块类,在某个方法中需要实现这种功能:

public ActionResultType onBlockActivated(BlockState state, World worldIn, BlockPos pos, PlayerEntity player, Hand handIn, BlockRayTraceResult hit) {

    if (!worldIn.isRemote && handIn == Hand.MAIN_HAND) {
        Screen screen = MinecraftDisplay.canvasContainer("test", 200, 200, false, true);
        screen.loadFromCanvas(new ExampleCanvas(screen));
    }

    return ActionResultType.SUCCESS;

}

可以看到在第四、五行调用了GI封装的方法。

MinecraftDisplay.canvasContainer(String title, int width, int height, boolean pauseGame, boolean notHide) {...}

这个方法的参数解读如下:

  • title 窗口标题

  • width/height 窗口宽、高

  • pauseGame 是否打开时暂停游戏

  • notHide 不在MC窗口被单击时隐藏(永远置于顶层),使得观看窗口的同时还可以操作游戏

方法返回一个Screen对象,这是画布的重要容器,你不需要接触它,只需要知道Canvas需要加载到Screen里才能显示就可以了。

接着保存Screen对象,用于创建画布。

screen.loadFromCanvas(Canvas canvas) {...}

用这个方法加载画布到Screen里,会自动更新。ExampleCanvas是教程使用的默认画布,可以去看源码实现,理解逻辑是怎么运行的。

要提一嘴的是,Window对象是必须即用即创建、即关即销毁的,不能保存。

如果你按照上述方法做了,正常应该看到右键方块时会打开一个窗口。(但是不推荐用在方块上,还得声明一个限制变量,不然每点一次就会生成一个窗口……)

不过,为什么这个窗口的球无法拖动呢?

这是因为我们没有设置bufferSize。

现在我们在构造函数添加这一行,就可以正确让Input获取鼠标位置了。

setDefaultSize(200, 200);

到现在为止我们只用了示例的画布,那么如何给画布添加自定义内容呢?

如下的代码是一个自定义画布,含有一个改变世界天气的按钮:

public class CustomCanvas extends Canvas {

    ElementButtonText button;
    World world;

    public ExampleCanvas(Screen scr, World worldIn) {
    
        super(scr);
        world = worldIn;

        setDefaultSize(200, 200);

    }

    public void init() {

        buttons.add(button = new ElementButtonText(100, 100, 128, 32, "setRain"));

    }

    public void tick() {

        if(button.isClicking()) {
            ((ServerWorld) world).setWeather(0, 1000, true, false);
        }

    }

}

很简单吧?

现在,把它添加到你的方法中:

public ActionResultType onBlockActivated(BlockState state, World worldIn, BlockPos pos, PlayerEntity player, Hand handIn, BlockRayTraceResult hit) {

    if (!worldIn.isRemote && handIn == Hand.MAIN_HAND) {
        Screen screen = MinecraftDisplay.canvasContainer("test", 200, 200, false, true);
        screen.loadFromCanvas(new CustomCanvas(screen, worldIn));
    }

    return ActionResultType.SUCCESS;

}

完工!现在打开MC,右击方块,按下窗口的按钮,回到游戏就能看见世界开始下雨了。

在MC中拥有一个独立的窗口!-第1张图片

没有背景总觉得很空,我们开始为它添加额外的效果吧。

在init方法添加这么一行:

bgManager.staticBackground(Reader.image(getClass(), "test", "textures/bg"));

这个方法调用背景管理器,添加了一张静态背景图片。(实际路径:resources/assets/test/textures/bg.png)

这里涉及读取文件问题,直接调用Reader的静态方法即可读取相应类型的资源了。

这个resources读取花了我两天调试,而且网上根本没有解决方案啊啊!

PS:(在v1.0.0中读取资源有问题,请更换至v1.0.1或更高)

IO读取大量文件会严重消耗性能,那么尝试新建一个Resource类存储吧。在你模组的构造器中,初始化Resource即可。

在MC中拥有一个独立的窗口!-第2张图片

PS:如果你的jar包中可以读取到资源,项目却不行,请看这个:

在build.gradle中写入:

task cpRes(type:Copy) {
    from "${projectDir}/src/main/resources"
    into "${buildDir}/classes/java/main"
}

processResources {
    exclude {"**/*.*"}
}

processResources.dependsOn()

依次运行这两个任务(IDEA会有绿色按钮,eclipse不清楚),就会在build/classes下生成资源文件,就可以读取了。

==========================我是分割线=============================

接下来来认识常用的类:

  • Element 最基础的组件,构造器定位点在左上角,一般用于渲染背景图片(定位更简单,即[0,0])

  • ElementImage 以中心位置为定位点的组件,当然调用locateOffset方法也可以从组件左上角定位

  • ElementEffect 实现了大小和透明度变化的组件,一般用ElementFactory批量生成,内有sizeCrease(大小变化,一般是负数) tranCrease(透明度变化,必须是负数) leastSize(最小大小)变量,可以用setter设置属性,当大小等于0或透明度等于0会自动销毁。

  • ElementButton 即上文的按钮。但是这个按钮是图片按钮。

  • ElementButtonText 即上文的文字按钮。

  • ElementProgressBar 进度条组件,这玩意也是左上角定位的。有spike(分割线数量) value(值) maxValue(最大值)属性。

接下来说一说Graph类(实例对象直接可以在画布引用哦,和上文的bgManager一样是成员变量)

Graph 其实就是画笔,封装了很多绘制的方法。最常用的是画文本相关的方法(因为其它的都封装进Element了)。

Graph画文本,一共有四种可选:

renderString(double x, double y, String str) {...}
renderStringLeft(double x, double y, String str) {...}
renderStringDefault(double x, double y, String str) {...}
renderStringLeftDefault(double x, double y, String str) {...}
renderFontNumber(double x, double y, int width, int height, int number, NumberFont nfr) {...}

带Left的是以左上角为基准开始绘制,反之则是中心绘制。

带Default的是自动设置颜色和字体为默认,但是会污染graph的颜色,字体属性,需要重新设置。(调用setColor/Font,Color可以用Graph.color4f/color3f得到,Font自行百度)

renderFontNumber则是绘制数字字体,需要自己画一个1:10的图像,依次画0~9,新建NumberFont就能自动读取绘制。