19 мая 2011

Простое меню с кнопками

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

Итак, сегодня будем делать вот это:


Видео происходящего:


Давайте разберем, из чего состоит наше простое меню. По сути на экране 4 кнопки и одна панелька. Кнопки реагируют на события MouseOver/MouseOut, а также на клик. Панелька умеет выезжать/уезжать, меняя за границами экрана текст. Из действий и анимации вроде все, больше ничего нет. Как всегда я сначала нарисовал желаемый интерфейс в графическом редакторе, представил как все будет двигаться. Цветовую гамму и вдохновение черпал отсюда. Вот элементы, из которых строится наш результат:


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

Идем дальше, для отображения панельки воспользуемся классом, который мы недавно создали. Итак, для анимации возьмем модуль uTweener, для отображения панели - uSimplePanel. Для кнопок же я решил создать отдельный класс, поместив туда базовый функционал:

  TSimpleButton = class (TGLHudSprite)
  protected
    fState: TButtonState;
    fMousePressed: Boolean;
    fOnMouseEvent: TOnMouseEvent;
    fOnMouseClick: TOnMouseClick;
  protected
    Procedure MainInit;
    Procedure SetState(aState: TButtonState);
  public
    property OnMouseEvent: TOnMouseEvent read fOnMouseEvent write fOnMouseEvent;
    property OnMouseClick: TOnMouseClick read fOnMouseClick write fOnMouseClick;
  
    Function IsHit(aPosition: TVector): Boolean;
    Procedure SetMouseState(aMouseX, aMouseY: Single; aIsButtonDown: Boolean);
    Constructor Create(AOwner: TComponent); reintroduce;
    Constructor CreateAsChild(aParentOwner: TGLBaseSceneObject);
  end;

где

  TButtonState = (bs_MouseOut, bs_MouseIn);
  TOnMouseEvent = Procedure (aSender: TSimpleButton; aOldState, aNewState: TButtonState) of object;
  TOnMouseClick = Procedure (aSender: TSimpleButton) of object;

Вот и все, теперь после создания кнопки, например вот таким вот образом:

    fSprite := TSimpleButton.CreateAsChild(fMainDummy);
    with fSprite do
    begin
      Material.LibMaterialName := 'newgame';
      Material.MaterialLibrary := fMatLib;
      Width  := 256;
      Height := 64;
      Position.SetPoint(-5, 100, 0);
    end;
    fSprite.OnMouseEvent := OnMouseEvent;
    fSprite.OnMouseClick := OnMouseClick;

останется только передавать текущее состояние мыши:

fSprite.SetMouseState(MPos.X, MPos.Y, IsKeyDown(Vk_LButton));

Кнопка-кнопкой, но с каждой кнопкой нужно связать некоторые данные - текст на панельке, ее положение tween'инга, а также маленькую полоску с тенью у левой границы кнопки. Связать эти данные я решил в маленьком классе TMenuButton, который поместил в основной модуль демки:

  TMenuButton = class
    fPanel: TGlHudSprite;
    fSprite: TSimpleButton;
    fPanelText: String;
    fX: Single;
  end;

Так будет проще управлять данными и переключать состояния элементов.
Теперь насчет анимации кнопок. Здесь все просто. Ловим событие OnMouseEvent, а в нем уже добавляем необходимые tween-элементы:

Procedure TfrmMain.OnMouseEvent(aSender: TSimpleButton; aOldState, aNewState: TButtonState);
var
  btn: TMenuButton;
begin
  btn := GetButtonBySprite(aSender);
  if btn = nil then
    exit;

  if aNewState = bs_MouseIn then
    fTweener.AddTweenPSingle(@btn.fX, ts_ElasticEaseIn, aSender.Position.X, 50, 1.5)
  else
    fTweener.AddTweenPSingle(@btn.fX, ts_ElasticEaseIn, aSender.Position.X, -5, 1.5);
end;

Немного поясню. Как мы помним, нам нужно получить объект класса TMenuButton, чтобы передать адрес переменной, которая будет tween'иться. Поэтому мы сначала вызываем GetButtonBySprite(), чтобы по объекту класса TSimpleButton найти объект класса TMenuButton. А затем уже "достать" нужный адрес @btn.fX. Вот такая ерунда, а на удивление работает!

Теперь о панельке. При клике по кнопке она сначала уезжает, что прописано в обработчике события OnMouseClick(). А в момент, когда панелька уехала за границы экрана, ее состояние меняется на "появись!", при этом сменяется отображаемый текст и запускается tween'инг:

  if (fPanelItem <> nil) then
    if (fPanelState = ps_GoHide) and (fPanelX > 530) then
    begin
      fPanelState := ps_GoShow;
      fPanelText.Text := fPanelItem.fPanelText;
      fTweener.AddTweenPSingle(@fPanelX, ts_ExpoEaseIn, fPanel.Position.X, 280, 2.0);
    end;

Пожалуй это все. Демка простая, но хорошо вписывается в текущие наработки.
Исходники и exe как всегда качаем отсюда. В репозитории добавление этой демки значится под 7ой ревизией.

На этом все, до скорых встреч, друзья!

p.s. Данное сообщение я опубликовал неделю назад, но из-за сбоев на blogspot оно, почему-то, пропало. Сейчас восстановил, что сохранилось, дописал и залил заново. Надеюсь теперь оно будет висеть без приключений. Прошу прощения за неудобства!

Сообщения, схожие по тематике:

0 коммент.:

Отправить комментарий