Невозможно пройти тесты, в окне с вопросами пусто |
Пространственные преобразования объектов
24.3. Настройка перемещения камеры: продвинутые приемы
Рассмотрим пример, иллюстрирующий работу с камерой в режиме от первого лица и в режиме вида от третьего лица. Для подготовки этого примера мы использовали справочные материалы по XNA.
Создадим новый проект P17_3. Наш проект выполняет следующие действия. Он выводит на экран плоскость и набор кубов, среди которых может двигаться шар.
Причем, движение может осуществляться в двух режимах. В первом режиме демонстрируется работа камеры при виде от первого лица – шар в таком случае не выводится, сцена выглядит так, как будто мы смотрим на нее с позиции шара.
Во втором режиме демонстрируется вид от третьего лица – камера находится выше объекта. В играх часто применяется следующий прием – главный игровой объект неподвижен, а игровой мир перемещается, создавая иллюзию движения этого объекта. В нашем случае все выглядит точно так же – шар неподвижно находится в центре экрана, но за счет перемещения игровой сцены создается иллюзия его движения по экрану и по сцене. Если в подобной ситуации хочется показать ускоренное движение объекта, можно немного сместить его в направлении движения, ускоряя перемещение сцены. Это будет воспринято, если, например, говорить о гоночном симуляторе, как сильное нажатие на педаль газа. После ускорения объект можно вернуть на прежнее место (в центр экрана, например), продолжив перемещение игрового мира.
В листинге 24.4 вы можете найти код класса Game1, который содержит пример. Для перемещения используются экранные элементы управления. Кнопки-стрелки перемещают сцену, двойное касание экрана (жест DoubleTap) циклически изменяет состояние камеры между видом от первого лица и видом от третьего лица, кнопки-стрелки, изображённые в перспективе, меняют поле зрения камеры.
using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Input.Touch; namespace P17_3 { /// <summary> /// Это главный тип игры /// </summary> public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; //Матрицы 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; //Стрелки 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); //Включить обработку жестов - DoubleTap TouchPanel.EnabledGestures = GestureType.DoubleTap; } /// <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"); cube = Content.Load<Model>("cube"); plane = Content.Load<Model>("plane"); 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(); //обновить положение объекта UpdateAvatarPosition(); //Если состояние камеры 1 - вызвать процедуру //обновлеие камеры от первого лица //если 2 - от третьего лица switch (cameraState) { case 1: UpdateCamera(); break; case 2: UpdateCameraThirdPerson(); break; } base.Update(gameTime); } /// <summary> /// Вызывается, когда игра отрисовывается. /// </summary> /// <param name="gameTime">Предоставляет моментальный снимок значений времени.</param> protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); //вывести объекты сцены DrawScene(); //Если выбран вид от третьего лица - вывести объект if (cameraState == 2) { DrawModel(ball, Matrix.CreateRotationY(avatarlRotation) * Matrix.CreateTranslation(avatarPosition),false); } //Вывод стрелок spriteBatch.Begin(); spriteBatch.Draw(txtArrows, new Rectangle(500, 280, 300, 200), Color.White); spriteBatch.End(); //Для нормального отображение 3D-сцены после работы spriteBatch GraphicsDevice.DepthStencilState = DepthStencilState.Default; //base.Draw(gameTime); } //Обновляем состояние объекта void UpdateAvatarPosition() { 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 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 (MenuSelect(keyBack, touchLocation.Position)) { viewAngle += MathHelper.ToRadians(1.0f); } //Поворот влево if (MenuSelect(keyLeft, touchLocation.Position)) { avatarlRotation += rotationSpeed; } //Назад if (MenuSelect(keyDown, touchLocation.Position)) { 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 (MenuSelect(keyRight, touchLocation.Position)) { avatarlRotation -= rotationSpeed; } } } //До тех пор, пока разрешена обработка жестов while (TouchPanel.IsGestureAvailable) { //прочитаем жест GestureSample gs = TouchPanel.ReadGesture(); //Если жест - FreeDrag и разрешено перемещение //то есть, пользователь касается объекта на экране if (gs.GestureType == GestureType.DoubleTap) { //1 - режим камеры от первого лица //2 - режим камеры от третьего лица if (cameraState == 1) cameraState = 2; else cameraState = 1; } } //Если новый угол обзора вышел за дозволенные пределы //изменяем его 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), false); } } } //Вывести плоскость - при выводе увеличиваем модель и поворачиваем соответствующим //образом DrawModel(plane, Matrix.CreateScale(100) * Matrix.CreateRotationY(MathHelper.ToRadians(90)) * Matrix.CreateRotationZ(MathHelper.ToRadians(90)) * Matrix.CreateTranslation(80, -2.5f, 80), true); } //Процедура вывода модели //Принимает мировую матрицу для позиционирования модели, //переменную модели, параметр, задающий освещение модели void DrawModel(Model model, Matrix world, bool light) { foreach (ModelMesh mesh in model.Meshes) { foreach (BasicEffect effect in mesh.Effects) { if (light) { //Особое освещение для плоскости effect.DiffuseColor = new Vector3(0.3f, 0.8f, 0.8f); } else { //Освещение по умолчанию для других объектов effect.EnableDefaultLighting(); } //Включить освещение effect.LightingEnabled = true; //Матрицы effect.Projection = projMatrix; effect.View = viewMatrix; effect.World = world; } mesh.Draw(); } } } }Листинг 24.4. Код класса Game1
На рис. 24.3 вы можете видеть состояние игрового экрана проекта P17_3 в режиме вида от третьего лица.
24.4. Выводы
В этой лабораторной работе мы рассмотрели пространственные преобразования объектов на примере перемещения, вращения и масштабирования, рассмотрели методики управления камерой.
24.5. Задание
В предыдущем задании вы выводили на экран трехмерные объекты. Модифицируйте результаты работы по предыдущему заданию, добавив пространственные преобразования объектов, управление камерой. Исследуйте самостоятельно использование акселерометра и гироскопа для управления трехмерными играми.