| Невозможно пройти тесты, в окне с вопросами пусто |
Анимация, эффекты
Цель работы: Научиться применять графические эффекты и приемы анимации при работе с трехмерной графикой в 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 вы можете видеть игровой экран проекта.
