Возможно вы искали: 'Color Balls'

May 12 2025 01:24:29
  • Как сделать 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 » ИИ в компьютерных играх

ИИ в компьютерных играх

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

Цель работы
  • Научиться применять искусственный интеллект в компьютерных играх


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

  • Освоить основные принципы применения искусственного интеллекта в компьютерных играх.
  • Рассмотреть алгоритмы искусственного интеллекта, применимые в играх
  • Создать компьютерную игру, использующую принципы искусственного интеллекта


Обзор подходов к разработке системы ИИ

Тема искусственного интеллекта (artifical intelligence, AI, ИИ) будоражит умы многих начинающих программистов, разработчиков и любителей компьютерных игр. ИИ кажется многим чем-то удивительно сложным, интересным, таинственным. Действительно, ведь технологии ИИ заставляют персонажей игр действовать разумно. Однако в ИИ нет ничего таинственного. За десятилетия развития этой области знаний было разработано огромное количество алгоритмов, применимых в самых разных областях деятельности. И компьютерные игры – лишь сравнительно небольшое поле для технологий искусственного интеллекта.

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

Игровой ИИ, в первом приближении, можно разделить на два вида. Первый – наиболее очевидный – это интеллект отдельных игровых персонажей. Например, каждый танк в популярной некогда приставочной игре Battle City пытается добраться до базы игрока, уничтожить её и его танк. Танки в игре действуют неслаженно, они не отличаются особенным умом, однако играть интересно – всё дело в том, что для данной игры такой вид ИИ вполне подходит. Он не делает игру скучной.

Второй уровень ИИ – это групповой интеллект. Например, вспомним StarCraft. Игрок вынужден сражаться с армией, контролируемой компьютером. Получается, что компьютер управляет большим количеством юнитов (от англ. Unite – единица). Но несложно заметить, что каждое существо, которым управляет групповой ИИ в StarCraft, обладает собственным «разумом». Например, групповой ИИ может направить некоторую группу юнитов на патрулирование местности, но если они встретят на пути неприятеля – отвечать за их действия будет уже их собственный ИИ.

Если бы действия армии в StarCraft никак не контролировались, а ИИ присутствовал лишь на уровне отдельного юнита – игра превратилась бы в скучный поиск и уничтожение врагов. А StarCraft, несмотря на серьёзный возраст (порядка 10 лет) остаётся увлекательной игрой. Даже в однопользовательской кампании StarCraft способна очень сильно «затянуть» игрока, не говоря уже о сетевых баталиях.

Кстати, несложно заметить, что в том же StarCraft индивидуальный ИИ есть и у юнитов, которыми управляет пользователь. Например, та же команда «патрулировать», отданная пользователем, заставит существо из StarCraft послушно ходить по указанному пути. Но если на пути появится препятствие (например, игрок построит там здание, преграждающее путь) – юнит сам решит, что ему делать. Аналогично, он самостоятельно примет решение об атаке, если в поле его видимости появятся враги.

Системы ИИ, применяемые в компьютерных играх, можно разделить на два основных вида. Во-первых – это так называемые детерминированные системы. Они отличаются предсказуемостью действий персонажа. И во-вторых – это недетерминированные системы – персонаж, управляемый таким ИИ, может действовать непредсказуемо, принимать неожиданные решения.
Как мы уже сказали, индивидуальный ИИ юнитов играет подчинённую роль в сравнении с групповым ИИ. А может ли ИИ отдельного юнита повлиять на игру вцелом? Может – в том случае, если предусмотрено распространение успехов отдельного юнита на всех схожих. Например, какой-то юнит столкнулся с сильным противником и чудом вышел победителем в схватке. Этот юнит набрался опыта, который, благодаря групповому ИИ, может быть распространён на других юнитов. Т.е. если один юнит чему-то научился, другие, благодаря групповому ИИ, смогут перенять у него новые умения. Таким образом, индивидуальный и групповой ИИ взаимосвязаны, а в некоторых случаях и взаимозависимы.

Персонаж, оснащённый недетерминированным ИИ, отличается непредсказуемостью поведения, большей «живостью». Играть против таких персонажей обычно гораздо интереснее, чем против жёстко детерминированных. Популярным в последнее время способом реализации недетерминированного ИИ является технология нейронных сетей. Она позволяет создавать персонажи с очень сложным поведением. К тому же, нейросети обладают свойством обучаемости. То есть персонажи игр не только разумно ведут себя, но и учатся на своих ошибках.

