Create/delete object with RPC

Dear all,

I try to understand the network principles for multi-user environments.
I spent time in the great “Ultimate Unity Networking Project by M2H” tutorial. Great thanks to Leepo !!!
But I’m still confused about how to construct the RPC calls.

I have success with some simple example found in the resources like that Java script…

var cubePrefab : Transform;
function OnGUI () {
if (GUI.Button (Rect (45,150,100,30),"SpawnBox")) 

{
var viewID : NetworkViewID= Network.AllocateViewID();
networkView.RPC("SpawnBox",RPCMode.AllBuffered,viewID,transform.position);
}
}

@RPC
function SpawnBox (viewID : NetworkViewID, location : Vector3) {
// Instantate the prefab locally
var clone : Transform;
clone = Instantiate(cubePrefab, location, Quaternion.identity) as Transform;
var nView : NetworkView;
nView = clone.GetComponent(NetworkView);
nView.viewID = viewID;
}

But I’m pretty lost when I try to adapt that C# script

using UnityEngine;
using System.Collections;

public class MouseInteraction : MonoBehaviour {

	GameObject minePrefab;
	
	// Use this for initialization
	void Start () {
		minePrefab = Resources.Load("minePrefab") as GameObject;
	}
	
	// Update is called once per frame
	void Update () {
		Ray ray = Camera.mainCamera.ScreenPointToRay(Input.mousePosition);
		RaycastHit hit = new RaycastHit();
		if(Input.GetMouseButtonDown(0))
		{
			if(Physics.Raycast(ray,out hit))
			{
				//We've hit something
				if(hit.collider.gameObject.name.Equals("ground"))
				{
					//We've hit the ground
					CreateMine(hit.point);
				}
			}
		} else if(Input.GetMouseButtonDown(1)) {
			if(Physics.Raycast(ray,out hit))
			{
				//We've hit something
				if(hit.collider.gameObject.name.Equals("minePrefab(Clone)"))
				{
					//We've hit a mine
					GameObject.Destroy(hit.collider.gameObject);
					
		networkView.RPC("CreateMine", RPCMode.All);
				}
			}
		}
	}
	[RPC]
	void CreateMine(Vector3 pos)
	{
		Instantiate(minePrefab, pos, Quaternion.identity);
	}
}

Basically, that script allow to create instanced prefab on mouse position, left click, or to delete the object on mouse right click.
I’d like that to be visible and available to all users (clients or server).

I’ll continue to search but if one of you around could advise me and help to understand how to construct it in C#, it will be simply great.

Thank you all,
Pascal

I don’t understand. You’re basically there.
Vector3 pos = Vector3.zero;
networkView.RPC(“CreateMine”, RPCMode.Server, myPos);

That’s all you need.

Send the RPC to create the prefab to only the server. Then have the server use Network.Instantiate to create the prefab.

Also, because your CreateMine function expects a Vector3 to be passed to it, you must include a Vector while calling the RPC as I have shown you above.

What is this new variable “myPos” ?!
I’m a bit confused with your explanation?!
I think what he wants to do is to create a cube at mouse click, and then those cubes to be visible to all other players and exactly in the same position as the creator of the cubes!

Regards

Dear Crowe T. Robot,

I’m still confused about what how to managed it.
I tried that,

using UnityEngine;
using System.Collections;

public class MouseInteraction : MonoBehaviour {

	GameObject minePrefab;
	
	// Use this for initialization
	void Start () {
		minePrefab = Resources.Load("minePrefab") as GameObject;
	}
	
	// Update is called once per frame
	void Update () {
		Ray ray = Camera.mainCamera.ScreenPointToRay(Input.mousePosition);
		RaycastHit hit = new RaycastHit();
		if(Input.GetMouseButtonDown(0))
		{
			if(Physics.Raycast(ray,out hit))
			{
				//We've hit something
				if(hit.collider.gameObject.name.Equals("ground"))
				{
					//We've hit the ground
					CreateMine(hit.point);
				}
			}
		} else if(Input.GetMouseButtonDown(1)) {
			if(Physics.Raycast(ray,out hit))
			{
				//We've hit something
				if(hit.collider.gameObject.name.Equals("minePrefab(Clone)"))
				{
					//We've hit a mine
					GameObject.Destroy(hit.collider.gameObject);
			Vector3 pos = Vector3.zero;
			networkView.RPC("CreateMine", RPCMode.All, pos);		
		//networkView.RPC("CreateMine", RPCMode.All);
				}
			}
		}
	}
	[RPC]
	void CreateMine(Vector3 pos)
	{
		Instantiate(minePrefab, pos, Quaternion.identity);
	}
}

I have no error any more but I’m not able to share the function.

