Возможно вы искали: 'ARCA Sim Racing '08'

May 12 2025 01:25:39
  • Как сделать 8Gamers.Ru домашней страницей?
  • Игры
    • База данных по играх
    • Игровые новости
    • Игровая индустрия
    • Обзоры на игры
    • Прохождения игр
    • Гайды к играм
    • Превью о играх
    • Игровые тизеры
    • Игровые арты
    • Игровые обои
    • Игровые скриншоты
    • Игровые обложки
    • Игровые трейлеры
    • Игровое видео
    • Вышедшие игры
    • Ближайшие релизы игр
  • Кино и ТВ
    • База данных по кино
    • Статьи о кино
    • Постеры
    • Кадры из кино
    • Кино трейлеры
    • Сегодня в кино
    • Скоро в кино
  • Комиксы и манга
    • Манга по алфавиту
    • База данных по комиксах
    • Читать онлайн комиксы
    • Читать онлайн манга
    • База персонажей
  • Читы и коды
    • Чит-коды для PC игр
    • Чит-коды для консольных игр
    • Трейнеры
    • Коды Game Genie
  • Моддинг
    • Модификации
    • Карты к играм
    • Программы для моддинга
    • Статьи о моддинге
  • Геймдев
    • Всё о создании игр
    • Список движков
    • Утилиты в помощь игроделу
    • Конструкторы игр
    • Игровые движки
    • Библиотеки разработки
    • 3D-модели
    • Спрайты и тайлы
    • Музыка и звуки
    • Текстуры и фоны
  • Рецензии
    • Игры
    • Кино
    • Аниме
    • Комиксы
    • Мангу
    • Саундтреки
  • Саундтреки
    • Лирика
  • Файлы
    • Патчи к играм
    • Русификаторы к играм
    • Сохранения к играм
    • Субтитры к кино
  • Медиа
    • Видео
    • Фото
    • Аудио
    • Фан-арты
    • Косплей
    • Фото с виставок
    • Девушки из игр
    • Рисунки
    • Рисуем онлайн
    • Фотохостинг
  • Юмор
    • Анекдоты
    • Афоризмы
    • Истории
    • Стишки и эпиграммы
    • Тосты
    • Цитаты
  • Флеш
    • Азартные
    • Аркады
    • Бродилки
    • Гонки
    • Для девочек
    • Для мальчиков
    • Драки
    • Квесты
    • Леталки
    • Логические
    • Мультфильмы
    • Открытки
    • Приколы
    • Разное
    • Спорт
    • Стратегии
    • Стрелялки
Статистика

Статей: 87772
Просмотров: 96030355
Игры
Injustice:  Gods Among Us
Injustice: Gods Among Us
...
Dark Souls 2
Dark Souls 2
Dark Souls II - вторая часть самой хардкорной ролевой игры 2011-2012 года, с новым героем, сюжето...
Battlefield 4
Battlefield 4
Battlefield 4 - продолжение венценосного мультиплеер-ориентированного шутера от первого ли...
Кино
Steins;Gate
Steins;Gate
Любители японской анимации уже давно поняли ,что аниме сериалы могут дать порой гораздо больше пи...
Ку! Кин-дза-дза
Ку! Кин-дза-дза
Начинающий диджей Толик и всемирно известный виолончелист Владимир Чижов встречают на шумной моск...
Обзоры на игры
• Обзор Ibara [PCB/PS2] 18346
• Обзор The Walking ... 18789
• Обзор DMC: Devil M... 19864
• Обзор на игру Valk... 15867
• Обзор на игру Stars! 17754
• Обзор на Far Cry 3 17935
• Обзор на Resident ... 16012
• Обзор на Chivalry:... 17495
• Обзор на игру Kerb... 17970
• Обзор игры 007: Fr... 16604
Превью о играх
• Превью к игре Comp... 17946
• Превью о игре Mage... 14451
• Превью Incredible ... 14706
• Превью Firefall 13462
• Превью Dead Space 3 16325
• Превью о игре SimC... 14717
• Превью к игре Fuse 15432
• Превью Red Orche... 15532
• Превью Gothic 3 16334
• Превью Black & W... 17345
Главная » Статьи » Всё о XNA » Взаимодействие 2D объектов (столкновения)

Взаимодействие 2D объектов (столкновения)

Обработка взаимодействия объектов – это очень важная часть создания игры. В этой лабораторной работе мы рассмотрим обработку столкновений объектов.

Цель работы:
  • Научиться применять концепции обработки столкновения объектов

Задачи работы:


  • Изучить теоретические основы обработки столкновений объектов в двумерном пространстве
  • Изучить алгоритмы обработки столкновений объектов в двумерном пространстве
  • Рассмотреть алгоритм проверки принадлежности точки фигуре в двумерном пространстве
  • Создать простую игру – игровой объект автоматически перемещается по экрану, отскакивая от его границ. Игрок должен с помощью мыши попасть по объекту. Каждое попадание должно привести к выводу информации о количестве попаданий в заголовок игрового окна.
  • Создать простую игру «Стрельба по мишени» с реализацией алгоритма проверки принадлежности точки окружности

Обработка столкновений


Ранее мы уже имели дело с простым примером обработки столкновений объектов. Были созданы правила, в соответствии с которыми игровой объект не мог пересечь границы экрана. Часто для обработки столкновений двумерных объектов обрабатывают столкновения прямоугольников, описывающих эти объекты. Для этого нужно знать координаты прямоугольника, в нашем случае координаты задаются координатой левой верхней вершины, шириной и высотой фигуры.

На рис. 4.1. вы можете видеть пример объектов, которые не сталкиваются друг с другом.


Рис. 4.1. Непересекающиеся прямоугольники

На рис. 4.2. вы можете видеть пересекающиеся прямоугольники.


Рис. 4.2. Пересекающиеся прямоугольники

Алгоритмически условие пересечения прямоугольников можно записать в виде такого условия (листинг 4.1.)

Листинг 4.1. Проверка пересечения прямоугольников
1
2
3
4
5
6
7
8
Если (А.X+A.Ширина > B.X И
    A.X < B.X+B.Ширина И
    A.Y+A.Высота>В.Ширина И
    A.Y+B.Высота)
Тогда
    Есть столкновение
Иначе
    Нет столкновения

 


Создадим пример, иллюстрирующий этот алгоритм. Возьмем за основу проект P3_6. Назовем новый проект P4_1. Напомним, что в проекте P3_6 мы рассматривали централизованное управление объектами из основного игрового объекта. Продолжим этот пример, доработав основной игровой объект таким образом, чтобы при перемещении объектов проводилась проверка на их столкновение.

В листинге 4.2. приведен код объекта Game1, где мы и реализовали такую проверку. Так как объекты перемещаются путем модификации их позиции на игровом экране, реализуем проверку на столкновение при попытке перемещения объекта. Если объекты сталкиваются – мы таким образом модифицируем их координаты, чтобы они не перекрывались.

Листинг 4.2. Код класса Game1, реализующий проверку столкновений объектов
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
 
namespace P4_1
{
    /// 
    /// This is the main type for your game
    /// 
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        spriteComp gameObject1, gameObject2;
        Texture2D texture;
        Rectangle scrBounds;
        float sprSpeed;
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }
 
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
 
            base.Initialize();
        }
 
        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");
            scrBounds = new Rectangle(0, 0,
                this.Window.ClientBounds.Width,
                this.Window.ClientBounds.Height);
            sprSpeed = 5;
            CreateNewObject();
            // TODO: use this.Content to load your game content here
        }
 
        protected void CreateNewObject()
        {
            gameObject1 = new spriteComp(this, ref texture,
                new Rectangle(18, 9, 17, 88),
                new Vector2(100, 150));
            Components.Add(gameObject1);
            gameObject2 = new spriteComp(this, ref texture,
                new Rectangle(17, 106, 17, 88),
                new Vector2(200, 150));
            Components.Add(gameObject2);
        }
 
        void Test(spriteComp spr, Rectangle scr)
        {
            if (spr.sprPosition.X < scr.Left)
            {
                spr.sprPosition.X = scr.Left;
            }
            if (spr.sprPosition.X > scr.Width - spr.sprRectangle.Width)
            {
                spr.sprPosition.X = scr.Width - spr.sprRectangle.Width;
            }
            if (spr.sprPosition.Y < scr.Top)
            {
                spr.sprPosition.Y = scr.Top;
            }
            if (spr.sprPosition.Y > scr.Height - spr.sprRectangle.Height)
            {
                spr.sprPosition.Y = scr.Height - spr.sprRectangle.Height;
            }
        }
        void MoveUp(spriteComp spr, float speed)
        {
            spr.sprPosition.Y -= speed ;
        }
        void MoveDown(spriteComp spr,float speed)
        {
            spr.sprPosition.Y += speed;
        }
        void MoveLeft(spriteComp spr, float speed)
        {
            spr.sprPosition.X -= speed ;
        }
        void MoveRight(spriteComp spr, float speed)
        {
            spr.sprPosition.X += speed ;
        }
        bool  IsCollide(spriteComp sp1, spriteComp sp2)
        {
            if (sp1.sprPosition.X < sp2.sprPosition.X + sp2.sprRectangle.Width &&
                sp1.sprPosition.X + sp1.sprRectangle.Width > sp2.sprPosition.X &&
                sp1.sprPosition.Y < sp2.sprPosition.Y + sp2.sprRectangle.Height &&
                sp1.sprPosition.Y + sp1.sprRectangle.Height > sp2.sprPosition.Y)
            {
                return true;
            }
            else return false;
 
        }
 
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }
 
        protected override void Update(GameTime gameTime)
        {
            // TODO: Add your update logic here
            KeyboardState kbState = Keyboard.GetState();
            if (kbState.IsKeyDown(Keys.Up))
            {
                MoveUp(gameObject1, sprSpeed );
                while (IsCollide(gameObject1, gameObject2))
                {
                    MoveDown(gameObject1, (sprSpeed / 10));
                }
            }
            if (kbState.IsKeyDown(Keys.Down))
            {
                MoveDown(gameObject1, sprSpeed );
                while (IsCollide(gameObject1, gameObject2))
                {
                    MoveUp(gameObject1, (sprSpeed / 10));
                }
            }
            if (kbState.IsKeyDown(Keys.Left))
            {
                MoveLeft(gameObject1, sprSpeed );
                while (IsCollide(gameObject1, gameObject2))
                {
                    MoveRight(gameObject1, (sprSpeed / 10));
                }
            }
            if (kbState.IsKeyDown(Keys.Right))
            {
                MoveRight(gameObject1,sprSpeed );
                while (IsCollide(gameObject1, gameObject2))
                {
                    MoveLeft(gameObject1, (sprSpeed / 10));
                }
            }
            Test(gameObject1, scrBounds);
            if (kbState.IsKeyDown(Keys.W))
            {
                MoveUp(gameObject2,sprSpeed );
                while (IsCollide(gameObject1, gameObject2))
                {
                    MoveDown(gameObject2, (sprSpeed / 10));
                }
            }
            if (kbState.IsKeyDown(Keys.S))
            {
                MoveDown(gameObject2,sprSpeed );
                while (IsCollide(gameObject1, gameObject2))
                {
                    MoveUp(gameObject2, (sprSpeed / 10));
                }
            }
            if (kbState.IsKeyDown(Keys.A))
            {
                MoveLeft(gameObject2,sprSpeed );
                while (IsCollide(gameObject1, gameObject2))
                {
                    MoveRight(gameObject2, (sprSpeed / 10));
                }
            }
            if (kbState.IsKeyDown(Keys.D))
            {
                MoveRight(gameObject2,sprSpeed );
                while (IsCollide(gameObject1, gameObject2))
                {
                    MoveLeft(gameObject2, (sprSpeed / 10));
                }
            }
            Test(gameObject2, scrBounds);
 
            base.Update(gameTime);
        }
 
        /// 
        /// This is called when the game should draw itself.
        /// 
        /// Provides a snapshot of timing values.
        protected override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
 
            // TODO: Add your drawing code here
            spriteBatch.Begin();
            base.Draw(gameTime);
            spriteBatch.End();
        }
    }
}

 


