Como criar a interface de um seletor de fases na Unity

Uma coisa que que basicamente todo jogo tem são fases. Em alguns jogos nem percebemos que estamos mudando de fase. Já em outros essa divisão é mais clara. Mas independente de como os jogos são estruturados, eles costumam oferecer alguma forma do jogador escolher de que ponto ele gostaria de começar a jogar, especialmente quando o jogador já concluiu alguma tarefa para concluir a fase atual e liberar a próxima. Pensando nisso criei este tutorial em que vamos aprender como podemos criar um seletor de fases.

Este tutorial será dividido em duas partes. Na primeira, iremos criar a interface que exibirá as fases e permitirá que o jogador escolha qual ele deseja jogar. Durante o processo aprenderemos como estruturar a hierarquia e como os componentes funcionam para que possamos criar diferentes designs para a nossa tela de seleção de fase.

Na segunda parte, iremos implementar as funcionalidades do sistema de seleção de fases. Iremos aprender uma forma de guardar as informações de uma fase e como exibi-las na interface através do código. Aprenderemos também como impedir que o jogador escolha uma fase que ainda não foi liberada e como carregamos a fase escolhida.

Antes de começar

Para este tutorial usarei a Unity 2021.3.4f1 lts. Mas desde que você esteja usando uma versão relativamente recente você está livre para usar a versão que preferir uma vez que o projeto não requer nada que seja específico dessa versão. Porém é possível que existam pequenas diferenças na UI da Unity.

O projeto completo está disponível no GitHub. Os assets usados para criar a UI estão disponíveis aqui

Caso encontre algum problema ou dificuldade, sinta-se à vontade para entrar em contato comigo através dos comentários, e-mail ou redes sociais.

Instale TextMeshPro

Para esse projeto iremos usar o pacote TextMeshPro para criar os textos na UI. Nas versões mais recentes da Unity ele já vem instalado por padrão. Mas para verificar se ele está instalado em nosso projeto podemos usar o Package Manager. 

Para abrir o Package Manager, clique em Window > Package Manager. 

Na janela que se abriu clique no botão ao lado do sinal de + com o nome Packages e selecione a opção Unity Registry. 

Usando a barra de pesquisa procure TextMeshPro. Selecione o pacote na lista e caso ainda não esteja instalado o botão Install irá aparecer, clique nele.

Agora que temos o TextMeshPro instalado em nosso projeto, precisamos importar os assets padrões. Para isso clique em Window > TextMeshPro > Import TMP Essential Resources. 

Na janela que se abriu selecione tudo e clique no botão Import. Aguarde o processo de importação.

Importe os Assets

Caso queira você pode importar os assets que eu disponibilizei no início do tutorial. 

Primeiro vamos criar uma pasta no projeto chamada Sprites, ela será usada para guardar as imagens que utilizaremos na interface. Para importar os asset abra a pasta com eles no seu sistema operacional, selecione todos os arquivos e arraste para a pasta que criamos na Unity.

Em seguida, selecione os sprites Border e Arrow na Unity e ajuste as configurações para que fiquem igual a imagem abaixo. Clique em Apply para aplicar as modificações.

Logo após, selecione o sprite Mask e ajuste as configurações para que fiquem igual a imagem abaixo. Clique em Apply para aplicar as modificações.

Após configurar os sprites, selecione o arquivo Border e clique em Sprite Editor para fatiar o sprite. 

Na janela que abriu clique no botão Slice, mude a propriedade Type para Grid by Cell Size e na propriedade Pixel Size insira x = 32 y = 32. Clique no botão Slice.

Em seguida, clique na primeira fatia, você verá alguns pontos verdes aparecerão nos lados da fatia. Usamos esses pontos para definir como esse sprite deve ser fatiado quando usado na UI usando uma técnica chamada 9-Slice, ela permite que a imagem seja redimensionada sem que perca a proporção. Isso faz com que as fatias do canto não sejam repetidas e as dos meio sejam repetidas.

Clique no quadradinho verde e arraste até que a linha guia se ajuste com a curva, conforme a imagem abaixo. Faça isso para todos as fatias.

Uma alternativa caso você tenha dificuldade de clicar no quadradinho verde é preencher a informação manualmente. Para isso, selecione a fatia que você quer editar e no canto inferior direito irá aparecer uma caixa com as propriedades que podemos editar. Na propriedade Border digite 5 nos campos L, T, R, B.

Após isso clique no botão Apply e feche essa janela. 

Repita o processo para o sprite Mask.

Por último, selecione o arquivo Arrow e clique em Sprite Editor para fatiar o sprite. 

Na janela que abriu clique no botão Slice, mude a propriedade Type para Grid by Cell Size e na propriedade Pixel Size insira x = 16 y = 32. Clique no botão Slice.

Renomeie o arquivo de cena

A última coisa que temos que fazer antes de começarmos é renomear a cena em que criaremos a interface para seleção de fases. Isso irá ajudar a manter o projeto organizado, ainda mais que iremos criar mais cenas na segunda parte do tutorial para testar o sistema.

Abra a pasta Scenes na janela Project, clique com o botão direito no arquivo SampleScene e selecione a opção Rename, nomeie esse arquivo como LevelSelect. 

Ao fazer isso, a Unity irá avisar que o arquivo foi modificado e a cena tem que ser recarregada. Fique atento porque isso fará com que qualquer modificação que não tenha sido salva seja perdida. Clique no botão Reload para recarregar a cena.

Com isso estamos prontos para começar a construir a interface para exibir as fases.

Como criar a interface para selecionar a fase

O sistema de UI da Unity pode parecer confuso à primeira vista. Mas é extremamente poderoso e nos permite criar basicamente qualquer design que quisermos. Porém é importante termos em mente que a ordem em que os objetos aparecem na hierarquia é importante. 

Primeiro a hierarquia é usada para definir a ordem em que os objetos são renderizados. A Unity renderiza de cima para baixo, ou seja, os objetos que estão no topo da hierarquia serão renderizados no fundo. Além disso, vários componentes usados para calcular o layout usam a hierarquia para decidir a ordem dos objetos.

Uma coisa que me ajuda a organizar a hierarquia é pensar nos objetos que serão usados apenas para posicionar os elementos na interface e os que apenas contém conteúdo. E como você vai ver, essa não é uma regra que sigo à risca, mas que pode ajudar a simplificar designs mais complexos. Ainda assim, é normal que a hierarquia de objetos na UI seja grande.

Como criar a estrutura base do layout

Na janela Hierarchy, clique com o botão direito do mouse e selecione UI > Canvas. 

Isso irá criar um novo GameObject com os componentes necessários para que a interface seja exibida na tela. Nomeiei esse GameObject como LevelSelectCanvas.

Também irá criar um GameObject chamado EventSystem, ele contém os componentes  necessários para que a UI responda aos comandos do jogador. Caso ele não seja criado, clique com o botão na janela Hierarchy e escolha UI > Event System. Porém devemos ter apenas um GameObject na cena com o componente Event System.

Selecione o GameObject LevelSelectCanvas pressione F na janela Scene, a câmera irá focar nesse GameObject. Clique no botão 2D, para ligar o modo 2D na janela da cena, isso fará com que fique mais fácil trabalhar na UI. Essa opção só afeta a câmera na janela Scene.

Em seguida, na janela Inspector, clique no dropdown da propriedade UI Scale Mode do componente Canvas Scaler e selecione a opção Scale With Screen Size. Isso fará com que a interface seja redimensionada conforme a resolução da tela

Na propriedade Reference Resolution podemos definir qual a resolução padrão da interface. Eu usarei a resolução HD, ou seja, x = 1280 y = 720. Porém você pode ajustar para a resolução que melhor se adeque ao seu jogo. 

Após configurar o Canvas Scaler, clique com o botão direito no GameObject LevelSelectCanvas e selecione Create Empty. Nomeie esse GameObject como Body. 

Com o GameObject que acabamos de criar selecionado, na janela Inspector, clique no ícone de Anchors Preset no componente Rect Transform e segurando as teclas Shift e Alt no teclado clique no ícone Stretch Stretch. Isso fará com que esse GameObject preencha toda a área de seu pai, nesse caso, como o seu pai é possui o componente Canvas ele irá preencher a tela inteira.

Em seguida insira o valor 32 nas propriedades Left, Top, Right, Bottom do componente Rect Transform. Isso irá criar uma borda entre o conteúdo que criamos dentro desse GameObject e da tela.

Em seguida, iremos criar três GameObject como filhos do GameObject Body. Para isso clique no Body com o botão direito e selecione Create Empty. Faça esse processo três vezes. O primeiro GameObject que você criar será chamado de Header, o segundo de Levels e o último de Footer.

Após criar esses GameObjects, iremos adicionar alguns componentes para montar o layout da nossa interface.

Primeiro selecione o GameObject Body, clique no botão Add Component e procure por Vertical Layout Group. Esse componente faz com que a Unity calcule os tamanhos que os GameObjects filhos desse componente devem ter e os organiza para que eles fiquem um em cima do outro. A ordem com que os GameObjects estão na hierarquia é importante para esse componente. O GameObject no topo da hierarquia estará no topo da tela.

Porém ainda precisamos fazer alguns ajustes para que tudo funcione do jeito que queremos. Primeiro na propriedade Spacing insira 16, isso fará com que exista um espaço entre cada GameObject, nesse tutorial não veremos muito efeito, mas essa propriedade é útil quando não queremos que os GameObjects não se toquem por algum motivo. 

Depois marque as opções Width e Heigth na propriedade Control Child Size, isso fará com que esse componente controle o tamanhos dos filhos, e desmarque a opção Height na propriedade Child Force Expand, ao fazer isso evitamos que a Unity faça que todos os GameObjects filhos tenham o mesmo tamanho.

Agora selecione o GameObject Header, clique no botão Add Component e procure por Layout Element. Esse componente nos permite ter mais controle em como o componente Vertical Layout Group calcula o tamanho dos objetos filhos. 

Marque a opção Preferred Height e insira 48 no campo que apareceu. Faça o mesmo com o GameObject Footer.

Em seguida adicione o componente Layout Element ao GameObject Levels. Porém, dessa vez marque a opção Flexible Height e insira 1 no campo que apareceu.

Configurando os componentes dessa forma, fazemos que os GameObject Header e Footer ocupem 48 pixels na tela e o GameObject Levels ocupe o espaço restante.

Agora vamos adicionar o título dessa tela. Para isso selecione o GameObject Header e clique com o botão direito sobre ele, selecione UI > Text – TextMeshPro. Nomeie como HeaderText.

Após criar o GameObject clique no ícone Anchors Presets no componente Rect Transform e com as teclas Shift e Alt pressionadas clique no Stretch Stretch para fazer com que esse GameObject preencha toda a área de seu pai. 

Em seguida edite o componente TextMeshPro – Text (UI) com o título dessa tela e estilize da forma que você achar melhor. Para esse tutorial, apenas inseri o texto Level Select, marquei a opção para que ele fique em negrito e centralize o texto.

Em seguida vamos adicionar o botão que será usado para carregar a fase selecionada. Para isso clique com o botão direito sobre o GameObject Footer e selecione UI > Button – TextMeshPro. Nomeie o GameObject como StartButton.

Isso criará um GameObject com os componentes Image que podemos usar para definir a imagem de fundo ou design do botão e o componente Button que será usado irá chamar o método que quisermos quando ele for clicado. Vamos ver como fazer isso na segunda parte do tutorial.

Ele também cria um GameObject como filho com o componente TextMeshPro que podemos usar para definir o texto que irá aparecer no botão. Para modificar o texto clique na setinha ao lado do GameObject StartButton e selecione o GameObject Text (TMP). Se quiser renomeie o GameObject para StartButtonText para manter a hierarquia organizada. E no Inspector escreva o texto que você quer que apareça, no meu caso escrevi apenas Start.

Agora, clique com o botão direito sobre o GameObject Levels e crie três GameObjects. O primeiro será nomeado como PreviouPage, o segundo como LevelCards e o terceiro como NextPage.

Selecione o GameObject PreviousPage e no componente Rect Transform, clique no ícone Anchor Presets e com Shift e Alt pressionados clique na opção Stretch Left. Isso fará com que o GameObject ocupe grude a borda esquerda e ocupe toda a altura do GameObject pai. 

Depois, mude o valor da propriedade Width para 64.

Agora clique com o botão direito sobre esse GameObject e selecione UI > Button – TextMeshPro, nomeie o GameObject como PreviousPageButton. Como vimos isso cria um botão e o texto, porém aqui não queremos o texto, então selecione o GameObject com o componente TextMeshPro e remova ele da cena. 

Selecione o botão e no componente Image clique na bolinha da propriedade Source Image procure e selecione o sprite da seta que aponta para a esquerda. Clique no botão Set Native Size para que a imagem fique com o tamanho correto.

Agora selecione o GameObject NextPage e no componente Rect Transform, clique no ícone Anchor Presets e com Shift e Alt pressionados clique na opção Stretch Right. Isso fará com que o GameObject ocupe grude a borda direita e ocupe toda a altura do GameObject pai.

Em seguida, mude o valor da propriedade Width para 64.

Agora clique com o botão direito sobre esse GameObject e selecione UI > Button – TextMeshPro, nomeie o GameObject como NextPageButton. Faça a mesma coisa que fizemos como o GameObject PreviousPageButton, porém selecione o sprite que aponta para a direita. 

Por fim selecione o GameObject LevelCards e no componente Rect Transform, clique no ícone Anchor Presets e com Shift e Alt pressionados clique na opção Stretch Center. Isso fará com que o GameObject fique no centro e ocupe toda a altura do GameObject pai. Mude o valor propriedade Width para 984. 

Esse valor é baseado no tamanho dos cartões que irão conter as informações de cada fase e o espaço entre cada uma. Para chegar a esse valor eu multipliquei a largura do cartão pela quantidade de cartão e somei a largura do espaço e a quantidade de espaço. No caso deste tutorial, cada cartão tem 240 de largura e serão 4 cartões e os espaços terão 8 de largura e serão 3, ou seja 240 * 4 + 8 * 3.

Com isso temos o layout base da nossa tela de seleção de fase. O próximo passo é criar os cartões que serão usados para exibir as informações das fases e para que o jogador possa selecionar qual ele gostaria de jogar.

Como criar os cartões para exibir as fases

Crie um novo GameObject como filho do objeto LevelCards, nomeie ele como LevelCard. No componente Rect Transform, insira 240 na propriedade Width e 360 na propriedade Height, esse será o tamanho do cartão de cada fase.

Em seguida clique com o botão direito sobre o GameObject LevelCard e selecione UI > Image. Nomeie como Background. Isso irá criar um GameObject com o componente Image. 

Após criar o GameObject, clique no botão Anchor Preset e com Shift e Alt pressionados clique na opção Stretch Strech. Isso fará com que a imagem preencha toda a área do cartão.

Clique na bolinha da propriedade Source Image e selecione o sprite Mask. Mude a propriedade Image Type para Sliced. Isso fará com a Unity use o 9-slice que configuramos quando importamos o sprite. Mude a propriedade Color para #E5E5E5. 

Crie mais uma imagem como filho do GameObject LevelCard. Nomeie como Border. Clique no botão Anchor Preset e com Shift e Alt pressionados clique na opção Stretch Strech. E no componente Source Image selecione o sprite com as bordas azul. Mude a propriedade Image Type para Sliced.

Crie mais uma imagem como filho do GameObejct LevelCard. Nomeie como BorderSelected. Repita o processo que acabamos de fazer, porém selecione o sprite com as bordas laranja.

Em seguida, clique com o botão direito sobre o GameObject LevelCard e selecione Create Empty. Nomeie como Content. Ajuste para que preencha toda a área do GameObject pai. No componente Rect Transform mude os valores das propriedades Left, Top, Right e Bottom para 24. 

Clique no botão Add Component e procure por Vertical Layout Group. Na propriedade Control Child Size marque as opções Width e Height e na propriedade Child Force Expand desmarque a opção Height.

Clique com o botão direito no GameObject Content e selecione UI > Text – TextMeshPro. Nomeie o GameObject como LevelName. Ajuste a propriedade Font Size para 16, deixe em negrito, centralize o texto e mude a cor para #000000. Clique em Add Component e procure por Layout Element e marque a opção Preferred Height e insira 16 no campo que apareceu.

Em seguida clique novamente com o botão direito no GameObject Content e selecione UI > Image e nomeie o GameObject como LevelImage. Clique em Add Component, procure por Layout Element e marque a opção Preferred Height e insira 192 no campo que apareceu.

Por último crie um GameObject com o componente TextMeshPro, nomeio LevelDescription. Ajuste a propriedade Font Size para 12 e mude a cor para #0000. Adicione o componente Layout Element e marque a opção Flexible Heigth e insira 1 no campo que apareceu.

Agora faremos com que possamos selecionar o cartão na interface. Para isso selecione o GameObject LevelCard e clique no botão Add Component e procure por Toggle.

Modifique a propriedade Transition para Sprite Swap. Na propriedade Target Graphic arraste o GameObject Border e na propriedade Disabled Sprite selecione o sprite com as bordas cinza. Por fim, na propriedade Graphic arraste o GameObject BorderSelected.

Em seguida, crie uma pasta chamada Prefabs no projeto e arraste o GameObject LevelCard para ela. Isso fará com que esse GameObject seja transformado em Prefab e assim poderemos criar cópias dele através de código.

Selecione o GameObject LevelCard na janela Hierarchy e duplique ele algumas vezes. 

Como podemos ver os cartões ficam todos no mesmo lugar, porém queremos que eles sejam distribuídos de forma igual pela largura do GameObject LevelCards. 

Com esse intuito, selecione o GameObject LevelCards e clique no botão Add Component e procure por Horizontal Layout Group. Esse componente é similar ao Vertical Layout Group, só que ao invés de organizar um GameObject em cima do outro ele organiza um do lado do outro.

Desmarque todas as opções. Na propriedade Child Alignment selecione Middle Center, isso fará com que os GameObjects fiquem centralizados. Por fim, mude o valor da propriedade Spacing para 8 para criar o espaço entre as cartas. 

Se você duplicou mais do que 4 cards verá que eles agora saem da tela. Para resolver esse problema podemos usar máscaras para esconder GameObjects que não ficam na área que queremos. 

Existem dois tipos de máscaras que podemos usar na Unity, o primeiro componente, chamado Mask, usa uma imagem para definir a área que deve ser exibida, ela é ideal quando temos áreas que não são retangulares. Caso a área seja retangular como a que temos aqui, podemos usar o componente, chamado Rect Mask 2D, ele é um pouco mais eficiente do que o componente Mask.

Conclusão

Com isso chegamos ao final deste tutorial. Como você deve imaginar existem diversas formas de fazer um seletor de fases. Essa é apenas uma, mas acredito que com as informações contidas aqui você será capaz de adaptar para as necessidades de seu jogo.

Porém, ainda não terminamos o projeto. Na segunda parte deste tutorial iremos implementar as funcionalidades do sistema. Não deixe de conferir o próximo post que será publicado na quinta-feira.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *