本篇教程由作者设定未经允许禁止转载。
如何使用图形接口(以下简称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,右击方块,按下窗口的按钮,回到游戏就能看见世界开始下雨了。
没有背景总觉得很空,我们开始为它添加额外的效果吧。
在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即可。
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就能自动读取绘制。