[游戏开发]Unity中随机位置_在圆/椭圆/三角形/多边形/内随机一个点

这篇具有很好参考价值的文章主要介绍了[游戏开发]Unity中随机位置_在圆/椭圆/三角形/多边形/内随机一个点。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

0. 前言

在做游戏的时候经常需要随机某一个点,而且形状各种各样,每次要随机的时候就容易忘记怎么弄了。这里总结一下各种常见形状内基础随机方式。

1. 矩形内随机

略~

/// <summary>
/// 在矩形区域内随机一个点
/// </summary>
public static Vector2 InRect(Rect rect)
{
    Vector2 pos = new Vector2();
    pos.x = Random.Range(0, rect.width) + rect.x;
    pos.y = Random.Range(0, rect.height) + rect.y;
    return pos;
}

2. 圆形内随机

圆形随机一般有两种。一种是通过极坐标来随机,另一种是先正常随机矩形在判断点是否在圆形内。第二种其实使用的范围很广,我们后面在 “ 6.拒绝采样随机 ” 单独讲,这里我们先讲第一种。

圆形通过极坐标随机就比较简单,分别随机半径和角度。不过直接随机的情况下,概率是不均匀的。也比较容易理解,比如随机到半径为1和为2的圆大小是不一样大的,但是其享受的概率是一样的,所以呢,会出现圆中心概率更高的情况。这个时候只要在[0,r*r]的范围内进行随机再sqrt开方就可以了,就可以弥补这个概率,具体推导可以用换元法试试。这里放一下代码

public struct Circle
{
    public Vector2 center;
    public float radius;

    public Circle(Vector2 center, float radius)
    {
        this.center = center;
        this.radius = radius;
    }
}
/// <summary>
/// 在圆形区域内随机一个点
/// </summary>
public static Vector2 InCircle(Circle circle)
{
    //  通过极坐标来随机
    float r = Mathf.Sqrt(Random.Range(0, circle.radius));
    float angle = Random.Range(0, Mathf.PI * 2);
    Vector2 pos = new Vector2(Mathf.Cos(angle) * r, Mathf.Sin(angle) * r);
    pos += circle.center;
    return pos;
}

至于从直角坐标的角度,先随机x,然后获取y的范围进行随机也会有概率不均匀的问题,具体处理还得重新推导,这里就不再研究了。

3. 三角形内随机

三角形随机意外要麻烦得多。简单讲一种思路。我们可以先随机三角形的一条边上的高,随机这个高度并再这个高度的情况下,去随机平行边的长度。

比如下图,我们先随机h再随机r的情况来得到点p。
[游戏开发]Unity中随机位置_在圆/椭圆/三角形/多边形/内随机一个点,游戏开发,unity,游戏引擎,游戏
那么在这种情况下也是会有概论不均衡的问题的,不过这里我们可以发现,因为做的是平行线的缘故,h和得到的平行线是等比放大和缩小的,所以可以用类似于圆的方式来随机。不过实现起来好麻烦呀,太难了,对于已经毕业的人来说,数学太难了。

这里我找到的了一种类似的方法,非常简单,具体论证如果有想了解的话,大家看一下后面链接吧。
[游戏开发]Unity中随机位置_在圆/椭圆/三角形/多边形/内随机一个点,游戏开发,unity,游戏引擎,游戏
那么实现代码就是这样的。

public struct Triangle
{
    public Vector2 a;
    public Vector2 b;
    public Vector2 c;

    public Triangle(Vector2 a, Vector2 b, Vector2 c)
    {
        this.a = a;
        this.b = b;
        this.c = c;
    }
}
/// <summary>
/// 在三角形区域内随机一个点
/// </summary>
public static Vector2 InTriangle(Triangle triangle)
{
    // Vector2 a, Vector2 b, Vector2 c
    Vector2 pos = new Vector2();
    float r1 = Random.Range(0f, 1f);
    float r2 = Random.Range(0f, 1f);
    pos = (1 - Mathf.Sqrt(r1)) * triangle.a +
        Mathf.Sqrt(r1) * (1 - r2) * triangle.b +
        Mathf.Sqrt(r1) * r2 * triangle.c;
    return pos;
}

4. 多边形内随机

多边形随机可以先把多边形分解为三角形,然后再根据三角形的面积分配不同的权重,随机一个三角形,然后在该三角形内进行随机。所以这个问题,又可以分解为以下几个点。

  • 多边形分隔为三角形
  • 三角形面积计算
  • 按权重随机
  • 三角形内随机一点

这样的方法其实流程颇为麻烦, 另一种方法是,像刚刚提及的一样,先矩形简单随机再判断是否在多边形内来达到随机目的,这个还是在 “6.拒绝采样随机” ,进行详细介绍。

那接下来就依次处理一下刚刚的问题。

(1)多边形分割为三角形

关于第一点其实还挺麻烦的,具体怎么分割为三角形有挺多不同的方法,耳切法是一种目前我能查到的一种常见方法,具体内容之前也有讲过,链接如下,实在太长这里就不展开解释了。

https://blog.csdn.net/Blue_carrot_/article/details/131192660

(2)三角形面积计算

这里用的是公式直接去计算就可以了,S=√[p(p-l1)(p-l2)(p-l3)](p为半周长),又解决咯。

/// <summary>
/// 获取三角形面积
/// </summary>
/// <returns></returns>
public float Area()
{
    //  S=√[p(p-l1)(p-l2)(p-l3)](p为半周长)
    float l1 = (b - a).magnitude;
    float l2 = (c - b).magnitude;
    float l3 = (a - c).magnitude;
    float p = (l1 + l2 + l3) * 0.5f;
    return Mathf.Sqrt(p * (p - l1) * (p - l2) * (p - l3));
}

(3)数据缓存

其实到这里我们可以发现,这种情况下要随机一个多边形的操作还是非常麻烦的。但是包括划分为三角形,计算三角形面积,这些其实都是可以把数据缓存下来的,这样下次还是可以接着用,就会快非常多。那么我们可以定义数据结构如下。

public struct PolygonRandomData
{
    public Polygon polygon;
    public Triangle[] triangles;
    public float polygonArea;
    public float[] trianglesArea;

    public PolygonRandomData(Polygon polygon)
    {
        // 三角化
        this.polygon = polygon;
        triangles = polygon.Triangulate();

        // 计算面积
        float area;
        trianglesArea = new float[triangles.Length];
        polygonArea = 0;
        for (int i = 0; i < triangles.Length; i++)
        {
            area = triangles[i].Area();
            polygonArea += area;
            trianglesArea[i] = area;
        }
    }
}

如果需要重复随机的话就可以先把这些数据缓存一下,降低开销。

(4)按权重随机

权重随机,目前做法就是先从0到权重和随机一个数,然后遍历叠加,去判断到达了没,以此达到目的。代码如下,权重和这里是由外部提供 ,因为我们刚刚其实也以及已经把面积和计算好了,就不用再计算一次。

/// <summary>
/// 按照数组内数的非负数权重,获取随机的索引。
/// 如果数组为空或者长度为0,将返回-1。
/// </summary>
/// <param name="weightArr">权重数组,应为非负数</param>
/// <param name="weightSum">给定的权重和,应为weightArr的权重之和</param>
/// <returns></returns>
public static int IndexInWeightArr(float[] weightArr, float weightSum)
{
    int index = -1;
    float cur = Random.Range(0, Mathf.Max(0, weightSum));
    float sum = 0;
    if (weightArr != null && weightArr.Length > 0)
    {
        index = 0;
        for (; index < weightArr.Length; index++)
        {
            sum += weightArr[index];
            if (sum > cur)
            {
                break;
            }
        }
        if (index >= weightArr.Length)
        {
            index = weightArr.Length - 1;
        }
    }
    return index;
}

