我正在尝试为模具制作显示器。我想要做的是模具上随机面的闪烁图像,然后以显示滚动数字的面结束。 在发生这种情况后,我希望该功能继续并返回模具卷的编号。这就是我所拥有的
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
调用并冻结程序的情况下弄清楚如何执行此操作。
有什么建议吗?
答案 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)
调用以使用不同的值随着时间的推移使用计算列表,具体取决于窦的上升部分以及让骰子随时间减慢的所需持续时间。但我让这部分对读者开放(或下一个问题)。