1

こんにちは私はxnaに不慣れで、あなたの小さな船が動き回り、小惑星が上から下に落ちるのを避ける簡単なゲームを作ろうとしています。船を動かして小惑星を1つ落下させましたが、同じテクスチャからたくさんの小惑星を落下させる方法と、間隔を置いて落下させる方法がわかりません。これはこれまでの私の小惑星のクラスです:

namespace Asteroids
{
class Asteroids
{
    Texture2D AsteroidTexture;
    Vector2 Position;
    Random random = new Random();
    float AsteroidSpeed = 5;

    public void Initialize()
    {
        Position.Y = 0;
        Position.X = random.Next(0, 1000);
    }

    public void Update()
    {
        Position.Y += AsteroidSpeed;
        if (Position.Y > 600)
        {
            Position.Y = 0;
            Position.X = random.Next(0, 1000);
        }
    }

    public void Load_Content(ContentManager Content)
    {
        AsteroidTexture = Content.Load<Texture2D>("asteroid");
    }

    public void Draw(SpriteBatch SpriteBatch)
    {
        SpriteBatch.Draw(AsteroidTexture, Position, Color.White);
    }
}
}

そしてこれは私のGame1クラスです:

namespace Asteroids
{
public class Game1 : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;
    KeyboardState keyboardState;

    Ship ship;
    Asteroids asteroids;

    public Game1()
    {
        graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
    }

    /// <summary>
    /// Allows the game to perform any initialization it needs to before starting to run.
    /// This is where it can query for any required services and load any non-graphic
    /// related content.  Calling base.Initialize will enumerate through any components
    /// and initialize them as well.
    /// </summary>
    protected override void Initialize()
    {
        ship = new Ship();
        asteroids = new Asteroids();

        asteroids.Initialize();

        this.graphics.PreferredBackBufferWidth = 1000;
        this.graphics.PreferredBackBufferHeight = 600;
        //this.graphics.IsFullScreen = true;
        this.graphics.ApplyChanges();

        base.Initialize();
    }

    /// <summary>
    /// LoadContent will be called once per game and is the place to load
    /// all of your content.
    /// </summary>
    protected override void LoadContent()
    {
        // Create a new SpriteBatch, which can be used to draw textures.
        spriteBatch = new SpriteBatch(GraphicsDevice);

        ship.Load_Content(this.Content);
        asteroids.Load_Content(this.Content);
    }

    /// <summary>
    /// UnloadContent will be called once per game and is the place to unload
    /// all content.
    /// </summary>
    protected override void UnloadContent()
    {
        // TODO: Unload any non ContentManager content here
    }

    /// <summary>
    /// Allows the game to run logic such as updating the world,
    /// checking for collisions, gathering input, and playing audio.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    protected override void Update(GameTime gameTime)
    {
        // Allows the game to exit
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            this.Exit();
        keyboardState = Keyboard.GetState();
        if (keyboardState.IsKeyDown(Keys.Escape))
            this.Exit();

        ship.Update();
        asteroids.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>
    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.Black);

        spriteBatch.Begin();
        asteroids.Draw(this.spriteBatch);
        ship.Draw(this.spriteBatch);
        spriteBatch.End();

        base.Draw(gameTime);
    }
}
}

前もって感謝します!

4

2 に答える 2

6

重要:私のコードの大文字と小文字の区別と複数形に細心の注意を払ってください。大事です。


最初に、小惑星ごとにどのデータが存在する必要があるか、およびすべての小惑星間でどのデータを共有する必要があるかを決定する必要があります。また、プログラム全体でどのデータを共有する必要があるか。

  • 小惑星の位置は、すべての小惑星に存在する必要があります
  • 小惑星のテクスチャは、小惑星のすべてのインスタンス間で共有できます
  • クラスのインスタンスは、Randomプログラムごとに1回存在する必要があります(技術的にはスレッドごとに1回ですが、今のところは気にしないでください)。

これがコードでの様子です。(読みやすくするために、クラスの内容を回答全体に広げていることに注意してください。各セクションのコードを自分でマージする必要があります。)

class Asteroid
{
    // Static variables are shared between all instances of a class
    static Texture2D asteroidTexture;

    // Non-static variables exist once for each instance of the class
    Vector2 position;

    // Constants are fixed at compile time and cannot be modified
    const float asteroidSpeed = 50; // units per second
}

// A static class can only contain static variables (and constants)
// (You can't create an instance of it, so you can't have variables.)
static class Shared
{
    // "readonly" prevents anyone from writing to a field after it is initialised
    public static readonly Random Random = new Random();
}

次に、そのデータを初期化および変更する方法を決定する必要があります。

(上記の)クラスShared.Randomの新しいインスタンスにすでに初期化されていることに注意してください。Random(実際の初期化は、最初に使用される前にランタイムによって自動的に行われます。)

まず、テクスチャの読み込みを見てみましょう。

class Asteroid
{
    // Static methods can only act on static data
    public static void LoadContent(ContentManager content)
    {
        asteroidTexture = content.Load<Texture2D>("asteroid");
    }
}

public class Game1 : Microsoft.Xna.Framework.Game
{
    protected override void LoadContent()
    {
        Asteroid.LoadContent(Content);
    }
}

Game1.LoadContentプログラムの開始時に一度呼び出されることがわかっているので、を呼び出すのに適した場所Asteroid.LoadContentです。


次に、各小惑星のインスタンスごとのデータを見てみましょう。各小惑星は、最初に作成されたときにその位置を設定する必要があります。これを行うには、Asteroidクラスにコンストラクターを指定します。コンストラクターは、小惑星を作成するときにいつでも呼び出します。

class Asteroid
{
    public Asteroid(Vector2 position)
    {
        this.position = position;
    }
}

