Search Unity

Making an RTS style game work

Discussion in 'Multiplayer' started by eelbaz, Jul 26, 2015.

  1. eelbaz

    eelbaz

    Joined:
    Feb 21, 2015
    Posts:
    19
    Hi everyone.

    I'm extremely new to networking in general and was wondering how I could implement the following on UNET.

    The local player sends a command to the server which spawns a unit (for example). I'd like to implement one of the following if possible:
    1. That player should be able to control it and send commands through a network behavior script on that game object. I can't find a way of giving a particular player authority over a game object over the network.
    2. Or, at least be able to easily register hooks on components within that game object after the server spawns it.
    If 2, how do I get notified on the client when a particular game object (with a reference to it) is spawned?

    Also, would there be an easy way to check that the local player trying to control a unit is the 'owner' of it.
     
  2. eelbaz

    eelbaz

    Joined:
    Feb 21, 2015
    Posts:
    19
  3. Glabrezu

    Glabrezu

    Joined:
    Aug 30, 2014
    Posts:
    24
    If you're new, your best bet would be to start with Photon. UNet doesn't have much in terms of working examples, has no guides, etc.
     
  4. Stardog

    Stardog

    Joined:
    Jun 28, 2010
    Posts:
    1,913
    An RTS game should be simple enough.

    Everything non-local has to be done through the server/host. To control a spawned object you don't have authority over, you have to run a Command from a behaviour you do have authority over. You can't run functions on an unowned "Spawned" object, even if they are Commands, I believe.

    To check if you have control of something, get its NetworkIdentity component and check "localPlayerAuthority".

    Code (CSharp):
    1. void Update()
    2. {
    3.     if (!isLocalPlayer) return;
    4.  
    5.     if (Input.GetMouseButtonDown(0))
    6.         CmdSpawnBuilding();
    7. }
    8.  
    9. [Command]
    10. void CmdSpawnBuilding()
    11. {
    12.     GameObject instance = (GameObject)Instantiate(buildingGO);
    13.  
    14.     NetworkServer.Spawn(instance);
    15.  
    16.     // This should work, if not, you'll maybe have to get the component from the spawned object directly, rather than the instance.
    17.     RpcOnBuildingSpawned(instance.GetComponent<NetworkIdentity>().netId);
    18. }
    19.  
    20. [ClientRPC]
    21. void RpcOnBuildingSpawned(NetworkInstanceId id)
    22. {
    23.     GameObject go = ClientScene.FindLocalObject(id);
    24.  
    25.     Debug.Log("The GameObject that spawned was " + go.name);
    26. }
    I have a simple RTS for testing where building placement is synced to all clients, so you can even see where they are planning to place buildings before they do.
     
    Last edited: Jul 28, 2015
  5. eelbaz

    eelbaz

    Joined:
    Feb 21, 2015
    Posts:
    19
    Hi stardog

    Thanks for the great suggestion!

    I actually ended up with a similar solution which I was going to post.
     
  6. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    I beg to differ.

    In my case, both the server and the client doesn't have authority over an object, for unknown reasons. I have set a prefab to auto-spawn in upon connection, but once it finishes instantiating, the game object's isLocalPlayer flag for the server and the client are always false.

    I'm still tackling this simple problem. I'm frustrated, and may need to fall back to the old networking API if I can't move past what should be a simple form of network communication.

    EDIT:

    Took a break, ate dinner, came back, an idea struck me, and now I finally see the light at the end. Ignore my entire post. Thank you.
     
    Last edited: Aug 5, 2015
  7. Stormouse

    Stormouse

    Joined:
    Sep 11, 2014
    Posts:
    8
    may i ask the method you solved it?
     
  8. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    I read your post in one of my threads asking about how to spawn multiple prefabs, and now finished reading your post.

    This is what my idea boils down to:

    Code (CSharp):
    1. /*
    2.     Steps to use:
    3.  
    4.     [Client -> Server] is the [Command] attribute.
    5.     Use [ClientCallback] to call a [Command] method.
    6.     Use [Command] to "sync" or give corrections to an important/useful variable.
    7.     Use [SyncVar] to "sync" the important/useful variable across all clients.
    8.     Use the important/useful variable to update other game object related properties.
    9.  
    10. */
    Basically, your code must be designed in a strange way where the clients must call a command method (function) to the server and have the server calls the methods the clients need to call upon. (The two-way handshake)

    Once the server finishes calling that, the server needs to call on a method that the client needs, as a response to the server's command callback. That method is called to sync whatever you need to do with the game objects/prefabs.

    Finally, the clients need to call methods for themselves ([Client] or [ClientCallback], maybe even [ClientRpc]) and use the synced variables to update whatever they need for the game objects spawned into the game. Hopefully, it helps.
     
    Stormouse likes this.
  9. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    981
    For that one person who still have no idea what this does and was sending me a PM, here's a code snippet for you to try:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Networking;
    3. using System.Collections;
    4.  
    5. public class TestManager : NetworkBehaviour {
    6.     [SyncVar]
    7.     public Vector3 mousePosition;
    8.  
    9.     protected void Start() {
    10.         mousePosition = Vector3.zero;
    11.     }
    12.  
    13.     protected void Update() {
    14.         if (Input.GetMouseButton(0)) {
    15.             if (this.isLocalPlayer || this.hasAuthority) {
    16.                 CmdSendCommandToServer(Input.mousePosition);
    17.             }
    18.         }
    19.     }
    20.  
    21.     [Command]
    22.     public void CmdSendCommandToServer(Vector3 value) {
    23.         Debug.Log("Sending command to server.");
    24.         CallbackHandleCommand(value);
    25.     }
    26.  
    27.     [ClientCallback]
    28.     public void CallbackHandleCommand(Vector3 value) {
    29.         Debug.Log("Calling back to client to handle command.");
    30.         RpcSendCommandToClient(value);
    31.     }
    32.  
    33.     [ClientRpc]
    34.     public void RpcSendCommandToClient(Vector3 value) {
    35.         Debug.Log("Sending command to client. Mouse Position: " + value.ToString());
    36.         this.mousePosition = value;
    37.     }
    38. }
    39.  
    To use this code, all you do is to create a brand new project, add Network Manager HUD, and add this TestManager class script to a game object prefab. This prefab is then set to spawn via Network Manager of a game object that the Network Manager HUD was added as a component to. You also need to register this prefab through the Network Manager.

    Once done, build your game and run it, and have the Unity editor play an instance of the game, and finally click anywhere in the game screen. Read the log messages.
     
    Li0nMo0se and GixG17 like this.