Невозможно пройти тесты, в окне с вопросами пусто |
Спрайтовая анимация
Цель работы: Ознакомиться с методологией анимирования спрайтов и фонов
17.1. Трансформация спрайтов
XNA поддерживает различные операции по трансформации спрайтов при их выводе на экран. Создадим новый игровой проек P11_1 на основе игрового проекта P5_3. Напомню, что в P5_3 мы рассматривали простой пример перемещения спрайта по экрану в соответствии с касаниями экранного элемента управления. Кроме того, для некоторых манипуляций со спрайтом мы использовали обработку жестов.
Метод Draw() объекта класса SpriteBatch имеет 7 перегруженных вариантов. До этого мы пользовались простыми вариантами метода – теперь используем более сложный вариант, который предусматривает использование некоторых дополнительных параметров при выводе спрайта на экран. В листинге 17.1 вы можете видеть код объекта Game1, реализующий различные операции по трансформации спрайта.
using System; using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.GamerServices; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Input.Touch; using Microsoft.Xna.Framework.Media; namespace P11_1 { /// <summary> /// Это главный тип игры /// </summary> public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; //Текстура Texture2D texture; //Изображение стрелок Texture2D textureArrows; //Исходная позиция спрайта private Vector2 position = new Vector2(150, 200); //Угол поворота спрайта (в радианах) private float rotation = 0; //Исходный оттенок спрайта private Color color = new Color(255, 255, 255); //Генератор случайных чисел для задания нового цвета private Random rand = new Random(); //Размер спрайта private Vector2 scale = new Vector2(1, 1); //Начало координат спрайта private Vector2 origin; //Прямоугольник, ограничивающий спрайт private Rectangle spRec = new Rectangle(0, 0, 17, 17); public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; graphics.IsFullScreen = true; // Частота кадра на Windows Phone по умолчанию — 30 кадров в секунду. TargetElapsedTime = TimeSpan.FromTicks(333333); //Разрешим использовать жесты TouchPanel.EnabledGestures = GestureType.DoubleTap|GestureType.Pinch; // Дополнительный заряд аккумулятора заблокирован. 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); Services.AddService(typeof(SpriteBatch), spriteBatch); texture = Content.Load<Texture2D>("Ball"); textureArrows = Content.Load<Texture2D>("Arrows"); } /// <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(); //Получаем коллекцию объектов, содержащих информацию о касаниях экрана TouchCollection touchLocations = TouchPanel.GetState(); //Перебираем коллекцию, присваивая объекту координаты касания foreach (TouchLocation touchLocation in touchLocations) { if (touchLocation.State == TouchLocationState.Pressed||touchLocation.State==TouchLocationState.Moved) { //Стрелка "Влево" if (touchLocation.Position.X > 500 && touchLocation.Position.X < 600 && touchLocation.Position.Y > 380 && touchLocation.Position.Y < 480) { position.X -= 1; rotation -= 0.1f; } //Стрелка "Вправо" if (touchLocation.Position.X > 700 && touchLocation.Position.X < 800 && touchLocation.Position.Y > 380 && touchLocation.Position.Y < 480) { position.X += 1; rotation += 0.1f; } //Стрелка "Вниз" if (touchLocation.Position.X > 600 && touchLocation.Position.X < 700 && touchLocation.Position.Y > 380 && touchLocation.Position.Y < 480) { position.Y += 1; } //Стрелка "Вверх" if (touchLocation.Position.X > 600 && touchLocation.Position.X < 700 && touchLocation.Position.Y > 280 && touchLocation.Position.Y < 380) { position.Y -= 1; } } } while (TouchPanel.IsGestureAvailable) { //прочитаем жест GestureSample gs = TouchPanel.ReadGesture(); //Если обнаружен жест двойного касания экрана if (gs.GestureType == GestureType.DoubleTap) { //случайным образом изменим цвет спрайта color = new Color((byte)rand.Next(0, 255), (byte)rand.Next(0, 255), (byte)rand.Next(0, 255)); } //Если обнаружен жест сведения или разведения пальцев if (gs.GestureType == GestureType.Pinch) { //Найдем предыдущие координаты, вычтя из текущих координат приращение Vector2 oldPos = gs.Position - gs.Delta; Vector2 oldPos2 = gs.Position2 - gs.Delta2; //Получим расстояние между текущими точками касания float currentDist = Vector2.Distance(gs.Position, gs.Position2); //Получим расстояние между предыдущими точками касания float oldDist = Vector2.Distance(oldPos, oldPos2); //Умножим переменную, отвечающую за масштабирование спрайта //на результат деления текущего расстояния на предыдущее scale *=currentDist/oldDist; } } origin = (new Vector2 (spRec.Width ,spRec.Height ))/2; base.Update(gameTime); } /// <summary> /// Вызывается, когда игра отрисовывается. /// </summary> /// <param name="gameTime">Предоставляет моментальный снимок значений времени.</param> protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); spriteBatch.Begin(); //Сначала выведем изображение стрелок spriteBatch.Draw(textureArrows, new Rectangle(500, 280, 300, 200), Color.White); //Игровой объект будет выведен поверх изображения стрелок spriteBatch.Draw(texture, position, spRec, color, rotation, origin , scale , SpriteEffects.None, (float)0); spriteBatch.End(); base.Draw(gameTime); } } }Листинг 17.1. Трансформации спрайта
Назначение переменных, используемых в программе, указано в листинге. При касании стрелок "вверх" и "вниз" спрайт перемещается в соответствующем направлении. При нажатии стрелок "влево" и "вправо" спрайт так же перемещается в соответствующих направлениях, однако, вместе с изменением его координат по оси X модифицируется и переменная rotate, которая задает угол поворота спрайта.
Двойное касание экрана приводит к изменению цвета спрайта – новый цвет выбирается случайным образом.
Сведение или разведение пальцев на экране приводит к модификации размера спрайта. Когда пальцы сводят – спрайт уменьшается, когда разводят – увеличивается.
Параметр origin задает начало координат для спрайта. По умолчанию координата позиции спрайта соответствует его левому верхнему углу. Относительно левого верхнего угла, в таком случае, осуществляется и вращение спрайта.
Для того, чтобы вращение происходило вокруг центра спрайта мы записываем в переменную Origin результат от деления длины и ширины спрайта на 2. В итоге спрайт, во-первых, выводится на экран с учетом нового для него начала координат, а во-вторых – при вращении спрайта оно осуществляется вокруг центра спрайта, а не вокруг его левого верхнего угла. Рассмотрим подробнее команду Draw, которую мы использовали для вывода спрайта на экран. В табл. 17.1. описан каждый из её параметров.
На рис. 17.1 вы можете видеть игровой экран.
Эмулятор запущен на компьютере с обычным экраном, поэтому жест Pinch мы испытать на нём не можем.