generated from vrchat-community/template-package
-
Notifications
You must be signed in to change notification settings - Fork 18
/
RuntimeUtil.cs
143 lines (128 loc) · 4.7 KB
/
RuntimeUtil.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using UnityEngine;
using UnityEngine.SceneManagement;
#if NDMF_VRCSDK3_AVATARS
using VRC.SDK3.Avatars.Components;
#endif
namespace nadena.dev.ndmf.runtime
{
/// <summary>
/// A collection of general-purpose utilities that are available from Runtime-scope scripts.
/// </summary>
public static class RuntimeUtil
{
/// <summary>
/// Invoke this function to register a callback with EditorApplication.delayCall from a context that cannot
/// access EditorApplication.
/// </summary>
public static Action<Action> DelayCall { get; internal set; }
static RuntimeUtil()
{
DelayCall = action => { throw new Exception("delayCall() cannot be called during static initialization"); };
}
// Shadow the VRC-provided methods to avoid deprecation warnings
internal static T GetOrAddComponent<T>(this GameObject obj) where T : Component
{
var component = obj.GetComponent<T>();
if (component == null) component = obj.AddComponent<T>();
return component;
}
internal static T GetOrAddComponent<T>(this Component obj) where T : Component
{
return obj.gameObject.GetOrAddComponent<T>();
}
/// <summary>
/// Returns whether the editor is in play mode.
/// </summary>
#if UNITY_EDITOR
public static bool IsPlaying => UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode;
#else
public static bool IsPlaying => true;
#endif
/// <summary>
/// Returns the relative path from root to child, or null is child is not a descendant of root.
/// </summary>
/// <param name="root"></param>
/// <param name="child"></param>
/// <returns></returns>
[CanBeNull]
public static string RelativePath(GameObject root, GameObject child)
{
if (root == child) return "";
List<string> pathSegments = new List<string>();
while (child != root && child != null)
{
pathSegments.Add(child.name);
child = child.transform.parent?.gameObject;
}
if (child == null && root != null) return null;
pathSegments.Reverse();
return String.Join("/", pathSegments);
}
/// <summary>
/// Returns the path of a game object relative to the avatar root, or null if the avatar root could not be
/// located.
/// </summary>
/// <param name="child"></param>
/// <returns></returns>
[CanBeNull]
public static string AvatarRootPath(GameObject child)
{
if (child == null) return null;
var avatar = FindAvatarInParents(child.transform);
if (avatar == null) return null;
return RelativePath(avatar.gameObject, child);
}
/// <summary>
/// Check whether the target component is the root of the avatar.
/// </summary>
/// <param name="target"></param>
/// <returns></returns>
public static bool IsAvatarRoot(Transform target)
{
#if NDMF_VRCSDK3_AVATARS
return target.GetComponent<VRCAvatarDescriptor>();
#else
var an = target.GetComponent<Animator>();
if (!an) return false;
var parent = target.transform.parent;
return !(parent && parent.GetComponentInParent<Animator>());
#endif
}
/// <summary>
/// Returns the component marking the root of the avatar.
/// </summary>
/// <param name="target"></param>
/// <returns></returns>
public static Transform FindAvatarInParents(Transform target)
{
while (target != null)
{
if (IsAvatarRoot(target)) return target;
target = target.parent;
}
return null;
}
/// <summary>
/// Returns the component marking the root of the avatar.
/// </summary>
/// <param name="scene"></param>
/// <returns></returns>
internal static IEnumerable<Transform> FindAvatarsInScene(Scene scene)
{
foreach (var root in scene.GetRootGameObjects())
{
#if NDMF_VRCSDK3_AVATARS
foreach (var avatar in root.GetComponentsInChildren<VRCAvatarDescriptor>())
#else
foreach (var avatar in root.GetComponentsInChildren<Animator>())
#endif
{
if (IsAvatarRoot(avatar.transform)) yield return avatar.transform;
}
}
}
}
}