Создадим новый проект P15_1 и на его примере рассмотрим следующие пространственные преобразования объектов
Для выполнения этих операций нам понадобится модификация мировой матрицы. Модификации, соответствующие перемещению объекта мы выполним по клавиатурным командам, остальные модификации будут выполняться автоматически в цикле обновления.
Для этого примера мы создали несколько простых трехмерных моделей в редакторе Blender. В частности – это два разноцветных шара и куб. Вот как выглядит игровой экран проекта P15_1 (рис. 20.1.).
(рис 20.1) Игровой экран проекта P15_1Для упрощения работы с несколькими игровыми объектами мы создадим игровой компонент - modCls, который будет отвечать за хранение параметров, соответствующих этим объектам и за их визуализацию. В листинге 20.1. вы можете видеть код этого объекта.
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
namespace P15_1
{
public class modCls : Microsoft.Xna.Framework.DrawableGameComponent
{
//Модель
Model myModel;
//Мировая матрица
public Matrix WorldMatrix;
//Соотношение сторон экрана
public float aspectRatio;
//Для управления графическим устройством
GraphicsDeviceManager graphics;
//Конструктор получает на вход
//игровой класс, модель, объект для управления графическим устройством
public modCls(Game game, Model mod, GraphicsDeviceManager grf)
: base(game)
{
myModel = mod;
graphics = grf;
aspectRatio = (float)graphics.GraphicsDevice.Viewport.Width /
(float)graphics.GraphicsDevice.Viewport.Height;
}
public override void Initialize()
{
// TODO: Add your initialization code here
base.Initialize();
}
public override void Update(GameTime gameTime)
{
foreach (ModelMesh mesh in myModel.Meshes)
{
//Для каждого эффекта в сети
foreach (BasicEffect effect in mesh.Effects)
{
//Установить освещение по умолчанию
effect.LightingEnabled = true;
effect.EnableDefaultLighting();
//установить матрицы
effect.World = WorldMatrix;
effect.View = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, 10.0f), Vector3.Zero, Vector3.Up);
effect.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f),
aspectRatio, 1.0f, 1000.0f);
}
}
base.Update(gameTime);
}
public override void Draw(GameTime gameTime)
{
foreach (ModelMesh mesh in myModel.Meshes)
{
mesh.Draw();
}
base.Draw(gameTime);
}
}
}
Управление объектами возложено на основной игровой класс – его метод Update используется для вычисления новых позиций объектов и обновления информации объектов. Код этого класса вы можете видеть в листинге 20.2. Шар, расположенный сверху, автоматически циклически изменяет размер, куб вращается вокруг осей X и Y, шар, расположенный ниже, можно перемещать. Клавиши-стрелки используются для перемещения по осям X и Y (вверх, вниз, влево и вправо), клавиши W и S перемещают объект вперед и назад по оси Z.
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.Net;
using Microsoft.Xna.Framework.Storage;
namespace P15_1
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
//Мировая матрица
Matrix WorldMatrixM, WorldMatrixM1, WorldMatrixM2;
//Направление движения для модели modelCls1
Vector3 directM1;
//Модели -
//modelCls вращается
//modelCls1 перемещается по клавиатурным командам
//modelCls2 изменяет размер
modCls modelCls, modelCls1, modelCls2;
//Переменные для текущего размера и
//текущего приращения размера модели modelCls2
float size, sizeDelta;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
protected override void LoadContent()
{
//загрузка моделей и создание объектов, используемых для
//хранения информации о моделях и их вывода на экран
modelCls = new modCls(this, Content.Load<Model>("cube"), graphics);
modelCls1 = new modCls(this, Content.Load<Model>("ball"), graphics);
modelCls2 = new modCls(this, Content.Load<Model>("ball2"), graphics);
//Добавляем объекты в коллекцию Components - для их
//автоматического вывода на экран
Components.Add(modelCls);
Components.Add(modelCls1);
Components.Add(modelCls2);
//Значения для приращения размера и текущего размера
sizeDelta = 0.01f;
size = 0.5f;
//Установка исходного положения для модели modelCls1
directM1 = new Vector3(2.0f, -1.0f, 1.0f);
//Установка матриц, которые используются для управления объектами
WorldMatrixM = Matrix.Identity;
WorldMatrixM1 = Matrix.CreateTranslation(directM1);
WorldMatrixM2 = Matrix.CreateScale(size);
}
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
protected override void Update(GameTime gameTime)
{
KeyboardState kb = Keyboard.GetState();
//По нажатии клавиши-стрелки "вверх" перемещаем модель
//modelCls1 вверх
if (kb.IsKeyDown(Keys.Up))
{
directM1.Y=directM1 .Y +0.1f;
WorldMatrixM1 = Matrix.CreateTranslation(directM1);
}
//Нажатие клавиши "вниз" - перемещение вниз
if (kb.IsKeyDown(Keys.Down))
{
directM1.Y = directM1.Y - 0.1f;
WorldMatrixM1 = Matrix.CreateTranslation(directM1);
}
//Нажатие клавиши "влево" - перемещение влево
if (kb.IsKeyDown(Keys.Left))
{
directM1.X = directM1.X - 0.1f;
WorldMatrixM1 = Matrix.CreateTranslation(directM1);
}
//Нажатие клавиши "вправо" - перемещение вправо
if (kb.IsKeyDown(Keys.Right))
{
directM1.X = directM1.X + 0.1f;
WorldMatrixM1 = Matrix.CreateTranslation(directM1);
}
//Нажатие клавиши "W" - перемещение вперед
if (kb.IsKeyDown(Keys.W))
{
directM1.Z = directM1.Z - 0.1f;
WorldMatrixM1 = Matrix.CreateTranslation(directM1);
}
//Нажатие клавиши "S" - перемещение назад
if (kb.IsKeyDown(Keys.S))
{
directM1.Z = directM1.Z + 0.1f;
WorldMatrixM1 = Matrix.CreateTranslation(directM1);
}
//Если текущий размер меньше 0.5 -
//устанавливаем приращение, равное 0.01
//Если текущий размер больше 1.5 -
//устанавливаем приращение -0.01
if (size < 0.5) sizeDelta = 0.01f;
if (size > 1.5) sizeDelta = -0.01f;
size += sizeDelta;
//Матрица для объекта modelCls1 устанавливается для поворота его вокруг
//осей X и Y на один градус
WorldMatrixM = WorldMatrixM * Matrix.CreateRotationY(MathHelper.ToRadians(1))*
Matrix .CreateRotationX (MathHelper .ToRadians (1));
//Установка матрицы для объекта modelCls2
WorldMatrixM2 = Matrix.CreateScale(size) * Matrix.CreateTranslation(new Vector3(-5.0f, 2.0f, -3.0f));
//Передаем матрицы объектам
modelCls.WorldMatrix = WorldMatrixM;
modelCls1.WorldMatrix = WorldMatrixM1;
modelCls2.WorldMatrix = WorldMatrixM2;
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
base.Draw(gameTime);
}
}
}
Рассмотрев способы модификации объектов, поговорим об особенностях настройки камеры.
Камера играет огромную роль в трехмерных играх. В играх от первого лица позиция камеры ассоциируется с позицией игрока. Примерами таких игр игры класса simulator, shooter. В играх от третьего лица (strategy) камера расположена выше игрока и сдвинута назад – играющий может видеть своего персонажа и часть карты.
Рассмотрим простой пример управления камерой, в котором реализовано перемещение камеры, а так же изменение угла зрения. Фактически это – пример работы камеры от первого лица. Изменение угла зрения очень похоже на изменение фокусного расстояния в оптических приборах – увеличение фокусного расстояния, которое приводит к уменьшению угла зрения, позволяет "приближать" далеко расположенные объекты. Уменьшение фокусного расстояния, соответствующее увеличению угла зрения, позволяет охватить взглядом большую территорию.
Создадим новый проект – P15_2. Рассмотрим на его примере вышеописанные операции с камерой.
В листинге 20.3. вы можете найти код класса Game1, реализующего демонстрацию работы с камерой.
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.Net;
using Microsoft.Xna.Framework.Storage;
namespace P15_2
{
public class Game1 : Microsoft.Xna.Framework.Game
{
//соотношение сторон окна вывода
float aspectRatio;
//Матрицы
Matrix viewMatrix;
Matrix projMatrix;
Matrix worldMatrix;
//Модель
Model ball;
// Позиция и вращение камеры
Vector3 camPos = new Vector3(0, 0, -50);
float camRotation;
// Направление камеры
Vector3 cameraDirection = new Vector3(0, 0, 1);
// Установка скорости поворота и передвижения
float rotationSpeed = 0.01f;
float forwardSpeed = 1.0f;
//Угол зрения камеры
float viewAngle = MathHelper.ToRadians(45.0f);
GraphicsDeviceManager graphics;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
protected override void LoadContent()
{
//Загрузка модели
ball = Content.Load<Model>("ball");
//Установка мировой матрицы
worldMatrix = Matrix.Identity;
//Соотношение сторон
aspectRatio = (float)graphics.GraphicsDevice.Viewport.Width /
(float)graphics.GraphicsDevice.Viewport.Height;
}
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
protected override void Update(GameTime gameTime)
{
//Получить состояние клавиатуры
KeyboardState keyboardState = Keyboard.GetState();
//Поворот влево
if (keyboardState.IsKeyDown(Keys.Left))
{
camRotation += rotationSpeed;
}
//Поворот вправо
if (keyboardState.IsKeyDown(Keys.Right))
{
camRotation -= rotationSpeed;
}
//Переедвижение вперед
if (keyboardState.IsKeyDown(Keys.Up))
{
//Создаем матрицу поворота
Matrix forwardMov = Matrix.CreateRotationY(camRotation);
//Создаем новый вектор, содержащий скорость движения
Vector3 v = new Vector3(0, 0, forwardSpeed);
//Трансформируем матрицу в вектор
v = Vector3.Transform(v, forwardMov);
//модифицируем позицию таким образом, чтобы
//она соответствовала новым данным
camPos.Z += v.Z;
camPos.X += v.X;
}
//Передвижение назад
if (keyboardState.IsKeyDown(Keys.Down))
{
Matrix forwardMovement = Matrix.CreateRotationY(camRotation);
Vector3 v = new Vector3(0, 0, -forwardSpeed);
v = Vector3.Transform(v, forwardMovement);
camPos.Z += v.Z;
camPos.X += v.X;
}
//Уменьшение угла обзора камеры
if (keyboardState.IsKeyDown(Keys.W))
{
viewAngle -= MathHelper.ToRadians(1.0f);
}
//Увеличение угла обзора камеры
if (keyboardState.IsKeyDown(Keys.S))
{
viewAngle += MathHelper.ToRadians(1.0f);
}
//Если новый угол обзора вышел за дозволенные пределы
//изменяем его
if (viewAngle > MathHelper.ToRadians(180.0f)) viewAngle = MathHelper.ToRadians(179.9f);
if (viewAngle < MathHelper.ToRadians(0.0f)) viewAngle = MathHelper.ToRadians(0.1f);
Matrix rotationMatrix = Matrix.CreateRotationY(camRotation);
// Вектор направления камеры
Vector3 cameraTransformed = Vector3.Transform(cameraDirection, rotationMatrix);
// Вычисляем положение камеры
Vector3 cameraLookat = camPos + cameraTransformed;
// Устанавливаем матрицу вида
viewMatrix = Matrix.CreateLookAt(camPos, cameraLookat, new Vector3(0.0f, 1.0f, 0.0f));
//Устанавливаем проекционную матрицу
projMatrix = Matrix.CreatePerspectiveFieldOfView(viewAngle, aspectRatio, 1.0f, 1000.0f);
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values</param>
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
foreach (ModelMesh mesh in ball.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.LightingEnabled = true;
effect.EnableDefaultLighting();
effect.Projection = projMatrix;
effect.View = viewMatrix;
effect.World = worldMatrix;
}
mesh.Draw();
}
base.Draw(gameTime);
}
}
}
На рис. 20.2. вы можете видеть изображение игрового экрана проекта P15_2.
(рис 20.2) Экран проекта P15_2Шар, который мы видим в окне, искажен – угол обзора камеры увеличен, а расстояние от камеры до объекта сильно уменьшено.
Рассмотрим пример, иллюстрирующий работу с камерой в режиме от первого лица и в режиме вида от третьего лица. Для подготовки этого примера мы использовали справочные материалы по XNA.
Создадим новый проект P15_3. Наш проект выполняет следующие действия. Он выводит на экран плоскость и набор кубов, среди которых может двигаться шар. Причем, движение может осуществляться в двух режимах. В первом режиме демонстрируется работа камеры при виде от первого лица – шар в таком случае не выводится, сцена выглядит так, как будто мы смотрим на нее с позиции шара. Во втором режиме демонстрируется вид от третьего лица – камера находится выше объекта. В играх часто применяется следующий прием – главный игровой объект неподвижен, а игровой мир перемещается, создавая иллюзию движения этого объекта. В нашем случае все выглядит точно так же – шар неподвижно находится в центре экрана, но за счет перемещения игровой сцены создается иллюзия его движения по экрану и по сцене.
В листинге 20.4. вы можете найти код класса Game1, который содержит пример. Для перемещения используются клавиши-стрелки, для смены состояний камеры – клавиши A и S. Клавиша A переключает программу в режим камеры от первого лица, S – в режим вида от третьего лица. Клавиша R уменьшает поле зрения камеры, клавиша F – увеличивает.
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.Net;
using Microsoft.Xna.Framework.Storage;
namespace P15_3
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
//Матрицы
Matrix viewMatrix;
Matrix projMatrix;
//Модели
Model ball, cube, plane;
// Позиция объекта, поворот
Vector3 avatarPosition = new Vector3(0, 0, -50);
float avatarlRotation;
// Положение камеры
Vector3 cameraReference = new Vector3(0, 0, 10);
Vector3 thirdPersonReference = new Vector3(0, 200, -200);
// Скорости поворота и движения
float rotationSpeed = 1f / 60f;
float forwardSpeed = 50f / 60f;
//Поле зрения камеры
float viewAngle = MathHelper.ToRadians(45.0f);
//Расстояние от камеры до переднего и заднего плана
//static float nearClip = 5.0f;
float nearClip = 1.0f;
float farClip = 2000.0f;
// Установка позиции камеры 2 - вид от третьего лица
//1 - от первого лица
int cameraState = 2;
//Соотношение сторон экрана
float aspectRatio;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
protected override void LoadContent()
{
//Загрузка моделей
ball = Content.Load<Model>("ball");
cube = Content.Load<Model>("cube");
plane = Content.Load<Model>("plane");
aspectRatio = (float)graphics.GraphicsDevice.Viewport.Width /
(float)graphics.GraphicsDevice.Viewport.Height;
}
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
protected override void Update(GameTime gameTime)
{
//обновить положение объекта
UpdateAvatarPosition();
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
//Если состояние камеры 1 - вызвать процедуру
//обновлеие камеры от первого лица
//если 2 - от третьего лица
switch (cameraState)
{
case 1:
UpdateCamera();
break;
case 2:
UpdateCameraThirdPerson();
break;
}
//вывести объекты сцены
DrawScene();
//Если выбран вид от третьего лица - вывести объект
if (cameraState == 2)
{
DrawModel(ball, Matrix.CreateRotationY(avatarlRotation) * Matrix.CreateTranslation(avatarPosition));
}
base.Draw(gameTime);
}
//Обновляем состояние объекта
void UpdateAvatarPosition()
{
KeyboardState keyboardState = Keyboard.GetState();
//Поворот влево
if (keyboardState.IsKeyDown(Keys.Left))
{
avatarlRotation += rotationSpeed;
}
//Поворот вправо
if (keyboardState.IsKeyDown(Keys.Right) )
{
avatarlRotation -= rotationSpeed;
}
//Движение вперед
if (keyboardState.IsKeyDown(Keys.Up) )
{
Matrix forwardMovement = Matrix.CreateRotationY(avatarlRotation);
Vector3 v = new Vector3(0, 0, forwardSpeed);
v = Vector3.Transform(v, forwardMovement);
avatarPosition.Z += v.Z;
avatarPosition.X += v.X;
}
//Движение назад
if (keyboardState.IsKeyDown(Keys.Down) )
{
Matrix forwardMovement = Matrix.CreateRotationY(avatarlRotation);
Vector3 v = new Vector3(0, 0, -forwardSpeed);
v = Vector3.Transform(v, forwardMovement);
avatarPosition.Z += v.Z;
avatarPosition.X += v.X;
}
//Переключить на режим камеры от первого лица
if (keyboardState.IsKeyDown(Keys.A))
{
cameraState=1;
}
//Переключить на режим камеры от третьего лица
if (keyboardState.IsKeyDown(Keys.S))
{
cameraState = 2;
}
//Уменьшение угла обзора камеры
if (keyboardState.IsKeyDown(Keys.R))
{
viewAngle -= MathHelper.ToRadians(1.0f);
}
//Увеличение угла обзора камеры
if (keyboardState.IsKeyDown(Keys.F))
{
viewAngle += MathHelper.ToRadians(1.0f);
}
//Если новый угол обзора вышел за дозволенные пределы
//изменяем его
if (viewAngle > MathHelper.ToRadians(180.0f)) viewAngle = MathHelper.ToRadians(179.9f);
if (viewAngle < MathHelper.ToRadians(0.0f)) viewAngle = MathHelper.ToRadians(0.1f);
}
//Обновляем состояние камеры при выбранном виде от первого лица
void UpdateCamera()
{
//Текущая позиция камеры
Vector3 cameraPosition = avatarPosition;
//Поворот
Matrix rotationMatrix = Matrix.CreateRotationY(avatarlRotation);
// Направление камеры
Vector3 transformedReference = Vector3.Transform(cameraReference, rotationMatrix);
// Новая позиция камеры
Vector3 cameraLookat = cameraPosition + transformedReference;
//Матрица вида
viewMatrix = Matrix.CreateLookAt(cameraPosition, cameraLookat, new Vector3(0.0f, 1.0f, 0.0f));
//проекционная матрица
projMatrix = Matrix.CreatePerspectiveFieldOfView(viewAngle, aspectRatio, nearClip, farClip);
}
//Обновляем положение камеры при выбранном виде от третьего лица
void UpdateCameraThirdPerson()
{
//Поворот камеры
Matrix rotationMatrix = Matrix.CreateRotationY(avatarlRotation);
// Направление камеры
Vector3 transformedReference = Vector3.Transform(thirdPersonReference, rotationMatrix);
// Позиция камеры
Vector3 cameraPosition = transformedReference + avatarPosition;
//Матрица вида
viewMatrix = Matrix.CreateLookAt(cameraPosition, avatarPosition, new Vector3(0.0f, 1.0f, 0.0f));
//Проецкионная матрица
projMatrix = Matrix.CreatePerspectiveFieldOfView(viewAngle, aspectRatio, nearClip, farClip);
}
//Вывод объектов сцены
void DrawScene()
{
//Вывести кубы, расположенные в пять рядов
//по пять штук в трех уровнях
for (int x = 0; x < 5; x++)
{
for (int y = 0; y < 3; y++)
{
for (int z = 0; z < 5; z++)
{
DrawModel(cube, Matrix.CreateTranslation(x * 40, y * 3, z * 40));
}
}
}
//Вывести плоскость - при выводе увеличиваем модель и поворачиваем соответствующим
//образом
DrawModel(plane, Matrix.CreateScale (100)*Matrix .CreateRotationY (MathHelper .ToRadians (90))*
Matrix.CreateRotationZ(MathHelper.ToRadians(90))*
Matrix .CreateTranslation (80,-2.5f,80));
}
//Процедура вывода модели
//Принимает мировую матрицу для позиционирования модели
//и переменную модели
void DrawModel(Model model, Matrix world)
{
foreach (ModelMesh mesh in model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.LightingEnabled = true;
effect.EnableDefaultLighting();
effect.Projection = projMatrix;
effect.View = viewMatrix;
effect.World = world;
}
mesh.Draw();
}
}
}
}
На рис. 20.3. вы можете видеть состояние игрового экрана проекта P15_3 в режиме вида от третьего лица.
(рис 20.3) Экран проекта P15_3, вид от третьего лицаНа рис. 20.4. вы можете видеть состояние экрана проекта P15_3 в режиме вида от первого лица.
(рис 20.4) Экран проекта P15_3, вид от первого лицаНа рис. 20.5. вы можете видеть состояние экрана проекта P15_3 в режиме вида от третьего лица с уменьшенным полем видимости камеры.
(рис 20.5) Экран проекта P15_3, вид от третьего лица, уменьшенное поле видимости Создадим новый проект P15_1 и на его примере рассмотрим следующие пространственные преобразования объектов
Для выполнения этих операций нам понадобится модификация мировой матрицы. Модификации, соответствующие перемещению объекта мы выполним по клавиатурным командам, остальные модификации будут выполняться автоматически в цикле обновления.
Для этого примера мы создали несколько простых трехмерных моделей в редакторе Blender. В частности – это два разноцветных шара и куб. Вот как выглядит игровой экран проекта P15_1 (рис. 20.1.).
(рис 20.1) Игровой экран проекта P15_1Для упрощения работы с несколькими игровыми объектами мы создадим игровой компонент - modCls, который будет отвечать за хранение параметров, соответствующих этим объектам и за их визуализацию. В листинге 20.1. вы можете видеть код этого объекта.
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
namespace P15_1
{
public class modCls : Microsoft.Xna.Framework.DrawableGameComponent
{
//Модель
Model myModel;
//Мировая матрица
public Matrix WorldMatrix;
//Соотношение сторон экрана
public float aspectRatio;
//Для управления графическим устройством
GraphicsDeviceManager graphics;
//Конструктор получает на вход
//игровой класс, модель, объект для управления графическим устройством
public modCls(Game game, Model mod, GraphicsDeviceManager grf)
: base(game)
{
myModel = mod;
graphics = grf;
aspectRatio = (float)graphics.GraphicsDevice.Viewport.Width /
(float)graphics.GraphicsDevice.Viewport.Height;
}
public override void Initialize()
{
// TODO: Add your initialization code here
base.Initialize();
}
public override void Update(GameTime gameTime)
{
foreach (ModelMesh mesh in myModel.Meshes)
{
//Для каждого эффекта в сети
foreach (BasicEffect effect in mesh.Effects)
{
//Установить освещение по умолчанию
effect.LightingEnabled = true;
effect.EnableDefaultLighting();
//установить матрицы
effect.World = WorldMatrix;
effect.View = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, 10.0f), Vector3.Zero, Vector3.Up);
effect.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f),
aspectRatio, 1.0f, 1000.0f);
}
}
base.Update(gameTime);
}
public override void Draw(GameTime gameTime)
{
foreach (ModelMesh mesh in myModel.Meshes)
{
mesh.Draw();
}
base.Draw(gameTime);
}
}
}
Управление объектами возложено на основной игровой класс – его метод Update используется для вычисления новых позиций объектов и обновления информации объектов. Код этого класса вы можете видеть в листинге 20.2. Шар, расположенный сверху, автоматически циклически изменяет размер, куб вращается вокруг осей X и Y, шар, расположенный ниже, можно перемещать. Клавиши-стрелки используются для перемещения по осям X и Y (вверх, вниз, влево и вправо), клавиши W и S перемещают объект вперед и назад по оси Z.
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.Net;
using Microsoft.Xna.Framework.Storage;
namespace P15_1
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
//Мировая матрица
Matrix WorldMatrixM, WorldMatrixM1, WorldMatrixM2;
//Направление движения для модели modelCls1
Vector3 directM1;
//Модели -
//modelCls вращается
//modelCls1 перемещается по клавиатурным командам
//modelCls2 изменяет размер
modCls modelCls, modelCls1, modelCls2;
//Переменные для текущего размера и
//текущего приращения размера модели modelCls2
float size, sizeDelta;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
protected override void LoadContent()
{
//загрузка моделей и создание объектов, используемых для
//хранения информации о моделях и их вывода на экран
modelCls = new modCls(this, Content.Load<Model>("cube"), graphics);
modelCls1 = new modCls(this, Content.Load<Model>("ball"), graphics);
modelCls2 = new modCls(this, Content.Load<Model>("ball2"), graphics);
//Добавляем объекты в коллекцию Components - для их
//автоматического вывода на экран
Components.Add(modelCls);
Components.Add(modelCls1);
Components.Add(modelCls2);
//Значения для приращения размера и текущего размера
sizeDelta = 0.01f;
size = 0.5f;
//Установка исходного положения для модели modelCls1
directM1 = new Vector3(2.0f, -1.0f, 1.0f);
//Установка матриц, которые используются для управления объектами
WorldMatrixM = Matrix.Identity;
WorldMatrixM1 = Matrix.CreateTranslation(directM1);
WorldMatrixM2 = Matrix.CreateScale(size);
}
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
protected override void Update(GameTime gameTime)
{
KeyboardState kb = Keyboard.GetState();
//По нажатии клавиши-стрелки "вверх" перемещаем модель
//modelCls1 вверх
if (kb.IsKeyDown(Keys.Up))
{
directM1.Y=directM1 .Y +0.1f;
WorldMatrixM1 = Matrix.CreateTranslation(directM1);
}
//Нажатие клавиши "вниз" - перемещение вниз
if (kb.IsKeyDown(Keys.Down))
{
directM1.Y = directM1.Y - 0.1f;
WorldMatrixM1 = Matrix.CreateTranslation(directM1);
}
//Нажатие клавиши "влево" - перемещение влево
if (kb.IsKeyDown(Keys.Left))
{
directM1.X = directM1.X - 0.1f;
WorldMatrixM1 = Matrix.CreateTranslation(directM1);
}
//Нажатие клавиши "вправо" - перемещение вправо
if (kb.IsKeyDown(Keys.Right))
{
directM1.X = directM1.X + 0.1f;
WorldMatrixM1 = Matrix.CreateTranslation(directM1);
}
//Нажатие клавиши "W" - перемещение вперед
if (kb.IsKeyDown(Keys.W))
{
directM1.Z = directM1.Z - 0.1f;
WorldMatrixM1 = Matrix.CreateTranslation(directM1);
}
//Нажатие клавиши "S" - перемещение назад
if (kb.IsKeyDown(Keys.S))
{
directM1.Z = directM1.Z + 0.1f;
WorldMatrixM1 = Matrix.CreateTranslation(directM1);
}
//Если текущий размер меньше 0.5 -
//устанавливаем приращение, равное 0.01
//Если текущий размер больше 1.5 -
//устанавливаем приращение -0.01
if (size < 0.5) sizeDelta = 0.01f;
if (size > 1.5) sizeDelta = -0.01f;
size += sizeDelta;
//Матрица для объекта modelCls1 устанавливается для поворота его вокруг
//осей X и Y на один градус
WorldMatrixM = WorldMatrixM * Matrix.CreateRotationY(MathHelper.ToRadians(1))*
Matrix .CreateRotationX (MathHelper .ToRadians (1));
//Установка матрицы для объекта modelCls2
WorldMatrixM2 = Matrix.CreateScale(size) * Matrix.CreateTranslation(new Vector3(-5.0f, 2.0f, -3.0f));
//Передаем матрицы объектам
modelCls.WorldMatrix = WorldMatrixM;
modelCls1.WorldMatrix = WorldMatrixM1;
modelCls2.WorldMatrix = WorldMatrixM2;
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
base.Draw(gameTime);
}
}
}
Рассмотрев способы модификации объектов, поговорим об особенностях настройки камеры.
Камера играет огромную роль в трехмерных играх. В играх от первого лица позиция камеры ассоциируется с позицией игрока. Примерами таких игр игры класса simulator, shooter. В играх от третьего лица (strategy) камера расположена выше игрока и сдвинута назад – играющий может видеть своего персонажа и часть карты.
Рассмотрим простой пример управления камерой, в котором реализовано перемещение камеры, а так же изменение угла зрения. Фактически это – пример работы камеры от первого лица. Изменение угла зрения очень похоже на изменение фокусного расстояния в оптических приборах – увеличение фокусного расстояния, которое приводит к уменьшению угла зрения, позволяет "приближать" далеко расположенные объекты. Уменьшение фокусного расстояния, соответствующее увеличению угла зрения, позволяет охватить взглядом большую территорию.
Создадим новый проект – P15_2. Рассмотрим на его примере вышеописанные операции с камерой.
В листинге 20.3. вы можете найти код класса Game1, реализующего демонстрацию работы с камерой.
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.Net;
using Microsoft.Xna.Framework.Storage;
namespace P15_2
{
public class Game1 : Microsoft.Xna.Framework.Game
{
//соотношение сторон окна вывода
float aspectRatio;
//Матрицы
Matrix viewMatrix;
Matrix projMatrix;
Matrix worldMatrix;
//Модель
Model ball;
// Позиция и вращение камеры
Vector3 camPos = new Vector3(0, 0, -50);
float camRotation;
// Направление камеры
Vector3 cameraDirection = new Vector3(0, 0, 1);
// Установка скорости поворота и передвижения
float rotationSpeed = 0.01f;
float forwardSpeed = 1.0f;
//Угол зрения камеры
float viewAngle = MathHelper.ToRadians(45.0f);
GraphicsDeviceManager graphics;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
protected override void LoadContent()
{
//Загрузка модели
ball = Content.Load<Model>("ball");
//Установка мировой матрицы
worldMatrix = Matrix.Identity;
//Соотношение сторон
aspectRatio = (float)graphics.GraphicsDevice.Viewport.Width /
(float)graphics.GraphicsDevice.Viewport.Height;
}
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
protected override void Update(GameTime gameTime)
{
//Получить состояние клавиатуры
KeyboardState keyboardState = Keyboard.GetState();
//Поворот влево
if (keyboardState.IsKeyDown(Keys.Left))
{
camRotation += rotationSpeed;
}
//Поворот вправо
if (keyboardState.IsKeyDown(Keys.Right))
{
camRotation -= rotationSpeed;
}
//Переедвижение вперед
if (keyboardState.IsKeyDown(Keys.Up))
{
//Создаем матрицу поворота
Matrix forwardMov = Matrix.CreateRotationY(camRotation);
//Создаем новый вектор, содержащий скорость движения
Vector3 v = new Vector3(0, 0, forwardSpeed);
//Трансформируем матрицу в вектор
v = Vector3.Transform(v, forwardMov);
//модифицируем позицию таким образом, чтобы
//она соответствовала новым данным
camPos.Z += v.Z;
camPos.X += v.X;
}
//Передвижение назад
if (keyboardState.IsKeyDown(Keys.Down))
{
Matrix forwardMovement = Matrix.CreateRotationY(camRotation);
Vector3 v = new Vector3(0, 0, -forwardSpeed);
v = Vector3.Transform(v, forwardMovement);
camPos.Z += v.Z;
camPos.X += v.X;
}
//Уменьшение угла обзора камеры
if (keyboardState.IsKeyDown(Keys.W))
{
viewAngle -= MathHelper.ToRadians(1.0f);
}
//Увеличение угла обзора камеры
if (keyboardState.IsKeyDown(Keys.S))
{
viewAngle += MathHelper.ToRadians(1.0f);
}
//Если новый угол обзора вышел за дозволенные пределы
//изменяем его
if (viewAngle > MathHelper.ToRadians(180.0f)) viewAngle = MathHelper.ToRadians(179.9f);
if (viewAngle < MathHelper.ToRadians(0.0f)) viewAngle = MathHelper.ToRadians(0.1f);
Matrix rotationMatrix = Matrix.CreateRotationY(camRotation);
// Вектор направления камеры
Vector3 cameraTransformed = Vector3.Transform(cameraDirection, rotationMatrix);
// Вычисляем положение камеры
Vector3 cameraLookat = camPos + cameraTransformed;
// Устанавливаем матрицу вида
viewMatrix = Matrix.CreateLookAt(camPos, cameraLookat, new Vector3(0.0f, 1.0f, 0.0f));
//Устанавливаем проекционную матрицу
projMatrix = Matrix.CreatePerspectiveFieldOfView(viewAngle, aspectRatio, 1.0f, 1000.0f);
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values</param>
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
foreach (ModelMesh mesh in ball.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.LightingEnabled = true;
effect.EnableDefaultLighting();
effect.Projection = projMatrix;
effect.View = viewMatrix;
effect.World = worldMatrix;
}
mesh.Draw();
}
base.Draw(gameTime);
}
}
}
На рис. 20.2. вы можете видеть изображение игрового экрана проекта P15_2.
(рис 20.2) Экран проекта P15_2Шар, который мы видим в окне, искажен – угол обзора камеры увеличен, а расстояние от камеры до объекта сильно уменьшено.
Рассмотрим пример, иллюстрирующий работу с камерой в режиме от первого лица и в режиме вида от третьего лица. Для подготовки этого примера мы использовали справочные материалы по XNA.
Создадим новый проект P15_3. Наш проект выполняет следующие действия. Он выводит на экран плоскость и набор кубов, среди которых может двигаться шар. Причем, движение может осуществляться в двух режимах. В первом режиме демонстрируется работа камеры при виде от первого лица – шар в таком случае не выводится, сцена выглядит так, как будто мы смотрим на нее с позиции шара. Во втором режиме демонстрируется вид от третьего лица – камера находится выше объекта. В играх часто применяется следующий прием – главный игровой объект неподвижен, а игровой мир перемещается, создавая иллюзию движения этого объекта. В нашем случае все выглядит точно так же – шар неподвижно находится в центре экрана, но за счет перемещения игровой сцены создается иллюзия его движения по экрану и по сцене.
В листинге 20.4. вы можете найти код класса Game1, который содержит пример. Для перемещения используются клавиши-стрелки, для смены состояний камеры – клавиши A и S. Клавиша A переключает программу в режим камеры от первого лица, S – в режим вида от третьего лица. Клавиша R уменьшает поле зрения камеры, клавиша F – увеличивает.
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.Net;
using Microsoft.Xna.Framework.Storage;
namespace P15_3
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
//Матрицы
Matrix viewMatrix;
Matrix projMatrix;
//Модели
Model ball, cube, plane;
// Позиция объекта, поворот
Vector3 avatarPosition = new Vector3(0, 0, -50);
float avatarlRotation;
// Положение камеры
Vector3 cameraReference = new Vector3(0, 0, 10);
Vector3 thirdPersonReference = new Vector3(0, 200, -200);
// Скорости поворота и движения
float rotationSpeed = 1f / 60f;
float forwardSpeed = 50f / 60f;
//Поле зрения камеры
float viewAngle = MathHelper.ToRadians(45.0f);
//Расстояние от камеры до переднего и заднего плана
//static float nearClip = 5.0f;
float nearClip = 1.0f;
float farClip = 2000.0f;
// Установка позиции камеры 2 - вид от третьего лица
//1 - от первого лица
int cameraState = 2;
//Соотношение сторон экрана
float aspectRatio;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
}
protected override void LoadContent()
{
//Загрузка моделей
ball = Content.Load<Model>("ball");
cube = Content.Load<Model>("cube");
plane = Content.Load<Model>("plane");
aspectRatio = (float)graphics.GraphicsDevice.Viewport.Width /
(float)graphics.GraphicsDevice.Viewport.Height;
}
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
protected override void Update(GameTime gameTime)
{
//обновить положение объекта
UpdateAvatarPosition();
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
//Если состояние камеры 1 - вызвать процедуру
//обновлеие камеры от первого лица
//если 2 - от третьего лица
switch (cameraState)
{
case 1:
UpdateCamera();
break;
case 2:
UpdateCameraThirdPerson();
break;
}
//вывести объекты сцены
DrawScene();
//Если выбран вид от третьего лица - вывести объект
if (cameraState == 2)
{
DrawModel(ball, Matrix.CreateRotationY(avatarlRotation) * Matrix.CreateTranslation(avatarPosition));
}
base.Draw(gameTime);
}
//Обновляем состояние объекта
void UpdateAvatarPosition()
{
KeyboardState keyboardState = Keyboard.GetState();
//Поворот влево
if (keyboardState.IsKeyDown(Keys.Left))
{
avatarlRotation += rotationSpeed;
}
//Поворот вправо
if (keyboardState.IsKeyDown(Keys.Right) )
{
avatarlRotation -= rotationSpeed;
}
//Движение вперед
if (keyboardState.IsKeyDown(Keys.Up) )
{
Matrix forwardMovement = Matrix.CreateRotationY(avatarlRotation);
Vector3 v = new Vector3(0, 0, forwardSpeed);
v = Vector3.Transform(v, forwardMovement);
avatarPosition.Z += v.Z;
avatarPosition.X += v.X;
}
//Движение назад
if (keyboardState.IsKeyDown(Keys.Down) )
{
Matrix forwardMovement = Matrix.CreateRotationY(avatarlRotation);
Vector3 v = new Vector3(0, 0, -forwardSpeed);
v = Vector3.Transform(v, forwardMovement);
avatarPosition.Z += v.Z;
avatarPosition.X += v.X;
}
//Переключить на режим камеры от первого лица
if (keyboardState.IsKeyDown(Keys.A))
{
cameraState=1;
}
//Переключить на режим камеры от третьего лица
if (keyboardState.IsKeyDown(Keys.S))
{
cameraState = 2;
}
//Уменьшение угла обзора камеры
if (keyboardState.IsKeyDown(Keys.R))
{
viewAngle -= MathHelper.ToRadians(1.0f);
}
//Увеличение угла обзора камеры
if (keyboardState.IsKeyDown(Keys.F))
{
viewAngle += MathHelper.ToRadians(1.0f);
}
//Если новый угол обзора вышел за дозволенные пределы
//изменяем его
if (viewAngle > MathHelper.ToRadians(180.0f)) viewAngle = MathHelper.ToRadians(179.9f);
if (viewAngle < MathHelper.ToRadians(0.0f)) viewAngle = MathHelper.ToRadians(0.1f);
}
//Обновляем состояние камеры при выбранном виде от первого лица
void UpdateCamera()
{
//Текущая позиция камеры
Vector3 cameraPosition = avatarPosition;
//Поворот
Matrix rotationMatrix = Matrix.CreateRotationY(avatarlRotation);
// Направление камеры
Vector3 transformedReference = Vector3.Transform(cameraReference, rotationMatrix);
// Новая позиция камеры
Vector3 cameraLookat = cameraPosition + transformedReference;
//Матрица вида
viewMatrix = Matrix.CreateLookAt(cameraPosition, cameraLookat, new Vector3(0.0f, 1.0f, 0.0f));
//проекционная матрица
projMatrix = Matrix.CreatePerspectiveFieldOfView(viewAngle, aspectRatio, nearClip, farClip);
}
//Обновляем положение камеры при выбранном виде от третьего лица
void UpdateCameraThirdPerson()
{
//Поворот камеры
Matrix rotationMatrix = Matrix.CreateRotationY(avatarlRotation);
// Направление камеры
Vector3 transformedReference = Vector3.Transform(thirdPersonReference, rotationMatrix);
// Позиция камеры
Vector3 cameraPosition = transformedReference + avatarPosition;
//Матрица вида
viewMatrix = Matrix.CreateLookAt(cameraPosition, avatarPosition, new Vector3(0.0f, 1.0f, 0.0f));
//Проецкионная матрица
projMatrix = Matrix.CreatePerspectiveFieldOfView(viewAngle, aspectRatio, nearClip, farClip);
}
//Вывод объектов сцены
void DrawScene()
{
//Вывести кубы, расположенные в пять рядов
//по пять штук в трех уровнях
for (int x = 0; x < 5; x++)
{
for (int y = 0; y < 3; y++)
{
for (int z = 0; z < 5; z++)
{
DrawModel(cube, Matrix.CreateTranslation(x * 40, y * 3, z * 40));
}
}
}
//Вывести плоскость - при выводе увеличиваем модель и поворачиваем соответствующим
//образом
DrawModel(plane, Matrix.CreateScale (100)*Matrix .CreateRotationY (MathHelper .ToRadians (90))*
Matrix.CreateRotationZ(MathHelper.ToRadians(90))*
Matrix .CreateTranslation (80,-2.5f,80));
}
//Процедура вывода модели
//Принимает мировую матрицу для позиционирования модели
//и переменную модели
void DrawModel(Model model, Matrix world)
{
foreach (ModelMesh mesh in model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.LightingEnabled = true;
effect.EnableDefaultLighting();
effect.Projection = projMatrix;
effect.View = viewMatrix;
effect.World = world;
}
mesh.Draw();
}
}
}
}
На рис. 20.3. вы можете видеть состояние игрового экрана проекта P15_3 в режиме вида от третьего лица.
(рис 20.3) Экран проекта P15_3, вид от третьего лицаНа рис. 20.4. вы можете видеть состояние экрана проекта P15_3 в режиме вида от первого лица.
(рис 20.4) Экран проекта P15_3, вид от первого лицаНа рис. 20.5. вы можете видеть состояние экрана проекта P15_3 в режиме вида от третьего лица с уменьшенным полем видимости камеры.
(рис 20.5) Экран проекта P15_3, вид от третьего лица, уменьшенное поле видимости Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.