В раздел объявления переменных класса мы добавили новую переменную sprSpeed типа float, которую будем использовать при модификации позиции спрайта в процедурах изменения координат в зависимости от нажатой клавиши на клавиатуре.
Обратите внимание на логическую функцию IsCollide, её код приведен в листинге -

Листинг 4.3. Логическая функция IsCollide
1
2
3
4
5
6
7
8
9
10
11
12
bool  IsCollide(spriteComp sp1, spriteComp sp2)
        {
            if (sp1.sprPosition.X < sp2.sprPosition.X + sp2.sprRectangle.Width &&
                sp1.sprPosition.X + sp1.sprRectangle.Width > sp2.sprPosition.X &&
                sp1.sprPosition.Y < sp2.sprPosition.Y + sp2.sprRectangle.Height &&
                sp1.sprPosition.Y + sp1.sprRectangle.Height > sp2.sprPosition.Y)
            {
                return true;
            }
            else return false;
 
        }

 

Эта функция принимает в качестве параметров два объекта класса spriteComp – то есть два наших объекта, перемещением которых мы управляем из кода игрового класса. Если выполняется условие столкновения объектов – функция возвращает true, иначе – false.
Мы вызываем эту функцию при перемещении объектов, корректируя их позиции при столкновении. Рассмотрим пример перемещения одного из объектов в направлении «Вверх» - листинг 4.4.

Листинг 4.4. Фрагмент кода, применяющий функцию IsCollide()
1
2
3
4
5
6
7
8
9
if (kbState.IsKeyDown(Keys.Up))
{
        MoveUp(gameObject1, sprSpeed );
 
        while (IsCollide(gameObject1, gameObject2))
        {
             MoveDown(gameObject1, (sprSpeed / 10));
        }
}

 


В этом фрагменте мы перемещаем объект на количество пикселей, задаваемых параметром sprSpeed в направлении «Вверх». После перемещения мы вызваем функцию IsCollide(). Если она возвратила false – никаких действий больше не предпринимаем. Но если она возвратила true – перемещаем объект в обратном направлении (вызывая процедуру MoveDown), изменяя координаты со скоростью, равной 1/10 обычного изменения координат. В примере скорость изменения позиции спрайта равна 5. Если спрайт на этой скорости зашёл за границы другого спрайта – данная ситуация обрабатывается как столкновение. После этого спрайт перемещается в обратном направлении со скоростью 0,5 до тех пор, пока не окажется, что столкновения больше нет. Таким образом при столкновении объекты останавливаются строго около границ друг друга, не пересекая их, но и не находясь на некотором расстоянии друг от друга при скорости перемещения, которая меньше, чем размеры объектов.

На рис. 4.3. вы можете видеть пример столкновения объектов.

Рис. 4.3. Столкновение объектов

Теперь рассмотрим пример обработки столкновений объектов при их автоматическом перемещении с заданием скорости перемещения объектов.

Обработка столкновений автоматически перемещаемых объектов

Создадим новый игровой проект P4_2, взяв за основу проект P3_7. Будем автоматически перемещать объекты с некоторой случайно заданной скоростью. При столкновении объекта с границей экрана изменим скорость по X и по Y на противоположную. Так же поступим при столкновении объектов друг с другом.

В листинге 4.5. вы можете видеть пример кода класса Game1.

Листинг 4.5. Код класса Game1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
 
namespace P4_2
{
    /// 
    /// This is the main type for your game
    /// 
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Texture2D texture;
        Random randNum;
 
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }
 
 
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
 
            base.Initialize();
        }
 
        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");
            randNum = new Random();
            CreateNewObject();
            // TODO: use this.Content to load your game content here
        }
 
        protected void CreateNewObject()
        {
            //Цикл от 1 до случайного числа в диапазоне 20,200
            for (int i = 0; i < randNum.Next(20, 200); i++)
            {
                //Добавляем в список компонентов новый компонент класса spriteComp
                Components.Add(new spriteComp(this, ref texture,
                new Rectangle(16, 203, 17, 17), i));
            }
        }
 
        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();
 
            // TODO: Add your update logic here
 
            base.Update(gameTime);
        }
 
        protected override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
 
            // TODO: Add your drawing code here
            spriteBatch.Begin();
            base.Draw(gameTime);
            spriteBatch.End();
        }
    }
}

 