На практике находят применение как детерминированные, так и недетерминированные виды ИИ. Обычно они действуют совместно. Например, для выполнения каких-то простых однозначных действий (скажем, при приближении к стене свернуть) могут применяться простые и быстрые детерминированные алгоритмы. В более сложных случаях (например – купить ли акции компании Х учитывая огромное количество параметров, напасть ли на врага, учитывая его возможности, свои возможности, наличие подкрепления и т.д.) – применяются более сложные недетерминированные алгоритмы. Частично детерминированные (например, при приближении к стене персонаж с вероятностью 50% повернёт налево, с вероятностью 30% - направо, и с 20% вероятностью развернётся и пойдёт обратно) так же находят широкое применение в играх.

Реализация алгоритма преследования

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

Создадим новый игровой проект P8_1 на основе проекта P5_1. Будем использовать два объекта – преследователя и жертву. Преследователь будет перемещаться в сторону жертвы со скоростью, на 1 меньше, чем скорость жертвы. Если объекты столкнуться – жертва будет уничтожена.

На рис. 8.1. приведено окно Solution Explorer игрового проекта P8_1.


Рис. 8.1. Окно Solution Explorer

Мы используем базовый класс gBaseClass, класс для объекта-преследователя (Enemy), класс объекта-жертвы (Me) и класс для объекта-стены. Класс объекта-стены будет нужен нам для того, чтобы изучить поведение объекта, реализующего алгоритм преследования, при столкновении с непреодолимым препятствием на пути к жертве. Мы значительно модифицировали код классов в сравнении с исходным проектом P5_1, поэтому ниже вы найдете полный код классов игры. В листинге 8.1. вы можете найти код класса Game1.

Листинг 8.1. Код класса 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
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 P8_1
{
    /// 
    /// This is the main type for your game
    /// 
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Texture2D txtBackground;
        Texture2D txtEnemy;
        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] { 
            { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
            { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, 
            { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, 
            { 0, 0, 0, 0, 1, 6, 0, 0, 0, 0 }, 
            { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
            { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
            { 0, 0, 0, 0, 0, 0, 0, 0, 5, 0 },
            { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
            };
            //Устанавливаем разрешение игрового окна
            //640х512
 
            graphics.PreferredBackBufferWidth = 640;
            graphics.PreferredBackBufferHeight = 512;
            graphics.ApplyChanges();
            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);
            txtBackground = Content.Load<Texture2D>("background");
            txtEnemy = Content.Load<Texture2D>("enemy");
            txtMe = Content.Load<Texture2D>("me");
            txtWall = Content.Load<Texture2D>("wall");
            //Вызываем процедуру расстановки объектов в игровом окне
            AddSprites();
 
            // TODO: use this.Content to load your game content here
        }
        //Процедура расстановки объектов в игровом окне
        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] == 5)
                        Components.Add(new GameObj.Enemy(this, ref txtEnemy, new Vector2(j, i), new Rectangle(0, 0, 32, 32)));
                    //Если обнаружен объект игрока - запишем его координаты
                    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()
        {
        }
 
 
        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();
        }
    }
}

В данном классе производится инициализация матрицы игрового мира и расстановка объектов. Здесь мы, помимо объекта-игрока и объекта-преследователя, разместили на игровом поле несколько стену (рис. 8.2.).


Рис. 8.2. Непреодолимое препятствие

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

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

Листинг 8.2. Код класса 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
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
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 P8_1.GameObj
{
 
    public class gBaseClass : Microsoft.Xna.Framework.DrawableGameComponent
    {
        Texture2D sprTexture;
        public Vector2 sprPosition;
        public Rectangle sprRectangle;
        //Прямоугольник, представляющий игровое окно
        public Rectangle scrBounds;
        //Скорость объекта
        public float sprSpeed;
        public gBaseClass(Game game, ref Texture2D _sprTexture,
            Vector2 _sprPosition, Rectangle _sprRectangle)
            : base(game)
        {
            sprTexture = _sprTexture;
            //Именно здесь производится перевод индекса элемента массива
            //в координаты на игровом экране
            sprPosition = _sprPosition * 64;
            sprRectangle = _sprRectangle;
            scrBounds = new Rectangle(0, 0,
             game.Window.ClientBounds.Width,
             game.Window.ClientBounds.Height);
        }
 
 
        public override void Initialize()
        {
            base.Initialize();
        }
 
        /// 
        /// Allows the game component to update itself.
        /// 
        /// Provides a snapshot of timing values.
        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);
        }
        //Проверка на столкновение с каким-либо объектом
       public 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);
 
 
        }
        //Процедуры для перемещения объекта
        public void MoveUp(float speed)
        {
            this.sprPosition.Y -= speed;
        }
        public void MoveDown(float speed)
        {
            this.sprPosition.Y += speed;
        }
        public void MoveLeft(float speed)
        {
            this.sprPosition.X -= speed;
        }
        public void MoveRight(float speed)
        {
            this.sprPosition.X += speed;
        }
        //Проверка на столкновение со стеной
       public bool IsCollideWithWall()
        {
            foreach (gBaseClass spr in Game.Components)
            {
                if (spr.GetType() == (typeof(Wall)))
                {
                    if (IsCollideWithObject(spr)) return true;
                }
            }
 
            return false;
        }
        //Проверка на столкновение с границами экрана
       public 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;
            }
        }
 
    }
}

 