次に、小惑星クラスの複数のインスタンスを作成して保存します。

ループを使用して複数のインスタンスを作成します。各インスタンスは、画面の幅内にランダムなX位置を持ちます。そして、リストを使用して、作成時にそれらを保存します。

public class Game1 : Microsoft.Xna.Framework.Game
{
    List<Asteroid> asteroids = new List<Asteroid>();

    protected override void Initialize()
    {
        int screenWidth = GraphicsDevice.Viewport.Width;

        // Create 15 asteroids:
        for(int i = 0; i < 15; i++)
        {
            float xPosition = Shared.Random.Next(0, screenWidth);
            asteroids.Add(new Asteroid(new Vector2(xPosition, 0)));
        }
    }
}

最後に、以前に作成したリスト内のすべての小惑星を更新して描画します。これは、小惑星のリストをループし、各インスタンスでDraworメソッドを呼び出すという単純な問題です。Update

class Asteroid
{
    public void Update(float elapsedSeconds)
    {
        position.Y += asteroidSpeed * elapsedSeconds;
    }

    public void Draw(SpriteBatch spriteBatch)
    {
        spriteBatch.Draw(asteroidTexture, position, Color.White);
    }
}

public class Game1 : Microsoft.Xna.Framework.Game
{
    protected override void Update(GameTime gameTime)
    {
        foreach(Asteroid asteroid in asteroids)
        {
            asteroid.Update((float)gameTime.ElapsedGameTime.TotalSeconds);
        }
    }

    protected override void Draw(GameTime gameTime)
    {
        foreach(Asteroid asteroid in asteroids)
        {
            asteroid.Draw(spriteBatch);
        }
    }
}

メソッドの経過時間を考慮に入れる方法に注意してくださいUpdate。そのため、小惑星の速度について「1秒あたりの単位数」というコメントを付けました。

これは、ゲームのロジックがゲームの実行フレームレートに依存しないため、良い習慣です。(XNAは、デフォルトでは固定フレームレートで実行されますが、とにかくフレームレートに依存しないコードを作成することをお勧めします。)


Asteroidこの時点で、クラスとそれを使用するための完全なコードができているはずです。いくつか追加してみましょう:

あなたは小惑星を間隔を置いて落下させる方法を知りたいと思っていました。これを行うには、時間が経過するにつれて時間を蓄積する必要があり、あるしきい値に達するたびに、新しい小惑星を作成してタイマーをリセットします。

public class Game1 : Microsoft.Xna.Framework.Game
{
    float asteroidSpawnTimer;
    const float asteroidSpawnDelay = 5; // seconds

    void CreateAsteroid()
    {
        // This is the same code as I used in Initialize().
        // Duplicate code is extremely bad practice. So you should now modify 
        // Initialize() so that it calls this method instead.

        int screenWidth = GraphicsDevice.Viewport.Width;
        float xPosition = Shared.Random.Next(0, screenWidth);
        asteroids.Add(new Asteroid(new Vector2(xPosition, 0)));
    }

    protected override void Update(GameTime gameTime)
    {
        // ... other stuff ...

        asteroidSpawnTimer += (float)gameTime.ElapsedGameTime.TotalSeconds;
        if(asteroidSpawnTimer >= asteroidSpawnDelay)
        {
            asteroidSpawnTimer -= asteroidSpawnDelay; // subtract "used" time
            CreateAsteroid();
        }
    }
}

小惑星を追加している間(画面の下部に到達したときなど)、不要になった古い小惑星を削除することをお勧めします。

まず第一に、あなたは小惑星の位置に外部からアクセスするための何らかの方法が必要です。アクセス修飾子を指定しなかったため、フィールドはデフォルトでprivateになり、クラスpositionの外部からアクセスすることはできません。Asteroidしかし、外部からアクセスできる公共のプロパティを作成することはできます。これにより、次のような位置が提供されます。

class Asteroid
{
    public Vector2 Position { get { return position; } }
}

position(フィールドを完全に取り除き、プライベートセッターで自動実装されたプロパティを使用することを検討することをお勧めします。)

小惑星と相互作用させたい場合は、これと同じ方法を使用して、船のオブジェクトのプロパティにアクセスすることをお勧めします。単純なゲームの場合、この種のオブジェクト間ロジックをで実行しても問題ありませんGame1.Updateここで詳細に説明します)。

とにかく、アクセスする方法ができAsteroid.Positionたので、画面から落ちた小惑星を取り除くことができます。

public class Game1 : Microsoft.Xna.Framework.Game
{
    protected override void Update(GameTime gameTime)
    {
        // ... other stuff ...

        int screenHeight = GraphicsDevice.Viewport.Height;

        // Loop backwards through all asteroids.
        //
        // Note that you must iterate backwards when removing items from a list 
        // by index, as removing an item will change the indices of all items in
        // the list following (in a forward order) the item that you remove.
        for(int i = asteroids.Count - 1; i >= 0; i--)
        {
            if(asteroids[i].Position.Y > screenHeight)
                asteroids.RemoveAt(i);
        }
    }
}

さて、これは驚くほど長い答えでした-私は(初心者レベルで)多くのトピックをカバーしました。ですから、私が触れていない詳細がたくさんあります。したがって、何かについて混乱している場合は、そのことを説明するために使用したキーワードを探して検索してください。MSDNに関するMicrosoftのドキュメントは、特に見るのに適した場所です(XNAのドキュメントは次のとおりです)。

于 2012-07-08T13:19:34.200 に答える
0

それらを配列で定義することができ、Updateメソッドから一定の間隔で、gameTimeに基づいて新しい小惑星を追加できます。これがお役に立てば幸いです

于 2012-07-08T09:57:05.053 に答える