技巧 · EXTRA
自己动手实现 Split
标准库没有 Split?社区早就写好了轮子——一个函数把本课全部技能串起来练。
进入拓展 →数字会算账,还得有人会播报。这一课把 string 拆开讲透:双引号里的台词、花括号 {X} 的实时填空魔法、加号拼接,以及用 Print 打调试日志的艺术——顺便拆掉「大括号转义」这个新手必踩的地雷。
游戏里到处是文字:玩家名、任务描述、击杀播报、商店里那句「装备已售罄」。在 Verse 中,这些文字都装在 string 类型里。写一段 string 最直接的方式是「字面量」(蓝图里没有这个词,就是你在文本框里直接敲进去的那句话,不接任何计算节点)——把台词原样放进一对双引号:
using { /Fortnite.com/Devices }
npc_device := class(creative_device):
# 常量 string:开场白定了就不改
Greeting:string = "欢迎来到新手竞技场"
# var string:称号会随战绩变化
var Title:string = "见习勇者"
读图翻译:这段等于新建一个蓝图类(基于 creative_device),在变量面板里加两个 string 变量——Greeting 是常量(不给它接 Set 节点,值就焊死),Title 允许中途改(后面能接 Set 节点)。
双引号里几乎什么都能放:中英文、数字、标点、空格,统统合法。上一课的规则在这里照常生效——不写 var 就是挂牌即焊死的常量,写了 var 以后改动时要喊 set 口令。上一课的规则在这里照常生效——从没给它接过 Set 节点、值焊死不改的就是常量;想让它中途变值的,改的时候接一个 Set 节点把新值送进去(这道「口令」在 Verse 里写作 set)。
顺带认识一位小个子亲戚:char,单个字符类型,字面量用单引号,比如 'e'。事实上 string 在 Verse 里的真身就是 []char——一串字符排成的数组,这个身世会在后面数组课和本课的拓展页里派上大用场。现在只要记住:双引号是一句话,单引号是一个字。
固定台词是死的,好日志得能报出变量的实时数值。Verse 的办法优雅极了:在字符串里写一对花括号,把变量名放进去——Print("Score: {Score}")。打印的一瞬间,{Score} 会被换成格子里的真实内容。这招叫「字符串插值」:台词挖个空,Verse 帮你实时填好。
更妙的是,填进去的不必是 string:int、float 也能直接插。Verse 会在幕后调用该类型的 ToString() 函数,把数字翻译成文字再嵌进句子。花括号里甚至可以写表达式,比如 {Coins * 2}——只有一条红线:可失败的表达式不能直接插(比如数组下标取值),得先在 if 里把值安全地取出来,再插那个结果。这条红线我们在「常见坑」里细说。
更妙的是,填进去的不一定是文字:整数(int)、小数(float)也能直接塞进花括号——Verse 会在幕后自动把数字转成文字再嵌进句子,不用你手动转换。花括号里甚至能放一小段计算,比如 {Coins * 2}。只有一条红线:「有可能失败的取值」不能直接插(比如按下标从数组里取一个元素,万一越界就取不到),得先用一个 Branch(Verse 里写成 if)确认能取到、把值稳稳拿出来,再插那个结果——只有这条线走得通才继续。这条红线我们在「常见坑」里细说。
下面这台「播报员」把常量、变量、插值、转义一次串起来。点「运行下一步」,亲眼看每行代码干了什么。
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }
announcer_device := class(creative_device):
HeroName:string = "夜行者"
var Coins:int = 0
OnBegin<override>()<suspends>:void =
Print("欢迎回来,{HeroName}!")
set Coins += 30
Print("钱包:{Coins} 金币")
Print("按 \{E\} 打开背包")
点「运行下一步」,看代码怎么一行行执行。
注意最后一行:想打印真正的花括号,必须写 \{ 和 \}。因为花括号在字符串里是保留给插值的——这正是本课头号大坑,第四节专门审判它。
两段文字合成一段,用的就是最朴素的加号。string 的 + 是「接在后面」:"Hello " + Name 得到一句完整的问候;配合 set配合 Set 节点 还能不断往后追加,这在逐步拼一句长播报时特别顺手:
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }
concat_device := class(creative_device):
var Report:string = "战报:"
OnBegin<override>()<suspends>:void =
Kills := 3
set Report += "击杀 {Kills} 人,"
set Report += "存活到最后"
Print(Report)
Print("战报共 {Report.Length} 个字符单元")
读图翻译:游戏一开始(Event BeginPlay),先把 Report 起头设成「战报:」、把 Kills 记成 3;接着两次用 Set 节点往后接文字(+= 就是拿旧值接上新段再塞回去);最后用两个 Print String,分别打出整句战报、以及这句话的长度。
最后一行偷偷剧透了 string 的数组身世:既然它是 []char,那数组的 .Length 它也有,返回一个 int(注意:对中文这类多字节字符,计数按编码单元算,可能比肉眼字数多——拓展页有详解)。
再来认识字符串里的「暗号系统」——反斜杠转义。有些字符没法直接敲进字面量,得用反斜杠开头的暗号来表示:
| 写法 | 得到 | 什么时候用 |
|---|---|---|
\{ 和 \} |
{ 和 } | 想打印真花括号,而不是插值 |
\" |
" | 台词里带引号:他说 \"冲!\" |
\\ |
\ | 想打印反斜杠本身 |
\n |
换行 | 一条日志分成两行 |
\t |
制表符 | 日志列对齐 |
规律很好记:凡是「在字符串里有特殊身份」的字符——花括号管插值、双引号管边界、反斜杠管暗号——想按原样打印,就在前面加一个 \,相当于跟编译器说「这个字免检,照原样放行」。
Print(蓝图里的 Print String 节点) 是你在 UE6 里最忠实的侦察兵:代码行为不对劲时,在关键位置插几句 Print,日志会把「程序到底走到了哪、变量到底是几」原原本本报给你。几条老兵经验:
▢ 带上前缀。Print("[商店] 余额:{Coins}") 比光秃秃的数字好查得多——日志一多,没名字的输出等于没输出。
◇ 打「前后对比」。改动一个变量前后各打一句,状态变化一目了然,比盯着代码猜快十倍。
△ 用完就撤。调试 Print 是脚手架,功能验收后删干净,别让正式版日志刷屏。
接下来是本课的「坑位巡逻」,四个坑个个都是新手高频事故:
坑一:Print(42) 编译不过。Print 只收 string,int、float 一概拒收。想打印数字,请让插值当翻译:Print("{42}")、Print("血量:{Health}")。忘了这条,编译器会用类型不匹配的错误提醒你。
坑一:直接 Print 一个数字会亮红。Print String 的输入引脚只认文字(string),你把整数、小数直接连进去,引脚类型对不上,点 Compile 就报错。想打印数字,让插值当翻译:Print("{42}")、Print("血量:{Health}")——把数字包进花括号,Verse 自动转成文字。忘了这条,Compile 会用「类型不匹配」的红字提醒你。
坑二:裸写花括号,编译器当场翻脸。Print("按 {E} 互动") 看着无辜,但编译器认定 {E} 是插值,转头去找变量 E——找不到就报错;就算真有个 E,打印的也是它的值而不是「{E}」三个字符。想要真花括号,老老实实 \{E\}。
坑二:裸写花括号,一点 Compile 就翻脸。Print("按 {E} 互动") 看着无辜,但 Verse 认定 {E} 是要填空的插值,转头去找一个叫 E 的变量——找不到就报红;就算真有个 E,打出来的也是它的值,而不是「{E}」这三个字符。想要真花括号,老老实实写 \{E\}。
坑三:可失败表达式不能直接插。"{MyString[0]}" 这种写法编译不过——数组下标可能失败,而插值不给失败留后路。正确姿势是先掷骰再报数:if (First := MyString[0]) { Print("首字符:{First}") }。
坑三:「可能失败的取值」不能直接插。"{MyString[0]}" 这样写会亮红——按下标从数组里取字符有可能扑空(越界),而插值不接受「万一失败」的情况。正确姿势是先用一个 Branch(Verse 写成 if)确认取得到,再报数:if (First := MyString[0]) { Print("首字符:{First}") }——只有这条线走得通,才会往下打印。
坑四:float 插出来自带六位小数。"{1.5}" 打印的是 1.500000——float 的 ToString 固定六位小数,做 UI 文本时尤其扎眼,需要手动格式化(上一课的拓展页里有四舍五入配方)。
坑四:小数(float)插出来自带六位小数。"{1.5}" 打印的是 1.500000——Verse 把小数转成文字时固定补足六位小数,做 UI 文本时尤其扎眼,需要手动格式化(上一课的拓展页里有四舍五入配方)。
最后一条预防针:Verse 标准库的字符串工具箱非常克制,没有 Split、Replace、Contains、ToUpper 这些别的语言里的老朋友。缺的轮子社区早就造好了——见本课拓展页。
最后一条预防针:Verse 的字符串工具箱非常克制,没有 Split、Replace、Contains、ToUpper——这些你在蓝图 String 节点库里随手就有的节点,Verse 标准库统统没内置。缺的轮子社区早就造好了——见本课拓展页。
光看不练假把式。下面这台「升级播报机」被挖掉了三个关键点:一处插值、一处拼接符号、一处花括号转义。把它们填回去,再点「检查答案」。
读图翻译:开局(Event BeginPlay)先用 Set 节点把 Level 加 4,剩下三个空就是本课三件套——插值、拼接、转义——各补一处。
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }
levelup_device := class(creative_device):
var Level:int = 1
OnBegin<override>()<suspends>:void =
set Level += 4
# 空 1:用插值报出 Level(花括号 + 变量名)
Print("当前等级:____")
# 空 2:把两段文字接成「夜行者大人」
Title := "夜行者" ____ "大人"
Print("获得称号:{Title}")
# 空 3:转义左花括号,打印出「{VIP}」
Print("____VIP\}")
台词功底验收时间。答错零惩罚,可以一直重试。
想把变量 Kills(int 类型)打进日志,写了 Print(Kills),会发生什么?
想在日志里打印出真正的一对花括号 {},正确的写法是?
Print("{1.5}") 会打印出什么?