名单 删除精灵

问题描述 投票:0回答:4

我有一个填充了我创建的精灵类的列表。我用它来调用更新并在每个上绘制方法。问题是,当一个被销毁时,我需要将其从该列表中删除。当我尝试这样做时,下一个更新或绘制方法会出错:“集合已被修改;枚举操作可能无法执行。”我不知道如何解决这个问题...任何帮助将不胜感激。谢谢!

编辑:好的,源代码是,但要注意,它分散在许多类中,并且它不是很有条理:

好的,所以这里的GameComponent类都来自:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using Microsoft.Xna.Framework; 
using Microsoft.Xna.Framework.Audio; 
using Microsoft.Xna.Framework.Content; 
using Microsoft.Xna.Framework.GamerServices; 
using Microsoft.Xna.Framework.Graphics; 
using Microsoft.Xna.Framework.Input; 
using Microsoft.Xna.Framework.Media; 
using ShipBattle.Classes; 
using ShipBattle.Classes.Ships.Fighters; 

namespace ShipBattle 
{ 
    /// <summary> 
    /// This is a game component that implements IUpdateable. 
    /// </summary> 
    public class BuildMenu : DrawableGameComponent 
    { 
        enum RaceSelected { Tauri, Goauld } 
        RaceSelected raceSelected; 

        Button f302Button, engageButton; 
        ListButton tauriButton, goauldButton; 
        Sprite deathGlider, menu, f302Stats; 

        SpriteBatch spriteBatch; 
        SpriteFont font; 

        /*
         * [0] F302s 
         * [1] Death Gliders
         * [2] Al'kesh's
         * [3] Promethei
         * [4] Ha'tak's
         * [5] Daedaluses
         */ 
        List<int> buildList; 
        List<int> enemyBuildList; 

        public BuildMenu(Game game) 
            : base(game) 
        { 
            spriteBatch = new SpriteBatch(Game.GraphicsDevice); 
            buildList = new List<int>(); 
            enemyBuildList = new List<int>(); 
            raceSelected = RaceSelected.Tauri; 
        } 

        /// <summary> 
        /// Allows the game component to perform any initialization it needs to before starting 
        /// to run.  This is where it can query for any required services and load content. 
        /// </summary> 
        public override void Initialize() 
        { 
            buildList.Add(0); 
            base.Initialize(); 
            enemyBuildList.Add(10); 
        } 

