Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Network Proximity Checker and major FPS drop with large GC collection

Discussion in 'Multiplayer' started by Teedo145, Sep 21, 2015.

  1. Teedo145

    Teedo145

    Joined:
    Apr 14, 2014
    Posts:
    23
    I have 450 objects that use Network Proximity Checker and the servers fps drops by 30 every time it updates. I find it strange that it seems to check every object with the proximity checker if there is a player nearby, rather than checking every player if there are any objects with the proximity checker.

    The 450 objects is mainly a test, as my game is going to have a lot more than that. Is this meant only for very small scale games or am I doing something wrong?

    I tried with 2,500 objects and the GC allocation went up to 130mb and my fps would drop to 1 from 120 every time it updated.

     
    Last edited: Sep 21, 2015
  2. Teedo145

    Teedo145

    Joined:
    Apr 14, 2014
    Posts:
    23
    Anybody?
    Is there not an option to spread it over multiple frames?
     
  3. seanr

    seanr

    Unity Technologies

    Joined:
    Sep 22, 2014
    Posts:
    669
    You could stagger creation of the objects, or give them slightly different visUpdateIntervals so that they diverge.

    The NetworkProximityCheck is not highly optimized, but is uses the public APIs of the visibility system.

    http://docs.unity3d.com/Manual/UNetVisibility.html

    You could create your own component to do visibility in a way that is optimized for your game.


    Code (CSharp):
    1. #if ENABLE_UNET
    2. using System;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. namespace UnityEngine.Networking
    7. {
    8.  
    9. [AddComponentMenu("Network/NetworkProximityChecker")]
    10. [RequireComponent(typeof(NetworkIdentity))]
    11. public class NetworkProximityChecker : NetworkBehaviour
    12. {
    13.     public enum CheckMethod
    14.     {
    15.         Physics3D,
    16.         Physics2D
    17.     };
    18.  
    19.     public int visRange = 10;
    20.     public float visUpdateInterval = 1.0f; // in seconds
    21.     public CheckMethod checkMethod = CheckMethod.Physics3D;
    22.  
    23.     public bool forceHidden = false;
    24.  
    25.     float m_VisUpdateTime;
    26.  
    27.     void Update()
    28.     {
    29.         if (!NetworkServer.active)
    30.             return;
    31.  
    32.         if (Time.time - m_VisUpdateTime > visUpdateInterval)
    33.         {
    34.             GetComponent<NetworkIdentity>().RebuildObservers(false);
    35.             m_VisUpdateTime = Time.time;
    36.         }
    37.     }
    38.  
    39.     // called when a new player enters
    40.     public override bool OnCheckObserver(NetworkConnection newObserver)
    41.     {
    42.         if (forceHidden)
    43.             return false;
    44.  
    45.         // this cant use newObserver.playerControllers[0]. must iterate to find a valid player.
    46.         GameObject player = null;
    47.         foreach (var p in newObserver.playerControllers)
    48.         {
    49.             if (p != null && p.gameObject != null)
    50.             {
    51.                 player = p.gameObject;
    52.                 break;
    53.             }
    54.         }
    55.         if (player == null)
    56.             return false;
    57.  
    58.         var pos = player.transform.position;
    59.         return (pos - transform.position).magnitude < visRange;
    60.     }
    61.  
    62.     public override bool OnRebuildObservers(HashSet<NetworkConnection> observers, bool initial)
    63.     {
    64.         if (forceHidden)
    65.         {
    66.             // ensure player can still see themself
    67.             var uv = GetComponent<NetworkIdentity>();
    68.             if (uv.connectionToClient != null)
    69.             {
    70.                 observers.Add(uv.connectionToClient);
    71.             }
    72.             return true;
    73.         }
    74.  
    75.         // find players within range
    76.         switch (checkMethod)
    77.         {
    78.             case CheckMethod.Physics3D:
    79.             {
    80.                 var hits = Physics.OverlapSphere(transform.position, visRange);
    81.                 foreach (var hit in hits)
    82.                 {
    83.                     // (if an object has a connectionToClient, it is a player)
    84.                     var uv = hit.GetComponent<NetworkIdentity>();
    85.                     if (uv != null && uv.connectionToClient != null)
    86.                     {
    87.                         observers.Add(uv.connectionToClient);
    88.                     }
    89.                 }
    90.                 return true;
    91.             }
    92.  
    93.             case CheckMethod.Physics2D:
    94.             {
    95.                 var hits = Physics2D.OverlapCircleAll(transform.position, visRange);
    96.                 foreach (var hit in hits)
    97.                 {
    98.                     // (if an object has a connectionToClient, it is a player)
    99.                     var uv = hit.GetComponent<NetworkIdentity>();
    100.                     if (uv != null && uv.connectionToClient != null)
    101.                     {
    102.                         observers.Add(uv.connectionToClient);
    103.                     }
    104.                 }
    105.                 return true;
    106.             }
    107.         }
    108.         return false;
    109.     }
    110.  
    111.     // called hiding and showing objects on the host
    112.     public override void OnSetLocalVisibility(bool vis)
    113.     {
    114.         SetVis(gameObject, vis);
    115.     }
    116.  
    117.     static void SetVis(GameObject go, bool vis)
    118.     {
    119.         foreach (var r in go.GetComponents<Renderer>())
    120.         {
    121.             r.enabled = vis;
    122.         }
    123.         for (int i=0; i < go.transform.childCount; i++)
    124.         {
    125.             var t = go.transform.GetChild(i);
    126.             SetVis(t.gameObject, vis);
    127.         }
    128.     }
    129. }
    130.  
    131. }
    132. #endif
    133.  
     
    Teedo145 likes this.
  4. Teedo145

    Teedo145

    Joined:
    Apr 14, 2014
    Posts:
    23
    Thank you very much!
    I have created a couple classes which seem to work a lot faster, I tested with thousands of objects and it was fine :)
    Here is the code if anyone else would like it.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using UnityEngine.Networking;
    4. using System.Collections.Generic;
    5.  
    6. // Attach this to objects that need their visibility updated as the player moves around
    7. [RequireComponent(typeof(NetworkIdentity))]
    8. public class NetworkVisbility : NetworkBehaviour {
    9.  
    10.     public List<NetworkConnection> playersObserving = new List<NetworkConnection>();
    11.     public NetworkIdentity networkIdentity;
    12.  
    13.     void Awake(){
    14.         networkIdentity = GetComponent<NetworkIdentity>();
    15.     }
    16.  
    17.     public override bool OnRebuildObservers(HashSet<NetworkConnection> observers, bool initial){
    18.          foreach (NetworkConnection net in playersObserving){
    19.             observers.Add(net);
    20.         }
    21.         return true;
    22.     }
    23.  
    24.     public override bool OnCheckObserver(NetworkConnection newObserver){
    25.         return false;
    26.     }
    27. }
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using UnityEngine.Networking;
    4. using System.Collections.Generic;
    5.  
    6. // Attach this to player prefab
    7. [RequireComponent(typeof(SphereCollider))]
    8. public class NetworkPlayerVisibility : NetworkBehaviour {
    9.  
    10.     [SerializeField]
    11.     private int visRadius = 50; // Radius of sphere collider
    12.     [SerializeField]
    13.     private float visUpdateInterval = 2000; // Update time in ms
    14.     private SphereCollider collider;
    15.     private List<NetworkVisbility> changedObjects = new List<NetworkVisbility>(); // Objects that have changed visibility
    16.     private float visUpdateTime;
    17.  
    18.     void Awake(){
    19.         collider = GetComponent<SphereCollider>();
    20.         collider.isTrigger = true;
    21.         collider.radius = visRadius;
    22.     }
    23.  
    24.     void Update(){
    25.         if (!NetworkServer.active)
    26.             return;
    27.        
    28.         if (Time.time - visUpdateTime > visUpdateInterval){
    29.             RebuildChangedObjects();
    30.             visUpdateTime = Time.time;
    31.         }
    32.     }
    33.  
    34.     void RebuildChangedObjects(){
    35.         foreach (NetworkVisbility net in changedObjects){
    36.             net.networkIdentity.RebuildObservers(false);
    37.         }
    38.         changedObjects.Clear();
    39.     }
    40.  
    41.     void OnTriggerEnter(Collider col){
    42.         NetworkVisbility net = col.GetComponent<NetworkVisbility>();
    43.         if (net != null && connectionToClient != null){
    44.             net.playersObserving.Add(connectionToClient);
    45.             changedObjects.Add(net);
    46.         }
    47.     }
    48.  
    49.     void OnTriggerExit(Collider col){
    50.         NetworkVisbility net = col.GetComponent<NetworkVisbility>();
    51.         if (net != null && connectionToClient != null){
    52.             net.playersObserving.Remove(connectionToClient);
    53.             changedObjects.Add(net);
    54.         }
    55.     }
    56.  
    57.     // Use these to update radius and interval in game
    58.     public void SetVisualRadius(int radius){
    59.         visRadius = radius;
    60.         collider.radius = radius;
    61.     }
    62.  
    63.     public void SetUpdateInterval(float interval){
    64.         visUpdateInterval = interval;
    65.     }
    66.  
    67. }
    68.  
     
  5. DRRosen3

    DRRosen3

    Joined:
    Jan 30, 2014
    Posts:
    683
    So @Teedo145 the first script you posted would be used for NPC objects?
     
  6. Royall

    Royall

    Joined:
    Jun 15, 2013
    Posts:
    118
    @Teedo145 Is it possible to let your script work without trigger enter/exit? Cause it interferes with my combat system wich uses a trigger box... Would like to use Physics.OverlapSphere like the original, but it lacks the option to remove a client from the playersObserving list like you do in trigger exit. Any thoughts?
     
  7. ObliviousHarmony

    ObliviousHarmony

    Joined:
    Jul 3, 2014
    Posts:
    79
    You could keep a record of the entities that are observed each time you use OverlapSphere, and drop visibility from those that are no longer in range.
     
  8. Royall

    Royall

    Joined:
    Jun 15, 2013
    Posts:
    118
    @Teedo145 I am using your altered proximity checker and I love it! Have a weird problem now tho.. It all works fine the first time a client connects and plays. But when I quit and reconnect the server trows a lot of warnings for all objects affected by the checker. Weird part is that it all still works but it just trows a lot of warnings:



    Also when I check the obeservers of a BlackPine I see this:



    Do you have any ideas??
     
  9. Royall

    Royall

    Joined:
    Jun 15, 2013
    Posts:
    118
    Anyone?

    It looks like the connection is reused or something and bugs the observer list after a reconnect..
     
  10. Royall

    Royall

    Joined:
    Jun 15, 2013
    Posts:
    118
    Any Unity staff? Do I need to bug report? Cause it looks like a bug to me... (Bottom screenshot observers)
     
  11. Royall

    Royall

    Joined:
    Jun 15, 2013
    Posts:
    118
  12. Stormy102

    Stormy102

    Joined:
    Jan 17, 2014
    Posts:
    495
    I hate to necro but this is because your connection isn't set to ready (see in connectionId: 1 isReady: false). If anyone else has this issue, make sure you are checking that the connection is ready