How to create several spawnpoints without spawning at the same place in Photon
Hello,
yes I know there's a similar question to this already, but it's not the same question, because I'm trying to spawn players at several spawnpoints and not objects.
So this is my code:
using System.Collections.Generic;
public Transform[] spawns;
public int amountThings = 2;
public int i = 0;
void Start()
{
Connect();
}
void Connect ()
{
PhotonNetwork.ConnectUsingSettings(verNum);
Debug.Log("Connecting...");
}
public void spawnPlayer()
{
List<Transform> freeSpawnPoints = new List<Transform>(spawns);
int index = Random.Range(0, freeSpawnPoints.Count);
for (i = 0; i < amountThings; i++)
{
if (freeSpawnPoints.Count <= 0)
Debug.LogError("Not enough spawnpoints!!!!!!!!!");
Transform pos = freeSpawnPoints[index];
freeSpawnPoints.RemoveAt(index); // remove the spawnpoint from our temporary list
GameObject pl = PhotonNetwork.Instantiate(playerPref.name, pos.position, pos.rotation, 0) as GameObject;
pl.GetComponent<RigidbodyFPSWalker>().enabled = true;
pl.GetComponent<RigidbodyFPSWalker>().fpsCam.SetActive(true);
RigidbodyFPSWalker.speed = 10.0f;
SimpleSmoothMouseLook.disabled = false;
pl.GetComponent<Animations>().enabled = true;
}
}
The players are still spawning at the same place. Every player should has an own spawn place.
So I think, that the list of the spawnpoints must be synchronized to the other players. How can I synchronize it to the others?
I'll be very happy about an helpful answer. Thank you for reading.
Answer by ChristianSimon · Oct 04, 2016 at 11:42 AM
With the logic above you have to synchronize the original structure ('spawns'). So whenever a player spawn you remove one entry from the copy created within the spawnPlayer() function. You need to apply this change to the original structure too and share this event with all others active and upcoming players. since this seems somehow too complicated I want to make another suggestion you might think about.
In short: Master Clients sets spawn position for himself and all upcoming players. In detail: Master Client creates a structure containing all available spawn points at the beginning. When OnJoinedRoom() is called he selects one of these points, creates his character from prefab and removes the spawn point entry from the list. Most likely before. Whenever OnPhotonPlayerConnected(...) is called (another players joined the room) the Master Client selects another spawn position and stores it in the custom room properties as key-value-pair (e.g. userId and spawn position). OnPhotonCustomRoomPropertiesChanged(...) will be called and the client can check the spawn position allocated to his userId.
However, when going with this attempt (Master Clients selects spawn positions for other players), there are additional options for solving this problem.
I will try this in the next days. I'm sitting on this many weeks already. I hope I will get a solution in the following week.
Answer by MikGames · Dec 03, 2016 at 08:50 PM
So now I have this code and my question is how do I get values from the Hashtable for a specific player?:
public void OnPhotonPlayerConnected()
{
DebugConsole.Log("Player " + PhotonNetwork.player.name + " joined.");
if (PhotonNetwork.isMasterClient)
{
List<Transform> freeSpawnPoints = new List<Transform>(spawns);
int index = Random.Range(0, freeSpawnPoints.Count);
Transform pos = freeSpawnPoints[index];
Hashtable t = new Hashtable();
t.Add("Player", PhotonNetwork.player.name);
t.Add("Location", pos);
PhotonNetwork.room.SetCustomProperties(t);
}
else
{
RoomOptions roomOptions = new RoomOptions();
string pos = (string) PhotonNetwork.room.customProperties[RoomProperty.Location]; <-- ??????????
spawnPlayer(pos);
}
}
public void OnJoinedRoom()
{
isConnected = true;
if (PhotonNetwork.isMasterClient)
{
List<Transform> freeSpawnPoints = new List<Transform>(spawns);
int index = Random.Range(0, freeSpawnPoints.Count);
DebugConsole.Log("SpawnPoints: " + freeSpawnPoints.Count);
DebugConsole.Log("index: " + index);
if (freeSpawnPoints.Count <= 0)
{
DebugConsole.Log("Not enough spawnpoints!!!!!!!!!", "error");
}
else
{
Transform pos = freeSpawnPoints[index];
DebugConsole.Log("Pos: " + pos + pos.position + pos.rotation);
freeSpawnPoints.RemoveAt(index);
spawnPlayer(pos);
}
}
}
Is the connecting player network instantiated (PhotonNetwork.Instantiate(...)) or manually instantiated on each client? The following is for the first case.
The callback OnPhotonPlayerConnected needs a parameter in order to work as expected. It is void OnPhotonPlayerConnected(PhotonPlayer newPlayer)
. Additionally when this callback is executed, the room properties might not be updated when you try to get them in the 'else' case.
$$anonymous$$aybe in this case you should use player properties ins$$anonymous$$d of room properties.
void OnPhotonPlayerConnected(PhotonPlayer newPlayer)
{
if (PhotonNetwork.is$$anonymous$$asterClient)
{
// Select Spawn Position and add to Hashtable
newPlayer.SetCustomProperties(Hashtable propertiesToSet, null, false);
}
}
Then you need a callback to handle the change of the properties: void OnPhotonPlayerPropertiesChanged(object[] playerAndUpdatedProps)
. In this object[] you receive a PhotonPlayer object at index 0 and a Hashtable at index 1. When this function is called, check if object[0].ID equals PhotonNetwork.player.ID. In that case you are on the new connected client and can call PhotonNetwork.Instantiate with the received position.
Hopefully this is somehow understandable.
In short: Client joins room -> OnPhotonPlayerConnected(...) is called and $$anonymous$$asterClient selects Spawn position and sets Custom Player Properties -> Changes are received by all clients, but only matching client will proceed and calls PhotonNetwork.Instantiate(...).
So now I've changed my code. The "OnPhotonPlayerPropertiesChanged" method looks like this:
public void OnPhotonPlayerPropertiesChanged(object[] playerAndUpdatedProps)
{
PhotonPlayer player = playerAndUpdatedProps[0] as PhotonPlayer;
Hashtable t = playerAndUpdatedProps[1] as Hashtable;
Debug.Log("photonplayer.id: " + player.ID + " photonid: " + PhotonNetwork.player.ID);
if ((player.ID.Equals(PhotonNetwork.player.ID))
{
DebugConsole.Log("True");
Debug.Log(t);
string position = (string) t[1];
DebugConsole.Log("pos:" + position);
Debug.Log(position);
DebugConsole.Log(player.name);
Transform pos = (Transform) t[0];
spawnPlayer(pos);
}
}
The "OnPhotonPlayerConnected":
public void OnPhotonPlayerConnected(PhotonPlayer newPlayer) { DebugConsole.Log("Player " + newPlayer + " joined."); DebugConsole.Log("Is not $$anonymous$$asterClient");
if (PhotonNetwork.is$$anonymous$$asterClient)
{
//Spawnpoint aussuchen und "pos" setzen
List<Transform> freeSpawnPoints = new List<Transform>(spawns);
int index = Random.Range(0, freeSpawnPoints.Count);
Transform pos = freeSpawnPoints[index];
freeSpawnPoints.RemoveAt(index);
DebugConsole.Log("FreeSpawnPoints: " + freeSpawnPoints.Count);
//Hashtable
Hashtable t = new Hashtable();
t.Add("Loc", pos);
//Für den neuen Spieler den Spawnpunkt definieren
newPlayer.SetCustomProperties(t, null, false);
}
}
$$anonymous$$y problem now is, that "player.id" is equals 1 while the "PhotonNetwork.player.ID" is equals 2. I don't find any solution for this for a while.,..
$$anonymous$$y problem now is, that "player.id" is equals 1 while the "PhotonNetwork.player.ID" is equals 2. I don't find any solution for this for a while.,..
This is correct if you use network instantiation now. Ins$$anonymous$$d of your spawnPlayer(...) function [I don't know what happens inside] you can call PhotonNetwork.Instantiate(...) to create the player object at the given position across the network. This also makes sure, that Ownership and other properties are set correctly. The condition (player.ID == PhotonNetwork.player.ID) prevents other players from creating the object, too.
Hello @ChristianSimon,
thank you for the detailed answer and for the source code.
$$anonymous$$y problem is further on that the PhotonPlayer.ID not equals the Photon ID.
Here is a picture about it: http://imgur.com/4xfpgT5
And here's another picture, with a "Can't serialize" exception: http://i.imgur.com/BqpNW$$anonymous$$f.png
So everytime the PhotonPlayer.ID is equals one and with every join the Photon ID gets up.
P.S.: Are you german?
Hi,
sorry for the late response, I haven't been in office the last two weeks.
Can you share me your latest source code so I can check on that? This might be easier solving the problem.
Some words on the "Can't serialize" exception: by default PUN is not able to serialize Unity's Transform objects as far as I know. This is the reason why I only used a Vector3 object to tell the new client his spawn position. However when adding the spawn data to the Hashtable, you can also add Rotation (Quaternion) and Scale (Vector3) data to it ins$$anonymous$$d of using the Transform object itself. To be honest I'm currently not sure if a Quaternion will work. If this also throws an exception you can use an array with four elements representing the Quaternion.
And yes, I'm german. :)
Hey,
ich habe jetzt ausgehend von deinem Sourcecode ein paar Änderungen vorgenommen. Das Ganze funktioniert soweit in meinem relativ leerem Testprojekt. Hier der aktualisierte Code: http://pastebin.com/2$$anonymous$$5Xuf9h
Wenn ein Spieler das Spiel verlässt, kannst du das mit Hilfe des callbacks void OnPhotonPlayerDisconnected(PhotonPlayer otherPlayer) { ... }
erkennen und darauf reagieren. In deinem Fall macht das entsprechend nur der $$anonymous$$asterClient. Du kannst über otherPlayer.customProperties
nun wieder auf die Properties zugreifen, die du vorher für "Position" und "Rotation" gespeichert hast und diese Daten dann "zurückführen". Problem ist, dass man keine Transform Objekte per Code erzeugen kann (soweit ich das gerade weiß). Das heißt, dass man das Ganze etwas anders angehen muss. Dazu fallen mir spontan zwei $$anonymous$$öglichkeiten ein.
1) Die ursprüngliche "spawns" Liste nicht bearbeiten, sondern immer nur auf einer $$anonymous$$opie davon. Dadurch kannst du immer wieder auf alle Spawnpunkte zugreifen und kannst bei einem Spielerdisconnect von "spawns" in "spawnsWorkingCopy" kopieren.
2) Ein neues GameObject erzeugen und dessen Transform $$anonymous$$omponente anpassen. Dann hast du nach einiger Zeit allerdings zahlreiche unnütze Objekte herumliegen, die sich irgendwann negativ auf die Performance auswirken können (außer du entfernst die Vorherigen).
Okay also: Ich habe nun den folgenden Code:
public void OnPhotonPlayerDisconnected(PhotonPlayer player)
{
if (PhotonNetwork.is$$anonymous$$asterClient)
{
Hashtable ht = player.CustomProperties;
Vector3 position = (Vector3)ht["Position"];
Quaternion rotation = (Quaternion)ht["Rotation"];
int index = (int)ht["index"];
List<Transform> freeSpawnPoints = new List<Transform>(spawns);
freeSpawnPoints.Insert(index, position); //Funktioniert nicht
}
}
Aber ich kann "freeSpawnPoints.Insert(index, position);" nicht benutzen, da ja ein Transform Objekt benötigt wird. Ich habe gerade keine Ahnung, wie ich das nun machen könnte, da man ja keine Transform Objekte per Code erstellen kann.
Your answer
Follow this Question
Related Questions
How to identify players on Photon Unity? 2 Answers
How can I Instantiate only one GameObject Using PhotonNetwork. 0 Answers
Sync movements Photon 0 Answers
Raycast over Photon 0 Answers
Bolt and PUN in same Unity project?? 1 Answer