Skip to content
This repository has been archived by the owner on Aug 15, 2022. It is now read-only.

Decoupling Netcoding (Events)

Balroth edited this page May 29, 2019 · 10 revisions

When an RPC is called on a networkObject, it is obvious that the code inside the RPC of the networkObject will be called.
However, if you want to scale the game, or make your project more readable and clean, the logic should not be contained in the networkObject.

For example, if you want to shoot a gun, you invoke SendRpc(RPC_Shoot, Receivers.All)

//RPC for Shooting
public override void Shoot(RpcArgs args)
{
    // Shoot Logic here...
    // Which means that for every RPC,
    // networkObject won't really have a single responsibility
    // and end up bloated over time, with an ugly amount of code.
}

However, using the Observer Pattern, we can simply notify other components/scripts, to do the logic instead! Hence, the project will be scale-able, since the code is properly divided.
And single-responsibility pattern applies, which is always a part of clean code.

Look at it in practice!

public System.Action ShootEvent;

//RPC for Shooting
public override void Shoot(RpcArgs args)
{
    // The subscriber(s) to shootEvent, are notified 
    // to do the actual logic.
    if (ShootEvent != null)
       ShootEvent();
}

So, assuming you have a reference to the networkObject (by the Inspector usually), then all you have to do is subscribe!
Let's see a fully fleshed out example!

using System;

public class ExampleNetworkObject: ExampleNetworkObjectBehavior
{
	public Action ShootEvent;

	// RPC for Shooting
	public override void Shoot(RpcArgs args)
	{
		// The subscriber to shootEvent, does the actual logic.
		if (ShootEvent != null)
		   ShootEvent();
	}
}



public class ShootLogic : MonoBehaviour
{
	// This is by default referenced, via the Inspector
	public ExampleNetworkObject networkObject;
	
	public void Start()
	{
		// When networkObject fires off the ShootEvent
		// it will trigger OnShoot automatically
		networkObject.ShootEvent += OnShoot;
	}
	
	public void OnShoot()
	{
		// Shoot Logic here...
	}
	
}

So, whenever we invoke SendRPC(RPC_SHOOT, Receivers.All);, it will always and succesfully call OnShoot(), and the logic is decoupled clearly. It is also extremely readable for everyone, and in this way, networkObject will never get bloated, since the single-responsibility pattern applies.

If you want to pass RpcArgs, instead of just notifying the RPC function happened, with a few small tweaks on the above, it should all work smoothly.

using System;

public class ExampleNetworkObject: ExampleNetworkObjectBehavior
{
	// Note that we have 1 parameter instead of none ;)
	public Action<RpcArgs> ShootEvent;

	// RPC for Shooting
	public override void Shoot(RpcArgs args)
	{
		// The subscriber to shootEvent, does the actual logic.
		// Note that we pass `args` inside the event parameter.
		if (ShootEvent != null)
		   ShootEvent(args);
	}
}

public class ShootLogic : MonoBehaviour
{
        // This is by default referenced, via the Inspector.
	public ExampleNetworkObject networkObject;
	
	public void Start()
	{
		// When networkObject fires off the ShootEvent
		// it will trigger OnShoot automatically
		networkObject.ShootEvent += OnShoot;
	}

        
	public void OnShoot(RpcArgs args)
	{
		// Shoot Logic here...
	}
}

Lastly, like all events and actions, they should be used with care, so don't forget to unsubscribe when necessary!

Home

Getting Started
Network Contract Wizard (NCW)
Network Object
Remote Procedure Calls (RPCs)
Unity Integration
Basic Network Samples
Scene Navigation
NetWorker
Master Server
Web Server
Netcoding Design Patterns
Troubleshooting
Miscellaneous
Forge Networking Alloy
Steamworks
Clone this wiki locally