Search Unity

Problem With PlayerList using new UI [PUN]

Discussion in 'UGUI & TextMesh Pro' started by Mrslayer01, May 30, 2015.

  1. Mrslayer01

    Mrslayer01

    Joined:
    Mar 23, 2014
    Posts:
    30
    Hell everyone well I seem to be having a big problem with making a playerlist for my lobby with the new UI. I was told not to use not to use the photonnetwork.Instantiate and just make an rpc call like this:

    Code (CSharp):
    1.     void OnJoinedRoom() {
    2.         PreLobby();
    3.         GetComponent<PhotonView>().RPC ("UpdatePlayerList", PhotonTargets.AllBuffered);
    4.     }
    5.  
    6.     [RPC]
    7.     public void UpdatePlayerList() {
    8.         foreach (PhotonPlayer player in PhotonNetwork.playerList) {
    9.             temp = (GameObject)Instantiate(playerButton);
    10.             temp.transform.SetParent(panel_target_PlayerList);
    11.             temp.GetComponentInChildren<Text>().text = "" + player.name;
    12.         }
    13.     }
    Basically as soon as a player joins the lobby it calls updateplayerlist but with this code specifically the foreach statement it causes the the gameobject to be created once for each player in the room any time someone joins. Now what i would like to do is use photons Instantiate so when a player disconnects from the room it would remove the players button since it's owned by that player but with photon for some reason you cannot parent the objects you Instantiate. Sorry if i did not explain this the best but I've been stuck on this for so long now so any help would be appreciated! Thanks again.
     
  2. TechCor

    TechCor

    Joined:
    Apr 3, 2015
    Posts:
    56
    You just need to listen to the connect and disconnect callbacks for everything after the local player joins the room.

    Photon provides these event messages.
    void OnPhotonPlayerConnected(PhotonPlayer newPlayer)
    void OnPhotonPlayerDisconnected(PhotonPlayer otherPlayer)

    1. When you join the room, loop through the player list and instantiate the buttons.
    2. On player connected, add a button.
    3. On player disconnected, remove a button.

    Store the player name somewhere on the button so you can iterate through the buttons and remove it when needed.

    Note: Questions about the use of PUN should be asked on the Exit Games forum.
     
  3. Mrslayer01

    Mrslayer01

    Joined:
    Mar 23, 2014
    Posts:
    30
    Thank you so much for the Info TechCor I'm still just a listtle confused on what you mean by loop through the playerlist would it just be:

    Code (CSharp):
    1.     void OnPhotonPlayerConnected(PhotonPlayer newPlayer) {
    2.         temp = (GameObject)Instantiate(playerButton);
    3.         temp.transform.SetParent(panel_target_PlayerList);
    4.         temp.GetComponentInChildren<Text>().text = "" + newPlayer.name;
    5.     }
    or something else sorry i'm new to the whole playerlist thing and thank you for letting me know to post on the exitgames forums.
     
  4. TechCor

    TechCor

    Joined:
    Apr 3, 2015
    Posts:
    56
    That fulfills the second step of what I posted, when a new player joins the room after the local player has already joined.

    For step 1,

    Code (csharp):
    1.  
    2. private bool m_bLocalPlayerJoined = false;
    3.  
    4. void OnJoinedRoom() {
    5.   if (!m_bLocalPlayerJoined)
    6.   {
    7.     PreLobby();
    8.     UpdatePlayerList();
    9.     m_bLocalPlayerJoined = true;
    10.   }
    11.   }
    12.  
    13.   private void UpdatePlayerList() {
    14.   foreach (PhotonPlayer player in PhotonNetwork.playerList) {
    15.   GameObject temp = (GameObject)Instantiate(playerButton);
    16.   temp.transform.SetParent(panel_target_PlayerList);
    17.   temp.GetComponentInChildren<Text>().text = "" + player.name;
    18.   }
    19.   }
    I'm not 100% sure, but I believe the first call to OnJoinedRoom is the local player, and subsequent calls are remote players, so it would be best to react only to the first call.
     
    alarm656 likes this.
  5. TechCor

    TechCor

    Joined:
    Apr 3, 2015
    Posts:
    56
    For step 3, something like:

    Code (csharp):
    1.  
    2. void OnPhotonPlayerDisconnected(PhotonPlayer otherPlayer) {
    3.   foreach (Transform t in panel_target_PlayerList)
    4.   {
    5.   if (t.GetComponentInChildren<Text>().text == otherPlayer.name)
    6.   {
    7.   GameObject.Destroy(t.gameObject);
    8.   }
    9.   }
    10.   }
    11.  
    Also, this is just working with what code you posted. Iterating on the transforms and using GetComponentInChildren are potentially slow and subject to bugs when you need to expand your code. You can't be certain of what you are iterating over at all times. You should perhaps consider a monobehaviour that has the Text assigned to a member variable. I'd probably go with:

    Code (csharp):
    1.  
    2. PlayerButton[] buttons = panel_target_PlayerList.GetComponentsInChildren<PlayerButton>();
    3. foreach (PlayerButton btn in buttons)
    4. {
    5.   if (btn.playerName == otherPlayer.name) // Where playerName is public string playerName { get { return m_TxtPlayerName.text; } set { m_TxtPlayerName.text = value; } }
    6.   {
    7.     GameObject.Destroy(btn.gameObject);
    8.     break;
    9.   }
    10. }
    11.  
    It's easy to understand the PlayerButton's functionality because it's a real object and has appropriate encapsulation, rather than relying on cryptic GetComponent access and iterating on generic transforms.
     
    alarm656 likes this.
  6. Mrslayer01

    Mrslayer01

    Joined:
    Mar 23, 2014
    Posts:
    30
    Thank you so much TechCor The code worked perfectly I hope others that have the same type of problem will be able to find your posts thanks again. Now just have to figure out how to deal with when 2 players join and have the same name but that is for another post thanks again TechCor
     
    Last edited: May 31, 2015
  7. alarm656

    alarm656

    Joined:
    Jul 6, 2013
    Posts:
    111
    I'm sorry, but looks like you forgot to add Step 2.
    my first player creates room and it spawns a info text about his name on "A" position.
    when second player joins he also spawns a info text about his name but he spawns on "A" position also((
    I can't figure out how do it. how can i give lp[] positions to other players?

    Code (CSharp):
    1. private bool m_blocalPlayerJoined = false;
    2.  
    3.      void OnJoinedRoom ()
    4.     {
    5.         if (!m_blocalPlayerJoined)
    6.         {
    7.             UpdatePlayerList ();
    8.             m_blocalPlayerJoined = true;
    9.         }
    10.     }
    11.  
    12.     private void UpdatePlayerList ()
    13.     {
    14.         foreach (PhotonPlayer player in PhotonNetwork.playerList)
    15.         {
    16.             GameObject temp = (GameObject)Instantiate (playerInfo, lp[0], Quaternion.identity);
    17.             temp.transform.SetParent (Hover.transform, false);
    18.             temp.GetComponentInChildren<Text> ().text = "" + player.name;
    19.  
    20.  
    21.         }
    22.     }
     

    Attached Files:

  8. alarm656

    alarm656

    Joined:
    Jul 6, 2013
    Posts:
    111
    I have tried like this but does not works(

    Code (CSharp):
    1.  
    2. private void UpdatePlayerList ()
    3.     {
    4.         foreach (PhotonPlayer player in PhotonNetwork.playerList)
    5.         {
    6.             GameObject temp = (GameObject)Instantiate (playerInfo);
    7.             temp.transform.SetParent (Hover.transform, false);
    8.             temp.GetComponentInChildren<Text> ().text = "" + player.name;
    9.  
    10.             if (PhotonNetwork.player.ID == 1)
    11.             {
    12.                 temp.GetComponent<RectTransform> ().localPosition = new Vector3 (200, 80, 0);
    13.             }
    14.             if (PhotonNetwork.player.ID == 2)
    15.             {
    16.                 temp.GetComponent<RectTransform> ().localPosition = new Vector3 (200, 40, 0);
    17.             }
    18.         }
    19.     }
    20.  
     
  9. Jean-Fabre

    Jean-Fabre

    Joined:
    Sep 6, 2007
    Posts:
    429
    Hi,

    Dealing with the new UI and contents that updates often requires caching data so that you only destroy or create instances of the UI content when required.

    so keep a dictionnary of the Player ids you are already showing in the list, with a reference of the gameobject representing that player int he UI list, then on update, you only update information on existing player ids int he list, delete the one not found on photon player's list and create the new ones not yet inside the UI list.

    I am currently reworking a photon sample to use the new UI, it will feature this caching. I'll post back here when it's available.

    Bye,

    Jean
     
  10. Jean-Fabre

    Jean-Fabre

    Joined:
    Sep 6, 2007
    Posts:
    429
    ok,

    Please find a fully working scene dealing with player's list attached to this post. It's addressing a lot of typical issues when having to deal with caching information to display it inside the new UI system, because unlike the old UI you can't simply feed on every update the data, it has to be efficient, and only creating/destroying object when absolutely necessary, and also not updating content every update as it impact performances a lot.

    The package was done with Unity 4.7, but works on Unity 5 too.



    This sample is a good starting point for simple Player listing. It doesn't take in consideration custom Player properties for example. To do this, each item would then get the responsability to watch for player properties changes, it's a lot more work, but totally feasible of course.

    No rpc calls or any networking data sharing should be done to achieve this, PUN already provides everything without having to take up more bandwidth, so watch out for this, it's important. Listing players should be totally done locally.

    Let me know if you have questions or issues :)

    Bye,

    Jean
     

    Attached Files:

    alarm656 likes this.
  11. alarm656

    alarm656

    Joined:
    Jul 6, 2013
    Posts:
    111
    Yeah, it is amazing! Thank you very much Jean. I almost was thinking about forgetting new UI and start with old GUI. I just downloaded and testing now. There is not only player listing there are much more good stuff like leaving and rejoining. This is great support from Photon engineers. Thank you again Photon for network solution and thank you Jean for great support! I'm just playing and amazing, if i have any questions and issues I'll let you to know. T.jpg
     
  12. Jean-Fabre

    Jean-Fabre

    Joined:
    Sep 6, 2007
    Posts:
    429
    Hi,

    You are welcome. I am glad this put you on the right track.

    Bye,

    Jean
     
    alarm656 likes this.
  13. alarm656

    alarm656

    Joined:
    Jul 6, 2013
    Posts:
    111
    Hi Jean, I don't know is it issue or not, but I got something strange.
    I have implemented childCount on UIList
    Code (CSharp):
    1. if (UIList.transform.childCount == 1)
    2. {
    3. fakePlayers [0].SetActive (false);
    4. fakePlayers [1].SetActive (true);
    5. fakePlayers [2].SetActive (true);
    6. fakePlayers [3].SetActive (true);
    7. fakePlayers [4].SetActive (true);
    8. }
    9. if (UIList.transform.childCount == 2)
    10. {
    11. fakePlayers [0].SetActive (false);
    12. fakePlayers [1].SetActive (false);
    13. fakePlayers [2].SetActive (true);
    14. fakePlayers [3].SetActive (true);
    15. fakePlayers [4].SetActive (true);
    16. }
    I have tried to put this lines of code under public void UpdateUI () => under else
    also tried to put void Update () - just to be sure, if UpdateUI does not calls.
    Strange happened when second player (PlayerB) left room but PlayerA can't see "fakePlayers[1]". "fakePlayers[1] still false.
    It's because PlayerB prefab does not destroyed on PlayerA. I saw it in Unity's Hierarchy. I uploaded screen image.
    As you can see on screen image PlayerB has vanished (destroyed) when I launched Safari Browser.
    I didn't touched to original(your)script. I'm experimenting on copied version. You can't see "fakePlayers[] on screen image, it's because I also didn't edited your original scene.
    https://www.dropbox.com/s/nt7z5w0o733gx8s/Strange1.png?dl=0
    https://www.dropbox.com/s/w6ichg0uo9n7k2d/Strange2.png?dl=0

    Edit:
    fakePlayers are gameobjects which is similar with "Player List Item UI" prefab. But contains just only text, written "Waiting for player..."
    There are 5 fakePlayers on panel. Every time joins new player the fakePlayer disables by one by one. And must enable back when players left the room.
    Do I need implement it with
    if(PhotonNetworking.playerList.Length == 1)
    {

    }
    ?

    It will be great if you explain me how to disable and enable fakePlayers like you did with this:
    GameObject _item = (GameObject)Instantiate(PlayerItemPrefab);
    Thanks

    Edit 2:

    I put these lines of codes under void Update ()

    Code (CSharp):
    1. if(PhotonNetwork.playerList.Length == 1)
    2. {
    3. fakePlayer [0].SetActive (false);
    4. fakePlayer [1].SetActive (true);
    5. fakePlayer [2].SetActive (true);
    6. }
    7.  
    8. if(PhotonNetwork.playerList.Length == 2)
    9. {
    10. fakePlayer [0].SetActive (false);
    11. fakePlayer [1].SetActive (false);
    12. fakePlayer [2].SetActive (true);
    13. }
    And disabled animations. Works good but I'm not sure how I can implement "Team Match" by this method.
     
    Last edited: Sep 4, 2016
  14. Jean-Fabre

    Jean-Fabre

    Joined:
    Sep 6, 2007
    Posts:
    429
    Hi,

    ok, so I assume that you have solved your fake players list items. I would call them "PlaceHolders" instead of fake, it makes more sense. These placeholders players are "Slots" for players to join and starts a game.

    I would suggest that instead of creating separate prefabs for placeholders and players, simply create a prefab that accounts for both. Modify your List UI Management so that you always have the four players items, already instantiated in the list ( at development time), and then simply update them four items with photonPlayers data or not, this would be a lot simpler to manage then trying to swap things around, and it will be easier to deal with teams ( see below).

    As for teams. It depends on your logic, can you explain how teams works in your game?

    But my guess would be that you have two teams or let's say 2 players, so 2 Vs 2

    We have a utility script aiming at helping with teams, it's called "PunTeams.cs", it provides extensions to the PhotonPlayer to work with teams. Please check our advanced demo "Pickup" it features implementation of teams.

    inside the UI, you'll have to account for the team value, and change the background color, or swap indexes around so that team A players are the first two, and team B Players are the last two of the list.

    If you follow my advice on having static items in the list, then you can organize both teams at development time and not caring about their sorting at runtime. It would be easier.

    Bye,

    Jean
     
  15. alarm656

    alarm656

    Joined:
    Jul 6, 2013
    Posts:
    111
    Thank you, Pickup demo sets team by pressing a button. In my case teams must separates automatically when they join, (For now I did it by randomly)
    PlayerList Prefab has this script:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3. using System;
    4. using ExitGames.Client.Photon;
    5. using Hashtable = ExitGames.Client.Photon.Hashtable;
    6.  
    7. public class TeamMember : MonoBehaviour
    8. {
    9.     public int teamID = 0;
    10.  
    11.     void Awake ()
    12.     {
    13.         teamID = UnityEngine.Random.Range (1, 3);
    14.     }
    15.  
    16.     void Start ()
    17.     {
    18.         if (teamID == 1)
    19.         {
    20.             PhotonNetwork.player.SetTeam (PunTeams.Team.blue);
    21.             print ("Blue");
    22.         }
    23.         if (teamID == 2)
    24.         {
    25.             PhotonNetwork.player.SetTeam (PunTeams.Team.red);
    26.             print ("Red");
    27.         }
    28.     }
    29.  
    30. }
    I can't test this on UI Panel because when second player joined he can't see the UI panel. First Player also can't update that second player has joined. It's because I'm getting error in "PlayerItem" script.
    Error Message :
    NullReferenceException: Object reference not set to an instance of an object
    ExitGames.Demos.UI.PItem.AnimateRevealItem () (at Assets/Scripts/Menu/PItem.cs:46)
    On this line:
    Code (CSharp):
    1.  
    2. public void AnimateRevealItem()
    3.         {
    4.             if (this.transform.parent.gameObject.activeInHierarchy)
    5.             {
    6.                 StartCoroutine(AnimateRevealItem_cr());
    7.             }
    8.  
    9.         }
    10.  
    I don't know where and how can I put randomizing code lines for GameType (TeamMatch/DeathMatch).
    For now I created static int in Menu script and call it "gameType".
    Code (CSharp):
    1.  
    2. public void OnPhotonRandomJoinFailed()
    3.         {
    4.             if (!ConnectionInProgress)
    5.             {
    6.                 return;
    7.             }
    8.              
    9.                 int[] numbers = new int[2] { 1, 2 };
    10.                 gameType = numbers [Random.Range (0, numbers.Length)];
    11.  
    12.             if (gameType == 1)
    13.             {
    14.                 Team_Match_Lobby.SetActive (true);
    15.             }
    16.             if (gameType == 2)
    17.             {
    18.                 Death_Match_Lobby.SetActive (true);
    19.             }
    20.             PhotonNetwork.CreateRoom(null, new RoomOptions() { MaxPlayers = 10 }, null);
    21.         }
    22.  
    Works ok. First player creates a room no matter Team or Death Match.
    But second player can't see the playerList panel.
    Didn't tested on DeathMatch.

    This lines for PlayerList_Prefab
    where they have to child
    Code (CSharp):
    1.  
    2. GameObject _item =  (GameObject)Instantiate(PlayerItemPrefab);
    3.                     // Game Mode TeamMatch or DeathMatch
    4.                     if (Menu.gameType == 1) // TeamMatch
    5.                     {
    6.                         if (_item.GetComponent<TeamMember>().teamID == 1)
    7.                         {
    8.                             _item.transform.SetParent (tmUIList1.transform);
    9.                             _item.GetComponent<Image> ().color = new Color32 (0, 118, 255, 255);
    10.                             globalTeamID = 1; //for disabling and enabling "PlaceHolders" fakePlayers
    11.                         }
    12.  
    13.                         if (_item.GetComponent<TeamMember>().teamID == 2)
    14.                         {
    15.                             _item.transform.SetParent (tmUIList2.transform);
    16.                             _item.GetComponent<Image> ().color = new Color32 (255, 185, 0, 255);
    17.                             globalTeamID = 2; //for disabling and enabling "PlaceHolders" fakePlayers
    18.                         }
    19.                     }
    20.                     if (Menu.gameType == 2) //DeathMatch
    21.                     {
    22.                         _item.transform.SetParent (dmUIList.transform);
    23.                         globalTeamID = 0;
    24.                     }
    25.                     PItem _playerItem = _item.GetComponent<PItem>();
    26.                     _items.Add(_player.ID,_playerItem);
    27.                     _items[_player.ID].RefreshData(_player);
    28.  
    29.                     _playerItem.AnimateRevealItem();
    30.  
    31.                     processedIDS.Add(_player.ID);
    32.                     //globalTeamID = _item.GetComponent<TeamMember> ().teamID;
    33.                     print (globalTeamID);
    34.  
    edit:
    Same problem when even Player 1 creates DeathMatch. Player 2 can't see PlayerListUI. But Player 1 updates and sees that Player 2 has joined.
    Edit: I have noticed that I can see in Unity's Hierarchy there is a PlayerListPrefab(Clone) out of in game Lobby UI. So problem occurs when gameType(1/2) other players can't see or can't get variable about gameType. I don't know(((
     
  16. Jean-Fabre

    Jean-Fabre

    Joined:
    Sep 6, 2007
    Posts:
    429
    Hi,

    Your error is that you haven't parented your UI item instance to the GameObject container for all items, so this.transform.parent is null because the transform is at the root of the scene Hierarchy.

    Apart from that, assigning team to joining users is a matter to scanning the existing list of players and find out where this new coming player what team it can be in. So don't overthink this. One very quick way is to hardcode players, player 1 and 3 are team A, player 2 and 4 are team B, and done.

    Bye,

    Jean
     
    alarm656 likes this.
  17. alarm656

    alarm656

    Joined:
    Jul 6, 2013
    Posts:
    111

    I understood my problem. When Player 1 creates room, he will enable specific UI Panel for PlayerList. I also tried to open specific unity scene. Works ok but other players can't join or can't enable specific UI panel or open unity scene. Because they don't creates they just joining.
    Example code:
    Code (CSharp):
    1.  
    2. void OnCreatedRoom()
    3.         {
    4.             Debug.Log( "OnCreatedRoom" );
    5.  
    6.             PhotonNetwork.room.SetCustomProperties( properties );
    7.             int[] numbers = new int[2] { 1, 2 };
    8.             gameType = numbers [Random.Range (0, numbers.Length)];
    9.  
    10.             if (gameType == 1)
    11.             {
    12.                 //Team_Match_Lobby.SetActive (true);
    13.                 PhotonNetwork.LoadLevel( "TM_Lobby_Scene" );
    14.                 ConnectionStatusText.text = "" + PhotonNetwork.playerList.ToString();
    15.             }
    16.  
    17.             if (gameType == 2)
    18.             {
    19.                 //Death_Match_Lobby.SetActive (true);
    20.                 PhotonNetwork.LoadLevel( "DM_Lobby_Scene" );
    21.             }
    22.         }
    23.  
    As u can see I'm trying to give specific playlist panel for game modes. There are only 2 game modes TeamDeathMatch/Death Match (maybe later "CaptureFlag")
    When player presses "PLAY" button, if there is no room that, he will create a new room randomly [TeamDeathMatch or DeathMatch] if TeamDeathMatch please enable specific player list panel. if DeathMatch so please enable specific player list panel.

    I think some people solves such as questions by using Room properties. But I have no idea how to implement it.
    I have also created something similar to Room properties but I don't know how to assign it

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class RoomProperty
    6. {
    7.     public const string tm = "TeamMatch";
    8.     public const string dm = "DeathMatch";
    9.  
    10. }
    Thank you Jean and sorry. I hope you have easy solution for my every problems:) Thanks again
     
  18. alarm656

    alarm656

    Joined:
    Jul 6, 2013
    Posts:
    111
    I could not achieve with Room properties and with "int" variable. I'm going to try with sending PunRPC and ataching commponent to PlayerListPerfab. Hope this will work fine
    how do I assigned "int" varaiable:

    Code (CSharp):
    1.  
    2. public static int gameType = 0;
    3. public GameObject Team_Match_Lobby;
    4. public GameObject Death_Match_Lobby;
    5.  
    6. void OnJoinedRoom ()
    7. {
    8. if (PhotonNetwork.isMasterClient)
    9. {
    10. int[] numbers = new int[2] { 1, 2 };
    11. gameType = numbers [Random.Range (0, numbers.Length)];
    12. }
    13. if (gameType == 1)
    14. {
    15.          Team_Match_Lobby.setActive (true);        
    16. }
    17.  
    18. if (gameType == 2)
    19. {
    20.          Death_Match_Lobby.setActive (true);        
    21. }
    22. }
    23.  
    Other joined Players can't see the enabled gameobject. I don't know how to assign MasterClient's variable for other players without RPC. So I'm going to try to use "PhotonView" component and try to assign it with "RPC"
     
  19. Jean-Fabre

    Jean-Fabre

    Joined:
    Sep 6, 2007
    Posts:
    429
    Hi,

    You should keep trying to make it work with Custom room Properties.

    Bye,

    Jean