Я решил заняться наиболее интересным и тем, что в моём первом варианте было реализовано слабовато - это бомба.
Далее идёт описание принятых решений по задумкам из первого отчёта и относительно подробный экскурс по проектированию и реализации бомбы.
Начну с того, что я солидарен с Altmer'ом, что "бомбермен это не тетрис и не змейка, эта игра имеет все признаки 'большой' игры" и поэтому я не поленился разработать мини отчёт характеризующий разработку данной игры:
- Реализация. Для данной программы я выбрал итеративный подход реализации. Хотя основные требования выработал вначале и опубилковал их в первом отчёте.
- Жизненный цикл разработки данного проекта получается более как эволюционный нежели прототипированный. Хотя элементы прототипирования присутствуют. Например набросок из первого отчёта можно смело считать подобием прототипа. Хотя при подавляющем эволюционировании я многое уже изменил в структуре и принципах взаимодействия классов.
- Планирование проекта получается по большей части формальное заблаговременное, но так же имеются элементы инкрементации. Т.к. я могу последовательно размышлять над добавлением или уменьшением тех или иных деталей конкретных элементов.
- Проектирование я комбинирую с написанием кода.
Теперь нужно сказать о моих окончательных решениях в реализации некоторых деталей, вопросами о которых я задавался в первом отчёте.
- Я изменил взгляды на цифровое представление поля, но это я поясню в следующей теме о проектировании поля боя, тем более что я это ещё не реализовал окончательно.
- Определился с динамикой бомбы, что и буду описывать чуть ниже.
- Модель главного героя будет 3-х мерной и в её реализации мне поможет Almost, который в отличии меня умеет моделить). Насчёт крипов пока неизвестно.
И так, приступим к проектированию и написанию объекта-бомбы.
Начинаю с того, что открываю заготовку приложения для разработки какой-либо 3D штучек(в этот каркас приложения у меня входит создание базы 3D пространства, камер и обработка основных контролов).
Создаю основной класс бомбы - наследника от TGLDummyCube и начинаю размышлять...
Необходимо, чтобы отображался спрайт бомбы, который будет пульсировать пока она не взорвётся по таймеру или по приказу. Таким образом создаём объекты и методы в нашем классе для этих пунктов "правильной" бомбы)
После прототипирования данных элементов класс бомбы начинает выглядить так:
Класс бомбы
unit uBomb;interface
uses
...стандартные модули..., ...модули сцены..., ...свои модули...;
type
TBomb = class(TGLDummyCube)
private
fBombSprite : TGLPlane;
fRadius : smallint;
fPulsFactor : single;
fTimer : single;
fExploded : boolean; // взорвалась ли
procedure MainInit;
public
procedure DoProgress(const progressTime: TProgressTimes); override;
public
constructor CreateAsChild(aParentOwner: TGLBaseSceneObject; aRadius: smallint);
destructor Destroy; override;
property Radius: smallint read fRadius;
property Exploded: boolean read fExploded;
end;
Т.е. спрайт бомбы будет отображаться на TGLPlain'е, который будет крутиться фронтом всегда к камере(биллборд), как спрайты у систем частиц.
Теперь можно написать следующие обработчики: это создание основных элементов и биллбординг бомбы.
И вот что получилается:
Раздел реализации модуля бомбы
implementationconst
c_MIN_SIZE = 0.7;
c_MAX_SIZE = 1;
c_PULS_FACTOR = 1;
{ TBomb }
constructor TBomb.CreateAsChild(aParentOwner: TGLBaseSceneObject; aRadius: smallint);
begin
inherited CreateAsChild(aParentOwner);
fRadius := aRadius;
MainInit;
end;
destructor TBomb.Destroy;
begin
FreeAndNil(fBombSprite);
inherited Destroy;
end;
procedure TBomb.MainInit;
begin
fBombSprite := TGLPlane.CreateAsChild(Self);
with fBombSprite do
begin
Width := c_MIN_SIZE;
Height := c_MIN_SIZE;
Material.MaterialLibrary := BSF.MatLib;
Material.LibMaterialName := 'bomba';
end;
fPulsFactor := c_PULS_FACTOR;
fTimer := 3;
fExploded := False;
end;
procedure TBomb.DoExplosion;
begin
fBombSprite.Visible := False;
fExploded := True;
end;
procedure TBomb.DoProgress(const progressTime: TProgressTimes);
var
DTime : single;
cam_ap : TVector;
begin
DTime := progressTime.deltaTime;
if not(fExploded) then
begin
// пульсация
fBombSprite.Width := fBombSprite.Width + DTime * fPulsFactor;
if fBombSprite.Width > c_MAX_SIZE then fBombSprite.Width := c_MAX_SIZE;
if fBombSprite.Width < c_MIN_SIZE then fBombSprite.Width := c_MIN_SIZE;
fBombSprite.Height := fBombSprite.Width;
if fBombSprite.Width >= c_MAX_SIZE then fPulsFactor := -c_PULS_FACTOR;
if fBombSprite.Width <= c_MIN_SIZE then fPulsFactor := c_PULS_FACTOR;
// делаем билборд
fBombSprite.Up.SetVector(VectorMake(0, 1, 0));
cam_ap := Self.Scene.CurrentGLCamera.AbsolutePosition;
fBombSprite.Direction.SetVector(VectorSubtract(cam_ap, fBombSprite.AbsolutePosition));
// timer
fTimer := fTimer - DTime;
if fTimer <= 0 then
DoExplosion;
end;
// обработка взрыва
end;
end.
Запускаем, проверяем - нормалёк)
Сразу хотел бы заметить, что моя версия билборда бомбы не очень корректно смотрится при наличии нескольких бомб на поле - когда камера передвигается бомбы начинают крутиться чтобы всегда смотреть на неё)
Если кто подскажет мне как правильнее сделать билборд, то я буду ток рад)
Теперь можно подумать и о взрыве.
В плане взрыва я решил немножко отойти от привычного типа взрыва классического бомбера и сделал, на мой взгляд, чутка поинтереснее.
Я сделал осколочный взрыв. Т.е. добавил в класс бомбы 4 экземпляра класса осколка, создал некий контроллер системы частиц для эффекта огненного шлейфа, прицепил к осколкам и запустил это дело посмотреть, получилось весьма красиво)
В результате класс бомбы стал выглядеть так:
unit uBomb;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls,
GLScene, GLWin32Viewer, GLCrossPlatform, BaseClasses, GLPolyhedron,
GLMaterial, GLCoordinates, GLObjects, GLCadencer, GLContext,
GLRenderContextInfo, GLBitmapFont, GLWindowsFont, GLHUDObjects,
VectorGeometry, VectorTypes, GLKeyboard,
uBombFragment, uBaseGLSceneFrame, uSimpleUtils;
type
TFragName = (fnNorth, fnSouth, fnEast, fnWest);
TBomb = class(TGLDummyCube)
private
fBombSprite : TGLPlane;
fNorthFragment : TBombFragment;
fSouthFragment : TBombFragment;
fEastFragment : TBombFragment;
fWestFragment : TBombFragment;
fRadius : smallint;
fPulsFactor : single;
fTimer : single;
fExploded : boolean; // взорвалась ли
fDestroyed : boolean; // уничтожена ли полностью(осколки тоже)
procedure MainInit;
public
function IsFragActual(aFN: TFragName): boolean;
function GetFragPos(aFN: TFragName): TPointFloat;
procedure FragDoExplosion(aFN: TFragName);
procedure DoExplosion;
procedure DoProgress(const progressTime: TProgressTimes); override;
public
constructor CreateAsChild(aParentOwner: TGLBaseSceneObject; aRadius: smallint);
destructor Destroy; override;
property Radius: smallint read fRadius;
property Exploded: boolean read fExploded;
property Destroyed: boolean read fDestroyed;
end;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls,
GLScene, GLWin32Viewer, GLCrossPlatform, BaseClasses, GLPolyhedron,
GLMaterial, GLCoordinates, GLObjects, GLCadencer, GLContext,
GLRenderContextInfo, GLBitmapFont, GLWindowsFont, GLHUDObjects,
VectorGeometry, VectorTypes, GLKeyboard,
uBombFragment, uBaseGLSceneFrame, uSimpleUtils;
type
TFragName = (fnNorth, fnSouth, fnEast, fnWest);
TBomb = class(TGLDummyCube)
private
fBombSprite : TGLPlane;
fNorthFragment : TBombFragment;
fSouthFragment : TBombFragment;
fEastFragment : TBombFragment;
fWestFragment : TBombFragment;
fRadius : smallint;
fPulsFactor : single;
fTimer : single;
fExploded : boolean; // взорвалась ли
fDestroyed : boolean; // уничтожена ли полностью(осколки тоже)
procedure MainInit;
public
function IsFragActual(aFN: TFragName): boolean;
function GetFragPos(aFN: TFragName): TPointFloat;
procedure FragDoExplosion(aFN: TFragName);
procedure DoExplosion;
procedure DoProgress(const progressTime: TProgressTimes); override;
public
constructor CreateAsChild(aParentOwner: TGLBaseSceneObject; aRadius: smallint);
destructor Destroy; override;
property Radius: smallint read fRadius;
property Exploded: boolean read fExploded;
property Destroyed: boolean read fDestroyed;
end;
И скрин этого дела:
Но о подробностях я пожалуй умолчу, чтобы всётаки сохранять некоторую интригу)
Ну вот. Вроде больше про бомбу говорить нечего) Ведь требований для её реализации оказалось совсем не много.
Но если я чего забыл, то вопросы всегда можно задать в комментах.

круто!
ОтветитьУдалитьвозможно я пропустил, но вот это как работает BSF.MatLib? видимо, BSF - глобальный объект? с чем его едят?))
биллборды - всегда смотрят на камеру, как ее не крути... в этом их функция... поэтому конечно взрывы будут поворачиваться...
Lampogolovii,
ОтветитьУдалитьBSF - это синглтон с главными элементами сцены.
У меня проблемы-то не с взрывами, а с бомбами(когда ещё не взорвались), которые как хомячки начинают крутиться на камеру)
А с огоньками-то у взрывов всё нормуль как раз) там-то функция билбордов выполняется как и в любой системе частиц ровно.
Насчет биллбордов:
ОтветитьУдалитьКогда делал SpaceSim меня не устроил худ-спрайт (и спрайт) в качестве биллборда, в итоге делал велосипед через TGLPlane. Если интересует - могу копнуть код и выложить основные мысли.
Выделение кода - адЪ, старый добрый turbo pascal :)
Первые строки - про проектирование, ЖЦ, прототипирование, итерационный подход и прочее - вышли для меня наиболее актуальными - у меня завтра экзамен по проектированию информационных систем, и все эти термины до боли знакомы.
Пойду готовиться и впервые читать 256 страниц лекций...
Да я гляжу сейчас не совсем понятна моя проблемка с билбордингом бомб... тут просто надо бы наглядность показать для ясности..
ОтветитьУдалитьВ следующей теме постараюсь заснять видео и всё будет видно-понятно.
А не скажешь, зачем эта строка в DoProgress ?
ОтветитьУдалитьDTime := progressTime.deltaTime;
Для чего сохраняешь в локальной переменной процедуры - ведь это лишняя итерация, ведь можно же сразу обращаться к progressTime.deltaTime ? Или я не прав ?
LOLO,
ОтветитьУдалитьизначально у меня всё было завязано на эту переменную, поэтому я присваивание сделал чтобы просто не переписывать переменную в коде... Да и по объёму куда меньше)
Monax,
ОтветитьУдалитьхотелось бы отдельного сообщения по BSF, есть несколько вопросов по его использованию... удобно? просто я в итоге так и не решился на создание синглтона для таких целей... через всю иерархию всегда пробрасываю библиотеку материалов и т.д... не шибко удобно, поэтому интересно услышать про впечатления о синглтоне в этом деле!
про бомбы и взрывы ясно, но непонятно, что должно быть в итоге... если бомбы спрайты, тогда лучше вращать за камерой... но увидеть динамику и проблему на видео - это всегда нагляднее, да!
perfect daemon,
+1, разметка кода действительно напрягает глаза, если честно... очень трудно читать...
Lampogolovii,
ОтветитьУдалитья тоже раньше как-то сообщал менеджерам и т.п. главные общии объекты, но это ваще нагромождает, когда например тройное и более вложение менеджеров... А если ещё и камеру нужно использовать "где-то там" в глубине иерархии?
Сделал синглтон - стало намного удобнее. Ещё тем более локализовал многие базовые операнты, связанные, например, с инициализацией, в самом синглтоне.
Насчёт цветовой разметки - всегда писал в старом добром классическом стиле, он мне весьма нравится.