Unity通过脚本实现不规则多边形的碰撞检测

先上效果图
多边形碰撞检测
可能大家都已经对Polygon Collider 2D这个组件已经非常的熟悉,就是一个判断多边形碰撞的组件,我们可以通过编辑形状大小来实现对不同多边形的碰撞检测。在这里插入图片描述
但是如果遇到较为复杂的多边形,我们在调节时就可能会相对困难,例如下边这个复杂的多边形
在这里插入图片描述
在这里分享一个脚本,可以去识别多边形最边缘的边,把最边缘的边赋值给Polygon Collider 2D,就可以使得Polygon Collider 2D的大小完美贴合多边形的边缘。

查找边缘的主要算法就是取到多边形内不共用的边(可以发现只有边缘的边是不被三角形共用的,被三角形公用的边都在内部)

代码:

using UnityEngine;
using System.Collections.Generic;
using UnityEditor;

[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(PolygonCollider2D))]
[ExecuteInEditMode]
public class Mesh2DColliderMaker : MonoBehaviour {

	/// <summary>
	/// 定义一个结构体,用来表示边
	/// </summary>
	struct Edge2D
	{

		public Vector2 a;
		public Vector2 b;

		public override bool Equals(object obj)
		{
			if (obj is Edge2D)
			{
				var edge = (Edge2D)obj;
				//An edge is equal regardless of which order it's points are in
				return (edge.a == a && edge.b == b) || (edge.b == a && edge.a == b);
			}

			return false;

		}

		public override int GetHashCode()
		{
			return a.GetHashCode() ^ b.GetHashCode();
		}

		public override string ToString()
		{
			return string.Format("[" + a.x + "," + a.y + "->" + b.x + "," + b.y + "]");
		}

	}

	MeshFilter filter;
	PolygonCollider2D polyCollider;


	void Start() 
	{
		filter = GetComponent<MeshFilter>();
		polyCollider = GetComponent<PolygonCollider2D>();
		CreatePolygon2DColliderPoints();
	}

	void Update()
	{		
			
	}


	public void CreatePolygon2DColliderPoints()
	{
		var edges = BuildEdgesFromMesh();
		var paths = BuildColliderPaths(edges);
		ApplyPathsToPolygonCollider(paths);
	}
	
	/// <summary>
	/// 对多边形碰撞体设置路径
	/// </summary>
	/// <param name="paths"></param>
	private void ApplyPathsToPolygonCollider(List<Vector2[]> paths) 
	{
		if (paths == null)
			return;

		polyCollider.pathCount = paths.Count;
		for (int i = 0; i < paths.Count; i++) {
			var path = paths [i];
			polyCollider.SetPath(i, path);
		}
	}

	/// <summary>
	/// 通过mesh去找所有的边,存到一个字典里
	/// 如果 int = 1 则证明是边缘的边,如果 int = 2 则证明则是公用的(不是边缘)
	/// </summary>
	/// <returns>返回存Edge2D的字典</returns>
	private Dictionary<Edge2D, int> BuildEdgesFromMesh() 
	{
		var mesh = filter.sharedMesh;

		if (mesh == null)
			return null;

		var verts = mesh.vertices;
		var tris = mesh.triangles;
		var edges = new Dictionary<Edge2D, int>();

		for (int i = 0; i < tris.Length - 2; i += 3) {

			var faceVert1 = verts[tris[i]];
			var faceVert2 = verts[tris[i + 1]];
			var faceVert3 = verts[tris[i + 2]];

			Edge2D[] faceEdges;
			faceEdges = new Edge2D[] {
				new Edge2D{ a = faceVert1, b = faceVert2 },
				new Edge2D{ a = faceVert2, b = faceVert3 },
				new Edge2D{ a = faceVert3, b = faceVert1 },
			};

			foreach(var edge in faceEdges) {
				if (edges.ContainsKey(edge))
					edges[edge]++;
				else
					edges[edge] = 1;
			}
		}

		return edges;
	}

	private List<Vector2[]> BuildColliderPaths(Dictionary<Edge2D, int> allEdges) 
	{

		if (allEdges == null)
			return null;	

		var outerEdges = GetOuterEdges(allEdges);

		var paths = new List<List<Edge2D>>();
		List<Edge2D> path = null;
		
		while (outerEdges.Count > 0) {
			
			if (path == null) {
				path = new List<Edge2D>();
				path.Add (outerEdges[0]);
				paths.Add (path);

				outerEdges.RemoveAt(0);
			}

			bool foundAtLeastOneEdge = false;

			int i = 0;
			while (i < outerEdges.Count) {
				var edge = outerEdges [i];
				bool removeEdgeFromOuter = false;

				if (edge.b == path[0].a) {
					path.Insert (0, edge);
					removeEdgeFromOuter = true;
				}
				else if (edge.a == path[path.Count - 1].b) {
					path.Add(edge);
					removeEdgeFromOuter = true;
				}

				if (removeEdgeFromOuter) {
					foundAtLeastOneEdge = true;
					outerEdges.RemoveAt(i);
				} else
					i++;
			}

			if (!foundAtLeastOneEdge)
				path = null;
			
		}
		
		var cleanedPaths = new List<Vector2[]>();
		
		foreach(var builtPath in paths) {
			var coords = new List<Vector2>();
			
			foreach(var edge in builtPath)
				coords.Add (edge.a);
			
			cleanedPaths.Add (CoordinatesCleaned(coords));
		}		
		
		return cleanedPaths;
	}

