1

邪魔にならない最初のことです。(非常に) 明らかな何かが欠けていたら申し訳ありません。

とにかく、私は XNA で小惑星のクローンに取り組んできましたが、[デバッグの開始] ボタンをクリックしても、何らかの理由で開始されないことがありました。この問題を AsteroidsManager クラスに追跡しました。このクラスは、生成する初期小惑星の int、最小速度と最大速度、回転速度、および小惑星と粒子の 2 つのテクスチャ リストを受け取ります。今奇妙さ:

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 以上に設定すると、デバッガーで開始されず、フォルダーから開始しようとすると、タスク マネージャーにプロセスが表示されますが、何も起こりません。または、起動時にクラッシュするだけです。正直なところ、何が原因なのかわかりません。必要に応じて喜んでコードを提供しますが、関連性があると思われるものは次のとおりです。

完全な AsteroidManager:

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
}

Game1 引き分け:

    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 に答える