| Невозможно пройти тесты, в окне с вопросами пусто |
Пространственные преобразования объектов
24.2. Настройка перемещения камеры: базовые приемы
Камера играет огромную роль в трехмерных играх. В играх от первого лица позиция камеры ассоциируется с позицией игрока. Примерами таких игр игры класса simulator, shooter. В играх от третьего лица (strategy) камера расположена выше игрока и сдвинута назад – играющий может видеть своего персонажа и часть карты.
Рассмотрим простой пример управления камерой, в котором реализовано перемещение камеры, а так же изменение угла зрения. Фактически это – пример работы камеры от первого лица. Изменение угла зрения очень похоже на изменение фокусного расстояния в оптических приборах – увеличение фокусного расстояния, которое приводит к уменьшению угла зрения, позволяет "приближать" далеко расположенные объекты. Уменьшение фокусного расстояния, соответствующее увеличению угла зрения, позволяет охватить взглядом большую территорию.
Создадим новый проект – P17_2. Рассмотрим на его примере вышеописанные операции с камерой.
В листинге 24.3 вы можете найти код класса Game1, реализующего демонстрацию работы с камерой.
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
namespace P17_2
{
/// <summary>
/// Это главный тип игры
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
//соотношение сторон окна вывода
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);
//Стрелки
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);
txtArrows = Content.Load<Texture2D>("Arrows");
//Загрузка модели
ball = Content.Load<Model>("ball");
//Установка мировой матрицы
worldMatrix = Matrix.Identity;
//Соотношение сторон
aspectRatio = (float)graphics.GraphicsDevice.Viewport.Width /
(float)graphics.GraphicsDevice.Viewport.Height;
// ЗАДАЧА: используйте здесь this.Content для загрузки контента игры
}
/// <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();
TouchCollection touchLocations = TouchPanel.GetState();
foreach (TouchLocation touchLocation in touchLocations)
{
//Если пользователь коснулся элемента управления либо продолжает касаться
if (touchLocation.State == TouchLocationState.Pressed || touchLocation.State == TouchLocationState.Moved)
{
//Увеличение угла обзора
if (MenuSelect(keyForward, touchLocation.Position))
{
viewAngle += MathHelper.ToRadians(1.0f);
}
//Вперед
if (MenuSelect(keyUp, touchLocation.Position))
{
//Создаем матрицу поворота
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 (MenuSelect(keyBack, touchLocation.Position))
{
viewAngle -= MathHelper.ToRadians(1.0f);
}
//Влево
if (MenuSelect(keyLeft, touchLocation.Position))
{
camRotation -= rotationSpeed;
}
//Нащад
if (MenuSelect(keyDown, touchLocation.Position))
{
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 (MenuSelect(keyRight, touchLocation.Position))
{
camRotation += rotationSpeed;
}
}
}
//Если новый угол обзора вышел за дозволенные пределы
//изменяем его
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>
/// Вызывается, когда игра отрисовывается.
/// </summary>
/// <param name="gameTime">Предоставляет моментальный снимок значений времени.</param>
protected override void Draw(GameTime gameTime)
{
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();
}
spriteBatch.Begin();
spriteBatch.Draw(txtArrows, new Rectangle(500, 280, 300, 200), Color.White);
spriteBatch.End();
//Для нормального отображение 3D-сцены после работы spriteBatch
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
base.Draw(gameTime);
}
}
}
Листинг
24.3.
Код класса Game1
На рис. 24.2 вы можете видеть изображение игрового экрана проекта P17_2.