        /// <summary> 
        /// LoadContent will be called once per game and is the place to load 
        /// all of your content. 
        /// </summary> 
        protected override void LoadContent() 
        { 
            //Fonts needed for info 
            font = Game.Content.Load<SpriteFont>(@"Fonts/MenuFont"); 

            //The little enemy ship icons at the top 
            deathGlider = new Sprite(Game.Content.Load<Texture2D>(@"Textures/Menus/Build Menu/Enemy Ships/Death Glider"), new Vector2(30, 85), Color.White, 
                0.0f, Vector2.Zero, 1.0f, SpriteEffects.None, 1.0f); 

            //The actual build buttons 
            f302Button = new Button(new Texture2D[] { Game.Content.Load<Texture2D>(@"Textures/Menus/Build Menu/Player Ships/F-302/F-302") }, 
                new Texture2D[] { Game.Content.Load<Texture2D>(@"Textures/Menus/Build Menu/Player Ships/F-302/F-302 On Over") }, 
                new Texture2D[] { Game.Content.Load<Texture2D>(@"Textures/Menus/Build Menu/Player Ships/F-302/F-302 On Click") }, 
                new Vector2(50, 175), Color.White, 0.0f, Vector2.Zero, 0.5f, SpriteEffects.None, 0.5f, 1, false, false, false, true); 
            f302Button.onClick += new OnClick(f302_onClick); 
            f302Button.onRightClick +=new OnClick(f302Button_onRightClick); 
            f302Stats = new Sprite(Game.Content.Load<Texture2D>(@"Textures/Menus/Build Menu/Player Ships/F-302/Stats"),  
                new Vector2(f302Button.Position.X + (f302Button.Textures[0].Width / 2), f302Button.Position.Y), Color.White, 0.0f, Vector2.Zero, 0.5f, SpriteEffects.None, 1.0f); 

            //The button to change the race 
            tauriButton = new ListButton(Game.Content.Load<Texture2D>(@"Textures/Menus/Build Menu/Race Buttons/Tauri/Tauri"), 
                Game.Content.Load<Texture2D>(@"Textures/Menus/Build Menu/Race Buttons/Tauri/Tauri On Over"), 
                Game.Content.Load<Texture2D>(@"Textures/Menus/Build Menu/Race Buttons/Tauri/Tauri On Click"), 
                Game.Content.Load<Texture2D>(@"Textures/Menus/Build Menu/Race Buttons/Tauri/Tauri Selected"), 
                Game.Content.Load<Texture2D>(@"Textures/Menus/Build Menu/Race Buttons/Tauri/Tauri Selected On Over"), 
                Game.Content.Load<Texture2D>(@"Textures/Menus/Build Menu/Race Buttons/Tauri/Tauri Selected On Click"), 
                new Vector2(512, 710), true); 
            tauriButton.onClick +=new OnClick(tauriButton_onClick); 
            goauldButton = new ListButton(Game.Content.Load<Texture2D>(@"Textures/Menus/Build Menu/Race Buttons/Goauld/Goauld"), 
                Game.Content.Load<Texture2D>(@"Textures/Menus/Build Menu/Race Buttons/Goauld/Goauld On Over"), 
                Game.Content.Load<Texture2D>(@"Textures/Menus/Build Menu/Race Buttons/Goauld/Goauld On Click"), 
                Game.Content.Load<Texture2D>(@"Textures/Menus/Build Menu/Race Buttons/Goauld/Goauld Selected"), 
                Game.Content.Load<Texture2D>(@"Textures/Menus/Build Menu/Race Buttons/Goauld/Goauld Selected On Over"), 
                Game.Content.Load<Texture2D>(@"Textures/Menus/Build Menu/Race Buttons/Goauld/Goauld Selected On Click"), 
                new Vector2(562, 710), true); 
            goauldButton.onClick += new OnClick(goauldButton_onClick); 

            //The menu background 
            menu = new Sprite(Game.Content.Load<Texture2D>(@"Textures/Menus/Build Menu/Build Menu"), Vector2.Zero, Color.White, 0.0f, Vector2.Zero, 1.0f, 
                SpriteEffects.None, 0.0f); 

            //The little button that says, "Engage" at the buttom 
            engageButton = new Button(new Texture2D[] { Game.Content.Load<Texture2D>(@"Textures/Menus/Build Menu/Engage Button/Engage Button") }, 
                new Texture2D[] { Game.Content.Load<Texture2D>(@"Textures/Menus/Build Menu/Engage Button/Engage Button On Over") }, 
                new Texture2D[] { Game.Content.Load<Texture2D>(@"Textures/Menus/Build Menu/Engage Button/Engage Button On Click") }, 
                new Vector2 (1024 - 125, 768 - 125), Color.White, 0.0f, Vector2.Zero, 1.0f, SpriteEffects.None, 1.0f, 1, false, false, false, false); 
            engageButton.onClick += new OnClick(engageButton_onClick); 


            base.LoadContent(); 
        } 

        /// <summary> 
        /// Allows the game component to update itself. 
        /// </summary> 
        /// <param name="gameTime">Provides a snapshot of timing values.</param> 
        public override void Update(GameTime gameTime) 
        { 
            switch (((Game1)Game).Level) 
            { 
                case 1: 
                    Level1Update(); 
                    break; 
            } 

            switch (raceSelected) 
            { 
                case RaceSelected.Tauri: 
                    tauriButton.IsSelected = true; 
                    goauldButton.IsSelected = false; 
                    break; 

                case RaceSelected.Goauld: 
                    tauriButton.IsSelected = false; 
                    goauldButton.IsSelected = true; 
                    break; 
            } 



            tauriButton.Update(); 
            goauldButton.Update(); 
            engageButton.Update(); 

            base.Update(gameTime); 
        } 

        /// <summary> 
        /// This is called when the game should draw itself. 
        /// </summary> 
        /// <param name="gameTime">Provides a snapshot of timing values.</param> 
        public override void Draw(GameTime gameTime) 
        { 
            spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend); 


