Multiplayer FPS Root Object No NetworkIdentity?

I’m working on a multiplayer FPS game and I’m having a bit of trouble. I’m trying to change the color of 3 objects that are the children of the player.

113416-heirarchy.png

The 3 objects are Body, TopSphere, and BottomSphere. Whenever I try and change the color, I get the error “NetworkWriter TopSphere has no NetworkIdentity”.

However, there can only be 1 NetworkIdentity and it has to be on the root object. What am I doing wrong? Here is the code to change the color of the objects.

    [Command]
    public void CmdChangeColor(GameObject obj, Color color)
    {
        if(obj == null)
        {
            Debug.Log("CmdChangeColor, obj is null");
        } else
        {
            Debug.Log("CmdChangeColor, obj is not null");
            RpcChangeColor(obj, color);
        }
    }

    [ClientRpc]
    public void RpcChangeColor(GameObject obj, Color color)
    {
        if (obj == null)
        {
            Debug.Log("RpcChangeColor, obj is null");
        }
        else
        {
            Debug.Log("RpcChangeColor, obj is not null");
            if(obj.GetComponent<Renderer>() != null)
            {
                obj.GetComponent<Renderer>().material.color = color;
            }
        }
    }

I get the message that RpcChangeColor obj is null, because I guess it doesn’t exist on the server?

Uhm why do you think that you can’t attach a NetworkIdentity to a child object? If you want to be able to send a gameobject reference across the network the object has to have a NetworkIdentity component. However this seems to only work on root objects of spawned objects. To work on a child object there are several possibilities:

First you can create seperate methods for changing the Body / TopSphere / BottomSphere color and use references you setup on the prefab. So you can declare 3 public variables for each of the child objects. The RPC can then just use it’s local reference to the desired object.

To keep it dynamic instead of sending a GameObject reference (which as we said is not possible) you could send the child name as string and let the RPC method search for that child locally using transform.Find(name). Keep in mind that the child(s) would need unique names within it’s parent or you can’t reach the desired object. In your case it would be fine since each object has a different name. Also note that transform.Find can work with “path like” child names to access even deeper nested children.

For the last solution you could use a method like this to determine the child name path for an arbitrary child object:

public static class TransformExt
{
    public static string GetChildPath(this Component aParent, Transform aChild)
    {
        var root = aParent.transform;
        string path = aChild.name;
        var tmp = aChild.parent;
        while (tmp != null && tmp != root)
        {
            path = tmp.name + "/" + path;
            tmp = tmp.parent;
        }
        return path;
    }
    public static string GetChildPath(this GameObject aParent, Transform aChild)
    {
        return GetChildPath(aParent.transform, aChild);
    }
}

With this extension method you can get the relative path name to a certain root / parent object.

So in the script (which should be on the player root object) you can do

string childPath = transform.GetChildPath(yourChildObject); 

This string can be send across the network. On the receiving side you can simply do

transform.Find(childPath);

To find the same gameobject child locally.