原题:怎么设计pubg的随机空投点,假如是一个圆形的小岛
首先说的常见错误答案
选取两个随机变量,一个(0-360)的随机角变量,一个(0-安全区半径)的距离变量,两个变量组合为一个新的极坐标。但该方法其实会导致,越靠近原点的地方,随机点越密集。
如下图:
该图拷贝自链接
正确思路
设圆的半径为R
我们需要取三个系数,首先是随机系数 t,t是(0-1)均匀分布的随机数,半径系数 r,r=sqrt(t)∗R,角度系数θ, θ=2π∗t,要注意r和θ要分别使用两个不同的随机数t来生成,
那么生成的x和y规则为
x=r∗cos(θ),y=r∗sin(θ)
公式参考 连接 需要用到概率论和高等数学的知识
先放生成效果
Tips:代码中加了颜色系数,颜色越绿的,生成的越晚,也侧面反映出,点是纯随机分布的。
核心逻辑(后面有放完整代码)
void Awake()
{
Debug.Log("开始时间 "+GetTimeStamp());
pointCount = 5000;//要生成点的数量
resultList = new double[pointCount, 2];
double radius = 2; //圆的半径,要生成点在该半径的圆内
double randomValue; //0-1的随机值
double r;//公式的r系数
double theta; //公式的角度系数
double x;
double y;
System.Random random = new System.Random();
for (int i = 0; i < pointCount; i++)
{
// Tip 此处要注意! r和theta的生成要分别生成随机数,公式概念中明确说明,r和theta要互不相干
randomValue = random.NextDouble();
r = Math.Sqrt(randomValue) * radius;
randomValue = random.NextDouble();
theta = 2 * Math.PI * randomValue;
//生成x,y坐标,
x = r * Math.Cos(theta);
y = r * Math.Sin(theta);
resultList[i,0] = x;
resultList[i, 1] = y;//* 0.5; 若要变成椭圆,将X和Y结果值乘上你想要的比例系数即可
}
Debug.Log("结束时间 " + GetTimeStamp());
}
Unity内可以运行的代码
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//解法思路:公式来自:https://wenku.baidu.com/view/de291225aaea998fcc220ef9.html
//设半径为R
//x=r∗cos(θ)
//y=r∗sin(θ)
//t为0-1均匀分布产生的随机数,r=sqrt(t)∗R,θ=2π∗t
public class Run : MonoBehaviour {
/// <summary>
/// 获取时间戳
/// </summary>
/// <returns></returns>
public static string GetTimeStamp()
{
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalMilliseconds).ToString();
}
//存储结果列表
public double[,] resultList;
//要生成点的数量
int pointCount;
void Awake()
{
Debug.Log("开始时间 "+GetTimeStamp());
pointCount = 5000;//要生成点的数量
resultList = new double[pointCount, 2];
double radius = 2; //圆的半径,要生成点在该半径的圆内
double randomValue; //0-1的随机值
double r;//公式的r系数
double theta; //公式的角度系数
double x;
double y;
System.Random random = new System.Random();
for (int i = 0; i < pointCount; i++)
{
// Tip 此处要注意! r和theta的生成要分别生成随机数,公式概念中明确说明,r和theta要互不相干
randomValue = random.NextDouble();
r = Math.Sqrt(randomValue) * radius;
randomValue = random.NextDouble();
theta = 2 * Math.PI * randomValue;
//生成x,y坐标,
x = r * Math.Cos(theta);
y = r * Math.Sin(theta);
resultList[i,0] = x;
resultList[i, 1] = y;//* 0.5; 若要变成椭圆,将X和Y结果值乘上你想要的比例系数即可
}
Debug.Log("结束时间 " + GetTimeStamp());
}
void OnDrawGizmos()
{
float colorOffset = 1f / pointCount;
for (int i = 0; i < pointCount; i++)
{
Gizmos.color = new Color(colorOffset * i, 1f, 1f);
Gizmos.DrawSphere(new Vector3((float)resultList[i, 0], (float)resultList[i, 1], 0), 0.02f);
}
}
}
Unity内实现的效果
加了颜色系数,颜色越绿的,生成的越晚,也侧面反映出,点是纯随机分布的。
加了坐标系数后的椭圆效果,y*0.5
简单的性能比较
除了上述方法,还有一种方法也可以达到同样的效果。
即 先在矩形内选随机点,然后判断这个点是否在圆内,如果在,则保留,如果不在则舍去。
核心代码如下:
//存储结果列表
public double[,] resultList;
//要生成点的数量
int pointCount;
void Awake()
{
Debug.Log("开始时间 " + GetTimeStamp());
pointCount = 100000;//要生成点的数量
resultList = new double[pointCount, 2];
double radius = 2; //要生成点半径
double Radius = radius * 2; //要生成点半径
double radiusPow2 = Math.Pow(radius, 2);
double randomValue;
double x;
double y;
int tempCount = 0;
System.Random random = new System.Random();
while(tempCount < pointCount)
{
randomValue = random.NextDouble();
x = randomValue * Radius - radius;
randomValue = random.NextDouble();
y = randomValue * Radius - radius;
if (Math.Pow(x, 2) + Math.Pow(y, 2) <= radiusPow2)
{
resultList[tempCount, 0] = x;
resultList[tempCount, 1] = y;
}
tempCount++;
}
Debug.Log("结束时间 " + GetTimeStamp());
}
经比较,同样生成50000个随机点,该方法比第一种慢了5ms
第一种
第二种
- 参考链接 https://www.cnblogs.com/yunlambert/p/10161339.html 该链接讲述了三种方法,需要看思路,看多种实现的差异性的,建议看这个博客
- 参考链接 https://www.cnblogs.com/Atanisi/p/8849215.html 该链接比较简洁,直接点明了各个参数如何声明。需要Copy代码的建议看这个博客
如果您看到了最后,希望大佬给个赞可以嘛,想攒个人气,以后面试加个分。谢谢。