Невозможно пройти тесты, в окне с вопросами пусто |
Взаимодействие объектов
Цель работы: Научиться организовывать взаимодействие объектов на экране
14.1. Обработка столкновений
В предыдущих работах мы уже рассматривали некоторые элементы взаимодействия объектов. В частности, ограничивали область перемещения объекта по экрану границами экрана, проверяли, попадает ли касание экрана в некоторую область. Сейчас мы вплотную займёмся организацией взаимодействия объектов
Выше мы уже имели дело с простым примером обработки столкновений объектов. Были созданы правила, в соответствии с которыми игровой объект не мог пересечь границы экрана. Часто для обработки столкновений двумерных объектов обрабатывают столкновения прямоугольников, описывающих эти объекты. Для этого нужно знать координаты прямоугольника, в нашем случае координаты задаются координатой левой верхней вершины, шириной и высотой фигуры.
На рис. 14.1 вы можете видеть пример объектов, которые не сталкиваются друг с другом.
На рис. 14.2 вы можете видеть пересекающиеся прямоугольники.
Алгоритмически условие пересечения прямоугольников можно записать в виде такого условия (листинг 14.1.)
Если (А.X+A.Ширина > B.X И A.X < B.X+B.Ширина И A.Y+A.Высота>В.Ширина И A.Y<B.Y+B.Высота) Тогда Есть столкновение Иначе Нет столкновенияЛистинг 14.1. Проверка пересечения прямоугольников
Создадим пример, иллюстрирующий этот алгоритм. Возьмем за основу проект P5_3. Назовем новый проект P8_1. Напомним, что в проекте P5_3 мы рассматривали управление объектом с помощью экранного элемента управления. На рис. 14.3 вы можете видеть окно Обозреватель решений для этого проекта.
В листинге 14.2 приведен код объекта 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 P8_1 { /// <summary> /// Это главный тип игры /// </summary> public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; spriteComp gameObject; //Для запоминания позиции объекта Vector2 oldSprPos; //Для игрового объекта Texture2D texture; //Изображение стрелок Texture2D textureArrows; //Шаг перемещения спрайта, то есть - скорость float sprSpeed = 4; //Идентификатор касания int gameObjectId; 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 } //Проверка на столкновение //игрового объекта и прямоугольника //ограничивающего блок управления bool IsCollide(spriteComp sp1) { if (sp1.sprPos().X < 500 + 300 && sp1.sprPos().X + sp1.sprRect().Width > 500 && sp1.sprPos().Y < 280 + 200 && sp1.sprPos().Y + sp1.sprRect().Height > 280) { return true; } else return false; } /// <summary> /// Позволяет игре запускать логику обновления мира, /// проверки столкновений, получения ввода и воспроизведения звуков. /// </summary> /// <param name="gameTime">Предоставляет моментальный снимок значений времени.</param> protected override void Update(GameTime gameTime) { // Позволяет выйти из игры if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); //Запоминаем текущую позицию игрового объекта oldSprPos = gameObject.sprPos(); //Получаем коллекцию объектов, содержащих информацию о касаниях экрана 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)); gameObjectId = touchLocation.Id; } //Стрелка "Вправо" if (touchLocation.Position.X > 700 && touchLocation.Position.X < 800 && touchLocation.Position.Y > 380 && touchLocation.Position.Y < 480) { gameObject.Move(new Vector2(sprSpeed, 0)); gameObjectId = touchLocation.Id; } //Стрелка "Вниз" if (touchLocation.Position.X > 600 && touchLocation.Position.X < 700 && touchLocation.Position.Y > 380 && touchLocation.Position.Y < 480) { gameObject.Move(new Vector2(0, sprSpeed)); gameObjectId = touchLocation.Id; } //Стрелка "Вверх" if (touchLocation.Position.X > 600 && touchLocation.Position.X < 700 && touchLocation.Position.Y > 280 && touchLocation.Position.Y < 380) { gameObject.Move(new Vector2(0, -sprSpeed)); gameObjectId = touchLocation.Id; } } //Если касание окончилось if (touchLocation.State == TouchLocationState.Released) { if (touchLocation.Id == gameObjectId) { gameObject.Move(new Vector2(0, 0)); } } } base.Update(gameTime); //Если произошло столкновение - возвращаем игровой объект в предыдущую позицию if (IsCollide(gameObject)) { gameObject.setSprPos(oldSprPos); } } /// <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(); } } }Листинг 14.2. Код объекта Game1
Обратите внимание на логическую функцию IsCollide. Она возвращает True, если обнаружено пересечение, и False, если пересечения нет. Мы передаем в функцию игровой объект и при выяснении, произошло ли пересечение объектов, пользуемся данными этого объекта. Кроме того, мы используем координаты и размеры области, в которой находятся элементы управления объектом, их мы задаем, для наглядности, в виде отдельных чисел – например, сравнивая координату Y объекта с числом 800, которое представлено как 500+300, мы имеем в виду координату Х левого верхнего угла области, ограничивающей элементы управления (500) и ширину прямоугольника, ограничивающего эту область (300). Сравнивая координату Y, мы записываем число 480 как 280+200, имея в виду координату Y верхнего левого угла области (280) и высоту ограничивающего ее прямоугольника (200).
Мы проверяем объекты на столкновение после того, как модифицировали параметры игрового объекта с использованием функции Move и вызвали метод Update этого объекта (то есть – после команды base.Update(gameTime) метода Update главного игрового объекта. То есть – в силу вступили новые координаты объекта. Если объект с новыми координатами пересекается с областью элементов управления, мы возвращаем ему старые координаты.