下面是只提供权重数组的方法。

/// <summary>
/// 按照数组内数的非负数权重,获取随机的索引。
/// 如果数组为空或者长度为0,将返回-1。
/// </summary>
/// <param name="weightArr">权重数组,应为非负数</param>
/// <returns></returns>
public static int IndexInWeightArr(float[] weightArr)
{
    float weightSum = ArrayMathF.Sum(weightArr);
    return IndexInWeightArr(weightArr, weightSum);
}
public static float Sum<T>(T arr) where T : IEnumerable<float>
{
    float sum = 0;
    if (arr != null)
    {
        foreach (var value in arr)
        {
            sum += value;
        }
    }
    return sum;
}

(5)实现随机

至于三角形内随机,我们在前面已经解决了,此时就可以实现这个多边形随机了,如下!

 /// <summary>
/// 在多边形区域内内随机一个点
/// </summary>
public static Vector2 InPolygon(Polygon polygon, Vector2 defaultValue)
{
    return InPolygon(new PolygonRandomData(polygon), defaultValue);
}

/// <summary>
/// 在多边形区域内内随机一个点,polygonData为多边形三角化相关数据
/// </summary>
public static Vector2 InPolygon(PolygonRandomData polygonData, Vector2 defaultValue)
{
    Vector2 pos = defaultValue;
    int index = IndexInWeightArr(polygonData.trianglesArea, polygonData.polygonArea);
    if (index != -1)
    {
        pos = InTriangle(polygonData.triangles[index]);
    }
    return pos;
}

这里用了defaultValue主要是考虑到,多边形可能为非简单多边形,比如边有交叉或者点少于3个这种情况,会有分割三角形失败的问题,所以使用了这个值来可以提供后续识别或者保护处理。

5. 随机点测试

前面也列举的了挺多情况,这里先简单测试一下随机效果。每个图形绘制为红色,其中每个图形随机500点,画为绿色,测试结果如下。
[游戏开发]Unity中随机位置_在圆/椭圆/三角形/多边形/内随机一个点,游戏开发,unity,游戏引擎,游戏
可以看出也是随机点的分布还是比较均匀的,效果还可以。

6. 拒绝采样随机

终于到了这个环节。通常我们会有很多奇奇怪怪的形状,没办法具体来形容或者很难去推断去随机方法,甚至还有新的还有额外的条件。那么这个时候我们就可以用到拒绝采样的这样一种方式了。这种方式就是为了从一个简单的概率分布得到一个复杂的概率分布。

比如,我们想要随机一个圆内的点,我们先在矩形内随机,然后判断是否在圆形内,如果不在就拒绝,然后重新随机一下。这样我们就可以得到一个分布均匀的圆啦。这个过程就是拒绝采样了,那么程序上实现的话可以如下:

/// <summary>
/// 拒绝采样,在矩形区域内随机一个符合条件的点
/// </summary>
/// <param name="rect"></param>
/// <param name="judgeFunc"></param>
/// <returns></returns>
public static Vector2 RejectSampling(Rect rect, System.Func<Vector2, bool> judgeFunc)
{
    return RejectSampling(rect, judgeFunc, Vector2.zero, -1);
}

/// <summary>
/// 拒绝采样,在矩形区域内随机一个点,并判断是否符合条件,不符合再次随机
/// </summary>
/// <param name="rect">范围</param>
/// <param name="judgeFunc">判断条件</param>
/// <param name="defaultValue">失败返回坐标</param>
/// <param name="maxRandomTime">最大尝试次数,当 maxRandomTime <= 0 时,将无限尝试直到要求被满足</param>
/// <returns></returns>        
public static Vector2 RejectSampling(Rect rect, System.Func<Vector2, bool> judgeFunc, Vector2 defaultValue, int maxRandomTime)
{
    Vector2 pos;
    for (; maxRandomTime != 0; maxRandomTime--)
    {
        pos = InRect(rect);
        if (judgeFunc(pos))
        {
            return pos;
        }
    }
    return defaultValue;
}

