| Невозможно пройти тесты, в окне с вопросами пусто |
Взаимодействие объектов
Цель работы: Научиться организовывать взаимодействие объектов на экране
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 главного игрового объекта. То есть – в силу вступили новые координаты объекта. Если объект с новыми координатами пересекается с областью элементов управления, мы возвращаем ему старые координаты.


