跳转到内容

13. 按一下,灯亮了

约 35 分钟 难度:新手 动手章

第二部的房间里有一颗暖色 GlowBall,Inspector 里 Light 默认一直开着。这一章做按一下切换开关。

这个开关动作背后只有一个事件:Interact。VRChat 里几乎所有「走到 X 前面按一下」的交互都是它。这一章把这条最小回路打通:放一个按钮,写一段五行脚本,按一下,灯切换。

一个 Interact 交互需要凑齐三件东西

Section titled “一个 Interact 交互需要凑齐三件东西”

Interact 不是凭空生效。VRChat 客户端要知道「这个物体能被按、按了之后跑哪段逻辑、提示文字写什么」。要凑齐三件东西:

  1. 一个带 Collider 的 GameObject,让 VRChat 知道光标该停在哪。
  2. 一段 UdonSharp 脚本,挂在同一个 GameObject 上,里面有一个 Interact 方法。
  3. Udon Behaviour 的 Interaction Text 字段填一句提示,玩家准星附近才会出现那行小字(写动作如 Press,不写具体按键;键位各人可在 VRChat 的 Settings → Controls 里改)。

三件齐了才能按。少哪一个,按下去都没反应,下面会一一对照。

打开第二部那间小屋的场景。Hierarchy 空白处右键 3D ObjectCube,命名 LampSwitch

选中 LampSwitch,Inspector 里把 Transform 调成:

Position -1.5, 1.0, -2
Rotation 0, 0, 0
Scale 0.3, 0.3, 0.1

得到一块大概手掌大、贴在墙上的扁方块。位置数值不必照搬,原则是:站在房间里能看见、走得到、不挡路。

可以顺手给它做一个稍微亮一点的材质(按第 9 章的步骤新建 Mat_Switch_Brass,Albedo 设为 #C49A6C),让它从墙上跳出来一点。这一步不是必须的。

第 11 章用过这个脚本骨架。这一章把它当成模板,改两个字。

Project 面板的 Assets 下,如果还没有 Scripts 文件夹就新建一个。进入 Scripts 文件夹,右键 CreateU# Script,命名 LampToggle

双击打开它,把内容替换成:

LampToggle.cs
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) 就对了。

Inspector 里检查 LampSwitch 上的 Box Collider:默认就有一个,Cube 自带的,这次刚好用得上。Is Trigger 保持 不勾选。这一章用普通 Collider,下一章再勾。

Udon Behaviour 区域里找到 Interaction Text 字段,把默认值改成:

Press

桌面玩家的视角里,光标对准开关时头顶会浮出 Press,VR 玩家在控制器上方看到同样的字。公开给全球玩家用的世界,常用英文短词(PressToggleOpen);只给中文圈朋友测时可以写中文,但外国玩家看不懂头顶提示。

Proximity 字段保持默认 2(米)。意思是玩家离开关 2 米以内才能交互。想让按钮远距离也能按再调高它。

VRChat SDKShow Control Panel → Builder 标签 → Build & Test

进世界后走到 LampSwitch 前两米内,光标对准它,头顶提示 Press。对开关发起一次 Interact,暖光熄灭。再 Interact 一次,亮起来。

按一下,灯亮了。

走两步退到 3 米外,提示消失:超出 Proximity 距离了。回头再走近,提示重新出现。

桌面或 VR,按下那一刻 VRChat 客户端在本地查找:注视/触碰的物体、上面挂的 UdonSharpBehaviour、里面的 Interact 方法,本地调用它。

这件事拉朋友进来才看得清楚:

  • 拉朋友进同一个测试世界。
  • 按一下开关,本地的灯灭了。
  • 朋友那边的灯还亮着。

不是 bug。Interact 默认只在按下的人身上跑,灯也只在按下的人客户端切换。两个人按各自看到自己那一半的状态。

要让所有玩家看到同一个状态,得显式把这条信息广播出去。这是「网络同步」的活,第六部《和别人一起》一整部专门讲。先把单人版做扎实。

回 Unity 改几样东西,重新 Build & Test 一次:

  • Interaction TextPress 改成 开灯按一下。中文也行。
  • Proximity2 改成 0.5,进去会发现要凑得很近才能按。
  • LampSwitch 复制一份放到房间另一头,把它的 Target Light 拖到另一盏灯上。一份脚本,两个开关,各管各的灯。两个 Target Light 槽各指各的灯,因为 Inspector 引用是显式拖的。

把这段五行脚本当骨架,往里加一点:

  • 让按钮按下后,灯亮三秒自动灭。提示:用第 12 章提到的 SendCustomEventDelayedSeconds,搜一下「延时事件」那一节。
  • 加一个 int pressCount 字段,每按一次自加一。第三次时不切换灯,而是把灯颜色变成红色。
  • targetLight.enabled = ... 换成 targetMesh.enabled = ...MeshRenderer 也有 enabled 字段),做一个能让某个物体瞬间消失的开关。

这三个变体不给完整代码。如果改到一半卡住了,回头读这一章和第 11 章,答案分散在那两个文件里。第二个变体是最容易写歪的,因为 if (pressCount % 3 == 0)if (pressCount == 3) 行为完全不同,留给读者自己想。

回头看,整个 Interact 链条只有四件东西:

  • 一个挂在物体上的 UdonSharpBehaviour,里头有 public override void Interact()
  • 同一个物体上的一个 Collider,决定 VRChat 在哪儿能「瞄准」它。
  • Udon Behaviour 的 Interaction Text,决定提示显示什么。
  • 脚本里通过 public 字段引用到的目标对象(这一章是那盏灯)。