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

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

Формирование трехмерной сцены

В этом разделе мы загрузим в игру модель стадиона, на фоне которой будут летать мячи, и установим камеру под определенным углом, одинаковым и для мячей и для стадиона.

  • В классе StartGame3D установите новое значение поля camera для выбора наилучшего ракурса (угла зрения) всей сцены игры и добавьте объект стадиона, в который мы позднее загрузим трехмерный мэш
public class StartGame3D : Microsoft.Xna.Framework.Game
{    
    #region Поля класса 
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;
    int screenWidth, screenHeight;  // Размеры экрана
    KeyboardState keyboardState;    // Буфер клавиатуры 
    GameState gameState = GameState.GameScreen;   // Переменная состояния игры

    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();
    #endregion
    ..................................................
    
}
  • Добавьте из прилагаемого каталога Source в папку Models проекта файл с именем stadium 1.x
  • В методе LoadContent() основного класса игры загрузите модель стадиона в созданный ранее объект stadium программы
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");
}
  • В методе Draw() основного класса игры вставьте код вывода изображения стадиона на экран монитора (раньше кода вывода других объектов)
protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);

    switch (gameState)
    {
        case GameState.AboutScreen:
            break;
        case GameState.GameOverScreen:
            break;
        case GameState.GameScreen:
            // Включить буфер глубины 
            graphics.GraphicsDevice.RenderState.DepthBufferEnable = true;
            // Установить камеру
            view = Matrix.CreateLookAt(camera, Vector3.Zero, Vector3.Up);
            // Задать проекционную матрицу
            proj = Matrix.CreatePerspectiveFieldOfView(FOV, 
                aspectRatio, nearClip, farClip);
            // Определить матрицу преобразования
            //world = Matrix.CreateTranslation(ball.Position);
            // Нарисовать модель в начальном состоянии
            //ball.DrawModel(world, view, proj);

            // Нарисовать модель стадиона самой первой 
            world = Matrix.CreateTranslation(new Vector3(0, 0, 0));
            stadium.DrawModel(world, view, proj);

            // Нарисовать модели мячей
            for (int i = 0; i < ball.Length; i++)
            {
                // Определить матрицу преобразования
                world = Matrix.CreateTranslation(ball[i].Position);
                // Нарисовать модель в начальном состоянии
                ball[i].DrawModel(world, view, proj);
            }

            // Нарисовать курсор-прицел
            spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
            cursor.DrawSprite(spriteBatch);
            spriteBatch.End();
            break;
        case GameState.HelpScreen:
            break;
        case GameState.MenuScreen:
            break;
        case GameState.SplashScreen:
            break;
        case GameState.VictotyScreen:
            break;
    }

    base.Draw(gameTime);
}
  • Запустите игру и убедитесь, что начальные позиции мячей нуждаются в корректировке

Обратите внимание на позиции установки всех моделей игры, включая камеру. Если бы камера была совмещена с экраном монитора, то начало мировой системы координат находилось бы в плоскости экрана. Позиции мячей мы формируем в отрицательном направлении оси Oz в диапазоне [-20,-150] пикселов. Сам стадион рисуем относительно начала мировой системы координат. И для того, чтобы увидеть его целиком, мы камеру отодвинули в положительном направлении на 250 пикселов. В результате мячи удалились от наблюдателя и к тому же падают на дальней границе стадиона. Поэтому нужно приблизить мячи к наблюдателю, скорректировав их начальные координаты по оси Oz в новом диапазоне [-50,150].

  • Введите изменения в код функций BeginPosition() и MouseClick(), где задается координата z полета мячей
// Устанавливаем начальную позицию объекта
void BeginPosition()
{
    for (int i = 0; i < ball.Length; i++)
    {
        ball[i].Position = new Vector3(
            rand.Next(-rand.Next(0, 80), rand.Next(0, 80)),
            rand.Next(0, 80),
            //-rand.Next(20, 150));
            rand.Next(-rand.Next(0, 50), rand.Next(0, 150)));
    }                                                           
}

.....................................................

