由于之前项目中有遇到那种需要做擦除效果,所以当时不知道用的什么技术,所以就把别人的游戏破解了,查到一个库:Clipper。结果在github上真的找到原作者写的c#库,原作者其实是写了一个mfc的exe运行文件,所以我们只需要原始的库文件就行了,至于怎么运行的就直接看的原mfc工程就是vs工程。这里有原工程的地址:Clipper download | SourceForge.net
大家可以用vs打开源工程并运行,可以看到代码是怎么实现的,简单讲一下它的原理,用聚合的原理来讲就是就求2个集合的差集了。
就是集合1-集合2所得到的一个集合了。 所以需要把集合的运用到程序中去了,特别需要运用到游戏中去了,在Unity我们知道一块地图它可能是不规则且地图的坐标都是浮点型的。这个库的原理是把所有地图上的点首先放大100倍 然后所有的顶点都取成整数。这样精度才不会发生大的丢失,同样计算起来也是比较方便的。所以用在unity中就是我们需要把传进去的每个坐标都放到n倍,然后把计算出来的结果再缩小n倍。最后渲染出来新的地图就是的了。所以这里效果擦除用的这个库,渲染使用的网格绘制方面的知识,网格绘制方面的知识我之前的博客都有讲到的。先看效果图吧
这个就是主要的效果了,首先在Awake的时候渲染出地图来。先定义地图的大小为10X10的大小
定义四个顶点分别为:(0, -5 * Scale),(5 * Scale, -5 * Scale),(5 * Scale, 5 * Scale),(0, 5 * Scale)。这里坐标乘了一个系数,到最后绘制的时候会除以这个系数的。
private GameObject GeneratorBody(string bodyName,Polygon points)
{
GameObject obj = new GameObject(bodyName);
obj.transform.position = Vector3.zero;
Mesh mesh = new Mesh();
obj.AddComponent<MeshRenderer>();
var mf = obj.AddComponent<MeshFilter>();
List<Vector3> verticles = new List<Vector3>();
List<Vector2> tempVerticles = new List<Vector2>();
for (int i = 0; i < points.Count; i++)
{
tempVerticles.Add(new Vector2(points[i].X / FloatScale, points[i].Y / FloatScale));
verticles.Add(new Vector3(points[i].X / FloatScale, points[i].Y / FloatScale, 0f));
}
var list = Triangulate.Points(verticles);
mesh.vertices = verticles.ToArray();
mesh.triangles = list;
mf.mesh = mesh;
obj.AddComponent<MeshCollider>().sharedMesh = mesh;
return obj;
}
Triangulate.Points这个方法是求所有三角面的绘制信息的。这个静态类我之前的博客中有提供的。起始网格生成完成之后就开始来进行核心的擦除,首先在网格下面放一个碰撞器,用来记录鼠标按下的世界坐标的位置。
private void Update()
{
if (Input.GetMouseButton(0))
{
var p = Input.mousePosition;
bool b = Physics.Raycast(Camera.main.ScreenPointToRay(p), out RaycastHit info, 1000f, 1 << LayerMask.NameToLayer("Ground"));
if (b)
{
Vector3 t = info.point;
ClipMap(t);
}
}
}
ClipMap(t)这个方法传进去的是一个具体的坐标,以这个坐标为中心并且以一个固定半径来生成一个多边形,把这个多边形转换成一个集合称为A集合,原始网格为一个集合称为B集合,所以最后得到就是B-A集合了。首先计算出A集合,A集合的原始模型是一个多边形
Polygon clip = new Polygon();
float radius = 0.5f;
int c = 15;
float offset = Mathf.PI * 2 / c;
for (int i = 0; i < c; i++)
{
var t1 = new IntPoint(centerPos.x * Scale, centerPos.y * Scale);
var t2 = new IntPoint(Mathf.Cos(i * offset) * radius * Scale, Mathf.Sin(i * offset) * radius * Scale);
clip.Add(t1 + t2);
}
_clips.Clear();
_clips.Add(clip);
具体擦除的方法如下,首先把2个集合添加到计算的数组中,然后计算出他们的差集,添加的时候标识他是集合A还是集合B,即那个是被减数,那个是减数。
_clipper.Clear();
_clipper.AddPaths(_subjects, PolyType.ptSubject, true);
_clipper.AddPaths(_clips, PolyType.ptClip, true);
_solution.Clear();
_succeeded = _clipper.Execute(ClipType.ctDifference, _solution);
if (_succeeded)
{
_solution2 = new Polygons(_solution);
_subjects.Clear();
int c1 = _objs.Count;
for (int i = 0; i < c1; i++)
{
Destroy(_objs[0]);
_objs.RemoveAt(0);
}
int count = _solution.Count;
for (int i = 0; i < count; i++)
{
var s = _solution[i];
var obj = GeneratorBody("Body" + i, s);
_objs.Add(obj);
_subjects.Add(s);
}
}
PolyType.ptSubject 可以理解为集合B,PolyType.ptClip可以理解为集合A,_clipper.Execute(ClipType.ctDifference, _solution);这个方法回返回一个bool值如果为ture说明_solution里面求得有效的值了,反之就是无效的值。ClipType.ctDifference就是求2个集合的差集。这个枚举分别有四种状态,
分别为交集,并集,差集,对称差集。经过计算得出一个集合后,计算2个集合的差集的时候这个集合里面可能会有多个多边形,所以这里根据多边形的个数又分别生成网格,同时更新_subjects数据,这里需要讲解一个坑,因为这边对有洞的多边形网格的绘制暂时还无法突破这样,所以我在擦除的时候一般都是围绕边界擦除而没有在网格的中心擦除。当然这里也可以中心擦除,这里有一个取巧的方案就是把整个大网格分成几个小的网格,只要每个小的网格的面积比擦除的面积小就行了。
项目源码:链接: https://pan.baidu.com/s/1DokatKnPrrY_ATcqBwiqZg 提取码: w6xq