Spot Light not visible even though NetworkBehaviour and RPCs work correctly

Hi,
I’m using Unity Netcode and trying to toggle a Spot Light on my player prefab using RPCs.
The code and logs show that everything is working as expected:

  • The ServerRpc and ClientRpc are called correctly
  • The spotlight.enabled value changes as intended
  • The logs confirm the state changes on both server and client

However, players cannot see each other’s Spot Light in the scene, even though the component is enabled and all settings (Intensity, Range, Spot Angle, etc.) are correct.
I’ve checked the following:

  • The Spot Light is a child of the player prefab
  • The light is assigned in the inspector
  • Culling Mask, Layer, and Render Mode are all set properly
  • The scene is dark enough to see the light
  • No errors or warnings in the console

Here is a screenshot of the logs:

using Unity.Netcode;
using UnityEngine;

public class PlayerSpotlight : NetworkBehaviour
{
    public Light spotlight;
    private bool isLightOn = true;

    void Start()
    {
        spotlight.enabled = isLightOn;
        Debug.Log($"[PlayerSpotlight] Start - spotlight enabled: {spotlight.enabled}");
    }

    void Update()
    {
        if (IsOwner && Input.GetKeyDown(KeyCode.F))
        {
            Debug.Log("[PlayerSpotlight] F key pressed, sending ServerRpc");
            RequestToggleLightServerRpc();
        }
    }

    [ServerRpc]
    void RequestToggleLightServerRpc(ServerRpcParams rpcParams = default)
    {
        isLightOn = !isLightOn;
        Debug.Log($"[Server] Toggle light to: {isLightOn}");
        UpdateLightClientRpc(isLightOn);
    }

    [ClientRpc]
    void UpdateLightClientRpc(bool newState)
    {
        isLightOn = newState;
        Debug.Log($"[Client] Received light state: {newState}");
        if (spotlight != null)
        {
            spotlight.enabled = isLightOn;
            Debug.Log($"[Client] Spotlight enabled set to: {spotlight.enabled}");
        }else
        {
            Debug.LogError("[Client] Spotlight is null!");
        }
    }
}


Is there anything I might be missing that would cause the Spot Light to not be visible, even though it is enabled and all the logic is working?
Any help would be appreciated!

Be sure to verify logs on both ends to find any discrepancies. It can get quite confusing to reason about network state without that synchronicity.

Your simplified setup probably is:

Host:

  • Host Player (Owner)
  • Client Player (Remote)

Client:

  • Host Player (Remote)
  • Client Player (Owner)

So what you do when Client enables light is the following:

  • Client: Player (Owner) sends “on” to Player (Remote) on the Host side
  • Host: Player (Remote) toggles the isLightOn bool and sends the ClientRPC
  • Client: receives RPC, turns on light for “Client Player (Owner)”
  • Host: the ClientRPC should also run for the host and turn on the light

In theory this should work but I wonder if somehow the ClientRPC doesn’t run for the Host? The use of the ServerRpcParams might cause this but you don’t seem to use them.

You could also check in the hierarchy whether manually enabling the light works on both host and client for both local and remote players.

These are outdated. We use [Rpc] these days which has parameters. You are using Netcode 2.x right, not the old 1.x?

PS: I changed tag to Netcode for GameObjects since you aren’t using Netcode for Entities.

Sorry, I’m using Netcode for GameObjects 2.8.0.
I already updated my code to use [Rpc] instead of [ServerRpc], but the problem still persists.
Players still cannot see each other’s Spot Light in the scene.
I followed the recommended approach, but when one player toggles their light, it only updates locally and is not visible to other players.
Is there anything else I should check or update in my code to make sure the Spot Light state is properly synchronized across all clients?
Any advice would be appreciated.

using Unity.Netcode;
using UnityEngine;

public class PlayerSpotlight : NetworkBehaviour
{
    public Light spotlight;

    // Network-synchronized light state (read: everyone, write: server only)
    private NetworkVariable<bool> isLightOn = new NetworkVariable<bool>(
        true,
        NetworkVariableReadPermission.Everyone,
        NetworkVariableWritePermission.Server
    );

