Невозможно пройти тесты, в окне с вопросами пусто |
Пространственные преобразования объектов
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.