问题:
在游戏中,我们经常会遇到以下问题:在指定的范围内生成随机不重复的位置。
比如某次“神官赐福”活动中,需要在城门口生成n个不重复的宝箱。
针对这种问题,我们可以先将范围按照宝箱(基本单元格)大小,切割为m个不同的位置,并用int数组记录它们的编号。最后通过随机剔除的方式,取得需要的随机数。
解决方案:
抽象为以上图形,即红色区域内,按照绿色单元格切割为长*宽个单元格,最后随机抽取。实际代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 将区域切分为单元格,并在其中选取随机数组生成
/// </summary>
public class RandomCreat : MonoBehaviour
{
public Vector3 startPos;
public Vector3 endPos;
public Vector3 size;
public int num;
// Start is called before the first frame update
void Start()
{
var _list = GetRandomPosInPlane(startPos, endPos, size, num);
foreach (var idx in _list)
{
var _obj = GameObject.CreatePrimitive(PrimitiveType.Sphere);
_obj.transform.position = idx;
}
}
// Update is called once per frame
void Update()
{
}
/// <summary>
/// 在同一平面中生成随机不重复数组
/// </summary>
/// <param name="_startPos">起始坐标</param>
/// <param name="_endPos">结束坐标</param>
/// <param name="_size">格子大小</param>
/// <param name="_num">获得的个数</param>
/// <returns></returns>
public List<Vector3> GetRandomPosInPlane(Vector3 _startPos, Vector3 _endPos, Vector3 _size,int _num)
{
//先将位置切割为n个单元格
var _sizePos = _endPos - _startPos;
int _x = (int)(_sizePos.x / _size.x);
int _z = (int)(_sizePos.z / _size.z);
//将这些单元格按照序号编入数组
var _allList = new List<int>();
var _maxNum = _x * _z;
for (var _i = 0; _i < _maxNum; _i++) _allList.Add(_i);
//从编号数组中随机出个数
var _getListNum = new List<int>();
while (_getListNum.Count < _num && _allList.Count > 0)
{
var _r = _allList[Random.Range(0, _allList.Count)];
_getListNum.Add(_r);
_allList.Remove(_r);
}
//将编号还原为坐标
var _getList = new List<Vector3>();
foreach (var idx in _getListNum)
_getList.Add(new Vector3(_startPos.x + (0.5f + idx % _x) * _size.x, _startPos.y, _startPos.z + (0.5f + idx / _x) * _size.z));
return _getList;
}
}
测试:
我们可以把脚本挂到一个物体上,从而测试具体的实现效果:
选取10个的时候,则每次结果都不一样。
总结:
通过以上示例,我们可以抽象出更通用的解决方案。这样以后遇到其它图形或者3D区域,都可以通过这个思路解决:
1,将空间按照基本单元切割为m*n个区域;
2,将所有区域编号并写入int数组
3,通过剔除从int数组中取得我们想要的编号数组
4,再将编号数组转译为我们最终的位置值