Можно заметить, что в основном игровом классе мы лишь создаем случайное количество новых объектов класса spriteComp. Управление объектами и обработка столкновений ведется непосредственно в коде самих объектов. В листинге 4.6. вы можете видеть код объекта spriteComp.

Листинг 4.6. Код объекта spriteComp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Content;
 
namespace P4_2
{
    /// 
    /// This is a game component that implements IUpdateable.
    /// 
    public class spriteComp : Microsoft.Xna.Framework.DrawableGameComponent
    {
        protected Texture2D sprTexture;
        protected Rectangle sprRectangle;
        protected Vector2 sprPosition;
        protected Rectangle scrBounds;
        //Для генерирования случайных чисел
        protected Random randNum;
        //Объект для доступа к основному игровому объекту
        protected Game myGame;
        //Цвет спрайта
        protected Color sprColor;
        //Скорость перемещения
        public Vector2 speed;
 
        public spriteComp(Game game, ref Texture2D newTexture,
            Rectangle newRectangle, int Seed)
            : base(game)
        {
            sprTexture = newTexture;
            sprRectangle = newRectangle;
            //Инициализируем счетчик
            randNum = new Random(Seed);
            myGame = game;
            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);
        }
        /// 
        /// Allows the game component to update itself.
        /// 
        /// Provides a snapshot of timing values.
        public override void Update(GameTime gameTime)
        {
            // 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);
        }
    }
}

 

При создании экземпляра класса мы устанавливаем каждый спрайт в случайную позицию на экране. При этом возможно перекрытие спрайтов, поэтому осуществляется поиск допустимой позиции (процедура howManyCollides()) для каждого спрайта до тех пор, пока не будет выяснено, что текущая позиция не перекрывает другие позиции.
Переменная speed хранит скорость перемещения по X и по Y. В процедуре Move() мы прибавляем значение этой переменной к координате спрайта. После этого проводим две проверки. Первая – на столкновение с границей экрана – с помощью процедуры Check(). Если объект касается одной из сторон границы экрана, позиция объекта фиксируется на границе и соответствующая составляющая скорости объекта меняется на противоположную. Далее следует проверка на столкновение с другими спрайтами – с помощью процедуры IsSpriteCollide(). Здесь проводится обход всех спрайтов с проверкой на столкновение. Если столкновение произошло – скорость спрайта по обеим составляющим меняется на противоположную, изменения тут же применяются, далее происходит проверка на столкновение со всеми спрайтами (с помощью уже упомянутой процедуры howManyCollides), корректировка положения спрайта производится до тех пор, пока не будет выяснено, что он не перекрывает ни одного из других спрайтов. На рис. 4.4. вы можете видеть игровой экран проекта P4_2.

Рис. 4.4. Игровой экран проекта P4_2

Рассмотрим еще один пример обработки столкновений.

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

Создадим простую игру на базе проекта P4_2. Игрок должен за минимальное время попасть по каждому из 100 находящихся на экране объектов, мышью. Объект, по которому попали мышью, уничтожается.

Здесь нам понадобится алгоритм проверки попадания точки в пределы спрайта. Как и прежде, спрайты представлены прямоугольниками. Для проверки попадания точки в прямоугольник, о котором известна координата его левой верхней точки, ширина и высота, воспользуемся таким алгоритмом (листинг 4.7.). A – это прямоугольник, B – это точка.

Листинг 4.7. Алгоритм проверки попадания точки в прямоугольник
1
2
3
4
5
6
7
8
Если (А.X+A.Ширина > B.X И
    A.X < B.X И
    A.Y+A.Высота>В.Y И
    A.Y)
Тогда
    Есть столкновение
Иначе
    Нет столкновения

 


Создадим новый проект P4_3 на базе проекта P4_2. В листинге 4.8. вы можете видеть код объекта Game1.

