Навигация
· XNA FAQ
· С чего начать
· Конкурсы
· Обратная связь
· XNA Блоги
Сейчас на сайте
· Гостей: 1

· Пользователей: 0

· Всего пользователей: 3,684
· Новый пользователь: headron
Последние фото
Эх, чуть не проспал закрытие.
Эх, чуть не проспал ...
Альбом: XNA Engine

GB
GB
Альбом: XNA Engine

South Park Coon & Friends
South Park Coon & Fr...
Альбом: XNA Games

Блоги
yavshoke
» XboxOne - интерес...
dampirik
» Push уведомления ...
dampirik
» Реклама,статистик...
Chort
» XNA и StartCoroutine
Chort
» Curve Class
dampirik
» Реклама, статисти...
dampirik
» Увеличение скорос...
dampirik
» Реклама, статисти...
general
» Распаковка DxtCom...
general
» Как работать с XN...
Поддержка
microsoft.com
1gb.ru - Дом для вашего сайта
Статистика посещений:

Простейшая игровая физика
В этой лабораторной работе мы поговорим о реализации физических законов в компьютерных играх.

Цель работы
  • Научиться добавлять в игры механизмы, имитирующие физические законы
Задачи работы
  • Создать шаблон платформенной игры
Немного теории

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

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

Шаблон платформенной игры

Платформенные игры или платформеры получили такое название потому, что игрок управляет персонажем, который перемещается по площадкам – платформам. В ходе прохождения игры он переходит или перепрыгивает с платформы на платформу, собирает так называемые бонусы, сражается с компьютерными «врагами» и т.д. Действие подобных игр происходит в двумерной среде. Разработаем шаблон такой игры.

Для начала подумаем о том, как нам конструировать игровые экраны. Экран типичной платформенной игры состоит из элементов, которые по-разному взаимодействуют с объектом пользователя. Некоторые из них (стены) непроницаемы для объекта, некоторые (бонусы) при столкновении с объектом исчезают, а игроку начисляются очки или даются какие-то другие улучшения игрового персонажа. Объекты-лестницы позволяют перемещаться между отдельными «этажами» игрового экрана, которые недоступны при других способах перемещения, объекты-враги отнимают некоторое количество очков (или «жизней») у персонажа. Как правило, объекты, которыми заполнен экран, имеют определенный размер, обычно – одинаковый. То есть при их расстановке по экрану можно представить, что экран разбит на клетки, равные размерам объекта. Перемещение игрового объекта, контролируемого пользователем, совсем необязательно должно быть прерывистым, поклеточным, однако сам игровой экран удобнее всего конструировать именно таким способом.

Прежде чем начинать разработку, создадим несколько изображений, которые будем использовать в качестве графических образов игровых объектов. На рис. 5.1. вы можете видеть эти объекты.


Рис. 5.1. Изображения для визуализации игровых объектов и фона

Перечислим объекты, которые понадобятся нам для создания прототипа платформенной игры.
  1. Объект игрока (me.png) – им управляет играющий.
  2. Фон (background.png) – фоновое изображение.
  3. Бонус №1 (bonus1.png) – бонус первого вида. Если объект игрока соприкасается с этим бонусом, игроку начисляется 100 очков.
  4. Бонус №2 (bonus2.png) – бонус второго вида. Если объект игрока соприкасается с этим бонусом, игроку добавляется 1 «жизнь».
  5. Враг (enemy.png) – объект «врага». Если игрок соприкасается с объектом врага – количество «жизней» уменьшается на 1. При количестве жизней меньше 0 игра заканчивается. Если игрок сможет прыгнуть и приземлиться на объект врага – враг будет уничтожен, а игрок получит 50 очков.
  6. Лестница (ladder.png) – игрок может подниматься и опускаться по лестнице там, где другим способом ему не пройти.
  7. Стена (wall.png) – стена. Стена непроницаема для объекта игрока. Объект игрока может подпрыгнуть только тогда, когда снизу находится стена.

