Вот и наступило время для реализации долгожданных кнопок. Как я говорил в одном из прошлых сообщений, мы будем делать не просто статичные спрайтовые картинки, а кнопки, подсвечивающие свое состояние. Навели курсор, кнопка подсветилась, увели курсор, кнопка вернулась в прежнее состояние.
Конечно, реализовать данный механизм можно полсотней строк, но на данный момент gui-демонстрации дошли до такого уровня, когда хочется сделать все по науке: адекватная иерархия объектов, подумать о дальнейших планах, заготовить фундамент для более сложных кнопок. |
Так как обилие кода обычно отпугивает читателей, поэтому сегодня у нас будет только реализация базовой анимации кнопок. Дальше мы возьмем за основу сегодняший результат и, отталкиваясь от него, будем наращивать функционал кнопок и собирать красивые демонстрации.
Что мы имеем?
Две картинки двух состояний кнопки:
А также в нашем распоряжении здравый смысл, Delphi, GlScene и желание добиться результата...
Что хотим?
Необходимо анимировать кнопку при наведении курсора мыши.
Если говорить о конечном результате, то код рендера кнопки выглядит до безумия простым:
Procedure TGUIAlphaButton.DoRender(var ARci: TRenderContextInfo; ARenderSelf, ARenderChildren: Boolean); begin // устанавливаем позицию кнопки fHudSprite.Position.x := Position.x; fHudSprite.Position.y := Position.y; // рисуем неактивное состояние кнопки fHudSprite.Material := fShowMaterial; fHudSprite.Width := fShowMaterial.Texture.Image.Width; fHudSprite.Height := fShowMaterial.Texture.Image.Height; fHudSprite.AlphaChannel := 1; fHudSprite.DoRender(ARci, true, false); // поверх рисуем активное состояние кнопки fHudSprite.Material := fActiveMaterial; fHudSprite.Width := fActiveMaterial.Texture.Image.Width; fHudSprite.Height := fActiveMaterial.Texture.Image.Height; fHudSprite.AlphaChannel := (fAnimationTime - cst_animTimeShow) / (cst_animTimeActive - cst_animTimeShow); fHudSprite.DoRender(ARci, true, false); end;
Сначала устанавливаем позицию кнопки, а затем рисуем спрайт два раза с разными настройками: для неактивного и активного состояний объекта. При этом активный спрайт рисуем с прозрачностью, вычисляя значение исходя из времени анимации, но об этом чуть ниже. В итоге такую простую анимацию можно посмотреть на видео:
Чего же более?
Но есть один нюанс... помните, мы говорили о том, что хочется иметь фундамент для большого разнообразия всяких анимаций кнопки: масштабирование, поворот, движение и т.д... чтобы не дублировать получившийся код из одного места в другое, давайте реализуем вот такую цепочку наследования:
TGUIBaseObject
Чтобы иметь меньше проблем с рендером и обновлением наших gui-объектов, я решил отнаследовать этот базовый класс от TGLCustomSceneObject. Возможно, это временное решение, и в конечном счете мы уйдем от этого ненужного хвоста, но сейчас, в нашем старте, это сильно облегчит жизнь...
Итак, TGUIBaseObject - это самый простой класс, на данный момент имеет только "команду" объекта, то есть числовой идентификатор, по которому можно будет легко находить нужный gui-объект в каком-нибудь списке.
TGUIBaseAnimatedObject
Класс, в котором появляется анимация интерфейса. Имеет три конечных состояния: Hide, Show, Active. То есть скрытый (невидимый), показанный и активный (при наведении мыши). Вся идея в том, что объект данного класса переключается между этими тремя состояниями не моментально, а плавно, используя для этого счет времени в переменной fAnimationTime.
TGUIBaseInteractiveObject
Интерактивный gui-объект. Здесь появляется понятие hit-области (необходима для правильного реагирования на события мыши). В дальнейшем именно в этом классе мы реализуем события на нажатие, наведение и уход мыши.
TGUIBaseButton
Чует мое сердце, что этот промежуточный класс пригодится! Но пока он пуст, пропуская через себя наследование напрямую.
TGUICustomHudButton
Класс, в котором мы жестко привязываемся к GlScene (конечно, базовый класс тоже завязан на этот графический движок, но мы опять же держим в голове, что до этого момента привязки были минимальны). Именно здесь мы добавляем HudSprite, который будет отображаться на экране.
TGUIAlphaButton
Вот мы и добрались до первого наследника от TGUICustomHudButton, в котором мы как раз реализуем вышеприведенный код с вычислением альфы нашего спрайта.
Вот такая цепочка наследования у нас получилась... зачем такие сложности? Например, захочется нам сделать обычный спрайт, который будет висеть на экране, тогда мы легко отнаследуемся от TGUIBaseObject и будем хранить его в том же списке, что и остальные gui-объекты. Захотим сделать спрайт с нестандартной хит-областью, тогда перекинем наследование на TGUIBaseInteractiveObject и будем довольны уже готовым полем fHitArea. Можно сделать целые подменю, которые будут плавно сменять друг друга, имея в своем распоряжении fAnimationTime и три заветных состояния: скрыто, показано, активно. При этом дублирование кода будет минимальным. Захотели кнопку, "разбухающую" при наведении? Просто отнаследовались от TGUICustomHudButton и изменили scale картинки.
Лично у меня в голове все выглядит замечательно :)
Итак, первую ступеньку данной демонстрации можно посмотреть на видео выше... при этом добавление кнопки в основной программе выглядит совсем прозрачно:
fRightBtn := TGUIAlphaButton.CreateAsChild(fMainDummy); fRightBtn.SetMaterials('btn_1', 'btn_2', fMatLib); fRightBtn.Position.SetPoint(435, 200, 0); fRightBtn.Show;
Установили материалы, позицию и включаем Show.
Обновляем кнопку одной строчкой:
fRightBtn.SetMouseState(MPos.x, MPos.y, IsKeyDown(vk_LButton));
Скриншот результата:
Думаю предсказуемо, что в центре экрана нужно будет положить объекты, которые будут сменяться при нажатии наших кнопок :)
Уф-ф-ф-ф... начало положено, а это самое главное и сложное! Демку можно скачать здесь, или обновитесь через репозиторий (23ья ревизия).
О да! то что нужно :)) спасибо большое)
ОтветитьУдалитьА на какой версии GLScene это собиралось? У меня какие-то непонятные глюки с альфа-каналом.
ОтветитьУдалить