How to make a gunner - driver coop game

Hi there, i am trying to make a game in which you can control one Object with multiple players.
At the moment i am fighting with Unitys Unet to achive that. But no success so far. Does anybody know if it is possible to do so.

This is not the real game, its just a very basic test.

I have a PlayerController sitting on the Player Prefab (it is a Camera)
After Player Spawn each gets a unique name (this is done in a PlayerSetup script)

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;

public class PlayerController : NetworkBehaviour
{
    public delegate void Move(float x, float z);
    public delegate void Mouse(float x, float y);

    [SyncEvent]
    public static event Move EventOnMove;
    [SyncEvent]
    public static event Mouse EventOnMouse;

    private float moveX;
    private float moveZ;
    private float mouseX;
    private float mouseY;


    void Start()
    {
  
    }


    // Update is called once per frame
    void Update ()
    {
        if(gameObject.name == "Cpt")
        {
            CmdMove();
        }
        else
        {
            CmdWeapon();
        }
  
    }


    [Command]
    void CmdMove()
    {
        moveX = Input.GetAxis("Horizontal") * Time.deltaTime * 150.0f;
        moveZ = Input.GetAxis("Vertical") * Time.deltaTime * 3.0f;

        if (EventOnMove != null)
        {
            EventOnMove(moveX, moveZ);
        }
    }

    [Command]
    void CmdWeapon()
    {
        mouseX = Input.GetAxis("Mouse X") * 30 * Time.deltaTime;
        mouseY = Input.GetAxis("Mouse Y") * 30 * Time.deltaTime;

        if (EventOnMouse != null)
        {
            EventOnMouse(mouseX, mouseY);
        }
    }
}

and a AvatarController that actually controls the Avatar (at the moment one should be able to move the avatar, the other should be able to aim with the gun)
The weapon is a child of the Avatar.

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;

public class AvatarController : NetworkBehaviour {

    [SyncVar]
    private float x;
    [SyncVar]
    private float z;
    [SyncVar]
    private float v;
    [SyncVar]
    private float h;

    public GameObject weapon;

    void Start()
    {
 
    }


    // Update is called once per frame
    void Update ()
    {
        RpcMove();
        RpcWeapon();
    }


    void OnEnable()
    {
        PlayerController.EventOnMove += PlayerControllerOnMove;
        PlayerController.EventOnMouse += PlayerControllerOnMouse;
    }


    void OnDisable()
    {
        PlayerController.EventOnMove -= PlayerControllerOnMove;
        PlayerController.EventOnMouse += PlayerControllerOnMouse;
    }


    [ClientRpc]
    void RpcMove()
    {
        transform.Rotate(0, x, 0);
        transform.Translate(0, 0, z);
    }

    [ClientRpc]
    void RpcWeapon()
    {
        weapon.transform.Rotate(v, h, 0);
    }


    private void PlayerControllerOnMove(float moveX, float moveZ)
    {
        x = moveX;
        z = moveZ;
    }


    private void PlayerControllerOnMouse(float hor, float ver)
    {
        v = -ver;
        h = hor;
    }
}

I actually did manage to make it work but it does only work for the host ( the client sees the action done by the host) but not vice versa.

any suggestions? Should I do it with Photon instead? if someone needs the full project to play around just tell me :wink:
Ty

[Command]s need local authority for clients to call them.

I only use [Command] in the PlayerController which has local authority.

I spawn the Avatar here btw.

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;

public class SpawnManager : NetworkBehaviour {


    public GameObject avatar;
    private GameObject instanceAvatar;


    public override void OnStartServer()
    {
        Debug.Log("SPAWN");
        SpawnAvatar();
    }


    void OnStopServer()
    {
        Debug.Log("UNSPAWN");
        NetworkServer.UnSpawn(instanceAvatar);
    }

    void SpawnAvatar()
    {
        instanceAvatar = Instantiate(avatar, transform.position, transform.rotation) as GameObject;
        NetworkServer.Spawn(instanceAvatar);
    }
}

And i realized a bug with OnServerStart(): If I join as host the first time, the Avatar gets spawned. When I disconnect and reconnect as Host (As I understand when the host connects, the server starts) the avatar wont spawn again…in fact the OnServerStart() Method doesnt get called at all after the first time…

