VRChat编程速查手册

收录Udon编程、Unity编辑器扩展、网络链接交互等内容、便于协助构建World、Avatar、或其他玩法。

由于部分代码无法通用,会进行以下标注区分

[World]、[Avatar]、[通用]

Udon奇葩报错收集

这里收集了我遇到过的麻烦的报错,已开放权限,大家有其他报错也可以在这里补充~

请将已找到解决办法的报错在这里追加,方便后来的小伙伴查询,没有找到解决办法的建议群里询问。

1,正常界面无报错,上传世界时弹框提示,世界更改无法上传。

The VRCSDK build was aborted because the IVRCSDKPreprocessSceneCallback 'AssignSceneNetworkIDs' reported a failure.

控制台和Builder提示报错:

UploadException: This file was already uploaded, you should make a new build

实际原因,因为某个U#脚本暂时不用了,就把对应的GameObject删了,但是脚本没删,并且没有挂到其他GameObject上,导致此报错,删除后报错消失。

此bug还会出现在删除udon脚本后,在VRCWorld中有NetworkIDs的列表中,可能会残留删除udon脚本遗留的UdonSharpBehaviour,导致此报错。

还存在已删除的脚本幽灵一般的再次生效的情况,应该是缓存问题。

总之为了避免此类bug,请尽量避免删除或创建非必要的脚本。

 

解决方法(by cheese)

VRChat SDK > Utilities > Network ID Import and Export Utility > Regenerate Scene IDs

[通用] 编辑器自动保存

防止各种意外崩溃导致未保存的内容丢失,可以按照需求删减或修改 。

使用方法:在Assets内创建一个C#脚本,删除脚本内预制的代码,将以下代码黏贴到脚本内,保存后即可生效。


#if UNITY_EDITOR

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;


[InitializeOnLoad]
public class AutoSaveScenes
{
    static AutoSaveScenes()
    {
        //当层级视图中对象层级发生变化后触发事件
        EditorApplication.hierarchyChanged += EditorApplication_hierarchyChanged;
        //当切换其他程序或切换回编辑器时触发事件
        EditorApplication.focusChanged += EditorApplication_focusChanged;
        //当编辑器播放触发事件
        EditorApplication.playModeStateChanged += DoAutoSaveScenesWhenPlay;
    }
    static int count = 0;
    /// <summary>
    /// 当层级视图中对象层级发生10次变化后触发事件
    /// </summary>
    private static void EditorApplication_hierarchyChanged()
    {
        count++;
        if (count > 10)
        {
            count = 0;
            EditorSceneManager.SaveOpenScenes();
            AssetDatabase.SaveAssets();
        }
    }
    /// <summary>
    /// 获取编辑器焦点状态
    /// </summary>
    /// <param name="obj"> false为切出编辑器,true为切回编辑器 </param>
    private static void EditorApplication_focusChanged(bool obj)
    {
        if (!obj)
        {
            EditorSceneManager.SaveOpenScenes();
            AssetDatabase.SaveAssets();
        }
    }
    /// <summary>
    /// 播放前保存
    /// </summary>
    /// <param name="state">播放状态</param>
    private static void DoAutoSaveScenesWhenPlay(PlayModeStateChange state)
    {
        if (state == PlayModeStateChange.ExitingEditMode)
        {
            EditorSceneManager.SaveOpenScenes();
            AssetDatabase.SaveAssets(); 
        }
    }
}

#endif

 


 

[World]

[World]

碰撞盒触发

简单实用,实用 Udon 现成的方法:OnCollisionEnter(Collision collision)

实现两个物体碰撞后消失,并出现第三个物体,

创建 ABC 三个默认立方体,

在 Udon 脚本中定义两个物体,将脚本挂在A物体上,

B、C物体,拖入脚本对应位置。C物体处于未激活状态,

 [SerializeField]

 private GameObject B;

 [SerializeField]

 private GameObject C;

OnCollisionEnter(Collision collision)内:

 if ( collision.gameObject == B) //当A物体碰撞检测到的物体是B

 {

     this .gameObject.SetActive(false);  

     B.SetActive(false);

     C.SetActive (true);隐藏AB显示C

 }

记得给所有物体挂上 VRC Pickup 和 VRC Object Sync ,以便拾取

using UdonSharp;
using UnityEngine;

public class boxsystem : UdonSharpBehaviour
{
    [SerializeField]
    private GameObject B;

    [SerializeField]
    private GameObject C;

