使用定时器滚动骰子功能,该定时器为图片框设置动画并返回随机值

时间:2012-12-03 01:24:15

标签: c# winforms timer wait

我正在尝试为模具制作显示器。我想要做的是模具上随机面的闪烁图像,然后以显示滚动数字的面结束。 发生这种情况后,我希望该功能继续并返回模具卷的编号。这就是我所拥有的

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];
    }
}

其中dieRoll是随机对象,Display是Panel。图像闪烁起作用,它确实一致地返回卷的编号。不幸的是,它不会等待显示闪烁在继续之前完成,这是一个问题,当我在掷骰子后弹出自动消息。

我是一个相当缺乏经验的程序员,所以我可能错过了一个非常基本的概念。我知道如果我可以将它抽象为方法调用,我可以等待方法调用完成,但我无法在不使用Thread.Sleep调用并冻结程序的情况下弄清楚如何执行此操作。

有什么建议吗?

2 个答案:

答案 0 :(得分: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);
    }
}

答案 1 :(得分: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)调用以使用不同的值随着时间的推移使用计算列表,具体取决于窦的上升部分以及让骰子随时间减慢的所需持续时间。但我让这部分对读者开放(或下一个问题)。