// RendererVisibleForUser.cs using Simuspaces.Simulation; using System.Collections.Generic; public class RendererVisibleForUser : SimuScriptBase { // Paste/slot the object whose renderer will be changed. public SceneObject TargetObject; // Paste/slot the button or switch object here. public SceneObject Button; // Renderer selection settings. public int RendererIndex = 0; public bool IncludeChildren = true; // Interaction settings. public bool HighlightButton = true; public float InteractDistance = 10f; // Default visibility when a user joins or rejoins. public bool DefaultVisible = true; // Stores temporary visibility independently for each user. private readonly Dictionary userVisibility = new Dictionary( System.StringComparer.OrdinalIgnoreCase ); public override void Start() { base.Start(); // Wait 10 seconds before globally registering the button. StartCoroutine(RegisterButtonAfterDelay()); // Establish the shared starting state. ApplyRendererState(null, DefaultVisible); } public override void OnUserJoin(SVUserInfo user) { string username = user != null ? user.Username : null; // Make the button available to the joining user. RegisterButton(username); if (string.IsNullOrWhiteSpace(username)) return; username = username.Trim(); // Rejoining resets this user to the default state. userVisibility[username] = DefaultVisible; ApplyRendererState(username, DefaultVisible); } public override void OnUserPart(SVUserInfo user) { string username = user != null ? user.Username : null; if (!string.IsNullOrWhiteSpace(username)) userVisibility.Remove(username.Trim()); } private System.Collections.IEnumerator RegisterButtonAfterDelay() { yield return new WaitForSeconds(10f); RegisterButton(null); Log("[Renderer Switch] Finished 10-second button delay."); } private void RegisterButton(string targetUsername) { if (Button == null || Button.IsNull) { Log("[Renderer Switch] Slot a SceneObject into Button."); return; } MakeInteractable( Button, OnButtonClicked, HighlightButton, targetUsername, true, InteractDistance, "Toggle visibility" ); } private void OnButtonClicked(SVInteractionInfo info) { if (TargetObject == null || TargetObject.IsNull) { Log("[Renderer Switch] Slot a SceneObject into TargetObject."); return; } string username = info != null ? info.Username : null; if (string.IsNullOrWhiteSpace(username)) return; username = username.Trim(); bool isVisible = DefaultVisible; if (userVisibility.ContainsKey(username)) isVisible = userVisibility[username]; isVisible = !isVisible; userVisibility[username] = isVisible; // Change visibility only for the user who clicked. ApplyRendererState(username, isVisible); Log("[Renderer Switch] Visibility for '" + username + "' is now " + (isVisible ? "ON" : "OFF")); } private void ApplyRendererState( string targetUsername, bool visible ) { if (TargetObject == null || TargetObject.IsNull) return; if (string.IsNullOrWhiteSpace(targetUsername)) { // No username means the shared state is sent to everyone. SetRendererVisible( TargetObject, visible, null, RendererIndex, IncludeChildren ); } else { // A username changes visibility only for that user. SetRendererVisibleForUser( targetUsername, TargetObject, visible, RendererIndex, IncludeChildren ); } } }