1
This commit is contained in:
@@ -2,7 +2,8 @@
|
||||
"name": "BITKit.Physics",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:14fe60d984bf9f84eac55c6ea033a8f4"
|
||||
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
|
||||
"GUID:6babdba9f8b742f40904649736008000"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
99
Src/Unity/Scripts/Physics/GeometryUtils.cs
Normal file
99
Src/Unity/Scripts/Physics/GeometryUtils.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BITKit.Physics
|
||||
{
|
||||
public static class GeometryUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取一个点到一条线段的最近点
|
||||
/// </summary>
|
||||
/// <param name="point"></param>
|
||||
/// <param name="lineStart"></param>
|
||||
/// <param name="lineEnd"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector3 PointToLineSegmentDistance(Vector3 point, Vector3 lineStart, Vector3 lineEnd)
|
||||
{
|
||||
Vector3 lineDirection = lineEnd - lineStart;
|
||||
Vector3 pointDirection = point - lineStart;
|
||||
|
||||
float lineLength = lineDirection.magnitude;
|
||||
lineDirection.Normalize();
|
||||
|
||||
float dotProduct = Vector3.Dot(pointDirection, lineDirection);
|
||||
dotProduct = Mathf.Clamp(dotProduct, 0f, lineLength);
|
||||
|
||||
Vector3 closestPoint = lineStart + lineDirection * dotProduct;
|
||||
return closestPoint;
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取一个点到一个三角形内最短距离的点
|
||||
/// </summary>
|
||||
/// <param name="a">三角形顶点a</param>
|
||||
/// <param name="b">三角形顶点b</param>
|
||||
/// <param name="c">三角形顶点c</param>
|
||||
/// <param name="pos"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector3 GetPosInTriangle(Vector3 a, Vector3 b, Vector3 c, Vector3 pos)
|
||||
{
|
||||
Vector3 normal = Vector3.Cross(b - a, c - a).normalized;
|
||||
Vector3 toPoint = pos - a;
|
||||
float distance = Vector3.Dot(toPoint, normal);
|
||||
|
||||
Vector3 targetPos = pos - distance * normal;
|
||||
|
||||
if(PointInTriangle(targetPos, a, b, c))
|
||||
return targetPos;
|
||||
else
|
||||
{
|
||||
Vector3 p1 = PointToLineSegmentDistance(pos, a, b);
|
||||
Vector3 p2 = PointToLineSegmentDistance(pos, a, c);
|
||||
Vector3 p3 = PointToLineSegmentDistance(pos, b, c);
|
||||
|
||||
float d1 = Vector3.Distance(p1, pos);
|
||||
float d2 = Vector3.Distance(p2, pos);
|
||||
float d3 = Vector3.Distance(p3, pos);
|
||||
|
||||
if (d1 <= d2 && d1 <= d3)
|
||||
return p1;
|
||||
else if (d2 <= d3 && d2 <= d1)
|
||||
return p2;
|
||||
else /*if(d3 <= d1 && d3 <= d2)*/
|
||||
return p3;
|
||||
|
||||
//return default;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断一个点是否在三角形内
|
||||
/// </summary>
|
||||
/// <param name="pos"></param>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <param name="c"></param>
|
||||
/// <returns></returns>
|
||||
public static bool PointInTriangle(Vector3 pos, Vector3 a, Vector3 b, Vector3 c)
|
||||
{
|
||||
var v0 = c - a;
|
||||
var v1 = b - a;
|
||||
var v2 = pos - a;
|
||||
|
||||
var dot00 = Vector3.Dot(v0, v0);
|
||||
var dot01 = Vector3.Dot(v0, v1);
|
||||
var dot02 = Vector3.Dot(v0, v2);
|
||||
var dot11 = Vector3.Dot(v1, v1);
|
||||
var dot12 = Vector3.Dot(v1, v2);
|
||||
|
||||
var invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
|
||||
var u = (dot11 * dot02 - dot01 * dot12) * invDenom;
|
||||
var v = (dot00 * dot12 - dot01 * dot02) * invDenom;
|
||||
|
||||
// 如果u和v都在[0,1]的范围内,那么点P在三角形ABC内
|
||||
return (u >= 0) && (v >= 0) && (u + v < 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
11
Src/Unity/Scripts/Physics/GeometryUtils.cs.meta
Normal file
11
Src/Unity/Scripts/Physics/GeometryUtils.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae14ae5985cea8e40946355bfdf7113b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -1,7 +1,9 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using DrawXXL;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BITKit.Physics
|
||||
@@ -9,7 +11,7 @@ namespace BITKit.Physics
|
||||
public class GetClosePointFromCollider : IClosePointProvider
|
||||
{
|
||||
public string Name="Default";
|
||||
public Transform Transform;
|
||||
public readonly Transform Transform;
|
||||
public Vector3 Offset = default;
|
||||
public LayerMask LayerMask=LayerMask.NameToLayer("Default");
|
||||
public float Distance=1.6f;
|
||||
@@ -26,68 +28,97 @@ namespace BITKit.Physics
|
||||
|
||||
public bool TryGetValue(out Vector3 position, out Collider collider)
|
||||
{
|
||||
Vector3 vector3 = default;
|
||||
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())
|
||||
//for (var i = 0; i <detectedLength ; i++)
|
||||
{
|
||||
//reportBuilder.AppendLine($"----------------------------检测到了碰撞体{_mainCollider[i].name}");
|
||||
//var collider = _mainCollider[i];
|
||||
if (collider1.isTrigger)
|
||||
{
|
||||
reportBuilder?.AppendLine("碰撞体是触发器");
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector3 closePoint;
|
||||
|
||||
switch (collider1)
|
||||
{
|
||||
case MeshCollider meshCollider:
|
||||
if (meshCollider.convex is false)
|
||||
{
|
||||
reportBuilder?.AppendLine("MeshCollider未勾选Convex");
|
||||
continue;
|
||||
}
|
||||
case TerrainCollider terrainCollider:
|
||||
{
|
||||
closePoint = terrainCollider.ClosestPointOnBounds(samplePoint);
|
||||
}
|
||||
break;
|
||||
case MeshCollider { convex: false } meshCollider:
|
||||
{
|
||||
var getClosestPointFromMesh =
|
||||
new GetClosestPointFromMesh(meshCollider.sharedMesh,meshCollider.transform.InverseTransformPoint(samplePoint));
|
||||
|
||||
break;
|
||||
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;
|
||||
}
|
||||
|
||||
var bounds = collider1.bounds;
|
||||
vector3 = collider1.ClosestPoint(Transform.position + Vector3.up * 64);
|
||||
var top = bounds.center.y + bounds.extents.y;
|
||||
Debug.DrawLine(Transform.position, Transform.position + Vector3.up * top, Color.blue, 8f);
|
||||
if (Transform.position.y + MinHeight > top)
|
||||
if (Transform.position.y + MinHeight > closePoint.y)
|
||||
{
|
||||
reportBuilder?.AppendLine("高度不足");
|
||||
DrawBasics.PointTag(closePoint,"not enough height");
|
||||
continue;
|
||||
}
|
||||
|
||||
var nextPos = position;
|
||||
nextPos.y = collider1.bounds.center.y;
|
||||
|
||||
{
|
||||
var ray = new Ray(nextPos, Transform.forward);
|
||||
if (collider1.Raycast(new Ray(nextPos, Transform.forward), out _, Distance) is false)
|
||||
{
|
||||
reportBuilder?.AppendLine("未检测到前方");
|
||||
Debug.DrawRay(ray.origin,ray.direction,Color.red,Distance);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
var height = Mathf.Abs(top - Transform.position.y);
|
||||
var height = Mathf.Abs(closePoint.y - Transform.position.y);
|
||||
if (height > MaxHeight)
|
||||
{
|
||||
reportBuilder?.AppendLine($"高度差距过大:{height}");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (UnityEngine.Physics.Linecast(Transform.position, vector3, out var raycastHit2, LayerMask))
|
||||
if (UnityEngine.Physics.Linecast(Transform.position, closePoint, out var raycastHit2, LayerMask))
|
||||
{
|
||||
if (raycastHit2.collider != collider1)
|
||||
{
|
||||
@@ -95,40 +126,61 @@ namespace BITKit.Physics
|
||||
continue;
|
||||
}
|
||||
}
|
||||
var length = UnityEngine.Physics.OverlapSphereNonAlloc(vector3, 0.01f, _colliders, LayerMask);
|
||||
|
||||
|
||||
|
||||
|
||||
var length = UnityEngine.Physics.OverlapSphereNonAlloc(closePoint+Vector3.up*0.2f, 0.1f, _colliders, LayerMask);
|
||||
switch (length)
|
||||
{
|
||||
case 1:
|
||||
if (_colliders[0] != collider1)
|
||||
{
|
||||
reportBuilder.AppendLine($"检测到了其他碰撞体{_colliders[0].name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
case > 1:
|
||||
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;
|
||||
}
|
||||
|
||||
vector3.y = top;
|
||||
position = vector3;
|
||||
|
||||
collider = collider1;
|
||||
|
||||
|
||||
|
||||
reportBuilder.AppendLine("<color=green>成功</color>");
|
||||
|
||||
//BIT4Log.Log<GetClosePointFromCollider>(reportBuilder.ToString());
|
||||
return true;
|
||||
|
||||
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;
|
||||
//BIT4Log.Log<GetClosePointFromCollider>(reportBuilder.ToString());
|
||||
return false;
|
||||
}
|
||||
private float ByTop(Collider arg)
|
||||
|
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BITKit.Physics
|
||||
{
|
||||
[ExecuteAlways]
|
||||
public class GetClosePointFromColliderDebugger : MonoBehaviour
|
||||
{
|
||||
private GetClosePointFromCollider _getClosePointFromCollider;
|
||||
[SerializeField] private LayerMask layerMask;
|
||||
private void OnEnable()
|
||||
{
|
||||
_getClosePointFromCollider = new GetClosePointFromCollider(transform);
|
||||
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
_getClosePointFromCollider.LayerMask = layerMask;
|
||||
if (_getClosePointFromCollider.TryGetValue(out Vector3 vector3, out var collider1))
|
||||
{
|
||||
var offset = Vector3.up * -0.01f;
|
||||
Debug.DrawLine(transform.position+offset,vector3+offset,Color.cyan);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
Gizmos.DrawWireSphere(transform.position,_getClosePointFromCollider.Distance);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e856e0f7ca7746740a416303f76dc89d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
53
Src/Unity/Scripts/Physics/GetClosestPointFromMesh.cs
Normal file
53
Src/Unity/Scripts/Physics/GetClosestPointFromMesh.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BITKit.Physics
|
||||
{
|
||||
public readonly struct GetClosestPointFromMesh:IClosePointProvider
|
||||
{
|
||||
private readonly Vector3 _position;
|
||||
private readonly Mesh _mesh;
|
||||
|
||||
public GetClosestPointFromMesh(Mesh mesh, Vector3 position)
|
||||
{
|
||||
_mesh = mesh;
|
||||
_position = position;
|
||||
}
|
||||
|
||||
public bool TryGetValue(out Vector3 position, out Collider collider)
|
||||
{
|
||||
position = default;
|
||||
collider = default;
|
||||
|
||||
if (_mesh.isReadable is false) return false;
|
||||
|
||||
|
||||
var vertices = _mesh.vertices;
|
||||
|
||||
if (vertices.Length > 2048) return false;
|
||||
|
||||
var minPos = new Vector3(64, 64, 64);
|
||||
|
||||
for (var index = 0; index < _mesh.triangles.Length; index+=3)
|
||||
{
|
||||
var x = vertices[_mesh.triangles[index]];
|
||||
var y = vertices[_mesh.triangles[index + 1]];
|
||||
var z = vertices[_mesh.triangles[index + 2]];
|
||||
|
||||
var pos = GeometryUtils.GetPosInTriangle(x, y, z, _position);
|
||||
|
||||
if (Vector3.Distance(pos, _position) < Vector3.Distance(minPos, _position))
|
||||
{
|
||||
minPos = pos;
|
||||
}
|
||||
}
|
||||
|
||||
position = minPos;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
11
Src/Unity/Scripts/Physics/GetClosestPointFromMesh.cs.meta
Normal file
11
Src/Unity/Scripts/Physics/GetClosestPointFromMesh.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 979c184b4aaba6a439cbe74f0196267f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user