|
Быть может кто-то из Вас знает игру 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" - симулятор танкового боя на карте, подобной карте, применяемой в наших примерах. Создайте следующие объекты карты:
- Кирпичные стены – объекты не могут преодолевать их, выстрел из пушки уничтожает один сегмент стены.
- Бетонные стены – непроницаемы для объекта, не уничтожаются выстрелами.
- Лес – объект движется по лесу замедленно, лес частично скрывает объект, выстрел уничтожает сегмент леса
- Вода – объект не может преодолеть водную преграду, однако снаряд беспрепятственно преодолевает воду
- База игрока – несколько выстрелов врага уничтожают базу
- Два вида танков врага
- Танк первого вида перемещается по карте случайным образом, случайным же образом стреляя
- Танк второго вида перемещается по карте вдоль стен, прицельно стреляя по игроку и, при обнаружении базы, стреляя по ней. При обнаружении игрока танк второго вида пытается преследовать его.
- Система бонусных объектов
- Бонус, при подборе которого вражеские танки, находящиеся на карте, уничтожаются
- Бонус, добавляющий одну "жизнь" игроку
- Снаряд
Пример реализации этой учебной игры вы можете найти в одном из следующих занятий.