【问题标题】:c# RPG Removing from a listc# RPG 从列表中删除
【发布时间】:2017-07-17 21:11:36
【问题描述】:

我正在用 C# 开发一个简单的 RPG 类型游戏,它主要是一个原型,只是测试想法(我知道代码可以改进并且有些地方很粗糙,但大部分都可以工作)。

我目前在战斗系统上遇到了障碍。

目前,您控制 1 个角色。您最多可以面对 3 个敌人。这是一个基于回合的 ATB 系统,这部分工作正常。一旦轮到玩家,他们可以选择攻击并使用它。

我想添加一个新功能以产生随时间推移造成的伤害效果。然而,这已经打破了战斗。

我做了一个类来表示一个 DOT 效果:

 public class DoT
{
    public int Duration { get; set; }
    public int Elapsed { get; set; }
    public int Damage { get; set; }
    public Enemy Target { get; set; }
    public String DName { get; set; }
    public DoT()
    {
        Duration = 3;
        Elapsed = 0;
        DName = "";
    }
    public void Tick()
    {
        Elapsed++;
    }

}

这是在技能创建时添加的,这部分有效,我可以为技能添加DOT效果。

在战斗系统中,选择攻击时,我创建了一个名为点的点效应列表。当用户选择攻击时,如果该攻击附加了DOT效果,则将其添加到DOT效果列表中:

try
        {
            string s = ((Control)sender).Text;//Stores name of label that triggered the event
            int i = s.Length;//can't remember why I added this

            if (playerTarget != null)//check that we have a target
            {
                if (currentTurn.owner == c && canAttack)//if the current turn is ours and we can attack
                {
                    if (c.GetAbility(s).AbilityCost <= c.currentMP)//if we have enough MP for the attack
                    {
                       if(c.GetAbility(s).DamageOverTime!=null)//checks if the attack has a DOT effect
                       {
                           c.GetAbility(s).DamageOverTime.Target = playerTarget;//the DOT now targets the target
                           AddDOT(c.GetAbility(s).DamageOverTime );//Adds the DOT effect to DOTS
                           //Trace statements I was using for debugging
                           UpdateTest("Added "+ c.GetAbility(s).AbilityName + " to DOT");
                           UpdateTest("Time left- " + (c.GetAbility(s).DamageOverTime.Duration-c.GetAbility(s).DamageOverTime.Elapsed).ToString() );
                       }
                        currentTurn.ability = c.GetAbility(s);//Sets the current ability to be used
                        UseTurn();//Executes a turn
                    }
                    else
                    {
                        MessageBox.Show("Not enough MP!");
                    }
                }
            }
            else
            {
                MessageBox.Show("You must select a target!");
            }


        }
        catch (Exception ex)
        {
            MessageBox.Show("Error" + ex.ToString());
        }

添加DOT效果的方法是这样的:

public void AddDOT(DoT d)
    {
        int exists = 0;
        if (DOTS.Count > 0)
        {
            foreach (DoT dot in DOTS)
            {
                if (dot.Equals(d))
                {
                    dot.Elapsed = 0;
                    exists++;
                }
            }
            if(exists==0)
            {
                DOTS.Add(d);
            }
        }
        else
        {
            DOTS.Add(d);
        }

这是为了停止应用多个相同的 DOT,如果它已经存在,我们只需重置当前 DOT 上经过的时间。

我还有一个方法可以在每回合应用DOT效果:

 public void UpdateDots()
    {
        try
        {
            if (DOTS.Count > 0)//Check that we have a DOT
            {
                foreach (DoT d in DOTS)//Loop through each DOT
                {
                    DotDamage(d);//Apply the DOT damage to the DOT's target
                    d.Tick();//call the tick method of DOT to add 1 to elapsed
                    //Trace statement to help debug
                    UpdateTest("Duration on " + d.DName + " is " + (d.Duration - d.Elapsed).ToString());
                    if (d.Elapsed == d.Duration)//Check if we have ticks left
                    {
                        DOTS.Remove(d);//If not, remove the DOT effect                            
                    }
                }
            }
            else
            {
                UpdateTest("No DOTS active");//Trace statement
            }
        }
        catch(Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }

这里代码中调用了UpdateDots方法来使用转:

 public void UseTurn()
    {
        UpdateDots();//Execute any DOT effects
        pause.Tick += new EventHandler(PauseTick);//just a timer to delay each turn, I need to move this
        UpdateTest("Owner is..."+currentTurn.owner.characterName );//Trace statement

        if (currentTurn.owner == c)//Checks if the current turn is by the player (c=player)
        {
            if (currentTurn.ability.DamageOverTime == null)//if we aren't applying a DOT
            {
                if (currentTurn.ability.MultiTarget == false)//If it is single target
                {
                    Attack(c, playerTarget, currentTurn.ability);//Single target attack
                }
                else
                {
                    MultiAttack(playerTargets, currentTurn.ability);//Multi target attack
                }

            }

            lblPlayerTimer.Width = 0;//Resets the label showing the players ATB bar
            currentTurn.owner = null;//Free the current owner to be claimed by other characters
            canAttack = false;//Can no longer attack
            playerTimer.Start();//Start the players timer again
            UpdateAbilities();//Simply used to update the display of abilities
        }
        else if(currentTurn.owner is Enemy)//If an enemy is attacking
        {
            Enemy en = (Enemy)currentTurn.owner;//Get the details of the enemy attacking
            string n = en.characterName;//this was from previous code             
            Label l = new Label();
            l = Controls.Find(en.timer.lblName ,true).FirstOrDefault() as Label;//Get the label being used as the enemy timer
            PickEnemyAttack(en);//Select an attack for the enemy
            Attack(en, c, currentTurn.ability);//Execute attack for enemy
            l.Width = 0;//Reset the enemy ATB bar
            currentTurn.owner = null;//Free up the current turn

            //This part is just used to cause a short delay before the next turn
            pause.Start();
            enemyCanAttack = false;                
            en.timer.Start();               

        }
        ShowTurn();//Updates display of the current turn
        turnCount += 1;
        CheckWinner();//Checks if the player has won, or lost
    }

问题是,当我应用 DOT 时,它会被应用并伤害敌人。它会继续滴答作响,直到 DOT 到期,此时所有计时器都停止。如果没有 DOT 效果,则战斗正常进行。

这似乎是从 DOTS 中删除 DOT 时引起的。它抛出一个无效操作异常,说明该集合已被修改。是不是因为我删除了一个项目,但是因为我在 ForEach 循环中,所以删除这个会使枚举变得混乱?

如您所知,我不是列表专家,也看不到问题所在,我希望有更专业眼光的人可以帮助我解决此问题。

谢谢。

【问题讨论】:

标签: c#


【解决方案1】:

foreach 不允许您修改集合,但您可以使用简单的for 循环手动循环它,然后修改您喜欢的任何内容(因为您可以控制迭代)。所以不要这样:

foreach (DoT d in DOTS)
{
    // do things with d
}

你会得到这个:

for (var i = 0; i < DOTS.Count; i++)
{
    // do things with DOTS[i]
}

请注意,使用此手动控制会增加您的责任。如果你想继续在删除一个元素后迭代DOTS,你需要手动修改i(减一)以反映你现在的事实重新迭代到相同的索引,因为集合已被修改。但是,如果在从集合中删除项目后,您只需退出循环,那么这不是问题。像这样的:

DOTS.Remove(DOTS[i]);
break;

【讨论】:

  • 假设处理项目的顺序并不重要,当您删除项目时必须更改为i 的值的替代方法是从最后一个到第一个迭代项目。这样,当您删除当前项目时,您只会影响那些您已经处理并且不再关心的项目的位置,并且您可以在知道您仍然要处理的项目的索引的情况下继续下一次迭代没有受到影响。
【解决方案2】:

使用 foreach 时不能修改数组。改为使用。

【讨论】:

  • 谢谢,解决了。
【解决方案3】:

要考虑的一种选择是改变:

foreach (DoT d in DOTS)

到:

foreach (DoT d in DOTS.ToList())

这样,您迭代的内容与“原始”DOTS 数组/列表不同。这意味着您可以更改DOTS(添加/删除项目等)。

【讨论】:

    猜你喜欢
    • 2014-08-18
    • 2011-09-20
    • 2018-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-14
    • 1970-01-01
    相关资源
    最近更新 更多