Программируем игры на XNA и MonoGame для Windows 8. Часть 3. Астероиды!

С выходом новой версии MonoGame 3.0.1 информация из статьи могла устареть. Смотрите новые статьи в постах выше.

Теперь мы готовы приступить к разработке первой игры для Windows 8. Пускай она будет про астероиды J

Для начала создадим простой космический фон для игры. Для этого я заготовил картинку с яркой звездой (использование Content Pipeline обсуждалось в прошлой части), нарисуем ее и, заодно, заменим цвет фона.


        Texture2D sunTexture;

        /// <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);

            // TODO: use this.Content to load your game content here

            sunTexture = Content.Load<Texture2D>(«sun»);

            asteroidTexture = Content.Load<Texture2D>(«asteroid»);

        }

        /// <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(new Color(10, 10, 30));

            // TODO: Add your drawing code here

            spriteBatch.Begin();

            spriteBatch.Draw(sunTexture, new Rectangle(GraphicsDevice.Viewport.Width / 2 — 128, GraphicsDevice.Viewport.Height / 2 — 128, 256, 256),

                Color.White);

            base.Draw(gameTime);

        }

 

Заметьте, что я размещаю картинку со звездой в середине экрана, да этого я использую параметры Viewport. И они прекрасно работают в MonoGame. Мы еще погорим об этом позже, но сейчас замечу, что свойства Viewport содержат правильные значения даже при изменении размера окна, которое может происходить, например, при переводе окна в режим Snap.

Получим вот такую картинку:

 

 

 

 

 

 

 

 

 

 

 

Теперь добавим астероиды. Астероиды у нас будут летать и отскакивать от краев экрана.

Нам понадобится новый класс для астероидов.

 

    class Asteroid

    {

        public Vector2 Position { get; set; }

        public Vector2 Speed { get; set; }

        public Game Game { get; set; }

        public void Update(GameTime gameTime)

        {

            float seconds = (float)gameTime.ElapsedGameTime.TotalSeconds;

            Position += Speed * seconds;

            if (Position.X  < 0)

                Speed = new Vector2(-Speed.X, Speed.Y);

            if (Position.Y  < 0)

                Speed = new Vector2(Speed.X, -Speed.Y);

            if (Position.X + 128 > Game.GraphicsDevice.Viewport.Width)

                Speed = new Vector2(-Speed.X, Speed.Y);

            if (Position.Y + 128 > Game.GraphicsDevice.Viewport.Height)

                Speed = new Vector2(Speed.X, -Speed.Y);

        }

    }


Создадим список астероидов и заполним его так, чтобы астероиды появлялись в произвольных местах и имели разную скорость и направление движения.

 

 

List<Asteroid> asteroids = new List<Asteroid>();

 

/// <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()

 

{

 

// TODO: Add your initialization logic here

 

 

 

Random r = new Random();

 

for (int i = 0; i < 10; i++)

 

{

 

var asteroid = new Asteroid();

 

asteroid.Game = this;

 

asteroid.Position = new Vector2(r.Next(GraphicsDevice.Viewport.Width — 400) + 200, r.Next(GraphicsDevice.Viewport.Height — 400) + 200);

 

 

 

var j = r.Next(4);

 

int dx = 1;

 

int dy = 1;

 

 

 

if (j == 1) dx = -1;

 

if (j == 2) {dx = -1; dy = -1;}

 

if (j == 3) dy = -1;

 

 

 

 

 

asteroid.Speed = new Vector2(dx * (r.Next(50) + 20), dy * (r.Next(50) + 20));

 

 

 

 

 

asteroids.Add(asteroid);

 

}

 

 

 

base.Initialize();

 

}


Также нам нужно обновлять состояние астероидов:

 

 

/// <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)

 

{

 

// TODO: Add your update logic here

 

foreach (var asteroid in asteroids)

 

{

 

asteroid.Update(gameTime);

 

}

 

 

 

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(new Color(10, 10, 30));

 

 

 

// TODO: Add your drawing code here

 

spriteBatch.Begin();

 

spriteBatch.Draw(sunTexture, new Rectangle(GraphicsDevice.Viewport.Width / 2 — 128, GraphicsDevice.Viewport.Height / 2 — 128, 256, 256),

 

 

Color.White);

 

 

 

foreach (var asteroid in asteroids)

 

{

 

spriteBatch.Draw(asteroidTexture, asteroid.Position, Color.White);

 

}

 

 

 

spriteBatch.End();

 

 

 

 

 

base.Draw(gameTime);

 

}


Вот что должно получиться:

Немного усложним задачу и сделаем так, чтобы астероиды вращались и имели разный размер. В этом нет ничего сложного, если Вы раньше работали с XNA Framework.

Для это добавим новые свойства в класс в класс астероида:

 

 

    class Asteroid

    {

        public Vector2 Position { get; set; }

        public Vector2 Speed { get; set; }

        public float RotationSpeed { get; set; }

        public float Rotation { get; set; }

        public float Size { get; set; }

        public Game Game { get; set; }

        public int Width

        {

            get { return (int)(128 * Size); }

        }

        public Rectangle Rect

        {

            get

            {

                return new Rectangle((int)Position.X, (int)Position.Y, Width, Width);

            }

        }

        public void Update(GameTime gameTime)

        {

              …

        }

    }


Не забываем, что если мы будем использовать перегруженный вариант SpriteBatch.Draw, который поддерживает вращение (а мы будем использовать именно его), то нам нужно будет изменить и код проверки на выход за границы экрана, так как теперь Position будет содержать координаты центра астероида, а не левого верхнего угла.

 

        public void Update(GameTime gameTime)

        {

            float seconds = (float)gameTime.ElapsedGameTime.TotalSeconds;

            Rotation += RotationSpeed * seconds;

            Position += Speed * seconds;

            if (Position.X — Width / 2 < 0)

                Speed = new Vector2(-Speed.X, Speed.Y);

            if (Position.Y — Width / 2 < 0)

                Speed = new Vector2(Speed.X, -Speed.Y);

            if (Position.X + Width / 2 > Game.GraphicsDevice.Viewport.Width)

                Speed = new Vector2(-Speed.X, Speed.Y);

            if (Position.Y + Width / 2 > Game.GraphicsDevice.Viewport.Height)

                Speed = new Vector2(Speed.X, -Speed.Y);

        }


А для рисования будем использовать уже упомянутую перегрузку SpriteBatch.Draw


        /// <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()

        {

            // TODO: Add your initialization logic here

            Random r = new Random();

            for (int i = 0; i < 10; i++)

            {

                var asteroid = new Asteroid();

                asteroid.Game = this;

                asteroid.Position = new Vector2(r.Next(GraphicsDevice.Viewport.Width — 400) + 200, r.Next(GraphicsDevice.Viewport.Height — 400) + 200);

                asteroid.Size = (float)r.NextDouble() * 1.2f + 0.5f;

                var j = r.Next(4);

                int dx = 1;

                int dy = 1;

                if (j == 1) dx = -1;

                if (j == 2) {dx = -1; dy = -1;}

                if (j == 3) dy = -1;

                asteroid.Speed = new Vector2(dx * (r.Next(50) + 20), dy * (r.Next(50) + 20));

                asteroid.RotationSpeed = r.Next(6) — 3;

                asteroids.Add(asteroid);

            }

            base.Initialize();

        }

А для рисования будем использовать уже упомянутую перегрузку SpriteBatch.Draw

        /// <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(new Color(10, 10, 30));

            // TODO: Add your drawing code here

            spriteBatch.Begin();

            spriteBatch.Draw(sunTexture, new Rectangle(GraphicsDevice.Viewport.Width / 2 — 128, GraphicsDevice.Viewport.Height / 2 — 128, 256, 256),

                Color.White);

            foreach (var asteroid in asteroids)

            {

                spriteBatch.Draw(asteroidTexture, asteroid.Rect, null, Color.White, asteroid.Rotation, new Vector2(asteroid.Width / 2, asteroid.Width / 2), SpriteEffects.None, 0);

            }

            spriteBatch.End();

            base.Draw(gameTime);

        }

