Опубликован: 14.08.2012 | Доступ: свободный | Студентов: 881 / 20 | Оценка: 5.00 / 5.00 | Длительность: 09:59:00
Специальности: Программист
Самостоятельная работа 17:

Пространственные преобразования объектов

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.3. Вид от третьего лица
Вид от первого лица

Рис. 24.4. Вид от первого лица

24.4. Выводы

В этой лабораторной работе мы рассмотрели пространственные преобразования объектов на примере перемещения, вращения и масштабирования, рассмотрели методики управления камерой.

24.5. Задание

В предыдущем задании вы выводили на экран трехмерные объекты. Модифицируйте результаты работы по предыдущему заданию, добавив пространственные преобразования объектов, управление камерой. Исследуйте самостоятельно использование акселерометра и гироскопа для управления трехмерными играми.

Гулич Анна
Гулич Анна
Невозможно пройти тесты, в окне с вопросами пусто