Skip to content

Instantly share code, notes, and snippets.

@kawashirov
Last active December 10, 2024 05:21
Show Gist options
  • Save kawashirov/3a66ebc78dc2671a0e68c340590de056 to your computer and use it in GitHub Desktop.
Save kawashirov/3a66ebc78dc2671a0e68c340590de056 to your computer and use it in GitHub Desktop.
SyncVelocityLimiter
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.SceneManagement;
using VRC.SDKBase;
using UdonSharp;
using VRC.SDK3.Components;
#if UNITY_EDITOR && !COMPILER_UDONSHARP
using UnityEditor;
using UdonSharpEditor;
#endif
[UdonBehaviourSyncMode(BehaviourSyncMode.None)]
public class SyncVelocityLimiter : UdonSharpBehaviour
{
// The maximum linear velocity of the rigidbody measured in meters per second.
// https://docs.unity3d.com/2022.3/Documentation/ScriptReference/Rigidbody-maxLinearVelocity.html
public float MaxLinearVelocity = 100;
// The maximum angular velocity of the rigidbody measured in radians per second. (Default 7) range { 0, infinity }.
// https://docs.unity3d.com/2022.3/Documentation/ScriptReference/Rigidbody-maxAngularVelocity.html
public float MaxAngularVelocity = 7;
// Maximum velocity of a rigidbody when moving out of penetrating state.
// https://docs.unity3d.com/2022.3/Documentation/ScriptReference/Rigidbody-maxDepenetrationVelocity.html
public float MaxDepenetrationVelocity = 10;
// https://docs.unity3d.com/2022.3/Documentation/ScriptReference/Rigidbody-drag.html
public float MinDrag = 0.01f;
// https://docs.unity3d.com/2022.3/Documentation/ScriptReference/Rigidbody-angularDrag.html
public float MinAngularDrag = 0.05f;
public Rigidbody[] Rigidbodies;
public void Start()
{
_ApplyLimits();
SendCustomEventDelayedFrames(nameof(_ApplyLimits), 1);
SendCustomEventDelayedSeconds(nameof(_ApplyLimits), 1);
}
public void _ApplyLimits()
{
if (!Utilities.IsValid(Rigidbodies))
return;
foreach (var rb in Rigidbodies)
{
if (!Utilities.IsValid(rb))
continue;
rb.maxLinearVelocity = MaxLinearVelocity; // Mathf.Min(rb.maxLinearVelocity, MaxLinearVelocity);
rb.maxAngularVelocity = MaxAngularVelocity; // Mathf.Min(rb.maxAngularVelocity, MaxAngularVelocity);
rb.maxDepenetrationVelocity = MaxAngularVelocity; // Mathf.Min(rb.maxDepenetrationVelocity, MaxAngularVelocity);
rb.drag = Mathf.Max(rb.drag, MinDrag);
rb.angularDrag = Mathf.Max(rb.angularDrag, MinAngularDrag);
}
}
#if UNITY_EDITOR && !COMPILER_UDONSHARP
[CustomEditor(typeof(SyncVelocityLimiter), true)]
public class Editor : UnityEditor.Editor
{
public override void OnInspectorGUI()
{
if (UdonSharpGUI.DrawDefaultUdonSharpBehaviourHeader(target))
return;
DrawDefaultInspector();
var refreshable = target as SyncVelocityLimiter;
if (GUILayout.Button("Refresh Rigidbodies"))
{
refreshable.Refresh();
}
}
}
private static bool IsRuntimeHideFlags(Object obj) =>
(obj.hideFlags & (HideFlags.DontSaveInEditor | HideFlags.DontSaveInBuild)) == HideFlags.None;
public static bool IsRuntime(Object obj)
{
// Имеет ли объект шанс попасть в рантайм после сборки? Если точно известно, что нет, то возвращается false
if (obj == null || !IsRuntimeHideFlags(obj))
return false;
if (obj is GameObject gobj)
{
if (gobj.CompareTag("EditorOnly"))
return false;
var parent = gobj.transform.parent;
if (parent != null && !IsRuntime(parent)) // Рекурсивно
return false; // Имеет не-рантайм родителя
}
else if (obj is Component c)
{
// Примечание: hideFlags свои у компонента и у гейм объекта.
if (!IsRuntime(c.gameObject)) // Рекурсивно
return false; // Имеет не-рантайм родителя
}
return true;
}
public void Refresh()
{
Rigidbodies = SceneManager.GetActiveScene()
.GetRootGameObjects()
.SelectMany(gobj => gobj.GetComponentsInChildren<Rigidbody>(true))
.Where(IsRuntime)
.Where(rb => rb.GetComponent<VRCObjectSync>() != null)
.ToArray();
EditorUtility.SetDirty(this);
}
#endif // UNITY_EDITOR && !COMPILER_UDONSHARP
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment