Desenvolvimento - C#

Como criar botões em jogos SFML.NET

Neste artigo veremos como criar botões em jogos com a biblioteca SFML.NET, que facilita bastante o uso de sprites e seu desenho na tela.

por Joel Rodrigues



Botões são elementos amplamente utilizados em praticamente todos os tipos de aplicações. Em jogos não é diferente, pois encontramos botões nas mais diversas cenas e locais, como em menus e barras de ferramentas.

Geralmente esses botões são feitos com formas geométricas e/ou imagens que são armazenadas e carregadas na forma de sprites. Em times com funções bem divididas, a criação dessas imagens geralmente fica por conta de um designer/ilustrador, que repassa os assets que o programador precisa para dar vida aos elementos estáticos.

Neste artigo veremos como criar botões em jogos com a biblioteca SFML.NET, que facilita bastante o uso de sprites e seu desenho na tela.

Para promover o reaproveitamento de código e organizar melhor o código do projeto, trabalharemos aqui com uma classe Button que encapsulará as características de um botão. Esta classe, apresentada na Listagem 1, contém sprites para os três estados possíveis de um botão (estado padrão, com o mouse sobre o botão e pressionado), além das coordenadas em que o botão deve ser desenhado (Position), suas dimensões (Width e Height) e um delegate que nos permitirá definir seu comportamento quando for pressionado (Clicked).

Listagem 1. Classe Button

  public class Button
  {
      public Sprite NormalSprite { get; set; }
      public Sprite HoverSprite { get; set; }
      public Sprite PressedSprite { get; set; }
      public int Height { get; set; }
      public int Width { get; set; }
      public Vector2f Position { get; set; }
      public Action Clicked { get; set; }
      public void LoadSprites(Texture texture, Vector2i normalPosition, Vector2i hoverPosition, Vector2i pressedPosition)
      {
          NormalSprite = new Sprite(texture, new IntRect(normalPosition.X, normalPosition.Y, Width, Height));
          HoverSprite = new Sprite(texture, new IntRect(hoverPosition.X, hoverPosition.Y, Width, Height));
          PressedSprite = new Sprite(texture, new IntRect(pressedPosition.X, pressedPosition.Y, Width, Height));
          NormalSprite.Position = Position;
          HoverSprite.Position = Position;
          PressedSprite.Position = Position;
      }
  }

O método LoadSprites é responsável por criar os objetos da classe Sprite com base na textura que lhe é passada, a qual deve conter a imagem com os sprites (sprite sheet) e as posições de cada sprite na imagem.

Com essa classe definida, podemos usá-la já no método Main (considerando que estamos usando uma Console Application), em que criaremos uma janela (RenderWindow) e desenharemos os botões. O código da função Main pode ser visto na Listagem 2.

Listagem 2. Função Main

  private static RenderWindow window;
  private static Button[] buttons;
  static void Main(string[] args)
  {
      buttons = new Button[3]
      {
          new Button() { Height = 55, Width = 200, Position = new Vector2f(20, 100), Clicked = () => { Console.WriteLine("Jogar"); } },
          new Button() { Height = 55, Width = 200, Position = new Vector2f(20, 165), Clicked = () => { Console.WriteLine("Opções"); } },
          new Button() { Height = 55, Width = 200, Position = new Vector2f(20, 230), Clicked = () => { window.Close(); } }
      };
   
      Texture texture = new Texture("Assets/buttons.png");
   
      buttons[0].LoadSprites(texture, new Vector2i(0, 0), new Vector2i(0, 58), new Vector2i(0, 116));
      buttons[1].LoadSprites(texture, new Vector2i(202, 0), new Vector2i(202, 58), new Vector2i(202, 116));
      buttons[2].LoadSprites(texture, new Vector2i(403, 0), new Vector2i(403, 58), new Vector2i(403, 116));
   
      window = new RenderWindow(new VideoMode(640, 480), "Meu Jogo", Styles.None);
      window.Closed += Window_Closed;
      window.MouseButtonPressed += Window_MouseButtonPressed;
   
      while (window.IsOpen)
      {
          window.DispatchEvents();
          window.Clear(Color.White);
          foreach (Button b in buttons)
          {
              if (IsMouseHoverButon(b))
              {
                  if (Mouse.IsButtonPressed(Mouse.Button.Left))
                      window.Draw(b.PressedSprite);
                  else
                      window.Draw(b.HoverSprite);
              }
              else
                  window.Draw(b.NormalSprite);
          }
   
          window.Display();
      }
  } 

No início da função criamos um vetor com três botões que serão desenhados na tela, passando para cada um suas características iniciais. Observe que o uso do delegate Action facilitou a definição do comportamento de cada botão ao ser clicado, simplesmente utilizando um método anônimo.

Logo a seguir carregamos a sprite sheet, que pode ser vista na Figura 1, utilizando um objeto Texture. Essa textura é então passada para os botões, juntamente com as coordenadas do canto superior esquerdo de cada imagem (normal, mouse hover e pressionada).

Na sequência criamos a janela, atribuímos seus event handlers e com um loop sobre o vetor de botões desenhamos cada um deles de acordo com o estado.

Sprite sheet dos botões

Figura 1. Sprite sheet dos botões

A função IsMouseHoverButton, cujo código é visto na Listagem 3, verifica se o cursor do mouse encontra-se sobre um dos botões utilizando para isso as coordenadas e dimensões de cada botão e as coordenadas do mouse, que são obtidas pela função Mouse.GetPosition().

Listagem 3. Função que verifica se o cursor está sobre um botão

  private static bool IsMouseHoverButton(Button button)
  {
      Vector2i mousePos = Mouse.GetPosition() - window.Position;
   
      if (mousePos.X >= button.Position.X && mousePos.X <= button.Position.X + button.Width && mousePos.Y >= button.Position.Y && mousePos.Y <= button.Position.Y + button.Height)
          return true;
   
      return false;
  }

Por fim, tratamos o evento MouseButtonPressed, presente na Listagem 4, da janela para verificar quando um botão foi pressionado, para então executar o método encapsulado em seu delegate Clicked.

Listagem 4. Evento ButtonPressed da janela principal

  private static void Window_MouseButtonPressed(object sender, MouseButtonEventArgs e)
  {
      foreach (Button b in buttons)
      {
          if (IsMouseHoverButon(b))
          {
              if(b.Clicked != null)
                  b.Clicked();
          }
      }
  }

O último trecho de código visto aqui é o evento Closed da janela, no qual simplesmente a fechamos com o método Close quando o usuário assim desejar. Observe a Listagem 5.

Listagem 5. Evento Closed da janela principal

  private static void Window_Closed(object sender, EventArgs e)
  {
      ((Window)sender).Close();
  }

Executando a aplicação, devemos ter como resultado uma janela sem bordas, branca e com os três botões desenhados em seu estado inicial. Ao passar o mouse sobre algum deles, sua aparência deve mudar e ao clicarmos cada um deve apresentar um comportamento. A Figura 2 mostra a aplicação em execução.

Aplicação em execução

Figura 2. Aplicação em execução

Note no console o resultado do clique no botão Jogar, cujo delegate Clicked encapsula um método anônimo que simplesmente exibe uma mensagem na tela.

Para que essa estratégia de desenho dos botões funcione é necessário compreender como funcionam os sprites, bem como conhecer suas coordenadas e dimensões. Com base nesses conceitos é possível criar menus mais complexos, barras de ferramenta e muito mais, bastando para isso um pouco de criatividade e as imagens certas.

Joel Rodrigues

Joel Rodrigues - Técnico em Informática - IFRN Cursando Bacharelado em Ciências e Tecnologia - UFRN Programador .NET/C# e Delphi há quase 3 anos, já tendo trabalhado com Webservices, WPF, Windows Phone 7 e ASP.NET, possui ainda conhecimentos em HTML, CSS e Javascript (JQuery).