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

Компьютерная графика 3D в XNA

Добавление экранных заставок

В самом начале построения приложения мы в методах LoadContent() и Draw() с помощью оператора switch заложили несколько состояний игры, определенных в перечислении GameState

  • AboutScreen,         // Экран с информацией об игре
  • GameOverScreen, // Экран проигрыша
  • GameScreen,       // Экран игрового процесса
  • HelpScreen,          // Инструкция о правилах
  • MenuScreen,         // Меню игры
  • SplashScreen,       // Первая игровая заставка
  • VictotyScreen,        // Экран выигрыша

До сих пор мы реализовали только одно из них - саму игру. В этом разделе закодируем еще три состояния: SplashScreen, AboutScreen, HelpScreen. Смену режимов закрепим за клавишами Enter и Esc.

Экран игровой заставки SplashScreen

Первой будет появляться игровая заставка, из которой по клавише Enter попадем в меню, а по клавише Esc выйдем из игры. Игровая заставка будет состоять из нескольких отдельных компонентов:

  • Фоновый рисунок splash.png
  • Вехний заголовок игры title.png
  • Нижняя подсказка enter.png
  • Изображение пулевого отверстия hole.png, которое мы разбросаем по экрану заставки случайным образом в 30 экземплярах
  • Модели вращающегося мяча Soccerball.x посередине экрана на переднем плане заставки

Прежде всего нужно скопировать в проект эти компоненты заставки.

  • В панели Solution Explorer вызовите контекстное меню для каталога проекта Textures и дабавьте командой Add/Existing Item из прилагаемого каталога Source файлы с рисунками splash.png, title.png, enter.png, hole.png

Код игровой заставки упакуем в класс SplashScreen.

  • Командой Project/Add Class меню оболочки добавьте к проекту файл SplashScreen.cs и скопируйте в него все инструкции using из файла StartGame3D
  • Заполните файл SplashScreen.cs следующим кодом
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 Game3D
{
    class SplashScreen
    {
        #region Поля класса
        Texture2D splash, title, enter, hole;   // Объекты рисунков 
        Vector2[] posHole = new Vector2[30];    // Массив позиций рисунка пули
        Random rand = new Random();             // Генератор случайных чисел
    
        ModelClass ball = new ModelClass();     // Объект мяча
        float aspectRatio;                      // Коэффициент искажения проекции
        float FOV = MathHelper.PiOver4;         // Ракурс камеры 45 градусов
        float nearClip = 1.0f;                  // Ближняя отсекающая плоскость
        float farClip = 1000.0f;                // Дальняя отсекающай плоскость
        float angle = 0;                        // Угол поворота
        Matrix world, view, proj;               // Матрицы преобразований
        #endregion
    
        // Инициализация объектов
        // content - контент экземпляра основного класса игры
        // width - текущаяя ширина экрана
        // height - текущая высота экрана
        public void InitializeSplashScreen(ContentManager content, int width, int height)
        {
            // Загружаем в объекты изображения и модель мяча
            splash = content.Load<Texture2D>("Textures\\splash");
            title = content.Load<Texture2D>("Textures\\title");
            enter = content.Load<Texture2D>("Textures\\enter");
            hole = content.Load<Texture2D>("Textures\\hole");
            ball.Load(content, "Models\\Soccerball");
            ball.Position = new Vector3(0, 0, 0);// В центре экрана, где начало мировой системы координат
    
            // Разбрасываем по экрану пулевые отверстия
            for (int i = 0; i < posHole.Length; i++)
            {
                posHole[i].X = rand.Next(20, width - 60);
                posHole[i].Y = rand.Next(100, height - 60);
            }
    
            // Коэффициент искажения проекции
            aspectRatio = (float)width / height;
        }
    
        // Вывод заставки на экран
        // spriteBatch - объект рисования спрайтов
        // graphics - объект графического устройства GDI
        // width - текущаяя ширина экрана
        // height - текущая высота экрана
        // gameTime - длительность одного такта игры
        public void DrawScreen(SpriteBatch spriteBatch, GraphicsDeviceManager graphics,
            int width, int height, GameTime gameTime)
        {
            // Очищаем экран своим цветом
            graphics.GraphicsDevice.Clear(Color.DarkGreen); 
    
            // Рисуем в GDI плоские компоненты относительно центра экрана и в Z-последовательности
            spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
            spriteBatch.Draw(splash, new Vector2(width / 2 - splash.Width / 2,
                height / 2 - splash.Height / 2), Color.White);
            spriteBatch.Draw(title, new Vector2(width / 2 - title.Width / 2, 
                30), // Чуть опустили
                Color.White);
            spriteBatch.Draw(enter, new Vector2(width / 2 - enter.Width / 2,
                height - enter.Height - 30), // Чуть приподняли
                Color.White);
            for (int i = 0; i < posHole.Length; i++)
                spriteBatch.Draw(hole, posHole[i], Color.White);
            spriteBatch.End();
    
            // Рисуем объемный мяч на переднем плане
            graphics.GraphicsDevice.RenderState.DepthBufferEnable = true;// Включаем буфер глубины
            view = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, 150.0f), Vector3.Zero, Vector3.Up);
            proj = Matrix.CreatePerspectiveFieldOfView(FOV, aspectRatio, nearClip, farClip);
            world = Matrix.CreateTranslation(ball.Position);
            angle += (float)(gameTime.ElapsedGameTime.TotalSeconds * 2.0f);// Увеличиваем угол вращения мяча
            world *= Matrix.CreateRotationY(angle);
            ball.DrawModel(world, view, proj);
        }
    }
}
  • Для тестирования разработанного класса SplashScreen введите временно следующие изменения в основной класс игры
public class StartGame3D : Microsoft.Xna.Framework.Game
    {    
        #region Поля класса 
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        int screenWidth, screenHeight;  // Размеры экрана
        KeyboardState keyboardState;    // Буфер клавиатуры 
        //GameState gameState = GameState.GameScreen;   // Переменная состояния игры
        GameState gameState = GameState.SplashScreen;   // Переменная состояния игры
    
        Matrix world;                   // Мировая матрица
        Matrix view;                    // Матрица вида
        Matrix proj;                    // Проекционная матрица
        float aspectRatio;              // Коэффициент искажения проекции
        float FOV = MathHelper.PiOver4; // Ракурс
        float nearClip = 1.0f;          // Ближняя отсекающая плоскость перспективы
        float farClip = 1000.0f;        // Дальняя отсекающая плоскость перспективы
        //Vector3 camera = new Vector3(0.0f, 0.0f, 150.0f);
        Vector3 camera = new Vector3(0.0f, 20.0f, 250.0f);
        //ModelClass ball;   
        ModelClass[] ball = new ModelClass[3];  
        MouseState mouseState;                  
        Random rand = new Random();   
    
        // Создание объекта курсора-прицела
        Game2D.Sprite cursor = new Game2D.Sprite();
    
        // Создание объекта модели стадиона
        ModelClass stadium = new ModelClass();
    
        // Создание объекта для рисунка пейзажа
        Game2D.Sprite background = new Game2D.Sprite();
    
        // Создание объектов для других состояний экранов
        SplashScreen splash = new SplashScreen();
        #endregion
    
        protected override void LoadContent()
        {
            ....................................................
    
            // Загружаем рисунок заднего фона
            background.Load(this.Content, "Textures\\hallake001");
            // Чуть приподнимем вверх экрана 
            background.spritePosition = new Vector2(0, -50);
    
            // Инициализация объектов других состояний экранов
            splash.InitializeSplashScreen(this.Content, screenWidth, screenHeight);
        }
      
        protected override void Update(GameTime gameTime)
        {
            // Читать буфер клавиатуры 
            keyboardState = Keyboard.GetState();
    
            switch (gameState)
            {
                case GameState.AboutScreen:
                    break;
                case GameState.GameOverScreen:
                    break;
                case GameState.GameScreen:
                    // Выход из игрового процесса
                    if (keyboardState.IsKeyDown(Keys.Escape))
                        this.Exit();
    
                    MoveBalls();
                    MouseClick();
                    break;
                case GameState.HelpScreen:
                    break;
                case GameState.MenuScreen:
                    break;
                case GameState.SplashScreen:
                    // Выход из игрового процесса
                    if (keyboardState.IsKeyDown(Keys.Escape))
                        this.Exit();
    
                    break;
                case GameState.VictotyScreen:
                    break;
            }
    
            base.Update(gameTime);
        }
    
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
    
            switch (gameState)
            {
                case GameState.AboutScreen:
                    break;
                case GameState.GameOverScreen:
                    break;
                case GameState.GameScreen:
                    .......................................................
    
                    break;
                case GameState.HelpScreen:
                    break;
                case GameState.MenuScreen:
                    break;
                case GameState.SplashScreen:
                    splash.DrawScreen(this.spriteBatch, this.graphics, screenWidth, screenHeight, gameTime);
                    break;
                case GameState.VictotyScreen:
                    break;
            }
    
            base.Draw(gameTime);
        }
    }
  • Запустите приложение и убедитесь, что код класса SplashScreen работает нормально в основном цикле игры. Получится картинка, приведенная ниже, с вращающимся в центре мячом

Экран с информацией об игре AboutScreen

