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.
You could create your own component to do visibility in a way that is optimized for your game.
#if ENABLE_UNET
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEngine.Networking
{
[AddComponentMenu("Network/NetworkProximityChecker")]
[RequireComponent(typeof(NetworkIdentity))]
public class NetworkProximityChecker : NetworkBehaviour
{
public enum CheckMethod
{
Physics3D,
Physics2D
};
public int visRange = 10;
public float visUpdateInterval = 1.0f; // in seconds
public CheckMethod checkMethod = CheckMethod.Physics3D;
public bool forceHidden = false;
float m_VisUpdateTime;
void Update()
{
if (!NetworkServer.active)
return;
if (Time.time - m_VisUpdateTime > visUpdateInterval)
{
GetComponent<NetworkIdentity>().RebuildObservers(false);
m_VisUpdateTime = Time.time;
}
}
// called when a new player enters
public override bool OnCheckObserver(NetworkConnection newObserver)
{
if (forceHidden)
return false;
// this cant use newObserver.playerControllers[0]. must iterate to find a valid player.
GameObject player = null;
foreach (var p in newObserver.playerControllers)
{
if (p != null && p.gameObject != null)
{
player = p.gameObject;
break;
}
}
if (player == null)
return false;
var pos = player.transform.position;
return (pos - transform.position).magnitude < visRange;
}
public override bool OnRebuildObservers(HashSet<NetworkConnection> observers, bool initial)
{
if (forceHidden)
{
// ensure player can still see themself
var uv = GetComponent<NetworkIdentity>();
if (uv.connectionToClient != null)
{
observers.Add(uv.connectionToClient);
}
return true;
}
// find players within range
switch (checkMethod)
{
case CheckMethod.Physics3D:
{
var hits = Physics.OverlapSphere(transform.position, visRange);
foreach (var hit in hits)
{
// (if an object has a connectionToClient, it is a player)
var uv = hit.GetComponent<NetworkIdentity>();
if (uv != null && uv.connectionToClient != null)
{
observers.Add(uv.connectionToClient);
}
}
return true;
}
case CheckMethod.Physics2D:
{
var hits = Physics2D.OverlapCircleAll(transform.position, visRange);
foreach (var hit in hits)
{
// (if an object has a connectionToClient, it is a player)
var uv = hit.GetComponent<NetworkIdentity>();
if (uv != null && uv.connectionToClient != null)
{
observers.Add(uv.connectionToClient);
}
}
return true;
}
}
return false;
}
// called hiding and showing objects on the host
public override void OnSetLocalVisibility(bool vis)
{
SetVis(gameObject, vis);
}
static void SetVis(GameObject go, bool vis)
{
foreach (var r in go.GetComponents<Renderer>())
{
r.enabled = vis;
}
for (int i=0; i < go.transform.childCount; i++)
{
var t = go.transform.GetChild(i);
SetVis(t.gameObject, vis);
}
}
}
}
#endif
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.
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
using System.Collections.Generic;
// Attach this to objects that need their visibility updated as the player moves around
[RequireComponent(typeof(NetworkIdentity))]
public class NetworkVisbility : NetworkBehaviour {
public List<NetworkConnection> playersObserving = new List<NetworkConnection>();
public NetworkIdentity networkIdentity;
void Awake(){
networkIdentity = GetComponent<NetworkIdentity>();
}
public override bool OnRebuildObservers(HashSet<NetworkConnection> observers, bool initial){
foreach (NetworkConnection net in playersObserving){
observers.Add(net);
}
return true;
}
public override bool OnCheckObserver(NetworkConnection newObserver){
return false;
}
}
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
using System.Collections.Generic;
// Attach this to player prefab
[RequireComponent(typeof(SphereCollider))]
public class NetworkPlayerVisibility : NetworkBehaviour {
[SerializeField]
private int visRadius = 50; // Radius of sphere collider
[SerializeField]
private float visUpdateInterval = 2000; // Update time in ms
private SphereCollider collider;
private List<NetworkVisbility> changedObjects = new List<NetworkVisbility>(); // Objects that have changed visibility
private float visUpdateTime;
void Awake(){
collider = GetComponent<SphereCollider>();
collider.isTrigger = true;
collider.radius = visRadius;
}
void Update(){
if (!NetworkServer.active)
return;
if (Time.time - visUpdateTime > visUpdateInterval){
RebuildChangedObjects();
visUpdateTime = Time.time;
}
}
void RebuildChangedObjects(){
foreach (NetworkVisbility net in changedObjects){
net.networkIdentity.RebuildObservers(false);
}
changedObjects.Clear();
}
void OnTriggerEnter(Collider col){
NetworkVisbility net = col.GetComponent<NetworkVisbility>();
if (net != null && connectionToClient != null){
net.playersObserving.Add(connectionToClient);
changedObjects.Add(net);
}
}
void OnTriggerExit(Collider col){
NetworkVisbility net = col.GetComponent<NetworkVisbility>();
if (net != null && connectionToClient != null){
net.playersObserving.Remove(connectionToClient);
changedObjects.Add(net);
}
}
// Use these to update radius and interval in game
public void SetVisualRadius(int radius){
visRadius = radius;
collider.radius = radius;
}
public void SetUpdateInterval(float interval){
visUpdateInterval = interval;
}
}
@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?
@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:
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