实现对正方形地块tile组成的2D区域描边

最近开发的时候需要实现给定一组Tile,每个Tile都是独立的GameObject,需要返回这些tile所代表的区域的轮廓信息,然后用linerenderer进行描边。

效果如下方视频:

【游戏开发】9期-猛龙断空斩/警戒射击,近期开发的有趣机制分享

想了好一阵子才发现其实非常简单,就是从边界处任意一点开始沿着边界移动就行。

步骤如下(仅适用于单连通区域):

1、遍历所有Tile,记录每一个顶点的频次(例如一个正方形给四个顶点提供一次)

2、随便从一个频次为1的顶点开始,记为顶点a

3、找出顶点a相邻的所有顶点,排除已被识别为边界的顶点后,将频次最小的顶点记为顶点b

4、若顶点b等于起始顶点,结束;若不等于,则将顶点b识别为边界,将顶点b记为顶点a,执行第3步。

Unity的C#代码如下,tiles是输入的tile,tile.GetVertexPos()作用是返回该Tile四个顶点的坐标

public class TileOutlineCollect : MonoBehaviour
{
    // Start is called before the first frame update
    public List<BaseTile> tiles;
    public LineRenderer lineRenderer;

    Dictionary<Vector2, TileVertex> VertexMap = new Dictionary<Vector2, TileVertex>();

    public void FindOutline()
    {
        VertexMap = new Dictionary<Vector2, TileVertex>();
        TileVertex startTileVertex = null;
        foreach (var tile in tiles)
        {
            foreach (var v in tile.GetVertexPos())
            {
                if (VertexMap.TryGetValue(v,out var TV))
                {
                    TV.count++;
                }
                else
                {
                    VertexMap[v] = new TileVertex()
                    {
                        count = 1,
                        position = v,
                    };
                    if (startTileVertex==null || startTileVertex.count> 1)
                    {
                        startTileVertex = VertexMap[v];
                    }
                }
            }
        }
        List<TileVertex> outlineVertex = new List<TileVertex>() { startTileVertex };
        List<Vector2> directionVector = new List<Vector2>() { Vector2.up, Vector2.right,Vector2.down, Vector2.left};
        for (int i=0;i<100;i++)
        {
            TileVertex NextVertex = null;
            foreach (var direction in directionVector)
            {
                if (VertexMap.TryGetValue(outlineVertex.Last().position+ direction* CoreValue.current.tileLength, out var TV) && !outlineVertex.Contains(TV))
                {
                    if (NextVertex==null || NextVertex.count> TV.count)
                    {
                        NextVertex = TV;
                    }
                }
            }
            if (NextVertex!=null && NextVertex!= outlineVertex.First())
            {
                outlineVertex.Add(NextVertex);
            }
            else
            {
                break;
            }
        }
        lineRenderer.positionCount = outlineVertex.Count;
        lineRenderer.SetPositions(outlineVertex.Select(o=>(Vector3)o.position).ToArray());
    }

    public class TileVertex
    {
        public int count;
        public Vector2 position;
    }
}

拓展

1、用上面的方法对一些狭长的区域描边时,可能会出现“抄近路”的现象,想修正这个问题可以把节点检索的步长减半,同时每个tile需要提供8个节点的信息(4顶点+4边中点);

2、对于非单连通区域,可以考虑在每次生成轮廓集合时,检查是否有未被标记为轮廓,且频次为1的节点,从而继续检索剩下的轮廓集合。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值