I don’t yet understand how to set up the following:

I’m working in the Tutorial 3 of the M2H tutorial, that means an Authoritative server configuration.
Should I change that to an other solution?

Here is a screen shot of the workflow where you can see that I attached a network view to the “mouse interaction” game object.

What should I do to make it work properly?
Thanks a lot,
Pascal

Sorry about the confusion… the myPos was supposed to be just pos, and I just had that var there to demonstrate that he needs to pass SOME vector to the function…

The location vector should be obtained by where the mouse hit (raycast) struck an intersecting geometry.

In an authoritative setup, you can potentially have clients clicking the mouse button! If they do that with your script you posted, it will simply spawn a box on only the client that clicked’s screen… no one else will know about it, unless you tell the server about it.

so, in the mousehit function, you HAVE to call an RPC to the server to instantiate the object, unless the action comes FROM the server, then you just call the function directly.

So you would have

using UnityEngine;
using System.Collections;

public class MouseInteraction : MonoBehaviour {

	GameObject minePrefab;
	
	// Use this for initialization
	void Start () {
		minePrefab = Resources.Load("minePrefab") as GameObject;
	}
	
	// Update is called once per frame
	void Update () {
		Ray ray = Camera.mainCamera.ScreenPointToRay(Input.mousePosition);
		RaycastHit hit = new RaycastHit();
		if(Input.GetMouseButtonDown(0))
		{
			if(Physics.Raycast(ray,out hit))
			{
				//We've hit something
				if(hit.collider.gameObject.name.Equals("ground"))
				{
                                        if(Network.isServer)
					    CreateMine(hit.point);
                                        else
                                            networkView.RPC("CreateMine", RPCMode.Server, hit.point);  //We need to tell the server about the mine
				}                                                                                                     //Not all the clients, the server will do that
			}
		} else if(Input.GetMouseButtonDown(1)) {
			if(Physics.Raycast(ray,out hit))
			{
				//We've hit something
				if(hit.collider.gameObject.name.Equals("minePrefab(Clone)"))
				{
					//We've hit a mine
					GameObject.Destroy(hit.collider.gameObject);
				}
			}
		}
	}
	[RPC]
	void CreateMine(Vector3 pos)
	{
		Network.Instantiate(minePrefab, pos, Quaternion.identity);  // NOTE THE CHANGE HERE, Network.Instantiate
	}
}

Something like that should work.

Also note that you will have to do a similar thing with the Destroy function, you’ll have to send a message to the server to have the server delete the object, unless of course Network.isServer == true, in which case you can call it directly.

Thanks for the reply and for the work done, but I found another way (basically without RPC) and it works great:

using UnityEngine;
using System.Collections;

public class MouseInteraction : MonoBehaviour {

GameObject minePrefab;

bool onoff = false;
Vector3 myPos;
// Use this for initialization
void Start () {
minePrefab = Resources.Load(“minePrefab”) as GameObject;
}

// Update is called once per frame
void Update () {
if(Network.isServer) onoff=true;
if(Network.isClient) onoff=true;
Debug.Log(onoff);
Ray ray = Camera.mainCamera.ScreenPointToRay(Input.mousePosition);
RaycastHit hit = new RaycastHit();
//if(OnNetworkInstantiate){
if(Input.GetMouseButtonDown(0))
{
if(Physics.Raycast(ray,out hit))
{
//We’ve hit something
if(hit.collider.gameObject.name.Equals(“ground”))
{
if(onoff==true){
//We’ve hit the ground
CreateMine(hit.point);
// transform.position = myPos;

//networkView.RPC(“CreateMine”, RPCMode.Server, hit.point);

//}
//else networkView.RPC(“CreateMine”, RPCMode.Server, transform.position);
Debug.Log(“Mine RPC send”);
}
}
}
} else if(Input.GetMouseButtonDown(1)) {
if(Physics.Raycast(ray,out hit))
{
//We’ve hit something
if(hit.collider.gameObject.name.Equals(“minePrefab(Clone)”))
{
//We’ve hit a mine
GameObject.Destroy(hit.collider.gameObject);
//Network.RemoveRPCs(networkView.viewID);
}
}
}
//}
}

//[RPC]
void CreateMine(Vector3 pos)
{
// transform.position = pos;
Network.Instantiate(minePrefab, pos, Quaternion.identity,0);

//transform.position=pos;
}

}

I’m not sure how that’s going to work… In an authoritative server, you only ever want your server to do the network instantiate.

If it works for what you need, that’s awesome, but just be sure to verify that it works when the server player clicks the mouse (does it appear on the client’s screen), and when the client clicks, does it appear on the server and other client’s screens? And if the client does instantiation, it would only be a hybrid authoritative server, as the Server is now not the owner of those objects!

