关于C#:XNA游戏无法在非常奇怪的条件下启动

XNA game not launching under very strange conditions

首先要解决的问题,对不起,如果我(非常)遗漏了一些东西,那么我仍然有点陌生。

无论如何,我一直在XNA中研究小行星克隆,由于某些原因,如果我按"开始调试"按钮,它有时可能无法启动。我将问题跟踪到我的AsteroidsManager类,该类使用一小部分初始小行星来生成,最小和最大速度以及旋转速度,以及两个用于小行星和粒子的纹理列表。现在很奇怪:

1
temp = new AsteroidManager(1, 20, 50, 1, 2, asteroids, particles, true); //With this constructor, the game always starts fine...

但是如果我增加初始小行星的数量:

1
temp = new AsteroidManager(10, 20, 50, 1, 2, asteroids, particles, true); //This seems to start about 1/3 times in the Visual Studio debugger, but if I launch it without debugging or from the bin folder, it works fine.

最后,如果我将小行星设置为大于约20,则它永远不会在调试器中启动,并且如果我尝试从文件夹中启动它,则该过程将出现在任务管理器中,但是什么也没有发生。否则它只会在启动时崩溃。老实说,我不知道是什么原因造成的,并且很乐意在需要时提供任何代码,但是我认为这是相关的:

完整的AsteroidManager:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
public class AsteroidManager
{
    #region Declarations

    public List<GameObject> Asteroids { get; set; }
    public bool RegenerateAsteroids { get; set; }

    public readonly int InitialAsteroids;
    public readonly float MinVelocity;
    public readonly float MaxVelocity;
    public readonly float MinRotationalVelocity; //in degrees
    public readonly float MaxRotationalVelocity; //in degrees
    public readonly List<Texture2D> Textures;
    public readonly List<Texture2D> ExplosionParticleTextures;

    List<ParticleEmitter> emitters;
    Random rnd;

    const int MINPARTICLES = 50;
    const int MAXPARTICLES = 200;
    const int PARTICLEFTL = 40;

    #endregion

    public AsteroidManager(
        int initialAsteroids,
        float minVel,
        float maxVel,
        float minRotVel,
        float maxRotVel,
        List<Texture2D> textures,
        List<Texture2D> explosionParticleTextures,
        bool regenAsteroids)
    {
        rnd = new Random();

        InitialAsteroids = initialAsteroids;
        MinVelocity = minVel;
        MaxVelocity = maxVel;
        MinRotationalVelocity = minRotVel;
        MaxRotationalVelocity = maxRotVel;
        Textures = textures;
        ExplosionParticleTextures = explosionParticleTextures;
        RegenerateAsteroids = regenAsteroids;

        Asteroids = new List<GameObject>();
        emitters = new List<ParticleEmitter>();

        for (int i = 0; i < InitialAsteroids; i++)
            addAsteroid();
    }

    public void Update(GameTime gameTime)
    {
        for (int i = 0; i < Asteroids.Count; i++)
            Asteroids[i].Update(gameTime);

        for (int i = 0; i < emitters.Count; i++)
            emitters[i].Update(gameTime);

        if (Asteroids.Count < InitialAsteroids && RegenerateAsteroids)
            addAsteroid();
    }

    public void Draw(SpriteBatch spriteBatch)
    {
        for (int i = 0; i < Asteroids.Count; i++)
            Asteroids[i].Draw(spriteBatch);

        for (int i = 0; i < emitters.Count; i++)
            emitters[i].Draw(spriteBatch);
    }

    public void DestroyAsteroid(GameObject asteroid)
    {
        int x = rnd.Next(MINPARTICLES, MAXPARTICLES);
        List<Color> colors = new List<Color>();
        colors.Add(Color.White);

        emitters.Add(new ParticleEmitter( //TODO: Test
            x,
            asteroid.WorldCenter,
            ExplosionParticleTextures,
            colors,
            PARTICLEFTL,
            true,
            1,
            x,
            1f,
            0.3f,
            0f,
            180f));

        Asteroids.Remove(asteroid);
    }

    protected void addAsteroid()
    {
        GameObject tempAsteroid;
        bool isOverlap = false;

        do //Do-While to ensure that the asteroid gets generated at least once
        {
            Texture2D text = Textures.PickRandom<Texture2D>();

            float rot = MathHelper.ToRadians((float)rnd.NextDouble(0f, 359f));
            float rotVel = MathHelper.ToRadians((float)rnd.NextDouble(MinRotationalVelocity, MaxRotationalVelocity));

            int colRadius = (((text.Width / 2) + (text.Height / 2)) / 2); //Get the mean of text's height & width

            Vector2 vel = Vector2.Multiply( //calculate a random velocity
                rot.RotationToVectorFloat(),
                (float)rnd.NextDouble(MinVelocity, MaxVelocity));

            Vector2 worldPos = new Vector2(
                rnd.Next(Camera.WorldRectangle.X, Camera.WorldRectangle.Width),
                rnd.Next(Camera.WorldRectangle.Y, Camera.WorldRectangle.Height));

            tempAsteroid = new GameObject( //init a temporary asteroid to check for overlaps
                text, worldPos, vel, Color.White, false, rot, rotVel, 1f, 0f, colRadius);

            foreach (GameObject asteroid in Asteroids)
            {
                if (tempAsteroid.BoundingBox.Intersects(asteroid.BoundingBox))
                {
                    isOverlap = true;
                    break;
                }
            }

        } while (isOverlap); //if overlapping, loop

        Asteroids.Add(tempAsteroid); //add the temp asteroid
    }
}

完整GameObject:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
public class GameObject
{
    #region Declarations

    public Texture2D Texture { get; set; }
    public Vector2 Origin { get; set; }
    public Color TintColor { get; set; }
    public float Rotation { get; set; } //radians
    public float RotationalVelocity { get; set; }
    public float Scale { get; set; }
    public float Depth { get; set; }
    public bool Active { get; set; }
    public SpriteEffects Effects { get; set; }

    public Vector2 WorldLocation { get; set; }
    public Vector2 Velocity { get; set; }

    public int CollisionRadius { get; set; } //Radius for bounding circle collision
    public int BoundingXPadding { get; set; }
    public int BoundingYPadding { get; set; } //Padding for bounding box collision

    public int TotalFrames
    {
        get //simple get
        { return totalFrames; }
        set //check if given totalFrames is in possible range
        {
            if (value <= (Rows * Columns))
                totalFrames = value;
            else
                throw new ArgumentOutOfRangeException();
        }
    } //Used in spritesheet animation
    private int totalFrames;

    public int CurrentFrame
    {
        get { return currentFrame; }
        set
        {
            currentFrame = (int)MathHelper.Clamp(value, 0, totalFrames);
        }
    }
    private int currentFrame;

    public int Rows { get; set; }
    public int Columns { get; set; }
    public bool Animating { get; set; }

    public float RotationDegrees
    {
        get { return MathHelper.ToDegrees(Rotation); }
        set { Rotation = MathHelper.ToRadians(value); }
    }
    public float RotationVelocityDegrees
    {
        get { return MathHelper.ToDegrees(RotationalVelocity); }
        set { RotationalVelocity = MathHelper.ToRadians(value); }
    }

    public const float VELOCITYSCALAR = 1.0f / 60.0f; //Default to 60fps standard movement

    #endregion

    #region Properties

    public int GetWidth { get { return Texture.Width / Columns; } } //Width of a frame
    public int GetHeight { get { return Texture.Height / Rows; } } //Height of a frame
    public int GetRow { get { return (int)((float)CurrentFrame / (float)Columns); } } //Current row
    public int GetColumn { get { return CurrentFrame % Columns; } } //Current column

    public Vector2 SpriteCenter
    { get { return new Vector2(GetWidth / 2, GetHeight / 2); } } //Get this Sprite's center

    public Rectangle WorldRectangle //get rectangle in world coords with width of sprite
    {
        get
        {
            return new Rectangle(
                (int)WorldLocation.X,
                (int)WorldLocation.Y,
                GetWidth,
                GetHeight);
        }
    }

    public Rectangle BoundingBox //get bounding box for use in collision detection
    {
        get
        {
            return new Rectangle( //Get bounding box with respect to padding values
                (int)WorldLocation.X + BoundingXPadding,
                (int)WorldLocation.Y + BoundingYPadding,
                GetWidth - (BoundingXPadding * 2),
                GetHeight - (BoundingYPadding * 2));
        }
    }

