- Home /
RPC and inheritance
Hi, I just tested something and it seems that RPCs in Unity don't support inheritance. Is that correct?
class Unit : MonoBehaviour
{
[RPC]
public void Test () {}
}
class Melee : Unit
{
}
If I have a unit with Melee script on it, RPC won't be received as RPC function "Test" cannot be found.
class Unit : MonoBehaviour
{
}
class Melee : Unit
{
[RPC]
public void Test () {}
}
On the contrary, this works and Test will be executed.
This introduces a set of new problems for us, can you please confirm that this is Unity's standard behaviour? I hope we are doing something wrong...
We are using Photon but that should behave the same way as the integrated networking as far as I know.
EDIT: Photon (PUN) class for the solution below:
1, in case the solution scripts are in the Plugin folder, move them out to your script folder
2, InheritableRPC.cs, replace the second class with this:
public static class InheritableRPCExtensions
{
public class StoredPlayer
{
public string ipAddress, guid;
public int port;
}
public static void RPCEx(this PhotonView view, string routineName, PhotonTargets targets, params object[] parameters)
{
using(var m = new MemoryStream())
{
var b = new BinaryFormatter();
b.Serialize(m, parameters);
m.Flush();
var s = Convert.ToBase64String(m.GetBuffer());
view.RPC("PerformRPCCall", targets, routineName, s);
}
}
public static void RPCEx(this PhotonView view, string routineName, PhotonPlayer player, params object[] parameters)
{
using(var m = new MemoryStream())
{
var b = new BinaryFormatter();
b.Serialize(m, parameters);
m.Flush();
var s = Convert.ToBase64String(m.GetBuffer());
view.RPC("PerformRPCCall", player, routineName, s);
}
}
}
3, example code. SampleCaller.cs class:
public class SimpleCaller : MonoBehaviour {
public PhotonView otherView;
void Start ()
{
Network.InitializeServer(200,8081,true);
}
void OnGUI()
{
if(GUILayout.Button("Call Print"))
{
otherView.RPCEx("PrintThis", PhotonTargets.All, "Hello World");
}
}
}
http://forum.unity3d.com/threads/45224-Override-RPC-functions
It looks like the RPC is supposed to be declared virtual and then overwritten in all child classes...
Yeah, I think it's pretty easy to add something that stops that being the case for the cost of a reflection lookup and Invoke.
To $$anonymous$$izuho: Yes, that might work in a simple example but can you imagine an RTS with 50 unique units? Every time you need to make a new unit, you would have to override all the RPC just because your new unit has a tiny bit different feature. Introducing a new RPC is even worse, it would be necessary to go through all the units and override it there. I think this is fairly hard to manage in the long run.
Thank you for the input :]
this is because RPC's are meant to be called with the networkview component, in this case base class is not having it. also when you register a rpc function, it creates an unique rpc id for it, with which it is getting called.
did you try calling like
"base.Test" in the parameter for RPC ins$$anonymous$$d of just "Test".
btw i couldnt check at my end so im not sure if it would work.
Answer by whydoidoit · Jun 29, 2012 at 11:51 AM
Ok so you can't do that normally but it is possible to create a work around that will work generically on all objects and classes. To do this you create a normal RPC script that itself defers to the base classes of the other components. This works well generically but there may be times when it would be faster to do this directly by writing code for specific instances. Presuming RPC calls are already expensive and not frequent it should be fine to do it this way.
So I've create a script that you attach to the game objects that have inherited scripts attached to them. This handles the real RPC call and then forwards it on to the inherited components.
To use it you use networkView.RPCEx(normal parameters) and that's it.
You can also pass more complex objects as parameters to RPCEx - but you cannot pass NetworkPlayer or NetworkViewID so those would have to use normal RPC for setup. After that I'm presuming these parameters are not often passed in the case where you want inherited execution (it would be possible with more work to make those passable too).
You can get the code from here
If you were calling from Unity Script you would have to call it like this:
InheritableRPCExtensions.RPCEx(networkView, /* Normal Parameters */);
I'm checking the code now. "namespace name `Serialization' could not be found". There's several namespaces with this name. I'm using UnityEngine.Serialization for now. Looks promising so far, thank you
EDIT: taking back the path comment, it's fine
Ugh - just delete that you don't need it (damn, will fix the package). That's my UnitySerializer, just accidentally in there, it doesn't need it.
And I've added an example test scene to show it working.