I understand what you say. Now when I click in the screen the cube is created in all clients and server as well (both ways), but of course that the client which creates those cube he owns them, so only him (owner) can move them. I have attached to cubes a script which send and RPC so no matter who is the owner they sent the position on movement. It works but it is very laggy and slow?! Below is my RPC code:

private var lastPosition : Vector3;
private var lastRotation : Quaternion;
public var owner : NetworkPlayer;

var newPos : Vector3;
var newRot : Quaternion;

function Update(){

//SetPosition(newPos,newRot);
if(Vector3.Distance(transform.position, lastPosition)>=0.05){
lastPosition=transform.position;

networkView.RPC(“SetPosition”, RPCMode.Others, transform.position, transform.rotation);
}

}
@RPC
function SetPosition(newPos : Vector3, newRot : Quaternion){

transform.position=newPos;
transform.rotation = newRot;

}

Dear Crowe T. Robot,

I was trying your solution but I’m getting an error message

Do you know how to solve it?

Thanks a lot,
Pascal.

Dear Rajmond,

I can create the prefab in both server and client and see result but I can’t remove them in both clients and server.
Do you have the same issues?

Thanks,
Pascal

@ Rajmond

Ok solve about deleting the prefab, it was just because of a space in the prefab name I guess:

if(hit.collider.gameObject.name.Equals("minePrefab (Clone)"))

should be,

if(hit.collider.gameObject.name.Equals("minePrefab(Clone)"))

But the thing is that if I can create objects from server or client and see the created objects simultaneously in both side, client and server, that’s not working when deleting objects.

I can delete objects in single client or server views, but the result of the action is not visible in both side, only the owner of the action see the result, client or server.

Do you have the same issue?
Thanks,
Pascal

Hi Pascal!

It shows you this error because there should be one more integer (0) in the end of the instantiating code line. Like this:

Instead of: Network.Instantiate(minePrefab, pos, Quaternion.identity);
Should be: Network.Instantiate(minePrefab, pos, Quaternion.identity,0);

I think I know how to solve this.
I will try it and let you know tonight later.#

Cheers,
Rajmond

Great thanks Rajmond

This is the code with RPC:

using UnityEngine;
using System.Collections;

public class MouseInteraction : MonoBehaviour {

GameObject minePrefab;

bool onoff = false;
Vector3 myPos;
// Use this for initialization
void Start () {
minePrefab = Resources.Load(“minePrefab”) as GameObject;
}

// Update is called once per frame
void Update () {

if(Network.isServer) onoff=true;
if(Network.isClient) onoff=true;
Debug.Log(onoff);
Ray ray = Camera.mainCamera.ScreenPointToRay(Input.mousePosition);
RaycastHit hit = new RaycastHit();
//if(OnNetworkInstantiate){
if(Input.GetMouseButtonDown(0))
{
if(Physics.Raycast(ray,out hit))
{
//We’ve hit something
if(hit.collider.gameObject.name.Equals(“ground”))
{
if(onoff==true){
//We’ve hit the ground
if(Network.isServer) CreateMine(hit.point);

//NetworkViewID viewID = Network.AllocateViewID();
if (Network.isClient) networkView.RPC(“CreateMine”, RPCMode.Server, hit.point);

//}
//else networkView.RPC(“CreateMine”, RPCMode.Server, transform.position);
Debug.Log(“Mine RPC send”);
}
}
}
} else if(Input.GetMouseButtonDown(1)) {
if(Physics.Raycast(ray,out hit))
{
//We’ve hit something
if(hit.collider.gameObject.name.Equals(“minePrefab(Clone)”))
{
//We’ve hit a cube
//GameObject.Destroy(hit.collider.gameObject);

if (Network.isClient)
{
networkView.RPC(“DestroyMine”, RPCMode.All, networkView.viewID);
// Network.RemoveRPCs(networkView.viewID);
//Network.Destroy(networkView.viewID);
}
}
}
}
//}
}

[RPC]
void CreateMine(Vector3 pos)
{
// transform.position = pos;
//Network.Instantiate(minePrefab, pos, Quaternion.identity,0);

Network.Instantiate(minePrefab, pos, Quaternion.identity,0);

//transform.position=pos;
}

[RPC]
void DestroyMine(NetworkViewID viewID)
{
//NetworkView nView;
// nView = gameObject.GetComponent();
// nView.viewID = viewID;
// Network.Destroy(gameObject.networkView.viewID);
//Network.Destroy(GetComponent(NetworkView).viewID);

}

}

Create object works fine now in both view.
Great,
Pascal

Dear All,
Any idea how then to destroy the object on mouse hit in both views?
Thanks,
Pascal