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

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

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

Добавление спрайта в проект

2D игра основана на спрайтах - серии неподвижных рисунков, представляющих собой мгновенные снимки последовательности движений объекта игры. Рисунки должны смотреться на прозрачном фоне, чтобы не затенять фон всей игры. XNA Game Studio 3.0 автоматически распознает и поддерживает наиболее распространенные форматы bmp, jpg, png, tga, dds. Каждый отдельный рисунок спрайта называется фреймом.

Мы будем использовать готовые рисунки и управлять ими в своей программе. Для начала загрузим исходный спрайт летящего сверху объекта, состоящий из одного фрейма. Общепринято все ресурсы игры загружать в папку проекта с именем Content. В этой общей папке располагают каталоги:

  • Textures - содержит все двухмерные графические изображения, будь то рисунки, фотографии, спрайты, игровые карты, иконки и т.д.
  • Models - содержит трехмерные модели проекта, будь то мэш-объекты и другие модели
  • Папки для хранения звуковых файлов, спецэффектов, шрифтов и т.д.
  • В панели Solution Explorer вызовите контекстное меню для папки Content и командой Add/New Folder создайте в ней каталог с именем Textures
  • Вызовите контекстное меню для папки Textures и командой Add/Existing Item скопируйте из прилагаемого к работе каталога Source файл sprite.png. Не забудьте в диалоговом окне Add Existing Item установить фильтр типа файла в значение Content Pipeline Files

Теперь напишем код создания объектов, в которые будет загружаться рисунок в приложение для последующего вывода его на экран. Этот код помещается в метод LoadContent().

  • Добавьте перед методом LoadContent() поле-ссылку на объект рисунка, а в сам метод - код создания объекта
Texture2D spriteTexture;// Ссылка на объект рисунка
        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
            // Создаем объект рисунка и адресуем его ссылкой
            spriteTexture = this.Content.Load<Texture2D>("Textures\\sprite");
        }
  • Установите начальную точку вывода рисунка в центре экрана, добавив перед методом Initialize() и в него следующий код
Vector2 spritePosition;// Позиция рисования
        protected override void Initialize()
        {
            // Для прикрепления левого верхнего 
            // угла спрайта к центру экрана
            spritePosition = new Vector2(
                this.Window.ClientBounds.Width / 2,
                this.Window.ClientBounds.Height / 2);
    
            base.Initialize();
        }

Выполненными действиями мы подготовили 2 объекта для успешного рисования спрайта, а именно

  1. spriteTexture - объект с загруженным в него рисунком;
  2. spritePosition - объект с начальной точкой рисования (центр экрана)

Теперь нужно вывести на экран сам рисунок, для этого задействуем перегруженный метод Draw() основного класса.

  • Добавьте в метод Draw() основного класса следующий код вывода рисунка на экран
protected override void Draw(GameTime gameTime)
        {
            // Очистка буфера рисования
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
    
            spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
            spriteBatch.Draw(spriteTexture, spritePosition, Color.White);
            spriteBatch.End();
    
            base.Draw(gameTime);
        }

Метод Begin() отмечает начало формирования сцены в буфере рисования, а его параметр-перечисление устанавливает способ смешивания цветов (в данном случае прозрачная область рисунка не должна взаимодействовать с фоном и отображаться на экране). Экземплярный метод spriteBatch.Draw() формирует сцену в буфере графического устройства, а метод End() сигнализирует о завершении формирования сцены и требует отобразить содержимое буфера на экране.

  • Выделите в Проводнике решений Solution Explorer файл sprite.png и в панели Properties убедитесь, что для него в настройках оболочки стоит опция Build Action=Compile. Это значит, что файл будет компилироваться в стандарт XNA, имеющий расширение .xnb
  • Откройте окно вывода протокола компиляции командой View/Output
  • Перекомпилируйте проект командой Rebuild и убедитесь в окне Output, что файл sprite.png скомпилирован в файл sprite.xnb и скопирован в соответствующий каталог сборки. То же самое будет происходить и с другими файлами: текстурой, звуком, шрифтами, которые мы в дальнейшем будем добавлять к проекту
------ Rebuild All started: Project: Game2D, Configuration: Debug x86 ------
Building Textures\sprite.png -> E:\Tmp\XNAProjects\Game2D\bin\x86\Debug\Content\Textures\sprite.xnb
	
Compile complete -- 0 errors, 0 warnings
Game2D -> E:\Tmp\XNAProjects\Game2D\bin\x86\Debug\Game2D.exe
Done building project "Game2D.csproj".
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========
  • Запустите приложение и убедитесь в выводе рисунка куклы на экран, который будет выглядеть так

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

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

Не будем смешивать все в одном основном классе, а выделим код загрузки контента в отдельный класс.

  • Командой Project/Add Class добавьте к проекту новый файл с именем Sprite.cs, содержащий заготовку одноименного класса
  • Скопируйте из начала файла StartGame.cs в новый файл Sprite.cs код инструкций using с подключаемыми библиотечными пространствами имен и оформите класс Sprite следующим образом
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
    
namespace Game2D
{
    class Sprite
    {
        // Поля
        public Texture2D spriteTexture;// Ссылка на объект рисунка
        public Vector2 spritePosition; // Позиция рисования
    
        // Загрузка спрайта в игру
        public void Load(ContentManager content, String stringTexture)
        {
            spriteTexture = content.Load<Texture2D>(stringTexture);
        }
    
