C# Windows窗体应用程序功能循环

3

我需要将我创建的一款RPG游戏从控制台应用程序转化为Windows窗体应用程序,以供编程课程使用。一切都按照预期进行,但是在将战斗系统转换时卡住了。下面是调用战斗函数的按钮单击事件和战斗函数:

private void btStoryProgression_Click(object sender, EventArgs e)
{
     {...}

     else if (flagControl == 20)
        {
             Enemy enemy = new Enemy("Hellhound", "Oblivion Cave", 1);

             if (combat(player, enemy) == true)
             {
                 Lines.Text += "You won!\n";
                 player.itemList.Add(new Item("Fruit", 22));
             }
             else
             {
                 Lines.Text = "YOU DIED.";
                 Lines.Text += "GAME OVER.";
                 return;
             }
        }
}


bool combat(Player player, Enemy enemy)
{
    Lines.Text = enemy.getName() + " appeared!\n";
    int damage;
    bool battle = true;
    int battleChoice = 0, itemChoice = -1;

    turns = 0;
    Lines.Text += "What are you going to do?";
    buttonVisibility();
    buttonChoices("Attack", "Item");

    while (battle)
    {
         if (player.getSpeed() > enemy.getSpeed())
         {
            //Player turn
            if (battleChoice == 1)
            {
                Lines.Text = player.getName() + " attacked!\n";

                if (player.getAccuracy() < 100)
                {
                    Random rnd = new Random();
                    if (rnd.Next(1, 100) > player.getAccuracy())
                    {
                        Lines.Text += "The attack missed!\n";
                        goto enemyturn;
                    }
                }


                damage = player.getAtk() - enemy.getDef();
                if (damage < 0)
                    damage = 0;
                enemy.setHP(enemy.getHP() - damage);
                if (enemy.getHP() < 0)
                    enemy.setHP(0);

                    Lines.Text += "You dealt " + damage + " damage to " + enemy.getName();
                    Lines.Text += "Enemy HP is now " + enemy.getHP();

                    if (enemy.getHP() == 0)
                        return true;
                }
                else if (battleChoice == 2)
                {
                    Lines.Text += "Your items: ";
                    foreach (Item item in player.itemList)
                    {
                        Lines.Text += item.getName();
                    }
                    Lines.Text += "Choose a number based on the order the items appeared";
                    try
                    {
                        do
                        {
                            itemChoice = Convert.ToInt32(Console.ReadLine());
                            itemChoice--;

                        } while (itemChoice > player.itemList.Count());
                    }
                    catch (FormatException)
                    {
                        Console.WriteLine("SOMETHING WRONG!!");
                    }

                    Lines.Text += "Player HP was: " + player.getHP();
                    player.setHP(player.getHP() + player.itemList[itemChoice].getRecoverQtd());
                    Lines.Text += "Player HP is now: " + player.getHP();
                    player.itemList.RemoveAt(itemChoice);

                }

                enemyturn:
                //Enemy turn
                if (battleChoice != 0)
                {
                    Lines.Text += enemy.getName() + " attacked!\n";

                    damage = enemy.getAtk() - player.getDef();
                    if (damage < 0)
                        damage = 0;
                    player.setHP(player.getHP() - damage);
                    if (player.getHP() < 0)
                        player.setHP(0);

                    Lines.Text += enemy.getName() + " dealt " + damage + " damage to " + player.getName();
                    Lines.Text += player.getName() + " HP is now " + player.getHP();
                }

                if (player.getHP() == 0)
                    return false;

                turns++;
            }
            else
            {
                enemyturn:
                //Enemy turn
                if (battleChoice != 0)
                {
                    Lines.Text += enemy.getName() + " attacked!\n";

                    damage = enemy.getAtk() - player.getDef();
                    if (damage < 0)
                        damage = 0;
                    player.setHP(player.getHP() - damage);
                    if (player.getHP() < 0)
                        player.setHP(0);

                    Lines.Text += enemy.getName() + " dealt " + damage + " damage to " + player.getName();
                    Lines.Text += player.getName() + " HP is now " + player.getHP();

                    if (player.getHP() == 0)
                        return false;
                }
                //Player turn
                if (battleChoice == 1)
                {
                    Lines.Text += player.getName() + " attacked!\n";

                    if (player.getAccuracy() < 100)
                    {
                        Random rnd = new Random();
                        if (rnd.Next(1, 100) > player.getAccuracy())
                        {
                            Lines.Text += "The attack missed!";
                            goto enemyturn;
                        }
                    }

                    damage = player.getAtk() - enemy.getDef();
                    if (damage < 0)
                        damage = 0;
                    enemy.setHP(enemy.getHP() - damage);
                    if (enemy.getHP() < 0)
                        enemy.setHP(0);

                    Lines.Text += "You dealt " + damage + " damage to " + enemy.getName();
                    Lines.Text += "Enemy HP is now " + enemy.getHP();

                    if (enemy.getHP() == 0)
                        return true;
                }

                else if (battleChoice == 2)
                {
                    Lines.Text += "Your items: ";
                    for (int i = 0; i < player.itemList.Count; ++i)
                    {
                        Lines.Text += player.itemList[i].getName();
                    }
                    Lines.Text += "Choose a number based on the order the items appeared";

                }
                turns++;
            }
            return true;

        }

基本上,当我按下按钮并调用战斗时,整个函数会被跳过,直到找到一些松散的“else”,或者如果我限制每次比较,程序就会冻结。我进行了一些搜索,看到有人建议使用委托或BackgroundWorker,但我仍然对如何使用它们感到困惑。这个链接是我找到的最接近我的问题,但它也不能帮助我(可能因为我是新手...)。
请问你们可以帮我吗?先谢谢!

我不确定你是在说你的问题是方法不再起作用还是在运行时GUI变得无响应。如果是后者,你应该研究一下async和await。但要注意,在winforms中,你不能从任何线程访问GUI对象,除了主线程。你需要调用任何你正在进行的GUI更改。这个答案很好地解决了这个问题。 - pquest
哦,哇...我认为是这个,但看起来真的很复杂...我会尝试理解。在我的情况下,按钮单击事件和战斗方法都在Form类上(这就是我在战斗中访问标签“Lines”的原因)。哦,问题是第二个,执行函数时GUI无响应(如果找到任何未受限制的代码片段(如“else”语句),它实际上将跳过大部分战斗)。 - Diego Gonzales
你为什么要两次调用战斗方法?你可以将返回的布尔值存储在一个变量中,并在if语句中检查这个变量,不是吗? - joko
哦,这只是我在做的一个测试,不是代码的一部分。(我会编辑的,谢谢)。但我仍然卡在我的问题上。尝试使用Invoke,但我无法弄对... - Diego Gonzales
@DiegoGonzales,我的回答有帮到你吗? - pquest
1个回答

1
你的第一个问题是计算战斗的方法在GUI线程上运行。由于它很忙,无法实际执行更新GUI的真正工作,因此GUI会冻结。您永远不应该在GUI线程上执行长时间运行的任务。
您可以通过使用async和await来纠正这个问题,只需创建一个新的async方法并在按钮单击时运行即可:
private async Task RunCombat()
{
    Enemy enemy = new Enemy("Hellhound", "Oblivion Cave", 1);
    //this next line will run async now and will not block your gui thread.
    bool combatResult = await Task.Run(() => combat(player, enemy));

     if (combatResult)
     {
         Lines.Text += "You won!\n";
         player.itemList.Add(new Item("Fruit", 22));
     }
     else
     {
         Lines.Text = "YOU DIED.";
         Lines.Text += "GAME OVER.";
         return;
     }

然后只需在按钮点击事件中调用此方法,而不是您已经拥有的内容。一旦这样做,您将不能再使用Lines.Text +=在您的战斗方法中,因为它将在不同的线程上运行。

我会在您的窗体类声明之前创建一个名为LineAppenderDelegate的委托:

public delegate void LineAppenderDelegate(string lineToAdd);

然后创建一个AppendLine方法:

private void AppendLine(string lineToAdd)
{
    if(InvokeRequired) //if we are not on the gui thread
    {
        //re-call the same method on the gui thread
        LineAppenderDelegate d = new LineAppenderDelegate(AppendLine);
        Invoke(d, new object[]{lineToAdd});
    }
    else //we are on the gui thread and can just do the work
    {
        Lines.Text += lineToAdd;
    }
}

使用此方法添加行。该方法基本上检查您是否在正确的线程上与gui交互。如果不是,则在gui线程上重新调用该方法。如果是,则只是完成其工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接