    private void OnCollisionEnter(Collision collision)
    {
        if ( collision.gameObject == B)
        {
            this.gameObject.SetActive(false);
            B.SetActive(false);
            C.SetActive (true);
        }
    }

}

QQ图片20240417123519.png

QQ图片20240417123605.png

[World]

Udon 网络同步

学习案例来自 UKeyboard 包内的源码 

Udon 里进行网络同步的两种方法:

1、通过标注

此方法适合简单的不重要的数据,当网络繁忙时,旧数据有概率被新数据覆盖。

通过将udon的网络同步Attributes特性:[UdonSynced] 

标注在成员上时,Udon会定期将其值同步给每一个玩家。

在同步前会进行序列化和反序列化,

OnPreSerialization() 预序列化 

OnDeserialization() 反序列化

 具体逻辑:

当某一玩家对标注[UdonSynced] 的成员的值进行更改后,会分别在:

进行修改的玩家上触发 OnPreSerialization() 预序列化 

接收修改的玩家上触发 OnDeserialization() 反序列化

此方法需要自行写判断,判断值是否被修改。

例如:

_sendString 标注 [UdonSynced]

text 为玩家进行的修改。

则在 OnPreSerialization() 中以此判断,对 _sendString 进行修改,以便同步给其他玩家

  if (!string.IsNullOrEmpty(text) && _sendString != text)
  {
      _sendString = text; 
  } 

在OnDeserialization()中进行判断,这里_recieveString起到个中转作用,因为OnDeserialization()和OnPreSerialization() 写在同一类中,如果发生不同玩家的同时触发,可能产生意外,此处逻辑还有待改善。

  if (!string.IsNullOrEmpty(_sendString) && _sendString != text && _sendString != _recieveString)
  {
      _recieveString = _sendString;
      text = _recieveString;
      WriteUiText(text);
  } 

2、使用 SendCustomNetworkEvent() 发送网络事件

此方法适合执行重要方法,方法必定会被所有玩家执行。 

注意是让每个玩家调用这一方法,在方法内 Networking.LocalPlayer.displayName 得到的是本地玩家而不是网络上的发送方。 

学习内容均为个人学习理解,如有错误还请在评论区指出,欢迎讨论交流 作者:伊咪塔 https://www.bilibili.com/read/cv33857766/ 出处:bilibili

[Avatar]

[Avatar]

使用OSC与外部程序通讯

在VRChat中开启了OSC功能后,

设置.png


当Avatar中定义过的参数发生变化时,VRChat就会通过OSC向外部发送数据:

IP:127.0.0.1(本地IP) 9001为VRChat发出的端口,我们的程序接收这个端口的消息。

IP:127.0.0.1(本地IP) 9000是VRChat接收的端口,要给VRChat发消息通过这里。

面捕程序VRCFaceTracking也是通过此方法传递面捕的参数。

如果需要多个程序连接,要用到VOR作为路由器。

github.com/SutekhVRC/VOR

在正常连接好后,就可以读到参数了,

详细使用OSC可以参考官方wiki

github.com/vrchat-community/osc

收发消息是基础,像我这种非专业程序,建议使用大佬现成的库,

如果是用C#可以参考这个:

github.com/hecomi/uOSC

而且就算用了现成的库,也得小心,要正确的处理消息,

我之前就犯过一个低级错误,导致CPU占用爆表,因为处理的时候忘了给接收消息的代码所在的线程设置等待,导致CPU一有空就会去跑一遍,毫无意义的占用了CPU。

后续防止消息过多堵塞也是要注意,VRChat是无差别的将所有更改的内容全部吐出来,当角色 移动或者旋转视角时,会有一大堆的消息,需要自行丢弃一些过时重复的项目。

解决以上问题后,就差不多了~

这是我自己搓的小工具,

www.bilibili.com/video/BV12z421S7ho

在github开源了,

github.com/amoeet/VRChat_X_DGLAB

不过有疑问还是加群讨论,翻我那个屎山代码可能会比较痛苦

技术交流群群号:839674395 

新页面

这里收集了我遇到过的麻烦的报错,已开放权限,大家有其他报错也可以在这里补充~

请将已找到解决办法的报错在这里追加,方便后来的小伙伴查询,没有找到解决办法的建议群里询问。

还存在已删除的脚本幽灵一般的再次生效的情况,应该是缓存问题。

总之为了避免此类bug,请尽量避免删除或创建非必要的脚本。