Всем читателям привет! Давно не обновлял журнал, давно не программировал... Дело в том, что очень много разных дел, которые занимают все свободное время. Лето - сезон, когда не упеваешь ничего, а запланировал слишком много. Но сегодня дождь за окошком, поэтому я решил немного отвлечься и написать маленькую демонстрационную программу, несущую в себе простую идею - анимацию сделать нетрудно, а игроку от нее будет радостно! Итак, я решил сделать небольшую программу, на тему "графика и анимация". Ведь движения объектов, отклик на действия игрока - это основополагающая вещь любой интерактивной программы, в том числе и игры! Поэтому давайте не забывать, что доступные элементы должны подсвечиваться, хинты для них появляться, а кнопки нажиматься! Сегодня на растерзание возьмем три элемента:
|
Простая анимация полосы загрузки
Выглядит это чудо так:
Давайте разберемся, как сделать анимацию полоски, означающую процесс загрузки. Сам фокус здесь в том, что полосы на фоне и рамка спереди - два разных объекта:
При этом полоски смещаются с помощью изменения текстурных координат.
TextureOffset.x := TextureOffset.x + progressTime.deltaTime / 6;
Для удобства я обернул функционал в класс TLoaderBar, описание которого выглядит совсем не хитро:
TLoaderBar = class (TGlHudSprite) protected fLoaderFront: TGlHudSprite; Procedure MainInit; public Procedure CoordinateChanged(Sender: TGLCustomCoordinates); override; Function IsHit(aPosition: TVector): Boolean; Procedure SetMaterials(aMatLib: TGLMaterialLibrary; aLinesName: String; aFrontName: String); Procedure DoProgress(const progressTime: TProgressTimes); override; Constructor Create(AOwner: TComponent); override; end;
Из основной программы достаточно выполнить всего несколько действий:
fLoaderBar := TLoaderBar.CreateAsChild(fMainDummy); fLoaderBar.SetMaterials(fMatLib, 'loaderlines' , 'loaderfront'); fLoaderBar.Position.SetPoint(280, 360, 0);
Вот и все, теперь можно порадовать пользователей анимированной полосой...
Анимация крутяшки ожидания
Иногда места для элемента, показывающего процесс загрузки совсем мало, поэтому давайте создадим запасную анимацию в виде круга. Что-то вроде такого:
Выглядит не супер, но свою функцию выполняет. Описание класса оказалось вот такое:
TWaitAnimation = class (TGlHudSprite) protected fAnimationTime: Single; public Procedure DoRender(var ARci: TRenderContextInfo; ARenderSelf, ARenderChildren: Boolean); override; Procedure DoProgress(const progressTime: TProgressTimes); override; Constructor Create(AOwner: TComponent); override; end;
Обращаю внимание на то, что я перекрыл DoRender() для того, чтобы внутри него отрисовывать отдельные палочки одним объектом. Получается довольно удобно. Вот сам код, наполненный магическими числами:
Procedure TWaitAnimation.DoRender(var ARci: TRenderContextInfo; ARenderSelf, ARenderChildren: Boolean); var alpha: single; i: integer; pos: TVector; begin pos := Position.AsVector; for i := 0 to 11 do begin alpha := i * pi * 2 / 12; Position.x := pos[0] + 15 * cos(alpha); Position.y := pos[1] + 15 * sin(alpha); Rotation := 90 - alpha * 180 / pi; with Material.GetActualPrimaryMaterial.FrontProperties.Diffuse do Alpha := cos((fAnimationTime - i)) + 1; inherited DoRender(ARci, true, false); end; Position.SetPoint(pos); end;
Хоть элемент оказался довольно простым, но он все равно вносит живинку на экран... Да и начинать наполнять игру интерактивными анимированными объектами стоит с самых простых - хинты, кнопки, панельки...
Анимированный хинт на элементы интерфейса
Для простоты я "повесил" подсказку на полосу загрузки из первого пункта. Наводим курсор на полоску и появляется хинт:
Опять же для удобства я создал новый класс TSimpleImageHint:
TSimpleImageHint = class (TGlHudSprite) protected fShowSpeed, fHideSpeed: Single; fAnimationTime: Single; fIsShow: Boolean; fShowPos: TVector; fHidePos: TVector; public property ShowSpeed: Single read fShowSpeed write fShowSpeed; property HideSpeed: Single read fHideSpeed write fHideSpeed; Procedure Show; Procedure Hide; Procedure SetPositions(aShow, aHide: TVector); Procedure DoProgress(const progressTime: TProgressTimes); override; Constructor Create(AOwner: TComponent); override; end;
Описание вроде прозрачное; скажу только, что с помощью метода SetPositions() задаем позиции спрятанного и показанного состояний хинта. Свойства ShowSpeed и HideSpeed отвечают за скорость анимации. Из основной программы загружаем изображение и задаем базовые свойства объекта:
fLoadingHint := TSimpleImageHint.CreateAsChild(fMainDummy); with fLoadingHint do begin Material.LibMaterialName := 'loading'; Material.MaterialLibrary := fMatLib; Width := Material.GetActualPrimaryTexture.Image.Width; Height := Material.GetActualPrimaryTexture.Image.Height; SetPositions(VectorMake(280, 330, 0), VectorMake(280, 360, 0)); end;
Осталось только запускать показ и прятание хинта в соответствующие моменты:
if fLoaderBar.IsHit(VectorMake(MPos.x, MPos.y, 0)) then fLoadingHint.Show else fLoadingHint.Hide;
Безумно простое использование, не находите?!
Кстати, точно такой же элемент я использовал в конкурсной работе, подсвечивая назначение тех или иных кнопок.
Итого
Для красоты я добавил еще несколько иконок животных и хинты к ним... Наводим мышкой на каждою зверюшку и наблюдаем за анимацией подсказки. В итоге все это выглядит так:
Код зверюшек:
for i := 1 to 4 do begin ZooHint[i] := TSimpleImageHint.CreateAsChild(fMainDummy); with ZooHint[i] do begin Material.LibMaterialName := ZooNames[i] + '_hint'; Material.MaterialLibrary := fMatLib; Width := Material.GetActualPrimaryTexture.Image.Width; Height := Material.GetActualPrimaryTexture.Image.Height; SetPositions(VectorMake(80 + (i - 1) * 150, 180, 0), VectorMake(80 + (i - 1) * 150, 120, 0)); end; Zoo[i] := TGlHudSprite.CreateAsChild(fMainDummy); with Zoo[i] do begin Material.LibMaterialName := ZooNames[i]; Material.MaterialLibrary := fMatLib; Width := Material.GetActualPrimaryTexture.Image.Width; Height := Material.GetActualPrimaryTexture.Image.Height; Position.SetPoint(80 + (i - 1) * 150, 100, 0); end; end;
А также проверка на анимацию, находящаяся в OnCadencerProgress():
for i := 1 to 4 do with Zoo[i] do if (MPos.x >= Position.X - Width / 2) and (MPos.y >= Position.Y - Height / 2) and (MPos.x <= Position.X + Width / 2) and (MPos.y <= Position.Y + Height / 2) then ZooHint[i].Show else ZooHint[i].Hide;
С радостью выложил бы видео, но у меня сейчас настолько медленный интернет, что я не только не могу его выложить, а зачастую не получается даже поиграть в новые хитовые флешки. Зато работается более продуктивно :)
Скачать демку с исходниками можно здесь, либо обновляемся через svn, данная демка добавилась в 10ой ревизии...
Уф... очень рад, что смог найти время и доделать демку для журнала; так что я не пропал, я просто как бы отлучился ненадолго...
здорово :) глаз радует!
ОтветитьУдалитьКласс) спасибо за пример.
ОтветитьУдалитьEdward,
ОтветитьУдалитьблагодарю за приятные слова!
Ulop,
очень рад! я старался! надеюсь пригодится))
Lampogolovii, спасибо тебе большое за такие примеры и реализации.
Удалить1. Качественно.
2. Со вкусом(коего мало сейчас у кодеров наших)
+ я доолго искал примеры реализации контролов именно на GLScene, и только перейдя на твой блог по ссылке в подписе, понял где обитает один из лучших, не побоюсь этого слова :)
Спасибо тебе.
Mlex,
Удалитьспасибо за добрые слова!
рад, если эти нехитрые, но милые гуи-элементы пригодятся!
буду стараться и впредь, правда времени не хватает на полноценные демки((
А ты на каком делфи писал?
ОтветитьУдалитьраньше я прогал только на Delphi 7... старая, добрая делфи))..
Удалить