这里会发现我其实给了额外的停止条件,因为在程序中不断循环是有风险的,如果万一这个条件是不可能在范围内达到的,到时候就直接死循环了,所以给定一个范围来提供保护还是挺有用的。

至于判断条件用的是委托的方式,注意了委托在创建的时候会有额外的开销,但正常传值调用的开销是和正常函数差不多的。所以不用过于担心,如果要多次随机,可以把委托先保存一下。

7. 拒绝采样样例

这里测试一下拒绝采样的效果,其实做拒绝采样随机的步骤就可以变化为两步

  • 确定随机范围,获取外包裹矩形
  • 确定随机条件,判断点是否在形状内

下面我们用两个比较麻烦的图形,椭圆和多边形来做这个拒绝采样处理试试。

(1)椭圆内随机

椭圆内随机一点,变成椭圆外包裹矩形判断是否在椭圆内,这个都比较简单,就直接代码咯

public struct Ellipse
{
    public Vector2 center;
    public float a;
    public float b;

    public Ellipse(Vector2 center, float a, float b)
    {
        this.center = center;
        this.a = a;
        this.b = b;
    }

    public Rect OutsideRect()
    {
        Vector2 size = new Vector2(a, b);
        return new Rect(center - size, size * 2);
    }

    public bool Inside(Vector2 pos)
    {
        pos -= center;
        return pos.x * pos.x / (a * a) + pos.y * pos.y / (b * b) < 1;
    }
}

那么获得者两个条件后,就可以开始随机了,如下就可以得到椭圆的点了

System.Func<Vector2, bool> judgeFunc = ellipse.Inside;
Rect ellipseOutsideRect = ellipse.OutsideRect();
Vector2 pos = RandomU.RejectSampling(ellipseOutsideRect, judgeFunc, Vector2.zero, 100);

(2)多边形内随机

判断多边形的外包裹矩形,就可以直接遍历所有点,找出xmin,xmax,ymin,ymax就可以得到这个矩形了,代码如下

public Rect OutsideRect()
{
    if (points.Length <= 0)
    {
        return new Rect(0, 0, 0, 0);
    }
    Vector2 min = points[0];
    Vector2 max = points[0];
    for (int i = 0; i < points.Length; i++)
    {
        max.x = Mathf.Max(points[i].x, max.x);
        max.y = Mathf.Max(points[i].y, max.y);
        min.x = Mathf.Min(points[i].x, min.x);
        min.y = Mathf.Min(points[i].y, min.y);
    }
    return new Rect(min, max - min);
}

判断点是否在多边形内部(点如果在多边形边上,也不属于内部),方法就有很多了,这里用的是做射线判断交点的方法,可以看下图。
[游戏开发]Unity中随机位置_在圆/椭圆/三角形/多边形/内随机一个点,游戏开发,unity,游戏引擎,游戏
A点在多边形外做射线的交点都会是偶数个,而B点在多边形内做射线交点为奇数个,就可以作为我们判断的依据。而且一般会用水平右方向的射线,比较容易理解和计算。需要考虑特殊情况就是如果多边形的点在射线上的情况,可不可以判断为相交?如下图。
[游戏开发]Unity中随机位置_在圆/椭圆/三角形/多边形/内随机一个点,游戏开发,unity,游戏引擎,游戏
这里的CDF点都会有不同情况的交点表现,这里就可以做其他限制。

  • 对于边的线段有端点在射线上的情况(如F,D),只考虑下端点(记为N),忽视上端点(记为M)。就是N在射线上,交点数量+1,M在不在射线上都不考虑为交点。(这里上下端点相反过来也是一样效果)。
  • 对于边的射线都在端点上的情况,视为无交点。

在加上这个两个处理后,再去数交点的数量,是不是就可以满足之前提出的交点数奇偶判断在不在多边形内?大家可以试一下。那么代码就如下了。

/// <summary>
/// 点是否在多边形(在边上视为在多边外)
/// </summary>
/// <param name="pos"></param>
/// <returns></returns>
public bool Inside(Vector2 pos)
{
    int j = points.Length - 1;
    bool inside = false;
    Vector2 pi, pj;
    for (int i = 0; i < points.Length; i++)
    {
        pi = points[i];
        pj = points[j];
        j = i;
        // 水平右方向射线,看交点个数
        if ((
            // 点的y值是否在两点之间,区间只选一边,
            // 这样每个就只会算一次,避免当有点在射线上的干扰
            (pi.y <= pos.y && pos.y < pj.y) || (pj.y <= pos.y && pos.y < pi.y)) &&
            // x轴截距
            pos.x < (pj.x - pi.x) * (pos.y - pi.y) / (pj.y - pi.y) + pi.x)
        {
            inside = !inside;
        }
    }
    return inside;
}

(3)效果

对于上述的两个形状来试试效果,还挺均匀的。效率问题其实也还好,因为其实随机到图形内的这个情况还是概率比较高的,重复几次基本就随机到了。
[游戏开发]Unity中随机位置_在圆/椭圆/三角形/多边形/内随机一个点,游戏开发,unity,游戏引擎,游戏

(4)点是否三角形/圆形内

其他图形用这个方法也是大同小异,这里就再简单列举一下其他图形如何判断点是否在形状内。
三角形:

/// <summary>
/// 是否在三角形内
/// </summary>
/// <returns></returns>
public bool Inside(Vector2 pos)
{
    Vector3 pa = a - pos;
    Vector3 pb = b - pos;
    Vector3 pc = c - pos;
    Vector3 pab = Vector3.Cross(pa, pb);
    Vector3 pbc = Vector3.Cross(pb, pc);
    Vector3 pca = Vector3.Cross(pc, pa);
    float d1 = Vector3.Dot(pab, pbc);
    float d2 = Vector3.Dot(pab, pca);
    float d3 = Vector3.Dot(pbc, pca);
    return d1 > 0 && d2 > 0 && d3 > 0;
}

圆形:

/// <summary>
/// 是否在圆形内
/// </summary>
/// <returns></returns>
public bool Inside(Vector2 pos)
{
    pos -= center;
    return pos.x * pos.x + pos.y * pos.y < radius * radius;
}

8. 结束咯

到这里就结束咯,希望能够对游戏中进行随机处理,起到参考作用~

相关参考文章
三角形内随机处理
http://www.cs.princeton.edu/~funk/tog02.pdf
https://www.jianshu.com/p/36fa431311ac
不规则、三角形面积计算
https://blog.csdn.net/n_moling/article/details/115381804
Unity3d判断一个点是否在多边形内
https://blog.csdn.net/zouxin_88/article/details/109678109文章来源地址https://www.toymoban.com/news/detail-520378.html

