Невозможно пройти тесты, в окне с вопросами пусто |
Опубликован: 14.08.2012 | Уровень: специалист | Доступ: платный
Самостоятельная работа 19:
Игровой мир, освещение, тени
В листинге 26.4 вы можете найти код класса Game1.
using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Input.Touch; namespace P19_2 { /// <summary> /// Это главный тип игры /// </summary> public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; //Матрицы Matrix viewMatrix; Matrix projMatrix; //Модели modCls[] cls; //Положение моделей в пространстве Vector3[] offset; //Соотношение сторон окна вывода float aspectRatio; //Плоскость modCls plane; //Направление света Vector3 LightDirection; //Стрелки Texture2D txtArrows; Rectangle keyForward = new Rectangle(500, 280, 100, 100); Rectangle keyUp = new Rectangle(600, 280, 100, 100); Rectangle keyBack = new Rectangle(700, 280, 100, 100); Rectangle keyLeft = new Rectangle(500, 380, 100, 100); Rectangle keyDown = new Rectangle(600, 380, 100, 100); Rectangle keyRight = new Rectangle(700, 380, 100, 100); public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; graphics.IsFullScreen = true; // Частота кадра на Windows Phone по умолчанию — 30 кадров в секунду. TargetElapsedTime = TimeSpan.FromTicks(333333); // Дополнительный заряд аккумулятора заблокирован. InactiveSleepTime = TimeSpan.FromSeconds(1); } /// <summary> /// Позволяет игре выполнить инициализацию, необходимую перед запуском. /// Здесь можно запросить нужные службы и загрузить неграфический /// контент. Вызов base.Initialize приведет к перебору всех компонентов и /// их инициализации. /// </summary> protected override void Initialize() { // ЗАДАЧА: добавьте здесь логику инициализации base.Initialize(); } /// <summary> /// LoadContent будет вызываться в игре один раз; здесь загружается /// весь контент. /// </summary> protected override void LoadContent() { // Создайте новый SpriteBatch, который можно использовать для отрисовки текстур. spriteBatch = new SpriteBatch(GraphicsDevice); //Вычислим соотношение сторон aspectRatio = (float)graphics.GraphicsDevice.Viewport.Width / (float)graphics.GraphicsDevice.Viewport.Height; //Матрица вида viewMatrix = Matrix.CreateLookAt(new Vector3(0, 10, 15), new Vector3(0, 0, 0), new Vector3(0.0f, 1.0f, 0.0f)); //Матрица проекции projMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), aspectRatio, 1.0f, 1000.0f); LightDirection = new Vector3(-2, -2, 0); //Создаем плоскость plane = new modCls(this, Content.Load<Model>("plane"), graphics, new Plane()); //Настраиваем параметры плоскости plane.WorldMatrix = Matrix.CreateScale(25) * Matrix.CreateRotationY(MathHelper.ToRadians(90)) * Matrix.CreateRotationZ(MathHelper.ToRadians(90)) * Matrix.CreateTranslation(0, -4, 0); plane.ViewMatrix = viewMatrix; plane.ProjectMatrix = projMatrix; plane.LightDirection = LightDirection; Components.Add(plane); //Плоскость, на которую проецируется тень //Она немного выше плоскости, которую мы выводим на экран //для того, чтобы тень была видна Plane pl1 = new Plane(new Vector3(0, -3.9f, 0), new Vector3(2, -3.9f, 1), new Vector3(-1, -3.9f, -2)); //Три элемента в массиве объектов cls = new modCls[3]; //Три элемента в массиве положений объектов offset = new Vector3[3]; //Зададим начальные положения моделей offset[0] = new Vector3(0, 2, 0); offset[1] = new Vector3(-6, 3, -4); offset[2] = new Vector3(6, 4, -6); //Создадим модели cls[0] = new modCls(this, Content.Load<Model>("ball2"), graphics, pl1); cls[1] = new modCls(this, Content.Load<Model>("ball2"), graphics, pl1); cls[2] = new modCls(this, Content.Load<Model>("ball2"), graphics, pl1); //Задаем параметры матриц и направления света каждой из моделей for (int i = 0; i < 3; i++) { cls[i].WorldMatrix = Matrix.CreateTranslation(offset[i]); cls[i].ProjectMatrix = projMatrix; cls[i].ViewMatrix = viewMatrix; cls[i].LightDirection = LightDirection; Components.Add(cls[i]); } txtArrows = Content.Load<Texture2D>("Arrows"); } /// <summary> /// UnloadContent будет вызываться в игре один раз; здесь выгружается /// весь контент. /// </summary> protected override void UnloadContent() { // ЗАДАЧА: выгрузите здесь весь контент, не относящийся к ContentManager } //Проверка попадания касания в область, занимаемую одним из элементов управления private bool MenuSelect(Rectangle m, Vector2 p) { bool res = false; if (p.X > m.X && p.X < m.X + m.Width && p.Y > m.Y && p.Y < m.Y + m.Height) { res = true; } return res; } /// <summary> /// Позволяет игре запускать логику обновления мира, /// проверки столкновений, получения ввода и воспроизведения звуков. /// </summary> /// <param name="gameTime">Предоставляет моментальный снимок значений времени.</param> protected override void Update(GameTime gameTime) { // Позволяет выйти из игры if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); //Для случайных колебаний моделей Random rand = new Random(); //В каждом проходе цикла //Смещаем каждую модель вниз на 0.04 единицы //И на небольшое случайное число вдоль оси X и Y - модели //слегка колеблются при добавлении этого изменения //Мы не ввели ограничения на изменение X и Z - //При практической реалиации данного примера это //желательно сделать for (int i = 0; i < 3; i++) { offset[i].Y -= 0.04f; offset[i].X -= (0.08f * (float)rand.NextDouble() - 0.08f * (float)rand.NextDouble()); offset[i].Z -= (0.08f * (float)rand.NextDouble() - 0.08f * (float)rand.NextDouble()); cls[i].WorldMatrix = Matrix.CreateTranslation(offset[i]); } //Проверяем попадание мыши по объекту CheckTouch(); //Проверяем пересечение с плоскостью CheckPlane(); //Меняем позицию источника света по нажатию клавиш LightSourceControl(); base.Update(gameTime); } //Изменение позиции источника света void LightSourceControl() { TouchCollection touchLocations = TouchPanel.GetState(); foreach (TouchLocation touchLocation in touchLocations) { if (touchLocation.State == TouchLocationState.Pressed || touchLocation.State == TouchLocationState.Moved) { //Увеличим позицию Y if (MenuSelect(keyForward, touchLocation.Position)) { LightDirection.Y += 0.5f; } //Уменьшим позицию по Z if (MenuSelect(keyUp, touchLocation.Position)) { LightDirection.Z -= 0.5f; } //Уменьшим позицию Y if (MenuSelect(keyBack, touchLocation.Position)) { LightDirection.Y -= 0.5f; } //Уменьшим позицию X if (MenuSelect(keyLeft, touchLocation.Position)) { LightDirection.X -= 0.5f; } //Увеличим позицию Z if (MenuSelect(keyDown, touchLocation.Position)) { LightDirection.Z += 0.5f; } //Увеличим позицию X if (MenuSelect(keyRight, touchLocation.Position)) { LightDirection.X += 0.5f; } } } //Изменим направление источника света для плоскости plane.LightDirection = LightDirection; //Изменим направление источника света для моделей for (int i = 0; i < 3; i++) { cls[i].LightDirection = LightDirection; } } //Обработка касаний void CheckTouch() { TouchCollection touchLocations = TouchPanel.GetState(); foreach (TouchLocation touchLocation in touchLocations) { if (touchLocation.State == TouchLocationState.Pressed) { //Получим луч, идущий от позиции мыши на экране в пространство Ray pickRay = GetPickRay(touchLocation.Position); //Переменная для хранения сферы, соответствующей объекту BoundingSphere b1; //Переменная для хранения вектора размера модели Vector3 scale; //Переменная для хранения информации о повороте модели Quaternion rotation; //Переменая для хранения информации о позиции модели Vector3 translation; for (int i = 0; i < 3; i++) { //Получить BoundingSphere для текущего объекта b1 = cls[i].myModel.Meshes[0].BoundingSphere; //Получить параметры - размер, поворот, позицию для объекта cls[i].WorldMatrix.Decompose(out scale, out rotation, out translation); //Установить центр сферы в соответствии с позицией объекта b1.Center = translation; //Получить результат пересечения луча и сферы Nullable<float> result = pickRay.Intersects(b1); //Если луч и сфера пересеклись - "поднять" объект до позиции Y=4 if (result.HasValue) { offset[i].Y = 4; cls[i].WorldMatrix = Matrix.CreateTranslation(offset[i]); } } } } } //Проверка на столкновение с плоскостью void CheckPlane() { //Создадим плоскость по трем точкам - она пересекает ось Y в точке -4 и не пересекает //другие оси Plane pl = new Plane(new Vector3(0, -4, 0), new Vector3(2, -4, 1), new Vector3(-1, -4, -2)); //Переменная для хранения типа пересечения с плоскостью //Она может сигнализировать о нахождении объекта перед плоскостью (выше в нашем случае) //за плоскостью и о пересечении с плоскостью PlaneIntersectionType plType; //Сфера для объекта BoundingSphere b1; //Переменная для хранения вектора размера модели Vector3 scale; //Переменная для хранения информации о повороте модели Quaternion rotation; //Переменая для хранения информации о позиции модели Vector3 translation; for (int i = 0; i < 3; i++) { //Получить BoundingSphere для текущего объекта b1 = cls[i].myModel.Meshes[0].BoundingSphere; //Получить параметры - размер, поворот, позицию для объекта cls[i].WorldMatrix.Decompose(out scale, out rotation, out translation); //Установить центр сферы в соответствии с позицией объекта b1.Center = translation; //Получить тип пересечения сферы объекта и плоскости plType = pl.Intersects(b1); //Если сфера пересекла плоскость if (plType == PlaneIntersectionType.Intersecting) { //Удалить соответствующий игровой объект из коллекции Components.Remove(cls[i]); } } } //Функция для вычисления луча, исходящего из точки, в которой //находился указатель мыши в момент щелчка //Функция для вычисления луча, исходящего из точки касания Ray GetPickRay(Vector2 touchPos) { //Точки для вычисления направления луча - одна, соответствующая координате мыши //вторая - направленная по оси Z Vector3 nearsource = new Vector3(touchPos.X, touchPos.Y, 0f); Vector3 farsource = new Vector3(touchPos.X, touchPos.Y, 1f); //Мировая матрица для вычислений Matrix world = Matrix.CreateTranslation(0, 0, 0); //Переведем координаты мыши в координаты трехмерного пространства Vector3 nearPoint = graphics.GraphicsDevice.Viewport.Unproject(nearsource, projMatrix, viewMatrix, world); Vector3 farPoint = graphics.GraphicsDevice.Viewport.Unproject(farsource, projMatrix, viewMatrix, world); // Получим направление луча Vector3 direction = farPoint - nearPoint; //Нормализуем его (преобразуем к единичному вектору) direction.Normalize(); //Создадим новый луч, начинающийся в точке, соответствующей координате //указателя мыши в объектном пространстве, с направлением, //соответствующим вычисленному направлению Ray pickRay = new Ray(nearPoint, direction); //возвратим луч в процедуру, вызывавшую данную функцию return pickRay; } /// <summary> /// Вызывается, когда игра отрисовывается. /// </summary> /// <param name="gameTime">Предоставляет моментальный снимок значений времени.</param> protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); base.Draw(gameTime); //Вывод стрелок spriteBatch.Begin(); spriteBatch.Draw(txtArrows, new Rectangle(500, 280, 300, 200), Color.White); spriteBatch.End(); //Для нормального отображение 3D-сцены после работы spriteBatch GraphicsDevice.DepthStencilState = DepthStencilState.Default; } } }Листинг 26.4. Код класса Game1
На рис. 26.5 вы можете видеть игровой экран проекта.
26.4. Выводы
В данной лабораторной работе мы рассмотрели подходы к созданию игрового мира, рассмотрели некоторые особенности освещения объектов.
26.5. Задание
Разработайте на основе проекта P19_2 собственную игру, которая реализует следующие возможности:
- Систему меню
- Систему подсчета очков пользователя
- Увеличение сложности игры при прохождении в следующий уровень