首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >等待单击事件被触发,C#

等待单击事件被触发,C#
EN

Stack Overflow用户
提问于 2016-02-19 20:48:53
回答 4查看 5.5K关注 0票数 12

我正在开发一个纸牌游戏,但我需要有一个功能,以停止程序,直到玩家还没有点击他的卡的PictureBox放弃它。我游戏的算法是:

代码语言:javascript
复制
int nextDrawer = 0; // the players which will discard a card are determinated in counterclockwise starting from the human player
for (int i = 0; i < players; i++) // untill all the players hasn't drawed a card
{
    if (i == 0) .... // the human player has to click on a picture box to discard a card
    else .... // an AI player will discard a card which is selected randomly from the 3 cards which AI has got in its hand
}

问题是,当赔款结束时,第一个弃牌的人可能会改变。如果玩家被数为0(人类玩家)、1(第一AI玩家)、2(第二AI玩家)和3(第三AI球员),那么在第一次放弃一张卡片的第一位是人类玩家,但是在第二次放弃时第一位丢弃的可能是2 AI球员,而人类玩家必须等到他之前的所有AI玩家都丢弃一张牌(在这种情况下,这一轮将是2-3-0-1)。

如果AI玩家还没有丢弃卡片,我如何取消点击事件?

更新

我不需要总是等待所有的 AI玩家都抽了一张牌:如果比赛的赢家是2-3-0-1:这意味着玩家必须等待AI玩家2和3的抽签,然后玩家必须点击一个PictureBox,循环将返回给AI玩家,然后AI玩家1被允许丢弃它的卡。

更新2

我想过这样的事情:

代码语言:javascript
复制
int leader = 0; // who is going to discard first
int nextDiscarder = leader; // next player who's going to discard
for (int i = 0; i < nPlayers; i++) // until all the players hasn't discarded
{
    if (nextDiscarder == 0) // the human has to discard
    {
        enablePictureBoxClickEvent;
        // now before the loop continue the program has to wait the event click on a picture box
    }
    else
    {
        AI[nextDiscarder].discard(); // the ai player will discard
    }
    if (nextDiscarder == players - 1) // if nextDiscarder has reached the end of the table
        nextDiscarder = 0; // return to the begin until all player has discarded a card
    else
        ++nextDiscarder; // continue to discard with the next player
}

在我的活动点击中,我会做这样的事情:

代码语言:javascript
复制
private myEventClick(object sender, EventArgs e)
{
    .... // do the instructions needed to discard a card
    disableMyEventClick;
    returnToLoop;
}

但主要的问题是,我不知道如何用代码编写我的指令returnToLoop

EN

回答 4

Stack Overflow用户

发布于 2016-10-22 16:36:37

我知道大多数人会认为您应该使用事件驱动的方法,但是async/await特性可以很容易地实现类似w/o的东西--这是手动实现状态机的需要。

我已经在Force loop to wait for an eventA Better Way to Implement a WaitForMouseUp() Function?中发布了类似的方法,因此基本上这是与前者相同的助手,ButtonControl替换

代码语言:javascript
复制
public static class Utils
{
    public static Task WhenClicked(this Control target)
    {
        var tcs = new TaskCompletionSource<object>();
        EventHandler onClick = null;
        onClick = (sender, e) =>
        {
            target.Click -= onClick;
            tcs.TrySetResult(null);
        };
        target.Click += onClick;
        return tcs.Task;
    }
}

现在只需将方法标记为async并使用await

代码语言:javascript
复制
// ...
if (nextDiscarder == 0) // the human has to discard
{
    // now before the loop continue the program has to wait the event click on a picture box
    await pictureBox.WhenClicked();
    // you get here after the picture box has been clicked
}
// ...
票数 26
EN

Stack Overflow用户

发布于 2016-10-27 15:02:42

我喜欢Ivan解决方案,因为它看起来很好,而且在任何需要等待控件的地方都很容易重用。

