I want to keep network code separate from my game logic. Not only do I need to do that to be able to share game logic between single and multiplayer game modes, I also want it because of the Separation Of Concerns thing.
My current approach is to generate the code for my network related classes in such a way that there is an online and an offline version. I do this using T4 templates.
The resulting classes look like this:
Standalone/Singleplayer version:
// T4 GENERATED CODE
// Head (Singleplayer version)
class StandaloneHelloWorld : MonoBehaviour, IHelloWorld
{
private string name;
public void SayHello()
{
SayHelloInternal();
}
// Body
public string Name
{
get { return name; }
set { name = value; }
}
void SayHelloInternal()
{
Debug.Log(Name + ": Hello World");
}
}
Multiplayer version:
// T4 GENERATED CODE
// Head (Multiplayer version)
class NetworkedHelloWorld : NetworkBehaviour, IHelloWorld
{
[SyncVar]
private string name;
public void SayHello()
{
CmdSayHello();
}
[Command]
void CmdSayHello()
{
RpcSayHello();
}
[ClientRpc]
void RpcSayHello()
{
SayHelloInternal();
}
// Body
public string Name
{
get { return name; }
set { name = value; }
}
void SayHelloInternal()
{
Debug.Log(Name + ": Hello World");
}
}
They both share an interface to hide the implementation from the callers:
interface IHelloWorld
{
string Name { get; set; }
void SayHello();
}
So as you can see, both implementations use the same body, sharing most of the code, while the entry points depend on the implementation being networked or not. Also note that the two implementations inherit different base classes.
Advantages:
- Singleplayer code has no dependencies towards networked code and vice versa
- No duplicate code (none that has to be maintained manually at least)
Disadvantages:
- Support for interfaces in Unity is limited. I would not be able to reference scene instances of IHelloWorld from inside the Editor.
- Having to maintain separate Prefabs for singleplayer/multiplayer game modes
- Having to meddle with T4/code generation
Do you know of better ways to deal with this? How did you solve this problem?