Purpose: This pair of U# scripts enables you to hide collectables within a world, allowing you to track player progress and trigger specific in-game effects once the collectables are found.
When triggered, this script can change an object's visibility, interactability, material, or play audio. It then sends a function to the second script to track the total number of collectables found.
I have intentionally excluded a dedicated completion script. This gives users the flexibility to implement their own custom end-game effects, as designing a robust, one-size-fits-all solution for final events is difficult.
Details: Unity 2022.3.22f1, VRChat SDK - Worlds 3.8.2, VRChat Package Resolver Tool 0.1.29, VRChat SDK - Base 3.8.2
Last Edit: 16 November 2025 (Baste~)
CollectionMinigame Script Documentation
Purpose: Purpose: The CollectionMinigame class Script stores the amount of collectables the player has found. Additionally it can output text to a TextMeshPro object so that the counter can be updated in real time in game.
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class CollectionMinigame : UdonSharpBehaviour
{
//Makes header section in unity for our script
[Header("Pickup Script Options")]
//Total amount of items that can be picked up
public int TotalPickups;
//The amount of pickups the player has gethered so far
public int PickupsCollected;
//Optional text that can be modified to show how many collectables have been picked up
public TMPro.TextMeshProUGUI AmountCollectedText;
//Optional text that can be modified to show how many collectables can be picked up
public TMPro.TextMeshProUGUI TotalCollectableText;
// Start Function https://docs.unity3d.com/ScriptReference/MonoBehaviour.Start.html
public void Start()
{
//Sets our max collectable item text to the max collectable items. Int is converted to string using the //.ToString() Function
//https://learn.microsoft.com/en-us/dotnet/api/system.object.tostring?view=net-9.0
//Since I don't change the max In game this is only called in the Start function once as the game starts. / is //added plainly for formatting
TotalCollectableText.text = "/" + TotalPickups.ToString();
}
//Our custom function for Item Pickup
public void PickupItem()
{
// Adds +1 to our PickupsCollected integer variable.
PickupsCollected++;
//Send message to our debug log for testing purposes
Debug.Log("Item Collected! Total: " + PickupsCollected);
//Writes the new total we have collected so far to our AmountCollectedText for output (Optional)
AmountCollectedText.text = PickupsCollected.ToString();
}
}
Pickup Item Script Documentation
Purpose: The Pickup class Script defines the items the player interacts with. Upon triggering, it calls the PickupItem() function in the CollectionMinigame class to increment the global collectable counter by one. This script also manages post-pickup behavior, including playing audio, changing materials, and toggling visibility and interaction states.
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class Pickup : UdonSharpBehaviour
{
[Header("Pickup Script References")]
//Grabs our overall information and functions from the main Collectable Script.
public CollectionMinigame CollectionMinigame;
//References the Optional Audio source to play a sound when the object is interacted with.
public AudioSource PickupSound;
//References the Optional material that can be changed to upon triggering the object.
public Material NewMaterial;
[HideInInspector]// Hides variable in unity inspector. Cleaner than old way :3
//Variable for our GameObject's renderer so that we can change materials
public Renderer ItemRenderer;
//Makes header for out in-editor options
[Header("Pickup Script Options")]
//Makes InactiveOnPickup have an in-editor toggle
[SerializeField]
//Bool to determine if Item should become inactive after use (Inactive = I.E. Dissapears and is no longer interactable).
public bool InactiveOnPickup;
[SerializeField]
//Bool to determine if Item should become uninteractable after use (I.E. Item is still be visible but will not longer be able to be triggered).
//NoInteractionAfterPickup does not need to be used if InactiveOnPickup is already being used.
public bool NoInteractionAfterPickup;
[SerializeField]
//Bool to determine if item should play a sound with interact
public bool PlayAudio;
[SerializeField]
//Bool to determine if the material of the object should change after being triggered.
public bool ChangeMaterial;
void Start()
{
if (ChangeMaterial)
{
//Gets the SPECIFIC (single game object) Renderer from our GameObject so that we can replace the material
//Note gameObject as apposed to a variable GameObject refers to the item our script is attached to. (Identifies self)
ItemRenderer = gameObject.GetComponent<Renderer>();
}
}
//Interact is provided by VRChat and tiggers when a player interacts with an item (I.E. Clicks on the item, this does not
//include touching->
//the item without clicking on it. https://udonsharp.docs.vrchat.com/events
void Interact()
{
//Here we reference the PickupItem function from our CollectionMinigame script.
CollectionMinigame.PickupItem();
//Runs if PlayAudio is selected.
if(PlayAudio)
{
//Plays selected audio source.
PickupSound.Play();
}
//Runs if InactiveOnPickup is set to true.
if (InactiveOnPickup)
{
//Sets the item that was collected to be inactive (Inactive = I.E. Dissapears and is no longer interactable).
gameObject.SetActive(false);
}
//Runs if NoInteractionAfterPickup is set to true.
if(NoInteractionAfterPickup)
{
//Make the object unable to be triggered again but still visible.
DisableInteractive = true;
}
//Runs if ChangeMaterial is set to true.
if(ChangeMaterial)
{
//Changed from the normal material to the chosen second material. .material changes the specific (one) GameObjects Material. (Only ->
//This gameobjects material is change and other objects with this material are left unchanged.
ItemRenderer.material = NewMaterial;
//.sharedMaterial Can be used to change all instances of a material to a new material (IE all stone material in the game can change to ->
//Gold Material)
//ItemRenderer.sharedMaterial = NewMaterial;
}
}
}
In-Unity Application and Use
Last Edit: 10 November 2025 (Baste~)
Details: Unity 2022.3.22f1, VRChat SDK - Worlds 3.8.2, VRChat Package Resolver Tool 0.1.29, VRChat SDK - Base 3.8.2
Purpose: To provide a guide on how to implement and use both the CollectionMinigame and Pickup script in Unity.
Step 1: Download the scripts (or copy the code) from the sections above, and then place them into a relevant folder within your Unity Project.
Step 2: Inside your Unity project, we must create several GameObjects to act as containers for the code. You can use any 3D models you like (e.g., Cylinders and a Sign), as they are just placeholders for the scripts.
Step 3: Select the GameObject where the primary game logic will reside. Attach the CollectionMinigame script to this object. I recommend using a persistent object like a Scoreboard or Game Manager to make it easy to locate later.
This can be done by either:
1.Select the desired GameObject, then drag the script file from the Project window (the folder at the bottom) directly into the Inspector window on the right.
OR
2. Select the GameObject, scroll to the bottom of the Inspector, and click "Add Component". Search for "Udon Behavior" and click it to add the component.
From here you can add the Script or make a new Script and copy it over.
Step 4: In the Inspector panel, you can now customize the script's initial settings. Use the "Total Pickups" field to define the exact number of collectibles a player must gather. For example, if you have six objects, enter the value 6.
To provide players with a visible update, you can link a TextMeshPro (TMP) object to the script's properties. This will automatically update to show the current number of items collected compared to the total required.
Right-click in the Hierarchy, select UI > Text - TextMeshPro. (If prompted, import the TMP Essentials.) Once the TMP object is created, right click it and select "Properties..." to pull up a second inspector. In the script's Inspector panel, drag the TextMeshPro component from the newly created Text GameObject into the corresponding script field.
Step 5: Select a GameObject you intend to use as a collectible item. Drag and drop the Pickup script directly onto that object's Inspector panel to attach the script.
Within this script, you will establish the necessary links to CollectionMinigame and define the collectible's behavior after it has been successfully picked up (triggered).
Step 6: Link the CollectionMinigame slot with the script you placed earlier. Drag the CollectionMinigame script directly from the Inspector of the object you created in Step 3. (This is why placing it on a visible related object like a Scoreboard is helpful!)
Step 7: (Optional) If you plan to add a sound effect upon collection, you must first add an Audio Source component to your collectible item. Note: The Pickup script only triggers the sound; all volume, clip, and spatial settings are managed within the Audio Source component. Crucially, ensure the "Play On Awake" box is unchecked.
Step 8: (Optional) If you want the collectible to visually change after being picked up, drag the desired Material asset into the "New Material" field. This will cause the object's appearance to instantly swap upon collection.
Step 9: Select one or more of the following options to define how the collectible reacts when a player interacts with it:
Inactive On Pickup: The item is disabled immediately, making it visually disappear and unusable.
No Interaction After Pickup: The item remains visible but its collider is disabled, making it unusable for further collection.
Play Audio: Triggers the attached Audio Source component.
Change Material: Swaps the object's current material with the one specified in the "New Material" field.
Hint: If no options are selected, the item remains active and infinitely usable, continually raising the collection counter (useful for clicker-style games).
Step 10: Verify that all collectibles possess a Collider component. For the Pickup script to function correctly, go to the Collider's settings and confirm that both "Is Trigger" and "Convex" (if using a Mesh Collider) are checked.
Step 11: You can customize the message that appears to the user when they are near the item by editing the "Interaction Text" field and its related properties under the Pickup script. This provides clear feedback to the player.
END - Until someone finds my mistakes