unity3d - 在 Unity 中计算哪些 Collider 线段被击中?(将游戏对象放置在对撞机的周边)
问题描述
对于我正在开发的游戏,我正在尝试编写一些将 GameObjects 放置在 PolygonCollider2D 周边的代码。在我的游戏中,一个物体可以接触一个平台,然后开始在该平台周围散布物质。我希望内容在程序上散布在平台上,每个x
单元放置游戏对象。有关我的意思的示例,请查看此 .gif,其中我对 RayCast 做了同样的事情。
尝试使用光线投射来做到这一点会引入很多边缘情况。为了消除这些,我想应用一种更一致的方法。
在 Unity 中,对撞机包含一个数组,Collider.points
其中包含组成对撞机的点的坐标。理论上,如果您开始将游戏对象放置在 处point[0]
,查看 的方向point[1]
,然后开始沿该方向放置对象,直到到达point[1]
处,查看方向point[2]
并重复,您应该能够将对象整齐地放置在所述对撞机的周边周围。
我的问题是我不知道我的初始吊具对象是否必须在point[0]
andpoint[1]
或point[n]
and之间启动这个对象放置算法point[n+1]
。
请看一下这个例子:
如果我的碰撞发生在红色标记上,我需要以某种方式确定碰撞发生在 和 之间的线段 E 上point[4]
,point[5]
这样我就可以知道沿周边的“起始位置”并开始编写将对象放入的代码两个方向同时沿周边。
我的第一个想法是找到碰撞的世界位置,并找到距离points[]
该碰撞点最近的两个点的世界位置。但在上面的例子中,这行不通——它会找到位置2
和4
(甚至不是段),即使碰撞接触到4
和5
(段E)之间的线段。
有人对如何做到这一点有任何建议吗?
解决方案
如果您已经有了接触点,您可以遍历所有顶点 ( point
) 并检查接触点最接近哪条线。
以下两种方法取自HandleUtility
(参见源代码),但它仅存在于编辑器中,因此由于您想在运行时使用它,只需将其复制到自定义运行时类
public static class VectorUtils
{
// Project /point/ onto a line.
public static Vector3 ProjectPointLine(Vector3 point, Vector3 lineStart, Vector3 lineEnd)
{
Vector3 relativePoint = point - lineStart;
Vector3 lineDirection = lineEnd - lineStart;
float length = lineDirection.magnitude;
Vector3 normalizedLineDirection = lineDirection;
if (length > .000001f)
normalizedLineDirection /= length;
float dot = Vector3.Dot(normalizedLineDirection, relativePoint);
dot = Mathf.Clamp(dot, 0.0F, length);
return lineStart + normalizedLineDirection * dot;
}
// Calculate distance between a point and a line.
public static float DistancePointLine(Vector3 point, Vector3 lineStart, Vector3 lineEnd)
{
return Vector3.Magnitude(ProjectPointLine(point, lineStart, lineEnd) - point);
}
}
现在假设您的积分都是连续的,您可以使用它,例如
// Allows to do some iteration queries on IEnumerable collections
using System.Linq;
...
public static void GetTouchSegmentEndpoints(
// The Collider.points
PolygonCollider2D collider,
// Your given collision point
Vector3 touchPoint,
// After this method call these two will be filled with the information
out Vector3 resultA, out Vector3 resultB)
{
// Assign default values
resultA = Vector3.zero;
resultB = Vector3.zero;
var localPoints = collider.points;
// First of all the PolygonCollider2D.points are in LOCAL SPACE
// so firs we need to convert them to worldSpace
// using Linq we can do this in a single line
var worldPoints = collider.points.Select(p => collider.transform.TransformPoint(p)).ToArray();
// This basically equals doing something like
//var worldPoints = new Vector3 [localPoints.Length];
//for(var i = 0; i < localPoints.Length; i++)
//{
// worldPoints[i] = collider.transform.TransformPoint(localPoints[i]);
//}
// for comparing the distance to the current line
var minDistance = float.PositiveInfinity;
// Go through the world space points
for(var i = 0; i < worldPoints.Length; i++)
{
// Get the next i with wrap around at the end
var nextI = i == (worldPoints.Length - 1) ? 0 : i + 1;
// Get the two corner points for the current line
var pointA = worldPoints[i];
var pointB = worldPoints[nextI];
// get the distance between that line and the given touch point
var distance = VectorUtils.DistancePointLine(touchPoint, pointA, pointB);
// if it is smaller than the current minDistance
if(distance < minDistance)
{
// replace the results
resultA = pointA;
resultB = pointB;
minDistance = distance;
}
}
}
最后你会简单地称它为例如
PolygonCollider2D yourCollider;
Vector3 yourWorldTouchPoint;
VectorUtils.GetTouchSegmentEndpoints(yourCollider, yourWorldTouchPoint, out var lineA, out var lineB);
// Do something with lineA and lineB
如果这是我不知道的最有效的方法^^
推荐阅读
- laravel - 如何在 Laravel 中使用 exclude_if 验证数据字段?
- powerbi - 需要在 power bi 报告中应用公式
- python - 如何使用 Python 检查 MapR 卷是否存在?
- ios - 使用带有 Bloc 模式崩溃的 EasyRefresh 颤动
- python - 修剪列表的元素
- jquery - Select2 在选择上打开下拉菜单
- python - 在python中的图像中插入波斯文本,UnicodeEncodeError:'latin-1'编解码器无法编码位置0-4中的字符:序数不在范围内(256)
- php - 如何从laravel中的文本中提取base64图像?
- amazon-web-services - 将策略分配给用户组访问的 aws 函数时出错
- unit-testing - 有没有办法检查一个函数是否从 phpunit 中的两个给定数字中返回一个?