算法:在指定范围内生成随机不重复的位置

问题:

  在游戏中,我们经常会遇到以下问题:在指定的范围内生成随机不重复的位置。

  比如某次“神官赐福”活动中,需要在城门口生成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,再将编号数组转译为我们最终的位置值

猜你喜欢

转载自blog.csdn.net/Tel17610887670/article/details/130343260