Быть может кто-то из Вас знает игру Sims, к какому жанру она относиться? Жизненная симуляция, ролевая игра, там можно и дома строить..... |
Методы искусственного интеллекта (ИИ) в компьютерных играх
Листинг 12.10. содержит код класса Enemy. Именно здесь реализован алгоритм обхода препятствий.
using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.GamerServices; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Storage; using Microsoft.Xna.Framework.Content; namespace 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); } } /// <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) { //Если на пути нет препятствий //Перемещение к объекту игрока 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); } } }Листинг 12.10. Код класса Enemy
Комментарии к коду раскрывают особенности алгоритма.
Выводы
Задача реализации искусственного интеллекта в игровых программах сталкивается с двумя ограничениями. Во-первых – чем сложнее и продуманнее эта система, и чем лучше она, в результате, работает – тем естественнее ведут себя персонажи. Во-вторых – игровые программы требуют немало вычислительных ресурсов, поэтому любое усложнение – в том числе – усложнение системы ИИ – ведет к падению производительности. Поэтому программисты вынуждены идти на компромисс – максимально упрощать систему ИИ таким образом, чтобы ее работа не тормозила выполнение игры. Результаты такого упрощения обычно выражаются в некоторых странностях в поведении персонажей. Например, персонаж может "застрять" в двери или в каком-нибудь другом месте карты, при прохождении которого ИИ, встроенный в игру, не предусматривает однозначного решения. При разработке системы ИИ широко применяется предварительный просчет возможных действий персонажей, после чего полученные данные применяются в ходе игры. Например, предварительно может быть составлена карта обхода местности, которая предусматривает указание путей прохождения и точек, в которых требуются какие-то особые действия персонажа – прыжок для преодоления препятствия, поворот, выполнение определенной манипуляции с другими объектами игрового мира. Обычно игровые персонажи имеют комбинированный ИИ. Например, заранее могут быть просчитаны возможные пути прохождения карты, а если при прохождении персонаж сталкивается с какими-либо подвижными объектами, он ведет себя уже не в соответствии с общей картой прохождения, а в соответствии с правилами обхода локальных препятствий.
Задание
Разработайте игровой проект – клон игры "Battle City" - симулятор танкового боя на карте, подобной карте, применяемой в наших примерах. Создайте следующие объекты карты:
- Кирпичные стены – объекты не могут преодолевать их, выстрел из пушки уничтожает один сегмент стены.
- Бетонные стены – непроницаемы для объекта, не уничтожаются выстрелами.
- Лес – объект движется по лесу замедленно, лес частично скрывает объект, выстрел уничтожает сегмент леса
- Вода – объект не может преодолеть водную преграду, однако снаряд беспрепятственно преодолевает воду
- База игрока – несколько выстрелов врага уничтожают базу
- Два вида танков врага
- Танк первого вида перемещается по карте случайным образом, случайным же образом стреляя
- Танк второго вида перемещается по карте вдоль стен, прицельно стреляя по игроку и, при обнаружении базы, стреляя по ней. При обнаружении игрока танк второго вида пытается преследовать его.
- Система бонусных объектов
- Бонус, при подборе которого вражеские танки, находящиеся на карте, уничтожаются
- Бонус, добавляющий одну "жизнь" игроку
- Снаряд
Пример реализации этой учебной игры вы можете найти в одном из следующих занятий.