При выполнении в лабораторной работе упражнения №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" или один из зависимых от них компонентов. Не удается найти указанный файл. Делаю все пунктуально. В чем может быть проблема? |
Компьютерная 2D-графика в Microsoft XNA Game Studio 3.0
Добавление управляемого объекта и механизма остановки игры
Смысл игры заключается в том, что потерпел аварию самолет и игрок должен ловить живых людей (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]++; } } }