Невозможно пройти тесты, в окне с вопросами пусто |
Пространственные преобразования объектов
Цель работы: Научиться перемещать трехмерные объекты в пространстве
24.1. Пространственные преобразования объектов
Создадим новый проект P17_1 и на его примере рассмотрим следующие пространственные преобразования объектов
- Перемещение
- Вращение
- Масштабирование
Для выполнения этих операций нам понадобится модификация мировой матрицы. Модификации, соответствующие перемещению объекта мы выполним по клавиатурным командам, остальные модификации будут выполняться автоматически в цикле обновления.
Для этого примера мы создали несколько простых трехмерных моделей в редакторе Blender. В частности – это два разноцветных шара и куб. Вот как выглядит игровой экран проекта P17_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
Рассмотрев способы модификации объектов, поговорим об особенностях настройки камеры.