Verse Wiki — 写给 UE6 时代的 Verse 互动手册
拓展 · EXTRA

给开门加计分:score_manager_device 实战

毕业作品最自然的升级方向:谁按下按钮,谁得分。这一页用三个已核实签名的 API 把计分器接进那扇门,顺便看清一件贯穿全课的事——事件的 agent 载荷如何从按钮一路流到记分板。本文整理自官方 API 文档与社区教程。

毕业作品最自然的升级方向:谁按下按钮,谁得分。这一页用三个已核实的计分接口把计分器接进那扇门,顺便看清一件贯穿全课的事——事件带着的「是谁按的」那个玩家引脚(agent),如何从按钮一路流到记分板。本文整理自官方 API 文档与社区教程。

一、计分三件套:三个已核实的 API

score_manager_device 是关卡里的记分员。它的核心用法只有三招,签名都已对照官方 API 页核实:

score_manager_device 是关卡里的记分员。它的核心用法只有三招,每一招怎么调都已对照官方 API 页核实:

API作用备注
SetScoreAward(Score:int)设定「单次给多少分」先设额度,再发分
Activate(Agent:agent)把分数发给指定玩家参数正是事件载荷里的 agent参数正是事件带回的那个玩家引脚(agent)
GetCurrentScore(Agent:agent):int读回该玩家当前分数完整签名 GetCurrentScore<public>(Agent:agent)<reads><writes><allocates>:int调用时喂一个玩家引脚(agent),它回一个整数分数

注意 ActivateGetCurrentScore 都要一个 agent 参数——分数是发给「人」的,不是发到空气里。那这个人从哪来?正是毕业作品里我们一直没接住的那个返回值:InteractedWithEvent 的载荷类型就是 agent,也就是按下按钮的那位玩家。事件把「谁干的」一路带在身上,你在链条末端随取随用——官方把这叫事件载荷(payload),这条「从按钮到记分板」的直通管道,是事件驱动设计最漂亮的地方之一。

注意 ActivateGetCurrentScore 都要一个玩家引脚(agent)——分数是发给「人」的,不是发到空气里。那这个人从哪来?正是毕业作品里我们一直没接住的那个输出:按钮的 InteractedWithEvent 事件带回的正是一个 agent 玩家引脚,也就是按下按钮的那位玩家。事件把「谁干的」一路带在身上,你在链条末端随取随用——官方把这叫事件载荷(payload,就是事件触发时随事件一起送来的那些引脚值),这条「从按钮到记分板」的直通管道,是事件驱动设计最漂亮的地方之一。

二、接进毕业作品:接住 Await 的返回值

改动出奇地小。毕业作品 2.0 里我们写的是裸的 OpenButton.InteractedWithEvent.Await(),把返回值丢掉了;现在用 Presser := 接住它,再喂给计分器:

改动出奇地小。毕业作品 2.0 里我们只是裸等按钮事件(OpenButton.InteractedWithEvent.Await()),事件带回的「是谁按的」直接丢掉了;现在用 Presser := 把它接住(相当于从事件节点的玩家引脚拉一根线出来存着),再喂给计分器:

scored_door.verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }

# 毕业作品的计分升级:谁开门,谁得分
scored_door := class(creative_device):

    @editable
    OpenButton:button_device = button_device{}

    @editable
    Door:barrier_device = barrier_device{}

    @editable
    Scorer:score_manager_device = score_manager_device{}

    OnBegin<override>()<suspends>:void =
        Scorer.SetScoreAward(10)
        loop:
            Presser := OpenButton.InteractedWithEvent.Await()
            Door.Disable()
            Scorer.Activate(Presser)
            CurrentScore := Scorer.GetCurrentScore(Presser)
            Print("开门 +10 分,这位勇士当前 {CurrentScore} 分。")
            race:
                Sleep(5.0)
                OpenButton.InteractedWithEvent.Await()
            Door.Enable()

逐行看新东西:第三个 @editable 插槽指向关卡里的 Score Manager 设备(别忘了先把它拖进关卡再绑定);OnBegin 一上来先 SetScoreAward(10) 定好额度;主循环里 Presser := ... .Await() 接住载荷,开门之后 Activate(Presser) 发分、GetCurrentScore(Presser) 读回来打进日志。事件载荷从按钮出发,途经 Await 的返回值,最后落在记分板上——全程没有一个中间变量是多余的。

读图翻译:第三个开了小眼睛的变量指向关卡里的 Score Manager 设备(别忘了先把它拖进关卡再绑定);Event BeginPlay 一上来先调 SetScoreAward(10) 定好额度;主循环里 Presser := ... .Await() 接住「是谁按的」,开门之后调 Activate(Presser) 发分、GetCurrentScore(Presser) 读回来用 Print 打进日志。那个玩家引脚从按钮出发,途经等待事件带回的输出,最后落在记分板上——全程没有一个中间变量是多余的。

想把分数亮给玩家看而不只是躺在日志里?社区教程已经趟过路:Eisuke Kawano 的 Medium 双篇,前篇讲用 Verse 驱动 Score Manager,后篇讲把分数显示到 billboard(广告牌设备)上;Epic 社区也有一篇专讲用 Verse 读取当前分数的教程。链接都在文末。

三、社区反馈的坑:分数要从一个口子出

论坛上有开发者反馈:某些配置下,GetCurrentScore 读不到由其他途径(不是这台 score_manager_device 本身)发放的分数——比如淘汰得分、其他设备直接给的分。也就是说,它读回的数字未必等于玩家 HUD 上的总分。

社区总结的规避方式很朴素:全图的分数统一经由同一台 score_manager_device 发放。所有加分路径都走这一个口子,GetCurrentScore 的账本就永远和玩家看到的一致。这其实又是 x2 拓展页那条思路的镜像:状态(这里是分数)只留一份权威拷贝,谁都别私自记账。如果你的地图确实需要多个加分来源,至少在设计阶段就想清楚「谁是权威记分员」,别等排行榜对不上账再回头查。

四、小测验

OpenButton.InteractedWithEvent.Await() 的返回值是什么?对计分有什么用?会带回什么?对计分有什么用?

针对论坛反馈的 GetCurrentScore 读数问题,社区推荐的规避方式是?

来源与延伸

本文整理自官方 API 文档与社区教程: