1

首先要让路,对不起,如果我遗漏了一些(非常)明显的东西,我对此还是有点陌生​​。

无论如何,我一直在 XNA 中研究小行星克隆,由于某种原因,如果我点击 Start Debugging 按钮,它偶尔会无法启动。我将问题跟踪到我的 AsteroidsManager 类,该类需要一个 int 的初始小行星来生成、最小和最大速度和旋转速度,以及小行星和粒子的两个纹理列表。现在奇怪的是:

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

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

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,它永远不会在调试器中启动,如果我尝试从文件夹启动它,进程会出现在任务管理器中,但什么也没有发生。或者它只是在启动时崩溃。老实说,我不知道是什么原因造成的,如果需要,我很乐意提供任何代码,但这是我认为相关的:

完整的小行星管理器:

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
    }
}

完整的游戏对象:

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平局:

    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更新:

    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);
    }
4

1 回答 1

4

我猜你的重叠代码永远不会找到放置小行星的位置。它进入一个永不退出的近乎无限(或者如果空间被正确覆盖,则可能是无限)循环。您可以使用给出多次尝试的计数器,它只是“放弃”。或者你可以增加他们可以生成的游戏区域的最大大小,或者减小他们的大小;这将减少这种无限循环发生的可能性,但在有足够多的小行星的情况下不会使其成为不可能。

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!
}

即使你通过增加游戏大小或减小小行星大小来“修复”这个问题,我仍然建议你让它运行最大次数以避免无限循环。

于 2013-03-28T22:31:53.740 回答