1

I'm trying to build a display for a die roll. What I want to do is flicker images of random faces on the die, then end with the face that shows the number rolled. After this happens, I want the function to continue and return the number of the die roll. Here's what I have

public int RollDie()
{
    RollNum = dieRoll.Next(1, 7);
    DispCount = 0;
    Timer Time = new Timer();
    Time.Interval = TimerInterval;
    Time.Tick += DisplayRollHandler;
    Time.Start();
    System.Threading.Thread DispThread = new System.Threading.Thread(Time.Start);
    DispThread.Start();
    DispThread.Join();
    return RollNum;
}
private void DisplayRollHandler(object sender, EventArgs evt)
{
    if (DispCount < TargetDispCount)
    {
        Random Nums = new Random();
        Display.BackgroundImage = Faces[Nums.Next(0, 6)];
        DispCount++;
    }
    else
    {
        ((Timer)sender).Stop();
        Display.BackgroundImage = Faces[RollNum - 1];
    }
}

where dieRoll is a random object and Display is a Panel. The image flicker works, and it does return the number of the roll consistently. Unfortunately, it doesn't wait for the display flicker to finish before continuing, which is a problem when I have automatic messages that pop up after the die is rolled.

I'm a fairly inexperienced programmer, so I'm probably missing a really basic concept. I know that if I could abstract this into a method call, I can wait for a method call to finish, but I can't figure out how to do that without using a Thread.Sleep call and freezing the program.

Any suggestions?

4

2 回答 2

3

这个解决方案有一个根本性的错误,即随机化器永远不应该在骰子对象本身内实例化。一个简单的测试将说明原因。只需将另一个骰子对象添加到表单中并同时滚动两者。注意到一些有趣的事情了吗?他们总是一样的!

这是因为,默认情况下,随机化器使用当前时间来播种生成器。在代码的同一部分创建两个(或更多)对象将导致所有骰子对象具有相同的种子,因此每次滚动时的结果都相同。

更好的解决方案是创建一个静态单例类,该类将为您处理所有滚动(随机化)所有骰子。

这是一个简单的示例(使用更通用的 Dice 类):

public static class DiceRoller
{
    private static Random _roller;

    public static void RollDice(Dice dice)
    {
        if (dice.Faces.Count < 1)
            throw new InvalidOperationException("A dice must contain at least 1 side to be rolled.");

        if (_roller == null)
            _roller = new Random();

        int index = _roller.Next(dice.Faces.Count);
        dice.SetFacingIndex(index);
    }
}
于 2013-01-10T14:23:19.463 回答
2

刚刚写了一个Dice小类,它将为您提供所需的值:

public class Dice
{
    private Random _Random;
    private BackgroundWorker _Worker;

    /// <summary>
    /// Initializes a new instance of the <see cref="Dice"/> class.
    /// </summary>
    public Dice()
    {
        _Random = new Random();

        InitializeDefaultValues();
        InitializeBackgroundWorker();
    }

    /// <summary>
    /// Occurs when the dice finished rolling.
    /// </summary>
    public event EventHandler Rolled;

    /// <summary>
    /// Occurs while the dice is rolling and the value has changed.
    /// </summary>
    public event EventHandler RollingChanged;

    /// <summary>
    /// Gets or sets the including maximum value that the dice can return.
    /// </summary>
    /// <value>
    /// The maximum value.
    /// </value>
    [DefaultValue(6)]
    public int Maximum { get; set; }

    /// <summary>
    /// Gets or sets the including minimum value that the dice can return.
    /// </summary>
    /// <value>
    /// The minimum.
    /// </value>
    [DefaultValue(1)]
    public int Minimum { get; set; }

    /// <summary>
    /// Gets the result that this dice currently has.
    /// </summary>
    public int Result { get; private set; }

    /// <summary>
    /// Gets or sets the duration of the rolling.
    /// </summary>
    /// <value>
    /// The duration of the rolling.
    /// </value>
    [DefaultValue(typeof(TimeSpan), "00:00:03")]
    public TimeSpan RollingDuration { get; set; }

    /// <summary>
    /// Starts rolling the dice.
    /// </summary>
    public void Roll()
    {
        if (!_Worker.IsBusy)
        {
            CheckParameters();
            _Worker.RunWorkerAsync();
        }
    }

    private void CheckParameters()
    {
        if (Minimum >= Maximum)
        {
            throw new InvalidOperationException("Minimum value must be less than the Maximum value.");
        }

        if (RollingDuration <= TimeSpan.Zero)
        {
            throw new InvalidOperationException("The RollingDuration must be greater zero.");
        }
    }

    private void InitializeBackgroundWorker()
    {
        _Worker = new BackgroundWorker();
        _Worker.WorkerReportsProgress = true;
        _Worker.DoWork += OnWorkerDoWork;
        _Worker.ProgressChanged += OnWorkerProgressChanged;
        _Worker.RunWorkerCompleted += OnWorkerRunWorkerCompleted;
    }

    private void InitializeDefaultValues()
    {
        Minimum = 1;
        Maximum = 6;
        Result = Minimum;
        RollingDuration = TimeSpan.FromSeconds(3);
    }

    private void OnWorkerDoWork(object sender, DoWorkEventArgs e)
    {
        var finishTime = DateTime.UtcNow + RollingDuration;

        while (finishTime > DateTime.UtcNow)
        {
            Result = _Random.Next(Minimum, Maximum + 1);
            _Worker.ReportProgress(0);
            // ToDo: Improve sleep times for more realistic rolling.
            Thread.Sleep(50);
        }
    }

    private void OnWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        RaiseEvent(RollingChanged);
    }

    private void OnWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        RaiseEvent(Rolled);
    }

    private void RaiseEvent(EventHandler handler)
    {
        var temp = handler;

        if (temp != null)
        {
            temp(this, EventArgs.Empty);
        }
    }
}

在第一个简单的示例中,我只是将按钮添加到表单 ( buttonRoll) 和标签 ( labelDiceResult),并添加了以下代码(不要忘记将初始化方法添加到表单构造函数中):

private void InitializeDice()
{
    _Dice = new Dice();
    _Dice.RollingChanged += OnDiceRollingChanged;
    _Dice.Rolled += OnDiceRolled;
}

void OnDiceRolled(object sender, EventArgs e)
{
    buttonRoll.Enabled = true;
}

void OnDiceRollingChanged(object sender, EventArgs e)
{
    // ToDo: Select desired picture from image list depending on _Dice.Result
    labelDiceResult.Text = _Dice.Result.ToString();
}

private void OnButtonRollClick(object sender, EventArgs e)
{
    buttonRoll.Enabled = false;
    _Dice.Roll();
}

作为最后一步,我可能会Thread.Sleep(50)通过使用计算列表来调整调用以随着时间的推移使用不同的值,具体取决于窦的上升部分和所需的持续时间,让骰子随着时间的推移变慢。但我让这部分向读者开放(或下一个问题)。

于 2012-12-04T09:33:29.360 回答