            switch (((Game1)Game).Level) 
            { 
                case 1: 
                    Level1Draw(); 
                    break; 
            } 

            engageButton.Draw(spriteBatch); 

            spriteBatch.DrawString(font, "$" + ((Game1)Game).player.Money.ToString(), new Vector2(800, 160), Color.White, 0.0f, Vector2.Zero, 1.0f, 
                SpriteEffects.None, 1.0f); 

            menu.Draw(spriteBatch); 

            spriteBatch.End(); 
            base.Draw(gameTime); 
        } 

        /*
         * Event Handlers
         */ 

        //Click the button! 
        private void f302_onClick(object sender, EventArgs e) 
        { 
            if (((Game1)Game).player.Money > 0) 
            { 
                buildList[0]++; 
                ((Game1)Game).player.Money -= 200; 
            } 
        } 

        private void f302Button_onRightClick (object sender, EventArgs e) 
        { 
            if (buildList[0] > 0) 
            { 
                buildList[0]--; 
                ((Game1)Game).player.Money += 200; 
            } 
        } 

        //Click the go button! 
        private void engageButton_onClick (object sender, EventArgs e) 
        { 
            ((Game1)Game).fightScreen.GetShips(enemyBuildList, buildList); 
            ((Game1)Game).gameState = GameState.InGame; 
        } 

        //Change the race 
        private void tauriButton_onClick(object sender, EventArgs e) 
        { 
            raceSelected = RaceSelected.Tauri; 
        } 

        private void goauldButton_onClick(object sender, EventArgs e) 
        { 
            raceSelected = RaceSelected.Goauld; 
        } 

        //Level-specific Update and Draw  
        //functions 
        private void Level1Update() 
        { 
            switch (raceSelected) 
            { 
                case RaceSelected.Tauri: 
                    f302Button.Update(); 

                    break; 

                case RaceSelected.Goauld: 
                    break; 
            } 
        } 

        private void Level1Draw() 
        { 
            switch (raceSelected) 
            { 
                case RaceSelected.Tauri: 
                    //F-302 Stuff 
                    f302Button.Draw(spriteBatch); 
                    f302Stats.Draw(spriteBatch); 
                    spriteBatch.DrawString(font, buildList[0].ToString(), 
                        new Vector2(f302Button.Position.X, (f302Button.Position.Y + (f302Button.Textures[0].Height / 2)) - 35), 
                        Color.White, 0.0f, Vector2.Zero, 1.0f, SpriteEffects.None, 1.0f); 

                    break; 

                case RaceSelected.Goauld: 
                    break; 
            } 

            tauriButton.Draw(spriteBatch); 
            goauldButton.Draw(spriteBatch); 
            deathGlider.Draw(spriteBatch); 

        } 
    } 
} 

这是我的Ship类:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using Microsoft.Xna.Framework; 
using Microsoft.Xna.Framework.Graphics; 
using ShipBattle.Classes.Ships.Projectiles; 
using ShipBattle.Classes.Ships.Projectiles.Tauri; 

namespace ShipBattle.Classes.Ships 
{ 
    public abstract class Ship 
    { 
        public Texture2D Texture { get; set; } 
        public Vector2 Position 
        { 
            get 
            { 
                return _position; 
            } 
            set 
            { 
                _position = value; 
            } 
        } 
        public float Rotation { get; set; } 

        private Vector2 _position; 
        #region Health & Shielding 
        protected float hullIntegrity; 
        protected float shieldStrength; 
        #endregion 
        #region Guns 
        protected List<Vector2> WeaponsEnplacements = new List<Vector2>(); 
        protected List<Vector2> WeaponEmplacementOffsets = new List<Vector2>(); 
        /// <summary> 
        /// The rates of fire for all weapons, represented in terms of the delay between frames 
        /// </summary> 
        protected List<float> WeaponRatesOfFire = new List<float>(); 
        protected abstract List<float> CooldownLeft { get; set; } 
        protected List<Projectile> Projectiles = new List<Projectile>(); 
        protected int[] Ammo; 
        protected int[] Damage; 
        #endregion 
        #region Targeting Logic 
        bool hasTarget = false; 
        int targetHashCode; 
        Vector2 targetShipPosition; 
        Ship target; 
        #endregion 
        #region Physics Stuff 
        float angleA, b, a, speed = 0; 
        double turningRadius = 10 * (Math.PI / 180); 

