13. 按一下,灯亮了
第二部的房间里有一颗暖色 GlowBall,Inspector 里 Light 默认一直开着。这一章做按一下切换开关。
这个开关动作背后只有一个事件:Interact。VRChat 里几乎所有「走到 X 前面按一下」的交互都是它。这一章把这条最小回路打通:放一个按钮,写一段五行脚本,按一下,灯切换。
一个 Interact 交互需要凑齐三件东西
Section titled “一个 Interact 交互需要凑齐三件东西”Interact 不是凭空生效。VRChat 客户端要知道「这个物体能被按、按了之后跑哪段逻辑、提示文字写什么」。要凑齐三件东西:
- 一个带 Collider 的 GameObject,让 VRChat 知道光标该停在哪。
- 一段 UdonSharp 脚本,挂在同一个 GameObject 上,里面有一个
Interact方法。 - Udon Behaviour 的
Interaction Text字段填一句提示,玩家准星附近才会出现那行小字(写动作如Press,不写具体按键;键位各人可在 VRChat 的 Settings → Controls 里改)。
三件齐了才能按。少哪一个,按下去都没反应,下面会一一对照。
第一步:在房间里立一个开关
Section titled “第一步:在房间里立一个开关”打开第二部那间小屋的场景。Hierarchy 空白处右键 3D Object → Cube,命名 LampSwitch。
选中 LampSwitch,Inspector 里把 Transform 调成:
Position -1.5, 1.0, -2Rotation 0, 0, 0Scale 0.3, 0.3, 0.1得到一块大概手掌大、贴在墙上的扁方块。位置数值不必照搬,原则是:站在房间里能看见、走得到、不挡路。
可以顺手给它做一个稍微亮一点的材质(按第 9 章的步骤新建 Mat_Switch_Brass,Albedo 设为 #C49A6C),让它从墙上跳出来一点。这一步不是必须的。
第二步:写一段五行脚本
Section titled “第二步:写一段五行脚本”第 11 章用过这个脚本骨架。这一章把它当成模板,改两个字。
Project 面板的 Assets 下,如果还没有 Scripts 文件夹就新建一个。进入 Scripts 文件夹,右键 Create → U# Script,命名 LampToggle。
双击打开它,把内容替换成:
using UdonSharp;using UnityEngine;
public class LampToggle : UdonSharpBehaviour{ public Light targetLight;
public override void Interact() { targetLight.enabled = !targetLight.enabled; }}存盘(Ctrl+S)回到 Unity,等编译完成。Console 没红字才算通过。
逐行扫一眼,每一行都干一件具体的事:
using UdonSharp;引入 UdonSharp 的基础类型,包括UdonSharpBehaviour。using UnityEngine;引入Light这种 Unity 自带类型。public class LampToggle : UdonSharpBehaviour声明一个能挂到 GameObject 上的脚本类。public Light targetLight;声明一个Light类型的字段,public让它在 Inspector 里露出一个引用槽。public override void Interact()重写 VRChat 规定的Interact事件方法。玩家对物体发起 Interact(交互键,键位在 VRChat Settings → Controls)时,VRChat 调用它。- 方法体里那一行
targetLight.enabled = !targetLight.enabled;把灯的enabled状态翻转。!是「取反」:true 翻成 false,false 翻成 true。
第三步:把脚本挂上去,把灯拖进引用槽
Section titled “第三步:把脚本挂上去,把灯拖进引用槽”回到 Hierarchy,选中 LampSwitch。Inspector 底部 Add Component → 搜 Lamp Toggle(或者直接把 Project 面板里的 LampToggle.cs 拖到 Inspector 上)。
挂上之后,Inspector 会出现一个 Udon Behaviour 区域,里头有一个 Target Light 字段,值是 None (Light)。
把 Hierarchy 里的 GlowBall 拖到这个 Target Light 槽里。槽里写出 GlowBall (Light) 就对了。
第四步:配 Collider 和提示文字
Section titled “第四步:配 Collider 和提示文字”Inspector 里检查 LampSwitch 上的 Box Collider:默认就有一个,Cube 自带的,这次刚好用得上。Is Trigger 保持 不勾选。这一章用普通 Collider,下一章再勾。
Udon Behaviour 区域里找到 Interaction Text 字段,把默认值改成:
Press桌面玩家的视角里,光标对准开关时头顶会浮出 Press,VR 玩家在控制器上方看到同样的字。公开给全球玩家用的世界,常用英文短词(Press、Toggle、Open);只给中文圈朋友测时可以写中文,但外国玩家看不懂头顶提示。
Proximity 字段保持默认 2(米)。意思是玩家离开关 2 米以内才能交互。想让按钮远距离也能按再调高它。
第五步:Build & Test
Section titled “第五步:Build & Test”VRChat SDK → Show Control Panel → Builder 标签 → Build & Test。
进世界后走到 LampSwitch 前两米内,光标对准它,头顶提示 Press。对开关发起一次 Interact,暖光熄灭。再 Interact 一次,亮起来。
按一下,灯亮了。
走两步退到 3 米外,提示消失:超出 Proximity 距离了。回头再走近,提示重新出现。
Interact 是完全本地的
Section titled “Interact 是完全本地的”桌面或 VR,按下那一刻 VRChat 客户端在本地查找:注视/触碰的物体、上面挂的 UdonSharpBehaviour、里面的 Interact 方法,本地调用它。
这件事拉朋友进来才看得清楚:
- 拉朋友进同一个测试世界。
- 按一下开关,本地的灯灭了。
- 朋友那边的灯还亮着。
不是 bug。Interact 默认只在按下的人身上跑,灯也只在按下的人客户端切换。两个人按各自看到自己那一半的状态。
要让所有玩家看到同一个状态,得显式把这条信息广播出去。这是「网络同步」的活,第六部《和别人一起》一整部专门讲。先把单人版做扎实。
调整两个值,看看变化
Section titled “调整两个值,看看变化”回 Unity 改几样东西,重新 Build & Test 一次:
- 把
Interaction Text从Press改成开灯或按一下。中文也行。 - 把
Proximity从2改成0.5,进去会发现要凑得很近才能按。 - 把
LampSwitch复制一份放到房间另一头,把它的Target Light拖到另一盏灯上。一份脚本,两个开关,各管各的灯。两个Target Light槽各指各的灯,因为 Inspector 引用是显式拖的。
自己改两个版本
Section titled “自己改两个版本”把这段五行脚本当骨架,往里加一点:
- 让按钮按下后,灯亮三秒自动灭。提示:用第 12 章提到的
SendCustomEventDelayedSeconds,搜一下「延时事件」那一节。 - 加一个
int pressCount字段,每按一次自加一。第三次时不切换灯,而是把灯颜色变成红色。 - 把
targetLight.enabled = ...换成targetMesh.enabled = ...(MeshRenderer也有enabled字段),做一个能让某个物体瞬间消失的开关。
这三个变体不给完整代码。如果改到一半卡住了,回头读这一章和第 11 章,答案分散在那两个文件里。第二个变体是最容易写歪的,因为 if (pressCount % 3 == 0) 和 if (pressCount == 3) 行为完全不同,留给读者自己想。
一段最小交互的骨架
Section titled “一段最小交互的骨架”回头看,整个 Interact 链条只有四件东西:
- 一个挂在物体上的 UdonSharpBehaviour,里头有
public override void Interact()。 - 同一个物体上的一个 Collider,决定 VRChat 在哪儿能「瞄准」它。
- Udon Behaviour 的
Interaction Text,决定提示显示什么。 - 脚本里通过
public字段引用到的目标对象(这一章是那盏灯)。