">
Информатика Программирование
Информация о работе

Тема: Создание стратегической игры на DirectX

Описание: Планирование игрового процесса. Фаза идей, определения требований, технической документации, разработки. Контроль исходного кода. Управление метками. Отслеживание ошибок. Тестирование отдельных частей. Распространение условно-бесплатных программ.
Предмет: Информатика.
Дисциплина: Программирование.
Тип: Курсовая работа
Дата: 30.08.2012 г.
Язык: Русский
Скачиваний: 0
Поднять уникальность

Похожие работы:


кафедра Математики и информационных технологий

 Курсовая работа
 Создание стратегической игры на DirectX

2011

Содержание

I. Введение

II.Планирование игрового процесса.

1. Фаза идей.

2.фаза определения требований.

3.фаза технической документации.

4.фаза разработки.

5.фаза тестирования.

6.фаза распространения.

III.Блочная графика.

1. Хранение блоков в двухмерном массиве

2.Свойства блоков

IV. Воспроизведение WAV файлов.

1.Класс для чтения файлов формата WAV

2.Воспроизведение WAV файла.

V.Отрисовка спрайтов.

1.Процедура нарезки фрэймов и создания спрайтов

2.Процедура создания анимации персонажей

3.Вывод растра c клипированием и с учетом прозрачного цвета

VI.Волновой алгоритм поиска пути.

VII.Список источников.

Введение.

Создание игры в реальном времени сложная и кропотливая работа. Многие программисты сразу мчатся к написанию кода, не размышляя об имеющемся в руках проекте. Такой подход приводит к пропуску ключевых моментов, хаотическому развитию, уходу членов команды и даже закрытию проекта. Даже если группа состоит из одного или двух человек, не надо поддаваться искушению быстрого и беспорядочного развития проекта. Нужно выделить время, чтобы должным образом спланировать проект который включает в себя: фазу идей, фазу определения требований, фазу технической документации (только в больших проектах), фазу разработки, фазу тестирования и фазу выпуска программы.

Планирование Игрового процесса

Фаза идей

В фазе идей мы придумываем идею программы. Так как мы имеем дело со стратегическими играми, стоит задать несколько вопросов, таких как:

К какому типу относится стратегическая игра — реального времени или пошаговая?

Игра основана на реальных или на вымышленных событиях?

В какой период времени разворачивается действие игры — средневековье, Гражданская война, Вторая Мировая война или будущее?

Игра будет однопользовательской, многопользовательской или поддерживать оба режима?

Графика будет двухмерной, трехмерной или и той и другой?

Как долго длится средний раунд игры до завершения?

Какова цель игры?

Игра в основном стратегическая или тактическая?

Игра простая или сложная?

Как выиграть в игре?

Как проиграть в игре?

Как долго продолжается игра до победы?

Вопросы, перечисленные выше ни в коем случае не охватывают все, что мы должны понять о стратегической игре.

Создание наброска

На основе идей мы можем создать набросок игры:

Исходные данные

Фэнтезийный мир.

Сражения

Уровень бригад

Реализм

Отсутствует вид со спутника (отсутствие мини-карты)

Возможные маршруты

Игроки

Один игрок

Фаза определения требований

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

Используя в качестве примера набросок с многопользовательской игрой, вот как бы я перечислил требования для раздела многопользовательской игры:

Требования для многопользовательской игры.

Поддержка от 2 до 16 игроков.

Игра может быть сохранена и заново загружена.

Необязательный выделенный сервер.

Администратор игры может отключать и блокировать игроков.

Здесь хорошо следовать правилу гласящему, что больше информации — лучше.

Фаза технической документации

Оно основывается исключительно на документе с формулировкой требований и ни на чем более. Чтобы получить визуальное представление посмотрите на рисунок.

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

Требования к многопользовательской игре

Необязательный выделенный сервер

Данная возможность требует наличия отдельной программы, которая содержит только сетевой код для игры. Программа должна требовать как можно меньше ресурсов, чтобы игрок мог запустить на одном компьютере и выделенный сервер и саму игру.

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

Состояние игры отображается в окне интерфейса командной строки сервера. Должны выводиться такие сведения как количество игроков, текущее время игры, среднее время игры и сообщения об ошибках.

Фаза разработки

В течение фазы разработки мы разрабатываем игру. Ключевые моменты этой фазы:

Контроль исходного кода.

Управление метками.

Отслеживание ошибок.

Тестирование отдельных частей.

Контроль исходного кода

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

Управление метками

Благодаря управлению метками вы можете получать преимущества от ясно помеченных ревизий исходного кода. Достигнув какой либо вехи в разработке, вы сохраняете весь ваш код и назначаете ему метку. Это позволяет вернуться назад и проверить все различные версии кода, требуемые для построения программы в какой-либо контрольной точке.

Отслеживание ошибок

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

Тестирование отдельных частей

Тестирование частей — это испытание разработчиком его собственного изделия. Перед тем, как отправить игру команде тестировщиков, разработчики, как предполагается, проверяют ее самостоятельно. В моём случае команда тестировщиков отсутствовала.

Фаза тестирования

В фазе тестирования команда тестировщиков подвергает вашу программу различным испытаниям. Обычно у команды тестировщиков есть документ, называемый план возвратного тестирования (regression plan), содержащий список испытаний, которым следует подвергнуть вашу программу. Если все пункты плана возвратного тестирования выполняются без ошибок, ваш код значительно продвигается к стадии бета-версии.

Системные тестировщики также выполняют для вашего кода то, что называется стрессовым тестированием (negative testing). Предположим, у вас есть поле ввода в котором игрок указывает количество отрядов, которые будут отправлены в боевую группу. Системный тестировщик может ввести буквы вместо цифр, чтобы увидеть что случится. Также тестировщик может ввести отрицательное или слишком большое положительное число, чтобы посмотреть не приведет ли это к аварийному завершению вашего кода.



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

Распространение

Условно-бесплатные программы

Это простейший метод, который можно выбрать для распространения игры. Существует много открытых сайтов, на которых любой желающий может разместить свою игру для бесплатного скачивания. Все, что вам необходимо сделать — это разместить на сайте установочный пакет для скачивания и указать адрес для людей, которые пожелают сделать пожертвование. Одно из возможных мест для размещения ваших файлов называется FilePlanet. Оно располагается по адресу www.fileplanet.com.

Сайты аукционов

Упаковать игру и руководство к ней в коробку и выставить на продажу на eBay или другом Интернет - аукционе. Единственная проблема, связанная с этим методом, заключается в том, что у публики будет очень ограниченное представление о вашей игре. Чтобы противодействовать этой проблеме, надо разместить на общедоступных сайтах демонстрационную версию игры. В демонстрационную версию поместить ссылку на свой сайт, чтобы заинтересовавшиеся люди могли получить информацию о том, как купить полную версию игры.

Издатели

Если игра достаточно хороша для издателя, можно попробовать предложить ее некоторым сетевым издательствам. Одна из таких компаний — Garage Games, которая может издать игру за определенный процент с продаж. Дополнительную информацию можно получить на сайте www.garagegames.com.

Хранение блоков в двухмерном массиве

Простейший метод хранения блоков — использование двухмерного массива. Первое измерение образует горизонтальные строки, а второе — вертикальные столбцы. Взгляните на следующий код:

Установка размеров карты

int map_size_x=128;

int map_size_y=128;

Положение карты

int map_x=0;

int map_y=0;

Объявление массива с картой

map_cell map[128][128];

map_obj_cell map_obj[128][128];

Заполнение ,блока карты на который наведён курсор блоком.

//если не попадает на карту - выйти

if(cur_x<256)return;

//определить положение курсора на карте

int x=(cur_x-256)/32+map_x,y=cur_y/32+map_y;

//заполнение клетки покрытием

if(lbutton&&layer==dwater)

{

put_(x,y-1);//0

put_(x+1,y-1);//1

put_(x+1,y);//2

put_(x+1,y+1);//3

put_(x,y+1);//4

put_(x-1,y+1);//5

put_(x-1,y);//6

put_(x-1,y-1);//7

int lay=map[x][y-1].mark;

if(lay==sand)put_water(x,y-1);//0

lay=map[x+1][y-1].mark;

if(lay==sand)put_water(x+1,y-1);//1

lay=map[x+1][y].mark;

if(lay==sand)put_water(x+1,y);//2

lay=map[x+1][y+1].mark;

if(lay==sand)put_water(x+1,y+1);//3

lay=map[x][y+1].mark;

if(lay==sand)put_water(x,y+1);//4

lay=map[x-1][y+1].mark;

if(lay==sand)put_water(x-1,y+1);//5

lay=map[x-1][y].mark;

if(lay==sand)put_water(x-1,y);//6

lay=map[x-1][y-1].mark;

if(lay==sand)put_water(x-1,y-1);//7

check_put(x,y,dwater);

}