    public override void OnNetworkSpawn()
    {
        // Register callback for state changes
        isLightOn.OnValueChanged += OnLightStateChanged;
        if (spotlight != null)
            spotlight.enabled = isLightOn.Value;
    }

    public override void OnNetworkDespawn()
    {
        // Unregister callback
        isLightOn.OnValueChanged -= OnLightStateChanged;
    }

    void Update()
    {
        // Only the owner can toggle the light
        if (!IsOwner) return;
        if (Input.GetKeyDown(KeyCode.F))
        {
            ToggleLightRpc();
        }
    }

    // Called by the owner, executed on the server
    [Rpc(SendTo.Server)]
    private void ToggleLightRpc()
    {
        isLightOn.Value = !isLightOn.Value;
    }

    // Called when the light state changes (on all clients)
    private void OnLightStateChanged(bool prev, bool curr)
    {
        if (spotlight != null)
            spotlight.enabled = curr;
    }
}

Did you check if manually enabling the remote player’s spotlight makes it visible to the local player? Simply navigate the hierarchy and change the component’s enabled state in the Inspector. Just to rule out any issues unrelated to networking.

Because both the original and the new code with NetworkVariable seem to be okay, logically. You said that spotlight is assigned but better be safe and either log it or remove the null check, since that assignment should always be done right? In that case it’s safer to allow a NullRef because it would indicate a truly exceptional state.

When logging, add the name of the target and its GetInstanceId() (or GetEntityId() in 6.3) because the names might be the same. I also like to change the names of things in networked games, at least in Debug builds, like so:

public override void OnNetworkSpawn()
{
    gameObject.name = $"{gameObject.name} {(IsOwner ? "(OWNER)" : "(remote)")}"
}

This highlights in the hierarchy and logs whether an object is truly the owned object or a remote one. When you log the change of the enabled state, you should see the light change for “OWNER” on the client where you pressed the key, and on the other client you should see it change for “remote”.

Thank you for your response despite being busy. Although the logs are working correctly, players still cannot see each other’s lights. Could it still be a code issue?

using Unity.Netcode;
using UnityEngine;

public class PlayerSpotlight : NetworkBehaviour
{
    public Light spotlight;

    // Each player instance has its own light state
    private NetworkVariable<bool> isLightOn = new NetworkVariable<bool>(
        true,
        NetworkVariableReadPermission.Everyone,
        NetworkVariableWritePermission.Owner // Changed to Owner!
    );

    public override void OnNetworkSpawn()
    {
        gameObject.name = $"{gameObject.name} {(IsOwner ? "(OWNER)" : "(REMOTE)")} [ID:{NetworkObjectId}]";

        isLightOn.OnValueChanged += OnLightStateChanged;

        if (spotlight != null)
        {
            spotlight.enabled = isLightOn.Value;
            Debug.Log($"[{gameObject.name}] Initial light state: {isLightOn.Value}");
        }
        else
        {
            Debug.LogWarning($"[{gameObject.name}] Spotlight reference is null!");
        }
    }

    public override void OnNetworkDespawn()
    {
        isLightOn.OnValueChanged -= OnLightStateChanged;
    }

    void Update()
    {
        if (!IsOwner) return;

        if (Input.GetKeyDown(KeyCode.F))
        {
            Debug.Log($"[{gameObject.name}] Toggle key pressed. Current state: {isLightOn.Value}");
            // Directly modify NetworkVariable since Owner has write permission
            isLightOn.Value = !isLightOn.Value;
        }
    }

    private void OnLightStateChanged(bool prev, bool curr)
    {
        Debug.Log($"[{gameObject.name}] Light state changed from {prev} to {curr}");

        if (spotlight != null)
        {
            spotlight.enabled = curr;
        }
        else
        {
            Debug.LogError($"[{gameObject.name}] Cannot update light - spotlight is null!");
        }
    }
}

Like I said: manually enable the spotlights for both players in both clients. Do both clients see the spot-light of both players? Your logs point towards that it SHOULD be working but it isn’t, now you need to be CERTAIN that it is actually going to work as intended when both lights are on - purposefully bypassing the script.

There may be a chance that the spotlight is simply not rendering for technical reasons or because the lights settings make them hard to notice.

Thank you so much! The problem was actually somewhere else—another place was disabling the parent object of the spotlight.
Thank you very much!