Le olde Wire
Le olde Wire

Reputation: 79

Unity Unet Updating transform while clicked

I am currently trying to make a little drawing game where two players can draw at the same time over the network.

I am using a GameObject with a TrailRenderer to draw the lines.

Right now only the drawings of the host player are shown on both machines.

If a client player clicks and tries to draw I can see that a new object is spawned but the transform doesn't get updated. The spawned prefab has a NetworkIdentity (with Local Player Authority checked) and NetworkTransform attached to it. The following script is spawned by both players and also has a NetworkIdentity (with Local Player Authority checked).

I think I am actually doing something wrong with the CmdTrailUpdate and how to handle it but I can't really figure out what.

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

public class TrailDrawer : NetworkBehaviour {

    private Plane objPlane;
    private GameObject currentTrail;
    private Vector3 startPos;
    public GameObject trail;

    public void Start()
    {
        objPlane = new Plane(Camera.main.transform.forward * -1, this.transform.position);
    }

    // Update is called once per frame
    void FixedUpdate() {
        if (isLocalPlayer) {
            if (Input.GetMouseButtonDown(0)) {
                CmdSpawn();
            } else if (Input.GetMouseButton(0)) {
                CmdTrailUpdate();
            }
        }
    }

    [Command]
    private void CmdTrailUpdate() {
        Ray mRay = Camera.main.ScreenPointToRay(Input.mousePosition);
        float rayDistance;
        if (objPlane.Raycast(mRay, out rayDistance)) {
            currentTrail.transform.position = mRay.GetPoint(rayDistance);
        }
    }

    [Command]
    private void CmdSpawn(){
        Ray mRay = Camera.main.ScreenPointToRay(Input.mousePosition);

        float rayDistance;
        if (objPlane.Raycast(mRay, out rayDistance)) {
            startPos = mRay.GetPoint(rayDistance);
            currentTrail = (GameObject)Instantiate(trail, startPos, Quaternion.identity);
            NetworkServer.Spawn(currentTrail);
        }
    }
}

Upvotes: 0

Views: 211

Answers (1)

derHugo
derHugo

Reputation: 90629

I think your problem is:

[Command] means: Invoke the method from a client but only execute it on the server.

=> You are executing both methods CmdSpawn and CmdTrailUpdate only on the server.

But:

  1. how shoud the server know your client's Input.mousePosition?

  2. You don't want to raycast from the server's camera.main, but rather from the client's.

Solution:

  1. Do both things local on the client and pass the position as parameter on the [Cmd] methods to the server.

  2. Since you say the object already has a NetworkTransform, you wouldn't need to transmit the updated position to the server because NetworkTransform already does it for you. So calling CmdTrailUpdate from the client would not be neccesarry.

But: After spawning the object you have to tell the client who is calling CmdSpawn, which is his local currentTrail which's position he has to update. I would do this by simply passing the calling clients gameObject also to the CmdSpawn method and on the server call a [ClientRpc] method to set this client's currentTrail object.

(I'm assuming here, that the script you posted is attached diectly to the Player objects. If that is not the case, instead of the lines with this.gameObject you have to get the player's gameObject in a nother way.)

void FixedUpdate() {
    if (!isLocalPlayer) return;

    if (Input.GetMouseButtonDown(0)) {
        // Do the raycast and calculation on the client
        Ray mRay = Camera.main.ScreenPointToRay(Input.mousePosition);

        float rayDistance;
        if (objPlane.Raycast(mRay, out rayDistance)) {
            startPos = mRay.GetPoint(rayDistance);
                
            // pass the calling Players gameObject and the
            // position as parameter to the server
            CmdSpawn(this.gameObject, startPos);
        }
    } else if (Input.GetMouseButton(0)) {
        // Do the raycast and calculation on the client
        Ray mRay = Camera.main.ScreenPointToRay(Input.mousePosition);
            
        float rayDistance;
        if (objPlane.Raycast(mRay, out rayDistance)) {
            // only update your local object on the client
            // since they have NetworkTransform attached
            // it will be updated on the server (and other clients) anyway
            currentTrail.transform.position = mRay.GetPoint(rayDistance);
        }
    }
}

[Command]
private void CmdSpawn(GameObject callingClient, Vector3 spawnPosition){
    // Note that this only sets currentTrail on the server
    currentTrail = (GameObject)Instantiate(trail, spawnPosition, Quaternion.identity);
    NetworkServer.Spawn(currentTrail);

    // set currentTrail in the calling Client
    RpcSetCurrentTrail(callingClient, currentTrail);
}

// ClientRpc is somehow the opposite of Command
// It is invoked from the server but only executed on ALL clients
// so we have to make sure that it is only executed on the client
// who originally called the CmdSpawn method
[ClientRpc]
private void RpcSetCurrentTrail(GameObject client, GameObject trail){
    // do nothing if this client is not the one who called the spawn method
    if(this.gameObject != client) return;

    // also do nothing if the calling client himself is the server
    // -> he is the host
    if(isServer) return;

    // set currentTrail on the client
    currentTrail = trail;
}

Upvotes: 2

Related Questions