Skip to content

Latest commit

 

History

History
144 lines (89 loc) · 7.34 KB

7-3.md

File metadata and controls

144 lines (89 loc) · 7.34 KB

7-3 记分板显示

图片选自网络。

SCB

这就是记分板,在单人游戏中几乎看不到(除非有意创建),能够显示玩家的分数,在原版中也可以通过命令进行修改。

能够显示玩家的分数。

上面这个说法具有误导性,正确的说法是:

能够显示有样式的文本和数字。

记分板中不支持 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 中。数字几乎没什么用(除非你是真的在显示「分数」),数字越大该项就越靠上,通常用于排序。

如果要显示不止一项,那就再来一次 getScoresetScore 就可以了。

最后向玩家打开这个 Scoreboard

// 此处举的是从事件获取玩家
e.getPlayer().setScoreboard(scb);

这样记分板就设置好了。

效果大概是这样的:

FINAL

设置记分板时有些注意事项:

  • 记分板的名称以及 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 的!

这里唯一需要注意的就是,创建分数时是通过 ObjectivegetScore 方法完成的,而移除分数时则是通过调用 ScoreboardresetScores 方法,向其中传入设置时的标题(包括样式)完成的。

如果要修改一个记分「块」的标题,可以这样做:

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 类。

这就是有关记分板的全部内容了,记分板是个有用的东西,值得好好利用。