Создадим новый стандартный объект, назовем его P5_1. Добавим в папку Content нужные ресурсы. Все объекты (кроме объекта игрока), которыми мы будем пользоваться, имеют размеры 64х64 пикселя, объект игрока имеет размер 32х32 пикселя. Для начала нам нужно разработать систему конструирования игровых уровней, в данном случае – систему конструирования игровых экранов. Мы пользуемся разрешением игрового окна 640х512 пикселей, то есть – 8х10 элементов размера 64х64 пикселя. Игровой экран разбит на 80 ячеек, в каждой из которых может быть один из игровых объектов.

Для хранения информации о содержимом каждой из этих ячеек используем двумерный массив. Каждая ячейка массива соответствует ячейке игрового экрана. Для того, чтобы получить координаты ячейки, соответствующие ячейке массива с индексом (i, j), достаточно умножить каждый из элементов индекса на 64. Верхней левой ячейке экрана соответствует элемент массива с индексом (0, 0), ячейке в правом нижнем углу – элемент (7,9).

Мы назвали этот массив Layer, в листинге 5.1. приведен код его инициализации.

Листинг 5.1. Код инициализации массива
1
2
3
4
5
6
7
8
9
10
Layer = new int[8, 10] {
            { 4, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
            { 1, 5, 1, 3, 5, 1, 1, 2, 1, 5 },
            { 0, 0, 0, 1, 2, 1, 1, 2, 0, 4 },
            { 0, 1, 3, 0, 2, 0, 0, 2, 0, 3 },
            { 0, 5, 0, 0, 1, 1, 1, 2, 0, 1 },
            { 0, 0, 0, 1, 0, 0, 0, 2, 0, 1 },
            { 6, 0, 1, 3, 4, 0, 5, 2, 0, 1 },
            { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
            };

 


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

В табл. 5.1. приведено соответствие чисел, используемых в таблице-конструкторе уровня и объектов.


Таблица 5.1. Номера объектов в массиве Layer
Номер Название объекта
0 Объект отсутствует
1 Стена
2 Лестница
3 Бонус №1
4 Бонус №2
5 Враг
6 Игрок


На рис. 5.2. приведено изображение игрового экрана, сконструированного в соответствии с таблицей и содержимым массива.


Рис. 5.2. Игровой экран, сконструированный в соответствии с массивом Layer

На рис. 5.3. приведено окно проекта.


Рис. 5.3. Окно проекта

В табл. 5.2. приведено описание классов, которые мы используем в данном проекте.

Таблица 5.2. Классы проекта
Имя файла Описание
Game1.cs Класс стандартного проекта
gBaseClass.cs Базовый класс – родитель классов игровых объектов. Он содержит процедуры визуализации объектов и их расстановки по игровому полю. Этот класс наследует класс DrawableGameComponent
Bonus1.cs Класс для создания объектов бонусов вида №1
Bonus2.cs Класс для создания объектов бонусов вида №2
Enemy.cs Класс для создания объектов-врагов
Ladder.cs Класс для создания лестниц
Me.cs Класс для создания объекта игрока. Именно он содержит основную часть игровой логики, в том числе – реализацию игровой физики для объекта-игрока
Wall.cs Класс для создания стен


В листинге 5.2. приведен код класса Game1.

Листинг 5.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
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 P5_1
{
 
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Texture2D txtBackground;
        Texture2D txtBonus1;
        Texture2D txtBonus2;
        Texture2D txtEnemy;
        Texture2D txtLadder;
        Texture2D txtMe;
        Texture2D txtWall;
        //Массив для конструирования уровня
        public int[,] Layer;
        Rectangle recBackround = new Rectangle(0, 0, 640, 512);
        Rectangle recSprite = new Rectangle(0, 0, 64, 64);
 
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }
 
 
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            Layer = new int[8, 10] { 
            { 4, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
            { 1, 5, 1, 3, 5, 1, 1, 2, 1, 5 }, 
            { 0, 0, 0, 1, 2, 1, 1, 2, 0, 4 }, 
            { 0, 1, 3, 0, 2, 0, 0, 2, 0, 3 }, 
            { 0, 5, 0, 0, 1, 1, 1, 2, 0, 1 }, 
            { 0, 0, 0, 1, 0, 0, 0, 2, 0, 1 },
            { 6, 0, 1, 3, 4, 0, 5, 2, 0, 1 },
            { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
            };
            //Устанавливаем разрешение игрового окна
            //640х512
 
            graphics.PreferredBackBufferWidth = 640;
            graphics.PreferredBackBufferHeight = 512;
            graphics.ApplyChanges();
            base.Initialize();
        }
 
        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
            Services.AddService(typeof(SpriteBatch), spriteBatch);
            txtBackground = Content.Load<Texture2D>("background");
            txtBonus1 = Content.Load<Texture2D>("bonus1");
            txtBonus2 = Content.Load<Texture2D>("bonus2");
            txtBonus1 = Content.Load<Texture2D>("bonus1");
            txtEnemy = Content.Load<Texture2D>("enemy");
            txtLadder = Content.Load<Texture2D>("ladder");
            txtMe = Content.Load<Texture2D>("me");
            txtWall = Content.Load<Texture2D>("wall");
            //Вызываем процедуру расстановки объектов в игровом окне
            AddSprites ();
 
                    }
        //Процедура расстановки объектов в игровом окне
        void AddSprites()
        {
            //Переменные для временного хранения адреса
            //объекта-игрока
            int a=0,b=0; 
            //Просматриваем массив Layer
            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 10; j++)
                {
                    //Если элемент с индексом (i,j) равен 1 - 
                    //устанавливаем в соответствующую позицию элемент с
                    //номером 1, то есть - стену
                    if (Layer[i,j]==1) 
                        Components.Add(new GameObj.Wall(this, ref txtWall, new Vector2(j, i), recSprite));
                    if (Layer[i, j] == 2)
                        Components.Add(new GameObj.Ladder(this, ref txtLadder, new Vector2(j, i), recSprite));
                    if (Layer[i, j] == 3)
                        Components.Add(new GameObj.Bonus1(this, ref txtBonus1, new Vector2(j, i), recSprite));
                    if (Layer[i, j] == 4)
                        Components.Add(new GameObj.Bonus2(this, ref txtBonus2, new Vector2(j, i), recSprite));
                    if (Layer[i, j] == 5)
                        Components.Add(new GameObj.Enemy(this, ref txtEnemy, new Vector2(j, i), recSprite));
                    //Если обнаружен объект игрока - запишем его координаты
                    if (Layer[i, j] == 6)
                    {
                        a = i;
                        b = j;
                    }  
                }
            }
           //Последним установим объект игрока - так он гарантированно
           //расположен поверх всех остальных объектов
                Components.Add(new GameObj.Me(this, ref txtMe, new Vector2(b, a), new Rectangle (0,0,32,32)));
        }
 
        protected override void UnloadContent()
        {
            txtBackground.Dispose();
            txtBonus1.Dispose();
            txtBonus2.Dispose();
            txtBonus1.Dispose();
            txtEnemy.Dispose();
            txtLadder.Dispose();
            txtMe.Dispose ();
            txtWall.Dispose();
            spriteBatch.Dispose();
        }
 
        protected override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
        }
 
        protected override void Draw(GameTime gameTime)
        {
            spriteBatch.Begin();
            //выведем фоновое изображение
            spriteBatch.Draw(txtBackground, recBackround ,Color.White);
            //Выведем игровые объекты
            base.Draw(gameTime);
            spriteBatch.End();
        }
    }
}

