This commit is contained in:
CortexCore 2024-04-16 04:06:46 +08:00
parent 37e7fcea51
commit c798b224be
67 changed files with 1305 additions and 425 deletions

View File

@ -63,6 +63,11 @@ namespace BITKit
/// </summary>
ItemQuality Quality { get; }
bool CopyItemsFrom(IBasicItem item);
/// <summary>
/// 价值
/// </summary>
/// <returns></returns>
int Value=>10;
}
/// <summary>
/// 可配置化物品,通常用于配置
@ -84,6 +89,7 @@ namespace BITKit
public string AddressablePath { get; set; }
public string Description;
public ItemQuality Quality;
public int Value { get; set; }
/// <summary>
/// 本地属性
/// </summary>
@ -122,6 +128,7 @@ namespace BITKit
public bool CopyItemsFrom(IBasicItem item)
{
Value = item.Value;
Id=item.Id;
Name = item.Name;
AddressablePath = item.AddressablePath;

View File

@ -75,6 +75,21 @@ namespace BITKit
/// 已重构Items的回调
/// </summary>
event Action<IBasicItemContainer> OnRebuild;
/// <summary>
/// 是否已完成物品交换,例如false就是开始交换物品true就是已完成交换物品,可以处理物品了
/// </summary>
event Action<bool> OnRelease;
/// <summary>
/// 添加挂起句柄
/// </summary>
/// <param name="id"></param>
void AddHandle(int id);
/// <summary>
/// 移除挂起句柄
/// </summary>
/// <param name="id"></param>
void RemoveHandle(int id);
}
}

View File

