В различных играх часто приходится видеть выбор персонажа, автомобиля или другого объекта с помощью кнопок пролистывания < >
В рамках серии сообщений о gui-элементах я решил показать, что подобную смену картинок сделать невероятно просто. За основу решил взять предыдущую статью, в которой уже присутствуют сами кнопки пролистывания. Поэтому сегодня мы продолжим дорабатывать нашу демонстрацию по использованию анимированных кнопок. По сути, у нас уже имеется заготовка, куда мы просто добавим немного интерактивности и декораций. Заодно чуть подробнее остановимся на некоторых классах нашей иерархии gui-объектов. |
Итак, сегодня будем на практике применять созданные нами ранее кнопки.
Как видно, в прошлый раз мы оставили в центре экрана небольшое свободное пространство, в котором как раз и будем отображать персонажей на выбор:
Результат нашего сегодняшего труда можно посмотреть на видео:
Приготовились? Приступим к коду...
Для демонстрации и смены картинок, достаточно вот такой структуры, в которой поля fX и fAlpha нужны для красивого твининга спрайтов:
THero = class fSprite: TGlHudSprite; fX: Single; fAlpha: Single; end;
Применять значения из полей fX и fAlpha будем в методе UpdateHeroes(), где, пробежавшись по всем персонажам, выставим им соответствующую позицию и прозрачность:
Procedure TfrmMain.UpdateHeroes; var i: integer; begin for i := 0 to fHeroes.Count - 1 do with THero(fHeroes[i]) do begin fSprite.Position.SetPoint(fX, fSprite.Position.y, 0); fSprite.AlphaChannel := fAlpha; end; end;
Далее реализуем смену картинок, для этого заведем метод ActivateHero(), в котором будем запускать твины уходящего персонажа, а также твины на появление следующего. Для пущей красоты научим данный метод смещать картинки как в одну сторону, так и в другую (справа налево и слева направо), а регулировать направление будем аргуемнтом aDirection:
Procedure TfrmMain.ActivateHero(aIndex: integer; aDirection: integer); begin if (fActiveHero >= 0) then with THero(fHeroes[fActiveHero]) do begin fTweener.DeletePSingle(@fX); fTweener.DeletePSingle(@fAlpha); fTweener.AddTweenPSingle(@fX, ts_ExpoEaseIn, fX, 300 + aDirection * 100, 2); fTweener.AddTweenPSingle(@fAlpha, ts_ExpoEaseIn, fAlpha, 0, 0.7); end; if(aIndex >= 0) then with THero(fHeroes[aIndex]) do begin fTweener.DeletePSingle(@fX); fTweener.DeletePSingle(@fAlpha); fTweener.AddTweenPSingle(@fX, ts_ExpoEaseIn, 300 - aDirection * 100, 300, 2, 0.5); fTweener.AddTweenPSingle(@fAlpha, ts_ExpoEaseIn, 0, 1, 0.7, 0.5); end; fActiveHero := aIndex; end;
Надеюсь здесь все прозрачно, по два твина (позиция и альфа) на предыдущую и новую картинку. Также удаляем все предыдущие твины с помощью метода DeletePSingle(), для того, чтобы случайным образом не наплодить артефактов в виде промаргивания картинок при частых нажатия кнопок < >
Собственно, спрайты есть, анимация их смены тоже, осталось научиться фиксировать нажатие кнопок и запускать ActivateHero() с нужными параметрами, а метод UpdateHeroes() уже позиционирует изображения в зависимости от прогресса твинов.
Напомню, что в прошлый раз мы обошли стороной вопрос нажатия кнопок, скромно умолчав о том, что на самом деле почти все готово для того, чтобы отлавливать нажатия мыши на кнопке. Для этого перейдем в класс TGUIBaseInteractiveObject и допишем несколько коварных строк:
TGUIBaseInteractiveObject = class(TGUIBaseAnimatedObject) protected fHitArea: THitArea; fMouseState: TMouseState; fMousePressed: Boolean; fOnMouseClick: TOnMouseClick; protected Procedure ApplyMouseState(aState: TMouseState); public property OnMouseClick: TOnMouseClick read fOnMouseClick write fOnMouseClick; Function IsHit(aPosition: TVector): Boolean; Procedure SetMouseState(aMouseX, aMouseY: Single; aIsButtonDown: Boolean); Constructor Create(AOwner: TComponent); override; end;
Теперь, имея свойство OnMouseClick, вызовем его при необходимости из SetMouseState() следующим образом:
if aIsButtonDown and not fMousePressed and Assigned(fOnMouseClick) then fOnMouseClick(self);
Вот и все изменения в нашем классе интерактивных объектов. Осталось воспользовать волшебством и прописать обработчик события внутри основной программы:
............... fRightBtn.OnMouseClick := OnNextClick; fLeftBtn.OnMouseClick := OnPrevClick; ..................... Procedure TfrmMain.OnNextClick(aSender: TGUIBaseInteractiveObject); begin if(fActiveHero = fHeroes.Count - 1)then ActivateHero(0, -1) else ActivateHero(fActiveHero + 1, -1); end; Procedure TfrmMain.OnPrevClick(aSender: TGUIBaseInteractiveObject); begin if(fActiveHero = 0)then ActivateHero(fHeroes.Count - 1, 1) else ActivateHero(fActiveHero - 1, 1); end;
На каждую из кнопок свой обработчик, так проще определять, на какую кнопку нажали и запускать ActivateHero с соответствующими параметрами. Таким образом мы сможем легко отлавливать клик по кнопке и проигрывать анимацию смены спрайтов!
Как обычно, скачать демонстрацию (exe+исходники) можно здесь, либо необходимо обновиться через svn (24ая ревизия).
На этом все, первый вид кнопок, можно считать, мы сделали! В дальнейшем, конечно, нам наверняка потребуются улучшения и дополнения текущей архитектуры в целом и избранных классов в отдельности, но это все должно происходить безболезненно и очень мягко...
Если есть какие вопросы или неясности по работе демонстрации - смело пишите, мне важно знать, какую сторону освещать ярче, делая ее прозрачной для понимания...
Я наверное повторюсь, но скажу: лучше блога по гуи на делфи я не видел :)
ОтветитьУдалитьЧто касается замечаний - их нет, все прозрачно и доходчиво. Прям для меня :). Всегда с удовольствием читаю(жаль довольно не часто пишешь именно по этой теме). Но именно тут я вообще понял как вообще писать гуи, а то идей вообще небыло а тут уже готовые реализации и примеры, спасибо.
спасибо за теплые слова, приятно!
Удалитьочень рад, что код читаем))
да, писать получается нечасто, прошло более полугода с этого сообщения:
http://lampogolovii.blogspot.com/2011/09/gui.html
а я до сох пор третий пункт не раскрыл до конца, но надеюсь исправиться в ближайшее время!
надеюсь примеры и код пригодятся, пользуйся на здоровье!
Пригодятся конечно! Сейчас по тихоньку осваиваю сцену, в голове крутиться небольной проект казино, игровые автоматы и прочее(больше в учебных целях) и ваши идеи гуи очень даже подходят :), так что огромное спасибо :)
УдалитьЗЫ: в 3d пока не лезу - 2d не до конца освоил :)))
От себя хочу добавить, а вернее поблагодарить Вас, за Ваши статьи, в частности за статьи по GUI и Box2D!! СПАСИБО!!)
ОтветитьУдалитьАнонимный,
Удалитьух ты! очень приятно читать! спасибо за добрые слова!
удачи в разработке!