Теперь рассмотрим код класса gBaseClass (листинг 5.3.).

Листинг 5.3. Код класса gBaseClass
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
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.Content;
 
namespace P5_1.GameObj
{
 
    public class gBaseClass : Microsoft.Xna.Framework.DrawableGameComponent
    {
        Texture2D sprTexture;
        public Vector2 sprPosition;
        public Rectangle sprRectangle;
        public gBaseClass(Game game, ref Texture2D _sprTexture,
            Vector2 _sprPosition, Rectangle _sprRectangle)
            : base(game)
        {
            sprTexture = _sprTexture;
            //Именно здесь производится перевод индекса элемента массива
            //в координаты на игровом экране
            sprPosition = _sprPosition * 64;
            sprRectangle = _sprRectangle;
        }
 
 
        public override void Initialize()
        {
            base.Initialize();
        }
 
        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
        }
        public override void Draw(GameTime gameTime)
        {
            SpriteBatch sprBatch =
                (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));
            sprBatch.Draw(sprTexture, sprPosition, Color.White);
            base.Draw(gameTime);
        }
    }
}

 

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

Классы Bonus1, Bonus2, Ladder, Wall, Enemy полностью наследуют код класса gBaseComponent, фактически, являясь его точными копиями. Можно было бы отказаться от разработки этих классов, создав соответствующие объекты путем создания объекта класса gBaseComponent. Однако, подход с созданием отдельного класса для каждого компонента предпочтительнее по нескольким причинам. Во-первых – наличие самостоятельных классов для отдельных компонентов позволяет удобно осуществлять различные проверки при работе с игровым объектом. Во-вторых – если понадобится расширить функциональность одного из объектов (например, перемещать объект Enemy и т.д.) – отдельные классы готовы для модификаций. В листинге 5.4. приведен код класса Bonus1. Остальные классы, как уже было сказано, идентичны ему.