Мы включили в этот класс методы и свойства, которые будем использовать и для объекта игрока и для объекта-преследователя. Они знакомы вам по проекту P5_1.

Теперь рассмотрим код класса Wall – (листинг 8.3.) мы приводим его здесь лишь для полноты изложения – он не претерпел изменений.

Листинг 8.3. Код класса Wall
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
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 P8_1.GameObj
{
    public class Wall : gBaseClass
    {
        public Wall(Game game, ref Texture2D _sprTexture,
            Vector2 _sprPosition, Rectangle _sprRectangle)
            : base(game, ref _sprTexture, _sprPosition, _sprRectangle)
        {
            // TODO: Construct any child components here
        }
 
        public override void Initialize()
        {
            // TODO: Add your initialization code here
 
            base.Initialize();
        }
 
        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here
 
            base.Update(gameTime);
        }
    }
}

 


Листинг 8.4. содержит код класса Me.

Листинг 8.4. содержит код класса 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
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 P8_1.GameObj
{
    public class Me : gBaseClass
    {
        public Me(Game game, ref Texture2D _sprTexture,
            Vector2 _sprPosition, Rectangle _sprRectangle)
            : base(game, ref _sprTexture, _sprPosition, _sprRectangle)
        {
            sprSpeed = 2;
            // TODO: Construct any child components here
        }
 
        public override void Initialize()
        {
 
            base.Initialize();
        }
 
        public void IsCollideWithAny()
        {
            //Заводим переменную для временного хранения
            //ссылки на объект, с которым столкнулся игровой объект
            gBaseClass FindObj = null;
            //Проверка на столкновение с объектами Enemy
            foreach (gBaseClass spr in Game.Components)
            {
                if (spr.GetType() == (typeof(Enemy)))
                {
                    if (IsCollideWithObject(spr))
                    {
                        FindObj = spr;
                    }
                }
            }
 
            if (FindObj != null)
            {
 
                this.Dispose();
            }
        }
 
        //Процедура, отвечающая за перемещение игрового объекта
        void Move()
        {
            KeyboardState kbState = Keyboard.GetState();
            //При нажатии кнопки "Вверх"
            if (kbState.IsKeyDown(Keys.Up))
            {
                    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));
                }
            }
 
        }
 
        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here
            //Вызовем процедуру перемещения объекта по клавиатурным командам
            Move();
            //Проверим, не вышел ли он за границы экрана, если надо
            //исправим его позицию
            Check();
            IsCollideWithAny();
            base.Update(gameTime);
        }
    }
}

 


Объект класса Me умеет перемещаться по клавиатурным командам, не выходя за границы экрана, останавливаться, «натыкаясь» на стену, а при столкновении с объектом-преследователем объект класса Me уничтожается. Алгоритм преследования реализован в коде класса Enemy (листинг 8.5.).

