ArtyomV2X vor 2 Wochen
Commit
50018290c1

+ 7 - 0
README.md

@@ -0,0 +1,7 @@
+# Usage of the scripting system
+
+## Any scripts not listed here are deprecated and should not be used unless you have some clue what they do or how to fix em.
+
+Leave a fix/issue if you can figure out anything cause I sure don't care to change these junky files /shrug
+
+### More guides to come eventually.

+ 75 - 0
assets/BoneLookAtPlayer.cs

@@ -0,0 +1,75 @@
+using UdonSharp;
+using UnityEngine;
+using VRC.SDKBase;
+using VRC.Udon;
+using System;
+
+[DisallowMultipleComponent]
+[AddComponentMenu("Artyom Scripting System/Bone Look At Player")]
+public class BoneLookAtPlayer : UdonSharpBehaviour
+{
+    public GameObject objectToRotate; // The object to rotate
+    public float rotationSpeed = 90.0f; // Maximum rotation speed in degrees per second
+    public Vector3 rotationOffset; // Rotation offset for each axis
+
+    private Transform objectTransform;
+
+    void Start()
+    {
+        if (objectToRotate != null)
+        {
+            objectTransform = objectToRotate.transform;
+        }
+        else
+        {
+            Debug.LogError("Object to rotate not assigned.");
+        }
+    }
+
+    void Update()
+    {
+        VRCPlayerApi nearestPlayer = GetNearestPlayer(); // Method to get the nearest player
+        if (nearestPlayer != null)
+        {
+            Vector3 playerHeadPosition = nearestPlayer.GetTrackingData(VRCPlayerApi.TrackingDataType.Head).position;
+
+            // Calculate the direction to look
+            Vector3 directionToLook = playerHeadPosition - objectTransform.position;
+            if (directionToLook != Vector3.zero)
+            {
+                Quaternion targetRotation = Quaternion.LookRotation(directionToLook);
+                
+                // Apply the rotation offset
+                targetRotation *= Quaternion.Euler(rotationOffset);
+
+                objectTransform.rotation = Quaternion.RotateTowards(objectTransform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
+            }
+        }
+    }
+
+    VRCPlayerApi GetNearestPlayer()
+    {
+        VRCPlayerApi nearestPlayer = null;
+        float closestDistance = float.MaxValue;
+
+        VRCPlayerApi[] players = new VRCPlayerApi[VRCPlayerApi.GetPlayerCount()];
+        VRCPlayerApi.GetPlayers(players);
+
+        Vector3 currentPosition = transform.position;
+
+        foreach (VRCPlayerApi player in players)
+        {
+            if (player != null) // Allow local player for nearest search
+            {
+                float distance = Vector3.Distance(currentPosition, player.GetPosition());
+                if (distance < closestDistance)
+                {
+                    closestDistance = distance;
+                    nearestPlayer = player;
+                }
+            }
+        }
+
+        return nearestPlayer;
+    }
+}

+ 26 - 0
assets/Checkpoint.cs

@@ -0,0 +1,26 @@
+using UdonSharp;
+using UnityEngine;
+using VRC.SDKBase;
+using VRC.Udon;
+using System;
+
+[DisallowMultipleComponent]
+
+[AddComponentMenu("Artyom Scripting System/Checkpoint")]
+public class Checkpoint : UdonSharpBehaviour
+{
+    public GameObject targetObject; // The game object to move
+
+    [UdonSynced]
+    private bool hasBeenUsed = false;
+
+
+    public override void OnPlayerTriggerEnter(VRCPlayerApi player)
+    {
+        if (hasBeenUsed || !player.isLocal) return;
+
+        targetObject.transform.SetPositionAndRotation(transform.position, transform.rotation);
+        hasBeenUsed = true;
+        gameObject.SetActive(false); // Disables the script after use
+    }
+}

+ 44 - 0
assets/ImpactSoundPlayer.cs

@@ -0,0 +1,44 @@
+using UdonSharp;
+using UnityEngine;
+using VRC.SDKBase;
+using VRC.Udon;
+using System;
+
+[DisallowMultipleComponent]
+[AddComponentMenu("Artyom Scripting System/Impact Sound Player")]
+public class ImpactSoundPlayer : UdonSharpBehaviour
+{
+    public GameObject smallImpactSoundObject; // GameObject for small impact sound
+    public GameObject mediumImpactSoundObject; // GameObject for medium impact sound
+    public GameObject largeImpactSoundObject; // GameObject for large impact sound
+
+    public float mediumImpactThreshold = 5.0f; // Threshold for medium impact
+    public float largeImpactThreshold = 10.0f; // Threshold for large impact
+
+    private void OnCollisionEnter(Collision collision)
+    {
+        float impactForce = collision.relativeVelocity.magnitude;
+
+        if (impactForce >= largeImpactThreshold)
+        {
+            PlaySound(largeImpactSoundObject);
+        }
+        else if (impactForce >= mediumImpactThreshold)
+        {
+            PlaySound(mediumImpactSoundObject);
+        }
+        else
+        {
+            PlaySound(smallImpactSoundObject);
+        }
+    }
+
+    private void PlaySound(GameObject soundObject)
+    {
+        if (soundObject != null)
+        {
+            soundObject.SetActive(false); // Reset the sound object
+            soundObject.SetActive(true);  // Activate the sound object to play the sound
+        }
+    }
+}

+ 96 - 0
assets/InteractScript.cs

@@ -0,0 +1,96 @@
+using UdonSharp;
+using UnityEngine;
+using VRC.SDKBase;
+using VRC.Udon;
+using System;
+
+[Obsolete("Replaced with Linked Object System")]
+[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
+[DisallowMultipleComponent]
+
+[AddComponentMenu("Artyom Scripting System/_DEPRECATED_/Interact Script")]
+public class InteractScript : UdonSharpBehaviour
+{
+    public Animator[] animators; // Array to hold Animators
+    public string[] parameterNames; // Array to hold corresponding parameter names
+    public InteractionType interactionType = InteractionType.None; // Dropdown for interaction type
+    public float temporaryDuration = 2.0f; // Duration for temporary interaction
+    public int isOccupied;
+
+    public GameObject audioObjectTrue; // GameObject to enable when the state changes to true
+    public GameObject audioObjectFalse; // GameObject to enable when the state changes to false
+
+    void Start()
+    {
+        isOccupied = 0;
+    }
+
+    private void UpdateAnims(bool state)
+    {
+        for (int i = 0; i < animators.Length; i++)
+        {
+            animators[i].SetBool(parameterNames[i], state);
+        }
+
+        // Enable or disable GameObjects based on the state
+        if (state)
+        {
+            if (audioObjectFalse != null) audioObjectFalse.SetActive(false);
+            if (audioObjectTrue != null) audioObjectTrue.SetActive(true);
+        }
+        else
+        {
+            if (audioObjectTrue != null) audioObjectTrue.SetActive(false);
+            if (audioObjectFalse != null) audioObjectFalse.SetActive(true);
+        }
+    }
+
+    public override void Interact()
+    {
+        Networking.SetOwner(Networking.LocalPlayer, gameObject); // Ensure the local player is the owner
+        if (interactionType == InteractionType.Temporary)
+        {
+            isOccupied = isOccupied + 1;
+            UpdateAnims(true);
+            SendCustomNetworkEvent(VRC.Udon.Common.Interfaces.NetworkEventTarget.All, nameof(NetworkUpdateAnims));
+            SendCustomEventDelayedSeconds(nameof(RevertTemporaryInteraction), temporaryDuration);
+        }
+        else if (interactionType == InteractionType.Permanent)
+        {
+            isOccupied = isOccupied + 1;
+            UpdateAnims(true);
+            SendCustomNetworkEvent(VRC.Udon.Common.Interfaces.NetworkEventTarget.All, nameof(NetworkUpdateAnims));
+        }
+        else if (interactionType == InteractionType.Toggle)
+        {
+            switch(isOccupied)
+            {
+                case 0:
+                    isOccupied = isOccupied + 1;
+                    UpdateAnims(true);
+                    break;
+                case 1:
+                    isOccupied = 0;
+                    UpdateAnims(false);
+                    break;
+            }
+            SendCustomNetworkEvent(VRC.Udon.Common.Interfaces.NetworkEventTarget.All, nameof(NetworkUpdateAnims));
+        }
+    }
+
+    public void RevertTemporaryInteraction()
+    {
+        isOccupied = isOccupied - 1;
+        if (isOccupied < 1)
+        {
+            UpdateAnims(false);
+        }
+        SendCustomNetworkEvent(VRC.Udon.Common.Interfaces.NetworkEventTarget.All, nameof(NetworkUpdateAnims));
+    }
+
+    public void NetworkUpdateAnims()
+    {
+        bool state = isOccupied > 0;
+        UpdateAnims(state);
+    }
+}

+ 101 - 0
assets/JumpPad.cs

@@ -0,0 +1,101 @@
+
+using System.Numerics;
+using UdonSharp;
+using UnityEngine;
+using VRC.SDK3.Components;
+using VRC.SDKBase;
+using VRC.Udon;
+using Vector3 = UnityEngine.Vector3;
+
+[DisallowMultipleComponent]
+
+[AddComponentMenu("Artyom Scripting System/Jump Pad Controller")]
+public class JumpPad : UdonSharpBehaviour
+{
+    public bool debug;
+    public Animator launchAnim;
+    public string animName = "isLaunch";
+    public Transform jumpTarget;
+    public Transform self;
+    public float arcTime = 1.2f;
+    public float walkLimit = 0.2f;
+    public float runLimit = 0.4f;
+    public float strafeLimit = 0.2f;
+    private Vector3 appliedVelocity;
+
+    private float defaultWalk;
+    private float defaultStrafe;
+    private float defaultRun;
+
+    private VRCPlayerApi player;
+
+
+    void Start()
+    {
+        launchAnim = GetComponent<Animator>();
+        appliedVelocity = CalculateInitialVelocity(self.position, jumpTarget.position, -9.8f, arcTime);
+        if (debug) Debug.Log("Jump Pad Controller: Velocity Calculated to:"+ appliedVelocity.ToString()); 
+    }
+    
+    public static Vector3 CalculateInitialVelocity(Vector3 origin, Vector3 target, float gravity, float time)
+    {
+        // Compute the differences in position
+        Vector3 displacement = target - origin;
+
+        // Calculate the initial velocity components
+        Vector3 initialVelocity = new Vector3(
+            displacement.x / time,                              // Horizontal X velocity
+            (displacement.y - 0.5f * gravity * time * time) / time, // Vertical Y velocity
+            displacement.z / time                               // Horizontal Z velocity
+        );
+
+        return initialVelocity;
+    }
+
+    public override void OnPlayerTriggerEnter(VRCPlayerApi colidedPlayer) {
+        appliedVelocity = CalculateInitialVelocity(self.position, jumpTarget.position, -9.8f, arcTime);
+        launchAnim.SetBool(animName, true);
+        if (debug) Debug.Log("Jump Pad Controller: Animator State attempted to be set to True");
+        player = colidedPlayer;
+        // Get speeds to reinstate after jump imobilization
+        defaultRun = player.GetRunSpeed();
+        defaultStrafe = player.GetStrafeSpeed();
+        defaultWalk = player.GetWalkSpeed();
+        // Set current velocity to triggered player and limit their movement
+        LimitMobility();
+        player.SetVelocity(appliedVelocity);
+        
+        if (debug) Debug.Log("Jump Pad Controller: Player Velocity Set to:"+ appliedVelocity.ToString());
+        SendCustomEventDelayedSeconds(nameof(Mobilize), arcTime-0.2f);
+        
+    }
+
+    private void OnTriggerEnter(Collider other)
+    {
+        appliedVelocity = CalculateInitialVelocity(self.position, jumpTarget.position, -9.8f, arcTime);
+        VRC_Pickup pickup = other.GetComponent<VRC_Pickup>();
+        Rigidbody body = other.GetComponent<Rigidbody>();
+        if (pickup && body)
+        {
+            pickup.Drop();
+            body.velocity = appliedVelocity;
+        }
+    }
+
+    public void LimitMobility()
+    {
+        player.SetWalkSpeed(walkLimit);
+        player.SetRunSpeed(runLimit);
+        player.SetStrafeSpeed(strafeLimit);
+        if (debug) Debug.Log("Jump Pad Controller: Limited Player Mobility for " + arcTime.ToString() + " seconds");
+    }
+    public void Mobilize()
+    {
+        player.SetWalkSpeed(defaultWalk);
+        player.SetRunSpeed(defaultRun);
+        player.SetStrafeSpeed(defaultStrafe);
+        if (debug) Debug.Log("Jump Pad Controller: De-Limited Player Mobility after" + arcTime.ToString() + " seconds");
+        launchAnim.SetBool(animName,false);
+        if (debug) Debug.Log("Jump Pad Controller: Animator State attempted to be set to False");
+    }
+}

+ 46 - 0
assets/LaserBeam.cs

@@ -0,0 +1,46 @@
+using UdonSharp;
+using UnityEngine;
+using VRC.SDKBase;
+using VRC.Udon;
+using System;
+
+[DisallowMultipleComponent]
+
+[AddComponentMenu("Artyom Scripting System/Laser Beam Controller")]
+public class LaserBeam : UdonSharpBehaviour
+{
+    public LineRenderer lineRenderer; // Line Renderer for the laser beam
+    public Transform laserOrigin; // Origin point of the laser
+    public float maxDistance = 100.0f; // Maximum distance the laser can reach
+    public LayerMask layerMask; // Layer mask to specify which colliders to detect
+    public GameObject endPointObject; // GameObject to place at the end of the laser line
+
+    void Update()
+    {
+        if (lineRenderer == null || laserOrigin == null)
+        {
+            return;
+        }
+
+        RaycastHit hit;
+        Vector3 laserEndPoint = laserOrigin.position + (laserOrigin.forward * maxDistance);
+
+        if (Physics.Raycast(laserOrigin.position, laserOrigin.forward, out hit, maxDistance, layerMask))
+        {
+            laserEndPoint = hit.point;
+        }
+
+        // Convert world positions to local positions relative to the laser object's transform
+        Vector3 localLaserOrigin = transform.InverseTransformPoint(laserOrigin.position);
+        Vector3 localLaserEndPoint = transform.InverseTransformPoint(laserEndPoint);
+
+        lineRenderer.SetPosition(0, localLaserOrigin);
+        lineRenderer.SetPosition(1, localLaserEndPoint);
+
+        // Place the endPointObject at the end of the laser line
+        if (endPointObject != null)
+        {
+            endPointObject.transform.position = laserEndPoint;
+        }
+    }
+}

+ 133 - 0
assets/LaserReceiver.cs

@@ -0,0 +1,133 @@
+using UdonSharp;
+using UnityEngine;
+using VRC.SDKBase;
+using VRC.Udon;
+using System;
+
+[DisallowMultipleComponent]
+
+[AddComponentMenu("Artyom Scripting System/Laser Receiver Controller")]
+public class LaserReceiver : UdonSharpBehaviour
+{
+    public GameObject targetObject; // The object to turn on/off
+    public int laserLayer = 22; // Layer number to detect the laser
+    public Animator[] animators; // Array to hold Animators
+    public string[] parameterNames; // Array to hold corresponding parameter names
+    public GameObject audioObjectTrue; // GameObject to enable when the state changes to true
+    public GameObject audioObjectFalse; // GameObject to enable when the state changes to false
+    public GameObject audioObjectLoop; // GameObject to loop while the state is true
+    public Collider triggerCollider; // Reference to the trigger collider
+    public Renderer targetRenderer; // Renderer to change material
+    public Material materialTrue; // Material to apply when the state is true
+    public Material materialFalse; // Material to apply when the state is false
+    public int materialIndex = 0; // Index of the material to change
+
+    private int colliderCount = 0; // Counter for colliders with the laser layer
+    private Collider[] collidersInsideTrigger;
+
+    private void Start()
+    {
+        // Ensure the target object is initially off
+        if (targetObject != null)
+        {
+            targetObject.SetActive(false);
+        }
+
+        // Ensure the looping audio is initially off
+        if (audioObjectLoop != null)
+        {
+            audioObjectLoop.SetActive(false);
+        }
+
+        // Initial check for colliders inside the trigger
+        UpdateColliderCount();
+    }
+
+    private void Update()
+    {
+        // Continuously check for colliders inside the trigger
+        UpdateColliderCount();
+    }
+
+    private void UpdateColliderCount()
+    {
+        // Define the size and position of the box for the OverlapBox check
+        Vector3 boxCenter = triggerCollider.bounds.center;
+        Vector3 boxHalfExtents = triggerCollider.bounds.extents;
+
+        // Perform the OverlapBox check
+        collidersInsideTrigger = Physics.OverlapBox(boxCenter, boxHalfExtents, Quaternion.identity, 1 << laserLayer);
+
+        // Update the collider count based on the result
+        int newColliderCount = 0;
+
+        foreach (var col in collidersInsideTrigger)
+        {
+            if (col.GetComponent<VRC_Pickup>() == null)
+            {
+                newColliderCount++;
+            }
+        }
+
+        if (newColliderCount != colliderCount)
+        {
+            colliderCount = newColliderCount;
+            Debug.Log("Updated collider count: " + colliderCount);
+
+            // Turn on or off the target object based on the collider count
+            if (colliderCount > 0)
+            {
+                if (targetObject != null)
+                {
+                    targetObject.SetActive(true);
+                }
+                UpdateAnims(true);
+            }
+            else if (colliderCount == 0)
+            {
+                if (targetObject != null)
+                {
+                    targetObject.SetActive(false);
+                }
+                UpdateAnims(false);
+            }
+        }
+    }
+
+    private void UpdateAnims(bool state)
+    {
+        for (int i = 0; i < animators.Length; i++)
+        {
+            if (animators[i] != null)
+            {
+                animators[i].SetBool(parameterNames[i], state);
+            }
+        }
+
+        // Enable or disable GameObjects based on the state
+        if (state)
+        {
+            if (audioObjectFalse != null) audioObjectFalse.SetActive(false);
+            if (audioObjectTrue != null) audioObjectTrue.SetActive(true);
+            if (audioObjectLoop != null) audioObjectLoop.SetActive(true); // Enable looping audio
+            if (targetRenderer != null && materialTrue != null) SetMaterial(materialTrue); // Change material to true
+        }
+        else
+        {
+            if (audioObjectTrue != null) audioObjectTrue.SetActive(false);
+            if (audioObjectFalse != null) audioObjectFalse.SetActive(true);
+            if (audioObjectLoop != null) audioObjectLoop.SetActive(false); // Disable looping audio
+            if (targetRenderer != null && materialFalse != null) SetMaterial(materialFalse); // Change material to false
+        }
+    }
+
+    private void SetMaterial(Material material)
+    {
+        Material[] materials = targetRenderer.materials;
+        if (materialIndex >= 0 && materialIndex < materials.Length)
+        {
+            materials[materialIndex] = material;
+            targetRenderer.materials = materials;
+        }
+    }
+}

+ 102 - 0
assets/LogicScript.cs

@@ -0,0 +1,102 @@
+using UdonSharp;
+using UnityEngine;
+using VRC.SDKBase;
+using VRC.Udon;
+using System;
+
+[Obsolete("Replaced with Linked Object System")]
+[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
+[DisallowMultipleComponent]
+
+[AddComponentMenu("Artyom Scripting System/_DEPRECATED_/Logic Script")]
+public class LogicScript : UdonSharpBehaviour
+{
+    public Animator[] animators; // Array to hold Animators
+    public string[] parameterNames; // Array to hold corresponding parameter names
+    public bool playerTriggerEnabled = true;
+    public bool pickupTriggerEnabled = true;
+    
+    [UdonSynced]
+    public int isOccupied;
+    
+    public GameObject audioObjectTrue; // GameObject to enable when the state changes to true
+    public GameObject audioObjectFalse; // GameObject to enable when the state changes to false
+
+    void Start()
+    {
+        isOccupied = 0;
+    }
+
+    private void UpdateAnims(bool state)
+    {
+        for (int i = 0; i < animators.Length; i++)
+        {
+            animators[i].SetBool(parameterNames[i], state);
+        }
+
+        // Enable or disable GameObjects based on the state
+        if (state)
+        {
+            if (audioObjectFalse != null) audioObjectFalse.SetActive(false);
+            if (audioObjectTrue != null) audioObjectTrue.SetActive(true);
+        }
+        else
+        {
+            if (audioObjectTrue != null) audioObjectTrue.SetActive(false);
+            if (audioObjectFalse != null) audioObjectFalse.SetActive(true);
+        }
+    }
+
+    private void OnTriggerEnter(Collider other)
+    {
+        if (pickupTriggerEnabled)
+        {
+            VRC_Pickup pickup = other.GetComponent<VRC_Pickup>();
+            if (pickup != null)
+            {
+                isOccupied = isOccupied + 1;
+                UpdateAnims(true);
+            }
+        }
+    }
+
+    public override void OnPlayerTriggerEnter(VRCPlayerApi player)
+    {
+        if (playerTriggerEnabled)
+        {
+            isOccupied = isOccupied + 1;
+            if (isOccupied > 0)
+            {
+                UpdateAnims(true);
+            }
+        }
+    }
+
+    private void OnTriggerExit(Collider other)
+    {
+        if (pickupTriggerEnabled)
+        {
+            VRC_Pickup pickup = other.GetComponent<VRC_Pickup>();
+            if (pickup != null)
+            {
+                isOccupied = isOccupied - 1;
+                if (isOccupied < 1)
+                {
+                    UpdateAnims(false);
+                }
+            }
+        }
+    }
+
+    public override void OnPlayerTriggerExit(VRCPlayerApi player)
+    {
+        if (playerTriggerEnabled)
+        {
+            isOccupied = isOccupied - 1;
+            if (isOccupied < 1)
+            {
+                UpdateAnims(false);
+            }
+        }
+    }
+}

+ 35 - 0
assets/PickupDetector.cs

@@ -0,0 +1,35 @@
+using UdonSharp;
+using UnityEngine;
+using VRC.SDKBase;
+using VRC.Udon;
+using System;
+
+[Obsolete("Replaced with Linked Object System")]
+[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
+[DisallowMultipleComponent]
+
+[AddComponentMenu("Artyom Scripting System/_DEPRECATED_/Pickup Detector")]
+public class PickupDetector : UdonSharpBehaviour
+{
+    // You can specify the pickup object here if you want to check for a specific object
+    public Animator targetAnimator; // The Animator component to control
+    public string parameterName; // The name of the boolean parameter to change
+
+    private void OnTriggerEnter(Collider other)
+    {
+        VRC_Pickup pickup = other.GetComponent<VRC_Pickup>();
+        if (pickup != null)
+        {
+            targetAnimator.SetBool(parameterName, true);
+        }
+    }
+
+    private void OnTriggerExit(Collider other)
+    {
+        VRC_Pickup pickup = other.GetComponent<VRC_Pickup>();
+        if (pickup != null)
+        {
+            targetAnimator.SetBool(parameterName, false);
+        }
+    }
+}

+ 174 - 0
assets/PickupFizzler.cs

@@ -0,0 +1,174 @@
+using UdonSharp;
+using UnityEngine;
+using VRC.SDKBase;
+using VRC.SDK3.Components;
+using VRC.Udon;
+using System;
+[Obsolete("Component under review for further dubugging... Pardon our dust!")]
+
+[DisallowMultipleComponent]
+
+[AddComponentMenu("Artyom Scripting System/Pickup Fizzler")]
+public class PickupFizzler : UdonSharpBehaviour
+{
+    public AudioSource fizzlerSound; // Sound to play when a pickup is fizzled
+    public AudioSource playerEnterSound; // Sound to play when a player enters the collider
+    public int fizzlerLayer = 8; // Layer number for the fizzler
+    public int pickupLayer = 9; // Layer number for the pickup
+    public float fizzleDuration = 2.0f; // Duration for the fizzle effect
+    public Material fizzleMaterial; // Material to apply during the fizzle effect
+    private Material[][] originalMaterials;
+    private VRC_Pickup pickup;
+    private VRCObjectSync objectSync;
+    private Renderer[] renderers;
+    private Collider pickupCollider;
+    private Rigidbody pickupRigidbody;
+    private bool isFizzling = false;
+    private float fizzleTimer = 0.0f;
+    private Vector3 storedVelocity;
+    private Vector3 storedAngularVelocity;
+
+    private void Start()
+    {
+        // Ensure the AudioSource is initially inactive
+        if (fizzlerSound != null)
+        {
+            fizzlerSound.Stop();
+        }
+
+        if (playerEnterSound != null)
+        {
+            playerEnterSound.Stop();
+        }
+    }
+
+    private void OnTriggerEnter(Collider other)
+    {
+        // Check if the collider is on the pickup layer and not already fizzling
+        if (other.gameObject.layer == pickupLayer && !isFizzling)
+        {
+            // Check if the collider has a VRC_Pickup component
+            pickup = other.GetComponent<VRC_Pickup>();
+            if (pickup != null)
+            {
+                // Get the VRCObjectSync component
+                objectSync = pickup.GetComponent<VRCObjectSync>();
+                if (objectSync == null)
+                {
+                    Debug.LogError("No VRCObjectSync component found on the pickup object.");
+                    return;
+                }
+                UdonBehaviour behaviour = (UdonBehaviour)GetComponent(typeof(UdonBehaviour));
+                behaviour.DisableInteractive = true;
+
+                // Get the pickup's collider and rigidbody
+                pickupCollider = pickup.GetComponent<Collider>();
+                pickupRigidbody = pickup.GetComponent<Rigidbody>();
+
+                // Store the current velocity and angular velocity
+                if (pickupRigidbody != null)
+                {
+                    storedVelocity = pickupRigidbody.velocity;
+                    storedAngularVelocity = pickupRigidbody.angularVelocity;
+                }
+
+                // Get the renderers and materials of the pickup
+                renderers = pickup.GetComponentsInChildren<Renderer>();
+                originalMaterials = new Material[renderers.Length][];
+                for (int i = 0; i < renderers.Length; i++)
+                {
+                    originalMaterials[i] = renderers[i].materials;
+                    Material[] newMaterials = new Material[renderers[i].materials.Length];
+                    for (int j = 0; j < newMaterials.Length; j++)
+                    {
+                        newMaterials[j] = fizzleMaterial;
+                    }
+                    renderers[i].materials = newMaterials;
+                }
+
+                // Play the fizzler sound
+                if (fizzlerSound != null)
+                {
+                    fizzlerSound.transform.SetParent(pickup.transform);
+                    fizzlerSound.transform.localPosition = Vector3.zero;
+                    fizzlerSound.Play();
+                }
+
+                // Disable the pickup's collider
+                if (pickupCollider != null)
+                {
+                    pickupCollider.enabled = false;
+                }
+
+                // Force the player to drop the pickup
+                if (Networking.LocalPlayer != null)
+                {
+                    pickup.Drop(Networking.LocalPlayer);
+                }
+
+                // Start the fizzling process
+                isFizzling = true;
+                fizzleTimer = fizzleDuration;
+            }
+        }
+    }
+
+    public override void OnPlayerTriggerEnter(VRCPlayerApi player)
+    {
+        if (playerEnterSound != null && player.isLocal)
+        {
+            playerEnterSound.Play();
+        }
+    }
+
+    private void Update()
+    {
+        if (isFizzling)
+        {
+            fizzleTimer -= Time.deltaTime;
+
+            // Apply the stored velocity and angular velocity
+            if (pickupRigidbody != null)
+            {
+                pickupRigidbody.velocity = storedVelocity;
+                pickupRigidbody.angularVelocity = storedAngularVelocity;
+            }
+            if (Networking.LocalPlayer != null)
+            {
+                pickup.Drop(Networking.LocalPlayer);
+            }
+
+            if (fizzleTimer <= 0.0f)
+            {
+                // Respawn the pickup using VRCObjectSync
+                if (objectSync != null)
+                {
+                    objectSync.Respawn();
+                }
+
+                // Stop and detach the sound
+                if (fizzlerSound != null)
+                {
+                    fizzlerSound.Stop();
+                    fizzlerSound.transform.SetParent(null);
+                }
+
+                // Re-enable the pickup's collider
+                if (pickupCollider != null)
+                {
+                    pickupCollider.enabled = true;
+                }
+
+                // Reset the original materials
+                for (int i = 0; i < renderers.Length; i++)
+                {
+                    renderers[i].materials = originalMaterials[i];
+                }
+
+                isFizzling = false;
+                UdonBehaviour behaviour = (UdonBehaviour)GetComponent(typeof(UdonBehaviour));
+                behaviour.DisableInteractive = false;
+            }
+        }
+    }
+}

+ 79 - 0
assets/PickupMovementFixer.cs

@@ -0,0 +1,79 @@
+using UdonSharp;
+using UnityEngine;
+using VRC.SDKBase;
+using VRC.Udon;
+using System;
+
+[DisallowMultipleComponent]
+
+[AddComponentMenu("Artyom Scripting System/Pickup Following Mover Fix")]
+public class PickupMovementFixer : UdonSharpBehaviour
+{
+    public GameObject targetObject;  // The object to follow
+
+    private int pickupLayer = 13;  // The layer assigned to pickup objects
+    private Collider triggerCollider;
+    private Vector3 previousTargetPosition;
+    private Vector3 targetVelocity;
+
+    private void Start()
+    {
+        triggerCollider = GetComponent<Collider>();
+        if (triggerCollider == null || !triggerCollider.isTrigger)
+        {
+            Debug.LogError("PickupMovementFixer: Collider must be set as a trigger.");
+        }
+        previousTargetPosition = targetObject.transform.position;
+    }
+
+    private void Update()
+    {
+        Vector3 currentTargetPosition = targetObject.transform.position;
+        targetVelocity = (currentTargetPosition - previousTargetPosition) / Time.deltaTime;
+        previousTargetPosition = currentTargetPosition;
+    }
+
+    private void OnTriggerEnter(Collider other)
+    {
+        if (other.gameObject.layer == pickupLayer)
+        {
+            VRC_Pickup pickup = other.gameObject.GetComponent<VRC_Pickup>();
+            if (pickup != null)
+            {
+                Vector3 relativePosition = other.transform.position - targetObject.transform.position;
+                pickup.gameObject.transform.position = targetObject.transform.position + relativePosition;
+                Rigidbody pickupRigidbody = other.GetComponent<Rigidbody>();
+                if (pickupRigidbody != null)
+                {
+                    pickupRigidbody.velocity = targetVelocity;
+                }
+            }
+        }
+    }
+
+    private void OnTriggerStay(Collider other)
+    {
+        if (other.gameObject.layer == pickupLayer)
+        {
+            VRC_Pickup pickup = other.gameObject.GetComponent<VRC_Pickup>();
+            if (pickup != null)
+            {
+                Vector3 relativePosition = other.transform.position - targetObject.transform.position;
+                pickup.gameObject.transform.position = targetObject.transform.position + relativePosition;
+                Rigidbody pickupRigidbody = other.GetComponent<Rigidbody>();
+                if (pickupRigidbody != null)
+                {
+                    pickupRigidbody.velocity = targetVelocity;
+                }
+            }
+        }
+    }
+
+    private void OnTriggerExit(Collider other)
+    {
+        if (other.gameObject.layer == pickupLayer)
+        {
+            // Optionally, you can add code here to handle when the pickup exits the trigger
+        }
+    }
+}

+ 46 - 0
assets/PickupSpeedLimiter.cs

@@ -0,0 +1,46 @@
+using UdonSharp;
+using UnityEngine;
+using VRC.SDKBase;
+using VRC.Udon;
+using System;
+
+[DisallowMultipleComponent]
+
+[AddComponentMenu("Artyom Scripting System/Pickup Speed Limiter")]
+
+public class PickupSpeedLimiter : UdonSharpBehaviour
+{
+    public float maxSpeed = 5.0f; // Maximum allowed speed for the pickup
+    public bool debugSpeed;
+    private VRC_Pickup pickup; // Reference to the VRC_Pickup component
+
+    void Start()
+    {
+        pickup = GetComponent<VRC_Pickup>();
+
+        if (pickup == null)
+        {
+            Debug.LogError("No VRC_Pickup component found on this GameObject.");
+        }
+    }
+
+    void Update()
+    {
+        if (pickup != null && !(pickup.IsHeld))
+        {
+            Rigidbody rb = pickup.GetComponent<Rigidbody>();
+            if (rb != null)
+            {
+                Vector3 velocity = rb.velocity;
+                
+                if (debugSpeed) {
+                    if (velocity.magnitude > maxSpeed) { Debug.LogWarning("Speed of cube: " + rb.velocity.magnitude.ToString());} 
+                    Debug.Log("Speed of cube: " + rb.velocity.magnitude.ToString()); }
+                if (velocity.magnitude > maxSpeed)
+                {
+                    rb.velocity = velocity.normalized * maxSpeed;
+                }
+            }
+        }
+    }
+}

+ 32 - 0
assets/PlayerTrigger.cs

@@ -0,0 +1,32 @@
+
+using UdonSharp;
+using UnityEngine;
+using VRC.SDKBase;
+using VRC.Udon;
+using System;
+
+[Obsolete("Replaced with Linked Object System")]
+[DisallowMultipleComponent]
+
+[AddComponentMenu("Artyom Scripting System/Pickup Following Mover Fix")]
+public class PlayerTrigger : UdonSharpBehaviour
+{
+    public Animator targetAnimator; // The Animator component to control
+    public string parameterName; // The name of the boolean parameter to change
+
+    public override void OnPlayerTriggerStay(VRCPlayerApi player)
+    {
+        if (targetAnimator != null && !string.IsNullOrEmpty(parameterName))
+        {
+            targetAnimator.SetBool(parameterName, true); // Set the boolean parameter to true
+        }
+    }
+
+    public override void OnPlayerTriggerExit(VRCPlayerApi player)
+    {
+        if (targetAnimator != null && !string.IsNullOrEmpty(parameterName))
+        {
+            targetAnimator.SetBool(parameterName, false); // Set the boolean parameter to false
+        }
+    }
+}

+ 34 - 0
assets/TimedTeleport.cs

@@ -0,0 +1,34 @@
+
+using System;
+using UdonSharp;
+using UnityEngine;
+using VRC.SDKBase;
+using VRC.Udon;
+
+[AddComponentMenu("Artyom Scripting System/Timed Teleport")]
+[DisallowMultipleComponent]
+public class TimedTeleport : UdonSharpBehaviour
+{
+    [Header("Teleportation Settings")]
+    public Transform teleportTarget; // The transform to teleport the player to
+    public float delay = 1f; // The delay in seconds before teleportation
+
+    public override void OnPlayerTriggerEnter(VRCPlayerApi player)
+    {
+        if (Networking.LocalPlayer == player)
+        {
+            SendCustomEventDelayedSeconds(nameof(TeleportPlayer), delay);
+        }
+        
+    }
+
+    public void TeleportPlayer()
+    {
+        Networking.LocalPlayer.TeleportTo(teleportTarget.position, 
+                                          teleportTarget.rotation, 
+                                          VRC_SceneDescriptor.SpawnOrientation.Default, 
+                                          false
+                                        );
+    }
+}
+

+ 12 - 0
usage-docs/BoneLookAtPLayer.md

@@ -0,0 +1,12 @@
+# Bone Look At Player
+Intended as a rotation for objects like cameras, NPCs, or other items that should face the nearest player.
+
+## Requires:
+- Nothing
+
+## Object To Rotate - Type: GameObject
+- Takes a bone GameObject (or any other GameObject for all I care) and sets it as the target to rotate.
+## Rotation Speed - Type: Float
+- Limits the rotation speed to this value in degrees per second.
+## Rotation Offset - Type: Vector3
+- In the case that the bone is offset from the root object and needs an offset, the target point will be offset by this amount in degrees per axis of transform.

+ 9 - 0
usage-docs/Checkpoint.md

@@ -0,0 +1,9 @@
+# Checkpoint
+
+Intended as a save/load system, though ended up just moving the respawn location to prevent stinky cheaters or accidental softlocks.
+
+## Requires:
+- Collider set to Is Trigger
+
+## Target Object - Type: GameObject
+- Intended to be used for the VRCWorld component to track progress and move the respawn location for the local player.

+ 42 - 0
usage-docs/ImpactSoundPlayer.md

@@ -0,0 +1,42 @@
+## Impact Sound Player
+
+Diegetic sounds can be tough and scaling only volume with impact force doesn't sound right. May as well have three distinct sounds, right?
+
+## Requires:
+- **VRC Pickup**
+- **VRC Object Sync**
+- **Rigidbody**
+- **Audio Source attached to GameObject (3)**
+
+## Small Impact Sound Object - Type: GameObject
+- Enables the GameObject under the condition that the velocity on impact with another collider is less than the **Medium Impact Threshold**.
+
+## Medium Impact Sound Object - Type: GameObject
+- Enables the GameObject under the condition that the velocity on impact with another collider is more than the **Medium Impact Threshold**, but less than the **Large Impact Threshold**.
+
+## Large Impact Sound Object - Type: GameObject
+- Enables the GameObject under the condition that the velocity on impact with another collider is more than the **Large Impact Threshold**.
+
+## Medium Impact Threshold - Type: Float
+- The velocity requirement for a **Medium Impact Sound Object** to trigger instead of a **Small Impact Sound Object**.
+  
+- **Default Value:** `5.0`
+
+## Large Impact Threshold - Type: Float
+- The velocity requirement for a **Large Impact Sound Object** to trigger instead of a **Medium Impact Sound Object**.
+  
+- **Default Value:** `10.0`
+
+---
+
+### Usage Instructions:
+1. Attach this script to a GameObject in Unity.
+2. Assign the respective GameObjects for the **Small**, **Medium**, and **Large Impact Sound Objects**.
+3. Set the desired thresholds for **Medium Impact Threshold** and **Large Impact Threshold**.
+4. Ensure the GameObject has the required components: **Rigidbody**, **VRC Pickup**, and **VRC Object Sync**.
+
+### Script Behavior:
+- When a collision occurs, the script evaluates the impact velocity:
+  - If the velocity is **less than the Medium Impact Threshold**, the **Small Impact Sound Object** is activated.
+  - If the velocity is **between the Medium and Large Impact Thresholds**, the **Medium Impact Sound Object** is activated.
+  - If the velocity is **greater than the Large Impact Threshold**, the **Large Impact Sound Object** is activated.

+ 48 - 0
usage-docs/InteractScript.md

@@ -0,0 +1,48 @@
+## Interact Script
+
+**Important:** This script is marked as **DEPRECATED** and has been replaced with the **Linked Object System**. It is advised to use the newer system for future development.
+
+## Requires:
+- **VRC Pickup** (for ownership management)
+- **VRC Object Sync**
+- **Animator** component attached to GameObjects
+
+## Animators - Type: Array of Animators
+- Holds the list of animators to manipulate based on the interaction type.
+
+## Parameter Names - Type: Array of Strings
+- Defines the parameter names in the Animators corresponding to interaction states.
+
+## Interaction Type - Type: Enum
+- Specifies the type of interaction:
+  - **Temporary:** Activates state for a fixed duration.
+  - **Permanent:** Sets the state permanently.
+  - **Toggle:** Toggles between active and inactive states.
+
+## Temporary Duration - Type: Float
+- The duration (in seconds) for which the state is active in a **Temporary** interaction.
+  - **Default Value:** `2.0`
+
+## Audio Object True - Type: GameObject
+- The GameObject to activate when the state changes to true.
+
+## Audio Object False - Type: GameObject
+- The GameObject to activate when the state changes to false.
+
+---
+
+### Usage Instructions:
+1. Attach this script to a GameObject in Unity.
+2. Assign the desired **Animators** and corresponding **Parameter Names**.
+3. Set the **Interaction Type** according to the desired behavior.
+4. Specify additional parameters such as **Temporary Duration**, **Audio Object True**, and **Audio Object False**.
+
+### Script Behavior:
+- Upon interaction:
+  - Depending on the **Interaction Type**, the state of the GameObject is updated:
+    - **Temporary:** Activates the state and reverts it after the specified duration.
+    - **Permanent:** Activates the state indefinitely.
+    - **Toggle:** Switches between active and inactive states.
+  - Synchronizes the state across the network via **VRC Udon Networking**.
+
+**Note:** As this script is deprecated, transitioning to the **Linked Object System** is strongly recommended to ensure compatibility with future updates.

+ 57 - 0
usage-docs/JumpPad.md

@@ -0,0 +1,57 @@
+## Jump Pad Controller
+
+This script provides a fully functional Jump Pad system for use in VRChat worlds. The Jump Pad propels players or objects towards a target with a smooth arc, complete with optional animation and debug logging.
+
+## Requires:
+- **Animator** component (optional but recommended for visual feedback)
+- **Colliders** for trigger detection
+
+## Debug - Type: Boolean
+- Enables or disables debug messages for troubleshooting.
+
+## Launch Animation - Type: Animator
+- The Animator used to play a launch animation.
+- **Default Parameter Name:** `isLaunch`
+
+## Jump Target - Type: Transform
+- Specifies the target point where the Jump Pad propels the player or object.
+
+## Self - Type: Transform
+- The Transform of the Jump Pad itself, used for calculating the jump arc.
+
+## Arc Time - Type: Float
+- The duration (in seconds) for the arc trajectory.
+  - **Default Value:** `1.2`
+
+## Walk Limit - Type: Float
+- The restricted walk speed for players during the jump.
+  - **Default Value:** `0.2`
+
+## Run Limit - Type: Float
+- The restricted run speed for players during the jump.
+  - **Default Value:** `0.4`
+
+## Strafe Limit - Type: Float
+- The restricted strafe speed for players during the jump.
+  - **Default Value:** `0.2`
+
+---
+
+### Usage Instructions:
+1. Attach this script to the Jump Pad GameObject in Unity.
+2. Assign the **Jump Target** Transform to define the destination of the jump.
+3. (Optional) Assign an **Animator** component and set the animation parameter name if different from the default.
+4. Configure the **Arc Time**, **Walk Limit**, **Run Limit**, and **Strafe Limit** as needed.
+
+### Script Behavior:
+- When a player or object enters the Jump Pad:
+  - Calculates and applies the velocity required to reach the **Jump Target**.
+  - Limits the player’s movement for the duration of the jump.
+  - Activates the launch animation (if an Animator is assigned).
+  - Synchronizes the event using **VRC Udon Networking**.
+- Once the jump completes:
+  - Restores the player’s movement to default speeds.
+  - Deactivates the launch animation.
+
+### Debug Mode:
+- Outputs detailed logs to Unity’s console, including calculated velocities and state changes, to assist with testing and fine-tuning.