|
При выполнении в лабораторной работе упражнения №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" или один из зависимых от них компонентов. Не удается найти указанный файл. Делаю все пунктуально. В чем может быть проблема? |
Компьютерная графика 3D в XNA
Введение
Все необходимые для выполнения данной работы программы можно найти в
прилагаемом каталоге.
Сценарий игры состоит в том, что сверху вниз экрана падают мячи, а задача игрока не допустить, чтобы хотя бы один из них упал на площадь футбольного поля. С помощью выстрелов, которые должны попасть в мячик, игрок должен удерживать мячи на лету определенное время. Удачный игрок переходит на новый уровень.
В трехмерной графике используется мировая система координат, ориентированная так, что плоскость xOy направлена параллельно экрану монитора, а ось Oz направлена либо к пользователю перпендикулярно экрану (правая система координат), либо от пользователя вглубь экрана (левая система координат). Если вытянуть в разные стороны три пальца правой руки (большой, указательный и средний) под прямыми углами, то большой будет указывать направление оси x, указательный - оси y, а средний - оси z. В DirectX используется левая система координат, а в OpenGL и XNA Framework - правая.
Поверхность любого объемного тела описывается с помощью примитивов - простых плоских геометрических фигур, чаще всего треугольников. Каждый треугольник задан координатами своих вершин в мировой системе координат. Относительно этой системы задаются координаты вершин всех примитивов каждого объекта ( модели ). Совокупность трехмерных объектов называется сценой. Над трехмерными моделями могут выполняться следующие операции, которые называются модельными преобразованиями:
- Поворот или ротация (на любой угол относительно любой произвольной оси, чаще складывается из последовательности поворотов относительно осей системы координат)
- Сдвиг или трансляция (параллельное перемещение в пространстве)
- Масштабирование (увеличение или уменьшение)
Чтобы выполнить одно из указанных действий для объекта в целом, нужно применить его к вершине каждого примитива, описывающего модель. Для выполнения таких действий служит мировая матрица преобразования. Это матрица размером 4x4, которая может принимать одно из следующих значений, в зависимости от выполняемых действий:
Начальная расстановка моделей на сцене определяется начальными координатами примитивов модели, последовательно умноженными на одну или несколько матриц модельного преобразования. Применение одной и той же матрицы преобразования ко всем моделям меняет всю сцену вместе с предыдущими координатами вершин примитивов.
Кроме перечисленных модельных матриц имееются еще две матрицы:
- Матрица вида
- Проекционная матрица
Матрица вида содержит в себе координаты размещения камеры в трехмерном пространстве. У камеры есть вертикальная ось, совпадающая с координатой Oy мировой системы координат. Обычно ограничивают операции преобразования для координат камеры поворотами относительно этой оси и параллельными смещениями вправо/влево вверх/вниз, а также отдалением/приближением камеры к сцене.
Проекционная матрица создает проекцию трехмерной сцены на экран плоского монитора. Она строит прямоугольную пирамиду с вершиной в камере и обеспечивает переднюю и заднюю стенки отсечения трехмерного пространства. Это позволяет вычленять только определенные куски пространства и снижает нагрузку на процессор компьютера.
Создание заготовки приложения
Новый проект мы разместим в уже существующей папке XNAProjects, созданной нами ранее при выполнении работы Lab58.
-
Откройте решение XNAProjects работы Lab58 и
в панели Solution Explorer вызовите контекстное меню для узла самого
решения Solution
-
Командой Add/New Project вызовите
окно мастера и настройте его, с учетом версии библиотеки .NET Framework 3.0,
так
-
Переименуйте файл Game1.cs нового
проекта в StartGame3D.cs
Как видно из панели Solution Explorer, теперь в решении присутствуют два проекта: Game2D и новый проект Game3D. Обратите внимание, что Game2D выделен жирным шрифтом. Это значит, что он по умолчанию в оболочке установлен как стартовый. Если сейчас запустить выполнение решения, то будет выполнен выделенный проект Game2D. В дальнейшем есть два пути: либо назначить новый проект стартовым, либо исключить из решения старый проект, тогда новый проект автоматически станет стартовым. Для тренировки, мы вначале назначим новый проект стартовым, а потом исключим старый проект.
-
В панели Solution
Explorer вызовите контекстное меню для узла Game3D и выполните
команду Set as StartUp Project, чтобы сделать новый
проект стартовым -
Добавьте в конструктор
класса заготовки нового проекта игры код установки размеров окна
public StartGame3D()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferWidth = 300;
graphics.PreferredBackBufferHeight = 200;
}-
Щелкните на кнопке Start
Debugging (F5) и убедитесь, что запустилась именно заготовка приложения
нового проекта Game3D
-
В панели Solution
Explorer вызовите контекстное меню для узла Game2D и выполните
команду Remove, чтобы исключить из решения старый проект, который
после этого останется существовать в пассивном режиме
В прошлой игре мы устанавливали жестко размер экрана, и если это разрешение не совпадало с текущим перед запуском игры, то после запуска приложения система некоторое время настраивала требуемое разрешение, то есть была некоторая задержка в появлении экрана. В этом приложении мы оставим неизменными текущие размеры экрана, но определим их из видеоадаптера и запомним в полях класса игры для дальнейшего использования.
-
Удалите из конструктора
класса игры код жесткой установки размеров экрана
public StartGame3D()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
//graphics.PreferredBackBufferWidth = 300;
//graphics.PreferredBackBufferHeight = 200;
}Поля класса для удобства разместим в секции #region.
-
Дополните основной
класс игры и его методы кодом автоматической установки размеров
экрана и выхода из игры
public class StartGame3D : Microsoft.Xna.Framework.Game
{
#region Поля класса
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
int screenWidth, screenHeight; // Размеры экрана
KeyboardState keyboardState; // Буфер клавиатуры
#endregion
.....................................................
protected override void Initialize()
{
GraphicsAdapter adapter = graphics.GraphicsDevice.
CreationParameters.Adapter; // Получить ссылку на параметры адаптера
screenWidth = adapter.CurrentDisplayMode.Width; // Сохранить текущие размеры экрана
screenHeight = adapter.CurrentDisplayMode.Height;
graphics.PreferredBackBufferWidth = screenWidth; // Настроить размеры экранного буфера
graphics.PreferredBackBufferHeight = screenHeight;
graphics.IsFullScreen = true; // Перевести в полноэкранный режим
graphics.ApplyChanges(); // Применить настройки
base.Initialize();
}
.....................................................
protected override void Update(GameTime gameTime)
{
// Читать буфер клавиатуры
keyboardState = Keyboard.GetState();
// Выход из игрового процесса
if (keyboardState.IsKeyDown(Keys.Escape))
this.Exit();
base.Update(gameTime);
}
.....................................................
}-
В текущее
пространство имен перед классом StartGame3D добавьте объявление типа
перечисления GameState с
элементами обозначений игровых состояний, а в классе создайте поле
этого типа и инициализируйте ее заставкой начала игры
namespace Game3D
{
// Объявление состояний экранов
enum GameState
{
AboutScreen, // Экран с информацией об игре
GameOverScreen, // Экран проигрыша
GameScreen, // Экран игрового процесса
HelpScreen, // Инструкция о правилах
MenuScreen, // Меню игры
GameScreen, // Первая игровая заставка
VictotyScreen // Экран выигрыша
}
public class StartGame3D : Microsoft.Xna.Framework.Game
{
#region Поля класса
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
int screenWidth, screenHeight; // Размеры экрана
KeyboardState keyboardState; // Буфер клавиатуры
GameState gameState = GameState.GameScreen; // Переменная состояния игры
#endregion
..................................................
}
}-
Для работы нужного
блока кода в соответствии с установленным состоянием введите переключатель switch в
функциях Update() и Draw()
public class StartGame3D : Microsoft.Xna.Framework.Game
{
...................................................
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();
break;
case GameState.HelpScreen:
break;
case GameState.MenuScreen:
break;
case GameState.SplashScreen:
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:
break;
case GameState.VictotyScreen:
break;
}
base.Draw(gameTime);
}
}
}Загрузка модели мяча
В 3D-задачах используются уже не спрайты, а модели. Трехмерная модель в памяти компьютера называется мэш-объектом и хранится до загрузки в файле с расширением .x, возможно, вместе со своей текстурой. В трехмерной модели содержится набор координат примитивов (простых плоских фигур, чаще всего треугольников), с помощью которых конструируется сложная поверхность модели. Чем сложнее объемная фигура, тем больше треугольников требуется для ее точного описания.
Создадим класс, который будет загружать модель мяча и обеспечивать вывод его изображения на экран.
-
Командой Project/Add Class добавьте к проекту класс с именем ModelClass
-
Скопируйте из файла StartGame3D.cs в новый файл ModelClass.cs секцию using с подключениями пространств
имен -
Заполните файл ModelClass.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 ModelClass
{
// Поля
Model model;
Vector3 position;
// Свойства
public Vector3 Position
{
get { return position; }
set { position = value; }
}
// Конструктор
public ModelClass()
{
position = new Vector3(0, 0, 0);
}
// Загрузка модели
public void Load(ContentManager content, String strModel)
{
model = content.Load<Model>(strModel);
}
// Рисуем модель на экране
public void DrawModel(Matrix world, Matrix view, Matrix proj)
{
Matrix[] transforms = new Matrix[model.Bones.Count];
model.CopyAbsoluteBoneTransformsTo(transforms);
foreach (ModelMesh mesh in model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.EnableDefaultLighting();
effect.World = transforms[mesh.ParentBone.Index] * world;
effect.View = view;
effect.Projection = proj;
}
mesh.Draw();
}
}
}
}-
Добавьте в узел Content проекта папку Models командой Add/New Folder и скопируйте в нее командой Add/Existing Item файл Soccerball.x из прилагаемого каталога Source
-
Добавьте в секцию #region
Поля класса файла StartGame3D.cs следующие поля
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);
ModelClass ball;
#endregion
..........................................................
}-
Добавьте в конструктор
класса StartGame3D код создания экземпляра класса ModelClass
public StartGame3D()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
// Создание экземпляра класса ModelClass
ball = new ModelClass();
}-
Добавьте в функцию Initialize() класса StartGame3D код вычисления коэффициента aspectRatio для
компенсации искажения в проекционной матрице
protected override void Initialize()
{
GraphicsAdapter adapter = graphics.GraphicsDevice.
CreationParameters.Adapter; // Получить ссылку на параметры адаптера
screenWidth = adapter.CurrentDisplayMode.Width; // Сохранить текущие размеры экрана
screenHeight = adapter.CurrentDisplayMode.Height;
graphics.PreferredBackBufferWidth = screenWidth; // Настроить размеры экранного буфера
graphics.PreferredBackBufferHeight = screenHeight;
graphics.IsFullScreen = true; // Перевести в полноэкранный режим
graphics.ApplyChanges(); // Применить настройки
aspectRatio = (float)screenWidth / screenHeight; // Коэффициент искажения проекции
base.Initialize();
}Коэффициент aspectRatio служит для предотвращения искажения изображения, проецируемого на плоскость экрана. Поскольку областью просмотра в полноэкранном режиме служит весь экран и он не имеет равных сторон, то круглый мяч в трехмерном пространстве будет проецироваться на экран матрицей проекций как эллипс. А коэффициент aspectRatio как раз и компенсирует это искажение.
-
Добавьте в функцию LoadContent() класса StartGame3D код загрузки в программу модели мяча
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");
}-
Добавьте в вариант GameState.SplashScreen переключателя switch функции 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);
break;
case GameState.HelpScreen:
break;
case GameState.MenuScreen:
break;
case GameState.SplashScreen:
break;
case GameState.VictotyScreen:
break;
}
base.Draw(gameTime);
}-
Откомпилируйте проект
командой Rebuild и запустите его на выполнение. Должен получиться рисунок
с начальным положением мяча



