Невозможно пройти тесты, в окне с вопросами пусто |
Анимация, эффекты
Цель работы: Научиться применять графические эффекты и приемы анимации при работе с трехмерной графикой в XNA
27.1. Анимация персонажей
Компьютерные игры наполнены анимированными персонажами. XNA содержит встроенные средства, поддерживающие базовые операции по анимации трехмерных персонажей. В частности, это средства для работы с так называемым скелетом персонажа. Скелет состоит из взаимосвязанных костей, управляя которыми можно анимировать модель. Создание анимированных моделей – это отдельная, достаточно трудоемкая задача, которая требует серьезных познаний в области 3D-моделирования. Для подготовки анимированных трехмерных моделей вы можете использовать практически любой 3D-редактор.
XNA содержит лишь базовые средства для управления скелетами объектов. Так, мы можем получить коллекцию костей объекта и проводить с ними какие-либо преобразования, вызывающие движение модели. Однако, такой подход ограничен – с его использованием весьма сложно создать реалистичное движение для сложных моделей. На основе базовых средств XNA разработаны библиотеки кода, которые содержат компоненты, позволяющие анимировать персонажи на более высоком уровне. В частности, одну из таких библиотек – XNAAnimation – можно найти на сайте http://www.codeplex.com/xnanimation.
Здесь мы рассмотрим базовый подход к анимации модели. Он заключается в следующем. Можно менять параметры отдельных костей модели и выводить ее на экран с учетом этих изменений. Мы использовали бесплатную модель Ballpen.fbx, взятую с сайта http://www.turbosquid.com.
Создадим новый проект P18_1. Загрузим в него модель Ballpen.fbx.
Код проекта сосредоточен в классе Game1.cs, его код вы можете найти в листинге 27.1.
using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Input.Touch; namespace P20_1 { /// <summary> /// Это главный тип игры /// </summary> public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; //Матрицы Matrix viewMatrix; Matrix projMatrix; //Модель Model pen; 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() { //Загрузка модели pen = Content.Load<Model>("ballpen"); //Соотношение сторон float aspectRatio = (float)graphics.GraphicsDevice.Viewport.Width / (float)graphics.GraphicsDevice.Viewport.Height; //Устанавливаем камеру viewMatrix = Matrix.CreateLookAt(new Vector3(0, 30, 80), new Vector3(-32, 0, 0), new Vector3(0, 1, 0)); //Устанавливаем проекционную матрицу projMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f), aspectRatio, 1.0f, 1000.0f); } /// <summary> /// UnloadContent будет вызываться в игре один раз; здесь выгружается /// весь контент. /// </summary> protected override void UnloadContent() { // ЗАДАЧА: выгрузите здесь весь контент, не относящийся к ContentManager } /// <summary> /// Позволяет игре запускать логику обновления мира, /// проверки столкновений, получения ввода и воспроизведения звуков. /// </summary> /// <param name="gameTime">Предоставляет моментальный снимок значений времени.</param> protected override void Update(GameTime gameTime) { // Позволяет выйти из игры if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); //Перемещение частей ручки PenMove(); base.Update(gameTime); } void PenMove() { //Поворот зажима - он представлен родительской костью сети №3 //Умножим его матрицу трансформации на матрицу поворота по оси X pen.Meshes[3].ParentBone.Transform = pen.Meshes[3].ParentBone.Transform * Matrix.CreateRotationX(MathHelper.ToRadians(-1.0f)); TouchCollection touchLocations = TouchPanel.GetState(); foreach (TouchLocation touchLocation in touchLocations) { //Кнопка ручки перемещается при прикосновении к экрану if (touchLocation.State == TouchLocationState.Pressed) { //При прикосновении к левой части экрана //выходит из ручки if (touchLocation.Position.X < 400) { //Умножим матрицу трансформации родительской костью сети №1 //На матрицу трансляции, перемещающей ее на 2 позиции влево по оси Х pen.Meshes[1].ParentBone.Transform = pen.Meshes[1].ParentBone.Transform * Matrix.CreateTranslation(new Vector3(-20, 0, 0)); } //При прикосновении к правой части экрана - входит в ручку else { //Умножим матрицу трансформации родительской кости сети №1 //На матрицу трансляции, перемещающую ее на 2 позиции по оси X вправо pen.Meshes[1].ParentBone.Transform = pen.Meshes[1].ParentBone.Transform * Matrix.CreateTranslation(new Vector3(20, 0, 0)); } } } } /// <summary> /// Вызывается, когда игра отрисовывается. /// </summary> /// <param name="gameTime">Предоставляет моментальный снимок значений времени.</param> protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); //Новый массив матриц размером, соответствующим количеству //костей в скелете модели Matrix[] absoluteTransformations = new Matrix[pen.Bones.Count]; //Скопировать матрицы трансформации костей в массив матриц pen.CopyAbsoluteBoneTransformsTo(absoluteTransformations); foreach (ModelMesh mesh in pen.Meshes) { foreach (BasicEffect effect in mesh.Effects) { effect.LightingEnabled = true; effect.EnableDefaultLighting(); effect.Projection = projMatrix; effect.View = viewMatrix; //Установим новую мировую матрицу для родительской кости текущей сети //это приводит к перемещению кнопки и к перемещению зажима //Так же здесь мы уменьшаем модель, применяя коэффициент масштабирования 0,15 effect.World = absoluteTransformations[mesh.ParentBone.Index] * Matrix.CreateScale(0.15f); } //Выводим подготовленную сеть mesh.Draw(); } base.Draw(gameTime); } } }Листинг 27.1. Код класса Game1
На рис. 27.1 вы можете видеть игровой экран проекта.