Листинг 5.4. Код класса Bonus1
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
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 P5_1.GameObj
{
    public class Bonus1 : gBaseClass
    {
        public Bonus1(Game game, ref Texture2D _sprTexture,
            Vector2 _sprPosition, Rectangle _sprRectangle)
            : base(game, ref _sprTexture, _sprPosition, _sprRectangle)
        {
        }
 
        public override void Initialize()
        {
            base.Initialize();
        }
 
        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
        }
    }
}

 

Рассмотрим код класса Me (листинг 5.5.) – это основной игровой компонент, который реализует игровой процесс.

Листинг 5.5. Код класса Me
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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
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 P5_1.GameObj
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class Me : gBaseClass
    {
        //Прямоугольник, представляющий игровое окно
        Rectangle scrBounds;
        //Скорость, с которой будет перемещаться спрайт
        float sprSpeed=2;
        //"Сила тяжести"
        float sprGravity = 0.4f;
        //"Ускорение свободного падения"
        float sprAcceleration = 0.03f;
        //Скорость при падении
        float sprGrAcc = 0;
        //Скорость, с которой объект будет подпрыгивать
        float sprJump = 70;
        //Переменная для хранения количества "жизней" объекта
        int sprLives = 2;
        //Переменная для хранения набранных очков
        int sprScores = 0;
 
 
        public Me(Game game, ref Texture2D _sprTexture,
            Vector2 _sprPosition, Rectangle _sprRectangle)
            : base(game, ref _sprTexture, _sprPosition, _sprRectangle)
        {
 
            scrBounds = new Rectangle(0, 0,
              game.Window.ClientBounds.Width,
              game.Window.ClientBounds.Height);
 
        }
 
        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
 
 
            base.Initialize();
        }
        //Проверка на столкновение с границами экрана
        void Check()
        {
            if (sprPosition.X < scrBounds.Left)
            {
                sprPosition.X = scrBounds.Left;
            }
            if (sprPosition.X > scrBounds.Width - sprRectangle.Width)
            {
                sprPosition.X = scrBounds.Width - sprRectangle.Width;
            }
            if (sprPosition.Y < scrBounds.Top)
            {
                sprPosition.Y = scrBounds.Top;
            }
            if (sprPosition.Y > scrBounds.Height - sprRectangle.Height)
            {
                sprPosition.Y = scrBounds.Height - sprRectangle.Height;
            }
        }
        //Процедуры, которые используются для перемещения объекта в одном из указанных направлений
        void MoveUp(float speed)
        {
            this.sprPosition.Y -= speed;
        }
        void MoveDown(float speed)
        {
            this.sprPosition.Y += speed;
        }
        void MoveLeft(float speed)
        {
            this.sprPosition.X -= speed;
        }
        void MoveRight(float speed)
        {
            this.sprPosition.X += speed;
        }
        //Функция, которая проверяет, находится ли непосредственно под нашим объектом
        //объект типа Wall - то есть стена. Наш алгоритм обработки столкновений удерживает
        //объект на небольшом расстоянии от стены, не давая ему пройти сквозь нее.
        //Поэтому при проверке к координате Y объекта добавляется 1. Эта функция используется
        //при проверке возможности совершения объектом прыжка - он может подпрыгнуть
        //только в том случае, если по ним есть стена.
 
        bool IsWallIsInTheBottom()
        {
            int Collision = 0;
            foreach (gBaseClass spr in Game.Components)
            {
 
                if (spr.GetType() == (typeof(Wall)))
                {
                    if (this.sprPosition.X + this.sprRectangle.Width > spr.sprPosition.X &&
                         this.sprPosition.X < spr.sprPosition.X + spr.sprRectangle.Width &&
                         this.sprPosition.Y+1 + this.sprRectangle.Height+1 > spr.sprPosition.Y &&
                         this.sprPosition.Y+1 < spr.sprPosition.Y + spr.sprRectangle.Height) Collision++;
                }
            }
 
            if (Collision > 0) return true;
            else
                return false;
 
        }        
 
        //Функция используется как вспомогательная
        //Она проверяет, сталкивается ли наш объект с объектом
        //класса gBaseClass и возвращает True если столкновение есть
        bool IsCollideWithObject(gBaseClass 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);
 
 
        }
        //Функция проверяет, находится ли объект в пределах лестницы
        //Если объект находится на лестнице - его поведение меняется
        //Он может взбираться и спускаться по лестнице, но не может подпрыгивать
 
        bool IsCollideWithLadder()
        {
            foreach (gBaseClass spr in Game.Components)
            {
                if (spr.GetType() == (typeof(Ladder)))
                {
                    if (this.sprPosition.X + this.sprRectangle.Width+1 > spr.sprPosition.X &&
                        this.sprPosition.X+1 < spr.sprPosition.X + spr.sprRectangle.Width &&
                        this.sprPosition.Y + this.sprRectangle.Height+1 > spr.sprPosition.Y &&
                        this.sprPosition.Y+1 < spr.sprPosition.Y + spr.sprRectangle.Height)
 
                        return true;
                }
           }
           return false;
        }
 
        //Процедура, отвечающая за перемещение игрового объекта
         void Move()
        {
 
 
            KeyboardState kbState = Keyboard.GetState();
             //При нажатии кнопки "Вверх"
            if (kbState.IsKeyDown(Keys.Up))
            {
                //Если под объектом находится стена
                //и он не соприкасается с лестницей
                //Объект подпрыгивает
                if (IsWallIsInTheBottom()==true&& IsCollideWithLadder ()==false )
                {
 
                    MoveUp(sprJump);
                   //При прыжке проводится проверка на контакт со стеной
                    //Которая может быть расположена над объектом
                    //при необходимости его координаты корректируются
                    while (IsCollideWithWall())
                    {
                        MoveDown((sprSpeed / 10));
                    }
                }
                //Если объект находится на лестнице
                //Он перемещается вверх с обычной скоростью без прыжков
                if (IsCollideWithLadder() == true)
                {
                    MoveUp(sprSpeed);
 
                   while (IsCollideWithWall())
                    {
                        MoveDown((sprSpeed / 10));
                    }
 
                }
 
           }
             //При нажатии кнопки "Вниз"
             //Происходит обычная процедура перемещения объекта с проверкой
            if (kbState.IsKeyDown(Keys.Down))
            {
                MoveDown(sprSpeed);
                while (IsCollideWithWall())
                {
                    MoveUp((sprSpeed / 10));
                }
            }
             //Точно так же обрабатывается нажатие кнопки "Влево"
            if (kbState.IsKeyDown(Keys.Left))
            {
 
 
                MoveLeft(sprSpeed);
                while (IsCollideWithWall())
                {
                    MoveRight((sprSpeed / 10));
                }
            }
             //Аналогично - перемещение вправо
            if (kbState.IsKeyDown(Keys.Right))
            {
 
                MoveRight(sprSpeed);
                while (IsCollideWithWall())
                {
                    MoveLeft((sprSpeed / 10));
                }
            }
        }
        //Проверяем столкновение объекта со стеной
        //Результат проверки нужен для обработки перемещений 
        bool IsCollideWithWall()
        {
         foreach (gBaseClass  spr in Game.Components)
            {
              if (spr.GetType() == (typeof(Wall)))
                {
                    if (IsCollideWithObject(spr)) return true;
                }
            }
 
           return false ;
        }
        //В этой процедуре мы проверяем столкновения с объектами-бонусами
        //и объектами-врагами, причем, наш объект может уничтожать врагов,
        //прыгая на них сверху. С точки зрения данной процедуры такой прыжок
        //ничем не отличается от обычного контакта с объектом-врагом и приводит к
        //проигрышу. Поэтому проверка на прыжок нашего объекта на вражеский объект
        //вынесена в отдельную процедуру
        void IsCollideWithAny()
        {
            //Заводим переменную для временного хранения
            //ссылки на объект, с которым столкнулся игровой объект
            gBaseClass FindObj = null;
            //Проверка на столкновение с объектами Bonus1
            foreach (gBaseClass spr in Game.Components)
            {
 
                if (spr.GetType() == (typeof(Bonus1)))
                {
                    if (IsCollideWithObject(spr))
                    {
                        FindObj = spr;
 
                    }
                }
            }
 
            //Если было столкновение - уничтожаем объект, с которым было столкновение
            //и увеличиваем число очков
            if (FindObj != null)
            {
                sprScores += 100;
                FindObj.Dispose();
                FindObj = null;
            }
            //Проверка на столкновение с объектами Bonus2
 
            foreach (gBaseClass spr in Game.Components)
            {
                if (spr.GetType() == (typeof(Bonus2)))
                {
                    if (IsCollideWithObject(spr))
                    {
 
 
                        FindObj = spr;
                    }
                }
            }
            //Если столкновение было уничтожим объект
            //Увеличим число "жизней"
          if (FindObj != null)
            {
                sprLives += 1;
                FindObj.Dispose();
                FindObj = null;
            }
            //Проверка на столкновение с объектами Enemy
                foreach (gBaseClass spr in Game.Components)
                    {
                        if (spr.GetType() == (typeof(Enemy)))
                        {
                            if (IsCollideWithObject(spr))
                            {
                                FindObj = spr;
                            }
                        }
                }
 
        if (FindObj != null)
        {
 
                FindObj.Dispose();
 
                sprLives--;
        }
 
        }
        //Этот метод реализует игровую "силу тяжести"
        //Объект, который находится в свободном пространстве падает вниз
 
        void GoToDown()
        {
	   // перемещается вниз
           //при перемещении проверяется столкновение объекта со стеной
 
            if (IsCollideWithLadder() == false)
            {
                if (sprGrAcc == 0) sprGrAcc = sprAcceleration;
 
                MoveDown(sprGrAcc);
                while (IsCollideWithWall())
                {
                    MoveUp((sprSpeed / 10));
                }
                sprGrAcc += sprAcceleration;
                if (IsWallIsInTheBottom()) sprGrAcc = 0;
                if (IsCollideWithLadder()) sprGrAcc = 0;
            }
        }
        //Метод, проверяющий, не "прыгнул" ли игровой объект на "голову" объекту-врагу
        //Обратите внимание на то, что процедура проверки условия на первый взгляд кажется похожей
        //на обычные проверки, однако таковой не является
        void IsKillEnemy()
        {
            gBaseClass FindObj = null;
            foreach (gBaseClass spr in Game.Components)
            {
                if (spr.GetType() == (typeof(Enemy)))
                {
                    //Если игровой объект находится в пределах координат X объекта-врага
                    //Если при этом игровой объект находится выше, чем объект-враг
                    //менее чем на 35 пикселей (если считать по верхней стороне прямоугольника
                    //описанного около объектов - это значит, что игровой объект
                    //"прыгнул" на объект-врага и уничтожил его.
                    if (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&&(spr.sprPosition .Y -this.sprPosition .Y)<35)
                    {
                        //Если условие выполняется - сохраним ссылку на объект врага
                        FindObj = spr;
                    }
                }
            }
            //Если был удачный прыжок на врага
            //добавим игроку очков и уничтожим объект класса Enemy
            if (FindObj != null)
            {
                sprScores += 50;
                FindObj.Dispose();
            }
        }
        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            //Вызовем процедуру перемещения объекта по клавиатурным командам
            Move();
            //Проверим, не вышел ли он за границы экрана, если надо исправим его позицию
            Check();
            //Применим к объекту "силу тяжести"
            GoToDown();
            //Проверим, не прыгнул ли наш объект на объект класса Enemy
            //Вызов этого метода предшествует вызову метода IsCollideWithAny()
            //Иначе прыжок на врага с целью уничтожить его может обернуться
            //Печальными последствиями для нашего объекта
            IsKillEnemy();
            //Проверим на другие столкновения
            IsCollideWithAny();
 
            Game.Window.Title = "У вашего персонажа " + sprLives.ToString() +
                " жизни(ей) и " + sprScores + " очков";
            if (sprLives < 0)
            {
                Game.Window.Title = "Вы проиграли";
                this.Dispose();
            }
            base.Update(gameTime);
        }       
    }
}

 