// Механизм стрельбы по целям 
BoundingSphere[] bb = new BoundingSphere[3];
void MouseClick()
{
    // Получаем текущие координаты курсора и передаем их центру объекта прицела
    mouseState = Mouse.GetState();
    cursor.spritePosition.X = mouseState.X - cursor.spriteTexture.Width / 2;
    cursor.spritePosition.Y = mouseState.Y - cursor.spriteTexture.Height / 2;

    // Одеваем на каждый мячик ограничивающую сферу с заданным радиусом
    for (int i = 0; i < bb.Length; i++)
    {
        bb[i].Center = ball[i].Position;
        bb[i].Radius = ball[i].Radius;
    }

    // Определяем попадание и при успехе 
    // устанавливаем мяч в начальную позицию
    if (mouseState.LeftButton == ButtonState.Pressed)
    {
        // Пересчитываем координаты курсора на трехмерный объем
        Ray pickRay = GetPickRay();

        // Проверяем попадание
        for (int i = 0; i < ball.Length; i++)
        {
            Nullable<float> result = pickRay.Intersects(bb[i]);
            if (result.HasValue == true)
            {
                ball[i].Position = new Vector3(
                    rand.Next(-rand.Next(0, 80), rand.Next(0, 80)),
                    rand.Next(0, 80),
                    //-rand.Next(20, 150));
                    rand.Next(-rand.Next(0, 50), rand.Next(0, 150)));
            }
        }
    }
}
  • Запустите приложение и убедитесь, что мячи теперь падают над поверхностью поля стадиона

Нам нужно сделать, чтобы мячи останавливались на поверхности поля, а не проваливались сквозь него. Поверхность поля стадиона проходит через ось Oy мировой системы координат на расстоянии ( -32 ) пиксела, поэтому двигать мячи в вертикальном направлении следует только до этой величины.

  • Внесите следующие изменения в функцию MoveBalls() основного класса игры
void MoveBalls()
{
    for (int i = 0; i < ball.Length; i++)
    {
        if (ball[i].Position.Y > -32)
            ball[i].Position -= new Vector3(0, ball[i].Speed, 0);
    }
}
  • Запустите приложение и убедитесь, что мячи теперь останавливаются на поверхности поля стадиона

Дополним сцену подходящим задним фоном, чтобы стадион и вся сцена смотрелись более красочно.

  • Добавьте в папку Textures проекта файл hallake001.png из прилагаемого каталога Source
  • В классе StartGame3D добавьте поле-ссылку background для хранения объекта текстуры, а в методах LoadContent() и Draw() - код загрузки и вывода рисунка на экран
#region Поля класса 
..................................................

// Создание объекта модели стадиона
ModelClass stadium = new ModelClass();

// Создание объекта для рисунка пейзажа
Game2D.Sprite background = new Game2D.Sprite();
#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);
}

..................................................

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);

    switch (gameState)
    {
        case GameState.AboutScreen:
            break;
        case GameState.GameOverScreen:
            break;
        case GameState.GameScreen:
            // Нарисовать фон самым первым
            spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
            background.DrawSprite(spriteBatch);
            spriteBatch.End();

            // Включить буфер глубины 
            graphics.GraphicsDevice.RenderState.DepthBufferEnable = true;
            // Установить камеру
            view = Matrix.CreateLookAt(camera, Vector3.Zero, Vector3.Up);
            // Задать проекционную матрицу
            proj = Matrix.CreatePerspectiveFieldOfView(FOV, 
                aspectRatio, nearClip, farClip);
            // Определить матрицу преобразования
            //world = Matrix.CreateTranslation(ball.Position);
            // Нарисовать модель в начальном состоянии
            //ball.DrawModel(world, view, proj);

            // Нарисовать стадион
            world = Matrix.CreateTranslation(new Vector3(0, 0, 0));
            stadium.DrawModel(world, view, proj);

            // Нарисовать модели мячей
            for (int i = 0; i < ball.Length; i++)
            {
                // Определить матрицу преобразования
                world = Matrix.CreateTranslation(ball[i].Position);
                // Нарисовать модель в начальном состоянии
                ball[i].DrawModel(world, view, proj);
            }

            // Нарисовать курсор-прицел
            spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
            cursor.DrawSprite(spriteBatch);
            spriteBatch.End();
            break;
        case GameState.HelpScreen:
            break;
        case GameState.MenuScreen:
            break;
        case GameState.SplashScreen:
            break;
        case GameState.VictotyScreen:
            break;
    }

    base.Draw(gameTime);
}
  • Запустите приложение и полюбуйтесь на солидную сцену, сделанную своими руками

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

При выполнении в лабораторной работе упражнения №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" или один из зависимых от них компонентов. Не удается найти указанный файл.

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

Иван Циферблат
Иван Циферблат
Россия, Таганрог, 36, 2000