Все готово, теперь мы должны увидеть такую картину:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Весь код проекта сейчас должен выглядеть следующим образом:

using Microsoft.Xna.Framework;

using Microsoft.Xna.Framework.Graphics;

using System;

using System.Collections.Generic;

namespace Game1

{

    /// <summary>

    /// This is the main type for your game

    /// </summary>

    public class Game1 : Game

    {

        GraphicsDeviceManager graphics;

        SpriteBatch spriteBatch;

        Texture2D sunTexture;

        Texture2D asteroidTexture;

        List<Asteroid> asteroids = new List<Asteroid>();

        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()

        {

            // TODO: Add your initialization logic here

            Random r = new Random();

            for (int i = 0; i < 10; i++)

            {

                var asteroid = new Asteroid();

                asteroid.Game = this;

                asteroid.Position = new Vector2(r.Next(GraphicsDevice.Viewport.Width — 400) + 200, r.Next(GraphicsDevice.Viewport.Height — 400) + 200);

                asteroid.Size = (float)r.NextDouble() * 1.2f + 0.5f;

                var j = r.Next(4);

                int dx = 1;

                int dy = 1;

                if (j == 1) dx = -1;

                if (j == 2) {dx = -1; dy = -1;}

                if (j == 3) dy = -1;

                asteroid.Speed = new Vector2(dx * (r.Next(50) + 20), dy * (r.Next(50) + 20));

                asteroid.RotationSpeed = r.Next(6) — 3;

                asteroids.Add(asteroid);

            }

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

            // TODO: use this.Content to load your game content here

            sunTexture = Content.Load<Texture2D>(«sun»);

            asteroidTexture = Content.Load<Texture2D>(«asteroid»);

        }

        /// <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)

        {

            // TODO: Add your update logic here

            foreach (var asteroid in asteroids)

            {

                asteroid.Update(gameTime);

            }

            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(new Color(10, 10, 30));

            // TODO: Add your drawing code here

            spriteBatch.Begin();

            spriteBatch.Draw(sunTexture, new Rectangle(GraphicsDevice.Viewport.Width / 2 — 128, GraphicsDevice.Viewport.Height / 2 — 128, 256, 256),

                Color.White);

            foreach (var asteroid in asteroids)

            {

                spriteBatch.Draw(asteroidTexture, asteroid.Rect, null, Color.White, asteroid.Rotation, newVector2(asteroid.Width / 2, asteroid.Width / 2), SpriteEffects.None, 0);

            }

            spriteBatch.End();

            base.Draw(gameTime);

        }

    }

    class Asteroid

    {

        public Vector2 Position { get; set; }

        public Vector2 Speed { get; set; }

        publicfloat RotationSpeed { get; set; }

        publicfloat Rotation { get; set; }

        publicfloat Size { get; set; }

        public Game Game { get; set; }

        publicint Width

        {

            get { return (int)(128 * Size); }

        }

 

        publicRectangle Rect

        {

            get

            {

                returnnewRectangle((int)Position.X, (int)Position.Y, Width, Width);

            }

        }

        public void Update(GameTime gameTime)

        {

            float seconds = (float)gameTime.ElapsedGameTime.TotalSeconds;

            Rotation += RotationSpeed * seconds;

            Position += Speed * seconds;

            if (Position.X — Width / 2 < 0)

                Speed = newVector2(-Speed.X, Speed.Y);

            if (Position.Y — Width / 2 < 0)

                Speed = newVector2(Speed.X, -Speed.Y);

 

            if (Position.X + Width / 2 > Game.GraphicsDevice.Viewport.Width)

                Speed = newVector2(-Speed.X, Speed.Y);

            if (Position.Y + Width / 2 > Game.GraphicsDevice.Viewport.Height)

                Speed = newVector2(Speed.X, -Speed.Y);

        }

    }

}

 

Реклама
Запись опубликована в рубрике Компьютерная графика с метками , , , , , , . Добавьте в закладки постоянную ссылку.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s