Minecraft(我的世界)中文论坛

 找回密码
 注册(register)
查看: 2051|回复: 3

[插件开发教程] [上古之石]Pathfinder插件教程

[复制链接]
发表于 2018-1-21 00:43:05 | 显示全部楼层 |阅读模式

您尚未登录,立即登录享受更好的浏览体验!

您需要 登录 才可以下载或查看,没有帐号?注册(register)

x
本帖最后由 zghh008 于 2018-1-22 13:10 编辑

0.引言


其实这篇教程很早之前就想写了,但是上了高中没太多时间,现在统一写一个教程。

前段时间为了开发一个尸潮插件(ZombieComing:地址:http://www.mcbbs.net/thread-733022-1-1.html

因为我的主要目的是实现僵尸破坏方块和放置方块。所以我就想到僵尸有一个破坏门的ai,末影人有一个放置方块的ai,所以我就决定用ai来实现。于是我就系统的自己研究了一下ai的机制。

下面是一些心得。




1.先来简单介绍一下Pathfinder(AI)

Pathfinder是mc的一个内部实现,它在nms(net.minecraft.server)包下,被实现的ai一般以PathfinderGoal开头,它是一套mc自己的机制,用于实体操作。下面我简单叙述一下它的工作原理。

在一个标准的PathfinderGoal里面一共有五个基础方法。

boolean shouldExecute()

boolean continueExecuting()

void startExecuting()

void resetTask()

void updateTask()

(我相信会有人说根本没有这些方法)

在mc的AI机制中,首先它会调用shouldExecute判断是否开始这个AITask,如果返回true,则立刻调用startExecuting。在接下来的tickUpdate中,如果一个AI已经开始了,那么它就会调用continueExecuting判断是否继续,如果返回true则调用updateTask,否则则调用resetTask,然后退出这个AITask。

如果你听了很迷糊,那我就简单的举个梨子。

02.3.png

就拿铁傀儡看村民AI举个梨子。

当AI机制开始运行时,他会首先调用shouldExecute方法。很明显,第一个if是判断铁傀儡所在世界是不是白天,第二个if判断用铁傀儡的Random(随机数)来获取0-7999中任意一个是否为0(概率为1/8000),只有满足这两个条件它才会继续,最后就是获取这个铁傀儡周围的村民(具体实现的话,自己可以去研究一下,本人在这里不做阐述),当然获取村民是有范围的,与this.theGolem.boundingBox.expand(6.0D, 2.0D, 6.0D)有关(不然随便造个铁傀儡向千里之外的村民伸出玫瑰(好像叫罂粟,反正不重要!!!)),当满足最后的获取到的村民不是null时就会满足shouldExecute方法,然后返回true,紧接着调用startExecuting。

根据参数名就可以看出,设置了一个lookTime为400,并且设置伸出花花为true。调用完startExecuting后,这个AITask(AI每运行一次就是一个AITask)就被设置为已经启动了,然后就会每过1tick调用continueExecuting方法。

在continueExecuting方法中,是一个判断lookTime是不是大于0的语句。大于0就证明还可以继续看,就会返回true,小于0就不会继续看,就会返回false。

如果返回true,就会调用updateTask方法,第一个语句就是让铁傀儡看着这个村民,第二个语句就是lookTime减1(不可能让铁傀儡一直看着村民吧)。

如果返回false,就会调用resetTask方法,第一个语句就是让铁傀儡不再拿着花花,然后this.theVillager = null,将村民重置。AITask结束。

纵观一下,启动这个AI的概率大概是1s为1/400,这个AI持续的时间为20s。

(我都解释的这么清楚了,还不懂我真的....)



2.简单介绍一下如何自己写一个Pathfinder(AI)


首先你需要打开IDE,然后新建一个类,让它继承PathfinderGoal类。它会让你自动实现shouldExecute方法。

下面我就写一个蜘蛛生成网的AI。

(建议取名字以PathfinderGoal开头。我对于这个蜘蛛生成网的AI取的名字为PathfinderGoalNet)

为了只让蜘蛛有生成网的功能,我就要写一个参数为蜘蛛的构造函数。

注意:这个AI的实体选择可以使用nms的实体也可以使用BukkitAPI,我将以BukkitAPI的实体作为例子,因为毕竟BukkitAPI的实体比较容易操作。但是我在构建函数里面写的仍是nms下面的Spider,因为我要对于类似前面的Random操作。

类似于前面的随机方法,我将获得这个实体的Random来设置一个几率。当然我还要判断蜘蛛是否在地上(在空中织网你们不觉得有点怪么)。

fff.png

注意:这里肯定有人问我为什么不是shouldExecute方法和getRNG方法,因为被混淆过。

接下来我就要写startExecuting了。startExecuting里面就应该写在当前位置生成一个蜘蛛网。

fff.png

(用BukkitAPI还有一个原因,你不需要去callEvent和给客户端发包)

根据思路,我们生成一个网就可以退出了,所以我们可以在continueExecuting里面返回false结束这个AITask(因为continueExecuting默认返回调用startExecute,如果不重写返回false,那么这个AITask将不会结束,最终也只会执行一次)。而resetTask和updateTask方法就可以不写了。

fff.png

这个AI就搞定了。下面我就讲一下如何注册AI。


3.注册Pathfinder

Goal(AI)

册AI是调用PathfinerSelector的a方法,因为PathfinderSelector的实例在实体内部是protected,对于我们来说是不可视的,所以必须用到反射。

fff.png

现在我来简单说一下PathfinerSelector的a方法的两个参数,第二个参数很明显是PathfinderGoal实例,不用多讲,重点是第一个int,这个是优先级的意思。数值越小优先级越高,范围应该为1到正无穷。一般确定AI优先级是由比较同类AI优先级确定。


4.下面我就举个例子来说明一下AI的具体实现。



的ZombieComing插件的破坏方块实现。因为我是按照PathfinderGoalBreakDoor的思路来写的。于是我就学着它写了一个基类,和一个具体实现类。当然注册的是具体实现的类。

fff.png

ffff.png

先从构造函数开始,一个简单的构造函数,限定该AI只能僵尸干。

shouldExecute中的this.getBlock方法是我自己写的获取它周围方块的方法,然后判断是否为null如果不为null那么就会进行配置AITask,并开始AITask,如果为null则不会开始。配置主要是获取了Block的掉落物品和Block的Bukkit实例以及Block的BlockPosition(就是它的x,y,z值)。

在startExecuting方法中配置了isStop为false(判断是否停止),然后计算了一下x和y的值(具体作用我也忘了,我当时是按照BreakDoor来写的,大概是和updateTask一起判断这个方块是不是僵尸以及移除了(判断依据大概是僵尸可以到达那个位置,如果实在不懂也不必深究,因为这不重要,写AI是学结构和思想。))。

在continueExecuting中,是判断isStop是否为true如果是那么就结束AITask。

在updateTask方法中写了满足某种条件(上面已经解释了)将isStop设置为true(主要是为了后面实现这个类的类判断操作)。

接下来我们来看具体实现的类

fffff.png

ffffffff.png

构造函数这里写的Object是为了兼容版本。

shouldExecute方法是两个if判断,一个是判断父类的shouldExecute方法是否成立,也就是前面已经讲过的获取方块,基本配置。第二个是判断这个世界的GameRule是否允许Mob对地形造成修改。如果允许,那么才继续(如果这个rule不允许,僵尸不能破坏门,苦力怕不能破坏方块,末影人也一样,我设置这个判定主要是合乎mc原版机制)。

startExecuting就是调用父类的startExecuting然后再将breakTime设置为0,breakTime主要是用于控制破坏时间的。

continueExecuting是判断breakTime是否小于等于30,如果大于就结束AITask,用于控制破坏时间。

updateTask是调用父类的updateTask,然后breakTime加1,这里的breakSit是破坏程度,因为破坏方块不都有一种特殊的视觉效果么,这里获取的breakSit就是为了确定不同的视觉效果。紧接着判断视觉效果和域内的是否相等,不相等就发一个视觉包。然后设置域内的breakSit等于目前的breakSit。然后判断breakTime是否等于30,即马上结束了,然后就手动callEvent,如果被cancel掉了就调用startExecuting重新破坏(这个属于Event机制,不必了解,我这里并没有使用BukkitAPI,如果使用BukkitAPI代码将简洁很多),然后就是破坏方块,掉落破坏后的物品。

resetTask就是调用父类的resetTask,注意这里因为我前面那个类并没有写resetTask,所以他调用的是PathfinderGoal的resetTask,但是这个方法里面啥都没有。说白了,其实这个语句没有用。然后就发了一个PacketPlayOutBlockBreakAnimation包结束。


5.谈谈使用Pathfinder(AI)


说了这么多,想必你们也明白了吧。其实这些AI如果不写PathfinderGoal同样也行。可以自己注册事件,自己写个Runnable同样也可以搞定。但是通过PathfinderGoal能够更快的处理。因为这是一套mc自己的机制。是它自己一直都在运行的。具有高效性。当然它也是一种内部实现,更能让你了解mc如何工作。

但是使用Pathfinder也要注意兼容问题。毕竟每个版本nms的包名是不一样的。所以建议各位在食用的时候注意兼容(目前一般使用兼容的方式为ASM艹字节码,还有就是接口实现。我的Zombiecoming插件即为接口实现,这里要感谢莫老指教。@602723113 )。









来自群组: 上古之石美工工作组

评分

参与人数 7人气 +18 金粒 +140 贡献 +2 收起 理由
熄灭的火把 + 3 MCBBS有你更精彩~
20050221yandasu + 1 + 20 MCBBS有你更精彩~
ustc_zzzz + 4 + 50 + 2 MCBBS有你更精彩~
1582952890 + 3 MCBBS有你更精彩~
810587921 + 3   
ablu2g + 2 + 40 MCBBS有你更精彩~
迷之芒果 + 2 + 30 神乎其技,不服不行!

查看全部评分

发表于 2018-2-2 17:02:48 来自手机 | 显示全部楼层
感谢lz,正好需要
真是雪中送炭,要不然不知道要查多少个API
能不多几个例子呢
例如玩家喂给猪人一些肉,然后猪人跟者玩家,帮玩家打怪,砍树。希望给一下,1294243258@qq.com
人气送你
回复

使用道具 举报

 楼主| 发表于 2018-2-3 12:38:25 | 显示全部楼层
20050221yandasu 发表于 2018-2-2 17:02
感谢lz,正好需要
真是雪中送炭,要不然不知道要查多少个API
能不多几个例子呢

谢谢建议。
回复

使用道具 举报

发表于 2018-6-10 19:31:10 | 显示全部楼层
大佬啊牛逼2323
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册(register)

本版积分规则

Archiver|小黑屋|Mcbbs.net ( 京ICP备15023768号-1 ) | 手机版本

GMT+8, 2018-10-21 17:47 , Processed in 0.063745 second(s), 9 queries , Memcache On.

"Minecraft"以及"我的世界"为Mojang Synergies AB的商标。本站与Mojang以及微软公司没有从属关系。

© 2010-2017 我的世界中文论坛 版权所有。本站原创图文内容版权属于原创作者,未经许可不得转载。

快速回复 返回顶部 返回列表