        //Acceleration 
        protected int mass; // kg 
        protected float force; // kN, thruster power 
        protected float acceleration; // m/s^2 

        //Velocity 
        protected float maxSpeed; // m/s, calculated using 30-second burn 
        protected float initialSpeed = 0; 
        protected float finalSpeed = 0; 
        protected float time = 0.016666f; 
        #endregion 

        public void Update(List<Ship> ships) 
        { 
            if (!hasTarget) 
            { 
                targetHashCode = GetTarget(ships).GetHashCode(); 
                hasTarget = true; 
            } 
            else 
                foreach (Ship ship in ships) 
                    if (targetHashCode == ship.GetHashCode()) 
                    { 
                        CheckTarget(ship); 
                        targetShipPosition = ship.Position; 
                        target = ship; 
                    } 


            //Rotate the sprite using the turning radius 
            Rotation = Utilities.TurnToFace(Position, new Vector2(targetShipPosition.X, targetShipPosition.Y), (float)Rotation, (float)turningRadius); 

            //Move the sprite, using KINEMATIC PHYSICS, ***!!! 
            Move(); 

            //Recalculate the List<Vector2> weapons enplacements based on the current rotation angle 
            RecalculateWeaponsPositions(); 

            //Cooldown the guns 
            Cooldown(); 

            for (int i = 0; i < Projectiles.Count; i++) 
                if (Projectiles[i].Remove) 
                    Projectiles.RemoveAt(i); 

            foreach (Projectile p in Projectiles) 
                p.Update(targetShipPosition); 
        } 

        public void Draw(SpriteBatch spriteBatch) 
        { 
            spriteBatch.Draw(Texture, Position, null, Color.White, Rotation, new Vector2(Texture.Width / 2, Texture.Height / 2), 0.5f, 
                SpriteEffects.None, 0.0f); 

            foreach (Projectile p in Projectiles) 
                p.Draw(spriteBatch); 
        } 

        /// <summary> 
        /// Uses trig and the thruster power to move the ship. b is the y distance to move, a is the x distance to move 
        /// </summary> 
        private void Move() 
        { 
            if (finalSpeed < maxSpeed) 
                finalSpeed = speed + (acceleration * time); 

            angleA = Rotation; 

            b = (float)Math.Cos(angleA) * finalSpeed; 
            a = (float)Math.Sin(angleA) * finalSpeed; 

            _position.Y -= b; 
            _position.X += a; 

            speed = finalSpeed; 
        } 

        /// <summary> 
        /// Acquires the closes enemy ship 
        /// </summary> 
        /// <param name="ships">The ships to search through</param> 
        /// <returns></returns> 
        private Ship GetTarget(List<Ship> ships) 
        { 
            Ship rVal = null; 
            int distance = int.MaxValue; 
            float c; 

            foreach (Ship ship in ships) 
            { 
                c = Vector2.Distance(Position, ship.Position); 

                if (c < distance) 
                    rVal = ship; 
            } 

            return rVal; 
        } 

        /// <summary> 
        /// Reorients the positions of all the weapon positions on this ship 
        /// </summary> 
        private void RecalculateWeaponsPositions() 
        { 
            for (int i = 0; i < WeaponsEnplacements.Count; i++) 
            { 
                WeaponsEnplacements[i] = RotateWeapons(WeaponEmplacementOffsets[i]); 
            } 
        } 

        /// <summary> 
        /// Recalculates the positions of the weapons on this ship based off their default offset and the current angle 
        /// </summary> 
        /// <param name="weapon">The weapon position to recalculate</param> 
        /// <param name="offset">The default offset of that weapon</param> 
        private Vector2 RotateWeapons(Vector2 offset) 
        { 
            Quaternion rotation = Quaternion.CreateFromAxisAngle(Vector3.Backward, (float)Rotation); 
            return Vector2.Transform(offset, rotation) + Position; 
        } 

