Спонсор: Microsoft
Опубликован: 10.04.2009 | Уровень: специалист | Доступ: платный
Самостоятельная работа 4:

Взаимодействие объектов

Обработка попадания точки в пределы объекта

Создадим простую игру на базе проекта 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.

Игровой экран проекта P4_3

увеличить изображение
Рис. 8.5. Игровой экран проекта P4_3

Рассмотрим еще один пример проверки на "попадание" точки в определенную область.

Alina Lasskaja
Alina Lasskaja

Быть может кто-то из Вас знает игру Sims, к какому жанру она относиться? Жизненная симуляция, ролевая игра, там можно и дома строить.....

Дмитрий Кацман
Дмитрий Кацман
Израиль
Андрей Веденин
Андрей Веденин
Россия, Белгород