首页 > 解决方案 > 等待用户点击多个控件中的一个控件

问题描述

这个问题以一堆其他人的话开头......“我正在尝试用c#做一个纸牌游戏”,我已经看到了很多类似的问题,但没有人真正解决我的问题。

我将用一个小场景介绍我的问题:

玩家 A 与目标 B 打牌 X

玩家 B 需要选择一张反牌 Y 或通过

玩家 B 视图包含他的牌,每张牌都由一个图片框表示。

要继续游戏,我需要玩家 B 的控制器等待,直到他点击类型 Y 的卡,或通过回合。

对我来说,做这样的事情最简单的方法是使用布尔值来伪装等待。很容易记录哪张牌是最后一张牌(例如 X),然后等到 card.Click 事件在 Y 类型的牌上触发或按下通过按钮。

问题在于它需要使用大量布尔变量和 if/else 来处理这些“事件”。

什么是这样的更好的实现?

编辑。

GameController.cs

public void XCardPlayed(string target)
{
    if (this.players[0].Username.Equals(target))
    {
        // I'm the target
        lastCardPlayed = "X";
        PlayYCard();
    } 
    else
    {
        // display the card on the table
    }
}

public void PlayYCard()
{
    gameView.NotifyPlayYCard();
}

GameForm.cs

public void PlayCardFromHand(int index)
{
    if (playY)
    {
       // Check if card at index is instance of Y
    }
}

public void NotifyPlayYCard()
{
    playY = true;
}

public void CardClick(object sender, EventArgs e)
{
    PictureBox current = (PictureBox)sender;
    PlayCardFromHand(pnl_cards.Controls.IndexOf(current));
}

标签: c#winformsasync-await

解决方案


一般来说,这类问题是通过创建一个状态机来解决的,即知道游戏处于什么状态的东西,并且根据这个状态,如何处理下一个输入。有几种方法可以对状态机进行编程,但一种方法是使用 async/await 让编译器来完成。

您可以将按钮包装在每次按下按钮时完成任务的类中:

public class ButtonAwaiter
{
    private readonly Button button;
    private TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
    public ButtonAwaiter(Button button)
    {
        this.button = button;
        this.button.Click += OnClick;
    }

    private void OnClick(object sender, EventArgs e)
    {
        tcs.SetResult(true);
        tcs  = new TaskCompletionSource<bool>();
    }

    public Task GetTask() => tcs.Task;
}

用“bool”代替按钮代表的任何东西。

并等待第一个按钮被按下,例如:

    public async Task DoGameLoop()
    {
        var b1 = new ButtonAwaiter(button1);
        var b2 = new ButtonAwaiter(button2);
        while (GameIsInProgress)
        {
            var pressed = await Task.WhenAny(new[] {b1.GetTask(), b2.GetTask()});
        }
    }

当用户需要遵循某些操作序列时,此方法特别好,因为它可以让您以相当简单的方式编写代码。


推荐阅读