到了这里,关于[游戏开发]Unity中随机位置_在圆/椭圆/三角形/多边形/内随机一个点的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 即将开源STD:用于3D位置识别的稳定三角形描述子

    文章:STD: Stable Triangle Descriptor for 3D place recognition 作者:Chongjian Yuan, Jiarong Lin , Zuhao Zou , Xiaoping Hong, and Fu Zhang 编辑:点云PCL 代码:https://github.com/hku-mars/STD.git(接受后开源) 欢迎各位加入免费知识星球,获取PDF论文,欢迎转发朋友圈。文章仅做学术分享,如有侵权联系删文

    2024年02月03日
    浏览(37)
  • [unity]三角形顶点顺序

    详见官方文档:Unity - Manual: Mesh data (unity3d.com) 翻译: 拓扑描述网格具有的面类型。 网格的拓扑定义了索引缓冲区的结构,索引缓冲区又描述了顶点位置如何组合成面。每种类型的拓扑都使用索引数组中不同数量的元素来定义单个面。 Unity支持以下网格拓扑: 三角形 Quad 线

    2024年02月07日
    浏览(37)
  • 【opencv】示例-minarea.cpp 如何寻找一组随机生成的点的最小外接矩形、三角形和圆...

    此段代码的主要功能是:利用OpenCV库生成随机点集,并计算并展示这些点的最小外包矩形、三角形和圆形。用户可以通过按键重新生成不同的随机点集,或者按ESC/Q退出程序。程序中利用了OpenCV的随机数生成函数、绘图函数以及几何形状的计算函数。

    2024年04月14日
    浏览(35)
  • shell脚本语句(画矩形、三角形、乘法表和小游戏)(#^.^#)

    目录 一、语句 一、条件语句 一、以用户为例演示 一、显示当前登录系统的用户信息  二、显示有多少个用户 二、单分支if 一、输入脚本  二、验证结果 三、双分支if 一、输入脚本 二、验证结果  四、多分支if 一、输入脚本  二、验证 二、循环语句 一、shell版本的循环语

    2024年02月11日
    浏览(26)
  • OpenCV项目开发实战-- 将一个三角形变形为另一个三角形 ( C++ / Python )代码实现

     文末附基于Python和C++两种方式实现的测试代码下载链接 图 1:左图中蓝色三角形内的所有像素都已转换为右图中的蓝色三角形。 在本教程中,我们将看到如何将图像中的单个三角形变形为不同图像中的另一个三角形。 在计算机图形学中,人们一直在处理扭曲三角形,因为任

    2024年02月09日
    浏览(63)
  • 【Unity】程序创建Mesh(一)Mesh网格、代码创建模型、顶点信息、三角形信息、MeshFilter、MeshRenderer

    Mesh在Unity中是一个核心的组件,被称为网格组件,它主要用于表示3D几何体的数据结构。Mesh由顶点、三角形面以及可选的材质等组成,这些元素共同构建了3D模型的基础。 在Unity中,Mesh的功能非常强大且多样化。它不仅可以用来创建3D模型、绘制几何体、渲染场景,还支持多

    2024年04月15日
    浏览(31)
  • 前端开发必备技能!用简单CSS代码绘制三角形,提升用户体验

        🎬 江城开朗的豌豆 :个人主页  🔥 个人专栏  :《 VUE 》 《 javaScript 》  📝  个人网站  :《 江城开朗的豌豆🫛 》  ⛺️ 生活的理想,就是为了理想的生活 ! 目录  ⭐  专栏简介  📘  文章引言 一、前言 二、实现过程 三、原理分析 ⭐  写在最后         欢

    2024年02月07日
    浏览(31)
  • 【游戏开发算法每日一记】使用随机prime算法生成错综复杂效果的迷宫(C#,C++和Unity版)

    👨‍💻个人主页 :@元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 收录于专栏 :Unity基础实战 1.首先全部判定为墙,最外的为路包裹墙( 类似于防止数组越界 ) 2.红色为它的检测范围(假设检测点在如图所示的位置)—

    2024年02月05日
    浏览(28)
  • 用python写九九乘法表(左上三角、左下三角、右上三角、右下三角、正三角形、倒三角形格式)

    1.左上三角格式:   2.左下三角格式:   3.右上三角格式:     4.右下角格式:     5.倒三角格式:      

    2024年02月11日
    浏览(44)
  • css实现圆角三角形,圆角三角形的实现

    今天给大家带来一个如何实现圆角三角形的方案,这个方案虽然可以实现,但是也是借助拼凑等方式来实现的,假如想一个div来实现圆角三角形,还是比较困难的。之前文章讲了如何实现对话框,里面介绍了三角形的实现方式。今天讲讲如何实现圆角三角形。 想要生成一个带

    2024年02月09日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包