【问题标题】:Await for the user to click on one control within multiple controls [duplicate]等待用户单击多个控件中的一个控件[重复]
【发布时间】:2020-07-12 00:51:29
【问题描述】:

这个问题以一堆其他人的同一句话开始......“我正在尝试用c#做一个纸牌游戏”,我见过很多类似的问题,但没有人真正解决我的问题.

我将通过一个小场景来介绍我的问题:

玩家 A 与目标 B 打牌 X

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

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

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

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

问题在于它需要使用大量布尔变量和 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));
}

【问题讨论】:

  • 也许你可以在你的 CardClass 中添加一个存储枚举值的变量。卡片已被点击,您可以使用 switchcase 禁用与点击类型不匹配的卡片......这只是一个想法......也许您可以在帖子中添加一些代码或 GUI 的屏幕截图,那真的很有帮助
  • async/await 关键字用于异步编程。您不是在这里尝试异步运行 IO 或 CPU 绑定代码。您试图确定玩家何时执行了某个动作(打出一张牌或通过)。玩家 B 的动作(打牌/传球)应该调用一个方法或调用一个将控制权交还给玩家 A 的事件。鉴于您提供的信息,无需 await 任何东西。

标签: c# winforms async-await


【解决方案1】:

一般来说,这类问题是通过创建状态机来解决的,即知道游戏处于什么状态的东西,并且根据这个状态,如何处理下一个输入。编程状态机有多种方法,但一种方法是使用 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()});
        }
    }

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

【讨论】:

  • 这实际上非常好,我不得不稍微更改代码以强制使用 Task 否则我无法获得结果。我仍然需要相当多的控件来做,但代码更简单。谢谢!
猜你喜欢
  • 2017-03-11
  • 2018-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-23
  • 2015-05-11
  • 2023-03-28
  • 2019-08-26
相关资源
最近更新 更多