	/// <summary>
	/// 取到边缘的边(即字典中int = 1的值)
	/// </summary>
	/// <param name="allEdges"></param>
	/// <returns>边缘的边</returns>
	private List<Edge2D> GetOuterEdges(Dictionary<Edge2D, int> allEdges)
	{
		var outerEdges = new List<Edge2D>();

		foreach (var edge in allEdges.Keys)
		{
			var numSharedFaces = allEdges[edge];
			if (numSharedFaces == 1)
				outerEdges.Add(edge);
		}

		return outerEdges;
	}

	private bool CoordinatesFormLine(Vector2 a, Vector2 b, Vector2 c)
	{
		//If the area of a triangle created from three points is zero, they must be in a line.
		float area = a.x * ( b.y - c.y ) + 
			b.x * ( c.y - a.y ) + 
				c.x * ( a.y - b.y );
		
		return Mathf.Approximately(area, 0f);
		
	}
	
	private Vector2[] CoordinatesCleaned(List<Vector2> coordinates) 
	{
		List<Vector2> coordinatesCleaned = new List<Vector2> ();
		coordinatesCleaned.Add (coordinates [0]);
		
		var lastAddedIndex = 0;
		
		for (int i = 1; i < coordinates.Count; i++) {
			
			var coordinate = coordinates [i];
			
			Vector2 lastAddedCoordinate = coordinates [lastAddedIndex];
			Vector2 nextCoordinate = (i + 1 >= coordinates.Count) ? coordinates[0] : coordinates [i + 1];
			
			if (!CoordinatesFormLine(lastAddedCoordinate, coordinate, nextCoordinate)) {
				
				coordinatesCleaned.Add (coordinate);
				lastAddedIndex = i;							
			}
			
		}
		
		return coordinatesCleaned.ToArray ();
		
	}

}
  • 4
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Unity中的碰撞通常是基于几何形状(如球、盒子、胶囊)来定义的,因此它们都是规则的。但是,如果你需要一个不规则碰撞,例如一个复杂的几何形状或者一个非凸多边形,你可以使用Mesh Collider来实现。 Mesh Collider是一种使用网格来定义碰撞的组件。它可以使用网格渲染器组件中的网格数据,或者使用自定义的网格数据来创建不规则碰撞。在使用Mesh Collider时,你需要确保网格是封闭的,也就是说没有任何开口或断裂。 如果你需要动态地创建不规则碰撞,你可以使用Physics API来手动创建和管理碰撞。这种方法需要更多的编程工作,但是可以实现更高级的碰撞效果。 总的来说,Unity提供了多种方法来创建不规则碰撞,你可以根据自己的需求选择最适合的方法。 ### 回答2: Unity中的不规则碰撞是指不能简单地用基本的几何形状(如方块、圆形等)来描述的碰撞。在游戏开发中,不规则碰撞通常是由复杂的多边形、曲线等形状构成的。 为了实现不规则碰撞Unity提供了多种方法。其中一种常用的方法是通过使用多边形碰撞组件来实现。这个组件可以与自定义的多边形形状一起使用,以便在游戏中检测和处理与其碰撞的物。 另一种方法是使用物理引擎的边界包围盒(Bounds)来检测不规则碰撞。物的边界包围盒是一个可以包裹物的最小立方或最小球。物游戏中的碰撞检测通常通过比较边界包围盒之间的重叠程度来进行判断。 除了以上方法外,还可以使用Unity碰撞检测回调函数来自定义不规则碰撞的行为。通过编写脚本,在碰撞发生时执行特定的逻辑,例如触发特效、改变物属性等。 不规则碰撞游戏开发中非常重要,因为它可以增加游戏的真实感和复杂性。比如在一个平台跳跃的游戏中,玩家角色和平台之间的碰撞就是不规则碰撞,它们的形状可以与实际的游戏对象相匹配。 总而言之,Unity提供了多种实现不规则碰撞的方法,开发者可以根据需要选择和使用。这些方法的应用可以使游戏更加真实、有趣和具有挑战性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值