然而,我想提供另一个解决方案,因为我觉得这样做的方式要复杂得多。

那么让我们继续这样说:

  • 在游戏中的某个时刻,你需要玩家选择一张他们不想扔掉的牌
  • 有一个人类玩家,它是数组中的数字0。
  • 人类玩家并不总是第一个决定扔哪一张牌。
  • 若要决定扔掉哪一张卡,请向播放机显示一个picturebox,然后等待他单击它。

我相信一个简单的解决办法是:

  1. 你首先要把人工智能玩家的卡移到人类之前(如果人类是第一个被丢弃的,这将什么也做不了,如果人类是最后一个,所有的人工智能都会在这里丢弃)。
  2. 您启用PictureBox并结束您的函数
  3. 在PictureBox的单击事件中,您移除用户卡,然后删除后面的AI玩家的卡(如果人为第一,所有AI将在这里移除一张卡,如果人工是最后一张,则什么也不做)

完成了..。

所以这个看起来是这样的

代码语言:javascript
复制
//We need an instance variable, to keep track of the first player
int _firstPlayerToDiscard = 0;

private void StartDiscardingProcess(int FirstToDiscard)
{
    _firstPlayerToDiscard = FirstToDiscard;
    if (FirstToDiscard != 0) //If the player is the first, we do nothing
    {
        //We discard for every other AI player after the human player
        for (int i = FirstToDiscard; i < nPlayers; i++)
        {
            AI[i].Discard(); 
        }
    }
    //Now we fill the PictureBox with the cards and we display it to the player
    DiscardPictureBox.Enabled = true;
    //or DiscardPictureBox.Visible = true;
    //and we are done here, we know basically wait for the player to click on the PictureBox.
}

private void pictureBox_click(Object sender, EventArgs e)
{
    //Now we remove the card selected by the player
    // ...
    //And we remove the cards from the other AI players
    //Note that if the player was first to discard, we need to change the instance variable
    if (_firstPlayerToDiscard == 0) { _firstPlayerToDiscard = nbPlayers; }
    for (int i = 1; i < _firstPlayerToDiscard; i++)
    {
        AI[i].Discard();
    }
}

你已经差不多完成了..。

注意:对不起,如果语法不好或不寻常,我通常用VB .Net编写.随意编辑语法问题..。

票数 2
EN

Stack Overflow用户

发布于 2016-02-20 04:12:20

下面的代码演示了一个简单的基于定时器的状态机。在这种情况下,机器的状态就是当前玩家的回合。这个例子允许每个游戏通过将状态设置为下一个玩家来决定何时让下一个玩家开始她的回合。为程序应该检查的其他内容添加其他状态。这个程序体系结构运行相对平稳,因为程序线程不会在紧循环中被阻塞。“更快”的每个玩家可以完成和退出回合,更好-即使玩家的回合重复10000次,没有做任何事情,然后让下一个玩家发挥。

在下面的示例中,Click事件处理程序将机器状态从人工回合提升到AI回合。这有效地暂停了游戏,直到人类点击。由于转弯没有在一个紧的循环中被阻塞,您可以有其他按钮供人点击,如“通过”、“重新开始”和“退出”。

代码语言:javascript
复制
using System;
using System.Windows.Forms;
using System.Timers;

namespace WindowsFormsApplication1
{
  public partial class Form1 : Form
  {
    private System.Timers.Timer machineTimer = new System.Timers.Timer();

    // These are our Machine States
    private const int BEGIN_PLAY = 0;
    private const int HUMAN_PLAYER_TURN = 1;
    private const int AI_PLAYER_TURN = 2;

    // This is the Current Machine State
    private int currentPlayer = BEGIN_PLAY;

    // Flag that lets us know that the Click Event Handler is Enabled
    private bool waitForClick = false;

    // The AI members, for example 100 of them
    private const int AIcount = 100;
    private object[] AIplayer = new object[AIcount];
    private int AIcurrentIndex = 0;    // values will be 0 to 99


