|
При выполнении в лабораторной работе упражнения №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
Расширение кода до трех разных моделей мячей и обеспечение их движения
Здесь мы выведем на экран три разных мяча и будем перемещать их в пространстве.
-
Добавьте
в папку Models проекта командой Add/New Item из прилагаемого
каталога Source файлы SoccerballGreen.x и SoccerballRed.x
-
Добавьте в класс ModelClass поле
и свойство доступа, определяющие скорость падения мячей сверху вниз
class ModelClass
{
// Поля
Model model;
Vector3 position;
float speed = 20.0f / 60.0f;
// Свойства
public Vector3 Position
{
get { return position; }
set { position = value; }
}
public float Speed
{ get { return speed; } }
// Конструктор
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();
}
}
}Переменная speed инициализируется значением 20.0f / 60.0f, где 60 означает количество секунд в одной минуте.
-
Добавьте в основной
класс игры код загрузки трех моделей мяча и установки их начальной позиции
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;
ModelClass[] ball = new ModelClass[3];
MouseState mouseState;
Random rand = new Random();
#endregion
public StartGame3D()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
// Создание экземпляров класса ModelClass
//ball = new ModelClass();
for (int i = 0; i < ball.Length; i++)
{
ball[i] = new ModelClass();
}
}
protected override void Initialize()
{
.......................................................
}
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");
// Устанавливаем начальную позицию объектов
BeginPosition();
}
// Устанавливаем начальную позицию объекта
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));
}
}
.......................................................
}-
Модифицируйте метод 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);
for (int i = 0; i < ball.Length; i++)
{
// Определить матрицу преобразования
world = Matrix.CreateTranslation(ball[i].Position);
// Нарисовать модель в начальном состоянии
ball[i].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 и выполните его. Должно получиться примерно следующее
Для имитации движения мячей сверху вниз нам нужно уменьшать координату y в методе обновленных вычислений Update(), поскольку ось Oy направлена снизу вверх. Кроме того, на данном этапе предусмотрим временный механизм возврата мячей в исходное состояние с помощью щелчка мыши на экране во вспомогательном методе MouseClick(). Ну, и временно для этого включим стандартный курсор в полноэкранном режиме.
-
Добавьте в класс StartGame3D код
включения курсора, а также методы MoveBalls() и MouseClick() со следующим содержимым
public StartGame3D()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
// Создание экземпляров класса ModelClass
//ball = new ModelClass();
for (int i = 0; i < ball.Length; i++)
{
ball[i] = new ModelClass();
}
// Включаем стандартный курсор мыши
this.IsMouseVisible = true;
}
.........................................................
void MoveBalls()
{
for (int i = 0; i < ball.Length; i++)
{
ball[i].Position -= new Vector3(0, ball[i].Speed, 0);
}
}
void MouseClick()
{
mouseState = Mouse.GetState();
if (mouseState.LeftButton == ButtonState.Pressed)
BeginPosition();
}-
Добавьте в вычислительный
метод Update() основного класса игры вызов функций MoveBalls() и MouseClick()
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:
break;
case GameState.VictotyScreen:
break;
}
base.Update(gameTime);
}-
Запустите проект и
убедитесь в работоспособности добавленных в программу механизмов
Механизм стрельбы по целям
На данном этапе мы добавим к программе вместо стандартного курсора прицел для стрельбы по мячам и создадим механизм попаданий выстрелов в цель.
В любой трехмерной игре могут на равных применяться и двухмерные изображения. Наша программа не исключение и мы вместо курсора будем использовать прицел, который реализуем в виде спрайта. Управление спрайтом и его отображение на экране упакуем в класс. Но чтобы не повторять заново уже разработанное, используем для этих целей класс Sprite из предыдущей лабораторной работы.
-
Командой Project/Add Existing Item скопируйте из предыдущей лабораторной работы или прилагаемого каталога Source файл Sprite.cs в корневой каталог текущего проекта
В этом разделе мы будем использовать библиотечный класс BoundingSphere для определения столкновений моделей и курсора и нам понадобится радиус ограничивающей мячи сферы. Чтобы не измерять этот радиус, введем переменную radius и определим ее значение, которое немного больше радиуса мячей.
-
Добавьте в класс ModelClass поле и свойство доступа для работы с радиусом попадания в цель
class ModelClass
{
// Поля
Model model;
Vector3 position;
float speed = 20.0f / 60.0f;
float radius = 8.0F;
// Свойства
public Vector3 Position
{
get { return position; }
set { position = value; }
}
public float Speed
{ get { return speed; } }
public float Radius
{ get { return radius; } }
...........................................
}-
В конструкторе класса StartGame3D отключите стандартный курсор мыши, закомментировав соответствующий
код
public StartGame3D()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
// Создание экземпляров класса ModelClass
//ball = new ModelClass();
for (int i = 0; i < ball.Length; i++)
{
ball[i] = new ModelClass();
}
// Включаем стандартный курсор мыши
//this.IsMouseVisible = true;
}-
Через Проводник решений
создайте для узла Content командой Add/New Folder контекстного
меню папку Textures и
скопируйте в нее из прилагаемого каталога Source файл рисунка прицела cursor.png
-
В классе StartGame3D создайте поле для хранения ссылки на объект курсора и инициализируйте ее
экземпляром класса Sprite
#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; ModelClass[] ball = new ModelClass[3]; MouseState mouseState; Random rand = new Random(); // Создание объекта курсора-прицела Game2D.Sprite cursor = new Game2D.Sprite(); #endregion
-
В методе LoadContent() класса StartGame3D загрузите
спрайт курсора в объект, а в методе Draw() поместите
код рисования курсора последним - после рисования мячей, чтобы он оказался
самым ближним к пользователю на экране монитора
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");
// Устанавливаем начальную позицию объектов
BeginPosition();
// Загружаем рисунок курсора-прицела
cursor.Load(this.Content, "Textures\\cursor");
}
.......................................................
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);
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);
}Система координат двухмерного и трехмерного пространств в XNA Framework существенно отличаются. Поэтому нужен особый механизм перевода двухмерных координат курсора, генерируемых операционной системой, в трехмерное пространство, построенное XNA. Мы воспользуемся готовым решением, приведенным в справочной информации по XNA Game Studio. Главным условием достоверности пересчета координат курсора на трехмерную сцену является совпадение значений матрицы вида и проекционной матрицы, применяемых для построения сцены, с теми же матрицами, определения координат курсора на этой сцене.
-
Добавьте к классу StartGame3D метод GetPickRay() пересчета координат курсора в трехмерное пространство
сцены
public class StartGame3D : Microsoft.Xna.Framework.Game
{
............................................................
// Метод пересчета координат курсора в трехмерную сцену игры
Ray GetPickRay()
{
// Читаем стандартные координаты курсора
mouseState = Mouse.GetState();
// Вспомогательные переменные
int mouseX = mouseState.X;
int mouseY = mouseState.Y;
Vector3 nearSource = new Vector3((float)mouseX, (float)mouseY, 0.0f);
Vector3 farSource = new Vector3((float)mouseX, (float)mouseY, 1.0f);
// Обнуляем мировую матрицу
world = Matrix.CreateTranslation(0, 0, 0);
// Матрица вида, как в трехмерной сцене
view = Matrix.CreateLookAt(camera, Vector3.Zero, Vector3.Up);
// Матрица проекции, как в трехмерной сцене
proj = Matrix.CreatePerspectiveFieldOfView(FOV, aspectRatio, nearClip, farClip);
// Проекция курсора на ближнюю плоскость отсечения объема
Vector3 nearPoint = graphics.GraphicsDevice.Viewport.Unproject(nearSource, proj, view, world);
// Проекция курсора на дальнюю плоскость отсечения объема
Vector3 farPoint = graphics.GraphicsDevice.Viewport.Unproject(farSource, proj, view, world);
Vector3 direction = farPoint - nearPoint;
direction.Normalize();
return new Ray(nearPoint, direction);
}
}Теперь нужно применить этот метод для создания механизма перемещения спрайта курсора по экрану и определения попаданий в движущиеся объекты мячей. Спусковым крючком выстрела по мячу будет служить левая кнопка мыши, поэтому весь механизм поместим в уже созданную нами ранее заготовку метода MouseClick().
-
Добавьте в класс StartGame3D поле
массива объектов типа BoundingSphere и модифицируйте метод MouseClick() следующим
образом
// Механизм стрельбы по целям
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));
}
}
}
}-
Запустите приложение
и проверьте механизм стрельбы по целям