Листинг 8.5. Код класса Enemy
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
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 P8_1.GameObj
{
    public class Enemy : gBaseClass
    {
        Vector2 Direction;
        public Enemy(Game game, ref Texture2D _sprTexture,
            Vector2 _sprPosition, Rectangle _sprRectangle)
            : base(game, ref _sprTexture, _sprPosition, _sprRectangle)
        {
            sprSpeed = 1;
            Direction = = new Vector2(0, 0);
            // TODO: Construct any child components here
        }
 
        public override void Initialize()
        {
            // TODO: Add your initialization code here
 
            base.Initialize();
        }
        //Вычисление направления на объект-игрока и передвижение в
        //этом направлении
        void SeekAndDestroy()
        {
            foreach (gBaseClass spr in Game.Components)
            {
                if (spr.GetType() == (typeof(Me)))
                {
                    //найдем разницу между координатами преследователя и
                    //игрока
                    Direction  = (this.sprPosition - spr.sprPosition);
                    //Если разность по X положительная
                    //переместим преследователя влево
                    //с учетом того, что стены для него 
                    //непроницаемы.
                    if (Direction.X > 0)
                    {
                        MoveLeft(sprSpeed);
                        while (IsCollideWithWall())
                        {
                            MoveRight((sprSpeed / 10));
                        }
                    }
                    //При отрицательной разности по X
                    //переместим объект вправо
                    if (Direction.X < 0)
                    {
                        MoveRight(sprSpeed);
                        while (IsCollideWithWall())
                        {
                            MoveLeft((sprSpeed / 10));
                        }
                    }
                    //При положительной разности по Y
                    //переместим объект вверх
                    if (Direction.Y > 0)
                    {
                        MoveUp(sprSpeed);
                        while (IsCollideWithWall())
                        {
                            MoveDown((sprSpeed / 10));
                        }
                    }
                    //При отрицательной разности по Y переместим
                    //объект вниз
                    if (Direction.Y < 0)
                    {
                        MoveDown(sprSpeed);
                        while (IsCollideWithWall())
                        {
                            MoveUp((sprSpeed / 10));
                        }
 
                    }
 
                }
            }
        }
 
        public override void Update(GameTime gameTime)
        {
            // Перемещение к объекту игрока
            SeekAndDestroy();
            //Проверка на столкновение с границами экрана
            Check();
            base.Update(gameTime);
        }
    }
}


На каждом шаге игрового цикла объект-преследователь сравнивает свои координаты и координаты объекта игрока и проводит необходимую коррекцию своей позиции.
Как мы уже отмечали, наш объект-преследователь не умеет обходить препятствия. Рассмотрим реализацию алгоритма преследования с обходом препятствий.

Реализация алгоритма перемещения с обходом препятствий

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

Мы используем комбинированный алгоритм – он рассчитан на действия в трех ситуациях. Во-первых, если объект-преследователь «видит» объект-цель – он перемещается к ней в свободном пространстве – так же, как в вышеописанном примере. Если объект-преследователь теряет цель – например – она ушла из пределов прямой досягаемости – он действует различным образом в зависимости от того, находится ли он в свободном пространстве или около стены или границы экрана. Если преследователь находится в свободном пространстве – он перемещается в нем на случайные расстояния, делая повороты. Если он находится около стены – он продолжает обход игрового мира. Как правило, объект, находящийся в свободном пространстве некоторое время «блуждает» по нему – если он снова «увидит» объект-цель – он начнет преследовать его, если он столкнется со стеной или с границей экрана – он продолжит обход игрового мира.

Создадим новый проект P8_2 на основе проекта P8_1. Ранее объект-преследователь перемещался в направлении объекта-цели, мог передвигаться вдоль вертикальных «стен», но даже простейшая конфигурация из препятствий была способна задержать его. Теперь же мы реализуем в объекте-преследователе алгоритм обхода препятствий
Состав проекта, по сравнению с проектом P8_1, не изменился, однако код объектов и основного игрового класса претерпел некоторые изменения. Ниже приведен полный код проекта P8_2.

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

Листинг 8.6. Код класса 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
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 P8_2
{
 
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Texture2D txtBackground;
        Texture2D txtEnemy;
        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] { 
            { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, 
            { 0, 1, 1, 1, 1, 1, 0, 1, 0, 0 }, 
            { 0, 1, 0, 0, 0, 0, 0, 0, 1, 0 }, 
            { 0, 0, 0, 0, 1, 1, 0, 0, 1, 0 }, 
            { 0, 5, 0, 0, 1, 0, 1, 0, 0, 0 }, 
            { 0, 0, 1, 0, 0, 0, 0, 1, 1, 1 },
            { 0, 0, 0, 0, 0, 0, 0, 0, 1, 6 },
            { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
            };
            //Устанавливаем разрешение игрового окна
            //640х512
 
            graphics.PreferredBackBufferWidth = 640;
            graphics.PreferredBackBufferHeight = 512;
            graphics.ApplyChanges();
            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);
            txtBackground = Content.Load<Texture2D>("background");
            txtEnemy = Content.Load<Texture2D>("enemy");
            txtMe = Content.Load<Texture2D>("me");
            txtWall = Content.Load<Texture2D>("wall");
            //Вызываем процедуру расстановки объектов в игровом окне
            AddSprites();
 
            // TODO: use this.Content to load your game content here
        }
        //Процедура расстановки объектов в игровом окне
        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] == 5)
                        Components.Add(new GameObj.Enemy(this, ref txtEnemy, new Vector2(j, i), new Rectangle(0, 0, 32, 32)));
                    //Если обнаружен объект игрока - запишем его координаты
                    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()
        {
        }
        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();
        }
    }
}