        protected void Projectile_Hit(Projectile sender, EventArgs e) 
        { 
            sender.Remove = true; 

            if (sender is RailgunRound) 
                if (target.shieldStrength <= 0) 
                    target.hullIntegrity -= sender.Damage; 
                else 
                    target.shieldStrength -= sender.Damage / 4; 
        } 


        /// <summary> 
        /// Checks to see if the target ship is within weapons range 
        /// </summary> 
        /// <param name="target"></param> 
        protected abstract void CheckTarget(Ship target); 

        /// <summary> 
        /// Decrements the cooldown of all weapons 
        /// </summary> 
        protected abstract void Cooldown(); 
    } 
} 

这是产生Projectile类的子类F-302:这是实际发生错误的地方:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using Microsoft.Xna.Framework; 
using Microsoft.Xna.Framework.Content; 
using Microsoft.Xna.Framework.Graphics; 
using ShipBattle.Classes.Ships.Projectiles; 
using ShipBattle.Classes.Ships.Projectiles.Tauri; 

namespace ShipBattle.Classes.Ships.Fighters 
{ 
    public class F302 : Ship 
    { 
        const double missileMinimumFiringArc = Utilities.NINETY_DEGREES / 2; 
        const double missileMaximumFiringArc = Utilities.NINETY_DEGREES + (Utilities.NINETY_DEGREES / 2); 
        const double railgunMinimumFiringArc = 1.5; 
        const double railgunMaximumFiringArc = 1.6; 

        protected override List<float> CooldownLeft { get; set; } 
        private ContentManager content; 

        public F302(Vector2 position, ContentManager Content) 
        { 
            content = Content; 
            Texture = content.Load<Texture2D>(@"Textures\Ships\Tauri\Fighters\F302"); 
            Position = position; 
            Rotation = 0; 
            #region Physics Stuff 
            mass = 19200; 
            force = 76.3f; 

            acceleration = (force * 1000) / mass; 
            maxSpeed = Utilities.GetVelocity(acceleration); 
            #endregion 
            #region Hull & Shielding 
            hullIntegrity = 10; 
            shieldStrength = 0; 
            #endregion 
            #region Weapons!!! 
            /*
             * [0] = Port Missile
             * [1] = Starboard Missile
             * [2] = Port Railgun
             * [3] = Starboard Railgun
             */ 
            Ammo = new int[4]; 
            Damage = new int[4]; 

            //Port Missile 
            WeaponsEnplacements.Add(new Vector2(14, 5)); 
            WeaponEmplacementOffsets.Add(new Vector2(14, 5)); 
            Ammo[0] = 2; 
            Damage[0] = 50; 
            WeaponRatesOfFire.Add(0); 

            //Starboard Missile 
            WeaponsEnplacements.Add(new Vector2(35, 5)); 
            WeaponEmplacementOffsets.Add(new Vector2(35, 5)); 
            Ammo[1] = 2; 
            Damage[1] = 50; 
            WeaponRatesOfFire.Add(0); 

            //Cooldowns = 7.2f 
            //Port Railgun 
            WeaponsEnplacements.Add(new Vector2(24, 0)); 
            WeaponEmplacementOffsets.Add(new Vector2(24, 0)); 
            Ammo[2] = 10000; 
            Damage[2] = 2; 
            WeaponRatesOfFire.Add(30.0f); 

            //Starboard Railgun 
            WeaponsEnplacements.Add(new Vector2(26, 0)); 
            WeaponEmplacementOffsets.Add(new Vector2(26, 0)); 
            Ammo[3] = 10000; 
            Damage[3] = 2; 
            WeaponRatesOfFire.Add(30.0f); 

            CooldownLeft = WeaponRatesOfFire; 
            Projectiles = new List<Projectile>(); 
            #endregion 
        } 

        protected override void CheckTarget(Ship target) 
        { 
            double distance = Vector2.Distance(Position, target.Position); 

            Vector2 vector1 = Vector2.Normalize(Position - target.Position); 
            Vector2 vector2 = new Vector2((float)Math.Cos(Rotation), (float)Math.Sin(Rotation)); 

            double angle = Math.Acos(Vector2.Dot(vector1, vector2)); 

            if (angle > missileMinimumFiringArc && angle < missileMaximumFiringArc) 
            { 
                if (distance < 250) 
                    FireMissiles(); 
            } 

            if (angle > railgunMinimumFiringArc && angle < railgunMaximumFiringArc) 
                FireRailguns(); 
        } 

        protected void FireMissiles() 
        { 
            //Add code so you don't waste all your missiles on one death glider 
        } 

        protected void FireRailguns() 
        { 
            //If the port railgun is ready to fire 
            if (CooldownLeft[2] <= 0) 
            { 
                RailgunRound rr = new RailgunRound(WeaponsEnplacements[0], Rotation, content); 
                rr.hit += new ProjectileHit(Projectile_Hit); 
                Projectiles.Add(rr); 

                //Reset the cooldown 
                CooldownLeft[2] = WeaponRatesOfFire[2]; 
            } 

            //If the port railgun is ready to fire 

            if (CooldownLeft[3] <= 0) 
            { 
                Projectiles.Add(new RailgunRound(WeaponsEnplacements[1], Rotation, content)); 

                //Reset the cooldown 
                CooldownLeft[3] = WeaponRatesOfFire[3]; 
            } 
        } 

        protected override void Cooldown() 
        { 
            for (int f = 0; f < CooldownLeft.Count; f++) 
                CooldownLeft[f]--; 
        } 
    } 
} 

这是导致问题的抛射物类:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using Microsoft.Xna.Framework.Graphics; 
using Microsoft.Xna.Framework; 
using ShipBattle.Classes; 

namespace ShipBattle.Classes.Ships.Projectiles 
{ 
    public abstract class Projectile 
    { 
        protected Texture2D Texture { get; set; } 
        public Vector2 Position 
        { 
            get 
            { 
                return _position; 
            } 

            set 
            { 
                _position = value; 
            } 
        } 
        protected Vector2 _position; 
        protected float Rotation { get; set; } 

        public event ProjectileHit hit; 
        protected Rectangle enemyRect; 

        public float Damage { get; set; } 
        public bool Remove { get; set; } 

        #region Physics Stuff 
        protected float angleA, b, a, speed = 0; 
        protected double turningRadius = MathHelper.ToRadians(360); 

        //Acceleration 
        protected float mass; // kg 
        protected float force; // kN, thruster power 
        protected float acceleration; // m/s^2 

        //Velocity 
        protected float maxSpeed; // m/s, calculated using 30-second burn 
        protected float initialSpeed = 0; 
        protected float finalSpeed = 0.0f; 
        protected float time = 0.016666f; 
        #endregion 

        /// <summary> 
        /// Updates the projectile sprite 
        /// </summary> 
        /// <param name="Pos">The position of the enemy</param> 
        public void Update(Vector2 pos) 
        { 
            enemyRect.X = (int)pos.X; 
            enemyRect.Y = (int)pos.Y; 

            Rotation = Utilities.TurnToFace(Position, new Vector2(pos.X, pos.Y), (float)Rotation, (float)turningRadius); 
            //Is things up? 
            Move(); 

            if (enemyRect.Contains((int)Position.X, (int)Position.Y)) 
                hit(this, new EventArgs()); 
        } 

        public void Draw(SpriteBatch spriteBatch) 
        { 
            spriteBatch.Draw(Texture, Position, null, Color.White, Rotation, new Vector2(Texture.Width / 2, Texture.Height / 2), 1.0f, SpriteEffects.None, 0.1f); 
        } 

        protected void Move() 
        { 
            if (finalSpeed < maxSpeed) 
                finalSpeed = speed + (acceleration * time); 

            angleA = Rotation; 

            b = (float)Math.Cos(angleA) * finalSpeed; 
            a = (float)Math.Sin(angleA) * finalSpeed; 

            _position.Y -= b; 
            _position.X += a; 

            speed = finalSpeed; 
        } 

    } 
} 

最后,这是RailgunRounds,一个射弹的子类,是我目前唯一产生的弹丸,但它导致了这个问题:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using Microsoft.Xna.Framework; 
using Microsoft.Xna.Framework.Graphics; 
using Microsoft.Xna.Framework.Content; 

