Опубликован: 05.08.2010 | Уровень: специалист | Доступ: платный
Самостоятельная работа 1:

Компьютерная 2D-графика в Microsoft XNA Game Studio 3.0

Самостоятельная работа 1: 12345678910 || Самостоятельная работа 2 >

Добавление управляемого объекта и механизма остановки игры

Смысл игры заключается в том, что потерпел аварию самолет и игрок должен ловить живых людей (3 объекта) и уворачиваться от неживых объектов (чемодан и горящий обломок самолета). За подобные успешные действия будут начисляться очки. Спасение осуществляется с помощью управляемого объекта - некоторой подвижной платформы, которая будет двигаться в нижней части экрана по тросу на одном уровне. Для управления платформой закрепим клавиши-стрелки клавиатуры Влево и Вправо. Приостановку игры предусмотрим по клавише Space.

Изображение платформы будет состоять из одного фрейма, т.е. применим неанимированный спрайт. Рисунок представляет собой тележку с матрацем


  • Вызовите контекстное меню для папки Textures и командой Add/Existing Item скопируйте из прилагаемого к работе каталога Source файл platform.png
  • Объявите в классе StartGame поле для адресации объекта platform и инициализируйте его созданием объекта. Для создания объекта используйте конструктор без параметров класса Sprite, поскольку platform является неанимированным спрайтом
public class StartGame : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Sprite[] sprite = new Sprite[5];
        Sprite platform = new Sprite();
        ..........................................
    }
  • В методе Initialize() класса StartGame задайте начальную позицию платформы
protected override void Initialize()
        {
            ...................................................
    
            // Начальная позиция платформы
            platform.spritePosition = new Vector2(
                this.Window.ClientBounds.Width / 2,
                this.Window.ClientBounds.Height - 88);
    
            base.Initialize();
        }
  • В методе LoadContent() класса StartGame пропишите код загрузки рисунка платформы в объект программы
protected override void LoadContent()
        {
            ......................................................
    
            // Загрузка рисунка платформы в объект
            platform.Load(this.Content, "Textures\\platform");
        }
  • В методе обновления состояния игры Update() класса StartGame вставьте вызов функции MovePlatform() перемещения платформы, а в самом основном классе определите эту функцию
KeyboardState keyboardState;
        protected override void Update(GameTime gameTime)
        {
            ....................................................
    
            // Перемещение сверху вниз на величину speedSprite
            MoveSprite();
    
            // Перемещение платформы по экрану
            MovePlatform();
    
            base.Update(gameTime);
        }
    
        // Перемещение платформы по экрану
        void MovePlatform()
        {
            int speedPlatform = 10;// Скорость платформы
    
            // Проверка клавиш перемещения
            if (keyboardState.IsKeyDown(Keys.Left))
                platform.spritePosition.X -= speedPlatform;
            else if (keyboardState.IsKeyDown(Keys.Right))
                platform.spritePosition.X += speedPlatform;
    
            // Проверка границ
            int minValue = 20;
            int maxValue = this.Window.ClientBounds.Width
                - (platform.spriteTexture.Width + 265);
            if (platform.spritePosition.X < minValue)
                platform.spritePosition.X = minValue;
            else if (platform.spritePosition.X > maxValue)
                platform.spritePosition.X = maxValue;
        }
  • Вставьте в функцию Draw() класса StartGame код рисования платформы на экране. Платформу вставьте в самый верхний слой над тросом
protected override void Draw(GameTime gameTime)
        {
            // Очистка буфера рисования
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
    
            spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
            spriteBatch.Draw(background1, new Vector2(0, 0), Color.White);
            for (int i = 0; i < sprite.Length; i++)
                sprite[i].DrawAnimationSprite(spriteBatch);
            spriteBatch.Draw(background2,new Vector2(0,0),Color.White);
            platform.DrawSprite(spriteBatch);// В верхний слой над тросом 
            spriteBatch.End();
    
            base.Draw(gameTime);
        }

Для приостановки игры нужно ввести булево поле-флаг pause, состояние которого менять на противоположное при каждом нажатии закрепленной за паузой клавиши. Затем нужно охватить код движения объектов в функции Update() класса StartGame условием, которое зависит от состояния флага pause. Но такой механизм является неустойчивым в работе, поскольку извлечение клавиши, поднятие флага и проверка условия выполняются за один проход вызова функции Update(). Вот пример неустойчивого кода

KeyboardState keyboardState;
        bool pause = false;
        protected override void Update(GameTime gameTime)
        {
            // Выход из игры...
            // Читать буфер клавиатуры 
            keyboardState = Keyboard.GetState();
            // Распознать клавишу Esc
            if (keyboardState.IsKeyDown(
                Microsoft.Xna.Framework.Input.Keys.Escape) == true)
                this.Exit();
    
            // Отслеживание клавиши паузы и поднятие флага 
            if (keyboardState.IsKeyDown(Keys.Space))
                paused = !paused;
            // Все движение посадим на проверку условия флага паузы
            if (paused == false)
            {
                // Смена кадров
                double elapsed = gameTime.ElapsedGameTime.TotalSeconds;
                for (int i = 0; i < sprite.Length; i++)
                    sprite[i].UpdateFrame(elapsed);
    
                // Перемещение сверху вниз на величину speedSprite
                MoveSprite();
    
                // Перемещение платформы по экрану
                MovePlatform();
            }
    
            base.Update(gameTime);
        }

Для устойчивой работы механизма установления паузы в игре введем два поля-флага pauseKeyDown и paused и создадим функцию Pause(), вызов которой поместим в функцию Update() класса StartGame. Если будет нажата закрепленная за паузой клавиша ( Space ), то при первом вызове функции Pause() поднимется флаг, сигнализирующий об этом, и только при следующем вызове функции будет поднят флаг самой паузы. Таким образом, установка паузы растянется на два прохода функции Update().

  • Добавьте к классу StartGame устойчивый двухступенчатый код приостановки игры
KeyboardState keyboardState;
        protected override void Update(GameTime gameTime)
        {
            // Выход из игры...
            // Читать буфер клавиатуры 
            keyboardState = Keyboard.GetState();
            // Распознать клавишу Esc
            if (keyboardState.IsKeyDown(
                Microsoft.Xna.Framework.Input.Keys.Escape) == true)
                this.Exit();
    
            // Все движение посадим на проверку условия флага паузы
            Pause();// Отслеживание клавиши паузы и поднятие флага 
            if (paused == false)
            {
                // Смена кадров
                double elapsed = gameTime.ElapsedGameTime.TotalSeconds;
                for (int i = 0; i < sprite.Length; i++)
                    sprite[i].UpdateFrame(elapsed);
    
                // Перемещение сверху вниз на величину speedSprite
                MoveSprite();
    
                // Перемещение платформы по экрану
                MovePlatform();
            }
    
            base.Update(gameTime);
        }
    
        bool paused = false;// Флаг паузы в игре
        bool pauseKeyDown = false;
        void Pause()
        {
            if (keyboardState.IsKeyDown(Keys.Space) == true)
                pauseKeyDown = true;
            else if (pauseKeyDown == true)
            {
                pauseKeyDown = false;
                paused = !paused;
            }
        }
  • Перекомпилируйте проект командой Rebuild, запустите приложение и убедитесь в функционировании платформы и механизма приостановки игры

Добавление игровых столкновений

На данном этапе в нашей игре 5 объектов падают с неба в циклическом режиме. В нижней части экрана курсирует платформа, которая обязана ловить живые объекты и уворачиваться от неживых. За это будут насчитываться очки. В связи с этим необходимо добавить механизм, который бы фиксировал столкновение объекта с платформой и принимал решение о поощрении. После этого перехваченный объект будем перемещать на исходную позицию для нового движения.

В библиотеке XNA Framework детектором столкновений между объектами служит структура BoundingBox (ограничивающий прямоугольник). Этот невидимый прямоугольник подгоняется под размер каждого фрейма изображения различных спрайтов и при наложении прямоугольников этих объектов фиксируется факт столкновения экземплярным методом Intersects(). Размеры плоского прямоугольника BoundingBox определяются переменными-членами Min и Max, которые обязательно задаются трехмерной структурой Vector3 с нулевой координатой z, например

// Создаем ограничивающий прямоугольник фрейма
            BoundingBox bb;
            // Определяем его размер
            bb.Min = new Vector3(10, 20, 0);// Левый верхний угол
            bb.Max = new Vector3(60, 80, 0);// Правый нижний угол

Спрайты разного размера должны иметь свои ограничивающие прамоугольники. Чем плотнее будут подогнаны ограничивающие прамоугольники к спрайтам, тем точнее будут обрабатываться столкновения между объектами. Ограничивающий прямоугольник для платформы мы специально уменьшим по высоте, чтобы столкновения выглядели более реалистичными.

  • Добавьте к основному классу игры StartGame код создания ограничивающих прямоугольников для всех объектов спрайтов в текущем положении, включая платформу, код определения столкновений и реакцию на них. Упакуйте все в метод Collisions(), который будет таким
// Текущее создание ограничивающих прямоугольников 
        // для всех объектов игры и реакция на столкновения
        BoundingBox bbPlatform;
        BoundingBox[] bbSprite = new BoundingBox[5];
        void Collisions()
        {
            // Построение ограничивающего прямоугольника платформы
            bbPlatform.Min = new Vector3(// Левый верхний угол
                platform.spritePosition.X,
                platform.spritePosition.Y + 
                platform.spriteTexture.Height / 3,// Уменьшили высоту прямоугольника
                0);
            bbPlatform.Max = new Vector3(// Правый нижний угол
                platform.spritePosition.X + platform.spriteTexture.Width,
                platform.spritePosition.Y + platform.spriteTexture.Height,
                0);
    
            // Построение ограничивающих прямоугольников летящих объектов
            for (int i = 0; i < bbSprite.Length; i++)
            {
                bbSprite[i].Min = new Vector3(// Левый верхний угол
                    sprite[i].spritePosition.X,
                    sprite[i].spritePosition.Y,
                    0);
                bbSprite[i].Max = new Vector3(// Правый нижний угол
                    sprite[i].spritePosition.X +
                    sprite[i].spriteTexture.Width / 12,// Ширина одного фрейма
                    sprite[i].spritePosition.Y +
                    sprite[i].spriteTexture.Height,
                    0);
            }
    
            // Проверка пересечений ограничивающих прямоугольников
            // летящих объектов с каймой платформы и реакция на них
            for (int i = 0; i < bbSprite.Length; i++)
            {
                if (bbPlatform.Intersects(bbSprite[i]))
                {
                    // Сброс координат следующего прохода в начальную точку.
                    // То же, что в методе MoveSprite(), только по другой причине
                    int minValue = 10;
                    int maxValue = this.Window.ClientBounds.Width - 300
                         - sprite[i].spriteTexture.Width / 12;
                    sprite[i].spritePosition = new Vector2(
                        rand.Next(minValue, maxValue), -300);
                }
            }
        }
  • Вставьте вызов метода Collisions() для проверки столкновений и реакции на них в конец метода Update() после кода перемещения спрайтов и платформы
KeyboardState keyboardState;
        protected override void Update(GameTime gameTime)
        {
            // Выход из игры...
            // Читать буфер клавиатуры 
            keyboardState = Keyboard.GetState();
            // Распознать клавишу Esc
            if (keyboardState.IsKeyDown(
                Microsoft.Xna.Framework.Input.Keys.Escape) == true)
                this.Exit();
    
            // Все движение посадим на проверку условия флага паузы
            Pause();// Отслеживание клавиши паузы и поднятие флага 
            if (paused == false)
            {
                // Смена кадров
                double elapsed = gameTime.ElapsedGameTime.TotalSeconds;
                for (int i = 0; i < sprite.Length; i++)
                    sprite[i].UpdateFrame(elapsed);
    
                // Перемещение сверху вниз на величину speedSprite
                MoveSprite();
    
                // Перемещение платформы по экрану
                MovePlatform();
    
                // Проверка столкновений и реакция на них
                Collisions();
            }
    
            base.Update(gameTime);
        }
  • Постройте проект и убедитесь в работоспособности механизма столкновений

Подсчет очков

Начисление очков определяется стратегией игры и ее целью. Например, за пойманные живые объекты можно начислять очки, а за пропущенные живые или пойманные неживые - уменьшать баллы или даже отнимать жизнь у игрока. Можно также ограничить время игры и вместо уменьшения баллов отнимать игровое время.

