|
Быть может кто-то из Вас знает игру Sims, к какому жанру она относиться? Жизненная симуляция, ролевая игра, там можно и дома строить..... |
Взаимодействие объектов
Обработка попадания точки в пределы объекта
Создадим простую игру на базе проекта P4_2. Игрок должен за минимальное время попасть по каждому из 100 находящихся на экране объектов, мышью. Объект, по которому попали мышью, уничтожается.
Здесь нам понадобится алгоритм проверки попадания точки в пределы спрайта. Как и прежде, спрайты представлены прямоугольниками. Для проверки попадания точки в прямоугольник, о котором известна координата его левой верхней точки, ширина и высота, воспользуемся таким алгоритмом (листинг 8.7.). A – это прямоугольник, B – это точка.
Если (А.X+A.Ширина > B.X И A.X < B.X И A.Y+A.Высота>В.Y И A.Y<B.Y) Тогда Есть столкновение Иначе Нет столкновенияЛистинг 8.7. Алгоритм проверки попадания точки в прямоугольник
Создадим новый проект P4_3 на базе проекта P4_2. В листинге 8.8. вы можете видеть код объекта Game1.
using System;
using System.Collections.Generic;
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.Net;
using Microsoft.Xna.Framework.Storage;
namespace P4_3
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D texture;
public int Score;
bool IsWin = false;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
this.IsMouseVisible = true;
Score = 0;
}
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
Services.AddService(typeof(SpriteBatch), spriteBatch);
texture = Content.Load<Texture2D>("BallandBats");
CreateNewObject();
// TODO: use this.Content to load your game content here
}
protected void CreateNewObject()
{
for (int i = 0; i < 10; i++)
{
//Добавляем в список компонентов новый компонент класса spriteComp
Components.Add(new spriteComp(this, ref texture,
new Rectangle(16, 203, 17, 17), i, this));
}
}
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
//Если цель игры не достигнута – выводим в заголовок окна
//информацию о текущем состоянии игры
if (IsWin == false)
Window.Title = "Уничтожено " + Score.ToString() + " за " + gameTime.TotalRealTime.Seconds + " с.";
//Если цель достигнута – выводим сообщение об этом
if (Score == 10&&IsWin ==false )
{
Window.Title = "Вы уничтожили всех за " + gameTime.TotalRealTime.Seconds+" c.";
IsWin = true;
}
// TODO: Add your update logic here
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
spriteBatch.Begin();
base.Draw(gameTime);
spriteBatch.End();
}
}
}
Листинг
8.8.
Код объекта Game1
Здесь мы создаем 10 объектов класса spriteComp и проверяем, не достигнута ли цель игры – уничтожение всех объектов. Для контроля количества уничтоженных объектов используем переменную Score, для контроля за достижением цели – переменную IsWin. Основная работа ведется в объектах класса spriteComp. В листинге 8. 8. вы можете найти соответствующий им код.
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
namespace P4_3
{
public class spriteComp : Microsoft.Xna.Framework.DrawableGameComponent
{
protected Texture2D sprTexture;
protected Rectangle sprRectangle;
protected Vector2 sprPosition;
protected Rectangle scrBounds;
//Для генерирования случайных чисел
protected Random randNum;
//Объект для доступа к основному игровому объекту
protected Game1 myGame;
//Цвет спрайта
protected Color sprColor;
//Скорость перемещения
public Vector2 speed;
//Состояние мыши
MouseState mouse;
public spriteComp(Game game, ref Texture2D newTexture,
Rectangle newRectangle, int Seed, Game1 game1)
: base(game)
{
sprTexture = newTexture;
sprRectangle = newRectangle;
//Инициализируем счетчик
randNum = new Random(Seed);
myGame = game1;
scrBounds = new Rectangle(0, 0,
game.Window.ClientBounds.Width,
game.Window.ClientBounds.Height);
//Устанавливаем стартовую позицию спрайта
setSpriteToStart();
//Если спрайт сталкивается с каким-нибудь спрайтом - изменим его позицию
while (howManyCollides() > 0)
{
setSpriteToStart();
}
//Зададим случайный цвет для придания изображению
//соответствующего оттенка
sprColor = new Color((byte)randNum.Next(0, 255), (byte)randNum.Next(0, 255), (byte)randNum.Next(0, 255));
//Переменная для хранения скорости пока пуста
speed = new Vector2((float)randNum.Next(-5, 5), (float)randNum.Next(-5, 5));
// TODO: Construct any child components here
}
//Проверка, не установлены ли спрайты в позиции с перекрытием других спрайтов
int howManyCollides()
{
int howMany = 0;
foreach (spriteComp spr in myGame.Components)
{
if (this != spr)
{
if (this.sprCollide(spr))
{
howMany++;
}
}
}
return howMany;
}
//Установка спрайта в случайную стартовую позицию
void setSpriteToStart()
{
sprPosition.X = (float)randNum.NextDouble() * (scrBounds.Width - sprRectangle.Width);
sprPosition.Y = (float)randNum.NextDouble() * (scrBounds.Height - sprRectangle.Height);
}
public override void Initialize()
{
// TODO: Add your initialization code here
base.Initialize();
}
//Перемещение спрайта
public virtual void Move()
{
sprPosition += speed;
}
//Проверка допустимости перемещения
void Check()
{
if (sprPosition.X < scrBounds.Left)
{
sprPosition.X = scrBounds.Left;
speed.X *= -1;
}
if (sprPosition.X > scrBounds.Width - sprRectangle.Width)
{
sprPosition.X = scrBounds.Width - sprRectangle.Width;
speed.X *= -1;
}
if (sprPosition.Y < scrBounds.Top)
{
sprPosition.Y = scrBounds.Top;
speed.Y *= -1;
}
if (sprPosition.Y > scrBounds.Height - sprRectangle.Height)
{
sprPosition.Y = scrBounds.Height - sprRectangle.Height;
speed.Y *= -1;
}
}
public bool sprCollide(spriteComp spr)
{
return (this.sprPosition.X + this.sprRectangle.Width > spr.sprPosition.X &&
this.sprPosition.X < spr.sprPosition.X + spr.sprRectangle.Width &&
this.sprPosition.Y + this.sprRectangle.Height > spr.sprPosition.Y &&
this.sprPosition.Y < spr.sprPosition.Y + spr.sprRectangle.Height);
}
//Функция возвращает true если указатель мыши находился в
// пределах текущего объекта при щелчке мышью
bool MouseCollide()
{
return (this.sprPosition .X +this.sprRectangle.Width >mouse .X &&
this .sprPosition.X<mouse .X &&
this.sprPosition .Y+this.sprRectangle .Height >mouse.Y &&
this.sprPosition .Y <mouse .Y);
}
public override void Update(GameTime gameTime)
{
mouse = Mouse.GetState();
if (mouse.LeftButton == ButtonState.Pressed)
{
//Если при щелчке мышью указатель находился в пределах
//текущего объекта увеличиваем количество набранных
// очков и уничтожаем объект.
if (MouseCollide())
{
myGame.Score++;
this.Dispose();
}
}
// TODO: Add your update code here
//Вызов метода для перемещения спрайта
Move();
//Проверка на столкновение с границами экрана
Check();
//Вызов проверки на столкновение с другими спрайтами
IsSpriteCollide();
//Возможно, при коррекции спрайта относительно другого спрайта,
//произошло перекрытие с другим спрайтом или спрайтами
//это приводит к "зависанию" спрайтов - они остаются на одном
//месте в "сцепленном" состоянии. Для того, чтобы этого
// мы корректируем позиции спрайтов до тех пор, пока каждый из
// них гарантированно не окажется вне других спрайтов
while (howManyCollides() > 0)
{
IsSpriteCollide();
}
base.Update(gameTime);
}
//Если спрайт перекрыл другой спрайт - изменить его скорость и
//применить изменения к позиции спрайта
void IsSpriteCollide()
{
foreach (spriteComp spr in myGame.Components)
{
if (spr != this)
{
if (this.sprCollide(spr))
{
this.speed *= -1;
this.sprPosition += this.speed;
}
}
}
}
public override void Draw(GameTime gameTime)
{
SpriteBatch sprBatch =
(SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));
sprBatch.Draw(sprTexture, sprPosition, sprRectangle, sprColor);
base.Draw(gameTime);
}
}
}
Листинг
8.9.
Код объекта spriteComp
Здесь мы сначала расставляем спрайты таким образом, чтобы они не пересекались, перемещаем их, учитывая столкновения друг с другом и с границами экрана, а так же – отслеживаем состояние мыши. При щелчке мышью проверяем, не находится ли позиция, в которой в этот момент находился указатель, в пределах текущего спрайта. Если это так – увеличиваем количество очков и уничтожаем текущий объект. На рис. 8.5. вы можете видеть игровой экран проекта P4_3.
Рассмотрим еще один пример проверки на "попадание" точки в определенную область.