Код этой заставки мы спроектируем на основе только что созданного класса SplashScreen, внеся в него незначительные коррективы. Эти изменения будет связаны в основном с тем, что вместо одного мяча, вращающегося в центре экрана, мы введем три мяча, которые будут вращаться как и прежде - относительно оси y, и одновременно - относительно оси z. В итоге получим сложное вращение мячей в плоскости экрана относительно его центра.

  • Загрузите в папку Textures проекта файлы изображений about.png и esc.png из прилагаемого к работе каталога Source
  • В панели Solution Explorer сделайте копию файла SplashScreen.cs и переименуйте ее в файл AboutScreen.cs
  • Модифицируйте файл AboutScreen.cs так, чтобы он выглядел следующим образом
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 Game3D
{
    class AboutScreen
    {
        #region Поля класса
        Texture2D about, title, esc, hole;      // Объекты рисунков 
        Vector2[] posHole = new Vector2[30];    // Массив позиций рисунка пули
        Random rand = new Random();             // Генератор случайных чисел
    
        ModelClass [] ball = new ModelClass[3]; // Объекты мячей 
        float aspectRatio;                      // Коэффициент искажения проекции
        float FOV = MathHelper.PiOver4;         // Ракурс камеры 45 градусов
        float nearClip = 1.0f;                  // Ближняя отсекающая плоскость
        float farClip = 1000.0f;                // Дальняя отсекающай плоскость
        float angle = 0;                        // Угол поворота
        Matrix world, view, proj;               // Матрицы преобразований
        #endregion
    
        // Инициализация объектов
        // content - контент экземпляра основного класса игры
        // width - текущаяя ширина экрана
        // height - текущая высота экрана
        public void InitializeSplashScreen(ContentManager content, int width, int height)
        {
            // Создаем объекты для мячей 
            for (int i = 0; i < ball.Length; i++)
                ball[i] = new ModelClass();
    
            // Загружаем в объекты изображения и модель мяча
            about = content.Load<Texture2D>("Textures\\about");
            title = content.Load<Texture2D>("Textures\\title");
            esc = content.Load<Texture2D>("Textures\\esc");
            hole = content.Load<Texture2D>("Textures\\hole");
            ball[0].Load(content, "Models\\Soccerball");
            ball[0].Position = new Vector3(-30, 40, -30);
            ball[1].Load(content, "Models\\SoccerballGreen");
            ball[1].Position = new Vector3(50, 30, -50);
            ball[2].Load(content, "Models\\SoccerballRed");
            ball[2].Position = new Vector3(-50, -30, 40);
    
            // Разбрасываем по экрану пулевые отверстия
            for (int i = 0; i < posHole.Length; i++)
            {
                posHole[i].X = rand.Next(20, width - 60);
                posHole[i].Y = rand.Next(100, height - 60);
            }
    
            // Коэффициент искажения проекции
            aspectRatio = (float)width / height;
        }
    
        // Вывод заставки на экран
        // spriteBatch - объект рисования спрайтов
        // graphics - объект графического устройства GDI
        // width - текущаяя ширина экрана
        // height - текущая высота экрана
        // gameTime - длительность одного такта игры
        public void DrawScreen(SpriteBatch spriteBatch, GraphicsDeviceManager graphics,
            int width, int height, GameTime gameTime)
        {
            // Очищаем экран своим цветом
            graphics.GraphicsDevice.Clear(Color.DarkGreen); 
    
            // Рисуем в GDI плоские компоненты относительно центра экрана и в Z-последовательности
            spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
            spriteBatch.Draw(about, new Vector2(width / 2 - about.Width / 2,
                height / 2 - about.Height / 2), Color.White);
            spriteBatch.Draw(title, new Vector2(width / 2 - title.Width / 2, 
                30), // Чуть опустили
                Color.White);
            spriteBatch.Draw(esc, new Vector2(width / 2 - esc.Width / 2,
                height - esc.Height - 30), // Чуть приподняли
                Color.White);
            for (int i = 0; i < posHole.Length; i++)
                spriteBatch.Draw(hole, posHole[i], Color.White);
            spriteBatch.End();
    
            // Рисуем мячи на переднем плане
            graphics.GraphicsDevice.RenderState.DepthBufferEnable = true;// Включаем буфер глубины
            view = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, 260.0f), Vector3.Zero, Vector3.Up);
            proj = Matrix.CreatePerspectiveFieldOfView(FOV, aspectRatio, nearClip, farClip);
            angle += (float)(gameTime.ElapsedGameTime.TotalSeconds * 1.0f);
            Matrix rotationMatrixY = Matrix.CreateRotationY(angle);
            Matrix rotationMatrixZ = Matrix.CreateRotationZ(angle);
            for (int i = 0; i < ball.Length; i++)
            {
                world = Matrix.CreateTranslation(ball[i].Position);
                ball[i].DrawModel(rotationMatrixY * world * rotationMatrixZ, view, proj);
            }
        }
    }
}
  • Для тестирования разработанного класса AboutScreen введите временно следующие изменения в основной класс игры
