Best architecture for a moba style game?

Options
Hello. I am using Photon PUN for a project, and after some thought, I am wondering what is the best way to architect a moba style game using PUN.

In the last prototype, I applied a PhotonView component to the character of each player, and then added several other components to handle specific things: NetworkTimeSync, SyncedAgentRotation, SyncedAgentPosition, SyncedCombat, SyncedAnimator, and so on and so forth.

Each of these contained an OnPhotonSerializeView, and I added them to the ObservedComponents list on my PhotonView component.

The game is targeting mobile, and uses tap to move/click/attack, as well as a dynamic navmesh, along with the navmesh agents, etc.

Everything is working fine, but I am worried about the overall messages per second and enjoyment of the game, regarding dropped messages, etc.

The questions I have:

1. Although I have 5 Observed components, does this one PhotonView send a single message each 'stream', for example: 10 messages a second, or does it send one per observed component, meaning 50 per second?

2. How do things like SetCustomProperties for both Players and Rooms, PunRPC AllViaServer, etc. come into play regarding messages per room? Is there a different limit or cap regarding this? For example, couldn't I just set some data on my Player, and then update that 10 times per second, or as needed? Or does this use a different pipeline, making it a terrible idea?

3. Would getting rid of my observed components, and instead, go with RaiseEvent, and handle all the data myself be better? For example, with Tap to move, if no one has tapped in 2 seconds, why am I sending 'target position' every 10 times per second? Wouldn't it make more sense to only send that data when it happened, and handle it in a RaiseEvent callback?

Like I said, I'm making a moba style game, the limits are 6 players per 'room', I'll need to be sending at least position(x, y, z), rotation(y), target destination(x, y, z), attacks, health and mana regen 'ticks', npc data, effects like damage over time or buffs, etc. Skill point distribution, etc.

This is a side project, so budget is a concern. I'll be using PlayFab as my BaaS. Any advice or direction is greatly appreciated.

Best Answer

Answers

  • Bunzaga
    Options
    Hey I'm still working on things, but I've dug into the code a little, and it seems that UnreliableOnChange does indeed first check the old value against the new value, to see if they are 'Almost Equal'. If it hasn't changed, it doesn't send it in the stream, which is nice.

    So in other words, we could put 100 Observed components on one PhotonView, and if only one of those has changed, it is only sending one Observable set of data.

    It does count as one message. But they have a cap on how much you can cram into one send, if it's more than the limit, it breaks it up and sends it in different pieces.

    It also appears to split the changed values per read/write. So if you have to read/writes on one Observable, but only one of those values change, it only sends the changed one.

    Good idea about using an RPC for the Health/Mana tic. I put the Damage RPC call in the animation itself, so one RPC says to start the animation, and then another sends the damage, at the keyframe where the damage should occur.

    Really great info, for me to digest, thank you for replying. I'm currently working on the lobby/matchmaking.
  • i want to link you the tutorial i used to set up my lobby/matchmaking but i can't find it. This was the end code and it does everything i need. Ofc there will be too many errors to copy it directly, but you can see what things you need. I also intend to make an animation rpc and put the damage at a crucial part in the animation, but i don't have any animations yet (or models).

    public class Launcher : Photon.PunBehaviour { string _gameVersion = "1"; public PhotonLogLevel Loglevel = PhotonLogLevel.Informational; public byte MaxPlayersPerRoom = 10; public GameObject controlPanel; public GameObject progressLabel; public string WaitingForPlayersOrLobby; // I change this value in editor to test with 1 or 2 clients bool isConnecting; void Awake () { PhotonNetwork.autoCleanUpPlayerObjects = false; PhotonNetwork.autoJoinLobby = false; PhotonNetwork.automaticallySyncScene = true; PhotonNetwork.logLevel = Loglevel; WaitingForPlayersOrLobby = "WaitingForPlayers"; } void Start () { progressLabel.SetActive (false); controlPanel.SetActive (true); } public void Connect () { //is called by a UI button isConnecting = true; progressLabel.SetActive (true); controlPanel.SetActive (false); if (PhotonNetwork.connected) { PhotonNetwork.JoinRandomRoom (); } else { PhotonNetwork.ConnectUsingSettings (_gameVersion); } } public override void OnConnectedToMaster () { if (isConnecting == true) { PhotonNetwork.JoinRandomRoom (); } } public override void OnDisconnectedFromPhoton () { progressLabel.SetActive (false); controlPanel.SetActive (true); } public override void OnPhotonRandomJoinFailed (object[] codeAndMsg) { PhotonNetwork.CreateRoom (null, new RoomOptions () { MaxPlayers = MaxPlayersPerRoom }, null); } public override void OnJoinedRoom () { PhotonNetwork.LoadLevel (WaitingForPlayersOrLobby); // I change this value in editor to test with 1 or 2 clients } }