namespace ShipBattle.Classes.Ships.Projectiles.Tauri 
{ 
    public class RailgunRound : Projectile 
    { 
        public RailgunRound(Vector2 Pos, float Rot, ContentManager content) 
        { 
            this.Texture = content.Load<Texture2D>(@"Textures\Ships\Tauri\Weapons\Railgun Round"); 
            this.Position = Pos; 
            this.Rotation = Rot; 

            mass = 0.5f; 
            force = 5; 
            acceleration = (force * 1000) / mass; 
            maxSpeed = 28.3575f; 

            enemyRect = new Rectangle(); 
            enemyRect.Width = 50; 
            enemyRect.Height = 50; 

            Damage = 2; 
            Remove = false; 
        } 
    } 
} 
c# collections xna sprite
4个回答
2
投票

问题是你正在使用foreach,并且在循环通过你的精灵时,你正在移除一个改变容器的东西,这使得无法正确地向前循环。为了更好地说明导致问题的原因,让我们看一个使用标准for循环的简单示例。

// For this example let's say myContainer has 4 elements in it.
for ( int i = 0; i < myContainer.size(); ++i )
{
    if ( someConditionIsMet )
    {
        myContainer.Remove(i);
    } 
}

在上面的例子中,我们只是说someConditionIsMet每隔一个循环计算为true。所以这是循环的结果:

1.) i = 0, someConditionIsMet is false, so we do nothing.
2.) i = 1, someConditionIsMet is true, so we remove myContainer at index 1. myContainer.size() is now 3.
3.) i = 2, someConditionIsMet is false, so we do nothing.
4.) i = 3, so we do not loop again because 3 is not less than myContainer.size(), which is 3.

看到问题了吗?当i等于1时,我们从myContainer中移除了一些东西,这改变了它的大小,所以当i3时,我们没有机会从myContainer中移除另一个元素。如果您在使用foreach时能够更改容器,则会发生这种情况。

这个问题有几个解决方案:

1.)使用for循环,并从容器的末尾迭代。

// For this example let's say myContainer has 4 elements in it.
for ( int i = myContainer.size() - 1; i >= 0; --i )
{
    if ( someConditionIsMet )
    {
        myContainer.Remove(i);
    } 
}

1.) i = 3, someConditionIsMet is true, so we remove myContainer at index 3. myContainer.size() is now 3. 
2.) i = 2, someConditionIsMet is false, so we do nothing.
3.) i = 1, someConditionIsMet is true, so we remove myContainer at index 1. myContainer.size() is now 2. 
4.) i = 0, someConditionIsMet is false, so we do nothing.

现在你可以看到我们设法正确删除了someConditionIsMet为真的两个条目,而在第一个例子中我们只能删除一个条目。该解决方案提供比下一个示例(下面)更好的性能。

2.)您可以在使用foreach进行迭代时保留需要删除的所有内容的列表,然后使用反向for循环将其删除。这样效率较低,但在某些情况下,您会发现需要在正向迭代循环,因为顺序可能很重要。

List<entries> entriesToRemove = new List<entries>();

foreach(entry in myContainer)
{
    if ( someConditionIsMet )
    {
        entriesToRemove.Add(entry);
    } 
}

for ( int i = entriesToRemove.size() - 1; i > 0; --i )
{
    myContainer.Remove(entriesToRemove[i]);
}
entriesToRemove.clear();

3.)使用for循环,向前迭代,任何时候从容器中删除元素,然后减少迭代器。这已经在本页的另一个答案中已经提到(由Acidic发布),所以我不赞成这个:

for ( int i = 0; i < myContainer.size(); ++i )
{
    if ( someConditionIsMet )
    {
        myContainer.Remove(i);
        --i;
    } 
}

1.) i = 0, someConditionIsMet is false, so we do nothing.
2.) i = 1, someConditionIsMet is true, so we remove myContainer at index 1. myContainer.size() is now 3. We decrement i by 1, so it's now 0 again but will be 1 on the next iteration because of the ++i in the for loop.
3.) i = 1, someConditionIsMet is false, so we do nothing.
4.) i = 2, someConditionIsMet is true, so we remove myContainer at index 2. myContainer.size() is now 2. We decrement i by 1, so it's now 1 again but will be 2 on the next iteration because of the ++i in the for loop so we will not loop again because 2 is not less than myContainer.size(), which is now 2.