    public Vector2 ScreenLocation
    { get { return Camera.GetLocalCoords(WorldLocation); } } //get screen coordinates

    public Rectangle ScreenRectangle
    { get { return Camera.GetLocalCoords(WorldRectangle); } } //get screen rectangle

    public Vector2 WorldCenter
    {
        get { return WorldLocation + SpriteCenter; }
        set { WorldLocation = value - SpriteCenter; }
    } //gets/sets the center of the sprite in world coords

    public Vector2 ScreenCenter
    { get { return Camera.GetLocalCoords(WorldLocation + SpriteCenter); } } //returns the center in screen coords

    #endregion

    public GameObject( //main constructor, /w added optional parameters and call to SpriteBase init
        Texture2D texture,
        Vector2 worldLocation,
        Vector2 velocity,
        Color tintColor,
        bool animating = false,
        float rotation = 0f, //default to no rotation
        float rotationalVelocity = 0f,
        float scale = 1f, //default to 1:1 scale
        float depth = 0f, //default to 0 layerDepth
        int collisionRadius = 0, //collision radius used in bounding circle collision, default to 0 or no bounding circle
        int xPadding = 0, //amount of x padding, used in bounding box collision, default to 0, or no bounding box
        int yPadding = 0, //amount of y padding, used in bounding box collision, default to 0, or no bounding box
        SpriteEffects effects = SpriteEffects.None,
        int totalFrames = 0,
        int rows = 1,
        int columns = 1)

    {
        if (texture == null) { throw new NullReferenceException("Null texture reference."); }
        Texture = texture; //assign parameters
        WorldLocation = worldLocation;
        TintColor = tintColor;
        Rotation = rotation;
        RotationalVelocity = rotationalVelocity;
        Scale = scale;
        Depth = depth;
        Effects = effects;
        Velocity = velocity;
        Animating = animating;
        Active = true;

        BoundingXPadding = xPadding; BoundingYPadding = yPadding; CollisionRadius = collisionRadius; //assign collision data
        Rows = rows; Columns = columns; this.TotalFrames = totalFrames; //assign animation data

        Origin = SpriteCenter; //assign origin to the center of a frame
    }

    #region Methods

    public virtual void Update(GameTime gameTime)
    {
        if (Active) //if object is active
        {
            WorldLocation += Velocity * (1f / 60f);
            Rotation += RotationalVelocity; //Rotate according to the velocity
            //Move by Velocity times a roughly 60FPS scalar

            if (TotalFrames > 1 && Animating)
            {
                CurrentFrame++;
                if (CurrentFrame >= TotalFrames)
                    CurrentFrame = 0; //Loop animation
            }

            if (Camera.IsObjectInWorld(this.WorldRectangle) == false)
            {
                if (Camera.LOOPWORLD) //if world is looping and the object is out of bounds
                {
                    Vector2 temp = WorldCenter; //temporary Vector2 used for updated position

                    //X-Axis Component
                    if (WorldCenter.X > Camera.WorldRectangle.Width)
                        temp.X = Camera.WorldRectangle.X - (GetWidth / 2); //If X is out of bounds to the right, move X to the left side
                    if (WorldCenter.X < Camera.WorldRectangle.X)
                        temp.X = Camera.WorldRectangle.Width + (GetWidth / 2); //If X is out of bound to the left, move X to the right side

                    //Y-Axis Component
                    if (WorldCenter.Y > Camera.WorldRectangle.Height)
                        temp.Y = Camera.WorldRectangle.Y - (GetHeight / 2); //If Y is out of bounds to the bottom, move Y to the top
                    if (WorldCenter.Y < Camera.WorldRectangle.Y)
                        temp.Y = Camera.WorldRectangle.Height + (GetHeight / 2); //If Y is out of bounds to the top, move Y to the bottom

                    WorldCenter = temp; //Assign updated position
                }

                if (Camera.LOOPWORLD == false)
                {
                    Active = false; //if the object is outside the world but the LOOPWORLD constant is false, set inactive
                }
            }
        }
    }

    public virtual void Draw(SpriteBatch spriteBatch)
    {
        if (Active)
        {
            if (TotalFrames > 1 && Camera.IsObjectVisible(WorldRectangle)) //if multi-frame animation & object is visible
            {
                Rectangle sourceRectangle = new Rectangle(GetWidth * GetColumn,
                    GetHeight * GetRow, GetWidth, GetHeight); //get source rectangle to use

                spriteBatch.Draw(
                    Texture,
                    ScreenCenter,
                    sourceRectangle, //use generated source rectangle
                    TintColor,
                    Rotation,
                    Origin,
                    Scale,
                    Effects,
                    Depth);
            }
            else //if single frame sprite
            {
                if (Camera.IsObjectVisible(WorldRectangle)) //check if sprite is visible to camera
                {
                    spriteBatch.Draw(
                        Texture,
                        ScreenCenter, //center of the sprite in local coords
                        null, //full sprite
                        TintColor,
                        Rotation,
                        Origin,
                        Scale,
                        Effects, //spriteeffects
                        Depth); //layerdepth
                }
            }
        }
    }

    public bool IsBoxColliding(Rectangle obj) //bounding box collision test
    {
        return BoundingBox.Intersects(obj);
    }

    public bool IsBoxColliding(GameObject obj) //overload of previous which takes a GameObject instead of a rectangle
    {
        if (BoundingBox.Intersects(obj.BoundingBox))
            return true;
        else
            return false;
    }

    public bool IsCircleColliding(Vector2 objCenter, float objRadius)
    {
        if (Vector2.Distance(WorldCenter, objCenter) <
            (CollisionRadius + objRadius))  //if the distance between centers is greater than the sum
            return true;                    //of the radii, collision has occurred
        else
            return false; //if not, return false
    }

    public bool IsCircleColliding(GameObject obj) //overload of previous which takes a GameObject instead of loose values
    {
        if (Vector2.Distance(this.WorldCenter, obj.WorldCenter) <
            (CollisionRadius + obj.CollisionRadius))
            return true;
        else
            return false;
    }

    public void RotateTo(Vector2 point) //rotates the GameObject to a point
    {
        Rotation = (float)Math.Atan2(point.Y, point.X);
    }

    protected Vector2 rotationToVector()
    {
        return Rotation.RotationToVectorFloat();
    } //local version of extension method

    #endregion
}

游戏1平局:

1
2
3
4
5
6
7
8
9
10
11
    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);
        spriteBatch.Begin(); //BEGIN SPRITE DRAW

        fpsDisplay.Draw(spriteBatch);
        temp.Draw(spriteBatch);

        spriteBatch.End(); //END SPRITE DRAW
        base.Draw(gameTime);
    }

游戏1更新:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    protected override void Update(GameTime gameTime)
    {
        InputHandler.Update(); //update InputHandler

        if (InputHandler.IsKeyDown(Keys.Left))
            Camera.Position += new Vector2(-3f, 0f);
        if (InputHandler.IsKeyDown(Keys.Right))
            Camera.Position += new Vector2(3f, 0f);
        if (InputHandler.IsKeyDown(Keys.Up))
            Camera.Position += new Vector2(0f, -3f);
        if (InputHandler.IsKeyDown(Keys.Down))
            Camera.Position += new Vector2(0f, 3f);

        fpsDisplay.Value = (int)Math.Round(1 / gameTime.ElapsedGameTime.TotalSeconds, 0);
        //calculate framerate to the nearest int

        temp.Update(gameTime);

        base.Update(gameTime);
    }

我想您重叠的代码永远找不到找到放置小行星的地方。它进入永不退出的近无限(如果适当覆盖空间,则可能是无限)循环。您可以使用经过多次尝试的计数器,它只是"放弃"。或者,您可以增加它们可以产生的游戏区域的最大大小,或减小它们的大小;这将减少发生这种无限循环的可能性,但是如果有足够的小行星,这不会使它不可能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int attempts = 0;

do //Do-While to ensure that the asteroid gets generated at least once
{
    attempts++;
    ...
    foreach (GameObject asteroid in Asteroids)
    {
        if (tempAsteroid.BoundingBox.Intersects(asteroid.BoundingBox))
        {
            isOverlap = true;
            break;
        }
    }

} while (isOverlap && attempts < 20); //if overlapping, loop, give up after 20 tries

if (attempts == 20)
{
    //log it! Or fix it, or something!
}

即使您通过增加游戏大小或减小小行星大小来"修复"此问题,我仍然建议您使其运行最多次数以避免无限循环。