При выполнении в лабораторной работе упражнения №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); } } }
- Запустите приложение и убедитесь, что механизм смены состояний работает нормально