using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using DrawXXL; using UnityEngine; namespace BITKit.Physics { public class GetClosePointFromCollider : IClosePointProvider { public string Name="Default"; public readonly Transform Transform; public Vector3 Offset = default; public LayerMask LayerMask=LayerMask.NameToLayer("Default"); public float Distance=1.6f; public float MinHeight=0.32f; public float MaxHeight = 1f; private readonly Collider[] _colliders=new Collider[8]; private readonly Collider[] _mainCollider=new Collider[8]; public GetClosePointFromCollider(Transform transform) { Transform = transform; } public bool TryGetValue(out Vector3 position, out Collider collider) { StringBuilder reportBuilder = new(); reportBuilder.AppendLine($"检测任务:{Name}"); position = Transform.position + Transform.rotation * Offset; var detectedLength = UnityEngine.Physics.OverlapSphereNonAlloc(position, Distance, _mainCollider, LayerMask); reportBuilder.AppendLine($"检测到了{detectedLength}个碰撞体"); var validMeshColliders = new Queue<(Collider collider,Vector3 targetPosition)>(); var samplePoint = position; samplePoint.y += Distance / 2; foreach (var collider1 in _mainCollider.Take(detectedLength).OrderBy(ByTop).Reverse()) { if (collider1.isTrigger) { reportBuilder?.AppendLine("碰撞体是触发器"); continue; } Vector3 closePoint; switch (collider1) { case TerrainCollider terrainCollider: { closePoint = terrainCollider.ClosestPointOnBounds(samplePoint); } break; case MeshCollider { convex: false } meshCollider: { var getClosestPointFromMesh = new GetClosestPointFromMesh(meshCollider.sharedMesh,meshCollider.transform.InverseTransformPoint(samplePoint)); if (getClosestPointFromMesh.TryGetValue(out var localPosition, out collider)) { localPosition = meshCollider.transform.TransformPoint(localPosition); Debug.DrawLine(Transform.position,localPosition,Color.magenta); if (Vector3.Distance(localPosition, position) < Distance) { closePoint = localPosition; } else { continue; } } else { continue; } } break; default: closePoint = collider1.ClosestPoint(samplePoint); break; } if(collider1.Raycast(new Ray(Transform.position,Transform.forward),out var raycastHit,64) is false) { continue; } if (MathV.IsForward(position, Transform.forward, closePoint) is false) { //DrawBasics.PointTag(closePoint,"not forward"); continue; } if (Transform.position.y + MinHeight > closePoint.y) { DrawBasics.PointTag(closePoint,"not enough height"); continue; } var height = Mathf.Abs(closePoint.y - Transform.position.y); if (height > MaxHeight) { reportBuilder?.AppendLine($"高度差距过大:{height}"); continue; } if (UnityEngine.Physics.Linecast(Transform.position, closePoint, out var raycastHit2, LayerMask)) { if (raycastHit2.collider != collider1) { reportBuilder?.AppendLine($"检测到了其他碰撞体:{raycastHit2.transform.name}"); continue; } } var length = UnityEngine.Physics.OverlapSphereNonAlloc(closePoint+Vector3.up*0.2f, 0.1f, _colliders, LayerMask); switch (length) { case > 0: reportBuilder.AppendLine("检测到了更多碰撞体"); for (var ii = 0; ii < length; ii++) { //Debug.DrawLine(vector3, _colliders[ii].ClosestPoint(vector3), Color.red, 8); reportBuilder.AppendLine($"\t{_colliders[ii].name}"); } continue; } reportBuilder.AppendLine("成功"); //BIT4Log.Log(reportBuilder.ToString()); Debug.DrawLine(Transform.position,closePoint,Color.green); validMeshColliders.Enqueue(new(collider1,closePoint)); } var minDot = 64f; Collider resultCollider = default; Vector3 resultPosition=default; while (validMeshColliders.TryDequeue(out var result)) { var dot =Mathf.Abs(Vector3.Cross(Transform.forward, result.targetPosition-Transform.position).y); DrawBasics.LineFrom(Transform.position,result.targetPosition-Transform.position,Color.red,text:dot.ToString(CultureInfo.InvariantCulture)); if(dot>minDot)continue; resultCollider = result.collider; resultPosition = result.targetPosition; minDot = dot; } if (minDot < 64) { collider = resultCollider; position = resultPosition; return true; } collider = null; return false; } private float ByTop(Collider arg) { return arg.bounds.center.y + arg.bounds.extents.y; } } }