Initial commit
This commit is contained in:
8
Assets/VRCPrefabs/Stingray/UdonPlatformHook.meta
Normal file
8
Assets/VRCPrefabs/Stingray/UdonPlatformHook.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c5ba5e9533e05b14b98eb6ad18f7d604
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
32
Assets/VRCPrefabs/Stingray/UdonPlatformHook/!ReadMe.txt
Normal file
32
Assets/VRCPrefabs/Stingray/UdonPlatformHook/!ReadMe.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
https://github.com/Superbstingray/UdonPlayerPlatformHook
|
||||
|
||||
v1.5 - 20/3/24 - Made in Unity 2019.4.31f1 / SDK3-2022.02.16.19.13 / UdonSharp_v0.20.3
|
||||
|
||||
(Prefab Functionality)
|
||||
|
||||
* Drag and Drop solution for making Players correctly follow moving colliders in the scene when standing on them. The prefab makes Players functionally behave as if they were parented to any collider they stand on.
|
||||
* Behavior can be enabled or disabled based on layers.
|
||||
|
||||
|
||||
(Usage)
|
||||
|
||||
* Drag into the root of your scene and set your moving Objects/Platforms to layer 11(Environment) or a custom layer and assign layers in the scripts layer mask.
|
||||
* You only need to add one instance of the prefab to your scene, ideally not parented to any other Game Object.
|
||||
|
||||
|
||||
(Functionality / Features)
|
||||
|
||||
* Makes the Player move seamlessly with colliders.
|
||||
* Will correctly move and rotate the Player with colliders they stand on.
|
||||
* Behavior is enabled/disabled based on GameObject layers.
|
||||
* Correct physics/momentum when jumping off of moving colliders.
|
||||
* Ability to stop moving the player when they are accessing their menu(s)
|
||||
* Player will seamlessly teleport with colliders that get teleported / moved quickly.
|
||||
|
||||
|
||||
(Additional)
|
||||
|
||||
Objects with Rigidbodies that have Interpolate or Extrapolate set that move on FixedUpdate can have issues with fast linear vertical movement causing the player to detach from the platform, this includes any object that uses VRC Object Sync as it silently overrides Rigidody Interpolation settings.
|
||||
|
||||
If your world utilizes VRCPlayerAPI.Immobilize the ReduceIKDrift setting may interfere with it.
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: db662579ba3593148b303296058d2e10
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/VRCPrefabs/Stingray/UdonPlatformHook/Scripts.meta
Normal file
8
Assets/VRCPrefabs/Stingray/UdonPlatformHook/Scripts.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a80f329631af0714bbfe6836f43beeb7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 324706932a8965e44b4dce80c3bf5701
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,249 @@
|
||||
|
||||
using UnityEngine;
|
||||
using VRC.SDKBase;
|
||||
using VRC.Udon.Common;
|
||||
using UdonSharp;
|
||||
using VRC.Udon.Wrapper.Modules;
|
||||
|
||||
namespace Superbstingray
|
||||
{
|
||||
[UdonBehaviourSyncMode(BehaviourSyncMode.NoVariableSync)]
|
||||
public class UdonPlatformHook : UdonSharp.UdonSharpBehaviour
|
||||
{
|
||||
[Tooltip("Layers that the Player will move with.")]
|
||||
public LayerMask hookLayerMask;
|
||||
|
||||
[Tooltip("Vertical distance between the Player and Platform before the Script Unhooks from Colliders. You may want to increase this value if your world has a higher than average jump impulse.")]
|
||||
public float hookDistance = 0.75F;
|
||||
|
||||
[Tooltip("Will make the Player keep their Velocity + the Platforms Velocity when they detach from it.")]
|
||||
public bool inheritVelocity = true;
|
||||
|
||||
[Tooltip("Will Immobilize(true) players when standing still on moving platforms to prevent Avatars from having IK drift / IK walk.")]
|
||||
public bool reduceIKDrift = true;
|
||||
|
||||
[Tooltip("Try Detect if the Player has their Main Menu open and stop moving them. Will help with menu interactions.")]
|
||||
public bool mainMenuPause = true;
|
||||
|
||||
[Tooltip("Try Detect if the Player has their Quick Menu open and stop moving them. Will help with menu interactions.")]
|
||||
public bool quickMenuPause = false;
|
||||
|
||||
BoxCollider platformOverride;
|
||||
Collider[] colliderArray;
|
||||
Collider sceneCollider;
|
||||
Quaternion headRotation;
|
||||
RaycastHit hitInfo;
|
||||
Transform PlayerOffset, playerTracker, hook;
|
||||
Vector3 playerVelocity, hookLastPos, hookOffsetPos, lastFramePos;
|
||||
VRCPlayerApi localPlayer;
|
||||
|
||||
bool menuOpen;
|
||||
float InputMoveH, InputMoveV;
|
||||
int unhookThreshold, localColliders, intUI;
|
||||
|
||||
[FieldChangeCallback(nameof(isHookedCallback))]
|
||||
bool isHooked;
|
||||
bool isHookedCallback
|
||||
{ set
|
||||
{ isHooked = value;
|
||||
if (isHooked) // isHooked=true functions
|
||||
{
|
||||
hook.localPosition = Vector3.zero; hook.eulerAngles = Vector3.zero;
|
||||
platformOverride.enabled = true;
|
||||
PlayerOffset.SetPositionAndRotation(hook.position, hook.rotation);
|
||||
|
||||
// When hooking count the number of PlayerLocal colliders the player has
|
||||
// as this will help us know when the Player enters a station.
|
||||
localColliders = Mathf.Clamp(Physics.OverlapSphere((localPlayer.GetPosition()), 1024f, 1024).Length, 1, 100);
|
||||
}
|
||||
else // isHooked=false functions
|
||||
{
|
||||
if(inheritVelocity) { localPlayer.SetVelocity(playerVelocity); } // Sets the players velocity to their actual worldspace velocity when they Unhook.
|
||||
|
||||
OverridesOff();
|
||||
hook.localPosition = Vector3.zero; hook.eulerAngles = Vector3.zero;
|
||||
PlayerOffset.SetPositionAndRotation(hook.position, hook.rotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
InitializeVariables();
|
||||
IgnoreSceneCollision();
|
||||
}
|
||||
|
||||
void FixedUpdate()
|
||||
{
|
||||
FixedUpdateFunctions();
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
LateUpdateFunctions();
|
||||
}
|
||||
|
||||
void FixedUpdateFunctions()
|
||||
{
|
||||
if (!VRC.SDKBase.Utilities.IsValid(localPlayer)) { return; }
|
||||
|
||||
if (isHooked && inheritVelocity) // Average the last X frames of the players global velocity.
|
||||
{
|
||||
playerVelocity = Vector3.ClampMagnitude((playerVelocity * 3f + (localPlayer.GetPosition() - lastFramePos) / Time.deltaTime) / 4f, 100f);
|
||||
lastFramePos = localPlayer.GetPosition();
|
||||
}
|
||||
|
||||
if (!menuOpen)
|
||||
{
|
||||
// Override_Spherecast. Set position of override collider.
|
||||
Physics.SphereCast(localPlayer.GetPosition() + new Vector3(0F, .3f, 0f), 0.25f, Vector3.down, out hitInfo, 10f, hookLayerMask.value);
|
||||
platformOverride.center = hitInfo.point;
|
||||
|
||||
// FixedUpdate_Spherecast. Check for valid platforms.
|
||||
// Add to the unhookThreshold if it misses a valid platform and unhook if unhookThreshold is greater than X.
|
||||
if (!Physics.SphereCast(localPlayer.GetPosition() + new Vector3(0f, .3f, 0f), 0.25f, Vector3.down, out hitInfo, hookDistance + .3f, hookLayerMask.value))
|
||||
{
|
||||
unhookThreshold++;
|
||||
if (unhookThreshold > 10 && isHooked)
|
||||
{
|
||||
hook.parent = transform;
|
||||
SetProgramVariable(nameof(isHooked), false);
|
||||
SendCustomEventDelayedSeconds(nameof(OverridesOff), 0.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LateUpdateFunctions()
|
||||
{
|
||||
if (!VRC.SDKBase.Utilities.IsValid(localPlayer)) { return; }
|
||||
#if !UNITY_EDITOR
|
||||
if (isHooked) // Count the number of InterntalUI colliders as a means to know if the menu is open or not.
|
||||
{
|
||||
intUI = Physics.OverlapSphereNonAlloc(localPlayer.GetPosition(), 10f, colliderArray, 524288);
|
||||
menuOpen = (mainMenuPause && (intUI >= 7 && intUI <= 14)) || (quickMenuPause && (intUI >= 15 && intUI <= 19));
|
||||
}
|
||||
if (isHooked && !menuOpen) // Move the parented hook to the Players position
|
||||
{
|
||||
hookLastPos = hook.position;
|
||||
hook.position = localPlayer.GetPosition();
|
||||
hookOffsetPos = hook.position - hookLastPos;
|
||||
}
|
||||
if (isHooked && menuOpen) { localPlayer.SetVelocity(Vector3.zero); } // Override Player Velocity to make it easier to use their menu.
|
||||
|
||||
// Teleport the player to the new offset position only if the players PlayerLocal collider count
|
||||
// didn't decrease otherwise assume the player entered a station.
|
||||
if (isHooked && !menuOpen)
|
||||
{
|
||||
playerTracker.SetPositionAndRotation(localPlayer.GetTrackingData(VRCPlayerApi.TrackingDataType.Origin).position, localPlayer.GetTrackingData(VRCPlayerApi.TrackingDataType.Origin).rotation);
|
||||
PlayerOffset.SetPositionAndRotation(hook.position, hook.rotation);
|
||||
|
||||
if (Physics.OverlapSphere(localPlayer.GetPosition(), 1024f, 1024).Length >= localColliders)
|
||||
{
|
||||
localPlayer.TeleportTo(playerTracker.position - hookOffsetPos, playerTracker.rotation, VRC_SceneDescriptor.SpawnOrientation.AlignRoomWithSpawnPoint, true);
|
||||
}
|
||||
}
|
||||
#else
|
||||
// "EditorOnly" duplicate funtion from above specifically for CyanEmu/ClientSim as Origin tracking does not behave the same in editor as in client.
|
||||
if (isHooked)
|
||||
{
|
||||
playerTracker.SetPositionAndRotation(localPlayer.GetPosition(), localPlayer.GetRotation());
|
||||
PlayerOffset.SetPositionAndRotation(hook.position, hook.rotation);
|
||||
|
||||
if (Physics.OverlapSphere(localPlayer.GetPosition(), 1024f, 1024).Length >= localColliders)
|
||||
{
|
||||
localPlayer.TeleportTo(playerTracker.position - hookOffsetPos, playerTracker.rotation, VRC_SceneDescriptor.SpawnOrientation.AlignPlayerWithSpawnPoint, true);
|
||||
hookOffsetPos = Vector3.zero;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// Players in Desktop or 3 Point tracking will have their Inverse Kinematics drag behind and "IK Walk" while being moved when they aren't intentionally locomoting.
|
||||
// This function is to prevent that from occuring by Immobilizing the player when they are hooked to a platform and aren't moving relative to the platform.
|
||||
// This is scuffed and I should optimize it.
|
||||
if(reduceIKDrift && isHooked)
|
||||
{
|
||||
headRotation = localPlayer.GetTrackingData(VRCPlayerApi.TrackingDataType.Head).rotation;
|
||||
localPlayer.Immobilize(!(InputMoveH * 0.1f + InputMoveV != 0f)
|
||||
&& Mathf.Abs(playerVelocity.x) + Mathf.Abs(playerVelocity.z) > .1f
|
||||
&& Mathf.Abs(localPlayer.GetVelocity().x) + Mathf.Abs(localPlayer.GetVelocity().z) < .01f
|
||||
&& Quaternion.Angle(new Quaternion(0f, headRotation.y, 0f, headRotation.w).normalized, localPlayer.GetRotation()) < 90f);
|
||||
}
|
||||
// LateUpdate_Spherecast. Check for valid platforms.
|
||||
if (Physics.SphereCast(localPlayer.GetPosition() + new Vector3(0f, .3f, 0f), 0.25f, Vector3.down, out hitInfo, hookDistance + .3f, hookLayerMask.value))
|
||||
{
|
||||
unhookThreshold = 0;
|
||||
if (unhookThreshold < 10 && (localPlayer.IsPlayerGrounded())) // Hook to the valid platform if the Player is grounded.
|
||||
{
|
||||
hook.parent = hitInfo.transform;
|
||||
SetProgramVariable(nameof(isHooked), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// INPUT EVENTS: MoveVertical, MoveHorizontal, InputJump.
|
||||
public override void InputMoveVertical(float Value, UdonInputEventArgs InputMoveVerticalArgs)
|
||||
{
|
||||
InputMoveV = InputMoveVerticalArgs.floatValue;
|
||||
}
|
||||
public override void InputMoveHorizontal(float Value, UdonInputEventArgs InputMoveHorizontalArgs)
|
||||
{
|
||||
InputMoveH = InputMoveHorizontalArgs.floatValue;
|
||||
}
|
||||
public override void InputJump(bool outputJumpBool, UdonInputEventArgs inputJumpArgs)
|
||||
{
|
||||
if (reduceIKDrift) { localPlayer.Immobilize(false); }
|
||||
}
|
||||
|
||||
// Reset prefab state and call unhook on Respawn.
|
||||
public override void OnPlayerRespawn(VRCPlayerApi onPlayerRespawnPlayer)
|
||||
{
|
||||
hook.parent = transform;
|
||||
unhookThreshold = System.Int32.MaxValue;
|
||||
localPlayer.SetVelocity(Vector3.zero);
|
||||
SetProgramVariable(nameof(isHooked), false);
|
||||
OverridesOff();
|
||||
}
|
||||
|
||||
// The prefab uses an additional overriding collider to prevent the VRC player controller from being affected by moving world
|
||||
// colliders which can affect locomotion and animations, and this is to prevent the override collider from interacting with other colliders in the scene.
|
||||
public void IgnoreSceneCollision()
|
||||
{
|
||||
SendCustomEventDelayedSeconds(nameof(IgnoreSceneCollision), 60f);
|
||||
colliderArray = Physics.OverlapSphere(Vector3.zero, System.Single.MaxValue);
|
||||
for (int i = 0; i < colliderArray.Length; i++)
|
||||
{
|
||||
sceneCollider = colliderArray[i];
|
||||
if (VRC.SDKBase.Utilities.IsValid(sceneCollider))
|
||||
{
|
||||
Physics.IgnoreCollision(sceneCollider, platformOverride);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InitializeVariables() // Set Prefab Variables
|
||||
{
|
||||
localPlayer = Networking.LocalPlayer;
|
||||
if (!VRC.SDKBase.Utilities.IsValid(localPlayer)) { return; }
|
||||
lastFramePos = localPlayer.GetPosition();
|
||||
playerTracker = transform.GetChild(0).GetChild(0);
|
||||
PlayerOffset = transform.GetChild(0);
|
||||
hook = transform.GetChild(1);
|
||||
platformOverride = transform.GetComponent<BoxCollider>();
|
||||
platformOverride.size = new Vector3(0.5f, 0.05f, 0.5f);
|
||||
transform.position = Vector3.zero;
|
||||
|
||||
if (hookLayerMask.value == -1) // Override if using "Everything" as a layermask to Everything -PlayerLocal,-MirrorReflection to prevent interference with the prefabs functionality.
|
||||
{
|
||||
hookLayerMask.value = -263369;
|
||||
}
|
||||
}
|
||||
|
||||
// Disable Override Collider and set Immobilize state false.
|
||||
void OverridesOff()
|
||||
{
|
||||
if (!localPlayer.IsPlayerGrounded()) { platformOverride.enabled = false; }
|
||||
|
||||
if (reduceIKDrift) { localPlayer.Immobilize(false); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c6ba0c440cd811d45b8c1ad93549ed19
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 04a153bd56e937d4cae0c0779f232e6a
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user