I also dont understand OnStopServer() as it doesnt get called at all (As far as I understand, it should be called when the server stops or the host disconnects.

nobody? really?

I think you’re misusing Cmd:
You have a gameobject on the client and a game object on the server. When you call a Cmd function form the client, the Cmd function gets executed on the server. The server doesn’t have the client’s Input (unless the server & client are running together as Host). You need to pass the necessary player Inputs to the Cmd function as arguments. In this case you should first calculate moveX and moveY on the client before calling the Cmd function, and then pass them to the Cmd function. Arguments in Cmd functions are passed over the network to the server.

Edit:
Also, you shouldn’t be calling ClientRpc or Command functions in each Update() call. This means you’re sending data from client to server and from server to client 60 times per second or more, if running at higher frame rate! That’s way too often. Quoted from Unity Manual:
http://docs.unity3d.com/Manual/UNetActions.html

Again, it looks like you’re not passing any information between client and server.

Thanks for the reply. I understand what you mean (i honestly didnt get the cmd and rpc things^^) but how to do it with events. thould i raise the event and use the cmd in the avatarcontroller (because thats what actually should sync with the server). In that case how to do so because the cmd can only be run on a player.
Can onyone also tell me how rpcclient works (i read the documentation but i didnt really understand it)
Also is it at all possible to give different authorities to a object and their child object. Like Movement for the Avatar and fire/aim control for the weapon (which in this case would be a child)

I think it works now :wink:
I just did what you told me with the cmd command and its seems to work. I am now calling the Move() Funktion which gives the CmdMove() moveX and moveZ as argument. I removed the RpcClient from the avatarController and it still works :wink: ty man

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;

public class PlayerController : NetworkBehaviour
{
    public delegate void EventMove(float x, float z);
    public delegate void EventMouse(float x, float y);

    [SyncEvent]
    public static event EventMove EventOnMove;
    [SyncEvent]
    public static event EventMouse EventOnMouse;

    private float moveX;
    private float moveZ;
    private float mouseX;
    private float mouseY;
  

    void Start()
    {
      
    }


    // Update is called once per frame
    void Update ()
    {
        if(gameObject.name == "Cpt")
        {
            Move();
        }
        else
        {
            Weapon();
        }
      
    }

  
    void Move()
    {
        moveX = Input.GetAxis("Horizontal") * Time.deltaTime * 150.0f;
        moveZ = Input.GetAxis("Vertical") * Time.deltaTime * 3.0f;

        CmdMove(moveX, moveZ);
    }


    [Command]
    void CmdMove(float x, float z)
    {
        if (EventOnMove != null)
        {
            EventOnMove(x, z);
        }
    }


    void Weapon()
    {
        mouseX = Input.GetAxis("Mouse X") * 30 * Time.deltaTime;
        mouseY = Input.GetAxis("Mouse Y") * 30 * Time.deltaTime;

        CmdWeapon(mouseX, mouseY);
    }


    [Command]
    void CmdWeapon(float x, float y)
    {
        if (EventOnMouse != null)
        {
            EventOnMouse(x, y);
        }
    }
}

AvatarController:

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;

public class AvatarController : NetworkBehaviour {

    [SyncVar]
    private float x;
    [SyncVar]
    private float z;
    [SyncVar]
    private float aimX;
    [SyncVar]
    private float aimY;

    public GameObject weapon;
   
    void Start()
    {
      
    }


    // Update is called once per frame
    void FixedUpdate ()
    {
        Move();
        Aim();
    }


    void OnEnable()
    {
        PlayerController.EventOnMove += PlayerControllerOnMove;
        PlayerController.EventOnMouse += PlayerControllerOnMouse;
    }


    void OnDisable()
    {
        PlayerController.EventOnMove -= PlayerControllerOnMove;
        PlayerController.EventOnMouse += PlayerControllerOnMouse;
    }


    void Move()
    {
        transform.Rotate(0, x, 0);
        transform.Translate(0, 0, z);
    }


    void Aim()
    {
        weapon.transform.Rotate(-aimY, aimX, 0);
    }

    private void PlayerControllerOnMove(float moveX, float moveZ)
    {
        x = moveX;
        z = moveZ;
    }


    private void PlayerControllerOnMouse(float mouseX, float mouseY)
    {
        aimX = mouseX;
        aimY = mouseY;
    }
}

Hm i now tried to use this approach for my real game and it doesnt work…anybody knows what i am doing wrong here:

This is my DriverController:

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;

public class DriverControl : NetworkBehaviour
{
    public delegate void EventTraverse(float traverseInput);

    [SyncEvent]
    public static event EventTraverse EventOnTraverse;
 


    private void Update()
    {
        Traverse();
    }

    private void Traverse()
    {
        float traverseInput = Input.GetAxis("Move");
        CmdTraverse(traverseInput);
    }

    [Command]
    private void CmdTraverse(float input)
    {
        if (EventOnTraverse != null)
        {
            EventOnTraverse(input);
        }
    }
}

And this is my BuggyController which is controlled by the driver.

using UnityEngine;
using System.Collections;
using System;
using UnityEngine.Networking;

public class BuggyControl : NetworkBehaviour
{
    public float maxSpeed = 1;
    public float minSpeed = -0.5f;
    public float acceleration = 0.01f;

    [SyncVar]
    private float speed;
    [SyncVar]
    private Vector3 traverse;
    [SyncVar]
    private float traverseInput;
 
    private Rigidbody rb;

 
    private void Awake ()
    {
        rb = GetComponent<Rigidbody> ();
    } 
 
 
    // Use this for initialization
    void Start ()
    {
        speed = 0;
    }


    void FixedUpdate ()
    {
        Traverse();
    }

 
    void OnEnable()
    {
        DriverControl.EventOnTraverse += DriverControlOnTraverse;
    }


    void OnDisable()
    {
        DriverControl.EventOnTraverse -= DriverControlOnTraverse;
    }


    private void DriverControlOnTraverse(float input)
    {
        traverseInput = input;
    }
 

    private void Traverse()
    {
        if (traverseInput > 0.0f)
        {
            if (speed <= maxSpeed)
            {
                speed = speed + acceleration;
            }

        }

        if (traverseInput < 0.0f)
        {
            if (speed >= minSpeed)
            {
                speed = speed - acceleration;
            }
        }

        traverse = transform.forward * speed * Time.deltaTime;

        rb.MovePosition(rb.position + traverse);
    }
}

I didnt implement the turret yet because the client doesnt see any changes on the server…what did i miss?