图片选自网络。
这就是记分板,在单人游戏中几乎看不到(除非有意创建),能够显示玩家的分数,在原版中也可以通过命令进行修改。
能够显示玩家的分数。
上面这个说法具有误导性,正确的说法是:
能够显示有样式的文本和数字。
记分板中不支持 BaseComponent
的部分功能(包括最重要的 setClickEvent
),因此记分板通常只被用来显示数据,而不被用来制作 GUI。要制作 GUI,可以考虑使用之前提到过的聊天栏原始 JSON 文本(BaseComponent
)或者物品栏 GUI。由于 BaseComponent
可以用在成书中,也可以考虑在书本中写入 BaseComponent
来制作可点击的书页等等。
这里我们只介绍如何用记分板显示内容。
Bukkit 中有关记分板的内容都位于 org.bukkit.scoreboard
包下。
首先我们来了解一下记分板的结构。
- 一个玩家只能拥有一个记分板(
Scoreboard
),这是一个抽象的概念,玩家看不到 - 一个记分板中包含了许多「块」(
Objective
),「块」是真正显示在屏幕上的那个矩形 - 一个记分板中含有许多「项」(
Score
) - 一个「项」由名称和分数组成
大致就是这样的原理。
要创建记分板,首先我们需要获取记分板管理器(ScoreboardManager
),这可以通过 Bukkit.getScoreboardManager
获得。
ScoreboardManager manager = Bukkit.getScoreboardManager();
然后利用 getNewScoreboard
方法创建一个新的记分板:
Scoreboard scb = manager.getNewScoreboard();
这个记分板还是空的,里面没有「块」,我们来添加一个:
Objective obj = scb.registerNewObjective(
"ID",
"dummy",
ChatColor.GOLD + "" + ChatColor.BOLD + "闪亮的名字"
);
这里有三个参数,第一个是内部名称,也就是代码中识别该「块」的名字,不会给玩家显示。
第二个是记分类型,Minecraft 提供了大量的类型用于进行各种自动记分,但那是针对原版的,现在我们有了 Java,还要这东西干啥?dummy
就是「不自动记分」,关闭这个功能。
第三个是「块」的标题,这里只能使用 org.bukkit.ChatColor
进行一些点缀,似乎并不能设置点击事件。记住,颜色放在格式前面,更改颜色时要再写一次格式(即使格式没有变化)!
然后我们为这个「块」设置显示位置:
obj.setDisplaySlot(DisplaySlot.SIDEBAR);
唯一的参数有三个选择:
SIDEBAR
,给玩家自己看的,显示在屏幕右侧BELOW_NAME
,给其它玩家看的,显示在其它玩家名字的下方(头的上方),在 NPC 制作中比较有用PLAYER_LIST
,没多大用,在玩家列表中显示,这个连记分项都没有的说……
所以一般我们都使用 SIDEBAR
来向(第一人称)玩家显示一些信息,或者用 BELOW_NAME
来设置一些 NPC 头顶上的文字。不论哪种,后面的设置方法都是一样的。
接下来我们在「块」(Objective
)中创建「项」(Score
)。一个分数就是一项。
Score sc = obj.getScore(ChatColor.AQUA + "文本内容");
sc.setScore(999);
getScore
获得指定名字对应的 Score
对象,如果没有,它就会创建对应的。
setScore
设置后面的数字,是就地修改,并且修改会自动应用到 Objective
中。数字几乎没什么用(除非你是真的在显示「分数」),数字越大该项就越靠上,通常用于排序。
如果要显示不止一项,那就再来一次 getScore
和 setScore
就可以了。
最后向玩家打开这个 Scoreboard
:
// 此处举的是从事件获取玩家
e.getPlayer().setScoreboard(scb);
这样记分板就设置好了。
效果大概是这样的:
设置记分板时有些注意事项:
- 记分板的名称以及
getScore
的标签长度不能超过 40 个字符,否则会出错。 - 创建记分板的全过程都需要同步进行,如果你是在异步事件处理或者类似的可能不是同步的地方,必须使用
new BukkitRunnable() {...}.runTask(插件主类名.instance)
创建一个新的线程进行同步执行。 - 如果通过
getNewScoreboard
创建了新的记分板,最后一定要记得给玩家setScoreboard
来使记分板生效!
getScore
设置的分数项文本,一经设定是无法修改的。只有右边的数字可以修改(setScore
)。
那如果真的需要修改呢?那就把原来的删掉,换个新的呗……演示代码:
scb.resetScores(ChatColor.AQUA + "文本内容");
// 调用的是 Scoreboard 的 resetScores,不是 Objective 的!
// 参数是 getScore 时用的文本内容,需要原封不动地传入,「有借有还,还的时候要和借来的时候一样」
Score sc2 = obj.getScore(ChatColor.RED + "新的文本"); // 这里才是 Objective 的!
这里唯一需要注意的就是,创建分数时是通过 Objective
的 getScore
方法完成的,而移除分数时则是通过调用 Scoreboard
的 resetScores
方法,向其中传入设置时的标题(包括样式)完成的。
如果要修改一个记分「块」的标题,可以这样做:
obj.setDisplayName(ChatColor.BLUE + "新的名称");
虽然这里我只使用了一个颜色,但实际使用时可以为每一个字(或者连续一些字)设置颜色样式,也可以不使用样式。顺便说一下,ChatColor.RESET
用于恢复到默认样式。
?> 小技巧
一般在设计 RPG 插件时,我们会创建两个 Objective
,一个显示给玩家(DisplaySlot.SIDEBAR
),一个显示在玩家头上给别人看(DisplaySlot.BELOW_NAME
)。
如果仅仅想把记分板用作显示信息的工具,那就只需要显示给玩家的那一个。
那么头上的文字有什么用呢?显示玩家血量,显示玩家的「职业」(有些 RPG 中会涉及),显示称号等等……用处可大了!
最后,要注销一个记分「块」(Objective
)并移除其中的所有数据,可以使用:
obj.unregister();
这里调用的也是 Objective
对象的方法。
?> 一点小问题
上面这些代码的运行都很正常,但如果你频繁刷新显示的名称(例如,将记分项用作计时器,每秒更新一次时间显示),记分板就会闪烁,这是一边擦除一边设置的结果。
这个问题实际上有解决方案,但是如果要细说,我们还要牵扯 Team
在内的一堆东西,本小马自己也不是很了解,为了避免误导各位,我就暂时不讲了。
不过大家别沮丧,我在 RarityCommons 中添加了这个问题的解决方案(这是我后来加上的,6-3 中还没有实现这个功能),这个方法来自 Spigot 论坛,我稍微进行了一些修改,你可以查看 RarityCommons 的源代码,看看其中的 ScoreHelper
类。
这就是有关记分板的全部内容了,记分板是个有用的东西,值得好好利用。