Prechádzať zdrojové kódy

Linked Object System: trigger Udon scripts and a station (1.2.0)

Script links call an event on each linked UdonBehaviour when the output turns on and another when it turns
off: on every client locally, or (Network Events) by the owner as network events. A station seats the local
player who turns the output on by interacting or walks into the trigger; they leave it themselves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ArtyomV2X 1 mesiac pred
rodič
commit
79702ccd36

+ 15 - 1
Documentation~/LinkedObjectSystem.md

@@ -1,6 +1,6 @@
 ## Linked Object System
 
-Drives Animator bools and two audio objects from interactions and triggers: doors, buttons, pressure plates, lights. It replaces the Interact Script, Logic Script, Pickup Detector and Player Trigger.
+Drives Animator bools, two audio objects, Udon scripts and a station from interactions and triggers: doors, buttons, pressure plates, lights, rides. It replaces the Interact Script, Logic Script, Pickup Detector and Player Trigger.
 
 ## Requires:
 - **Collider** (set to Is Trigger for the trigger types; any collider for interactions)
@@ -35,11 +35,25 @@ Drives Animator bools and two audio objects from interactions and triggers: door
 ## Audio Object True / Audio Object False - Type: GameObject
 - Audio Object True is active while the output is on, Audio Object False while it's off. Either can be left empty.
 
+## Output Behaviours / On Event Names / Off Event Names - Type: Arrays
+- Each Udon script gets its matching **On Event Name** called when the output turns on, and its **Off Event Name** when it turns off. Leave a name empty to send nothing for that change.
+
+## Network Events - Type: Boolean
+- **Off** (default): every player's client calls the events itself when its output changes, the same way the Animators are set. Players who join later get the current state's event when their output first changes.
+- **On:** only this object's owner calls the events, as network events to everyone. Use it when the target script should run once per change, decided by one player. Players who join later don't get past events, the target scripts need networking (not sync method None), and network event names can't start with `_`.
+- **Default Value:** `false`
+
+## Station - Type: VRCStation
+- The player who turns the output on by interacting, or who walks into the trigger, is seated in this station. They leave it themselves; turning the output off doesn't eject anyone.
+- A station holds one player. For several seats, use a Linked Object System per seat.
+- A seated player may be moved out of the trigger, which counts as leaving it. Put the station outside the trigger, or use an interaction, if the output should stay on.
+
 ---
 
 ### Script Behavior:
 - The output is on while the interaction is on, **or** anyone or anything counted is inside the trigger.
 - Interactions are synced: everyone sees them, including players who join later.
+- Script events and the station follow the output: events on every change, the station when the local player turns it on or walks in.
 - Triggers aren't synced, because every player sees the same players and synced pickups enter. A player is only counted once, players who leave the instance stop counting, and pickups that are disabled or destroyed inside stop counting within a second.
 
 ### Upgrading from 1.0.x:

+ 1 - 1
README.md

@@ -12,7 +12,7 @@ UdonSharp utility scripts for VRChat worlds. Every component is under **Add Comp
 | Jump Pad Controller | Launches players and pickups in an arc onto a target | [JumpPad](Documentation~/JumpPad.md) |
 | Laser Beam Controller | Draws a laser to whatever it hits | [LaserBeam](Documentation~/LaserBeam.md) |
 | Laser Receiver Controller | Turns things on while a laser reaches it | [LaserReceiver](Documentation~/LaserReceiver.md) |
-| Linked Object System | Drives Animator bools from synced interactions and triggers | [LinkedObjectSystem](Documentation~/LinkedObjectSystem.md) |
+| Linked Object System | Drives Animators, audio, Udon scripts and a station from synced interactions and triggers | [LinkedObjectSystem](Documentation~/LinkedObjectSystem.md) |
 | Pickup Fizzler | Portal-style fizzler that respawns pickups (under review) | [PickupFizzler](Documentation~/PickupFizzler.md) |
 | Pickup Following Mover Fix | Keeps pickups riding on moving platforms | [PickupMovementFixer](Documentation~/PickupMovementFixer.md) |
 | Pickup Speed Limiter | Caps how fast a thrown pickup can fly | [PickupSpeedLimiter](Documentation~/PickupSpeedLimiter.md) |

+ 49 - 1
Runtime/LinkedObjectSystem.cs

@@ -1,6 +1,9 @@
 using UdonSharp;
 using UnityEngine;