В нашей игре мы примем простую стратегию и будем учитывать очки отдельно для каждого объекта. За каждый пойманный объект, живой или неживой, будем начислять одно очко. Такая задача подсчета очков реализуется очень просто.

  • Добавьте в класс StartGame объявление поля массива целого типа scores[5] вместе с его инициализацией, а в функции столкновений Collisions() установите подсчет очков для каждого объекта
// Текущее создание ограничивающих прямоугольников 
        // для всех объектов игры и реакция на столкновения
        BoundingBox bbPlatform;
        BoundingBox[] bbSprite = new BoundingBox[5];
        int[] scores = new int[5] { 0, 0, 0, 0, 0 };// Массив очков
        void Collisions()
        {
            ...............................................
    
            // Проверка пересечений ограничивающих прямоугольников
            // летящих объектов с каймой платформы и реакция на них
            for (int i = 0; i < bbSprite.Length; i++)
            {
                if (bbPlatform.Intersects(bbSprite[i]))
                {
                    // Сброс координат следующего прохода в начальную точку.
                    // То же, что в методе MoveSprite(), только по другой причине
                    int minValue = 10;
                    int maxValue = this.Window.ClientBounds.Width - 300
                         - sprite[i].spriteTexture.Width / 12;
                    sprite[i].spritePosition = new Vector2(
                        rand.Next(minValue, maxValue), -300);

                    // Подсчет очков по сумме столкновений с объектами
                    scores[i]++;
                }
            }
        }
Самостоятельная работа 1: 12345678910 || Самостоятельная работа 2 >
Алексей Бабушкин
Алексей Бабушкин

При выполнении в лабораторной работе упражнения №1 , а именно при выполнении нижеследующего кода:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using Microsoft.Xna.Framework.Graphics;

   

namespace Application1

{

    public partial class MainForm : Form

    {

        // Объявим поле графического устройства для видимости в методах

        GraphicsDevice device;

   

        public MainForm()

        {

            InitializeComponent();

   

            // Подпишемся на событие Load формы

            this.Load += new EventHandler(MainForm_Load);

   

            // Попишемся на событие FormClosed формы

            this.FormClosed += new FormClosedEventHandler(MainForm_FormClosed);

        }

   

        void MainForm_FormClosed(object sender, FormClosedEventArgs e)

        {

            //  Удаляем (освобождаем) устройство

            device.Dispose();

            // На всякий случай присваиваем ссылке на устройство значение null

            device = null;       

        }

   

        void MainForm_Load(object sender, EventArgs e)

        {

            // Создаем объект представления для настройки графического устройства

            PresentationParameters presentParams = new PresentationParameters();

            // Настраиваем объект представления через его свойства

            presentParams.IsFullScreen = false; // Включаем оконный режим

            presentParams.BackBufferCount = 1;  // Включаем задний буфер

                                                // для двойной буферизации

            // Переключение переднего и заднего буферов

            // должно осуществляться с максимальной эффективностью

            presentParams.SwapEffect = SwapEffect.Discard;

            // Устанавливаем размеры заднего буфера по клиентской области окна формы

            presentParams.BackBufferWidth = this.ClientSize.Width;

            presentParams.BackBufferHeight = this.ClientSize.Height;

   

            // Создадим графическое устройство с заданными настройками

            device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, DeviceType.Hardware,

                this.Handle, presentParams);

        }

   

        protected override void OnPaint(PaintEventArgs e)

        {

            device.Clear(Microsoft.Xna.Framework.Graphics.Color.CornflowerBlue);

   

            base.OnPaint(e);

        }

    }

}

Выбрасывается исключение:

Невозможно загрузить файл или сборку "Microsoft.Xna.Framework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=6d5c3888ef60e27d" или один из зависимых от них компонентов. Не удается найти указанный файл.

Делаю все пунктуально. В чем может быть проблема?

Dmitriy Ivanchenko
Dmitriy Ivanchenko
Украина, Кировоград, Виктория-П, 2011
Татьяна Ковалюк
Татьяна Ковалюк
Украина, Киев, Киевский политехнический институт, 1974