|
При выполнении в лабораторной работе упражнения №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
Управление состояниями программы
В предыдущих разделах мы подготовили графические заставки, включая саму игру, которые нам нужно показать пользователю в зависимости от его действий. При запуске программы мы автоматически покажем титульную заставку, определенную классом SplashScreen. Из этой заставки есть два пути: нажать клавишу Esc, чтобы завершить работу приложения, или нажать клавишу Enter. После нажатия клавиши Enter пользователь должен попасть на заставку меню, поддерживаемую классом MenuScreen. А далее после его выбора программа должна перейти в одно из состояний, определяемых основным классом игры StartGame3D или классами HelpScreen и AboutScreen.
По клавише Esc программа должна вернуться в состояние меню, откуда при повторном нажатии Esc или выбора пункта ' Выход ' - перейти в первую экранную заставку. В режиме меню для смены пунктов задействуем клавиши Up и Down (Стрелка вверх/Стрелка вниз), а для исполнения выбранной команды - клавишу Enter в соответствии с подсказкой в нижней части заставки меню.
Такую логику управления состояниями программы мы должны реализовать в этом разделе. Но здесь есть одна проблема, которую сразу нужно решить. Дело в том, что любая клавиша клавиатуры, кроме расширяющих клавиш Ctrl, Shift и Alt, при ее нажатии начинает непрерывно генерировать соответствующий ей код, пока не будет опущена. По этой причине в большинстве офисных программ, когда смена состояний закреплена за какими-то клавишами, ее связывают не с нажатием, а с отпусканием соответствующей клавиши.
В нашем игровом случае проблема смены состояния связана с тем, что пользователь ожидает ответных действий программы именно от нажатия командной клавиши, которое должно обеспечить одиночную реакцию программы. Для надежного блокирования эффекта генерации применим прием, который напоминает решение, используемое в гениальном создании под названием автомат Калашникова. В нем для гарантированного ведения огня одиночными выстрелами используется специальный запорный крючок - ' шептало одиночного огня '. Это 'шептало' после произведенного выстрела блокирует ударный механизм до тех пор, пока стрелок не отпустит спусковой крючок в исходное состояние. Только после этого можно производить следующий выстрел.
В нашей программе режим меню использует клавиши Enter, Up, Down и Esc. Поэтому в качестве запорного механизма будем использовать флаги enterFree, upFree, downFree и escFree. В исходном состоянии эти флаги подняты, но стоит пользователю нажать на управляющую клавишу, как срабатывает соответствующий блок кода и флаг опускается, блокируя повторное выполнение этого же кода при многократной генерации клавишей символов. Флаг восстанавливается только после отпускания пользователем активной клавиши.
Аналогичная проблема существует и с длительностью удержания кнопки мыши в нажатом состоянии. Если сейчас запустить приложение в режиме GameScreen, нажать левую кнопку мыши и, не отпуская ее, направлять курсор на мячи, то мы увидим этот эффект. В качестве запорного механизма для мыши будем использовать флаг mouseFree.
Пока мы разработали состояния программы, определяемые следующими значениями перечисления GameState:
- AboutScreen, // Экран с информацией об игре
- GameScreen, // Экран игрового процесса
- HelpScreen, // Инструкция о правилах
- MenuScreen, // Меню игры
- SplashScreen // Первая игровая заставка
-
Для реализации устойчивого
механизма управления этими состояниями внесите необходимые изменения
в код класса StartGame3D, как показано в следующем его полном,
на данный момент, листинге
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
{
// Объявление состояний экранов
enum GameState
{
AboutScreen, // Экран с информацией об игре
GameOverScreen, // Экран проигрыша
GameScreen, // Экран игрового процесса
HelpScreen, // Инструкция о правилах
MenuScreen, // Меню игры
SplashScreen, // Первая игровая заставка
VictotyScreen // Экран выигрыша
}
public class StartGame3D : Microsoft.Xna.Framework.Game
{
#region Поля класса
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
int screenWidth, screenHeight; // Размеры экрана
KeyboardState keyboardState; // Буфер клавиатуры
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();
AboutScreen about = new AboutScreen();
HelpScreen help = new HelpScreen();
MenuScreen menu = new MenuScreen();
int menuState = (int)MenuState.Game;
// Флаги освобождения задействованных клавиш
bool enterFree = true; // Enter
bool upFree=true; // Стрелка вверх
bool downFree = true; // Стрелка вниз
bool escFree = true; // Esc
// Флаг освобождения мыши
bool mouseFree = true;
#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();
}
// Включаем стандартный курсор мыши
//this.IsMouseVisible = true;
}
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();
}
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);
help.InitializeSplashScreen(this.Content, screenWidth, screenHeight);
menu.InitializeSplashScreen(this.Content, screenWidth, screenHeight);
}
// Устанавливаем начальную позицию объекта
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)));
}
}
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
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);
}
}
// Механизм стрельбы по целям
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 && mouseFree)
{
// Пересчитываем координаты курсора на трехмерный объем
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)));
}
}
}
// Ждем освобождения кнопки мыши
mouseFree = mouseState.LeftButton == ButtonState.Released;
}
protected override void Update(GameTime gameTime)
{
// Читать буфер клавиатуры
keyboardState = Keyboard.GetState();
switch (gameState)
{
case GameState.AboutScreen:
// Выход из AboutScreen
if (keyboardState.IsKeyDown(Keys.Escape) && escFree)
gameState = GameState.MenuScreen;
break;
case GameState.GameOverScreen:
break;
case GameState.GameScreen:
// Выход из MenuScreen
if (keyboardState.IsKeyDown(Keys.Escape) && escFree)
gameState = GameState.MenuScreen;
MoveBalls();
MouseClick();
break;
case GameState.HelpScreen:
// Выход из HelpScreen
if (keyboardState.IsKeyDown(Keys.Escape) && escFree)
gameState = GameState.MenuScreen;
break;
case GameState.MenuScreen:
// Выход из MenuScreen
if (keyboardState.IsKeyDown(Keys.Escape) && escFree)
gameState = GameState.SplashScreen;
// Вход в другие состояния
if (keyboardState.IsKeyDown(Keys.Enter) && enterFree)
{
if ((MenuState)menuState == MenuState.Game)
gameState = GameState.GameScreen;
else if ((MenuState)menuState == MenuState.Help)
gameState = GameState.HelpScreen;
else if ((MenuState)menuState == MenuState.About)
gameState = GameState.AboutScreen;
else if ((MenuState)menuState == MenuState.Esc)
gameState = GameState.SplashScreen;
}
// Управление пунктами меню
if (keyboardState.IsKeyDown(Keys.Up) && upFree)
{
menuState--;
if (menuState < 0)
menuState = 3;
}
else if (keyboardState.IsKeyDown(Keys.Down) && downFree)
{
menuState++;
menuState %= 4; // Деление по модулю
}
break;
case GameState.SplashScreen:
// Выход из SplashScreen
if (keyboardState.IsKeyDown(Keys.Escape) && escFree)
this.Exit();
else if (keyboardState.IsKeyDown(Keys.Enter) && enterFree)
gameState = GameState.MenuScreen;
break;
case GameState.VictotyScreen:
break;
}
// Устанавливаем флаги освобождения клавиш
enterFree = keyboardState.IsKeyUp(Keys.Enter);
upFree = keyboardState.IsKeyUp(Keys.Up);
downFree = keyboardState.IsKeyUp(Keys.Down);
escFree = keyboardState.IsKeyUp(Keys.Escape);
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:
// Нарисовать фон самым первым
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:
help.DrawScreen(this.spriteBatch, this.graphics, screenWidth, screenHeight, gameTime);
break;
case GameState.MenuScreen:
menu.DrawScreen(
this.spriteBatch, this.graphics, screenWidth, screenHeight, gameTime, menuState);
break;
case GameState.SplashScreen:
splash.DrawScreen(this.spriteBatch, this.graphics, screenWidth, screenHeight, gameTime);
break;
case GameState.VictotyScreen:
break;
}
base.Draw(gameTime);
}
// Метод пересчета координат курсора в трехмерную сцену игры
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);
}
}
}-
Запустите приложение
и убедитесь, что механизм смены состояний работает нормально