[Simple Networking] Remember member order in the array
Hello! I am trying to make a simple networking solution. When a player joins the game he gets put into the spectator array (before he chooses a team). Later when another player joins the same game he should know not only that array has this and that player, but also be able to tell who joined 1st, 2nd and so on.
For example, say 4 players want to join the same room: Player A, B, C and D.
Player D enters first, our spectator array has been created and consists of 1 member so far: spectator.Length = 1 spectator[0] = D
Now another player B joins. The array looks like this: spectator.Length = 2 spectator[0] = D spectator[1] = B
C joins. spectator.Length = 3 spectator[0] = D spectator[1] = B spectator[2] = A
D joins. spectator.Length = 3 spectator[0] = D spectator[1] = B spectator[2] = C spectator[3] = A
This array should be the same across all users. So not only the 1st one who joined is able to tell who joined afterwards, but also people who came in later, can be able to tell precisely not only the content of the array and its length, but also the exact order.
I don't know exactly how to do this. Maybe the logic is flawed, maybe not. This is what I imagine is the right way to it: using RPC:
using UnityEngine;
using System.Collections;
public class instantiate_spectator_at_runtime : MonoBehaviour {
public GameObject[] spectors;
// Use this for initialization
void Start () {
SpawnSpectator ();
Debug.Log ("We are spectator number: ");
Debug.Log(spectors.Length);
}
[RPC]
void SpawnSpectator(){
GameObject mySpawnSpot = GameObject.FindGameObjectWithTag ("SpawnSpot");
GameObject spectator = (GameObject)Instantiate("Spectator_prefab", mySpawnSpot.transform.position, mySpawnSpot.transform.rotation, 0);
spectator.GetComponent<NetworkView> ();
NetworkView.RPC("Spectator_prefab", RPCMode.AllBuffered, NetworkViewID, transform.position);
for (int i = 0; i < spectors.Length; i++){
spectors[i] = this.gameObject;
}
}
}
Can you suggest what is the right way to solve this problem? Thank you very much!
Answer by cjdev · Feb 03, 2016 at 01:17 PM
The problem with an array in this case is that they are of a fixed size. To use one you have to declare how big it is first so that it can allot a portion of memory for it's elements. In order to use a more dynamic storage method you can use a generic List. This is (essentially) a wrapper class around an array of some type T which you specify. The nice thing about it, however, is that you can add items to the list without limitations because the class automatically remakes the array to an appropriate size to store everything needed. This is how it works:
// using System.Collections.Generic;
// Instantiate a list with a specific type
List<GameObject> myList = new List<GameObject>();
// Add each element to the list, the order is preserved
myList.Add(nextGameObject);
// Accessing the elements is just like an array
GameObject goAtZero = myList[0];
First of all, thank you for a quick answer! Second of all, suppose I use List ins$$anonymous$$d of array, how do I tell connected clients to reproduce the same list without some kind of RPC or serialization? Is it possible?
The nice thing about Lists is that they come with a lot of helper methods, one of them being toArray(). So if you need to change a list to an array it's fairly simple, and to go the other way you just have to use the AddRange method.
Thanks! But I think I am not particularly clear. With your help, I've managed to write a functioning script, but it doesn't do what I intended: using UnityEngine; using System.Collections; using System.Collections.Generic;
public class instantiate_spectator_at_runtime : $$anonymous$$onoBehaviour {
public List<GameObject> myList;
// Use this for initialization
void Start () {
}
void Update () {
if (Input.Get$$anonymous$$eyDown ($$anonymous$$eyCode.A)) {
Debug.Log ("Index 1" + myList [0]);
Debug.Log ("Index 2" + myList [1]);
Debug.Log ("Index 3" + myList [2]);
Debug.Log ("Index 4" + myList [3]);
}
if (Input.Get$$anonymous$$eyDown ($$anonymous$$eyCode.W)) {
PhotonView photonView = PhotonView.Get(this);
photonView.RPC("addtolist", PhotonTargets.AllBuffered);
}
}
[PunRPC]
public void addtolist(){
if (myList.Contains(this.gameObject)){
Debug.Log("it contains");
}
else
myList.Add(this.gameObject);
}
}
It creates an array and adds old item to it. So when second player logs in to the server, it already has the 1st player. But it fails to distinguish between those items, when second player tries to add this.gameobject to the array - the array says it already contains it! (since objects are prefabs and are the same for all network clients). $$anonymous$$aybe it is needed to compare via NetworkViewID ?
When you're getting "this" it means a reference to the current Script so each time you get it you're still getting the same thing. I think you want to Instantiate a new GameObject for each player and then put that in the List ins$$anonymous$$d, although I'm not really familiar with the PhotonView API.
Fixed! Thanks!
I've wrote another scripts - works almost perfectly, but ins$$anonymous$$d of instantiating spectator prefab once on every client, it instantiates as many times as there are clients.
So if we are the only person on the server it spawns one spectator every time. If there are 2 people connected - every time it spawns 2 on EVERY client, and so on.
I created an object in the scene, gave that object a PhotonView and attached this script. using UnityEngine; using System.Collections; using System.Collections.Generic;
public class instantiate_spectator_at_runtime : $$anonymous$$onoBehaviour {
public List<GameObject> myList;
public PhotonView photonView;
public GameObject[] spawnSpots;
public GameObject myPlayerGO;
// Use this for initialization
void Start () {
photonView = this.gameObject.GetComponent<PhotonView> ();
}
void Update () {
if (Input.Get$$anonymous$$eyDown ($$anonymous$$eyCode.W)) {
//spawn spectators for the others clients
photonView.RPC("addtolist", PhotonTargets.OthersBuffered);
//spawn spectators for our client
spawnSpots = GameObject.FindGameObjectsWithTag("SpawnSpot");
GameObject mySpawnSpot = spawnSpots[ Random.Range (0, spawnSpots.Length) ];
myPlayerGO = (GameObject)PhotonNetwork.Instantiate("spectator", mySpawnSpot.transform.position, mySpawnSpot.transform.rotation, 0);
myList.Add(myPlayerGO.gameObject);
}
}
[PunRPC]
public void addtolist(){
//spawn spectator
spawnSpots = GameObject.FindGameObjectsWithTag("SpawnSpot");
GameObject mySpawnSpot = spawnSpots[ Random.Range (0, spawnSpots.Length) ];
myPlayerGO = (GameObject)PhotonNetwork.Instantiate("spectator", mySpawnSpot.transform.position, mySpawnSpot.transform.rotation, 0);
//add spectator to list
if (myList.Contains (myPlayerGO.gameObject)) {
for (int i = 0; i < myList.Count; i++){
if(myList[i].gameObject.GetComponent<PhotonView>().viewID == myPlayerGO.gameObject.GetComponent<PhotonView>().viewID){
Debug.Log("it's the same");
PhotonNetwork.Destroy(myPlayerGO);
myPlayerGO = null;
break;
}
else
myList.Add(myPlayerGO.gameObject);
}
}
else
myList.Add(myPlayerGO.gameObject);
}
}
Is there any idea, how to fix it? And again - thank you for your time and helpful advice.
Again, I'm not really familiar with the PhotonNetwork stuff but I think the problem might be in this block of code:
if (myList.Contains (myPlayerGO.gameObject)) {
for (int i = 0; i < myList.Count; i++){
if(myList[i].gameObject.GetComponent<PhotonView>().viewID ==
myPlayerGO.gameObject.GetComponent<PhotonView>().viewID){
Debug.Log("it's the same");
PhotonNetwork.Destroy(myPlayerGO);
myPlayerGO = null;
break;
}
else
myList.Add(myPlayerGO.gameObject);
}
}
else
myList.Add(myPlayerGO.gameObject);
It's checking if the GameObject's in the list and then iterating through to see if there's a match in order to destroy it. The problem could be that after destroying it, the loop continues but since the original was destroyed and there's no check for any new ones, the else statement will keep making copies.
Thank you again! This is not a complete solution, but I will mark it as an answer, if you change it from a comment.