Заполнение происходит после определения, рядом стоящего блока. Если он является таким же как используем мы, то заполнение происходит по принципу:1) С какой стороны блок который мы ставим относительно найденного. 2) И по этим данным мы выбираем текстуру склеивания в grass.bmp

Свойства блоков

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

Проходимость

Возвышенность

Яркость

Смещение

Проходимость

Свойство проходимости используется для того, чтобы определить, возможно ли продвижение через данный блок. Если блок помечен как непроходимый, подразделения не могут пересекать его. Главным образом это свойство используется в алгоритмах поиска пути, поскольку процедуры поиска пути должны знать, какие части карты являются проходимыми, а какие — нет. Взгляните на пример, изображенный на рис. 



На рис. видно, что для блоков с изображением воды значение проходимости равно 1, а для блоков с изображением земли значение этого же свойства равно 0. Для алгоритма поиска пути это означает, что по воде нельзя перемещаться. Если юнит, расположенный в правой части карты, требуется переместить в левую часть, надо маневрировать таким образом, чтобы переехать преграду по земляному перешейку. Программа узнает как это сделать, основываясь на карте преград.

Возвышенность

Возвышенность — полезное свойство для тех стратегических игр, на картах которых есть настоящие возвышения. Например, в игре Total Annihilation есть возвышенности, влияющие на стратегию. Пулемет, расположенный на вершине холма может стрелять поверх стен вражеской базы. Хороший способ хранить карту возвышенностей — задавать значение высоты каждого блока. Программа может читать значение высоты блока, чтобы определить линию прицеливания.

Яркость

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

Вы видите, что блоки в центре рис. яркие, а по мере приближения к краям они становятся все темнее и темнее. Это вызвано тем, что в центре рисунка располагается подразделение игрока, которое может видеть территорию вокруг себя. Присваивая блокам вокруг подразделения различные значения яркости, графическая библиотека реализует эффект области ухудшающейся видимости. Значение яркости, раное 1.0, означает, что блок полностью освещен, а значение, равное 0.0, означает что блок скрыт во тьме. Темные блоки указывают те области, которые данное подразделение не может видеть.

Смещение

Предположим, мы создали дерево высотой 64 пикселя и шириной 32 пикселя. Проблема с таким делевом заключается в том, что когда мы передаем этот блок процедуре визуализации, на изображении будет казаться, что дерево погрузилось в землю, поскольку оно является слишком высоким.

 Чтобы решить эту проблему вы должны при визуализации деревьев учитывать смещение. Чтобы вычислить смещение, возьмём высоту обычных блоков и вычтите из нее высоту блока с изображением дерева. В приведенном примере высота дерева равна 64 пикселям, а высота обычного блока — 32 пикселям. Таким образом, смещение будет равно 32 – 64 = –32 пикселя. Теперь, при отображении карты, все деревья будут выводиться в позиции Y + –32. Это перемещает изображение деревьев при визуализации на 32 точки вверх. Если желаете, можете выполнять подобные изменения, добавляя смещения по осям X и Y. В основном данный метод используется для выравнивания блоков, которые не вписываются в стандартный размер блоков.

Отображение двухмерных изометрических блоков

вывод карты

void map_view()

