-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
- Loading branch information
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
using System; | ||
using UnityEngine; | ||
using VRC.SDKBase; | ||
|
||
namespace TLP.UdonUtils.Common | ||
{ | ||
public static class UdonCommon | ||
{ | ||
/// <summary> | ||
/// Finds the component of a given type in the current gameobject hierarchy which is closest to the scene root | ||
/// </summary> | ||
/// <param name="type"></param> | ||
/// <param name="start"></param> | ||
/// <returns>the found component or null if none was found</returns> | ||
public static Component FindTopComponent(Type type, Transform start) | ||
{ | ||
if (!Utilities.IsValid(start)) | ||
{ | ||
return null; | ||
} | ||
|
||
Component topComponent = null; | ||
var topTransform = start; | ||
|
||
do | ||
{ | ||
var behaviour = topTransform.GetComponent(type); | ||
if (Utilities.IsValid(behaviour)) | ||
{ | ||
topComponent = behaviour; | ||
} | ||
|
||
topTransform = topTransform.parent; | ||
} while (Utilities.IsValid(topTransform)); | ||
|
||
return topComponent; | ||
} | ||
|
||
/** | ||
* O(n) - scales linearly with number of players present, don't use in Update()! | ||
*/ | ||
public static VRCPlayerApi GetClosestPlayer(ref VRCPlayerApi[] players, Vector3 location) | ||
{ | ||
int playerCount = VRCPlayerApi.GetPlayerCount(); | ||
if (players == null || players.Length < playerCount) | ||
{ | ||
players = new VRCPlayerApi[playerCount * 2]; | ||
} | ||
|
||
VRCPlayerApi.GetPlayers(players); | ||
VRCPlayerApi closestPlayer = null; | ||
float closestDistance = float.MaxValue; | ||
for (int i = 0; i < playerCount; i++) | ||
{ | ||
var playerInRange = players[i]; | ||
if (!Utilities.IsValid(playerInRange)) | ||
{ | ||
continue; | ||
} | ||
|
||
var distanceVector = location - playerInRange.GetPosition(); | ||
var projectedDistance = Vector3.ProjectOnPlane( | ||
distanceVector, | ||
playerInRange.GetRotation() * Vector3.up | ||
); | ||
float projectedDistanceMagnitude = projectedDistance.magnitude; | ||
if (projectedDistanceMagnitude < closestDistance) | ||
{ | ||
closestDistance = projectedDistanceMagnitude; | ||
closestPlayer = playerInRange; | ||
} | ||
} | ||
|
||
return closestPlayer; | ||
} | ||
|
||
public static VRCPlayerApi GetClosestNonLocalPlayer(ref VRCPlayerApi[] players, Vector3 location) | ||
{ | ||
int playerCount = VRCPlayerApi.GetPlayerCount(); | ||
if (players == null || players.Length < playerCount) | ||
{ | ||
players = new VRCPlayerApi[playerCount * 2]; | ||
} | ||
|
||
VRCPlayerApi.GetPlayers(players); | ||
VRCPlayerApi closestPlayer = null; | ||
float closestDistance = float.MaxValue; | ||
for (int i = 0; i < playerCount; i++) | ||
{ | ||
var playerInRange = players[i]; | ||
if (!Utilities.IsValid(playerInRange)) | ||
{ | ||
continue; | ||
} | ||
|
||
if (playerInRange.isLocal) | ||
{ | ||
continue; | ||
} | ||
|
||
var distanceVector = location - playerInRange.GetPosition(); | ||
var projectedDistance = Vector3.ProjectOnPlane( | ||
distanceVector, | ||
playerInRange.GetRotation() * Vector3.up | ||
); | ||
float projectedDistanceMagnitude = projectedDistance.magnitude; | ||
if (projectedDistanceMagnitude < closestDistance) | ||
{ | ||
closestDistance = projectedDistanceMagnitude; | ||
closestPlayer = playerInRange; | ||
} | ||
} | ||
|
||
return closestPlayer; | ||
} | ||
|
||
public static int GetPlayersInRangeNoAlloc( | ||
ref VRCPlayerApi[] players, | ||
Vector3 position, | ||
float radius, | ||
VRCPlayerApi[] outPlayers | ||
) | ||
{ | ||
int playerCount = VRCPlayerApi.GetPlayerCount(); | ||
if (players == null || players.Length < playerCount) | ||
{ | ||
players = new VRCPlayerApi[playerCount * 2]; | ||
} | ||
|
||
if (outPlayers == null) | ||
{ | ||
return 0; | ||
} | ||
|
||
VRCPlayerApi.GetPlayers(players); | ||
|
||
int playersFound = 0; | ||
|
||
for (int i = 0; i < playerCount; i++) | ||
{ | ||
var playerInRange = players[i]; | ||
if (!Utilities.IsValid(playerInRange)) | ||
{ | ||
continue; | ||
} | ||
|
||
if (Vector3.Distance(position, playerInRange.GetPosition()) < radius) | ||
{ | ||
if (outPlayers.Length >= playersFound) | ||
{ | ||
outPlayers[playersFound] = playerInRange; | ||
++playersFound; | ||
} | ||
} | ||
} | ||
|
||
return playersFound; | ||
} | ||
|
||
public static string UdonTypeNameShort(string udonTypeName) | ||
{ | ||
string[] productTypeName = udonTypeName.Split('.'); | ||
return productTypeName.Length > 0 ? productTypeName[productTypeName.Length - 1] : udonTypeName; | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
using JetBrains.Annotations; | ||
using UnityEngine; | ||
using VRC.SDKBase; | ||
|
||
namespace TLP.UdonUtils.DesignPatterns.MVC | ||
{ | ||
/// <summary> | ||
/// The controller in the MVC pattern is responsible for receiving user input and updating the model and view | ||
/// accordingly. | ||
/// It acts as a bridge between the model and the view, ensuring that any changes made to the model | ||
/// are reflected in the view. | ||
/// The controller also handles any business logic related to user input. | ||
/// </summary> | ||
[DefaultExecutionOrder(ExecutionOrder)] | ||
public abstract class Controller : MvcBase | ||
{ | ||
protected override int ExecutionOrderReadOnly => ExecutionOrder; | ||
|
||
[PublicAPI] | ||
public new const int ExecutionOrder = Model.ExecutionOrder + 1; | ||
|
||
public bool Initialized { get; private set; } | ||
protected Model Model { get; private set; } | ||
protected View View { get; private set; } | ||
|
||
#region PublicAPI | ||
|
||
[PublicAPI] | ||
public bool Initialize(Model model, View view) | ||
{ | ||
#if TLP_DEBUG | ||
DebugLog(nameof(Initialize)); | ||
#endif | ||
if (HasError) | ||
{ | ||
Error($"Can not initialize again due to previous critical error: '{CriticalError}'"); | ||
return false; | ||
} | ||
|
||
if (Initialized) | ||
{ | ||
Warn("Already initialized"); | ||
return false; | ||
} | ||
|
||
if (!Utilities.IsValid(view)) | ||
{ | ||
Error($"{nameof(view)} invalid"); | ||
return false; | ||
} | ||
|
||
if (!Utilities.IsValid(model)) | ||
{ | ||
Error($"{nameof(model)} invalid"); | ||
return false; | ||
} | ||
|
||
if (view.HasError) | ||
{ | ||
Error($"{nameof(view)} has critical error: '{view.CriticalError}'"); | ||
return false; | ||
} | ||
|
||
if (view.Initialized) | ||
{ | ||
Error($"{nameof(view)} is already initialized"); | ||
return false; | ||
} | ||
|
||
if (model.HasError) | ||
{ | ||
Error($"{nameof(model)} has critical error: '{model.CriticalError}'"); | ||
return false; | ||
} | ||
|
||
if (!model.Initialized) | ||
{ | ||
Error($"{nameof(model)} is not initialized"); | ||
return false; | ||
} | ||
|
||
View = view; | ||
Model = model; | ||
|
||
// setting it to true to prevent attempts to re-initialize controllers that have | ||
// failed to initialize and are in need of cleanup | ||
Initialized = true; | ||
|
||
if (InitializeInternal()) | ||
{ | ||
return true; | ||
} | ||
|
||
Error($"Initialization failed. Using {nameof(DeInitialize)} to cleanup."); | ||
DeInitialize(); | ||
return false; | ||
} | ||
|
||
protected virtual void OnDestroy() | ||
{ | ||
#if TLP_DEBUG | ||
DebugLog(nameof(OnDestroy)); | ||
#endif | ||
DeInitialize(); | ||
} | ||
|
||
public bool DeInitialize() | ||
{ | ||
#if TLP_DEBUG | ||
DebugLog(nameof(DeInitialize)); | ||
#endif | ||
if (!Initialized) | ||
{ | ||
return false; | ||
} | ||
|
||
if (Utilities.IsValid(View)) | ||
{ | ||
View.DeInitialize(); | ||
} | ||
|
||
View = null; | ||
|
||
if (Utilities.IsValid(Model)) | ||
{ | ||
Model.DeInitialize(); | ||
} | ||
|
||
Model = null; | ||
|
||
if (DeInitializeInternal()) | ||
{ | ||
Initialized = false; | ||
CriticalError = null; | ||
return true; | ||
} | ||
|
||
CriticalError = $"De-Initialization failed."; | ||
Error(CriticalError); | ||
HasError = true; | ||
return false; | ||
} | ||
|
||
#endregion | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.