LiteLoader识别Mod主类格式的方式是找到一个类实现了“com.mumfrey.liteloader.LiteMod”,并以“LiteMod”开头,作为Mod的主类。
不过。。。LiteLoader官方给的示例Mod是这么写的:
- public class LiteModExample implements Tickable, PreRenderListener
复制代码
说好的“LiteMod”接口呢?
实际上是这样子的:
- public interface Tickable extends LiteMod
- public interface PreRenderListener extends LiteMod
复制代码
好我们继续。再重复一遍,和Forge等Mod框架不同,LiteLoader识别Mod主类的方式是该类实现了“LiteMod”接口,并以“LiteMod”开头。
那么这些继承了“LiteMod”的接口又是做什么的呢?
熟悉开发插件以及Forge Mod的开发者应该比较熟悉注册事件的方式,在其中,用于执行事件逻辑的类中的方法,都加上了诸如“@EventHandler”或者“@SuscribeEvent”的注解,不过LiteLoader不是这么做的,LiteLoader的事件是基于接口的。换句话说,每种类型的事件都有其特定的接口,Mod主类只要实现了相应的接口,就代表监听了相应的事件。
如果有读者对旧版本的LiteLoader比较熟悉,就应该知道LiteLoader中历史最悠久,也是最知名的接口之一,就是一个名为“Tickable”的接口。该接口在每次客户端执行一次渲染时调用一次,也就是说该接口每个tick会调用多次。我们看看这个接口新添加的方法:
- public abstract void onTick(Minecraft minecraft, float partialTicks, boolean inGame, boolean clock);
复制代码
第一个参数是Minecraft的主类不解释。
第二个参数代表当前进行的tick进行了多久。因为刚刚我们说到该方法会在一个tick执行多次,所以说这个浮点数就代表执行时tick的小数部分,永远在0和1之间。
第三个参数表示我们是否在游戏中,也就是是否在一些诸如按下Esc出现的界面的状态。
最后一个参数如果为真,代表它是一个新的tick,否则代表它是当前tick的又一次执行。
通常的渲染HUD的方式为:
- @Override
- public void onTick(Minecraft minecraft, float partialTicks, boolean inGame, boolean clock)
- {
- if (inGame && minecraft.currentScreen == null && Minecraft.isGuiEnabled())
- {
- // DO SOMETHING
- }
- }
复制代码
这保证了HUD能够像游戏中的其他HUD一样渲染。
这里作为示例,我们想要制作的Mod是一个像弹幕(Danmaku)视频网站一样的Mod。服务端插件发送弹幕,同时在客户端显示出来。
我们先完成一下弹幕的逻辑(这里偷懒直接使用内部类了)。这段代码只是用于实现游戏逻辑的,具体内容是什么样子的不重要:
- private DanmakuRenderer danmakuRenderer;
- @Override
- public void onTick(Minecraft minecraft, float partialTicks, boolean inGame, boolean clock)
- {
- if (inGame && minecraft.currentScreen == null && Minecraft.isGuiEnabled())
- {
- ScaledResolution scaledresolution = new ScaledResolution(minecraft);
- int width = scaledresolution.getScaledWidth(), height = scaledresolution.getScaledHeight();
- this.danmakuRenderer.render(minecraft, width, height, partialTicks);
- }
- }
- private static class DanmakuRenderer
- {
- private final float movePerTick;
- private final Queue<Danmaku> strings = new ConcurrentLinkedQueue<Danmaku>();
- private DanmakuRenderer(float movePerTick)
- {
- this.movePerTick = movePerTick;
- }
- private void push(Minecraft minecraft, String string)
- {
- if (minecraft.world != null)
- {
- this.strings.add(new Danmaku(string, minecraft.world.getTotalWorldTime()));
- }
- }
- private void render(Minecraft minecraft, int width, int height, float partialTicks)
- {
- long now = minecraft.world.getTotalWorldTime();
- Iterator<Danmaku> iterator = this.strings.iterator();
- while (iterator.hasNext())
- {
- Danmaku danmaku = iterator.next();
- String text = danmaku.string;
- float timeDiff = now - danmaku.renderTick + partialTicks;
- if (timeDiff < 0)
- {
- break;
- }
- int offsetWidth = (int) (width - timeDiff * this.movePerTick);
- int offsetHeight = height / 2;
- FontRenderer fontRenderer = minecraft.fontRendererObj;
- int stringWidth = fontRenderer.getStringWidth(text);
- if (offsetWidth < -stringWidth)
- {
- iterator.remove();
- }
- else
- {
- fontRenderer.drawString(text, offsetWidth, offsetHeight, 0xFFFFFFFF);
- }
- }
- }
- }
- private static class Danmaku
- {
- private final String string;
- private final long renderTick;
- private Danmaku(String string, long renderTick)
- {
- this.string = string;
- this.renderTick = renderTick;
- }
- }
复制代码
我们暂时把“DanmakuRenderer”类的“push”方法闲置,以供接下来使用。
很好,下面我们来考查“LiteMod”接口(及其父接口)定义的四个方法:
- @Override
- public abstract String getName();
- public abstract String getVersion();
- public abstract void init(File configPath);
- public abstract void upgradeSettings(String version, File configPath, File oldConfigPath);
复制代码
getName方法用于显示名称(没错就是游戏主界面右侧的小鸡界面里的名称)。
getVersion方法用于设置版本号(这里最好和在“build.gradle”里设置的相同)。
init方法会在游戏尚未完全初始化时调用,用于对Mod进行初始化(这里不要轻易操作原版Minecraft的类)。传入的参数一般情况下代表“liteconfig/common”文件夹,你可以在这里读取并写入配置,不过我会在后面的内容中提供一种更好的方法。
upgradeSettings方法会在Minecraft发生版本更新时调用。如果LiteLoader没有找到当前Minecraft版本的配置文件却找到了旧的Minecraft版本的配置文件,就会调用这个方法。传入的第一个参数代表新的Minecraft版本,后两个参数一般情况下代表“liteconfig/config.{Minecraft版本}”文件夹。比如说这个1.11.2的Mod如果运行的整合包从1.10.2升级而来,那么对应的三个参数分别为“1.11.2”、代表“liteconfig/config.1.11.2”的文件夹,代表“liteconfig/config.1.10.2”的文件夹。
读者可能会有疑问:为什么同时有“liteconfig/common”和“liteconfig/config.{Minecraft版本}”两种文件夹呢?它们的区别是什么呢?别急,后面会讲到的。
然后我们实现一下这四个方法。这里我们利用“init”方法完成了“danmakuRenderer”字段的初始化:
- private float movePerTick = 5.0F;
- @Override
- public String getName()
- {
- return "LiteModTutor";
- }
- @Override
- public String getVersion()
- {
- return "0.1.0";
- }
- @Override
- public void init(File configPath)
- {
- this.danmakuRenderer = new DanmakuRenderer(this.movePerTick);
- }
- @Override
- public void upgradeSettings(String version, File configPath, File oldConfigPath)
- {
- // DO NOTHING
- }
复制代码
最后说一句,Mod主类的构造方法必须是无参的,请在“init”方法完成所有的初始化操作。
|