Быть может кто-то из Вас знает игру 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.
Рассмотрим еще один пример проверки на "попадание" точки в определенную область.