На рис. 8.3. вы можете видеть карту этого проекта.


Рис. 8.3. Игровое окно проекта P8_2

В листинге 8.7. вы можете найти код класса gBaseClass

Листинг 8.7. Код класса 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
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
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 P8_2.GameObj
{
    /// 
    /// This is a game component that implements IUpdateable.
    /// 
    public class gBaseClass : Microsoft.Xna.Framework.DrawableGameComponent
    {
        Texture2D sprTexture;
        public Vector2 sprPosition;
        public Rectangle sprRectangle;
        //Прямоугольник, представляющий игровое окно
        public Rectangle scrBounds;
        //Скорость объекта
        public float sprSpeed;
        public gBaseClass(Game game, ref Texture2D _sprTexture,
            Vector2 _sprPosition, Rectangle _sprRectangle)
            : base(game)
        {
            sprTexture = _sprTexture;
            //Именно здесь производится перевод индекса элемента массива
            //в координаты на игровом экране
            sprPosition = _sprPosition * 64;
            sprRectangle = _sprRectangle;
            scrBounds = new Rectangle(0, 0,
             game.Window.ClientBounds.Width,
             game.Window.ClientBounds.Height);
        }
 
        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);
        }
        //Проверка на столкновение с каким-либо объектом
        public 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);
 
 
        }
        //Процедуры для перемещения объекта
        public void MoveUp(float speed)
        {
            this.sprPosition.Y -= speed;
        }
        public void MoveDown(float speed)
        {
            this.sprPosition.Y += speed;
        }
        public void MoveLeft(float speed)
        {
            this.sprPosition.X -= speed;
        }
        public void MoveRight(float speed)
        {
            this.sprPosition.X += speed;
        }
        //Проверка на столкновение со стеной
        public bool IsCollideWithWall()
        {
            foreach (gBaseClass spr in Game.Components)
            {
                if (spr.GetType() == (typeof(Wall)))
                {
                    if (IsCollideWithObject(spr)) return true;
                }
            }
 
            return false;
        }
        //Проверка на столкновение с границами экрана
        public 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;
            }
        }
        public bool CheckBounds()
        {
            return ((sprPosition.X < scrBounds.Left|
                sprPosition.X > scrBounds.Width - sprRectangle.Width|
                sprPosition.Y < scrBounds.Top|
                sprPosition.Y > scrBounds.Height - sprRectangle.Height));
 
        }
    }
}

Листинг 8.8. Код класса Wall
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
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 P8_2.GameObj
{
    public class Wall : gBaseClass
    {
        public Wall(Game game, ref Texture2D _sprTexture,
            Vector2 _sprPosition, Rectangle _sprRectangle)
            : base(game, ref _sprTexture, _sprPosition, _sprRectangle)
        {
            // TODO: Construct any child components here
        }
 
 
        public override void Initialize()
        {
            // TODO: Add your initialization code here
 
            base.Initialize();
        }
 
        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here
 
            base.Update(gameTime);
        }
    }
}

 

Листинг 8.9. Код класса 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
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 P8_2.GameObj
{
    public class Me : gBaseClass
    {
        public Me(Game game, ref Texture2D _sprTexture,
            Vector2 _sprPosition, Rectangle _sprRectangle)
            : base(game, ref _sprTexture, _sprPosition, _sprRectangle)
        {
            sprSpeed = 2;
            // TODO: Construct any child components here
        }
 
        public override void Initialize()
        {
 
            base.Initialize();
        }
 
        public void IsCollideWithAny()
        {
            //Заводим переменную для временного хранения
            //ссылки на объект, с которым столкнулся игровой объект
            gBaseClass FindObj = null;
 
 
            //Проверка на столкновение с объектами Enemy
            foreach (gBaseClass spr in Game.Components)
            {
                if (spr.GetType() == (typeof(Enemy)))
                {
                    if (IsCollideWithObject(spr))
                    {
                        FindObj = spr;
                    }
                }
            }
 
            if (FindObj != null)
            {
 
                this.Dispose();
            }
        }
 
        //Процедура, отвечающая за перемещение игрового объекта
        void Move()
        {
            KeyboardState kbState = Keyboard.GetState();
            //При нажатии кнопки "Вверх"
            if (kbState.IsKeyDown(Keys.Up))
            {
                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));
                }
            }
 
        }
 
        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here
            //Вызовем процедуру перемещения объекта по клавиатурным командам
            Move();
            //Проверим, не вышел ли он за границы экрана, если надо
            //исправим его позицию
            Check();
            IsCollideWithAny();
            base.Update(gameTime);
        }
    }
}