3
投票
var list = new List<int> { 1, 2, 3, 4, 5, 6 };
foreach( var i in list )
{
    if( i % 2 == 0 )
    {
        list.Remove(i);  // crash
    }
}

你没有发布任何代码(你应该),所以我猜你有类似的东西。好吧,那不行。如果您更改了集合,那么枚举器的状态可能会被删除,并且它会让您知道。

你可以A)循环并创建一个要删除的项集合,然后在以后删除它们,或者B)使用一个花哨的LINQ方法,如下所示:

var odds = list.Where( i => i % 2 != 0 );

1
投票

我认为简单的解决方案是这样的:

for (int i = 0; i < list.Count; i++)
{
    list[i].Update(); // If the update is made at the same place
    if (list[i].Removed) // A flag that indicates the sprite should be removed
    {
        list.Remove(i);
        i--;
    }
}

0
投票

好吧,据我所知,XNA在同一个线程中运行Update和Draw,但也许对你来说有些不同。您是否可以尝试在Ship类中修改或访问Projectiles列表时添加一些线程同步?

可能会为你解决,然后可能不会:)

public void Update(List<Ship> ships) 
{ 
...

    lock(Projectiles)
    {
        for (int i = 0; i < Projectiles.Count; i++) 
            if (Projectiles[i].Remove) 
                Projectiles.RemoveAt(i); 

        foreach (Projectile p in Projectiles) 
            p.Update(targetShipPosition); 
    }
} 

public void Draw(SpriteBatch spriteBatch) 
{ 
    ...
    lock(Projectiles)
    {
        foreach (Projectile p in Projectiles) 
            p.Draw(spriteBatch); 
    } 
}

另外,你能说明你在哪里得到这个例外吗?它在Ship.cs Update / Draw中吗?哪一个:更新还是绘制?

另外你说Railgun轮导致问题,这是否意味着还有其他类型没有这个问题?

编辑

轻微编辑:您可能希望在您的Projectile类中添加一个条件来检查事件是否已注册,您可以在其中检查以触发命中事件。

       if (enemyRect.Contains((int)Position.X, (int)Position.Y)) 
           if(hit != null)
                hit(this, new EventArgs()); 

另外,在你发射其他轨道炮的地方,你不添加任何事件:这是故意的吗?那些射弹不会被移除,但我几乎认为这不会导致问题......

Aaditi

您是否考虑为您在所有类中访问的纹理编写某种公共存储库?

您可以创建一个辅助类的静态方法,该方法为您提供基于键的纹理引用,它在私有字典中保存。在加载关卡时填充这个,然后当你需要单独的纹理时,你只需要调用它们。这样你就不必在整个地方传递ContentManager,并且每次发射子弹时都不必加载纹理。

class Texture2DRepository
{
    static Dictionary<String, Texture2D> _textures = new Dictionary<String, Texture2D>();
    public static void Add(String key, Texture2D texture)
    {
        //maybe add logic to replace entries with same key
        Dictionary.Add(key, texture);
    }
    public static Texture2D Get(String key)
    {
        return Dictionary[key];
    }
}

当你绘制你的射弹时,假设你事先添加了纹理,你只需要打电话

Texture2DRepository.Get("RailgunSprite");

Aadita

移动精灵时,您不必计算角度,然后重新计算X和Y位置,您可以使用向量为您执行此操作:

public void Update(Vector2 pos) 
{ 
    //you get a vector pointing from position to enemy ship position
    Vector2 direction = pos - Position; 

    if (speed < maxSpeed) 
        speed += (acceleration * time); 

    //You add to your position the direction vector modified to the length of speed.
    Position += direction.Normalize() * speed;

    if (enemyRect.Contains((int)Position.X, (int)Position.Y)) 
        if(hit != null)
            hit(this, new EventArgs()); 
} 

请注意,你写它的方式,这些是归位射弹(如果你给pos论证赋予不同的值,射弹将会跟随)。

© www.soinside.com 2019 - 2024. All rights reserved.