@ -7,6 +7,17 @@ namespace BITKit
{
public static class MathE
{
public static int GetHash<T>(IEnumerable<T> self)
{
var hash = 0;
foreach (var x in self)
{
if (x is not null)
hash += x.GetHashCode();
}
return hash;
}
public static bool IndexInRange<T>(this IEnumerable<T> self, int index)
{
return index >= 0 && index < self.Count();

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e517c312e735e1042b911efc79deb1f6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 2f7d7f857f30f484ea163931f3bc4e0a
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,50 @@
using System.Collections;
using System.Collections.Generic;
namespace BITKit.Probability
{
/// <summary>
/// 概率属性
/// </summary>
public interface IProbabilityProperty
{
}
/// <summary>
/// 概率服务
/// </summary>
public interface IProbabilityService
{
/// <summary>
/// 根据概率属性获取值
/// </summary>
/// <param name="properties"></param>
/// <returns></returns>
object GetValueObject(params IProbabilityProperty[] properties);
/// <summary>
/// 根据概率属性获取值
/// </summary>
/// <param name="properties"></param>
/// <returns></returns>
object[] GetValuesObject(params IProbabilityProperty[] properties);
}
/// <summary>
/// 泛型概率服务
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IProbabilityService<T>:IProbabilityService
{
/// <summary>
/// 根据概率属性获取值
/// </summary>
/// <param name="properties"></param>
/// <returns></returns>
T GetValue(params IProbabilityProperty[] properties);
/// <summary>
/// 根据概率属性获取值
/// </summary>
/// <param name="properties"></param>
/// <returns></returns>
T[] GetValues(params IProbabilityProperty[] properties);
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: d6c28a6107497be44bfecb5e31b960c5
guid: 8e4f1cbf25a4f494d89fc3831dbc82ab
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -70,87 +70,84 @@ namespace BITKit
{
foreach (var x in factory)
{
AddPropertyInternal(x);
properties.Add(x);
}
}
Dictionary<string, object> properties=new();
private readonly List<object> properties=new();
public T GetProperty<T>()
{
return (T)properties[typeof(T).FullName];
return properties.OfType<T>().First();
}
public void SetProperty<T>(T value)
{
properties.Insert(typeof(T).FullName, value);
for (var i = 0; i < properties.Count; i++)
{
if (properties[i] is T)
properties[i] = value;
}
}
public bool Contains<T>()
{
return properties.ContainsKey(typeof(T).FullName);
return properties.OfType<T>().FirstOrDefault() is not null;
}
public T GetOrCreateProperty<T>()
{
return GetOrAddProperty(Activator.CreateInstance<T>);
}
public T GetOrAddProperty<T>(Func<T> addFactory)
{
if (properties.TryGetValue(typeof(T).FullName, out var x))
foreach (var obj in properties)
{
return (T)x;
}
else
{
AddPropertyInternal(x =addFactory.Invoke());
//properties.Add(typeof(T).FullName, x = addFactory.Invoke());
return (T)x;
if (obj is T t) return t;
}
T x;
properties.Add(x = addFactory.Invoke());
return x;
}
public bool TryGetProperty<T>(out T value)
{
if (properties.TryGetValue(typeof(T).FullName, out var x))
try
{
value = (T)x;
value = properties.OfType<T>().First();
return true;
}
else
catch (InvalidOperationException)
{
value=default(T);
value = default;
return false;
}
}
public bool TryRemoveProperty<T>()
{
foreach (var pair in properties.Where(x=>x.Value is T))
var removed=false;
foreach (var value in properties.OfType<T>().ToArray())
{
properties.Remove(pair.Key);
return true;
properties.Remove(value);
removed = true;
}
// if(properties.TryGetValue(typeof(T).FullName, out var x))
// {
// properties.Remove(typeof(T).Name);
// return true;
// }
return false;
return removed;
}
public bool TrySetProperty<T>(T value)
{
bool result = false;
foreach (var pair in properties.Where(x=>x.Value is T).ToArray())
var current = properties.OfType<T>().FirstOrDefault();
if (current is not null)
{
properties[pair.Key] = value;
result = true;
properties.Remove(current);
return true;
}
return result;
// if (properties.TryGetValue(typeof(T).FullName, out var x))
// {
// properties[typeof(T).FullName] = value;
// return true;
// }
// else
// {
// return false;
// }
properties.Add(value);
return false;
}
public object[] GetProperties()=>properties.Values.Distinct().ToArray();
public object[] GetProperties() => properties.ToArray();
public void Read(BinaryReader r)
{
@ -174,23 +171,8 @@ namespace BITKit
public bool CopyPropertiesFrom(IPropertable propertable)
{
ClearProperties();
foreach (var x in propertable.GetProperties())
{
AddPropertyInternal(x);
}
properties.AddRange( propertable.GetProperties());;
return true;
}
private void AddPropertyInternal(object value)
{
if (value is ICloneable cloneable)
{
value = cloneable.Clone();
}
properties.Set(value.GetType().FullName, value);
foreach (var att in value.GetType().GetCustomAttributes<CustomTypeAttribute>())
{
properties.Set(att.Type!.FullName, value);
}
}
}
}

View File

@ -2,6 +2,7 @@ namespace BITKit
{
public interface ITag
{
int Hash { get; }
string[] GetTags();
}
}

View File

@ -145,6 +145,13 @@ namespace BITKit
{
value = _directDirection.Get(key);
}
public T GetOrAddDirect<T>(int key,Func<T> factory)
{
if (_directDirection.TryGetValue(key, out var v)) return v is T t ? t : default;
var value = factory.Invoke();
_directDirection.Add(key, value);
return value;
}
public void GetDirect<T>(int key,out T value)
{
try

View File

@ -26,6 +26,7 @@ RenderTexture:
m_UseDynamicScale: 0
m_BindMS: 0
m_EnableCompatibleFormat: 1
m_EnableRandomWrite: 0
m_TextureSettings:
serializedVersion: 2
m_FilterMode: 1

View File

@ -1,28 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BITKit.Sensors;
namespace BITKit.Entities
{
public class EntityAudioObject : EntityBehavior, IAudioObject
{
float volume;
public override void OnStart()
{
UnityEntity.AddListener<AudioSO>(OnAuioSO);
}
public override void OnFixedUpdate(float deltaTime)
{
volume = Mathf.Lerp(volume, 0, deltaTime);
}
public float GetVolume()
{
return volume;
}
void OnAuioSO(AudioSO so)
{
if (so.distance > volume)
volume = so.distance;
}
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using BITKit.Mod;
using UnityEngine;
@ -15,6 +16,7 @@ namespace BITKit.Animations
public class ScriptableMotionMatchingService : MonoBehaviour,IMotionMatchingService
{
internal static ScriptableMotionMatchingService Singleton { get; private set; }
internal static readonly ConcurrentDictionary<int, IMotionMatchingObject> Cache = new();
private ObjectMatcher<string,IMotionMatchingObject> _objects;
@ -40,16 +42,23 @@ namespace BITKit.Animations
public bool TryMatch(out IMotionMatchingObject value, string[] key)
{
if (_objects.TryMatch(out value, key))
value = Cache.GetOrAdd(MathE.GetHash(key),Add);
return value is not null;
IMotionMatchingObject Add(int hash)
{
return true;
if (_objects.TryMatch(out IMotionMatchingObject x, key) is false)
{
BIT4Log.Log<ScriptableMotionMatchingService>($"找不到对应的MotionMatchingObject:{string.Join(",", key)}");
return null;
}
return x;
}
BIT4Log.Log<ScriptableMotionMatchingService>($"找不到对应的MotionMatchingObject:{string.Join(",", key)}");
return false;
}
private void Rebuild()
{
Cache.Clear();
var list = new List<ScriptableMotionMatchingObject>();
var tags = new []{nameof(IMotionMatchingObject)};
foreach (var x in YooAssets.GetAssetInfos(tags))

View File

@ -25,6 +25,7 @@ namespace BITKit
if (UnityEngine.Physics.Raycast(root.position, root.forward, out var raycastHit, distance, layerMask))
{
var collider = raycastHit.collider;
if (collider.isTrigger) return false;
switch (collider)
{
case MeshCollider meshCollider:

View File

@ -4,7 +4,7 @@ using System.Collections.Generic;
using BITKit.Core.Tuple;
using UnityEngine;
namespace BITKit.Physics
namespace BITKit
{
[Serializable]
public class JointConfigure

View File

@ -1,10 +1,9 @@
using System.Collections;
using System.Collections.Generic;
using BITKit.Events;
using UnityEditor;
using UnityEngine;
namespace BITKit.Physics
namespace BITKit
{
public class Prop_Physics : MonoBehaviour
{

View File

@ -41,6 +41,15 @@ namespace BITKit.SceneManagement
/// 场景加载完成的回调
/// </summary>
event Action<string> OnSceneLoaded;
/// <summary>
/// 注册加载任务
/// </summary>
void RegisterLoadTaskAsync(Func<UniTask> task);
/// <summary>
/// 注销加载任务
/// </summary>
/// <param name="task"></param>
void UnRegisterLoadTaskAsync(Func<UniTask> task);
/// <summary>
/// 当开始卸载场景时
/// </summary>
@ -89,6 +98,16 @@ namespace BITKit.SceneManagement
remove => _sceneServiceImplementation.OnSceneLoaded -= value;
}
public void RegisterLoadTaskAsync(Func<UniTask> task)
{
_sceneServiceImplementation.RegisterLoadTaskAsync(task);
}
public void UnRegisterLoadTaskAsync(Func<UniTask> task)
{
_sceneServiceImplementation.UnRegisterLoadTaskAsync(task);
}
public event Action<string> OnUnloadScene
{
add => _sceneServiceImplementation1.OnUnloadScene += value;

View File

@ -58,6 +58,16 @@ namespace BITKit.SceneManagement
remove => SceneService.OnSceneLoaded -= value;
}
public void RegisterLoadTaskAsync(Func<UniTask> task)
{
_sceneServiceImplementation.RegisterLoadTaskAsync(task);
}
public void UnRegisterLoadTaskAsync(Func<UniTask> task)
{
_sceneServiceImplementation.UnRegisterLoadTaskAsync(task);
}
public event Action<string> OnUnloadScene
{
add => _sceneServiceImplementation.OnUnloadScene += value;
@ -198,9 +208,15 @@ namespace BITKit.SceneManagement
OnSceneLoadProgress?.Invoke(sceneName, progress);
}
LoadedObjects.Add(sceneName, handle.SceneObject);
OnSceneLoadProgress?.Invoke(sceneName, 1);
await Task.Delay(384, cancellationToken);
foreach (var x in _onSceneLoadedAsyncList.ToArray())
{
await x.Invoke();
if (destroyCancellationToken.IsCancellationRequested) return;
}
OnSceneLoaded?.Invoke(sceneName);
stopwatchWatcher.Stop();
// if (activateOnLoad is false)
@ -252,6 +268,16 @@ namespace BITKit.SceneManagement
remove => OnSceneLoaded -= value;
}
private readonly List<Func<UniTask>> _onSceneLoadedAsyncList=new();
public void RegisterLoadTaskAsync(Func<UniTask> task)
{
_onSceneLoadedAsyncList.Add(task);
}
public void UnRegisterLoadTaskAsync(Func<UniTask> task)
{
_onSceneLoadedAsyncList.Remove(task);
}
public event Action<string> OnUnloadScene;
public event Action<string> OnSceneUnloaded;
}

View File

@ -1,49 +1,49 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.Pool;
using System.Linq;
using UnityEditor.Search;
namespace BITKit.Sensors
{
public interface IAudioObject
{
float GetVolume();
}
public class AudioSensor : Sensor
public class AudioSensor : MonoBehaviour,ISensor
{
[Header(Constant.Header.Settings)]
public float radius;
[Header(Constant.Header.InternalVariables)]
IAudioObject currentAudioObject;
Collider currentCollider;
Collider[] colliders = new Collider[32];
public override IEnumerable<Transform> Get() => detected;
public override UniTask Execute()
[SerializeField] private bool autoUpdate;
[SerializeField]private float radius;
private readonly CacheList<Transform> cache = new();
private void OnEnable()
{
var cacheList = ListPool<Transform>.Get();
for (int i = 0; i < Physics.OverlapSphereNonAlloc(transform.position, radius, colliders, detectLayer); i++)
Id = GetInstanceID();
SensorQueue.Register(Id,this);
}
private void OnDisable()
{
SensorQueue.UnRegister(Id);
}
public UniTask Execute(float delta)
{
var position = transform.position;
cache.Clear();
foreach (var x in AudioSensorService.QuadtreeRoot.Find(new Bounds(position, Vector3.one * radius)))
{
currentCollider = colliders[i];
if (IsValid(currentCollider))
{
cacheList.Add(currentCollider.transform);
}
var distance = Vector3.Distance(position, x.Position);
if(distance>radius) continue;
cache.Add(x.Transform);
}
detected = cacheList.ToArray();
ListPool<Transform>.Release(cacheList);
return UniTask.CompletedTask;
}
public override bool IsValid(Collider _collider)
{
if (ignoreColliders.Contains(_collider) is false)
if (Vector3.Distance(transform.position, _collider.transform.position) <= radius)
if (_collider.TryGetComponent<IAudioObject>(out currentAudioObject))
{
return currentAudioObject.GetVolume() >= 1;
}
return false;
}
public override float GetDistance() => radius;
public int Id { get; set; }
public IEnumerable<Transform> Get() => cache.ValueArray;
public bool IsValid(Collider _collider) => false;
public float GetDistance() => radius;
public bool AutoUpdate=>autoUpdate;
}
}

View File

@ -0,0 +1,67 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using kcp2k;
using Quadtree;
using Quadtree.Items;
using UnityEngine;
using UnityEngine.Pool;
namespace BITKit.Sensors
{
public class AudioSensorService : MonoBehaviour
{
public class AudioSensorData:IItem<AudioSensorData,Node<AudioSensorData>>
{
public int Id;
public Vector3 Position;
public float Radius;
public Transform Transform;
public Bounds Bounds;
public ITag Tag;
public Bounds GetBounds() => Bounds;
public Node<AudioSensorData> ParentNode { get; set; }
public void QuadTree_Root_Initialized(IQuadtreeRoot<AudioSensorData, Node<AudioSensorData>> root){}
}
internal static readonly QuadtreeRoot<AudioSensorData, Node<AudioSensorData>> QuadtreeRoot =
new(default, Vector3.one * 2048);
private static Pool<AudioSensorData> pool = new(()=>new(), x=>{}, 1000);
private static int count;
public static async void MakeNoise(Vector3 position,Transform transform)
{
var data = pool.Take();
data.Id = count++;
data.Position = position;
data.Transform = transform;
data.Bounds = new Bounds(position, Vector3.one *1);
data.Tag = transform.GetComponent<ITag>();
QuadtreeRoot.Insert(data);
await UniTask.Delay(3000);
if (disposed) return;
QuadtreeRoot.Remove(data);
pool.Return(data);
}
private static bool disposed;
[SerializeReference, SubclassSelector] private ITicker ticker;
private void Start()
{
disposed = false;
ticker.Add(OnTick);
destroyCancellationToken.Register(Dispose);
pool.Clear();
}
private void Dispose()
{
ticker.Remove(OnTick);
disposed = true;
QuadtreeRoot.Clear();
}
private void OnTick(float obj)
{
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: db1536b0b240b724f8741f900b2da455
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -9,7 +9,8 @@
"GUID:f51ebe6a0ceec4240a699833d6309b23",
"GUID:be17a8778dbfe454890ed8279279e153",
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
"GUID:9400d40641bab5b4a9702f65bf5c6eb5"
"GUID:9400d40641bab5b4a9702f65bf5c6eb5",
"GUID:1193c2664d97cc049a6e4c486c6bce71"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@ -17,6 +17,7 @@ namespace BITKit.Sensors
/// </summary>
public interface ISensor
{
int Id { get; }
/// <summary>
/// 自动更新
/// </summary>
@ -45,7 +46,7 @@ namespace BITKit.Sensors
/// 传感器执行检测
/// </summary>
/// <returns></returns>
UniTask Execute();
UniTask Execute(float delta);
}
[Serializable]
@ -53,6 +54,8 @@ namespace BITKit.Sensors
{
[SerializeField] private GameObject gameObject;
private ISensor _sensorImplementation => gameObject.GetComponent<ISensor>();
public int Id => _sensorImplementation.Id;
public IEnumerable<Transform> Get()
{
return _sensorImplementation.Get();
@ -67,10 +70,9 @@ namespace BITKit.Sensors
{
return _sensorImplementation.GetDistance();
}
public UniTask Execute()
public UniTask Execute(float delta)
{
return _sensorImplementation.Execute();
return _sensorImplementation.Execute(delta);
}
}
[System.Serializable]
@ -78,6 +80,8 @@ namespace BITKit.Sensors
{
[SerializeField] private MonoBehaviour monoBehaviour;
private ISensor _sensorImplementation=>monoBehaviour as ISensor;
public int Id => _sensorImplementation.Id;
public IEnumerable<Transform> Get()
{
return _sensorImplementation.Get();
@ -93,9 +97,9 @@ namespace BITKit.Sensors
return _sensorImplementation.GetDistance();
}
public UniTask Execute()
public UniTask Execute(float delta)
{
return _sensorImplementation.Execute();
return _sensorImplementation.Execute(delta);
}
}
public abstract class Sensor : MonoBehaviour, ISensor
@ -106,16 +110,15 @@ namespace BITKit.Sensors
[Header(Constant.Header.Gameobjects)]
public Collider[] ignoreColliders;
[Header(Constant.Header.InternalVariables)]
[NonSerialized]
[SerializeField,ReadOnly]
public Transform[] detected = Array.Empty<Transform>();
public abstract IEnumerable<Transform> Get();
public abstract bool IsValid(Collider _collider);
public abstract UniTask Execute();
public abstract float GetDistance();
public virtual UniTask Execute(float delta)=>UniTask.CompletedTask;
public int Id { get; private set; }
bool ISensor.AutoUpdate => autoUpdate;
protected int Id;
protected Transform Transform;
protected virtual void OnEnable()

View File

@ -0,0 +1,16 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BITKit.Sensors
{
public interface ISensorTarget
{
public int Id { get; }
Bounds Bounds { get; }
Transform Transform { get; }
void Detected(float weight,ISensor sensor,object sender);
event Action<float, ISensor, object> OnDetected;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d6b2f04f438f7bd4c812fb6441a5ca4a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -12,7 +12,7 @@ namespace BITKit.Sensors
public override IEnumerable<Transform> Get()=>sensors.SelectMany(x => x.Get());
public override bool IsValid(Collider _collider) => sensors.Any(x => x.IsValid(_collider) is false) is false;
public override float GetDistance() => sensors.Min(x => x.GetDistance());
public override UniTask Execute() => UniTask.WhenAll(sensors.Select(x => x.Execute()));
public override UniTask Execute(float delta) => UniTask.WhenAll(sensors.Select(x => x.Execute(delta)));
}
}

View File

@ -7,24 +7,25 @@ using Cysharp.Threading.Tasks;
using UnityEngine.Jobs;
using UnityEngine.Pool;
using UnityEngine.Profiling;
using Physics=UnityEngine.Physics;
namespace BITKit.Sensors
{
public class RangeSensor : Sensor
{
[Header(Constant.Header.Settings)]
public float radius;
[Header(Constant.Header.Settings)] public float radius;
public int fov;
public bool requireSight;
[Header(Constant.Header.Settings)]
public LayerMask blockLayer;
[Header(Constant.Header.InternalVariables)]
private FrameUpdate frameUpdater;
private readonly Collider[] colliders = new Collider[32];
private RaycastHit[] hits;
private bool isExecuting;
[Header(Constant.Header.Settings)] public LayerMask blockLayer;
[Header(Constant.Header.Debug)] [Header(Constant.Header.InternalVariables)]
private FrameUpdate frameUpdater;
private readonly Collider[] colliders = new Collider[32];
private RaycastHit[] hits = new RaycastHit[32];
private readonly HashSet<int> tempHashSet = new();
public override IEnumerable<Transform> Get()
{
if (!_detectedDoubleBuffer.TryGetRelease(out var newRelease)) return _detectedBuffer;
@ -33,12 +34,13 @@ namespace BITKit.Sensors
Profiler.EndSample();
return _detectedBuffer;
}
private readonly DoubleBuffer<IEnumerable<Transform>> _detectedDoubleBuffer=new();
private IEnumerable<Transform> _detectedBuffer;
public override UniTask Execute()
private readonly DoubleBuffer<IEnumerable<Transform>> _detectedDoubleBuffer = new();
private IEnumerable<Transform> _detectedBuffer=Array.Empty<Transform>();
public override UniTask Execute(float delta)
{
tempHashSet.Clear();
var length = Physics.OverlapSphereNonAlloc(Transform.position, radius, colliders, detectLayer);
Profiler.BeginSample("Filter Detected Colliders");
var _newDetected = from x in colliders.Take(length) where IsValid(x) select x.transform;
@ -54,7 +56,7 @@ namespace BITKit.Sensors
case var _ when ignoreColliders.Contains(_collider):
return false;
case var _ when fov > 0 && CheckFov(ref _collider) is false:
case var _ when requireSight && CheckSight(ref _collider) is false:
case var _ when requireSight && CheckSight(ref _collider) is false:
return false;
default:
return true;
@ -62,18 +64,21 @@ namespace BITKit.Sensors
}
public override float GetDistance() => radius;
private bool CheckFov(ref Collider _collider)
{
var _dir = _collider.transform.position - transform.position;
if (_dir.sqrMagnitude <= 0) return false;
var dir = Quaternion.LookRotation(_dir);
return Vector3.Dot(transform.forward, _dir) > 0 && fov > Quaternion.Angle(transform.rotation, dir);
var _dir = _collider.bounds.center - transform.position;
if (_dir.sqrMagnitude <= 0) return false;
var dir = Quaternion.LookRotation(_dir);
return Vector3.Dot(transform.forward, _dir) > 0 && fov > Quaternion.Angle(transform.rotation, dir);
}
private bool CheckSight(ref Collider _collider)
{
if (!requireSight) return false;
var transform1 = _collider.transform;
var position = transform1.position;
var position = _collider.bounds.center;
//是检测bounds的顶部
position.y += _collider.bounds.extents.y;
var location = new Location(Transform);
var length = Physics.RaycastNonAlloc(
location.position,
@ -82,23 +87,32 @@ namespace BITKit.Sensors
Vector3.Distance(location, position),
blockLayer
);
switch (length)
{
case 0:
Debug.DrawLine(location, position, Color.green, 1);
return true;
case 1:
if (hits[0].collider == _collider)
{
return true;
}
break;
default:
if (hits.Take(length).Any(x => ignoreColliders.Contains(x.collider) is false))
return hits[0].collider == _collider;
default:
var collider1 = _collider;
if (hits.Take(length).Any(Predicate))
{
return false;
}
break;
bool Predicate(RaycastHit x)
{
var result = ignoreColliders.Contains(x.collider) is false && x.collider != collider1;
if (result)
{
Debug.DrawLine(location, x.point, Color.red, 1);
}
return result;
}
}
return true;
}
}

View File

@ -13,7 +13,7 @@ namespace BITKit.Sensors
public float distance;
public override IEnumerable<Transform> Get() => detected;
private readonly RaycastHit[] raycasts = new RaycastHit[16];
public override UniTask Execute()
public override UniTask Execute(float delta)
{
var _transform = transform;
var forward = _transform.forward;
@ -46,7 +46,7 @@ namespace BITKit.Sensors
{
if (autoUpdate)
{
Execute().Forget();
Execute(Time.deltaTime).Forget();
}
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
@ -10,6 +11,7 @@ namespace BITKit.Sensors
public class SensorQueue : MonoBehaviour
{
internal static readonly Dictionary<int,ISensor> Sensors=new();
internal static readonly ConcurrentDictionary<int, float> LastDetectedTime = new();
private static bool IsDirty;
[SerializeField,ReadOnly] private int _position;
@ -32,10 +34,25 @@ namespace BITKit.Sensors
}
[SerializeField] private MonoBehaviour[] sensors;
private void Update()
[SerializeReference,SubclassSelector] private ITicker ticker;
private bool _isBusy;
private void Start()
{
ticker.Add(OnTick);
destroyCancellationToken.Register(Dispose);
}
private void Dispose()
{
ticker.Remove(OnTick);
}
private async void OnTick(float obj)
{
if (_isBusy) return;
if (SensorGlobalSettings.Enabled is false) return;
_isBusy = true;
if(IsDirty)
{
_position = 0;
@ -44,11 +61,25 @@ namespace BITKit.Sensors
sensors = Sensors.Values.Where(IsEnabled).OfType<MonoBehaviour>().ToArray();
}
if(Sensors.Count is 0) return;
Sensors.ElementAt(_position++).Value.Execute().Forget();
if (Sensors.Count is 0)
{
_isBusy = false;
return;
}
var current = Sensors.ElementAt(_position++).Value;
var currentUpdateTime = LastDetectedTime.GetOrAdd(current.Id,Time.time);
await current.Execute(Time.time-currentUpdateTime);
float UpdateValueFactory(int key, float old) => Time.time;
LastDetectedTime.AddOrUpdate(current.Id,Time.time,UpdateValueFactory);
if (destroyCancellationToken.IsCancellationRequested) {
_isBusy = false;
return;
}
_position %= Sensors.Count;
_isBusy = false;
}
private bool IsEnabled(ISensor sensor)
{
@ -60,4 +91,4 @@ namespace BITKit.Sensors
}
}
}
}

View File

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using BITKit.Sensors.States;
using BITKit.StateMachine;
using Cysharp.Threading.Tasks;
#if UNITY_EDITOR
using UnityEditor;
@ -16,212 +18,38 @@ using UnityEngine.UIElements;
namespace BITKit.Sensors
{
public interface ISmartTargetProperty{}
public interface ISmartTargetState:IState{}
/// <summary>
/// 智能目标传感器,根据条件,智能选择目标
/// </summary>
public class SmartTargetSensor :MonoBehaviour,ISensor,IAction
public class SmartTargetSensor :StateBasedMonoBehaviour<ISmartTargetState>,ISensor
{
/// <summary>
/// 自动更新
/// </summary>
[Header(Constant.Header.Settings)]
[SerializeField] private bool autoUpdate;
[SerializeField] private Optional<string[]> ignoreTags;
[SerializeField] private Optional<IntervalUpdate> optionalRetargetInterval;
/// <summary>
/// 主传感器
/// </summary>
[Header(nameof(Sensor))]
[SerializeField,SerializeReference,SubclassSelector] private ISensor sensor;
[Header(Constant.Header.Debug)]
[SerializeReference, ReadOnly] private int updateCount;
public IEnumerable<Transform> Get() =>CurrentTarget is not null ? new[] { CurrentTarget }:Enumerable.Empty<Transform>();
bool ISensor.AutoUpdate => autoUpdate;
internal StringBuilder reportBuilder;
internal DoubleBuffer<string> report=new();
public bool IsValid(Collider _collider)
{
if (!_collider) return false;
if (ignoreTags.Allow)
{
if (_collider.TryGetComponent<ITag>(out var iTags))
{
var tags = iTags.GetTags();
foreach (var x in ignoreTags.Value)
{
if (tags.Contains(x))
{
return false;
}
}
}
}
return true;
}
public float GetDistance() => sensor.GetDistance();
public Transform CurrentTarget { get; private set; }
private IEnumerable<Transform> detected = ArraySegment<Transform>.Empty;
private Vector3 _currentPosition;
private int Id;
private CancellationTokenSource timeoutCancellationTokenSource=new();
[SerializeField] private float radius;
[SerializeField] private RangeSensor rangeSensor;
[SerializeField] private AudioSensor audioSensor;
public int Id { get; set; }
private readonly CacheList<Transform> _detected=new();
private void OnEnable()
{
SensorQueue.Register(Id=GetInstanceID(),this);
Id = GetInstanceID();
SensorQueue.Register(Id,this);
}
private void Start()
{
TransitionState<Idle>();
}
private void OnDisable()
{
SensorQueue.UnRegister(Id);
}
public async UniTask Execute()
public IEnumerable<Transform> Get() => _detected.ValueArray;
public bool IsValid(Collider _collider) => false;
public float GetDistance() => radius;
public UniTask Execute(float delta)
{
try
{
_currentPosition = transform.position;
updateCount++;
timeoutCancellationTokenSource?.Cancel();
timeoutCancellationTokenSource = new CancellationTokenSource();
await sensor.Execute();
reportBuilder?.AppendLine($"-----BEGIN------{updateCount}");
Profiler.BeginSample("Release Detected Buffer");
var newDetected = sensor.Get();
if (reportBuilder is not null)
{
reportBuilder.AppendLine($"Detected:{newDetected.Count()}");
foreach (var x in newDetected)
{
reportBuilder.AppendLine(x.name);
}
reportBuilder.AppendLine();
}
Profiler.EndSample();
// ReSharper disable once PossibleMultipleEnumeration
if (newDetected.Contains(CurrentTarget))
{
if (optionalRetargetInterval.Allow && optionalRetargetInterval.Value.AllowUpdate)
{
reportBuilder?.AppendLine("Retarget Interval Catch,Search New Target");
}
else
{
reportBuilder?.AppendLine("Current Target Detected,Keep Target");
return;
}
}
Profiler.BeginSample("Filter Detected");
foreach (var x in newDetected.OrderBy(KeySelector))
{
if(IsValid(x.GetComponent<Collider>()) is false)continue;
CurrentTarget = x;
reportBuilder?.AppendLine($"New Target:{x.name},Is oder by distance");
break;
}
Profiler.EndSample();
reportBuilder?.AppendLine($"-----Complete------{updateCount}");
if(reportBuilder is not null)
{
report.Release(reportBuilder.ToString());
reportBuilder.Clear();
}
else
{
report.Clear();
}
}
catch (Exception e)
{
BIT4Log.LogException(e);
}
}
private float KeySelector(Transform x)
{
var distance = Vector3.Distance(_currentPosition, x.position);
reportBuilder?.AppendLine($"Distance:{distance}@{x.name}");
return distance;
}
void IAction.Execute()
{
Execute().Forget();
}
#if UNITY_EDITOR
private void OnDrawGizmosSelected()
{
try
{
if (!CurrentTarget) return;
Gizmos.DrawLine(transform.position,CurrentTarget.position);
}
catch (UnassignedReferenceException)
{
}
}
#endif
}
#if UNITY_EDITOR
[CustomEditor(typeof(SmartTargetSensor))]
public class SmartTargetSensorInspector:BITInspector<SmartTargetSensor>
{
private ObjectField _objectField;
private Label _reportLabel;
public override VisualElement CreateInspectorGUI()
{
FillDefaultInspector();
CreateSubTitle("Editor Debug Field");
_objectField = root.Create<ObjectField>();
_objectField.objectType = typeof(Transform);
_objectField.SetEnabled(false);
_reportLabel = root.Create<Label>();
_reportLabel.text = "Waiting agent report";
return root;
}
protected override void OnEnabled()
{
base.OnEnabled();
agent.reportBuilder = new();
}
protected override void OnDisabled()
{
base.OnDisabled();
agent.reportBuilder = null;
}
protected override void OnUpdate()
{
if (_objectField is not null)
{
_objectField.value = agent.CurrentTarget;
}
if (agent.reportBuilder is not null && _reportLabel is not null && agent.report.TryGetRelease(out var value))
{
_reportLabel.text = value;
}
CurrentState?.OnStateUpdate(delta);
return UniTask.CompletedTask;
}
}
#endif
}

View File

@ -0,0 +1,30 @@
using System;
using System.Collections;
using System.Collections.Generic;
using BITKit.StateMachine;
using UnityEngine;
namespace BITKit.Sensors.States
{
public abstract class SmartTargetSensorStates : ISmartTargetState
{
public bool Enabled { get; set; }
public virtual void Initialize()
{
}
public virtual void OnStateEntry(IState old)
{
}
public virtual void OnStateUpdate(float deltaTime)
{
}
public virtual void OnStateExit(IState old, IState newState)
{
}
}
[Serializable]
public sealed class Idle : SmartTargetSensorStates
{
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d55e592432e5ca648b8b9eddee224932
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -61,6 +61,8 @@ namespace BITKit.Sensors
onLost.Invoke(_collider);
}
}
public int Id => _id is 0 ? _id = GetInstanceID() : _id;
private int _id;
public IEnumerable<Transform> Get()
{
@ -89,13 +91,11 @@ namespace BITKit.Sensors
if (ignores.Contains(detectedObject)) return false;
return !detectedLayer.Allow || detectedLayer.Value.Includes(_collider.gameObject.layer);
}
public float GetDistance()
{
throw new NotImplementedException();
}
public UniTask Execute()=>
public UniTask Execute(float delta = 0)=>
UniTask.CompletedTask;
private void Update()

View File

@ -13,7 +13,8 @@ namespace BITKit
[SerializeField] private string[] tags;
[Tooltip("Disable when tags is not empty")]
[SerializeReference,SubclassSelector] private IReference[] reference;
public int Hash => _id is 0 ? _id = MathE.GetHash(GetTags()) : _id;
private int _id;
public string[] GetTags() => CacheTags ??= reference?.Length > 0 ? reference.Select(x => x.Value).ToArray() : tags;
private string[] CacheTags;
public void SetTags(IReference[] newReference)

View File

@ -55,7 +55,7 @@ namespace BITKit
{
await UniTask.SwitchToMainThread();
#if UNITY_EDITOR
if (EditorApplication.isPlaying is false)
if (EditorApplication.isPlaying is false || EditorApplication.isPaused)
{
return;
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 67732f32069d3bb4ca436ef8de5710e2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,24 @@
{
"name": "BITKit.UX.Chart.Runtime",
"rootNamespace": "",
"references": [
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
"GUID:6ef4ed8ff60a7aa4bb60a8030e6f4008",
"GUID:d525ad6bd40672747bde77962f1c401e",
"GUID:49b49c76ee64f6b41bf28ef951cb0e50",
"GUID:d8b63aba1907145bea998dd612889d6b",
"GUID:6de01b04fa4e14662b03fa46366da151",
"GUID:f19bbd83e3c264a5680926bf75d7e494"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [
"_SkiaSharp"
],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 994a3fb33a5627740b0712e7c483cc1f
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,202 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Newtonsoft.Json;
using SkiaSharp;
using UnityEngine;
using UnityEngine.UIElements;
namespace BITKit.UX
{
public class SkiaChart : VisualElement
{
public new class UxmlTraits : VisualElement.UxmlTraits
{
private readonly UxmlStringAttributeDescription m_JsonAttribute = new ()
{
name = "Json"
};
private readonly UxmlBoolAttributeDescription m_allowWarningsAttribute = new ()
{
name = "allowWarnings",
defaultValue = false
};
public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
{
base.Init(ve, bag, cc);
var x = (SkiaChart)ve;
x.Json = m_JsonAttribute.GetValueFromBag(bag, cc);
x.AllowWarning = m_allowWarningsAttribute.GetValueFromBag(bag, cc);
}
}
public new class UxmlFactory : UxmlFactory<SkiaChart, UxmlTraits> { }
public SkiaChart()
{
RegisterCallback<GeometryChangedEvent>(x =>
{
if (visible)
{
Rebuild();
}
}); RegisterCallback<CustomStyleResolvedEvent>(OnCustomStyleResolved);
style.flexDirection = FlexDirection.Row;
_dataContainer = this.Create<VisualElement>();
_chartContainer = this.Create<VisualElement>();
_chartContainer.style.flexGrow = 1;
_dataContainer.style.justifyContent = Justify.SpaceBetween;
_dataContainer.name = "Data";
_chartContainer.name = "Chart";
}
private void OnCustomStyleResolved(CustomStyleResolvedEvent evt)
{
if(evt.customStyle.TryGetValue(new CustomStyleProperty<Color>("--chart-secondary-color"), out var helpLineColor))
{
_secondaryColor = helpLineColor;
}
Rebuild();
}
public string Json
{
get => _json;
set
{
_json = value;
Rebuild();
}
}
private string _json;
public bool AllowWarning { get; set; }
public Color SecondaryColor
{
get => _secondaryColor;
set
{
_secondaryColor = value;
Rebuild();
}
}
private Color _secondaryColor;
private readonly VisualElement _dataContainer;
private readonly VisualElement _chartContainer;
private void Rebuild()
{
var data = Array.Empty<float>();
try
{
data = JsonConvert.DeserializeObject<float[]>(Json);
}
catch (Exception e)
{
if (AllowWarning)
Debug.LogException(e);
return;
}
if(float.IsNaN(layout.width) || float.IsNaN(layout.height))
return;
if (data is null or { Length: 0 }) return;
_dataContainer.Clear();
_chartContainer.Clear();
var max = data.Max();
var min = data.Min();
//假如min是x,max是y,将中间的差用data.Length划分
_dataContainer.style.flexDirection = FlexDirection.ColumnReverse;
for (var i = 0; i < data.Length; i++)
{
var label = _dataContainer.Create<Label>();
var value = (max - min) / (data.Length - 1) * i + min;
label.text = value.ToString();
}
if(_chartContainer.layout.width is float.NaN or <10 || _chartContainer.layout.height is float.NaN or <10)
return;
var info = new SKImageInfo(
width: (int)_chartContainer.layout.width,
height: (int)_chartContainer.layout.height,
colorType: SKColorType.Rgba8888,
alphaType: SKAlphaType.Premul
);
using var surface = SKSurface.Create(info);
using var canvas = surface.Canvas;
using var linePaint = new SKPaint();
linePaint.Color = resolvedStyle.color.ToSKColor();
linePaint.StrokeWidth = resolvedStyle.unityTextOutlineWidth;
linePaint.IsAntialias = true;
linePaint.Style = SKPaintStyle.Stroke;
using var helpLinePaint = new SKPaint();
helpLinePaint.Color = resolvedStyle.unityTextOutlineColor.ToSKColor();
helpLinePaint.StrokeWidth = 1;
helpLinePaint.Style = SKPaintStyle.Stroke;
using var fillPaint = new SKPaint();
fillPaint.Color = new SKColor(200, 200, 200, 200);
fillPaint.Style=SKPaintStyle.Fill;
DoubleBuffer<SKPoint> buffer = new();
var path = new SKPath { FillType = SKPathFillType.EvenOdd };
path.MoveTo(0,0);
path.LineTo(0,0);
for (var i = 0; i < data.Length; i++)
{
var value = data[i];
var posX = (float)info.Width / (data.Length - 1) * (i);
var d = max - min;
var p = (value - min) / d;
var poxY = info.Height * p;
var currentPoint = new SKPoint(posX, poxY);
if (buffer.TryGetRelease(out var previousPoint))
{
canvas.DrawLine(previousPoint, currentPoint, linePaint);
}
var label = _chartContainer.Create<Label>();
label.text = value.ToString(CultureInfo.InvariantCulture);
label.style.position = Position.Absolute;
label.style.left = posX;//-label.layout.width/2;
label.style.bottom = poxY;//- label.layout.height/2;
canvas.DrawLine(posX, 0, posX, poxY, helpLinePaint);
path.LineTo(posX,poxY);
buffer.Release(currentPoint);
}
path.LineTo(info.Width,0);
path.Close();
//canvas.DrawPath(path,filePaint);
var texture = info.ToTexture2D(surface);
_chartContainer.style.backgroundImage = texture;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 00511c331928b714d919870827a5c326
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,35 @@
using System.Collections;
using System.Collections.Generic;
using SkiaSharp;
using UnityEngine;
namespace BITKit.UX
{
public static class SkiaExtensions
{
public static Texture2D ToTexture2D(this SKImageInfo info,SKSurface surface)
{
// Okay, we're finished drawing. Now we create a Unity texture.
TextureFormat format = (info.ColorType == SKColorType.Rgba8888) ? TextureFormat.RGBA32 : TextureFormat.BGRA32;
var texture = new Texture2D(info.Width, info.Height, format, false, true);
texture.wrapMode = TextureWrapMode.Clamp;
// Pull a Skia image object out of the canvas...
var pixmap = surface.PeekPixels();
// Copy it to the Unity texture...
texture.LoadRawTextureData(pixmap.GetPixels(), pixmap.RowBytes * pixmap.Height);
texture.Apply(false, true);
// And drop it into the RawImage object.
return texture;
}
public static SKColor ToSKColor(this Color color,byte? alpha=null)
{
return new SKColor((byte)(color.r * 255), (byte)(color.g * 255), (byte)(color.b * 255), alpha??(byte)(color.a * 255));
}
public static SKColor ToSKColor(this Color32 color)
{
return new SKColor(color.r, color.g, color.b, color.a);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c23b988a0ca3904468edef1bd026f977
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 614549820bd3c514eaf369e4895d1ef0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,25 @@
{
"name": "BITKit.UX.Chart.Tests",
"rootNamespace": "",
"references": [
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
"GUID:6ef4ed8ff60a7aa4bb60a8030e6f4008",
"GUID:d525ad6bd40672747bde77962f1c401e",
"GUID:49b49c76ee64f6b41bf28ef951cb0e50",
"GUID:d8b63aba1907145bea998dd612889d6b",
"GUID:6de01b04fa4e14662b03fa46366da151",
"GUID:f19bbd83e3c264a5680926bf75d7e494",
"GUID:994a3fb33a5627740b0712e7c483cc1f"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [
"_SkiaSharp"
],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 8acc186ca11c9df42b8ab94c1e952e73
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,99 @@
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using SkiaSharp;
using UnityEngine;
namespace BITKit.UX
{
public class SKPaintComponent : MonoBehaviour
{
[SerializeField] private Texture2D texture;
[SerializeField] private float[] data;
[BIT]
private void Draw()
{
var info = new SKImageInfo(
width: 384,
height: 128,
colorType: SKColorType.Rgba8888,
alphaType: SKAlphaType.Premul
);
using var surface = SKSurface.Create(info);
using var canvas = surface.Canvas;
//canvas.Clear(new Color32(31,31,31,255).ToSKColor());
using var linePaint = new SKPaint();
linePaint.Color = new SKColor(255, 0, 0, 255);
linePaint.StrokeWidth = 8;
linePaint.IsAntialias = true;
linePaint.Style = SKPaintStyle.Stroke;
using var helpLinePaint = new SKPaint();
helpLinePaint.Color = new SKColor(200, 200, 200, 200);
helpLinePaint.StrokeWidth = 4;
helpLinePaint.Style = SKPaintStyle.Stroke;
using var textPaint = new SKPaint();
textPaint.TextAlign = SKTextAlign.Center;
textPaint.TextSize = 14;
textPaint.ColorF = new SKColor(0, 255, 0, 255);
using var filePaint = new SKPaint();
filePaint.Color = new SKColor(200, 200, 200, 200);
filePaint.Style=SKPaintStyle.Fill;
var min = data.Min();
var max = data.Max();
DoubleBuffer<SKPoint> buffer = new();
var path = new SKPath { FillType = SKPathFillType.EvenOdd };
path.MoveTo(0,0);
path.LineTo(0,0);
for (var i = 0; i < data.Length; i++)
{
var value = data[i];
var posX = (float)info.Width / (data.Length - 1) * (i);
var d = max - min;
var p = (value - min) / d;
var poxY = info.Height * p;
var currentPoint = new SKPoint(posX, poxY);
if (buffer.TryGetRelease(out var previousPoint))
{
canvas.DrawLine(previousPoint, currentPoint, linePaint);
}
canvas.DrawText(
value.ToString()
, currentPoint
, new SKFont(
SKTypeface.FromFile(@"D:\Iris\Documents\GitHub\iFactory-YL106.Unity\Assets\BITKit\Unity\Art\Fonts\TTF\Roboto\Roboto-Regular.ttf")
), textPaint);
canvas.DrawLine(posX, 0, posX, poxY, helpLinePaint);
path.LineTo(posX,poxY);
buffer.Release(currentPoint);
}
path.LineTo(info.Width,0);
path.Close();
//canvas.DrawPath(path,filePaint);
texture = info.ToTexture2D(surface);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4ce3dedf1c2068047a00040de957cb33
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -30,7 +30,7 @@ namespace BITKit.UX
container = this.visualElement.Q(UXConstant.ContextContainer);
toggle = this.visualElement.Q<Toggle>(UXConstant.Toggle);
}
public T Get<T>(int index) where T : VisualElement => visualElement.Q<T>($"{typeof(T).Name}--{index}");
public T Get<T>(int index = 0) where T : VisualElement => visualElement.Q<T>($"{typeof(T).Name}--{index}");
public void SetProcess(float process)
{
var radialProgress = visualElement.Q<RadialProgress>();

View File

@ -69,7 +69,7 @@ namespace BITKit.IData
if(attribute != null)
{
Name = attribute.Name;
VisualElement.GetType().GetProperty("label",ReflectionHelper.Flags)!.SetValue(VisualElement, Name);
VisualElement.GetType().GetProperty("label",ReflectionHelper.Flags)?.SetValue(VisualElement, Name);
}
if (fieldInfo.GetCustomAttribute<ReadOnlyAttribute>() is not null)
{
@ -111,11 +111,22 @@ namespace BITKit.IData
}
public sealed class UXStringBinder:UXDataBinder<string>
{
protected override VisualElement OnCreateUI() => new TextField()
protected override VisualElement OnCreateUI()
{
isDelayed = true,
multiline = true
};
if (this.fieldInfo.GetCustomAttribute<ReadOnlyAttribute>() is not null)
{
return new Label();
}
else
{
return new TextField()
{
isDelayed = true,
multiline = true
};
}
}
}
public sealed class UXFloat3Binder:UXDataBinder<float3>
{

View File

@ -0,0 +1,84 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
namespace BITKit.UX
{
public class ProgressBlock : VisualElement
{
public new class UxmlTraits : VisualElement.UxmlTraits
{
private readonly UxmlIntAttributeDescription m_ValueAttribute = new ()
{
name = "Value",defaultValue = 50
};
private readonly UxmlIntAttributeDescription m_SeparateAttribute = new ()
{
name = "Separate",defaultValue = 5
};
public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
{
base.Init(ve, bag, cc);
var x = (ProgressBlock)ve;
x.Value = m_ValueAttribute.GetValueFromBag(bag, cc);
x.Separate = m_SeparateAttribute.GetValueFromBag(bag, cc);
}
}
public ProgressBlock() : base()
{
}
public new class UxmlFactory : UxmlFactory<ProgressBlock, UxmlTraits> { }
/// <summary>
/// 值,默认50,范围0-100
/// </summary>
public int Value
{
get => value;
set
{
this.value = value;
Rebuild();
}
}
private int value;
/// <summary>
/// 分割线,默认5
/// </summary>
public int Separate
{
get => separate;
set
{
this.separate = value;
Rebuild();
}
}
private int separate;
private void Rebuild()
{
Clear();
//if value is 58
for (var i = 1; i <= Separate; i++)
{
var block = this.Create<VisualElement>();
block.style.flexGrow = 1;
if (i * 10 < Value)
{
block.style.opacity = 1;
}else if ((i + 1) * 10 > value)
{
block.style.opacity = 0.5f;
}
else
{
block.style.opacity = 0;
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ca822b68d3588fd48abf69d987af2607
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -51,7 +51,12 @@ namespace BITKit.UX
)
{
var bindPathAtt = fieldInfo.GetCustomAttribute<UXBindPathAttribute>();
var ve = document.rootVisualElement.Q(bindPathAtt.Path);
VisualElement ve = document.rootVisualElement;
foreach (var path in bindPathAtt.Path.Split("."))
{
ve = ve.Q(path);
}
//ve = document.rootVisualElement.Q(bindPathAtt.Path);
if(bindPathAtt.CanBeNull is false && ve is null)
BIT4Log.LogException(new NullReferenceException($"未找到{bindPathAtt.Path}"));
fieldInfo.SetValue(self,ve);

View File

@ -41,7 +41,7 @@ namespace BITKit
{
var obj = self.serializedObject.targetObject;
var type = obj.GetType();
var field = type.GetField(self.propertyPath, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var field = type.GetField(self.propertyPath, ReflectionHelper.Flags);
if (field is null)
{
throw new NullReferenceException($"Field {self.propertyPath} is null");
@ -51,7 +51,6 @@ namespace BITKit
public static void FillDefaultInspector(VisualElement container, SerializedObject serializedObject, bool hideScript)
{
container.Clear();
if (serializedObject.targetObject is null)
{
var label = container.Create<Label>();
@ -70,7 +69,7 @@ namespace BITKit
var type = serializedObject.targetObject.GetType();
var fieldInfo = serializedObject.targetObject.GetType().GetField(property.propertyPath, ReflectionHelper.Flags);
if (fieldInfo is not null && Attribute.IsDefined(fieldInfo, typeof(ReadOnlyAttribute),true))
if (fieldInfo is not null && type == typeof(string) && Attribute.IsDefined(fieldInfo, typeof(ReadOnlyAttribute),true))
{
var attribute = fieldInfo.GetCustomAttribute<ReadOnlyAttribute>();
var _container = container.Create<VisualElement>();
@ -92,16 +91,60 @@ namespace BITKit
{
//var label = container.Create<Label>();
//label.text =$"propertyPath:{property.propertyPath} fieldInfo:{fieldInfo} type:{type} fieldInfo:{fieldInfo}";
var _container = container;
var field = new PropertyField(property)
{
name = "PropertyField:" + property.propertyPath
};
if (fieldInfo is not null && Attribute.IsDefined(fieldInfo, typeof(ReadOnlyAttribute), true))
{
field.SetEnabled(false);
field.style.opacity = 1;
}
if (property.propertyPath == "m_Script" && serializedObject.targetObject != null)
{
field.SetEnabled(false);
}
// if (fieldInfo?.FieldType == typeof(Texture2D) && fieldInfo.GetValue(serializedObject) is Texture2D texture && texture)
// {
// _container = container.Create<VisualElement>();
//
// var foldout = _container.Create<Foldout>();
// foldout.text = property.displayName;
//
// var icon = foldout.Create<VisualElement>();
// var slider = foldout.Create<Slider>();
//
// var width = 128;
// var m = texture.height /width;
//
// icon.style.width = width;
// icon.style.height = width*m;
// icon.style.backgroundImage = texture;
//
// field.style.flexGrow = 1;
//
// _container.style.backgroundColor =new StyleColor(new Color32(31,31,31,255)) ;
// _container.style.borderBottomLeftRadius = 5;
// _container.style.borderBottomRightRadius = 5;
// _container.style.borderTopLeftRadius = 5;
// _container.style.borderTopRightRadius = 5;
//
// slider.label = "Scale";
// slider.lowValue = 1;
// slider.highValue = 2;
// slider.RegisterValueChangedCallback(x =>
// {
// icon.style.width = width * x.newValue;
// icon.style.height = width*m * x.newValue;
// });
// }
container.Add(field);
_container.Add(field);
}
// try
// {
@ -123,7 +166,10 @@ namespace BITKit
{
if (method.GetCustomAttribute<BITAttribute>() is null) continue;
if (method.GetParameters().Length is not 0) continue;
var button = new Button(() => method.Invoke(serializedObject.targetObject, null))
var button = new Button(() =>
{
method.Invoke(serializedObject.targetObject, null);
})
{
text = method.Name
};

View File

@ -1,4 +1,5 @@
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -20,11 +21,13 @@ namespace BITKit
private static VFXService sinleton;
public VFX[] vfxs;
private readonly Dictionary<string, UnityPool<Transform>> pools = new();
private readonly ConcurrentDictionary<int, Transform> cache = new();
[SerializeField] private bool debug;
[SerializeField] private Optional<int> defaultCapacity;
private void Awake()
{
cache.Clear();
sinleton = this;
DI.Register(this);
}
@ -68,16 +71,22 @@ namespace BITKit
public bool TryMatch(out Transform value, params string[] key)
{
foreach (var vfx in vfxs)
value=cache.GetOrAdd(MathE.GetHash(key),Add);
return value is not null;
Transform Add(int arg)
{
if (vfx.IsMatch(key))
foreach (var vfx in vfxs)
{
value = vfx.prefab;
return true;
if (vfx.IsMatch(key))
{
var x = vfx.prefab;
return x;
}
}
return null;
}
value = null;
return false;
}
}
}

View File

@ -59,6 +59,7 @@ namespace BITKit.Vehicles
private bool isBraking;
private readonly ValidHandle highSpeedHandle = new();
private IUnityEntity _unityEntity;
public Rigidbody Rigidbody => rigidbody;
[Inject(true)]
private IHealth _health;

View File

@ -8,7 +8,8 @@
"GUID:d525ad6bd40672747bde77962f1c401e",
"GUID:49b49c76ee64f6b41bf28ef951cb0e50",
"GUID:e34a5702dd353724aa315fb8011f08c3",
"GUID:f51ebe6a0ceec4240a699833d6309b23"
"GUID:f51ebe6a0ceec4240a699833d6309b23",
"GUID:045a42f233e479d41adc32d02b99631e"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@ -13,7 +13,8 @@ namespace BITKit.OpenWorld
[SerializeField,ReadOnly] private int id;
[SerializeField] protected Vector3 size = Vector3.one;
[SerializeField] protected Vector3 offset = Vector3.one * 0.5f;
public Bounds GetBounds()=>new Bounds(transform.position+transform.rotation * offset,transform.rotation * size);
public Bounds GetBounds() => _bounds;
private Bounds _bounds;
public Node<IWorldChunkObject> ParentNode { get; set; }
public void QuadTree_Root_Initialized(IQuadtreeRoot<IWorldChunkObject, Node<IWorldChunkObject>> root)
{
@ -30,6 +31,7 @@ namespace BITKit.OpenWorld
}
protected virtual void Start()
{
_bounds= new Bounds(transform.position+transform.rotation * offset,transform.rotation * size);
UnityWorldChunkService.Singleton.Register(this);
destroyCancellationToken.Register(Dispose);
}

View File

@ -18,8 +18,17 @@ namespace BITKit.OpenWorld
[SerializeField] private ChunkBehaviour chunkBehaviour;
private void Start()
{
chunkBehaviour.OnLodChangedEvent += OnLodChanged;
if(colliders is {Length:0})colliders = GetComponentsInChildren<Collider>();
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)
{
@ -30,22 +39,28 @@ namespace BITKit.OpenWorld
{
x.enabled = enabledCollider;
}
catch (UnassignedReferenceException)
{
GetCollidersInChildren();
OnLodChanged(arg1, arg2);
return;
}
catch (Exception e)
{
BIT4Log.Warning<ColliderChunkObject>(gameObject.name);
BIT4Log.LogException(e);
}
}
}
#if UNITY_EDITOR
[BIT]
private void GetCollidersInChildren()
{
colliders = GetComponentsInChildren<Collider>();
#if UNITY_EDITOR
EditorUtility.SetDirty(this);
#endif
}
#endif
}
}

View File

@ -2,8 +2,11 @@ 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
@ -21,13 +24,16 @@ namespace BITKit.OpenWorld
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;
@ -35,20 +41,35 @@ namespace BITKit.OpenWorld
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 void Dispose()
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 void OnTick(float deltaTime)
protected virtual async void OnTick(float deltaTime)
{
if (!enabled) return;
if (isBusy) return;
isBusy = true;
while (_unregisterQueue.TryDequeue(out var obj))
{
_quadtree.Remove(obj);
@ -63,28 +84,76 @@ namespace BITKit.OpenWorld
}
var cameraPosition = _camera.transform.position;
var cycle = 0;
for (var index = 0; index < lodDistances.Length; index++)
await UniTask.SwitchToThreadPool();
foreach (var chunkObject in _quadtree.Find(new Bounds(cameraPosition, Vector3.one * (lodDistances[^1]+lodDistances[0]))))
{
var distance = lodDistances[index];
foreach (var chunkObject in _quadtree.Find(new Bounds(cameraPosition, Vector3.one * distance)))
if (cycle++ > 64)
{
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);
}
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()

View File

@ -25,7 +25,7 @@ namespace BITKit.OpenWorld
if (_sceneHandle is not null) return;
var stopWatcher = new System.Diagnostics.Stopwatch();
stopWatcher.Start();
_sceneHandle = YooAssets.LoadSceneAsync(sceneName.Value,LoadSceneMode.Additive,suspendLoad:true,priority:8);
_sceneHandle = YooAssets.LoadSceneAsync(sceneName.Value,LoadSceneMode.Additive,priority:8);
await _sceneHandle;
stopWatcher.Stop();
Debug.Log($"加载场景 {sceneName.Value} 耗时 {stopWatcher.ElapsedMilliseconds}ms");

View File

@ -70,7 +70,7 @@ namespace BITKit.GameEditor
}
}
}
catch (Exception e)
catch (Exception)
{
continue;
}

View File

@ -18,7 +18,7 @@ namespace BITKit.GameEditor
protected readonly List<T> List=new();
private ListView _listView;
private VisualElement _container;
protected VisualElement _container { get; private set; }
private Button _createButton;
private void OnEnable()
@ -99,7 +99,7 @@ namespace BITKit.GameEditor
$"{GetType().Name} 已初始化,从{AssetsPath}获取到{List.Count}个{typeof(T).Name}");
}
private void ItemsChosen(IEnumerable<object> obj)
protected virtual void ItemsChosen(IEnumerable<object> obj)
{
var selected = obj.FirstOrDefault() as Object;
var serializedObject = new SerializedObject(selected);
@ -167,6 +167,7 @@ namespace BITKit.GameEditor
).ToArray();
List.AddRange(allItem.Cast<T>());
}
protected virtual void CreateScriptableObject(string name)
{
if (string.IsNullOrEmpty(name))
@ -178,6 +179,8 @@ namespace BITKit.GameEditor
if (EditorUtility.DisplayDialog("创建", $"是否创建{name}与{path}?", "是", "否") is false) return;
new DirectoryInfo(Path.GetDirectoryName(path)!).Create();
var item = CreateInstance<T>();
item.name = name;
AssetDatabase.CreateAsset(item, path);

View File

@ -3,7 +3,9 @@ using System.Collections;
using System.Collections.Generic;
using MeshCombineStudio;
using NGS.AdvancedCullingSystem.Dynamic;
using UnityEditor.Build;
using UnityEngine;
using UnityEngine.Events;
#if UNITY_EDITOR
using UnityEditor;
#endif
@ -12,15 +14,21 @@ namespace BITKit.OpenWorld
public class OpenWorldCombiner : MonoBehaviour
{
[SerializeField] private MeshCombiner meshCombiner;
[SerializeField] private UnityEvent onCombiningReady;
[SerializeField]
private bool allowCullingSource = true;
private void Start()
{
meshCombiner.onCombiningReady +=x=> onCombiningReady.Invoke();
meshCombiner.onCombiningReady += AddCullingSource;
}
private void AddCullingSource(MeshCombiner meshcombiner)
{
if (enabled)
if (allowCullingSource)
AddCullingSource();
}

View File

@ -12,20 +12,35 @@ namespace BITKit
public string questName;
public string qeustDescription;
public BBParameter<QuestSystem.Info> output;
private bool isInitiated;
private void Disposed()
{
if (output.isNoneOrNull) return;
if(output.value.State is QuestSystem.State.InProcess)
{
BIT4Log.Log<CreateQuest>($"任务{output.value.Name}已取消");
QuestSystem.Cancel(output.value);
}
}
protected override void OnExecute()
{
var quest = QuestSystem.Create(questName, qeustDescription);
if (output.isDefined)
output.SetValue(quest);
EndAction();
}
protected override void OnStop(bool interrupted)
{
base.OnStop(interrupted);
if (!interrupted) return;
if (QuestSystem.quests.TryGetValue(output.value, out var info) &&
info.State == QuestSystem.State.InProcess)
QuestSystem.Cancel(output.value);
}
if(isInitiated)return;
agent.As<MonoBehaviour>().destroyCancellationToken.Register(Disposed);
isInitiated = true;
; }
// protected override void OnStop(bool interrupted)
// {
// base.OnStop(interrupted);
// if (!interrupted) return;
// if (QuestSystem.quests.TryGetValue(output.value, out var info) &&
// info.State == QuestSystem.State.InProcess)
// QuestSystem.Cancel(output.value);
// }
}
}

View File

@ -34,7 +34,6 @@ namespace BITKit
{
blackboard.SetVariableValue("HP", hp);
}
public void OnEntryOverride(bool @override)
{
_allow.SetDisableElements(this,@override);

View File

@ -15,7 +15,7 @@ namespace BITKit.NodeCanvas
public BBParameter<Transform> target;
protected override void OnExecute()
{
target.SetValue(sensor.value.CurrentTarget);
//target.SetValue(sensor.value.CurrentTarget);
EndAction();
}
}