Назначение свойств и методов этого класса подробно описано внутри кода. Отметим ключевые методы, которые реализуют игровую физику.

Метод GoToDown() перемещает объект вниз до тех пор, пока он не коснется объекта-стены. Если объект «стоит» на стене (метод IsWallIsInTheBottom()) – он может подпрыгнуть. Если объект касается лестницы (IsCollideWithLadder()) – «сила тяжести» на него не действует, он может перемещаться по лестнице в любом направлении, однако не может подпрыгивать. Если объект сталкивается с бонусом №1 (IsCollideWithAny())– ему начисляются очки, если с бонусом №2 – жизни. Если объект столкнулся с врагом – одна жизнь теряется, если объект «прыгнул» на врага (IsKillEnemy()) – объект врага уничтожается, игроку начисляются очки.

Задание

  1. Разработайте собственные спрайты для визуализации объектов.
  2. Реализуйте самостоятельно игру, которая описана выше.
  3. Доработайте объект Enemy таким образом, чтобы он мог автоматически (по параметрам, переданным при его конструировании) перемещаться по экрану в горизонтальной или вертикальной плоскости, отталкиваясь от других объектов (кроме лестницы).

Материал публикуется с разрешения авторов - Ю.В. Дашко, А.А. Заика

Студенческий блог MS Russia
Интернет-Университет Информационных технологий - курс разработан по гранту от Microsoft и ИНТУИТ
сайт ИУБиП
Комментарии
#1 | Utugroma 28.05.2012 18:54:36
Спасибо очень помогло побольше таких примеров
#2 | Comanche_Ak 08.01.2013 11:04:44
это не только пример для физики Smile
Добавить комментарий
Пожалуйста, залогиньтесь для добавления комментария.
Рейтинги
Рейтинг доступен только для пользователей.

Пожалуйста, залогиньтесь или зарегистрируйтесь для голосования.

Отлично! Отлично! 100% [1 Голос]
Очень хорошо Очень хорошо 0% [Нет голосов]
Хорошо Хорошо 0% [Нет голосов]
Удовлетворительно Удовлетворительно 0% [Нет голосов]
Плохо Плохо 0% [Нет голосов]
Авторизация
Логин

Пароль



Вы не зарегистрированы?
Нажмите здесь для регистрации.

Забыли пароль?
Запросите новый здесь.
Мини-чат
Вы должны авторизироваться, чтобы добавить сообщение.

27.08.2014
Я умею немного на asp.net + html и css

22.08.2014
на ASP mvc 3 есть пару проектов. Могу помочь, если нужно. Обидно, если закроется Frown

21.08.2014
я тоже ноль

21.08.2014
Я в вебе только с php занимался да и то на уровне чтоб работало.

21.08.2014
Я в вебе полный ноль…

21.08.2014
Переводить его надо, хоть на ту же азуру. И двиг менять на что-то современное. Если есть веб-разрабы - можем скооперироваться. Один делать не буду.

21.08.2014
не знаю всех нюансов по оплате и все хорошее когда нибудь заканчивается

21.08.2014
А что случилось?

21.08.2014
похоже сайт будет работать до 28го числа

09.08.2014
Апи пока не видел. Но есть приложение в магазине Live Lock Screen BETA, так что думаю скоро будет

08.08.2014
Я про API для Update1. На нем работает это

08.08.2014
А что именно нужно? Чтото и сейчас открыто http://msdn.micro.
...105).aspx

06.08.2014
Кто-нибудь слышал об открытии доступа к Lock Screen Api?

31.07.2014
VPDExpress на базе MVS 2012, ни в какую не ловит исключения. Даже если их сам создаешь. И всех так?

25.07.2014
С днем системного администратора причастных к этой профессии! По случаю - тортик от жены

RSS каналы сайта
XNA - Новости
XNA - Статьи
XNA - Форум
XNA - Галерея
XNA - Файлы
Время загрузки: 0,05 секунд 8,709,627 уникальных посетителей