Search Unity

Third Party Photon Pun RPC Question

Discussion in 'Multiplayer' started by TomsTales, Mar 4, 2021.

  1. TomsTales

    TomsTales

    Joined:
    Nov 3, 2020
    Posts:
    91
    Hi guys,

    I need an advice. Below RPC is doing nothing (not even an error lol), it works for one client but that's it. I am not sure if I am missing a huge thing or if I just use the RPC on the wrong method, but yes I tried and tried and nothing.

    On the Gameobject there is a PhotonView which I am ofc calling in the Script.

    Code (CSharp):
    1. public void UpdateTheHealthOfKing()
    2. {
    3. view.RPC("UpdateKingHealth", RpcTarget.All);
    4. }
    5.  
    6.  
    7. [PunRPC]
    8. public void UpdateKingHealth()
    9. {
    10. if (isKing == true)
    11. {
    12. kingHealth.text = health.ToString();
    13. }
    14. }
    Can't this work for an obious reason that I just didnt understand?

    Cheers
     
  2. tobiass

    tobiass

    Joined:
    Apr 7, 2009
    Posts:
    3,068
    Put Debug.Log in both methods and make sure both get called as planned.
    The call of the RPC looks fine otherwise. Is the networked object something you instantiated with PhotonNetwork.Instantiate?
     
  3. TomsTales

    TomsTales

    Joined:
    Nov 3, 2020
    Posts:
    91
    hmm

    the object is in my scene from the beginning so I do not really instantiate it via code, it's just "there"

    could that be a problem? I tried to RPC various other things in my scene also with the same effect that nothing really happened
     
  4. tobiass

    tobiass

    Joined:
    Apr 7, 2009
    Posts:
    3,068
  5. TomsTales

    TomsTales

    Joined:
    Nov 3, 2020
    Posts:
    91
    Hi Tobias,

    I checked the documentations, I watched videos on YoutTube and I even finished a (short) Udemy course building a small MP game with Photon, so it's not that I do not try...also, I managed to get some things done already, so I dont think the setup is wrong:

    - I can open a room
    - I can join this room as the second player
    - I do have a resource folder ;)
    - Objects in the scene do have the View component and if I for example move a character of player 1, he moves for player 2 also
    - It is a turn-based game and I got the turn switch running, so if a player preses space it switches turn for both P1 and P2

    So I think the basics are there. I am very, very sure it is a minor detail I am missing (like always), but those are the hardest to find out :confused:

    So I will go into detail now on what I did and how it looks, and have the slight hope you actually read it and in the end can say "jo why didnt you just...". An when this happens, I will rename one of my kids to Hobbes to honor you :rolleyes:

    This is the UNIT object the Script "Unit" is attached to" goalie1.PNG

    This is the text it's all about and that should update
    goalie2.PNG

    This is the health text object again but well in the hierarchy
    goalie3.PNG

    In the Unit Script, I think interesting are:

    public Text kingHealth; - the Text that gets updated depending on damage

    In my start method I also call UpdateKingHealth(); and ofc I get the PhotonView component

    Then like said there is the actual RPC

    Code (CSharp):
    1.  public void UpdateTheHealthOfKing()
    2.     {
    3.         view.RPC("UpdateKingHealth", RpcTarget.All);
    4.     }
    5.  
    6.  
    7.     [PunRPC]
    8.     public void UpdateKingHealth()
    9.     {
    10.         if (isKing == true)
    11.         {
    12.             kingHealth.text = health.ToString();
    13.         }
    14.     }
    15.  
    and I do call UpdateKingHealth (and accordingly, enemy.UpdateKingHealth at various places in the Scripts). And of course, there is a

    public int health;

    to actually set the health in the inspector!

    I really hope that somewhere there is just that little bug I didnt understand, otherwise I probably spend the next weeks searching :cool:

    Wallo f Text, End!
     
    Last edited: Mar 4, 2021
  6. tobiass

    tobiass

    Joined:
    Apr 7, 2009
    Posts:
    3,068
    Did you do add Debug.Log to both methods and confirmed that a) the calling method is executed and b) the RPC is being called (or not)?
     
  7. tobiass

    tobiass

    Joined:
    Apr 7, 2009
    Posts:
    3,068
    You got more than one PhotonView in that hierarchy.
    Are you sure you call the RPC method on the correct "level" / object?
     
  8. TomsTales

    TomsTales

    Joined:
    Nov 3, 2020
    Posts:
    91
    Yes, I added

    Code (CSharp):
    1.  public void UpdateTheHealthOfKing()
    2.     {
    3.         Debug.Log("Hi");
    4.         view.RPC("UpdateKingHealth", RpcTarget.All);
    5.     }
    6.  
    7.  
    8.     [PunRPC]
    9.     public void UpdateKingHealth()
    10.     {
    11.         if (isKing == true)
    12.         {
    13.             Debug.Log("Hi");
    14.             kingHealth.text = health.ToString();
    15.         }
    16.     }
    And it gave me this

    debug.PNG

    so I think it got called perfectly.

    I think that what you mentioned is the right way but I cant get my head around it. In the Udemy course I did, I had one game health object for both players, put the script on THAT object, called RPC, Heureka. However in this game I am facing the problem that it is developed as a local multiplayer game (and works fine like that), but this also means there are a lot of objects and scripts that I somehow smashed together over the last many weeks and that makes it pretty complicated for me too understand how to call it on the correct object.

    The "Unit" Script with the Rpc Method above is on all Units as the name suggests. However it is NOT on the actual text that should by synchronized (or on its canvas or whatever) - not like in the tutorials where it is always on the same object that does something.


    I hope my babbleing is understandable and thanks that you still bear with me!
     
  9. tobiass

    tobiass

    Joined:
    Apr 7, 2009
    Posts:
    3,068
    So, we established that the RPCs are being called locally and remotely. This is nice.

    Yes, it's tricky to move from local multiplayer to online. I can't follow enough to help though, sorry. At the moment, I am out of ideas.

    The question would be, where does kingHealth.text point at? You can pass this to Debug.Log, after the string and in the editor, the log entry will highlight the target object on click (in the console)...
     
  10. TomsTales

    TomsTales

    Joined:
    Nov 3, 2020
    Posts:
    91
    I know it's hard without actually seeing the project

    Well, it is frustrating of course can`t deny that, but in the end, I guess all I can do is try everything out until something good happens eh

    Thanks that you tried, appreciated!
     
  11. TomsTales

    TomsTales

    Joined:
    Nov 3, 2020
    Posts:
    91
    So, for the first time in the project, I will have to surrender, step back for a while, train with some other, smaller games and hopefully come back at a later point when I am better, as I feel like I can't put more time into this without feeling I am not progressing at all. Hopefully I will be able to finish this in the future but for now I guess I just can't.

    For whatever reason, the Debugs only are activated when plaing in the Unity Game View, not in the Standalone, and whatever I do that stupid Text is not updated for both Client and Server, only for one.

    As it doesn't cost me anything, Ill just add the full code here, can't hurt and mabye in a different dimension it helps someone with something (or someone is super bored) :D

    itdoesntwork.jpg

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5. using Photon.Pun;
    6.  
    7. public class Unit : MonoBehaviour
    8. {
    9.  
    10.     public AudioSource source;
    11.     public AudioClip goal;
    12.     public AudioClip move;
    13.  
    14.     public GameObject victoryPanel;
    15.  
    16.     public Animator animator;
    17.  
    18.     public bool selected;
    19.     GameMaster gm;
    20.  
    21.     public Text kingHealth;
    22.     public bool isKing;
    23.  
    24.     public int tileSpeed;
    25.     public bool hasMoved;
    26.     public float moveSpeed;
    27.  
    28.     public int playerNumber;
    29.  
    30.     public int attackRange;
    31.     List<Unit> enemiesInRange = new List<Unit>();
    32.     public bool hasAttacked;
    33.  
    34.     public GameObject weaponIcon;
    35.  
    36.     public int health;
    37.     public int attackDamage;
    38.     public int defenseDamage;
    39.     public int armor;
    40.  
    41.     private Animator camAnim;
    42.     public GameObject deathEffect;
    43.  
    44.     public DamageIcon damageIcon;
    45.  
    46.     SpriteRenderer sprite;
    47.  
    48.     GameObject obj;
    49.  
    50.     PhotonView view;
    51.  
    52.  
    53.     private void Start()
    54.     {
    55.         source = GetComponent<AudioSource>();
    56.         animator = GetComponent<Animator>();
    57.         gm = FindObjectOfType<GameMaster>();
    58.         camAnim = Camera.main.GetComponent<Animator>();
    59.         UpdateKingHealth();
    60.         sprite = GetComponent<SpriteRenderer>();
    61.         obj = GameObject.FindGameObjectWithTag("GameMaster");
    62.         view = GetComponent<PhotonView>();
    63.     }
    64.  
    65.  
    66.     public void UpdateTheHealthOfKing()
    67.     {
    68.         PhotonView photonView = PhotonView.Get(this);
    69.         photonView.RPC("UpdateKingHealth", RpcTarget.All);
    70.     }
    71.  
    72.  
    73.     [PunRPC]
    74.     void UpdateKingHealth()
    75.     {
    76.         if (isKing == true)
    77.         {
    78.             kingHealth.text = health.ToString();
    79.         }
    80.     }
    81.  
    82.     private void OnMouseOver()
    83.     {
    84.         if (Input.GetMouseButtonDown(1))
    85.         {
    86.             gm.ToggleStatsPanel(this);
    87.         }
    88.     }
    89.  
    90.  
    91.     private void OnMouseDown()
    92.     {
    93.         ResetWeaponIcons();
    94.         if (selected == true)
    95.         {
    96.             selected = false;
    97.             gm.selectedUnit = null;
    98.             gm.ResetTiles();
    99.         }
    100.         else
    101.         {
    102.             if (playerNumber == gm.playerTurn)
    103.             {
    104.                 if (gm.selectedUnit != null)
    105.                 {
    106.                     gm.selectedUnit.selected = false;
    107.                 }
    108.  
    109.                 selected = true;
    110.                 gm.selectedUnit = this;
    111.  
    112.                 gm.ResetTiles();
    113.                 GetEnemies();
    114.                 GetWalkableTiles();
    115.             }
    116.         }
    117.  
    118.         Collider2D col = Physics2D.OverlapCircle(Camera.main.ScreenToWorldPoint(Input.mousePosition), 0.15f);
    119.         Unit unit = col.GetComponent<Unit>();
    120.  
    121.         if (gm.selectedUnit != null)
    122.         {
    123.             if (gm.selectedUnit.enemiesInRange.Contains(unit) && gm.selectedUnit.hasAttacked == false)
    124.             {
    125.                 gm.selectedUnit.Attack(unit);
    126.             }
    127.         }
    128.     }
    129.  
    130.     public void Attack(Unit enemy)
    131.  
    132.     {
    133.         animator.SetTrigger("Attack");
    134.         camAnim.SetTrigger("Shake");
    135.         hasAttacked = true;
    136.  
    137.         if (hasAttacked == true)
    138.         {
    139.             hasMoved = true;
    140.             gm.ResetTiles();
    141.             sprite.color = new Color(1, 0, 0, 1);
    142.         }
    143.  
    144.         int enemyDamage = attackDamage - enemy.armor;
    145.         int myDamage = enemy.defenseDamage - armor;
    146.  
    147.         if (enemyDamage >= 1)
    148.         {
    149.             DamageIcon instance = Instantiate(damageIcon, enemy.transform.position, Quaternion.identity);
    150.             instance.Setup(enemyDamage);
    151.             enemy.health -= enemyDamage;
    152.             Debug.Log("oisdjf");
    153.             enemy.UpdateTheHealthOfKing();
    154.         }
    155.  
    156.         if (transform.tag == "Archer" && enemy.tag != "Archer")
    157.         {
    158.             if (Mathf.Abs(transform.position.x - enemy.transform.position.x) + Mathf.Abs(transform.position.y - enemy.transform.position.y) <= 1)
    159.             {
    160.                 if (myDamage >= 1)
    161.                 {
    162.                     DamageIcon instance = Instantiate(damageIcon, transform.position, Quaternion.identity);
    163.                     instance.Setup(myDamage);
    164.                     health -= myDamage;
    165.                     Debug.Log("oisdjf");
    166.                     UpdateTheHealthOfKing();
    167.                 }
    168.             }
    169.         }
    170.         else
    171.         {
    172.             if (myDamage >= 1)
    173.             {
    174.                 DamageIcon instance = Instantiate(damageIcon, transform.position, Quaternion.identity);
    175.                 instance.Setup(myDamage);
    176.                 health -= myDamage;
    177.                 Debug.Log("oisdjf");
    178.                 UpdateTheHealthOfKing();
    179.             }
    180.         }
    181.         if (enemy.health <= 0)
    182.         {
    183.             Instantiate(deathEffect, enemy.transform.position, Quaternion.identity);
    184.             Destroy(enemy.gameObject);
    185.             GetWalkableTiles();
    186.             gm.RemoveStatsPanel(enemy);
    187.             if (enemy.isKing == true)
    188.             {
    189.                 enemy.victoryPanel.SetActive(true);
    190.                 Time.timeScale = 0f;
    191.             }
    192.         }
    193.         if (health <= 0)
    194.         {
    195.             Instantiate(deathEffect, transform.position, Quaternion.identity);
    196.             gm.ResetTiles();
    197.             gm.RemoveStatsPanel(this);
    198.         }
    199.         gm.UpdateStatsPanel();
    200.         obj.GetComponent<GameMaster>().currentTime -= 10;
    201.     }
    202.  
    203.     void GetWalkableTiles()
    204.     {
    205.         if (hasMoved == true)
    206.         {
    207.             return;
    208.         }
    209.  
    210.         foreach (Tile tile in FindObjectsOfType<Tile>())
    211.         {
    212.             if (Mathf.Abs(transform.position.x - tile.transform.position.x) + Mathf.Abs(transform.position.y - tile.transform.position.y) <= tileSpeed)
    213.             {
    214.                 if (tile.IsClear() == true)
    215.                 {
    216.                     tile.Highlight();
    217.                 }
    218.             }
    219.         }
    220.     }
    221.     public void GetEnemies()
    222.     {
    223.         enemiesInRange.Clear();
    224.         foreach (Unit unit in FindObjectsOfType<Unit>())
    225.         {
    226.             if (Mathf.Abs(transform.position.x - unit.transform.position.x) + Mathf.Abs(transform.position.y - unit.transform.position.y) <= attackRange)
    227.             {
    228.                 if (unit.playerNumber != gm.playerTurn && hasAttacked == false)
    229.                 {
    230.                     enemiesInRange.Add(unit);
    231.                     unit.weaponIcon.SetActive(true);
    232.                 }
    233.             }
    234.         }
    235.     }
    236.  
    237.     public void ResetWeaponIcons()
    238.     {
    239.         foreach (Unit unit in FindObjectsOfType<Unit>())
    240.         {
    241.             unit.weaponIcon.SetActive(false);
    242.         }
    243.     }
    244.  
    245.     public void ResetUnitColors()
    246.     {
    247.         foreach (Unit unit in FindObjectsOfType<Unit>())
    248.         {
    249.             sprite.color = new Color(255, 255, 255, 255);
    250.         }
    251.     }
    252.  
    253.     public void Move(Vector2 tilePos)
    254.     {
    255.         animator.SetBool("Speed", true);
    256.         gm.ResetTiles();
    257.         StartCoroutine(StartMovement(tilePos));
    258.         obj.GetComponent<GameMaster>().currentTime -= 10;
    259.     }
    260.     IEnumerator StartMovement(Vector2 tilePos)
    261.     {
    262.         source.clip = move;
    263.         source.Play();
    264.  
    265.         while (transform.position.x != tilePos.x)
    266.         {
    267.             transform.position = Vector2.MoveTowards(transform.position, new Vector2(tilePos.x, transform.position.y), moveSpeed * Time.deltaTime);
    268.             yield return null;
    269.         }
    270.  
    271.         source.Stop();
    272.  
    273.         while (transform.position.y != tilePos.y)
    274.         {
    275.             transform.position = Vector2.MoveTowards(transform.position, new Vector2(transform.position.x, tilePos.y), moveSpeed * Time.deltaTime);
    276.             yield return null;
    277.         }
    278.         hasMoved = true;
    279.         sprite.color = new Color(1, 0, 0, 1);
    280.         ResetWeaponIcons();
    281.         GetEnemies();
    282.         gm.MoveStatsPanel(this);
    283.         animator.SetBool("Speed", false);
    284.     }
    285. }
     
  12. tobiass

    tobiass

    Joined:
    Apr 7, 2009
    Posts:
    3,068
    Are the networked objects for both players loaded with the scene? No PhotonNetwork.Instantiate for a prefab?
    I wonder, cause the isKing is checked in various places but never set on instantiation (or not here...).
    If only one player isKing, then it's no wonder that the text only updates for one user...

    Without knowing the project / scenes, it's really tricky to help, so I can only agree: Build a minimal test project.
    In doubt, you could store the health in the Custom Player Properties, too. Or make it part of the observed components and send the value along with position updates in OnPhotonSerializeView.
     
  13. sachdevaanisha

    sachdevaanisha

    Joined:
    May 15, 2022
    Posts:
    1
    Hi TomsTales.
    I am stuck with the same error as you are facing. Can you please let me know how did you finally resolve it?