Листинг 4.8. Код объекта Game1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
 
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);
        }
 
        /// 
        /// This is called when the game should draw itself.
        /// 
        /// Provides a snapshot of timing values.
        protected override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
 
            // TODO: Add your drawing code here
            spriteBatch.Begin();
            base.Draw(gameTime);
            spriteBatch.End();
        }
    }
}

 


Здесь мы создаем 10 объектов класса spriteComp и проверяем, не достигнута ли цель игры – уничтожение всех объектов. Для контроля количества уничтоженных объектов используем переменную Score, для контроля за достижением цели – переменную IsWin. Основная работа ведется в объектах класса spriteComp. В листинге 4.9. вы можете найти соответствующий им код.

Листинг 4.9. Код объекта spriteComp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
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);
        }
    }
}

 


Здесь мы сначала расставляем спрайты таким образом, чтобы они не пересекались, перемещаем их, учитывая столкновения друг с другом и с границами экрана, а так же – отслеживаем состояние мыши. При щелчке мышью проверяем, не находится ли позиция, в которой в этот момент находился указатель, в пределах текущего спрайта. Если это так – увеличиваем количество очков и уничтожаем текущий объект. На рис. 4.5. вы можете видеть игровой экран проекта P4_3.

Рис. 4.5. Игровой экран проекта P4_3

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

Обработка попадания точки в круговую область

Идея проверки попадания точки в пределы окружности такова. Нужно рассчитать расстояние между точкой и центром окружности. Если это расстояние не превысит радиус окружности – значит точка находится внутри неё. Создадим новый стандартный игровой проект P4_4, нарисуем окружность и замерим её радиус. В нашем случае радиус окружности составил 250 пикселей. Добавим изображение в проект и выведем его на экран. Включим отображение указателя мыши в игровом окне. При щелчке левой кнопкой мыши вычислим расстояние от центра окружности до точки, в которой произошел щелчок. Если это расстояние меньше, чем радиус окружности – выведем в заголовок окна сообщение «Вы попали в мишень!», в противном случае выведем сообщение «Вы не попали в мишень». Для реализации данного примера мы ограничились классом Game1, не разрабатывая отдельных игровых компонентов. В листинге 4.10. вы можете видеть код класса Game1.

Листинг 4.10. Код класса Game1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
 
namespace P4_4
{
    /// 
    /// This is the main type for your game
    /// 
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        private Texture2D MySprite;
        private Vector2 position = new Vector2(150, 30);
        MouseState mouse;
        Vector2 Center;
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
 
        }
 
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
 
            base.Initialize();
            Center.X = position.X + 250+8;
            Center.Y = position.Y + 250+8;
            this.IsMouseVisible = true;
        }
 
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            MySprite = Content.Load<Texture2D>("circle");
            // TODO: use this.Content to load your game content here
        }
 
        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();
 
            mouse = Mouse.GetState();
            if (mouse.LeftButton == ButtonState.Pressed)
 
            if (IsPointInCircle()) Window.Title = "Вы попали в мишень!";
            else Window.Title = "Вы не попали в мишень!";
            // TODO: Add your update logic here
 
            base.Update(gameTime);
        }
 
        bool IsPointInCircle()
        {
                double length = Math.Pow(( Math.Pow((mouse.X - Center.X), 2) + Math.Pow((mouse.Y - Center.Y), 2)),0.5);
                if (length <= 251) return true;
                else return false;
        }
 
       protected override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
 
            // TODO: Add your drawing code here
            spriteBatch.Begin();
            spriteBatch.Draw(MySprite, position, Color.White);
            spriteBatch.End();
            base.Draw(gameTime);
        }
    }
}

 


На рис. 4.6. вы может видеть игровой экран проекта P4_4.

Рис. 4.6. Игровой экран проекта P4_6

Вопросы

1) Для упрощения обработки столкновений объектов в двумерном пространстве объект можно представить состоящим из
  • a. Прямоугольников, описанных около объекта
  • b. Прямоугольников, вписанных в объект
  • c. Такое представление не требуется – достаточно проверить попиксельное перекрытие объектов