        // Рисование спрайта
        public void DrawSprite(SpriteBatch spriteBatch)
        {
            spriteBatch.Draw(spriteTexture, spritePosition, Color.White);
        }
    }
}
  • Отредактируйте основной класс StartGame согласно листинга (красное убрать, синее добавить)
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
    
namespace Game2D
{
    public class StartGame : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Sprite sprite;
    
        public StartGame()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            // Включается стандартный курсор мыши !!!!!!!!!!!!!!!!!!!!!!!!!
            // this.IsMouseVisible = true;
    
            // Установка разрешения экрана 
            graphics.PreferredBackBufferWidth = 1024;
            graphics.PreferredBackBufferHeight = 768;
    
            // Переключение в полноэкранный режим
            graphics.ToggleFullScreen();// или graphics.IsFullScreen = true;
    
            // Создание объекта
            sprite = new Sprite();
        }
    
        //Vector2 spritePosition;// Позиция рисования
        protected override void Initialize()
        {
            // Для прикрепления левого верхнего 
            // угла спрайта к центру экрана
            //spritePosition = new Vector2(
            //    this.Window.ClientBounds.Width / 2,
            //    this.Window.ClientBounds.Height / 2);
            sprite.spritePosition = new Vector2(
                this.Window.ClientBounds.Width / 2,
                this.Window.ClientBounds.Height / 2);
    
            base.Initialize();
        }
    
        //Texture2D spriteTexture;// Ссылка на объект рисунка
        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
    
            //spriteTexture = this.Content.Load<Texture2D>("Textures\\sprite");
            sprite.Load(this.Content, "Textures\\sprite");
        }
    
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }
    
        KeyboardState keyboardState;
        protected override void Update(GameTime gameTime)
        {
            // Выход из игры...
            // Читать буфер клавиатуры 
            keyboardState = Keyboard.GetState();
            // Распознать клавишу Esc
            if (keyboardState.IsKeyDown(Keys.Escape) == true)
                this.Exit();
    
            base.Update(gameTime);
        }
    
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
    
            spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
            //spriteBatch.Draw(spriteTexture, spritePosition, Color.White);
            sprite.DrawSprite(spriteBatch);
            spriteBatch.End();
    
            base.Draw(gameTime);
        }
    }
}
  • Перекомпилируйте проект командой Rebuild, запустите приложение и убедитесь, что выдаваемый результат остался прежним

Добавление анимационной последовательности

Для того, чтобы создать иллюзию движущегося объекта, необходимо показать последовательность рисунков одинакового размера, каждый из которых будет фиксировать новое положение этого объекта. Такая последовательность называется анимационной или кинолентой (Timeout). Анимационная последовательность может состоять из любого числа фреймов (кадров), но обычно для простых объектов некоторый набор движений повторяется. В этом случае последовательность состоит из нескольких кадров, повторение который замыкают в петлю (Loop).

В нашем случае последовательность движений куклы будет состоять из 12 кадров одинакового размера, пронумерованных от 0 до 11. Изменим класс Sprite так, чтобы он загружал эту анимационную последовательность и позволял показывать любой из ее кадров. Тогда организация циклического показа кадров этой последовательности создаст иллюзию непрерывного движения куклы.

  • Дополните класс Sprite следующим кодом показа анимационной последовательности
public class Sprite
    {
        // Поля
        public Texture2D spriteTexture;// Ссылка на объект рисунка
        public Vector2 spritePosition; // Позиция рисования
    
        int frameCount;     // Количество кадров последовательности
        double timeFrame;   // Время показа одного фрейма на экране (задержка)
        int frame;          // Номер показываемого фрейма
        double totalElapsed;// Время, прошедшее с начала показа текущего фрейма
    
        // Параметризованный конструктор
        public Sprite(int frameCount, int framesPerSec)
        {
            this.frameCount = frameCount;
            timeFrame = 1D / framesPerSec;
            frame = 0;
            totalElapsed = 0d;
        }
    
        // Если есть параметризованный конструктор, то
        // нужно явно определить конструктор по умолчанию
        public Sprite()
        {
        }
    
        // Переход к следующему фрейму по истечении системного времени
        public void UpdateFrame(double elapsed)
        {
            totalElapsed += elapsed;
            if (totalElapsed > timeFrame)
            {
                frame++;// Счетчик кадров
                frame = frame % (frameCount - 1);// Деление по модулю
                totalElapsed = 0D;// Текущее время показа нового фрейма
            }
        }
    
        // Рисование текущего спрайта последовательности
        public void DrawAnimationSprite(SpriteBatch spriteBatch)
        {
            // Ширина одного кадра
            int frameWidth = spriteTexture.Width / frameCount;
            // Высота анимационной последовательности
            int frameHeight = spriteTexture.Height;
            // Вычленяющий прямоугольник Rectangle(x, y, width, height)
            Rectangle rect = new Rectangle(frameWidth * frame, 0,
                frameWidth, frameHeight);
            // Рисуем кадр
            spriteBatch.Draw(spriteTexture, spritePosition, rect, Color.White);
        }
    
        // Загрузка спрайта в игру
        public void Load(ContentManager content, String stringTexture)
        {
            spriteTexture = content.Load<Texture2D>(stringTexture);
        }
    
        // Рисование спрайта
        public void DrawSprite(SpriteBatch spriteBatch)
        {
            spriteBatch.Draw(spriteTexture, spritePosition, Color.White);
        }
    }

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

Самостоятельная работа 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