    public Form1()
    {
        InitializeComponent();
        this.Show();

        // The Timer Interval sets the pace of the state machine. 
        // For example if you have a lot of AIs, then make it shorter
        //   100 milliseconds * 100 AIs will take a minimum of 10 seconds of stepping time to process the AIs
        machineTimer.Interval = 100;  
        machineTimer.Elapsed += MachineTimer_Elapsed;

        MessageBox.Show("Start the Game!");
        machineTimer.Start();
    }


    private void MachineTimer_Elapsed(object sender, ElapsedEventArgs e)
    {
        // Stop the Timer
        machineTimer.Stop();
        try
        {
            // Execute the State Machine
            State_Machine();

            // If no problems, then Restart the Timer
            machineTimer.Start();
        }
        catch (Exception stateMachineException)
        {
            // There was an Error in the State Machine, display the message
            // The Timer is Stopped, so the game will not continue
            if (currentPlayer == HUMAN_PLAYER_TURN)
            {
                MessageBox.Show("Player Error: " + stateMachineException.Message, "HUMAN ERROR!",
                                MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else if (currentPlayer == AI_PLAYER_TURN)
            {
                MessageBox.Show("Player Error: " + stateMachineException.Message, "AI ERROR!",
                                MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else
            {
                MessageBox.Show("Machine Error: " + stateMachineException.Message, "Machine ERROR!",
                                MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
    }



    private void State_Machine()
    {
        // This routine is executing in the Timer.Elapsed Event's Thread, not the Main Form's Thread
        switch (currentPlayer)
        {
            case HUMAN_PLAYER_TURN:
                Play_Human();
                break;

            case AI_PLAYER_TURN:
                Play_AI();
                break;

            default:
                Play_Begin();
                break;
        }
    }


    private void Play_Human()
    {
        // This routine is executing in the Timer.Elapsed Event's Thread, not the Main Form's Thread
        // My Turn!
        if (!waitForClick)
        {
            // Please Wait until I take a card...
            // I am using this.Invoke here because I am not in the same thread as the main form GUI
            // If we do not wrap the code that accesses the GUI, we may get threading errors.
            this.Invoke((MethodInvoker)delegate
            {
                pictureBox1.Click += PictureBox1_Click;
            });

            // set this flag so we do not re-enable the click event until we are ready next time
            waitForClick = true;
        }
    }


    private void PictureBox1_Click(object sender, EventArgs e)
    {
        // This routine is executing in the Main Form's Thread, not the Timer's Thread

        // Stop the game for a little bit so we can process the Human's turn
        machineTimer.Stop();

        // Disable the Click Event, we don't need it until next time
        pictureBox1.Click -= PictureBox1_Click;
        waitForClick = false;

        // To Do:  Human's Turn code...

        // Let the AI Play now
        currentPlayer = AI_PLAYER_TURN;
        machineTimer.Start();
    }


    private void Play_AI()
    {
        // This routine is executing in the Timer.Elapsed Event's Thread, not the Main Form's Thread
        if (AIcurrentIndex < AIcount)
        {
            // If we do not wrap the code that accesses the GUI, we may get threading errors.
            this.Invoke((MethodInvoker)delegate
            {
                // To Do:  AI Player's Turn code...
            });

            // Advance to the next AI
            AIcurrentIndex++;
        }
        else
        {
            // Reset to the beginning
            AIcurrentIndex = 0;
            currentPlayer = BEGIN_PLAY;
        }
    }


    private void Play_Begin()
    {
        // This routine is executing in the Timer.Elapsed Event's Thread, not the Main Form's Thread
        // If we do not wrap the code that accesses the GUI, we may get threading errors.
        this.Invoke((MethodInvoker)delegate
        {
            // ... do stuff to setup the game ...
        });

        // Now let the Human Play on the next Timer.Elapsed event
        currentPlayer = HUMAN_PLAYER_TURN;

        // After the Human is done, start with the first AI index
        AIcurrentIndex = 0;
    }

  }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35514733

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档