随机值似乎不是真的随机?

Jes*_*a.D 24 c# winforms

试图在C#中使用WinForm进行简单的杀菌游戏,但细菌(我Panel暂时使用)似乎并没有随意移动.

具体来说,我遇到的问题是,细菌试图向左上角移动并仅在那里移动.理想情况下,细菌需要均匀地在矩形范围内移动,但我不确定如何实现.看看下面的gif文件.

如您所见,红色Panel仅在左上角移动.我怎样才能让它随意均匀地随处移动?

这是我的代码:

private Panel _pnlBacteria;     //Panel representing a piece of bacteria
private Random r = new Random();  //For randomly-generated values
private int _prevX;        //Stores the previous X location
private int _prevY;        //Stores the previous Y location

public Form1()
{
    InitializeComponent();
    _pnlBacteria = new Panel();
    /* Get more property assignments to this._pnlBacteria (omitted) */

    //Bacteria's start position is also randomly selected
    _prevX = r.Next(50, 300);
    _prevY = r.Next(50, 500);
}

//Timer runs every 100 seconds changing the location of the bacteria
private void TmrMoveBacteria_Tick(object sender, EventArgs e)
{
    int x, y;
    //Get random values for X and Y based on where the bacteria was previously
    //and move randomly within ±10 range. Also it cannot go off the screen.
    do 
    {       
        x = r.Next(_prevX - 10, _prevX + 10);
        y = r.Next(_prevY - 10, _prevY + 10);
    } 
    while ((y <= 0) || (y >= 500) || (x <= 0) || (x >= 300));

    //Save the new location to be used in the next Tick round as previous values
    _prevX = x;
    _prevY = y; 

    //Apply the actual location change to the bacteria panel
    _pnlBacteria.Top = y;
    _pnlBacteria.Left = x;
}
Run Code Online (Sandbox Code Playgroud)

我尝试将+10更改为+12,保留-10原样,但现在这只会使细菌仅移动到右下角.我很茫然.有人可以帮忙吗?

Tur*_*uro 22

如果您阅读了Random.next(int,int)的文档,您会发现下限是包含的,上限是独占的,这就是-10和+11的工作原理.


Ruf*_*s L 12

解决这个问题的一种稍微不同的方法可能是选择随机方向和随机距离来沿该方向行进.如果我们在改变方向之前增加细菌可以行进的最大距离,这将允许更多的移动.

我们还可以"加权"距离的随机性,以便更频繁地选择较短的距离.这将为运动模式增加更多种类和随机性,同时仍允许一些长冲刺到另一个位置.

这是一个示例,您可以将其复制/粘贴到实现此想法的新表单项目中.您可以使用maxDistance(更改方向前允许的最远距离)和stepDistance(每次迭代时所经过的距离Timer)的设置.较小的stepDistance将导致更平滑但更慢的运动.我已经把它做得很小,所以我也减少了它的Interval性能Timer来加速它.

您会注意到我还添加了一种方法来验证方向是否有效,这样细菌就不会从屏幕上流下来.我创建了一个enum代表方向,这使得检查特定方向的移动变得容易(例如,如果枚举值包含"北"并且我们太靠近顶部,则它是无效的方向 - 这涵盖"北" ,"西北"和"东北"方向).

编辑:我将"加权距离"列表的创建移动到构造函数,并对其进行修改以选择指数级更少的项目而不是线性更少的项目.

希望有道理:

public partial class Form1 : Form
{
    // Program Settings and Controls
    private readonly Panel pnlBacteria;             // Panel representing a piece of bacteria
    private readonly Random random = new Random();  // For randomly-generated values
    private readonly Timer tmrMoveBacteria;         // Timer used for bacteria movement
    private readonly int bacteriaSize = 20;         // Stores the size for our bacteria
    private const int maxDistance = 50;             // The maximum number of moves allowed in the same direction.
    private const int stepDistance = 3;             // The distance to travel on each iteration of the timer. Smaller number is slower and smoother
    private readonly List<int> weightedDistances;   // Contains a weighted list of distances (lower numbers appear more often than higher ones)

    // Bacteria state variables
    private Direction direction;  // Stores the current direction bacteria is moving
    private int distance;         // Stores the distance remaining to travel in current direction

    // Represents possible directions for bacteria to move
    private enum Direction
    {
        North, NorthEast, East, SouthEast, South, SouthWest, West, NorthWest
    }

    public Form1()
    {
        InitializeComponent();

        // Initialize our weighted differences array so that 1 is  
        // chosen most often and maxDistance is chosen the least often
        weightedDistances = new List<int>();
        for (var i = 0; i < maxDistance; i++)
        {
            var weight = maxDistance / (i + 1);

            for (var j = 0; j <= weight; j++)
            {
                weightedDistances.Add(i + 1);
            }
        }

        // Give life to the bacteria
        pnlBacteria = new Panel
        {
            BackColor = Color.Red,
            Width = bacteriaSize,
            Height = bacteriaSize,
            Left = random.Next(0, ClientRectangle.Width - bacteriaSize),
            Top = random.Next(0, ClientRectangle.Height - bacteriaSize)
        };
        Controls.Add(pnlBacteria);

        // Start bacteria movement timer
        tmrMoveBacteria = new Timer {Interval = 10};
        tmrMoveBacteria.Tick += TmrMoveBacteria_Tick;
        tmrMoveBacteria.Start();
    }

    /// <summary>
    /// Sets the direction and distance fields to valid values based on the
    /// current bacteria position, direction, and remaining distance
    /// </summary>
    private void UpdateDirectionAndDistance()
    {
        // Get all directions
        var validDirections = Enum.GetValues(typeof(Direction)).Cast<Direction>();

        // Remove invalid directions (based on the bacteria position)
        if (pnlBacteria.Top < bacteriaSize) validDirections = 
            validDirections.Where(dir => !dir.ToString().Contains("North"));

        if (pnlBacteria.Right > ClientRectangle.Width - bacteriaSize) validDirections = 
            validDirections.Where(dir => !dir.ToString().Contains("East"));

        if (pnlBacteria.Left < bacteriaSize) validDirections = 
            validDirections.Where(dir => !dir.ToString().Contains("West"));

        if (pnlBacteria.Bottom > ClientRectangle.Height - bacteriaSize) validDirections = 
            validDirections.Where(dir => !dir.ToString().Contains("South"));

        // If we're supposed to keep on moving in the same 
        // direction and it's valid, then we can exit
        if (distance > 0 && validDirections.Contains(direction)) return;

        // If we got here, then we're setting a new direction and distance
        distance = weightedDistances[random.Next(weightedDistances.Count)];
        var directions = validDirections.Where(d => d != direction).ToList();
        direction = directions[random.Next(directions.Count)];
    }

    /// <summary>
    /// Executes on each iteration of the timer, and moves the bacteria
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void TmrMoveBacteria_Tick(object sender, EventArgs e)
    {
        // Ensure direction and distance are valid
        UpdateDirectionAndDistance();

        // Move the bacteria
        var dirStr = direction.ToString();
        if (dirStr.Contains("North")) pnlBacteria.Top -= stepDistance;
        if (dirStr.Contains("East")) pnlBacteria.Left += stepDistance;
        if (dirStr.Contains("South")) pnlBacteria.Top += stepDistance;
        if (dirStr.Contains("West")) pnlBacteria.Left -= stepDistance;

        distance--;
    }
}
Run Code Online (Sandbox Code Playgroud)