# Forge事件系统
**难度分级:☆☆☆**
在前几章中,我们已经接触到了一系列以 **@SubscribeEvent** 注解开头的事件调用方法。那么究竟什么是 **“事件”** ,他们又是如何在游戏中起作用的?本章我们就来介绍Forge的事件系统。
## 事件系统的引入
在原版Minecraft中,所有的游戏逻辑都会按照封装好的代码运作。对模组开发者来说,有时候我们会希望能**干涉原版的运行机制**,例如在玩家登入时给发送欢迎词,在右键某个方块时打开一个自定义GUI等。然而,Minecraft本身**并没有**提供可供模组对游戏机制进行修改的接口或方法,因此开发者需要通过别的方法来实现对游戏机制的编辑。Forge通过**监听原版游戏运行周期中的某些重要代码触发节点**,并通过一种叫做 **依赖注入(Dependent Injection)** 的方法将模组中的相应代码注入游戏逻辑中,使得模组开发者能够对游戏机制进行编辑和改写,这便是**Forge事件系统**。
## 事件系统的订阅
正如前几章中已经出现过的那样,如果你想要将自己的代码在某一事件触发时执行,那么你必须**订阅**这一事件。在代码中,你就需要通过在你的函数前加上 **@SubscribeEvent** 注解,并**在参数中传入这一事件类型的变量**来完成。例如在EOK中,对**绑定实体的Capability**的订阅代码即为:
~~~
public class AnotherEventHandler {
@SubscribeEvent
public void onAttachCapabilitiesEntity(AttachCapabilitiesEvent<Entity> event){
...
}
}
~~~
Os-Ir:~~NotEnoughEventHandler~~
zijing_233:~~JustEnoughEventHandler~~
molybdenum:~~AlmostEnoughEventHandler~~
Nat Fr Gx:~~EventHandler1, EventHandler2, EventHandler3...~~
(逃)<br>
Forge所提供的所有可供订阅的事件类型可在你的开发项目的**ForgeSrc外部库**中找到,一般所在包名为`net.minecraftforge.event` 或 `net.minecraftforge.client.event`,部分和游戏加载及玩家相关的事件也可能出现在`net.minecraftforge.fml.client.event`及`net.minecraftforge.fml.common.gameevent`包下,你可以通过阅读源代码了解到这些事件的触发时刻。这里需要注意的是,所有的事件订阅方法**返回类型**必须为**空(void)**,否则将会抛出异常。关于**如何判断事件被成功触发**,我们将在后文中介绍。
## 注册你的事件至相应的事件总线
用于**在相应时刻被注入游戏代码的待注册事件列表**,被称为**事件总线**。 Forge在处理模组自身注册的相关代码时 **(几个在主类中以FML开头的事件)**,会通过调用**Google Guava**的事件总线将模组代码注入到Minecraft源代码中。除此之外,Forge自身还为模组开发者提供了三条事件总线:**一般事件总线(MinecraftForge.EVENT_BUS),矿物生成总线(MinecraftForge.ORE_GEN_BUS)以及地形生成总线(MinecraftForge.TERRAIN_GEN_BUS)**。这些总线负责的事件类型通过名字即可直接看出,所有**包含你订阅事件的类**都应当**被注册至相应的注册总线**(例如包含 **矿物生成事件(OreGenEvent)** 的类就应当被注册到**矿物生成总线**上)。
Forge为模组开发者提供了两种事件注册方式,即 **MinecraftForge.总线实例名.register()方法** 及 **@Mod.EventBusSubscriber** 注解。
对于前者,你可以在**游戏加载的FMLPreInitialization阶段** 调用该方法,并通过传入**包含你订阅事件的类对象**进行注册。例如EOK中对AnotherEventHandler类的注册:
~~~
public class EOK
{
...
@EventHandler
public void preInit(FMLPreInitializationEvent event)
{
...
MinecraftForge.EVENT_BUS.register(new AnotherEventHandler());
}
}
~~~
这里值得注意的是,如果你的事件注册类中的订阅事件方法为**静态的(static)**,如`public static void onItemRegister(RegistryEvent.Register<Item> event)`,那么你应当传入该类的**实例**,否则你应当传入**该类的class**,即**类名.class**。
为你的事件订阅类添加 **@Mod.EventBusSubscriber** 注解是另一种将你的事件订阅方法注册到 **一般事件总线(MinecraftForge.EVENT_BUS)** 上的方法。该注解相当于Forge自动为该类执行了`MinecraftForge.EVENT_BUS.register(类名.class)`方法,因此在该类中的所有事件订阅方法也必须都为**静态的(static)**。
**是否将事件订阅方法申明为静态的在实际游戏运行过程中几乎没有区别,其区别仅在与注册事件类时的方法不同。**
## 事件的优先级
在某些情况下,我们在**多个方法**中监听**同一事件**,此时我们就要为这些方法设置**执行优先级**。(**注意:非必要**) 设置优先级的方法十分简单,只需要在@SubscribeEvent注解后加入`priority = EventPriority.优先级对象`即可,例如:`@SubscribeEvent(priority = EventPriority.HIGHEST)`。Forge为事件提供了`HIGHEST`、`HIGH`、`NORMAL`、`LOW`和`LOWEST` 五种优先级对象,优先级依次降低。
**注:如果你想让你订阅的事件处理尽量不影响到原版和其他Mod的机制,请将优先级设为`LOWEST`。**
## 事件的取消和返回值
//待补全
- 0.引子
- 基础篇 - 1.构建开发环境
- 基础篇 - 2.主类和代理
- 基础篇 - 3.创建一个物品
- 基础篇 - 4.创建一个方块
- 基础篇 - 4.1自定义方块模型
- 基础篇 - 5.初探事件系统
- 基础篇 - 6.Capability系统
- 基础篇 - 7.创建一个方块实体
- 基础篇 - 8.你的第一个GUI
- 基础篇 - 9.网络与通讯
- 进阶篇 - 0.更复杂的Mod
- 进阶篇 - 1.Minecraft渲染原理
- 进阶篇 - 2.更复杂的GUI
- 进阶篇 - 3.物品进阶
- 高级篇 - 1.深入探索OpenGL
- 高级篇 - 1.1OpenGL颜色渲染
- 高级篇 - 1.2OpenGL光照系统
- 高级篇 - 2.Minecraft贴图加载原理