mirror of
				https://github.com/DerTyp7/defrain-shooter-unity.git
				synced 2025-11-04 15:18:59 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			140 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			140 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
// extremely fast spatial hashing interest management based on uMMORPG GridChecker.
 | 
						|
// => 30x faster in initial tests
 | 
						|
// => scales way higher
 | 
						|
using System.Collections.Generic;
 | 
						|
using UnityEngine;
 | 
						|
 | 
						|
namespace Mirror
 | 
						|
{
 | 
						|
    public class SpatialHashingInterestManagement : InterestManagement
 | 
						|
    {
 | 
						|
        [Tooltip("The maximum range that objects will be visible at.")]
 | 
						|
        public int visRange = 30;
 | 
						|
 | 
						|
        // if we see 8 neighbors then 1 entry is visRange/3
 | 
						|
        public int resolution => visRange / 3;
 | 
						|
 | 
						|
        [Tooltip("Rebuild all every 'rebuildInterval' seconds.")]
 | 
						|
        public float rebuildInterval = 1;
 | 
						|
        double lastRebuildTime;
 | 
						|
 | 
						|
        public enum CheckMethod
 | 
						|
        {
 | 
						|
            XZ_FOR_3D,
 | 
						|
            XY_FOR_2D
 | 
						|
        }
 | 
						|
        [Tooltip("Spatial Hashing supports 3D (XZ) and 2D (XY) games.")]
 | 
						|
        public CheckMethod checkMethod = CheckMethod.XZ_FOR_3D;
 | 
						|
 | 
						|
        // debugging
 | 
						|
        public bool showSlider;
 | 
						|
 | 
						|
        // the grid
 | 
						|
        Grid2D<NetworkConnection> grid = new Grid2D<NetworkConnection>();
 | 
						|
 | 
						|
        // project 3d world position to grid position
 | 
						|
        Vector2Int ProjectToGrid(Vector3 position) =>
 | 
						|
            checkMethod == CheckMethod.XZ_FOR_3D
 | 
						|
            ? Vector2Int.RoundToInt(new Vector2(position.x, position.z) / resolution)
 | 
						|
            : Vector2Int.RoundToInt(new Vector2(position.x, position.y) / resolution);
 | 
						|
 | 
						|
        public override bool OnCheckObserver(NetworkIdentity identity, NetworkConnection newObserver)
 | 
						|
        {
 | 
						|
            // calculate projected positions
 | 
						|
            Vector2Int projected = ProjectToGrid(identity.transform.position);
 | 
						|
            Vector2Int observerProjected = ProjectToGrid(newObserver.identity.transform.position);
 | 
						|
 | 
						|
            // distance needs to be at max one of the 8 neighbors, which is
 | 
						|
            //   1 for the direct neighbors
 | 
						|
            //   1.41 for the diagonal neighbors (= sqrt(2))
 | 
						|
            // => use sqrMagnitude and '2' to avoid computations. same result.
 | 
						|
            return (projected - observerProjected).sqrMagnitude <= 2;
 | 
						|
        }
 | 
						|
 | 
						|
        public override void OnRebuildObservers(NetworkIdentity identity, HashSet<NetworkConnection> newObservers, bool initialize)
 | 
						|
        {
 | 
						|
            // add everyone in 9 neighbour grid
 | 
						|
            // -> pass observers to GetWithNeighbours directly to avoid allocations
 | 
						|
            //    and expensive .UnionWith computations.
 | 
						|
            Vector2Int current = ProjectToGrid(identity.transform.position);
 | 
						|
            grid.GetWithNeighbours(current, newObservers);
 | 
						|
        }
 | 
						|
 | 
						|
        // update everyone's position in the grid
 | 
						|
        // (internal so we can update from tests)
 | 
						|
        internal void Update()
 | 
						|
        {
 | 
						|
            // only on server
 | 
						|
            if (!NetworkServer.active) return;
 | 
						|
 | 
						|
            // NOTE: unlike Scene/MatchInterestManagement, this rebuilds ALL
 | 
						|
            //       entities every INTERVAL. consider the other approach later.
 | 
						|
 | 
						|
            // IMPORTANT: refresh grid every update!
 | 
						|
            // => newly spawned entities get observers assigned via
 | 
						|
            //    OnCheckObservers. this can happen any time and we don't want
 | 
						|
            //    them broadcast to old (moved or destroyed) connections.
 | 
						|
            // => players do move all the time. we want them to always be in the
 | 
						|
            //    correct grid position.
 | 
						|
            // => note that the actual 'rebuildall' doesn't need to happen all
 | 
						|
            //    the time.
 | 
						|
            // NOTE: consider refreshing grid only every 'interval' too. but not
 | 
						|
            //       for now. stability & correctness matter.
 | 
						|
 | 
						|
            // clear old grid results before we update everyone's position.
 | 
						|
            // (this way we get rid of destroyed connections automatically)
 | 
						|
            //
 | 
						|
            // NOTE: keeps allocated HashSets internally.
 | 
						|
            //       clearing & populating every frame works without allocations
 | 
						|
            grid.ClearNonAlloc();
 | 
						|
 | 
						|
            // put every connection into the grid at it's main player's position
 | 
						|
            // NOTE: player sees in a radius around him. NOT around his pet too.
 | 
						|
            foreach (NetworkConnectionToClient connection in NetworkServer.connections.Values)
 | 
						|
            {
 | 
						|
                // authenticated and joined world with a player?
 | 
						|
                if (connection.isAuthenticated && connection.identity != null)
 | 
						|
                {
 | 
						|
                    // calculate current grid position
 | 
						|
                    Vector2Int position = ProjectToGrid(connection.identity.transform.position);
 | 
						|
 | 
						|
                    // put into grid
 | 
						|
                    grid.Add(position, connection);
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            // rebuild all spawned entities' observers every 'interval'
 | 
						|
            // this will call OnRebuildObservers which then returns the
 | 
						|
            // observers at grid[position] for each entity.
 | 
						|
            if (NetworkTime.localTime >= lastRebuildTime + rebuildInterval)
 | 
						|
            {
 | 
						|
                RebuildAll();
 | 
						|
                lastRebuildTime = NetworkTime.localTime;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
// OnGUI allocates even if it does nothing. avoid in release.
 | 
						|
#if UNITY_EDITOR || DEVELOPMENT_BUILD
 | 
						|
        // slider from dotsnet. it's nice to play around with in the benchmark
 | 
						|
        // demo.
 | 
						|
        void OnGUI()
 | 
						|
        {
 | 
						|
            if (!showSlider) return;
 | 
						|
 | 
						|
            // only show while server is running. not on client, etc.
 | 
						|
            if (!NetworkServer.active) return;
 | 
						|
 | 
						|
            int height = 30;
 | 
						|
            int width = 250;
 | 
						|
            GUILayout.BeginArea(new Rect(Screen.width / 2 - width / 2, Screen.height - height, width, height));
 | 
						|
            GUILayout.BeginHorizontal("Box");
 | 
						|
            GUILayout.Label("Radius:");
 | 
						|
            visRange = Mathf.RoundToInt(GUILayout.HorizontalSlider(visRange, 0, 200, GUILayout.Width(150)));
 | 
						|
            GUILayout.Label(visRange.ToString());
 | 
						|
            GUILayout.EndHorizontal();
 | 
						|
            GUILayout.EndArea();
 | 
						|
        }
 | 
						|
#endif
 | 
						|
    }
 | 
						|
}
 |