Невозможно пройти тесты, в окне с вопросами пусто |
Основы обработки сенсорного ввода, перемещение объектов
11.4. Управление несколькими объектами
В этом примере (стандартный игровой XNA-проект, P5_4) мы реализуем функционал независимого управления двумя игровыми объектами. В реализации этого проекта используется следующий код игрового компонента, листинг 11.6.
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.Media; namespace P5_4 { /// <summary> /// Это игровой компонент, реализующий интерфейс IUpdateable. /// </summary> public class spriteComp : Microsoft.Xna.Framework.DrawableGameComponent { //Изображение private Texture2D sprTexture; //Прямоугольник, ограничивающий спрайт private Rectangle sprRectangle; //Координата спрайта private Vector2 sprPosition; //Границы экрана private Rectangle scrBounds; //Направление движения спрайта private Vector2 sprMove = new Vector2(0, 0); public spriteComp(Game game, ref Texture2D newTexture, Rectangle newRectangle, Vector2 newPosition ) : base(game) { sprTexture = newTexture; sprRectangle = newRectangle; sprPosition = newPosition; //Работаем в портретном режиме, высота - 480, ширина - 800 scrBounds = new Rectangle(0, 0, game.Window.ClientBounds.Height, game.Window.ClientBounds.Width ); } /// <summary> /// Позволяет игровому компоненту выполнить необходимую инициализацию перед\r\запуском. Здесь можно запросить нужные службы и загрузить контент. /// /// </summary> public override void Initialize() { // ЗАДАЧА: добавьте здесь код инициализации base.Initialize(); } /// <summary> /// Позволяет игровому компоненту обновиться. /// </summary> /// <param name="gameTime">Предоставляет моментальный снимок значений времени.</param> public override void Update(GameTime gameTime) { //Изменение координат в соответствии с данными, имеющимися в sprMove sprPosition.Y = sprPosition.Y + sprMove.Y; sprPosition.X = sprPosition.X + sprMove.X; //Если вышли за пределы экрана - исправляем if (sprPosition.X < scrBounds.Left) { sprPosition.X = scrBounds.Left; } if (sprPosition.X > scrBounds.Width - sprRectangle.Width) { sprPosition.X = scrBounds.Width - sprRectangle.Width; } if (sprPosition.Y < scrBounds.Top) { sprPosition.Y = scrBounds.Top; } if (sprPosition.Y > scrBounds.Height - sprRectangle.Height) { sprPosition.Y = scrBounds.Height - sprRectangle.Height; } base.Update(gameTime); } public override void Draw(GameTime gameTime) { SpriteBatch sprBatch = (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch)); sprBatch.Draw(sprTexture, sprPosition, sprRectangle, Color.White); base.Draw(gameTime); } //Метод для установки направления движения спрайта public void Move(Vector2 move) { sprMove = move; } } }Листинг 11.6. Код игрового компонента
Этот игровой компонент очень похож на компонент, рассмотренный в проекте P5_3. Здесь реализованы ограничения на перемещения спрайта в пределах границ экрана, методика перемещения спрайта.
Рассмотрим код игры, который реализует данный пример, листинг 11.7.
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 P5_4 { /// <summary> /// Это главный тип игры /// </summary> public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; spriteComp gameObject, gameObject1; //Для хранения ID касаний int gameObjectId, gameObjectId1; Texture2D texture; //Шаг перемещения спрайта, то есть - скорость float sprSpeed = 4; 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); Services.AddService(typeof(SpriteBatch), spriteBatch); texture = Content.Load<Texture2D>("BallandBats"); CreateNewObject(); // ЗАДАЧА: используйте здесь this.Content для загрузки контента игры } protected void CreateNewObject() { gameObject = new spriteComp(this, ref texture, new Rectangle(18, 9, 17, 88), new Vector2(50, 150)); Components.Add(gameObject); gameObject1 = new spriteComp(this, ref texture, new Rectangle(17, 106, 17, 88), new Vector2(750, 150)); Components.Add(gameObject1); } /// <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) { //Управление объектом, расположенным у левого края экрана, перемещение вверх if (touchLocation.Position.X > 0 && touchLocation.Position.X < 200 && touchLocation.Position.Y>0&&touchLocation.Position.Y<240) { gameObjectId = touchLocation.Id; gameObject.Move(new Vector2(0, -sprSpeed)); } //Управление объектом, расположенным у левого края экрана, перемещение вниз if (touchLocation.Position.X > 0 && touchLocation.Position.X < 200 && touchLocation.Position.Y > 240 && touchLocation.Position.Y < 480) { gameObjectId = touchLocation.Id; gameObject.Move(new Vector2(0, sprSpeed)); } //Управление объектом, расположенным у правого края экрана, перемещение вверх if (touchLocation.Position.X > 600 && touchLocation.Position.X < 800 && touchLocation.Position.Y > 0 && touchLocation.Position.Y < 240) { gameObjectId1 = touchLocation.Id; gameObject1.Move(new Vector2(0, -sprSpeed)); } //Управление объектом, расположенным у правого края экрана, перемещение вниз if (touchLocation.Position.X > 600 && touchLocation.Position.X < 800 && touchLocation.Position.Y > 240 && touchLocation.Position.Y < 480) { gameObjectId1 = touchLocation.Id; gameObject1.Move(new Vector2(0, sprSpeed)); } } //Если касание окончилось if (touchLocation.State == TouchLocationState.Released) { if (touchLocation.Id == gameObjectId) { gameObject.Move(new Vector2(0, 0)); } if (touchLocation.Id == gameObjectId1) { gameObject1.Move(new Vector2(0, 0)); } } } base.Update(gameTime); } /// <summary> /// Вызывается, когда игра отрисовывается. /// </summary> /// <param name="gameTime">Предоставляет моментальный снимок значений времени.</param> protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); spriteBatch.Begin(); base.Draw(gameTime); spriteBatch.End(); } } }Листинг 11.7. Код игрового проекта
Многие части этого кода, подобного этому, уже обсуждались выше. Здесь мы остановимся лишь на наиболее серьезных особенностях. Мы используем два игровых компонента – gameObject и gameObject1. Кроме того, мы используем две новых переменных – gameObjectId и gameObjectId1. Они хранят идентификаторы (Id) касаний. Для управления объектами мы используем четыре зоны экрана. Две зоны шириной 200 и высотой по 240 пикселей, расположенные в левой части экрана для управления одним объектом (gameObject), и такие же зоны вдоль правого края для управления другим (gameObject1).
Таким образом, если пользователь прикасается к верхней зоне у соответствующего края экрана, соответствующий спрайт перемещается вверх, если к нижней – вниз. Объект перемещается до тех пор, пока пользователь касается соответствующей зоны. При касании в переменную gameObjectId или gameObjectId1 записывается идентификатор касания.
Этот идентификатор не изменяется всё время, когда пользователь касается экрана. Когда пользователь перестаёт касаться экрана, срабатывает проверка на то, что касание перешло в состояние Released. Если для какого-либо касания это условие выполняется, следует проверка идентификатора касания, когда обнаруживается, касание с каким идентификатором перешло в состояние Released, перемещение соответствующего спрайта прекращается.
На рис. 11.4 вы можете видеть игровой экран проекта.
11.5. Выводы
В этой лабораторной работе была рассмотрена методика обработки информации о касаниях экрана, методика управления экранными объектами на основе данной информации. Рассмотрены возможности использования данных от нескольких точек касания экрана для управления несколькими объектами, функции ограничения перемещения объектов.
11.6. Задание
Реализуйте управление тремя экранными объектами с использованием методик, описанных в данной лабораторной работе. Реализуйте ограничения для каждого из объектов.