+using VRC.SDK3.Components;
 using VRC.SDKBase;
+using VRC.Udon;
+using VRC.Udon.Common.Interfaces;
 
 [UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
 [DisallowMultipleComponent]
@@ -28,6 +31,21 @@ public class LinkedObjectSystem : UdonSharpBehaviour
     public GameObject audioObjectTrue;
     public GameObject audioObjectFalse;
 
+    [Header("Script Links\n")]
+    [Tooltip("Udon scripts told when the output turns on or off")]
+    public UdonBehaviour[] outputBehaviours;
+    [Tooltip("For each script, the event called when the output turns on. Leave an entry empty for none")]
+    public string[] onEventNames;
+    [Tooltip("For each script, the event called when the output turns off. Leave an entry empty for none")]
+    public string[] offEventNames;
+    [Tooltip("Off: every player's client calls the events itself. On: only this object's owner calls them, as network " +
+             "events to everyone (players who join later don't get them, and event names can't start with _)")]
+    public bool networkEvents;
+
+    [Header("Station\n")]
+    [Tooltip("The player who turns this on by interacting, or who walks into the trigger, is seated here. They leave it themselves")]
+    public VRCStation station;
+
     // PRIVATE VARS
 
     // Interactions only happen on the interacting player's client, so their result is synced.
@@ -60,16 +78,20 @@ public class LinkedObjectSystem : UdonSharpBehaviour
     {
         if (interactionType == InteractionType.Permanent)
         {
-            if (!interactState) SetInteractState(true);
+            if (interactState) return;
+            SetInteractState(true);
+            SeatLocalPlayer();
         }
         else if (interactionType == InteractionType.Toggle)
         {
             SetInteractState(!interactState);
+            if (interactState) SeatLocalPlayer();
         }
         else if (interactionType == InteractionType.Temporary)
         {
             if (interactState) return;
             SetInteractState(true);
+            SeatLocalPlayer();
             SendCustomEventDelayedSeconds(nameof(DisableTemp), delayedInteractTime);
         }
     }
@@ -121,6 +143,7 @@ public class LinkedObjectSystem : UdonSharpBehaviour
         playerCount++;
         if (debugMode) Debug.Log("Linked Object System: players inside: " + playerCount.ToString());
         UpdateOutput();
+        if (player.isLocal) SeatLocalPlayer();
     }
 
     public override void OnPlayerTriggerExit(VRCPlayerApi player)
@@ -245,6 +268,31 @@ public class LinkedObjectSystem : UdonSharpBehaviour
         if (debugMode) Debug.Log("Linked Object System: GameObject set audio to " + state.ToString());
         if (audioObjectTrue != null) audioObjectTrue.SetActive(state);
         if (audioObjectFalse != null) audioObjectFalse.SetActive(!state);
+
+        SendEvents(state);
+    }
+
+    private void SendEvents(bool state)
+    {
+        // Networked: the owner tells everyone, so the others mustn't send it again
+        if (networkEvents && !Networking.IsOwner(gameObject)) return;
+        string[] names = state ? onEventNames : offEventNames;
+        int count = Mathf.Min(outputBehaviours.Length, names.Length);
+        for (int i = 0; i < count; i++)
+        {
+            if (outputBehaviours[i] == null || string.IsNullOrEmpty(names[i])) continue;
+            if (debugMode) Debug.Log("Linked Object System: sending " + names[i] + (networkEvents ? " to everyone" : ""));
+            if (networkEvents) outputBehaviours[i].SendCustomNetworkEvent(NetworkEventTarget.All, names[i]);
+            else outputBehaviours[i].SendCustomEvent(names[i]);
+        }
+    }
+
+    /// <summary>Seats the local player in the station, if there is one: they turned this on, or walked into the trigger.</summary>
+    private void SeatLocalPlayer()
+    {
+        if (station == null) return;
+        if (debugMode) Debug.Log("Linked Object System: seating the local player");
+        station.UseStation(Networking.LocalPlayer);
     }
 }
 

+ 1 - 1
package.json

@@ -1,7 +1,7 @@
 {
     "name": "dev.av2x.aumss",
     "displayName": "AUMSS - Artyom's Udon Modular Scripting System",
-    "version": "1.1.1",
+    "version": "1.2.0",
     "unity": "2022.3",
     "description": "UdonSharp utility scripts for VRChat worlds: jump pads, checkpoints, lasers, pickup helpers, timed teleports and the Linked Object System.",
     "author": {