| Невозможно пройти тесты, в окне с вопросами пусто |
Опубликован: 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 собственную игру, которая реализует следующие возможности:
- Систему меню
- Систему подсчета очков пользователя
- Увеличение сложности игры при прохождении в следующий уровень
