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

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

Аннотация: Эта лабораторная работа посвящена техникам пространственных преобразований объектов и управлению камерой.

Цель работы: Научиться перемещать трехмерные объекты в пространстве

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

Создадим новый проект P17_1 и на его примере рассмотрим следующие пространственные преобразования объектов

  1. Перемещение
  2. Вращение
  3. Масштабирование

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

Для этого примера мы создали несколько простых трехмерных моделей в редакторе Blender. В частности – это два разноцветных шара и куб. Вот как выглядит игровой экран проекта P17_1 (рис. 24.1).

Игровой экран проекта

Рис. 24.1. Игровой экран проекта

Для упрощения работы с несколькими игровыми объектами мы создадим игровой компонент - modCls, который будет отвечать за хранение параметров, соответствующих этим объектам и за их визуализацию. В листинге 24.1 вы можете видеть код этого объекта.

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace P17_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()
        {
            
            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);
        }
    }

}
Листинг 24.1. Код класса modCls

Управление объектами возложено на основной игровой класс – его метод Update используется для вычисления новых позиций объектов и обновления информации объектов. Код этого класса вы можете видеть в листинге 24.2. Шар, расположенный сверху, автоматически циклически изменяет размер, куб вращается вокруг осей X и Y, шар, расположенный ниже, можно перемещать.

Для управления объектом используется экранный элемент управления в виде четырёх стрелок для перемещения вверх, вниз, влево и вправо (по осям X и Y), и двух стрелок, изображённых в перспективе, для перемещения объекта вперед и назад (ось Z)

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;

namespace P17_1
{
    /// <summary>
    /// Это главный тип игры
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        //Мировая матрица
        Matrix WorldMatrixM, WorldMatrixM1, WorldMatrixM2;
        //Направление движения для модели modelCls1
        Vector3 directM1;
        //Модели - 
        //modelCls вращается
        //modelCls1 перемещается по клавиатурным командам
        //modelCls2 изменяет размер
        modCls modelCls, modelCls1, modelCls2;
        //Переменные для текущего размера и 
        //текущего приращения размера модели modelCls2
        float size, sizeDelta;
        //Стрелки
        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");
            //загрузка моделей и создание объектов, используемых для 
            //хранения информации о моделях и их вывода на экран
            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);

        }

        /// <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)
                {
                    //Если пользователь коснулся кнопки "Вперед" или коснулся и удерживает её, перемещаем модель
                    //modelCls1 вперед
                    if (MenuSelect(keyForward, touchLocation.Position))
                    {
                        directM1.Z = directM1.Z - 0.1f;
                        WorldMatrixM1 = Matrix.CreateTranslation(directM1);
                    }
                    //Вверх
                    if (MenuSelect(keyUp, touchLocation.Position))
                    {
                        directM1.Y = directM1.Y + 0.1f;
                        WorldMatrixM1 = Matrix.CreateTranslation(directM1);
                    }
                    //Назад
                    if (MenuSelect(keyBack, touchLocation.Position))
                    {
                        directM1.Z = directM1.Z + 0.1f;
                        WorldMatrixM1 = Matrix.CreateTranslation(directM1);
                    }
                    //Влево
                    if (MenuSelect(keyLeft, touchLocation.Position))
                    {
                        directM1.X = directM1.X - 0.1f;
                        WorldMatrixM1 = Matrix.CreateTranslation(directM1);
                    }
                    //Вниз
                    if (MenuSelect(keyDown, touchLocation.Position))
                    {
                        directM1.Y = directM1.Y - 0.1f;
                        WorldMatrixM1 = Matrix.CreateTranslation(directM1);
                    }
                    //Вправо
                    if (MenuSelect(keyRight, touchLocation.Position))
                    {
                        directM1.X = directM1.X + 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);
        }

        /// <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;

        }
    }
}
Листинг 24.2. Код класса Game1

Рассмотрев способы модификации объектов, поговорим об особенностях настройки камеры.

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