Листинг 8.10. содержит код класса Enemy. Именно здесь реализован алгоритм обхода препятствий.

Листинг 8.10. Код класса Enemy
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
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 P8_2.GameObj
{
 
    public class Enemy : gBaseClass
    {
        Vector2 Direction;
        //Переменная, задающая первоначальное направление движения
        int WhereToMove = 1;
        //Генератор случайных чисел
        Random r = new Random();
        //Переменная для исключения повторного
        //поворота объекта при движении вдоль
        //стены или границы экрана
        bool flag = true;
        //Переменная для исключения повторного поворота объекта
        //при движении в свободном пространстве
        bool flag1 = false;
        //Количество ходов в свободном пространстве перед поворотом значение
        int FreeMoveCount = 150;
 
        public Enemy(Game game, ref Texture2D _sprTexture,
            Vector2 _sprPosition, Rectangle _sprRectangle)
            : base(game, ref _sprTexture, _sprPosition, _sprRectangle)
        {
            sprSpeed = 1;
            Direction = new Vector2(0, 0);
            // TODO: Construct any child components here
        }
 
        public override void Initialize()
        {
            // TODO: Add your initialization code here
 
            base.Initialize();
        }
 
        //Функция для реализации "взгляда" объекта. Если он видит
        //объект преследования - возвращает True, иначе - False
        bool simulateMove(Vector2 testPosition)
        {
            //Находится ли объект игрока так, что объект
            //управляемый компьютером может "видеть" его.
            //Предположим, что да
            bool IsCollide = true;
            bool CollisionDetected = false;
            Vector2 Dir;
            Vector2 OldPosition = this.sprPosition;
            if (testPosition.X == -1 & testPosition.Y == -1)
            {
                foreach (gBaseClass spr in Game.Components)
                {
 
                    if (spr.GetType() == (typeof(Me)))
                    {
 
                        while (CollisionDetected == false)
                        {
                            Dir = (this.sprPosition - new Vector2 (spr.sprPosition.X, spr.sprPosition .Y));
                            if (Dir.X > 0) MoveLeft(Math.Abs(Dir.X / 100));
                            if (Dir.X < 0) MoveRight(Math.Abs(Dir.X / 100));
                            if (Dir.Y > 0) MoveUp(Math.Abs(Dir.Y / 100));
                            if (Dir.Y < 0) MoveDown(Math.Abs(Dir.Y / 100));
                            if (IsCollideWithWall())
                            {
                                CollisionDetected = true;
                                IsCollide = false;
 
                            }
                            if (IsCollideWithObject(spr))
                            {
                                CollisionDetected = true;
                                IsCollide = true;
 
 
                            }
                        }
                    }
                }
 
            }
            else //Проверяем на столкновение объекта и стены
                //эта часть функции реализует "зрение" объекта
            {
                while (CollisionDetected == false)
                {
                    Dir = (this.sprPosition - testPosition);
                    if (Dir.X > 0) MoveLeft(Math.Abs(Dir.X / 100));
                    if (Dir.X < 0) MoveRight(Math.Abs(Dir.X / 100));
                    if (Dir.Y > 0) MoveUp(Math.Abs(Dir.Y / 100));
                    if (Dir.Y < 0) MoveDown(Math.Abs(Dir.Y / 100));
                    if (IsCollideWithWall())
                    {
                        CollisionDetected = true;
                        IsCollide = true;
 
                    }
                    if (CheckBounds())
                    {
                        CollisionDetected = true;
                        IsCollide = true;
                    }
                    if (Math.Abs(Dir.X) < 0.1 & Math.Abs(Dir.Y) < 0.1)
                    {
                        CollisionDetected = true;
                        IsCollide = false;
                    }
                }
 
            }
 
 
            this.sprPosition = OldPosition;
            return IsCollide;
 
        }
 
 
 
 
        void SeekAndDestroy()
        {
            foreach (gBaseClass spr in Game.Components)
            {
                if (spr.GetType() == (typeof(Me)))
                {
                    //найдем разницу между координатами преследователя и игрока
                    Direction = (this.sprPosition - spr.sprPosition);
                    //Если разность по X положительная
                    //переместим преследователя влево
                    //с учетом того, что стены для него непроницаемы.
                    if (Direction.X > 0)
                    {
                        MoveLeft(sprSpeed);
                        while (IsCollideWithWall())
                        {
                            MoveRight((sprSpeed / 10));
                        }
 
                    }
                    //При отрицательной разности по X переместим объект вправо 
                    if (Direction.X < 0)
                    {
                        MoveRight(sprSpeed);
                        while (IsCollideWithWall())
                        {
                            MoveLeft((sprSpeed / 10));
                        }
 
                    }
                    //При положительной разности по Y переместим объект вверх
                    if (Direction.Y > 0)
                    {
                        MoveUp(sprSpeed);
                        while (IsCollideWithWall())
                        {
                            MoveDown((sprSpeed / 10));
                        }
 
                    }
                    //При отрицательной разности по Y переместим объект вниз
                    if (Direction.Y < 0)
                    {
                        MoveDown(sprSpeed);
                        while (IsCollideWithWall())
                        {
                            MoveUp((sprSpeed / 10));
                        }
 
                    }
 
                }
            }
        }
        //Движение вдоль стены или в свободном пространстве
        //Направление движения
        //1 - влево
        //2 - вправо
        //3 - вверх
        //4 - вниз
        void WallWalk(int Direction)
        {
            if (Direction == 1)
            {
 
                MoveLeft(sprSpeed);
 
            }
 
            if (Direction == 2)
            {
 
                MoveRight(sprSpeed);
 
            }
            if (Direction == 3)
            {
                MoveUp(sprSpeed);
 
            }
            if (Direction == 4)
            {
                MoveDown(sprSpeed);
 
            }
 
        }
        /// 
        /// Allows the game component to update itself.
        /// 
        /// Provides a snapshot of timing values.
        public override void Update(GameTime gameTime)
        {
 
            //Если на пути нет препятствий
            //Перемещение к объекту игрока
 
 
            if (simulateMove(new Vector2(-1, -1)))
            {
                SeekAndDestroy();
 
            }
            else 
            {
                //Перемещение вдоль стены или в свободном пространстве
                WallWalk(WhereToMove);
 
                //Разрешить повороты вдоль стены
                flag = true;
 
 
                if (IsCollideWithWall() | CheckBounds())
                {
                    //Разрешить повороты в пространстве
                    //Найдя "разрыв" в стене объект повернет в него
                    flag1 = true;
 
                    //
                    if (WhereToMove == 1 & flag)
                    {
                        WhereToMove = 3;
                        //Если сделан поворот
                        //Нельзя сразу же делать поворот в другую сторону
                        flag = false;
                    }
                    if (WhereToMove == 2 & flag)
                    {
 
                        WhereToMove = 4;
                        flag = false;
                    }
                    if (WhereToMove == 3 & flag)
                    {
                        WhereToMove = 2;
                        flag = false;
                    }
                    if (WhereToMove == 4 & flag)
                    {
                        WhereToMove = 1;
                        flag = false;
                    }
                }
 
                //Блок обработки перемещений в пространстве
                //и в "разрывах" стен
 
                //Если выполнено количество шагов, заданное
                //в переменной FreeMoveCount
                //Установить новое случайное значение для этой переменной
                //Разрешить повороты в свободном пространстве
                if (FreeMoveCount <0)
                {
                    FreeMoveCount = r.Next(20, 100);
                    flag1 = true;
                }
 
                //Если не было поворота вдоль стены
                //И разрешены повороты в пространстве
                //И выше объекта нет препятствия
                //То - сменить направление движения на "вверх"
                //После
                if (flag&flag1 & WhereToMove == 2 & simulateMove(new Vector2(this.sprPosition.X, this.sprPosition.Y - 5)) == false)
                {
                    WhereToMove = 3;
                    flag1 = false;
                    flag = false;
                }
                //Если при движении вверх обнаруживаем, что нет препятствия слева
                //поворачиваем влево
 
                if (flag&flag1 & WhereToMove == 3 & simulateMove(new Vector2(this.sprPosition.X - 5, this.sprPosition.Y)) == false)
                {
                    WhereToMove = 1;
                    flag1 = false;
                    flag = false;
                }
 
                //Если при движении влево обнаруживаем, что нет препятсвия внизу
                //двигаемся вниз
                if (flag&flag1 & WhereToMove == 1 & simulateMove(new Vector2(this.sprPosition.X, this.sprPosition.Y + 5)) == false)
                {
                    WhereToMove = 4;
                    flag1 = false;
                    flag = false;
                }
 
                //Если двигались вниз и обнаружили, что справа нет препятствия
                //Двигаемся вправо
                if (flag&flag1 & WhereToMove == 4 & simulateMove(new Vector2(this.sprPosition.X + 5, this.sprPosition.Y)) == false)
                {
                    WhereToMove = 2;
                    flag1 = false;
                    flag = false;
                }
 
            }
 
            //Проверка на столкновение с границами экрана
            Check();
            //Уменьшаем на 1 количество допустимых ходов в 
            //свободном пространстве
            FreeMoveCount--;
            base.Update(gameTime);
        }
    }
}