2) Если спрайт перемещается со скоростью более чем 1 пиксель за один проход игрового цикла и обрабатывается его столкновение с другим спрайтом, нужно ли применять дополнительные меры для того, чтобы при столкновении спрайтов расположить их таким образом, чтобы между ними не было видимого промежутка?
  • a. Нет, не нужно ни при каких обстоятельствах – при столкновении спрайты автоматически располагаются вплотную друг к другу
  • b. Нужно, но лишь в том случае, если указанный недостаток вредит игровому процессу
3) Если игровые объекты перемещаются автоматически и есть необходимость обрабатывать их столкновения, нужно ли разрабатывать специальные процедуры для этого?
  • a. Нет, так как автоматически перемещаемые объекты автоматически обрабатывают столкновения друг с другом
  • b. Да, так как то, что объекты перемещаются без участия пользователя, еще не значит, что они автоматически обрабатывают столкновения друг с другом
4) Взаимодействие каких объектов можно проконтролировать, используя нижеприведенный алгоритм?
Если (А.X+A.Ширина > B.X И
A.X < B.X И
A.Y+A.Высота>В.Y И
A.YТогда
Есть столкновение
Иначе
Нет столкновения
  • a. Двух прямоугольников
  • b. Точки и окружности
  • c. Точки и прямоугольника
  • d. Прямоугольника и окружности
5) Взаимодействие каких объектов можно проконтролировать, используя нижеприведенный алгоритм?
Если (А.X+A.Ширина > B.X И
A.X < B.X+B.Ширина И
A.Y+A.Высота>В.Ширина И
A.YТогда
Есть столкновение
Иначе
Нет столкновения
  • a. Двух прямоугольников
  • b. Точки и окружности
  • c. Точки и прямоугольника
  • d. Прямоугольника и окружности
6) Для обработки взаимодействия точки и окружности достаточно знать
  • a. Координаты точки и координаты центра окружности
  • b. Координаты центра окружности и координату X точки
  • c. Координаты точки и длину окружности
  • d. Координаты точки, координаты центра окружности и радиус окружности

3130 Прочтений •  [Взаимодействие 2D объектов (столкновения)] [08.08.2012] [Комментариев: 0]
Добавил: Ukraine Vova
Ссылки
HTML: 
[BB Url]: 
Похожие статьи
Название Добавил Добавлено
• Взаимодействие 2D объектов (столкно... Ukraine Vova 08.08.2012
Ни одного комментария? Будешь первым :).
Пожалуйста, авторизуйтесь для добавления комментария.

Проект входит в сеть сайтов «8Gamers Network»

Все права сохранены. 8Gamers.NET © 2011 - 2025

Статьи
Рецензия на Pressure
Рецензия на Pressure
Чтобы обратить на себя внимание, начинающие маленькие разработчики, как правило, уходят в жанры, ...
Рецензия на Lost Chronicles of Zerzura
Рецензия на Lost Chron...
Игры, сделанные без любви и старания, похожи на воздушный шар – оболочка есть, а внутри пусто. Lo...
Рецензия на The Bridge
Рецензия на The Bridge
«Верх» и «низ» в The Bridge — понятия относительные. Прогуливаясь под аркой, можно запросто перей...
Рецензия на SimCity
Рецензия на SimCity
Когда месяц назад состоялся релиз SimCity, по Сети прокатилось цунами народного гнева – глупые ош...
Рецензия на Strategy & Tactics: World War 2
Рецензия на Strategy &...
Название Strategy & Tactics: World War II вряд ли кому-то знакомо. Зато одного взгляда на ее скри...
Рецензия на игру Scribblenauts Unlimited
Рецензия на игру Scrib...
По сложившейся традиции в информационной карточке игры мы приводим в пример несколько похожих игр...
Рецензия на игру Walking Dead: Survival Instinct, The
Рецензия на игру Walki...
Зомби и продукция-по-лицензии — которые и сами по себе не лучшие представители игровой биосферы —...
Обратная связь | RSS | Донейт | Статистика | Команда | Техническая поддержка