Конечный автомат, управляемый окружающей средой
Теперь наступает приятная часть. Мы можем создать КА, в котором состояний перехода представлены рядом переменных. Другими словами, мы управляем конечным автоматом с помощью самой игровой среды, в которой он находится, так же, как наш собственный мозг реагирует на окружающую обстановку.
Начнем создавать КА с того, что он будет случайным образом выбирать одно из нижеперечисленных состояний:
§ преследование;
§ уклонение;
§ случайное;
§ шаблон.
Но в настоящей игре для управления состоянием КА вместо случайных чисел было бы лучше привлечь саму игровую ситуацию. Ниже приведен пример КА действующего по обстоятельствам:
§ Если игрок близко, КА переключается в состояние Шаблон;
§ Если игрок далеко, можно заставить ПК охотиться за ним, используя состояние Преследование;
§ Если игрок обрушил на наше маленькое создание шквальный огонь, КА моментально переходит в состояние Уклонение;
§ Наконец, при любой иной ситуации КА отрабатывает состояние Случайное. Рисунок 13.5 демонстрирует подобное поведение конечного автомата.
![](image/index-image126.jpg)
Перейдем к программной реализации описанного КА. Мы не станем создавать сложное игровое пространство. Для демонстрации нам достаточно иметь чистый экран с парой точек, изображающих игрока и противника. Чтобы понять суть, этого вполне достаточно. Программа из Листинга 13.4 моделирует полет действительно назойливой мухи, которая буквально заедает игрока.
Листинг 13.4. Умная «Муха» (BFLY.C).
// ВКЛЮЧАЕМЫЕ ФАЙЛЫ /////////////////////////////////////
#include <stdio.h>
#include <graph.h>
#include <math.h>
// ОПРЕДЕЛЕНИЯ //////////////////////////////
#define STATE_CHASE 1
#define STATE_RANDOM 2
#define STATE_EVADE 3
#define STATE_PATTERN 4
// ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ ///////////////////////
// Указатель на системную переменную, содержащую значение таймера.
// Содержимое этой 32-битовой ячейки обновляется 18.2 раза
//в секунду
unsigned int far *clock=(unsigned int far *)0х0000046C;
// Х- и Y-компоненты шаблонов траекторий, по которым
// будет двигаться "Муха"
int patterns_x[33[20]={1,1,1,1,1,2,2,-1,-2,-3,-1,
0,0,1, 2, 2, -2,-2,-1,0,
0,0,1,2,3,4,5,4,3,2,1,3,3,3,3,
2,1,-2,-2,-1,
0,-1,-2,-3,-3,-2,-2,
0,0,0,0,0,0,1,0,0,0,1,0,1};
int patterns_y[3][20]={0,0,0,0,-l,-l,-l,-l,-l, 0,0,0,0,0,2,2,2,2,2,2, 1,1,1,1,1,1,2,2,2,2,2, 3,3,3,3,3,0,0,0,0, 1,1,1,2,2,-1,-1,-1,-2,-2, -1, -1, 0,0,0,1,1,1,1,1};
///////////////////////////////////
void Timer(int clicks)
{
//Эта функция использует значение таймера для формирования
// задержки. Необходимое время задержки задается в "тиках"
// интервалах в 1/18.2 сек. Переменная, содержащая 32-битовое
// текущее значение системного таймера, расположена
// по
адресу 0000:046Ch
unsigned int now;
// получить текущее время
now = *clock;
// Ничего не делать до тех пор. пока значение таймера не
// увеличится на требуемое количество "тиков".
// Примечание: один "тик" соответствует примерно 55мс.
while(abs(clock - now) < clicks){}
} // конец функции Timer
// ОСНОВНАЯ ФУНКЦИЯ /////////////////////////////////
void main(void)
{
int px=160,py=100, // начальные координаты игрока
ex=0,ey=0; // начальные координаты противника
int done=0, // флаг окончания работы программы
doing_pattern=0, // флаг выполнения шаблона
current_pattern, // номер текущего шаблона
pattern element, // текущая команда шаблона
select_state=0, // флаг необходимости смены состояния
clicks=20, // количество циклов, в течение которых
// сохраняется текущее состояние
fly_state = STATE CHASE; // "Муха" начинает с преследования
float distance; // используется при расчете
// расстояния между игроком и "Мухой"
_setvideomode(_MRES256COLOR);
printf(" Brainy Fly - Q to Quit");
//основной игровой цикл
while(!done)
{
// очистка точек
_setcolor(0);
_setpixel(px,py);
_setpixel(ex,ey);
// перемещение игрока
if (kbhit()) {
// определяем направление движения
switch(getch()}
{
case 'u': // вверх
{
py-=2;
} break;
case 'n': // вниз
{
py+=2;
} break;
case 'j': // вправо
{
px+=2 ;
} break;
case 'h'': // влево
{
px-=2;
} break;
case 'q':
{ done=l ;
} break;
} // конец оператора switch
}
// конец обработки нажатия клавиши
// теперь перемещаем противника
// начинается работа "мозга"
// определяем текущее состояние КА
switch(fly_state)
{
case STATE_CHASE:
{
_settextposition(24,2);
printf("current state:chase "};.
// реализуем Алгоритм Преследования
if (px>ex) ex++;
if (px<ex) ex--;
if (py>ey) ey++;
if (py<ey) ey--;
// не пора ли перейти к другому состоянию?
if (--clicks==0) select_state=l;
} break;
case STATE_RANDOM:
(
_settextposition(24,2} ;
printf("current state:random ") ;
// перемещаем "Муху" в случайном направлении
ex+=curr_xv;
ey+=curr_yv;
//не пора ли перейти к другому состоянию?
if (--clicks=0) select_state=l;
} break;
case STATE_EVADE:
{
_settextposition(24,2) ;
printf("current state:evade ");
// реализуем Алгоритм Уклонения
if (px>ex) ex--;
if (px<ex) ex++;
if (py>ey) ey—;
if (py<ey) ey++;
//не пора ли перейти к другому состоянию?
if (--clicks==0) select_state=l;
} break;
case STATE_PATTERN:
{
_settextposition(24,2);
printf("current state:pattern ");
// перемещаем "Муху", используя следующий
// элемент текущего шаблона
ex+=patterns_x[current_pattern][pattern_element];
ey+=patterns_y[current_pattern][pattern_element];
// обработка шаблона закончена?
if (++pattern element==20)
{
pattern_element = 0;
select_state=l;
} // конец проверки завершения шаблона
} break;
default;break;
} // конец обработки текущего состояния
// надо ли менять, состояние?
if (select_state==l)
{
// Выбор нового состояния основан на
// игровой ситуации и неявной логике.
// Для выбора используется расстояние до игрока
distance = sqrt(.5+fabs((рх-ех)*(рх-ех)+(ру-еу)*(ру-еу))) ;
if (distance>5&&distance<15&&rand()%2==1)
{
// выбираем новый шаблон
current_pattern = rand()%3;
// переводим "мозг" в состояние действий по шаблону
fly_state = STATE_PATTERN;
pattern_element = 0;
} // конец обработки ситуации "близко к игроку"
else if (distance<10) // слишком близко, убегаем!
clicks=20;
fly_state = STATE_EVADE;
} // конец обработки ситуации "слишком близко"
else
if (distance>25&sdistance<100&&rand()%3==1)
{
// преследуем игрока clicks=15;
fly_state = STATE_CHASE;
} // конец обработки ситуации Преследование
else
if (distance>30&&rand()%2==l)
{
clicks=10;
fly_State = STATE_RANDOM;
curr_xv = -5 + rand()%10; //от -5 до +5
curr_yv = -5 + rand()%10; //от -5 до +5
} // конец "неявной логики"
else
{
clicks=5;
fly_state = STATE_RANDOM;
curr_xv = -5 + rand()%10; //от -5 до +5
curr_yv = -5 + rand()%10; //от -5 до +5
} // конец оператора else // сбрасываем флаг смены состояния
select_state=0;
} // конец Алгоритма Смены Состояния
// Убеждаемся, что "Муха" находится в пределах экрана
if (ex>319) ех=0;
i£ (ex<0) ех=319;
if (ey>199) еу=0;
if (ey<0) еу=199;
// конец работы "мозга"
_setcolor(12);
_setpixel(ex,ey);
// Немного подождем...
Timer(1);
} // Конец цикла while
_setvideomode(_DEFAULTMODE) ;
} // Конец функции main
Запустив программу, вы наверняка обратите внимание на кажущуюся сложность поведения «Мухи».А ведь для получения такого результата использована весьма простая схема! Возможно, и вы заметите то, что увидел я - чуть ли не человеческую способность мыслить.