public class StartGame3D : Microsoft.Xna.Framework.Game
    {    
        #region Поля класса 
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        int screenWidth, screenHeight;  // Размеры экрана
        KeyboardState keyboardState;    // Буфер клавиатуры 
        GameState gameState = GameState.AboutScreen;   // Переменная состояния игры
    
        Matrix world;                   // Мировая матрица
        Matrix view;                    // Матрица вида
        Matrix proj;                    // Проекционная матрица
        float aspectRatio;              // Коэффициент искажения проекции
        float FOV = MathHelper.PiOver4; // Ракурс
        float nearClip = 1.0f;          // Ближняя отсекающая плоскость перспективы
        float farClip = 1000.0f;        // Дальняя отсекающая плоскость перспективы
        //Vector3 camera = new Vector3(0.0f, 0.0f, 150.0f);
        Vector3 camera = new Vector3(0.0f, 20.0f, 250.0f);
        //ModelClass ball;   
        ModelClass[] ball = new ModelClass[3];  
        MouseState mouseState;                  
        Random rand = new Random();   
    
        // Создание объекта курсора-прицела
        Game2D.Sprite cursor = new Game2D.Sprite();
    
        // Создание объекта модели стадиона
        ModelClass stadium = new ModelClass();
    
        // Создание объекта для рисунка пейзажа
        Game2D.Sprite background = new Game2D.Sprite();
    
        // Создание объектов для других состояний экранов
        SplashScreen splash = new SplashScreen();
        AboutScreen about = new AboutScreen();
        #endregion
    
        ..............................................................
    
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
    
            //ball.Load(this.Content, "Models\\Soccerball");            
            // Загружаем модели мячей
            ball[0].Load(this.Content, "Models\\Soccerball");           
            ball[1].Load(this.Content, "Models\\SoccerballGreen");      
            ball[2].Load(this.Content, "Models\\SoccerballRed");   
    
            // Загружаем трехмерную модель стадиона
            stadium.Load(this.Content, "Models\\stadium 1");
    
            // Устанавливаем начальную позицию объектов 
            BeginPosition();    
    
            // Загружаем рисунок курсора-прицела 
            cursor.Load(this.Content, "Textures\\cursor");
    
            // Загружаем рисунок заднего фона
            background.Load(this.Content, "Textures\\hallake001");
            // Чуть приподнимем вверх экрана 
            background.spritePosition = new Vector2(0, -50);
    
            // Инициализация объектов других состояний экранов
            splash.InitializeSplashScreen(this.Content, screenWidth, screenHeight);
            about.InitializeSplashScreen(this.Content, screenWidth, screenHeight);
        }
    
        ..............................................................
    
        protected override void Update(GameTime gameTime)
        {
            // Читать буфер клавиатуры 
            keyboardState = Keyboard.GetState();
    
            switch (gameState)
            {
                case GameState.AboutScreen:
                    // Выход из игрового процесса
                    if (keyboardState.IsKeyDown(Keys.Escape))
                        this.Exit();
                    break;
                case GameState.GameOverScreen:
                    break;
                case GameState.GameScreen:
                    // Выход из игрового процесса
                    if (keyboardState.IsKeyDown(Keys.Escape))
                        this.Exit();
    
                    MoveBalls();
                    MouseClick();
                    break;
                case GameState.HelpScreen:
                    break;
                case GameState.MenuScreen:
                    break;
                case GameState.SplashScreen:
                    // Выход из игрового процесса
                    if (keyboardState.IsKeyDown(Keys.Escape))
                        this.Exit();
                    break;
                case GameState.VictotyScreen:
                    break;
            }
    
            base.Update(gameTime);
        }
    
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
    
            switch (gameState)
            {
                case GameState.AboutScreen:
                    about.DrawScreen(this.spriteBatch, this.graphics, screenWidth, screenHeight, gameTime);
                    break;
                case GameState.GameOverScreen:
                    break;
                case GameState.GameScreen:
                    ..............................................................
    
                    break;
                case GameState.HelpScreen:
                    break;
                case GameState.MenuScreen:
                    break;
                case GameState.SplashScreen:
                    splash.DrawScreen(this.spriteBatch, this.graphics, screenWidth, screenHeight, gameTime);
                    break;
                case GameState.VictotyScreen:
                    break;
            }
    
            base.Draw(gameTime);
        }
    
        ..............................................................
    
    }
  • Запустите приложение и убедитесь, что код класса AboutScreen работает нормально в основном цикле игры. Получится картинка, приведенная ниже, с вращающимися в плоскости экрана относительно его центра мячами

Алексей Бабушкин
Алексей Бабушкин

При выполнении в лабораторной работе упражнения №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