Комментарии к коду раскрывают особенности алгоритма.

Выводы

Задача реализации искусственного интеллекта в игровых программах сталкивается с двумя ограничениями. Во-первых – чем сложнее и продуманнее эта система, и чем лучше она, в результате, работает – тем естественнее ведут себя персонажи. Во-вторых – игровые программы требуют немало вычислительных ресурсов, поэтому любое усложнение – в том числе – усложнение системы ИИ – ведет к падению производительности. Поэтому программисты вынуждены идти на компромисс – максимально упрощать систему ИИ таким образом, чтобы ее работа не тормозила выполнение игры. Результаты такого упрощения обычно выражаются в некоторых странностях в поведении персонажей. Например, персонаж может «застрять» в двери или в каком-нибудь другом месте карты, при прохождении которого ИИ, встроенный в игру, не предусматривает однозначного решения. При разработке системы ИИ широко применяется предварительный просчет возможных действий персонажей, после чего полученные данные применяются в ходе игры. Например, предварительно может быть составлена карта обхода местности, которая предусматривает указание путей прохождения и точек, в которых требуются какие-то особые действия персонажа – прыжок для преодоления препятствия, поворот, выполнение определенной манипуляции с другими объектами игрового мира. Обычно игровые персонажи имеют комбинированный ИИ. Например, заранее могут быть просчитаны возможные пути прохождения карты, а если при прохождении персонаж сталкивается с какими-либо подвижными объектами, он ведет себя уже не в соответствии с общей картой прохождения, а в соответствии с правилами обхода локальных препятствий.

Задание

Разработайте игровой проект – клон игры «Battle City» - симулятор танкового боя на карте, подобной карте, применяемой в наших примерах. Создайте следующие объекты карты:

1. Кирпичные стены – объекты не могут преодолевать их, выстрел из пушки уничтожает один сегмент стены.
2. Бетонные стены – непроницаемы для объекта, не уничтожаются выстрелами.
3. Лес – объект движется по лесу замедленно, лес частично скрывает объект, выстрел уничтожает сегмент леса
4. Вода – объект не может преодолеть водную преграду, однако снаряд беспрепятственно преодолевает воду.
5. База игрока – несколько выстрелов врага уничтожают базу
6. Два вида танков врага
a. Танк первого вида перемещается по карте случайным образом, случайным же образом стреляя
b. Танк второго вида перемещается по карте вдоль стен, прицельно стреляя по игроку и, при обнаружении базы, стреляя по ней. При обнаружении игрока танк второго вида пытается преследовать его.
7. Система бонусных объектов
a. Бонус, при подборе которого вражеские танки, находящиеся на карте, уничтожаются
b. Бонус, добавляющий одну «жизнь» игроку
8. Снаряд

Пример реализации этой учебной игры вы можете найти в одном из следующих занятий.

Вопросы

1. Каковы преимущества сложной системы ИИ, предусматривающей адекватную интерактивную реакцию на различные события игрового мира?

a. Игровые объекты ведут себя естественно
b. Производительность игры при такой организации ИИ увеличивается
c. Такую систему несложно разработать

2. Можно ли при разработке ИИ создать систему, которая интерактивно реагирует на игровые события, не используя предварительно рассчитанные данные?

a. Да
b. Нет

3. Можно ли при разработке ИИ создать систему, которая пользуется исключительно данными, рассчитанными заранее?

a. Да
b. Нет

4. Игровой персонаж может содержать реализацию нескольких алгоритмов ИИ, которые применяются в зависимости от текущей игровой ситуации. Какие преимущества имеет такой подход?

a. Сокращается время разработки игры
b. Объект может использовать сильные стороны каждого из алгоритмов, в результате его действия выглядят более естественными
c. Такой подход не имеет преимуществ

5. Между какими игровыми показателями программист вынужден идти на компромисс при разработке системы ИИ?

a. Красота игрового мира и производительность игры
b. Естественность действий персонажей и производительность игры
c. Простота разработки и производительность игры

6. Как называется популярный алгоритм поиска кратчайшего пути?

a. A-
b. A+
c. A*
d. A^

988 Прочтений •  [ИИ в компьютерных играх] [08.08.2012] [Комментариев: 0]
Добавил: Ukraine Vova
Ссылки
HTML: 
[BB Url]: 
Похожие статьи
Название Добавил Добавлено
• ИИ в компьютерных играх 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 | Донейт | Статистика | Команда | Техническая поддержка