Опубликован: 14.08.2012 | Доступ: свободный | Студентов: 880 / 20 | Оценка: 5.00 / 5.00 | Длительность: 09:59:00
Специальности: Программист
Самостоятельная работа 5:

Основы обработки сенсорного ввода, перемещение объектов

11.3. Разработка игрового компонента с функциями перемещения и с ограничениями

Можно заметить, что в предыдущих примерах игровые объекты легко пересекали границы игрового поля. В реальных проектах обычно требуется, чтобы подвижный объект не пересекал этих границ. Это значит, что, во-первых – нам нужно узнать координаты границ экрана, а во-вторых – нужно создать такой код, отвечающий за перемещение объекта, который прежде чем переместить объект в новую позицию, проверял бы допустимость такого перемещения. Кроме того, для управления спрайтом в данном примере мы создадим экранный элемент управления.

Создадим новый игровой проект (P5_3), в целом, аналогичный P3_3, разработанному в лабораторной работе №3.

Этот проект содержит игровой компонент, который в нашем примере и будет содержать весь необходимый код. Код компонента вы можете видеть в листинге 11.4

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_3
{
    /// <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.4. Код игрового компонента

Раcсмотрим ключевые моменты этого кода. Здесь мы используем переменную scrBound, в которой храним прямоугольник, соответствующий экрану. Его мы будем использовать для проверки на пересечение спрайтом границы экрана. Мы заполняем данные этого прямоугольника в конструкторе компонента. Объект Rectangle оперирует данными в следующей последовательности – Координата X левого верхнего угла, координата Y левого верхнего угла, ширина, высота. Свойство Height содержит высоту экрана в портретном режиме (800), Width – ширину (480), поэтому, для задания ширины прямоугольника мы используем свойство Height, для задания высоты – Width.

Переменная sprMove содержит скорость движения спрайта по координатам X и Y. По умолчанию она равна 0,0, то есть – спрайт не движется. В методе Update мы прибавляем данные скорости по X и по Y, хранящиеся в данной переменной, к координатам спрайта. Сначала – прибавляем, а потом сверяем полученные значения новых координат с координатами границ экрана. Если выясняется, что спрайт пересёк одну из границ, мы модифицируем координаты спрайта таким образом, чтобы спрайт, при таком пересечении, "упирался" бы в границу экрана. Делаем это мы с учётом размеров самого спрайта, а не ориентируемся лишь на левый верхний угол.

За изменение скорости движения спрайта в определенном направлении отвечает метод Move. Он принимает, при вызове его из основной программы, параметр типа Vector2, значение этого параметра принимает переменная sprMove, которая, в дальнейшем, используется при перемещении спрайта.

Рассмотрим теперь основной код игры. Код класса Game1 вы можете видеть в листинге 11.5.

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_3
{
    /// <summary>
    /// Это главный тип игры
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        spriteComp gameObject;
        Texture2D texture;
        //Изображение стрелок
        Texture2D textureArrows;
        //Шаг перемещения спрайта, то есть - скорость
        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();
            textureArrows = Content.Load<Texture2D>("Arrows");
            // ЗАДАЧА: используйте здесь this.Content для загрузки контента игры
        }

        protected void CreateNewObject()
        {
            gameObject = new spriteComp(this, ref texture,
                new Rectangle(18, 9, 17, 88), new Vector2(100, 150));
            Components.Add(gameObject);
        }


        /// <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 > 500 && touchLocation.Position.X < 600 
                        && touchLocation.Position.Y > 380 && touchLocation.Position.Y < 480)
                    {
                        gameObject.Move(new Vector2(-sprSpeed,0));
                    }
                    //Стрелка "Вправо"
                    if (touchLocation.Position.X > 700 && touchLocation.Position.X < 800 
                        && touchLocation.Position.Y > 380 && touchLocation.Position.Y < 480)
                    {
                        gameObject.Move(new Vector2(sprSpeed, 0));
                    }
                    //Стрелка "Вниз"
                    if (touchLocation.Position.X > 600 && touchLocation.Position.X < 700 
                        && touchLocation.Position.Y > 380 && touchLocation.Position.Y < 480)
                    {
                        gameObject.Move(new Vector2(0, sprSpeed));
                    }
                    //Стрелка "Вверх"
                    if (touchLocation.Position.X > 600 && touchLocation.Position.X < 700 
                        && touchLocation.Position.Y > 280 && touchLocation.Position.Y < 380)
                    {
                        gameObject.Move(new Vector2(0, -sprSpeed));
                    }
                }
                
                if (touchLocation.State == TouchLocationState.Released)
                {
                    gameObject.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();
            //Сначала выведем изображение стрелок
            spriteBatch.Draw(textureArrows, new Rectangle(500, 280, 300, 200), Color.White);
            //Игровой компонент будет выведен поверх изображения стрелок
            base.Draw(gameTime);
            spriteBatch.End();
            
        }
    }
}
Листинг 11.5. Код игрового проекта

Здесь мы используем изображение прямоугольника из изображения BallAndBats и изображение четырёх стрелок из изображения Arrows. Стрелки нарисованы на прозрачном фоне, для хранения этого изображения используется PNG-файл. Каждая из стрелок ограничена прямоугольником размером 100х100 пикселей, блок стрелок имеет ширину 300 пикселей, высоту – 200. Он размещен в правом нижнем углу экрана. Переменая sprSpeed задаёт скорость движения спрайта в выбранном пользователем направлении.

В методе Update мы пользуемся уже известным из прошлых примеров механизмом. Но здесь нет прямого воздействия координаты касания экрана на координаты спрайта. Здесь мы проверям координату касания, и, если она совпадает с областью, занимаемой одной из стрелок (вернее – попадает в квадрат, ограничивающий эту стрелку), передаём в игровой объект желаемое направление (координата Х или Y, положительное или отрицательное значение) и скорость (она задаётся единой для всех перемещений через переменную sprSpeed).

Обратите внимание – мы проверяем статус нажатия каждый цикл Update (30 раз в секунду, другими словами, в соответствии с настройками по умолчанию). Если нажатие зафиксировано – мы проверяем координаты, если координаты совпадают с одной из стрелок, выполняем перемещение спрайта. При первоначальном обнаружении касания состояние объекта, представляющего касание, устанавливается в Pressed. В этот момент мы устанавливаем желаемую скорость и направление перемещения спрайта.

Если пользователь не убирает палец с экрана, неважно, перемещает он его или нет, состояние устанавливается в Moved. Нас, в данном случае, это состояние не интересует. При появлении события Pressed в соответствующей координате мы начинаем перемещение спрайта.

Когда пользователь убирает палец со стрелки, объект, представляющий касание, переходит в состояние Released и мы передаем в объект gameObject информацию о том, что перемещение объекта следует остановить.

Спрайт перемещается по экрану в направлении, заданном стрелкой, которой касается пользователь, до тех пор, пока он её касается.

На рис. 11.3 вы можете видеть игровой экран проекта P5_3.

Игровое окно, проект P5_3

Рис. 11.3. Игровое окно, проект P5_3
Гулич Анна
Гулич Анна
Невозможно пройти тесты, в окне с вопросами пусто