This commit is contained in:
CortexCore
2024-04-16 04:15:06 +08:00
parent b673a9438d
commit 0362b2c606
183 changed files with 5695 additions and 1453 deletions

View File

@@ -0,0 +1,12 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BITKit.OpenWorld
{
public class AIChunkService : WorldChunkService<AIChunkService>
{
}
}

View File

@@ -1,18 +0,0 @@
{
"name": "BITKit.WorkdChunk",
"rootNamespace": "",
"references": [
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
"GUID:a9eec99827e569e45bfe3e5ea7494591",
"GUID:d8b63aba1907145bea998dd612889d6b"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,23 @@
{
"name": "BITKit.WorldChunk",
"rootNamespace": "",
"references": [
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
"GUID:1193c2664d97cc049a6e4c486c6bce71",
"GUID:d8b63aba1907145bea998dd612889d6b",
"GUID:d525ad6bd40672747bde77962f1c401e",
"GUID:49b49c76ee64f6b41bf28ef951cb0e50",
"GUID:e34a5702dd353724aa315fb8011f08c3",
"GUID:f51ebe6a0ceec4240a699833d6309b23",
"GUID:045a42f233e479d41adc32d02b99631e"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,35 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Pool;
namespace BITKit.OpenWorld
{
public sealed class BuildingChunkObject : MonoBehaviour
{
[SerializeField] private ChunkBehaviour chunkBehaviour;
[SerializeField] private Renderer[] interiorRenderers;
[SerializeField] private Optional<Transform> useRoot;
private void Start()
{
chunkBehaviour.OnLodChangedEvent += OnLodChanged;
if(useRoot.Allow is false)return;
var renderers = ListPool<Renderer>.Get();
renderers.AddRange(interiorRenderers);
renderers.AddRange(useRoot.Value.GetComponentsInChildren<Renderer>());
interiorRenderers = renderers.Distinct().ToArray();
renderers.Clear();
ListPool<Renderer>.Release(renderers);
}
private void OnLodChanged(int oldLod, int newLod)
{
var show = newLod is 0;
foreach (var x in interiorRenderers)
{
x.enabled = show;
}
}
}
}

View File

@@ -0,0 +1,46 @@
using System;
using System.Collections;
using System.Collections.Generic;
using BITKit.OpenWorld;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace BITKit.OpenWorld
{
public class ChunkBehaviour : ChunkObject
{
public event Action<int,int> OnLodChangedEvent;
protected override void OnLodChanged(int oldLod, int newLod)
{
base.OnLodChanged(oldLod, newLod);
OnLodChangedEvent?.Invoke(oldLod,newLod);
}
[BIT]
private void CalculateBounds()
{
if(TryGetComponent<Collider>(out var _collider))
{
var bounds1 = _collider.bounds;
size = bounds1.size;
offset = bounds1.center - transform.position;
return;
}
Bounds bounds = new();
foreach (var x in GetComponentsInChildren<Collider>())
{
bounds.Encapsulate(x.bounds);
if (x.bounds.size.sqrMagnitude > 64)
{
Debug.LogWarning($"{x.gameObject.name}:Size is too large, please check the size of the collider");
}
}
size = bounds.size;
offset = bounds.center - transform.position;
EditorUtility.SetDirty(this);
}
}
}

View File

@@ -0,0 +1,73 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Quadtree;
using UnityEngine;
namespace BITKit.OpenWorld
{
public class ChunkLodObject : MonoBehaviour, IWorldChunkObject
{
[Serializable]
public struct Data
{
public Renderer[] renderers;
}
[SerializeField]
private Data[] lodObjects;
[SerializeField,ReadOnly] private int _lod = -1;
[SerializeField,ReadOnly] private int id;
[SerializeField] private Vector3 initialSize;
private Bounds _bounds;
public Bounds GetBounds() => _bounds;
public Node<IWorldChunkObject> ParentNode { get; set; }
public void QuadTree_Root_Initialized(IQuadtreeRoot<IWorldChunkObject, Node<IWorldChunkObject>> root)
{
}
public int Id
{
get=>id;
set=>id = value;
}
public int Lod
{
get=>_lod;
set
{
if (_lod is not -1)
{
foreach (var x in lodObjects[_lod].renderers)
{
if(x)x.enabled = false;
}
}
_lod = value;
if (_lod is -1) return;
if (lodObjects.Length <= _lod) return;
foreach (var x in lodObjects[_lod].renderers)
{
if(x)x.enabled = true;
}
}
}
private void Start()
{
_bounds = new Bounds(transform.position, initialSize);
foreach (var x in lodObjects)
foreach (var xx in x.renderers)
{
xx.enabled = false;
}
UnityWorldChunkService.Singleton.Register(this);
destroyCancellationToken.Register(Dispose);
}
private void Dispose()
{
UnityWorldChunkService.Singleton.Unregister(this);
}
}
}

View File

@@ -0,0 +1,55 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net.NetworkInformation;
using Quadtree;
using UnityEngine;
namespace BITKit.OpenWorld
{
public class ChunkObject : MonoBehaviour,IWorldChunkObject
{
[SerializeField,ReadOnly] private int _lod = -1;
[SerializeField,ReadOnly] private int id;
[SerializeField] protected Vector3 size = Vector3.one;
[SerializeField] protected Vector3 offset = Vector3.one * 0.5f;
public Bounds GetBounds() => _bounds;
private Bounds _bounds;
public Node<IWorldChunkObject> ParentNode { get; set; }
public void QuadTree_Root_Initialized(IQuadtreeRoot<IWorldChunkObject, Node<IWorldChunkObject>> root)
{
}
public int Id
{
get=>id;
set=>id = value;
}
public int Lod
{
get=>_lod;
set => OnLodChanged(_lod,_lod=value);
}
protected virtual void Start()
{
_bounds= new Bounds(transform.position+transform.rotation * offset,transform.rotation * size);
UnityWorldChunkService.Singleton.Register(this);
destroyCancellationToken.Register(Dispose);
}
private void Dispose()
{
UnityWorldChunkService.Singleton.Unregister(this);
}
protected virtual void OnLodChanged(int oldLod, int newLod)
{
}
private void OnDrawGizmosSelected()
{
var bounds = GetBounds();
Gizmos.color = Color.red;
Gizmos.DrawWireCube(bounds.center, bounds.size);
}
}
}

View File

@@ -0,0 +1,67 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Quadtree;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
namespace BITKit.OpenWorld
{
/// <summary>
/// 这个应该载入后就销毁,托管给纯class
/// </summary>
public sealed class ColliderChunkObject : MonoBehaviour
{
[SerializeField] private Collider[] colliders;
[SerializeField] private ChunkBehaviour chunkBehaviour;
private void Start()
{
try
{
chunkBehaviour.OnLodChangedEvent += OnLodChanged;
if(colliders is {Length:0})colliders = GetComponentsInChildren<Collider>();
}
catch (Exception e)
{
BIT4Log.Warning<ColliderChunkObject>(gameObject.name);
BIT4Log.LogException(e);
}
}
private void OnLodChanged(int arg1, int arg2)
{
var enabledCollider = arg2 is 0;
foreach (var x in colliders)
{
try
{
x.enabled = enabledCollider;
}
catch (UnassignedReferenceException)
{
GetCollidersInChildren();
OnLodChanged(arg1, arg2);
return;
}
catch (Exception e)
{
BIT4Log.Warning<ColliderChunkObject>(gameObject.name);
BIT4Log.LogException(e);
}
}
}
[BIT]
private void GetCollidersInChildren()
{
colliders = GetComponentsInChildren<Collider>();
#if UNITY_EDITOR
EditorUtility.SetDirty(this);
#endif
}
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Quadtree;
using Quadtree.Items;
using UnityEngine;
using UnityEngine.Pool;
namespace BITKit.OpenWorld
{
public interface IWorldChunkObject:IItem<IWorldChunkObject,Node<IWorldChunkObject>>
{
int Id { get; set; }
int Lod { get; set; }
}
public interface IChunkService
{
void Register(IWorldChunkObject obj);
void Unregister(IWorldChunkObject obj);
}
}

View File

@@ -1,37 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BITKit.WorldChunk
{
public interface IWorldChunk
{
Rect GetRect();
void SetActive(bool active);
}
public abstract class WorldChunk : MonoBehaviour, IWorldChunk
{
public static void Add(IWorldChunk chunk)
{
chunks.Add(chunk);
ChunksArray = chunks.ToArray();
}
public static void Remove(IWorldChunk chunk)
{
chunks.Remove(chunk);
ChunksArray = chunks.ToArray();
}
private static readonly List<IWorldChunk> chunks=new();
public static IWorldChunk[] ChunksArray { get; private set; }= Array.Empty<IWorldChunk>();
public abstract Rect GetRect();
public abstract void SetActive(bool active);
private void OnEnable()
{
chunks.Add(this);
}
private void OnDisable()
{
chunks.Remove(this);
}
}
}

View File

@@ -1,39 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
namespace BITKit.WorldChunk
{
public class ObjectChunk : WorldChunk
{
public Vector2 size;
public Renderer[] renderers;
Vector2 posCenter
{
get
{
var pos = transform.position;
return new()
{
x = pos.x,
y = pos.z,
};
}
}
public override Rect GetRect()
{
return new()
{
position = posCenter,
size = size,
};
}
public override void SetActive(bool active)
{
renderers.ForEach(x =>
{
x.enabled = active;
});
}
}
}

View File

@@ -1,20 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BITKit.WorldChunk
{
public class TerrainChunk : WorldChunk
{
public Terrain terrain;
public override Rect GetRect()
{
Vector3 size = terrain.terrainData.size;
return new(transform.position, size);
}
public override void SetActive(bool active)
{
terrain.enabled = active;
}
}
}

View File

@@ -0,0 +1,24 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Quadtree;
using Quadtree.Items;
using UnityEngine;
using UnityEngine.Pool;
namespace BITKit.OpenWorld
{
public class UnityWorldChunkService : WorldChunkService<UnityWorldChunkService>
{
[Serializable]
public struct Unity:IChunkService
{
public void Register(IWorldChunkObject obj)=>Singleton.Register(obj);
public void Unregister(IWorldChunkObject obj)=>Singleton.Unregister(obj);
}
}
}

View File

@@ -1,42 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using NativeQuadTree;
using Unity.Mathematics;
namespace BITKit.WorldChunk
{
public class WorldChunkManager : MonoBehaviour
{
public Vector2Int playerSize;
private Rect playerRect;
private void Start()
{
var quadTree = new NativeQuadTree<int>(new AABB2D(default, new float2(1024,1024)));
}
private void FixedUpdate()
{
var cameraPos = Camera.main.transform.position;
Vector2 position = new()
{
x = cameraPos.x - playerSize.x / 2,
y = cameraPos.z - playerSize.y / 2,
};
playerRect = new(position, playerSize);
OnUpdate();
//ThreadHelper.Add(OnUpdate);
}
private void OnUpdate()
{
foreach (var x in WorldChunk.ChunksArray)
{
var isOverlay = playerRect.Overlaps(x.GetRect());
x.SetActive(isOverlay);
}
}
}
}

View File

@@ -0,0 +1,177 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using BITKit.SceneManagement;
using Cysharp.Threading.Tasks;
using Quadtree;
using UnityEngine;
using UnityEngine.Pool;
namespace BITKit.OpenWorld
{
public class WorldChunkService<T> : MonoBehaviour
where T : WorldChunkService<T>
{
public static T Singleton { get; private set; }
private readonly ConcurrentQueue<IWorldChunkObject> _registerQueue = new();
private readonly ConcurrentQueue<IWorldChunkObject> _unregisterQueue = new();
public void Register(IWorldChunkObject obj) => _registerQueue.Enqueue(obj);
public void Unregister(IWorldChunkObject obj) => _unregisterQueue.Enqueue(obj);
protected QuadtreeRoot<IWorldChunkObject, Node<IWorldChunkObject>> _quadtree;
private readonly ConcurrentDictionary<int, IWorldChunkObject> dictionary=new();
[SerializeReference, SubclassSelector] private ITicker ticker;
[SerializeReference, SubclassSelector] private ISceneService sceneService;
[SerializeField, ReadOnly] private int count;
[SerializeField, ReadOnly] private int tickTaskCount;
[SerializeField] private bool drawBounds;
[SerializeField, Range(0, 1024)] private int[] lodDistances;
[SerializeField] private Vector3 size;
private Camera _camera;
private readonly HashSet<int> cacheList = new();
private readonly Queue<(int, IWorldChunkObject)> lodQueue=new();
private bool isBusy;
protected virtual void Awake()
{
Singleton = (T) this;
}
protected virtual void Start()
{
sceneService?.RegisterLoadTaskAsync(LoadTask);
ticker.Add(OnTick);
destroyCancellationToken.Register(Dispose);
_quadtree = new QuadtreeRoot<IWorldChunkObject, Node<IWorldChunkObject>>(transform.position, size);
_camera = Camera.main;
}
private async UniTask LoadTask()
{
var frame=0;
while (count is 0 || frame++<32)
{
if (destroyCancellationToken.IsCancellationRequested) return;
await UniTask.NextFrame();
}
}
protected virtual void Dispose()
{
sceneService?.UnRegisterLoadTaskAsync(LoadTask);
ticker.Remove(OnTick);
_registerQueue.Clear();
_unregisterQueue.Clear();
}
protected virtual async void OnTick(float deltaTime)
{
if (!enabled) return;
if (isBusy) return;
isBusy = true;
while (_unregisterQueue.TryDequeue(out var obj))
{
_quadtree.Remove(obj);
dictionary.Remove(obj.Id, out _);
}
while (_registerQueue.TryDequeue(out var obj))
{
obj.Id = count++;
_quadtree.Insert(obj);
dictionary.TryAdd(obj.Id, obj);
}
var cameraPosition = _camera.transform.position;
var cycle = 0;
await UniTask.SwitchToThreadPool();
foreach (var chunkObject in _quadtree.Find(new Bounds(cameraPosition, Vector3.one * (lodDistances[^1]+lodDistances[0]))))
{
if (cycle++ > 64)
{
cycle = 0;
await UniTask.NextFrame();
if (destroyCancellationToken.IsCancellationRequested) return;
}
var distance = Vector3.Distance(cameraPosition, chunkObject.GetBounds().center);
//var distance = Vector3.Distance(cameraPosition, chunkObject.GetBounds().ClosestPoint(cameraPosition));
if (cacheList.Contains(chunkObject.Id)) continue;
cacheList.Add(chunkObject.Id);
var lod = -1;
for (var i = 0; i < lodDistances.Length; i++)
{
if (!(distance < lodDistances[i])) continue;
lod = i;
break;
}
if(chunkObject.Lod==lod)continue;
//chunkObject.Lod = lod;
lodQueue.Enqueue((lod,chunkObject));
}
// for (var index = 0; index < lodDistances.Length; index++)
// {
// var distance = lodDistances[index];
// foreach (var chunkObject in _quadtree.Find(new Bounds(cameraPosition, Vector3.one * distance)))
// {
// if (cacheList.Contains(chunkObject.Id)) continue;
// cacheList.Add(chunkObject.Id);
//
// var lod = chunkObject.Lod;
// if (lod == index) continue;
// try
// {
// chunkObject.Lod = index;
// }
// catch (Exception e)
// {
// BIT4Log.LogException(e);
// }
// }
// }
await UniTask.SwitchToMainThread();
tickTaskCount = lodQueue.Count;
if (destroyCancellationToken.IsCancellationRequested) return;
cycle = 0;
while (lodQueue.TryDequeue(out var value))
{
if (cycle++ > 8)
{
cycle = 0;
await UniTask.NextFrame();
if (destroyCancellationToken.IsCancellationRequested) return;
}
value.Item2.Lod = value.Item1;
}
isBusy = false;
cacheList.Clear();
}
private void OnDrawGizmosSelected()
{
var pos = transform.position;
Gizmos.DrawWireCube(pos,transform.rotation * size);
if(drawBounds)
_quadtree.CurrentRootNode.DrawBounds(true);
if(!_camera)return;
var cameraPosition = _camera.transform.position;
foreach (var VARIABLE in lodDistances)
{
Gizmos.DrawWireSphere(cameraPosition, VARIABLE);
}
}
}
}

View File

@@ -0,0 +1,85 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using Quadtree;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
using YooAsset;
namespace BITKit.OpenWorld
{
public class WorldTerrainBehaviour : MonoBehaviour,IWorldChunkObject
{
[SerializeReference, SubclassSelector] private IReference sceneName;
[SerializeField] private Vector3 size;
[SerializeField] private Vector3 position;
[SerializeField, ReadOnly] private int lod = -1;
private SceneHandle _sceneHandle;
[BIT]
public async void Load()
{
if (_sceneHandle is not null) return;
var stopWatcher = new System.Diagnostics.Stopwatch();
stopWatcher.Start();
_sceneHandle = YooAssets.LoadSceneAsync(sceneName.Value,LoadSceneMode.Additive,priority:8);
await _sceneHandle;
stopWatcher.Stop();
Debug.Log($"加载场景 {sceneName.Value} 耗时 {stopWatcher.ElapsedMilliseconds}ms");
}
[BIT]
public async void Unload()
{
if (_sceneHandle is null) return;
var stopWatcher = new System.Diagnostics.Stopwatch();
stopWatcher.Start();
await _sceneHandle.UnloadAsync();
_sceneHandle = null;
stopWatcher.Stop();
Debug.Log($"卸载场景 {sceneName.Value} 耗时 {stopWatcher.ElapsedMilliseconds}ms");
}
public Bounds GetBounds()=>
new Bounds(position, size);
public Node<IWorldChunkObject> ParentNode { get; set; }
public void QuadTree_Root_Initialized(IQuadtreeRoot<IWorldChunkObject, Node<IWorldChunkObject>> root)
{
}
public int Id { get; set; }
public int Lod
{
get=>lod;
set
{
lod = value;
switch (value)
{
case 0:
Load();
break;
default:
Unload();
break;
}
}
}
private void Start()
{
WorldTerrainService.Singleton.Register(this);
destroyCancellationToken.Register(() => WorldTerrainService.Singleton.Unregister(this));
}
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.red;
Gizmos.DrawWireCube(position, size);
}
}
}

View File

@@ -0,0 +1,10 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BITKit.OpenWorld
{
public class WorldTerrainService : WorldChunkService<WorldTerrainService>
{
}
}