{

char str[10];

int xx,yy;

int m_x=256,m_y=0;//положение карты на экране

//вывод тайлов

for(int y=0;y<24;y++)

for(int x=0;x<24;x++)

{

xx=x+map_x,yy=y+map_y;//для избежания повторных вычислений нарисовать тайл

DdrawBltFast(m_x+x*32,m_y+y*32//куда

,map[xx][yy].x,map[xx][yy].y//откуда

,32,32,surf_layer[map[xx][yy].layer]);//какая поверхность

}

for(int y=0;y<24;y++)

for(int x=0;x<24;x++)

{

xx=x+map_x,yy=y+map_y; //для избежания повторных вычислений

//--------------------------------

int r=map_obj[xx][yy].type;

int n=map_obj[xx][yy].number;

if(r==c_tree||r==c_rock)

{

int rox=res[n].ox;

int roy=res[n].oy;

int rx=res[n].x;

int ry=res[n].y;

int rw=res[n].w;

int rh=res[n].h;

int rastr=res[n].rastr;

blit_image_trans(m_x+x*32+rox,m_y+y*32+roy//куда

,rx,ry,rw,rh//откуда

,m_x,m_y,SizeWindowX,SizeWindowY//клипирование

,rastr);//какая поверхность

}

if(r==c_unit)

{

//определить какой ролик

int a=unit[n].spr;//какой спрайт

int r=unit[n].rastr;//какой растр

int act=unit[n].anim;//анимация

int dir=unit[n].dir;//направление

int k=unit[n].kadr;//какой кадр

int px=unit[n].px;//положение на карте в пикселах

int py=unit[n].py;

//данные кадра

int ox=an[a].spr[act][dir][k].ox;

int oy=an[a].spr[act][dir][k].oy;

int fx=an[a].spr[act][dir][k].x;

int fy=an[a].spr[act][dir][k].y;

int fw=an[a].spr[act][dir][k].w;

int fh=an[a].spr[act][dir][k].h;

if(r!=-1&&r,m_x,m_y,SizeWindowX,SizeWindowY,r);

}}}

Воспроизведение WAV файлов.



На рис. видно, что WinMain() вызывает функцию bInitializeSoundSystem(). Эта функция инициализации выполняет несколько вызовов функций DirectX для инициализации звуковой системы. Затем программа ожидает события мыши и воспроизводит файл WAV с помощью функции vPlaySound().

Класс для чтения файлов формата WAV

CMappedWave::CMappedWave()

{

//Обнуляем все члены класса

pbWaveFile=NULL;

pWaveHeader=NULL;

dwCurPosition=0;

dwDataLength=0;

}

CMappedWave::~CMappedWave()

{

//Закрываем файл, если он был открыт

Close();

}

BOOL CMappedWave::Open(CHAR* szFilename)

{

//Закрываем файл, если он был открыт

Close();

HANDLEhFile,hMappedFile;

DWORDdwFileLength;

//Пытаемся открыть файл

hFile=CreateFile(szFilename,GENERIC_READ,FILE_SHARE_READ,NULL,

OPEN_EXISTING,NULL,NULL);

if (hFile==INVALID_HANDLE_VALUE)

return (FALSE);

//Определяем размер файла

dwFileLength=GetFileSize(hFile,NULL);

//Создаём объект - проекция файла

hMappedFile=CreateFileMapping(hFile,NULL,PAGE_READONLY,

0,dwFileLength,NULL);

//Закрываем описатель файла (но не сам файл!!!)

CloseHandle(hFile);

if (!hMappedFile)

return (FALSE);

//Проецируем файл на адресное пространство

pbWaveFile=(PBYTE)MapViewOfFile(hMappedFile,FILE_MAP_READ,0,0,0);

//Закрываем описатель проекции файла

CloseHandle(hMappedFile);

if (!pbWaveFile)

return (FALSE);

//Инициализируем оставшиеся члены класса

pWaveHeader=(LPWAVEFORMATEX)(pbWaveFile+WAVEFORMATEX_SHIFT);

dwCurPosition=WAV_HEADER_SIZE;

dwDataLength=*((DWORD*)(pbWaveFile+WAV_HEADER_SIZE-sizeof(DWORD)));

return (TRUE);

}

void CMappedWave::Reset()

{

//Сбрасываем текущую позицию в значение по умолчанию

if (pbWaveFile)

dwCurPosition=WAV_HEADER_SIZE;

}

void CMappedWave::Close()

{

if (pbWaveFile)

{

//Отключаем проекцию файла от адресного пространства

UnmapViewOfFile(pbWaveFile);

pbWaveFile=NULL;

}

//Обнуляем члены класса

pWaveHeader=NULL;

dwDataLength=0;

dwCurPosition=0;

}

DWORD CMappedWave::Read(BYTE* pbData,DWORD dwSizeToRead)

{

DWORD dwRemainder;

//Если файл не был открыт, либо все данные прочитаны

//возвращаем 0 - больше делать нечего

if ((!pbWaveFile)||((dwDataLength+WAV_HEADER_SIZE)==dwCurPosition))

return (0);

if (dwSizeToRead<=(dwDataLength+WAV_HEADER_SIZE-dwCurPosition))

{

//Если достаточно данных для чтения

CopyMemory(pbData,pbWaveFile+dwCurPosition,dwSizeToRead);

dwCurPosition+=dwSizeToRead;

dwRemainder=dwSizeToRead;

}

else //Если данных недостаточно

{

//Копируем сколько есть

dwRemainder=dwDataLength-dwCurPosition+WAV_HEADER_SIZE;

CopyMemory(pbData,pbWaveFile+dwCurPosition,dwRemainder);

//Заполняем остаток молчанием

FillMemory(pbData+dwRemainder,dwSizeToRead - dwRemainder,

(pWaveHeader->wBitsPerSample==8) ? 128 : 0);

dwCurPosition+=dwRemainder;

}

return (dwRemainder);

}

DWORD CMappedWave::GetWaveSize()

{

//Возвращаем размер только звуковых данных - без заголовка

if (pbWaveFile)

return (dwDataLength);

else

return (0);

}

Воспроизведение WAV файла.

Создаёт буфер звукового эффекта на основе файла WAV

bool LoadEffect(int* pointer,char* filename)

{

int n=*pointer;

if(n==-1){n=effect_num;}//индикатор говорит,что звук не существует

//----------------------------------

HRESULT hRet;

DSBUFFERDESC dsbd;

// Cоздаём объект для работы с файлом WAV

pWave = new CMappedWave();

if (!pWave)

{

ErrorHandle(hWnd, "pWave");

return (false);

}

//Открываем нужный файл при помощи объекта класса CMappedWave

if (!pWave->Open( filename ))

{

ErrorHandle(hWnd, "pWave-Open music");

return (false);

}

//----------------------------------

//если звуковой буфер существует - уничтожить его

if(effect[n][0]){effect[n][0]->Release();effect[n][0]=NULL;}

if(effect[n][1]){effect[n][1]->Release();effect[n][1]=NULL;}

if(effect[n][2]){effect[n][2]->Release();effect[n][2]=NULL;}

//----------------------------------

//Заполняем структуру описания...

ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );

dsbd.dwSize = sizeof(DSBUFFERDESC);

dsbd.dwFlags = DSBCAPS_STATIC | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME |

DSBCAPS_CTRLFREQUENCY ;

dsbd.dwBufferBytes = pWave->GetWaveSize();

dsbd.lpwfxFormat = pWave->GetWaveHeader();

//... и с её помощью создаём музыкальный буфер!

hRet=pDS->CreateSoundBuffer( &dsbd, &effect[n][0], NULL );

if (hRet!=DS_OK)

{

ErrorHandle(hWnd, "CreateMusicBuffer");

return (false);

}

VOID* pPtr1 = NULL;

VOID* pPtr2 = NULL;

DWORD dwSize1;

DWORD dwSize2;

//Запоминаем размер звуковых данных

DWORD dwWaveSize=pWave->GetWaveSize();

pWave->Reset();

//Пытаемся заблокировать звуковой буфер

hRet = effect[n][0]->Lock( 0, dwWaveSize, &pPtr1, &dwSize1,

&pPtr2, &dwSize2, 0L);

if (hRet!=DS_OK)

{

ErrorHandle(hWnd, "effect[n][0]->Lock");

return (false);

}

//Если получилось, считываем в буфер звуковые данные

pWave->Read((PBYTE)pPtr1,dwWaveSize);

//Не забываем разблокировать буфер

effect[n][0]->Unlock( pPtr1, dwWaveSize, NULL, 0 );

//Дублировать звук

pDS->DuplicateSoundBuffer(effect[n][0],&effect[n][1]);//оказалось никчему

pDS->DuplicateSoundBuffer(effect[n][0],&effect[n][2]);//слишком большое колличество звуков

//если все прошло успешно - передаем номер созданного звукового буфера указателю

*pointer=n;

effect_num++;//увеличить счетчик звуковых эффектов

return (true);

}

//Проигрывает звуковой эффект

void playeffect(int pan,int volume,int buf,int n)

{

// проверить существует ли буфер

if(effect[buf][n]==NULL)return ;

HRESULT hRet;

//Перемещаем в начало текущую позицию проигрывания

effect[buf][n]->SetCurrentPosition(0);

//Установка дальности звука

effect[buf][n]->SetVolume(volume);//DSBVOLUME_MAX==0;DSBVOLUME_MIN==-10000.

//Установка Левого или Правого положения звука

effect[buf][n]->SetPan(pan);// -10000 и 10000

//Начинаем воспроизводить звук

hRet=effect[buf][n]->Play(0, 0, 0);//DSBPLAY_LOOPING );

}

//проигрывание звукового эффекта в тройном экземпляре

void PlayEffect(int pan,int volume,int buf)

{

if(!EffectStatus(buf,0)){playeffect(pan,volume,buf,0);return;}

//else if(!EffectStatus(buf,1)){playeffect(pan,volume,buf,1);return;}//оказались никчему

//else if(!EffectStatus(buf,2)){playeffect(pan,volume,buf,2);return;}

}

//остановка воспроизведения эффекта

void StopEffect(int buf,int n)

{

// проверить существует ли буфер

if(effect[buf][n]==NULL)return ;

effect[buf][n]->Stop();

}

//статус воспроизведения эффекта

bool EffectStatus(int buf,int n)

{

// проверить существует ли буфер

if(effect[buf][n]==NULL)return false;

ULONG status;

effect[buf][n]->GetStatus(&status);

if(status & DSBSTATUS_PLAYING)return true;

return false;

}

Отрисовка спрайтов.

Процедура нарезки фрэймов и создания спрайтов

void sprite_editor()

{

if(SizeWindowX!=1024){ErrorHandle(hWnd,"задании размера экрана");gameover=true;return;}

//------------------------------

//массив для создания и вывода строки

char str[256];

//------------------------------

//переменная выбора

int choice=0;

//------------------------------

//выделение памяти под массив структур фрэймов

memset(frm,0,sizeof(frm));

//------------------------------

//забить массив роликов нулями

memset(roll,0,sizeof(roll));

//------------------------------

//забить массив анимации отрицательными значениями

memset(anim,-1,sizeof(anim));

//------------------------------

//цикл идет пока gameover равен false

while(!gameover)

{

//инициализация задержки

DWORD start_time=GetTickCount();

//---------------------------------------

//поставщик сообщений

ProcessMessages();

//---------------------------------------

//панель

box(0,0,256,768,gray3);

box(256,0,SizeWindowX-256,768,gray);

rectangle(0,0,256,768,gray4,black);

rectangle(1,1,254,766,white,gray);

//---------------------------------------

//область выбора

int x=14,y=25,w=110,h=15;

//кнопка выбора

if(button(x,y,w,h,"open image")){open_rastr();}

//размеры растра

y+=25;

wsprintf(str,"Images Width %d Hight %d",rastr_w,rastr_h);

printSb(str,x,y);

y+=20;w=70;

//кнопка выбора

if(button(x,y,w,h,"frame")){choice=0;}

if(choice==0)rectangle(x-2,y-2,w+4,h+4,blue);

//кнопка выбора

x+=78;

if(button(x,y,w,h,"roll")){choice=1;}

if(choice==1)rectangle(x-2,y-2,w+4,h+4,blue);

x+=78;

if(button(x,y,w,h,"sprite")){choice=2;}

if(choice==2)rectangle(x-2,y-2,w+4,h+4,blue);

//---------------------------------------

//разделительная линия

x=15,y+=25;

box(x,y,225,1,gray);

box(x,y+1,225,1,white);

//---------------------------------------

//вызов соответствующей функции

if(choice==0)frame();//если frame

//--------

if(choice==1)Roll();//если roll

//--------

if(choice==2)Sprite();//если sprite

//---------------------------------------

//***************************************

//синхронизация частоты кадров

while((GetTickCount()-start_time)<10);

//---------------------------------------

//вывод скорости смены кадров и битовый режим

int nFps=UpdateFPS();

wsprintf(str,"%d Fps- %d",BIT_MODE,nFps);

printSb(str,50,5);

//вывод положения курсора

wsprintf(str,"cur_x %d cur_y %d",cur_x,cur_y);

printSb(str,110,5);

//---------------------------------------

Flip();//вывести вторичный буфер на экран

//---------------------------------------

//***************************************

//---------------------------------------

//запрос на выход

//если нажата клавиша ESCAPE

if(KEYDOWN(VK_ESCAPE))gameover=true;

key=0;//обнулить переменную клавиш

rbutton=false;//обнулить состояние правой клавиши мыши

}

Процедура создания анимации персонажей

состоящей из действий - стоит, идет, атакует, умирает.

int sprt[64];

int sprk[64];

void open_sprite();//объявление функции

void Sprite()

{

int ox,oy,fx,fy,fw,fh;

//кнопка выбора

int x=16,y=105,w=70,h=15;

if(button(x,y,w,h,"new sprites")){new_sprite();}//очистка массива спрайтов

x+=77;

if(button(x,y,w,h,"open sprites")){open_sprite();}//открытие файла спрайтов

x+=77;

if(button(x,y,w,h,"save sprites")){save_sprite();}//сохранение спрайтов в файл

//---------------------------

x=16;

y+=55;

//изменение общей временной задержки

spin(x+35,y,&time);

if(key==7)time--;

if(key==9)time++;

if(time<0)time=0;if(time>100)time=100;

//рамка вокруг области

rectangle(x-9,y-8,242,32,gray,white);

rectangle(x-8,y-7,240,30,white,gray);

//----------

//временная задержка воспроизведение для всего ролика

wsprintf(str,"%d",time);

text_box(x,y,30,15,str);

printSb("общая задержка ( 7 и 9 )",x+65,y+3);

//-----------------------------------------------

y+=35;

//изменение текущего направления

spin(x+35,y,&dir);

//изменение текущего направления

if(key==a)dir--;

if(key==s)dir++;

if(dir<0)dir=0;if(dir>7)dir=7;

//рамка вокруг области

rectangle(x-9,y-8,242,32,gray,white);

rectangle(x-8,y-7,240,30,white,gray);

//----------

//текущее направление

wsprintf(str,"%d",dir+1);

text_box(x,y,30,15,str);

printSb("текущее направление ( a и s )",x+65,y+3);

//-----------------------------------------------

y+=35;

//изменение текущего действия

spin(x+35,y,&act);

//изменение текущего действия

if(key==z)act--;

if(key==x)act++;

if(act<0)act=0;if(act>7)act=7;

//рамка вокруг области

rectangle(x-9,y-8,242,32,gray,white);

rectangle(x-8,y-7,240,30,white,gray);

//----------

//текущее действие

wsprintf(str,"%d",act+1);

text_box(x,y,30,15,str);

printSb("текущее действие ( z и x )",x+65,y+3);

//-----------------------------------------------

//удаление ролика

x=8,y+=35,w=240,h=20;

if(button(x,y,w,h,"Удалить ролик (Delete)")||KEYDOWN(VK_DELETE)){anim[act][dir]=-1;}

//-----------------------------------------------

int r=0;

for(y=0;y<8;y++)

for(x=0;x<8;x++)

{

if(sprt[r]++>time+frm[roll[r].frm[sprk[r]]].time)//задержка

{

sprk[r]++;if(roll[r].frm[sprk[r]]==0||sprk[r]>15)sprk[r]=0;//сменить кадр

sprt[r]=0;

}

//активирование действия

if(CursorLD(256+96*x,80*y,94,78))spr=x;

//показать в палитре все существующие кадры

ox=frm[roll[r].frm[sprk[r]]].ox;

oy=frm[roll[r].frm[sprk[r]]].oy;

fx=frm[roll[r].frm[sprk[r]]].x;

fy=frm[roll[r].frm[sprk[r]]].y;

fw=frm[roll[r].frm[sprk[r]]].w;

fh=frm[roll[r].frm[sprk[r]]].h;

if(rastr!=-1)blit_image_trans(256+1+96*x+ox+48,80*y+oy+55,fx,fy,fw,fh,256+1+96*x,80*y,96,80,rastr);

rectangle(256+96*x,80*y,96,80,black,white);

if(CursorRD(256+96*x,80*y,96,80))anim[act][dir]=r;//создать спрайт

r++;

}

//палитра спрайтов

box(0,640-5,255,1,gray);

box(0,640-4,1024,1,black);

box(0,640-3,1024,1,gray3);

box(0,640-2,1024,1,white);

box(0,640-1,1024,127,gray);

for(int i=0;i<8;i++)

{

rectangle(128*i,640,128,128,black,white);

//активирование действия

if(CursorLD(128*i,640,124,124))act=i;

//--------------------------------

//показать в палитре все существующие спрайты

int a=anim[i][dir];//номер спрайта

ox=frm[roll[a].frm[sprk[a]]].ox;

oy=frm[roll[a].frm[sprk[a]]].oy;

fx=frm[roll[a].frm[sprk[a]]].x;

fy=frm[roll[a].frm[sprk[a]]].y;

fw=frm[roll[a].frm[sprk[a]]].w;

fh=frm[roll[a].frm[sprk[a]]].h;

if(rastr!=-1&&a!=-1)blit_image_trans(128*i+1+ox+64,641+oy+80,fx,fy,fw,fh,128*i+1,641,124,124,rastr);

}

//рамка выделения активного спрайта

rectangle(128*act+1,641,126,126,green);

rectangle(128*act+2,642,124,124,green);

}

Вывод растра c клипированием и с учетом прозрачного цвета

void blit_image_trans(int x,int y,int dx,int dy,int width,int height,int c_x,int c_y,int c_w,int c_h,

UINT pSurface)//поверхность источник

{

//клипирование(урезание невидимых частей) выводимого прямоугольника

if(xc_w)width=c_w;}

if(yc_h)height=c_h;}

if(x+width>c_x+c_w)width=c_w-(x-c_x);

if(y+height>c_y+c_h)height=c_h-(y-c_y);

//---------------

RECT rPic;

//Установка размеров копируемого блока данных

SetRect(&rPic, dx, dy, dx+width, dy+height);

//функция DirectDraw для вывода ратра

buffer/*куда копировать*/->BltFast(x, y,//с учётом прозрачного цвета

image[pSurface]/*откуда копировать*/,&rPic,DDBLTFAST_SRCCOLORKEY |DDBLTFAST_WAIT);

}

Волновой алгоритм поиска пути.

функция вычисления пути

int findpath(int radius, int* sx,int* sy, int ex,int ey,WPOINT wpoint[lenpath] )

{

int pStartx=*sx;

int pStarty=*sy;

if(pStartx<1||pStartx>width||pStarty<1||pStarty>height)return -1;

if(ex<1||ex>width||ey<1||ey>height)return -1;

//копирование карты блокировок в карту поиска пути

memcpy(temp, wmap, sizeof wmap);

Start(pStartx,pStarty,radius);//установка возможных точек прибытия

temp[ex][ey]=end;//установка точки финиша

//начать список с конечной позиции(поиск идет от конца к старту)

list_i[0].x=ex;

list_i[0].y=ey;

//"обнулить" счетчик_источник

count_i=1;

bool ok;

//запустить генерацию волн(поиск пути)

for(wave=1;wave{

count_p=0;//обнулить счетчик_приемник

ok=Wave();

if(ok)break;

}//end for

if(!ok)return -1;//если путь не найден

//установка новых координат старта

pStartx=new_st.x;

pStarty=new_st.y;

int cur;

//Вернуться от старта к концу по направлениям из карты направлений

//Путаница в названиях - конец это где стоит объект(для программы имеют значения только числа)

// - старт это куда надо прийти

for(int i=0;i{

//-----------------

wpoint[i].x=pStartx;//передача координать объекту

wpoint[i].y=pStarty;

//-----------------

cur=curs[pStartx][pStarty];//взять направление из карты_направлений

pStartx=pStartx+dir[cur].x;//изменение координат

pStarty=pStarty+dir[cur].y;

}

*sx=new_st.x;//передать новые координаты финиша

*sy=new_st.y;

return wave;

}

Вычисление движения объекта

int step(int* x,int* y,int ex,int ey,int a)

{//a - длина шага

int z,b,tx,ty;

int sx=*x;

int sy=*y;

//направления 7 0 1

6 * 2

5 4 3

if((sx{

tx=sx+a;

if(tx>ex)tx=ex;

*x=tx;

*y=sy;

return r;}

Движение для других направлений определяется так же.

Список источников

http://www.iskint.ru/?xid=games-poisk_puti

http://www.koders.com/c/

http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=4064

http://www.boswars.org/download.shtml

http://netlib.narod.ru/library/book0051/