Controlar a interface do jogo com código

Introdução

In this tutorial, you will connect a character to a life bar and animate the health loss.

../../_images/lifebar_tutorial_final_result.gif

Aqui está o que você vai criar: a barra e o contador que animara quando o personagem recebe um hit. Eles desaparecem quando morre.

Você vai aprender:

  • Como conectar um caractere a uma interface gráfica com sinais
  • Como controlar uma GUI com GDscript
  • Como animar uma barra de vida com o nó: ref: Tween <class_Tween>

Se você quiser aprender como configurar a interface, confira os tutoriais passo a passo da interface do usuário:

  • Criar uma tela do menu principal
  • Crie uma interface de usuário do jogo

Quando você codifica um jogo, você quer construir a jogabilidade central primeiro: a mecânica principal, a entrada do jogador, as condições de vitória e derrota. A interface do usuário vem um pouco depois. Você quer manter todos os elementos que compõem seu projeto separados, se possível. Cada personagem deve estar em sua própria cena, com seus próprios scripts, assim como os elementos da interface do usuário. Isso evita bugs, mantém seu projeto gerenciável e permite que diferentes membros da equipe trabalhem em diferentes partes do jogo.

Quando a jogabilidade central e a interface do usuário estiverem prontas, você precisará conectá-las de alguma forma. Em nosso exemplo, temos o inimigo que ataca o jogador em intervalos de tempo constantes. Queremos que a barra de vida seja atualizada quando o jogador sofrer dano.

Para fazer isso, usaremos sinais.

Nota

Signals are Godot’s version of the Observer pattern. They allow us to send out some message. Other nodes can connect to the object that emits the signal and receive the information. It’s a powerful tool we use a lot for User Interface and achievement systems. You don’t want to use them everywhere, though. Connecting two nodes adds some coupling between them. When there’s a lot of connections, they become hard to manage. For more information, check out the signals video tutorial on GDquest.

Baixe e explore o projeto inicial

Baixe o projeto Godot: ui_code_life_bar.zip. Ele contém todos os recursos e scripts de que você precisa para começar. Extraia o arquivo .zip para obter duas pastas: start e` end`.

Load the start project in Godot. In the FileSystem dock, double click on LevelMockup.tscn to open it. It’s an RPG game’s mockup where 2 characters face each other. The pink enemy attacks and damages the green square at regular time intervals, until its death. Feel free to try out the game: the basic combat mechanics already work. But as the character isn’t connected to the life bar, the GUI doesn’t do anything.

Nota

Isso é típico de como você codificava um jogo: você primeiro implementa a jogabilidade central, controla a morte do jogador e somente então você adicionará a interface. Isso porque a interface do usuário ouve o que está acontecendo no jogo. Portanto, não pode funcionar se outros sistemas ainda não estiverem funcionando. Se você projetar a interface do usuário antes de prototipar e testar a jogabilidade, é provável que ela não funcione bem e você terá que recriá-la do zero.

A cena contém uma sprite de fundo, uma GUI e dois caracteres.

../../_images/lifebar_tutorial_life_bar_step_tut_LevelMockup_scene_tree.png

A árvore de cena, com a cena GUI configurada para exibir seus filhos

The GUI scene encapsulates all of the game’s Graphical User Interface. It comes with a barebones script where we get the path to nodes that exist inside the scene:

onready var number_label = $Bars/LifeBar/Count/Background/Number
onready var bar = $Bars/LifeBar/TextureProgress
onready var tween = $Tween
public class Gui : MarginContainer
{
    private Tween _tween;
    private Label _numberLabel;
    private TextureProgress _bar;

    public override void _Ready()
    {
        // C# doesn't have an onready feature, this works just the same.
        _bar = (TextureProgress) GetNode("Bars/LifeBar/TextureProgress");
        _tween = (Tween) GetNode("Tween");
        _numberLabel = (Label) GetNode("Bars/LifeBar/Count/Background/Number");
    }
}
  • `` number_label`` exibe uma contagem de vida como um número. É um nó `` Label``
  • `` bar`` é a própria barra de vida. É um nó `` TextureProgress``
  • `` tween`` é um nó de estilo de componente que pode animar e controlar qualquer valor ou método de qualquer outro nó

Nota

O projeto usa uma organização simples que funciona para competições de jogos e pequenos jogos.

Na raiz do projeto, na pasta res: //, você encontrará o LevelMockup. Essa é a cena principal do jogo e a que vamos trabalhar. Todos os componentes que compõem o jogo estão na pasta scenes /. A pasta assets / contém os sprites do jogo e a fonte do contador de HP. Na pasta scripts /, você encontrará o inimigo, o player e os scripts do controlador GUI.

Clique no ícone de edição de cena à direita do nó na árvore de cena para abrir a cena no editor. Você verá o LifeBar e o EnergyBar como subcenas.

../../_images/lifebar_tutorial_Player_with_editable_children_on.png

A árvore de cena, com a cena Player configurada para exibir seus filhos

Configurar a barra de vida com o máximo de segurança do jogador

Temos que dizer à GUI de alguma forma qual é a saúde atual do player, atualizar a textura da barra de vida e exibir a integridade restante no contador HP no canto superior esquerdo da tela. Para fazer isso, enviamos a saúde do jogador para a GUI toda vez que receberem dano. A GUI irá então atualizar os nós `` Lifebar`` e `` Number`` com este valor.

Nós poderíamos parar aqui para exibir o número, mas precisamos inicializar o `` max_value`` para que ele seja atualizado nas proporções corretas. O primeiro passo é dizer ao `` GUI`` qual é o `` max_health`` do personagem verde.

Dica

A barra, TextureProgress, tem um` max_value` de 100 por padrão. Se você não precisa exibir a saúde do personagem com um número, não é necessário alterar a propriedade max_value. Você envia uma porcentagem do Player para o` GUI` em vez disso:health / max_health * 100.

../../_images/lifebar_tutorial_TextureProgress_default_max_value.png

Clique no ícone de roteiro à direita da `` GUI`` no painel Cena para abrir seu roteiro. Na função _ready, vamos armazenar max_health do Player em uma nova variável e usá-la para definir o max_value do bar:

func _ready():
    var player_max_health = $"../Characters/Player".max_health
    bar.max_value = player_max_health
public override void _Ready()
{
    // Add this below _bar, _tween, and _numberLabel.
    var player = (Player) GetNode("../Characters/Player");
    _bar.MaxValue = player.MaxHealth;
}

Vamos acabar com isso. `` $ “../ Characters / Player” `` é uma abreviação que sobe um nó na árvore de cena, e recupera o nó `` Characters / Player`` de lá. Isso nos dá acesso ao nó. A segunda parte da instrução, `` .max_health``, acessa o `` max_health`` no nó do Player.

A segunda linha atribui este valor a `` bar.max_value``. Você pode combinar as duas linhas em uma, mas precisaremos usar `` player_max_health`` novamente mais tarde no tutorial.

`` Player.gd`` define o `` health`` para `` max_health`` no início do jogo, então podemos trabalhar com isso. Por que ainda usamos `` max_health``? Existem dois motivos:

Nós não temos a garantia de que `` health`` será sempre igual a `` max_health``: uma versão futura do jogo pode carregar um nível em que o jogador já perdeu alguma vida.

Nota

Quando você abre uma cena no jogo, Godot cria os nós um por um, seguindo a ordem no seu painel Cena, de cima para baixo. GUI e Player não fazem parte do mesmo ramo de nó. Para ter certeza de que ambos existem quando nós os acessamos, temos que usar a função _ready. Godot chama _ready logo após carregar todos os nós, antes do jogo começar. É a função perfeita para configurar tudo e preparar a sessão do jogo. Saiba mais sobre _ready: Roteirizando (continuação)

Atualize a vida com um sinal quando o jogador leva um golpe

Nossa GUI está pronta para receber as atualizações do valor `` health`` do `` Player``. Para conseguir isso, vamos usar ** sinais **.

Nota

Existem muitos sinais internos úteis como enter_tree e` exit_tree`, que todos os nós emitem quando são respectivamente criados e destruídos. Você também pode criar seus próprios usando a palavra-chave signal. No nó Player, você encontrará dois sinais que criamos para você:` died` e health_changed.

Por que nós não pegamos diretamente o nó Player na função _process e olhamos o valor de saúde? Acessar nós dessa maneira cria um acoplamento firme entre eles. Se você faz isso com moderação, pode funcionar. Conforme seu jogo cresce, você pode ter muito mais conexões. Se você pegar nós dessa maneira, as coisas ficam complexas rapidamente. Não só isso: você precisa ouvir a mudança de estado constantemente na função _process. Essa verificação acontece 60 vezes por segundo e você provavelmente quebrará o jogo por causa da ordem na qual o código é executado.

Em um determinado quadro, você pode olhar para a propriedade de outro nó antes de ele ser atualizado: você obtém um valor do último quadro. Isso leva a erros obscuros que são difíceis de corrigir. Por outro lado, um sinal é emitido logo após a ocorrência de uma mudança. Isso garante que você está recebendo uma informação recente. E você irá atualizar o estado do seu nó conectado logo após a mudança acontecer.

Nota

O padrão Observador, de onde os sinais derivam, ainda adiciona um pouco de acoplamento entre os ramos do nó. Mas geralmente é mais leve e mais seguro do que acessar nós diretamente para se comunicar entre duas classes separadas. Pode ser bom para um nó pai obter valores de seus filhos. Mas você vai querer favorecer sinais se estiver trabalhando com dois ramos separados. Leia os Padrões de Programação de Jogos para mais informações sobre o ‘Observer pattern <http://gameprogrammingpatterns.com/observer.html>’ . O livro completo <http://gameprogrammingpatterns.com/contents.html> _ está disponível online gratuitamente.

With this in mind, let’s connect the GUI to the Player. Click on the Player node in the scene dock to select it. Head down to the Inspector and click on the Node tab. This is the place to connect nodes to listen to the one you selected.

A primeira seção lista os sinais personalizados definidos em `` Player.gd``:

  • died (“morreu”) é emitido quando o personagem morre. Vamos usá-lo em um momento para esconder a interface do usuário.
  • `` health_changed`` é emitido quando o personagem é atingido.
../../_images/lifebar_tutorial_health_changed_signal.png

Estamos nos conectando ao sinal health_changed

Selecione health_changed e clique no botão Conectar no canto inferior direito para abrir a janela Conectar Sinal. No lado esquerdo, você pode escolher o nó que escutará este sinal. Selecione o nó GUI. O lado direito da tela permite empacotar valores opcionais com o sinal. Nós já cuidamos disso em Player.gd. Em geral, eu recomendo não adicionar muitos argumentos usando esta janela, já que eles são menos convenientes do que fazer a partir do código.

../../_images/lifebar_tutorial_connect_signal_window_health_changed.png

A janela Conectar Sinal com o nó GUI selecionado

Dica

Opcionalmente você pode conectar nós do código. Mas fazê-lo do editor tem duas vantagens:

  1. Godot pode escrever novas funções de retorno de chamada para você no script conectado
  2. Um ícone de emissor aparece próximo ao nó que emite o sinal no painel Cena

Na parte inferior da janela, você encontrará o caminho para o nó selecionado. Estamos interessados na segunda linha chamada “Método no Nó”. Este é o método no nó `` GUI`` que é chamado quando o sinal é emitido. Este método recebe os valores enviados com o sinal e permite processá-los. Se você olhar para a direita, há um botão de opção “Make Function” que está ativado por padrão. Clique no botão de conexão na parte inferior da janela. Godot cria o método dentro do nó `` GUI``. O editor de script é aberto com o cursor dentro de uma nova função `` _on_player_health_changed``.

Nota

Quando você conecta nós no editor, Godot gera um nome de método com o seguinte padrão: `` _on_EmitterName_signal_name``. Se você já escreveu o método, a opção “Make Function” irá mantê-lo. Você pode substituir o nome com o que quiser.

../../_images/lifebar_tutorial_godot_generates_signal_callback.png

Godot escreve o método de callback para você e leva você até ele

Inside the parentheses after the function name, add a player_health argument. When the player emits the health_changed signal, it will send its current health alongside it. Your code should look like:

func _on_Player_health_changed(player_health):
    pass
public void OnPlayerHealthChanged(int playerHealth)
{
}

Nota

The engine does not convert PascalCase to snake_case, for C# examples we’ll be using PascalCase for method names & camelCase for method parameters, which follows the official C# naming conventions.

../../_images/lifebar_tutorial_player_gd_emits_health_changed_code.png

No Player.gd, quando o Player emite o sinal health _changed, ele também envia seu valor de integridade

Inside _on_Player_health_changed, let’s call a second function called update_health and pass it the player_health variable.

Nota

Poderíamos atualizar diretamente o valor de saúde em LifeBar e` Number`. Existem duas razões para usar este método:

  1. O nome deixa claro para os nossos futuros eus e companheiros de equipe que, quando o jogador levou dano, nós atualizamos a contagem de saúde na GUI
  2. Nós vamos reutilizar este método um pouco mais tarde

Crie um novo método `` update_health`` abaixo de `` _on_Player_health_changed``. Leva um novo valor como seu único argumento:

func update_health(new_value):
    pass
public void UpdateHealth(int health)
{
}

Este método precisa:

  • setar o `` text`` do nó `` Number`` para `` new_value`` convertido para uma string
  • setar `` value`` do `` TextureProgress`` para `` new_value``
func update_health(new_value):
    number_label.text = str(new_value)
    bar.value = new_value
public void UpdateHealth(int health)
{
    _numberLabel.Text = health.ToString();
    _bar.Value = health;
}

Dica

str is a built-in function that converts about any value to text. Number’s text property requires a string, so we can’t assign it to new_value directly

Também chame `` update_health`` no final da função `` _ready`` para inicializar o `` text`` do nó `` Number`` com o valor correto no início do jogo. Pressione F5 para testar o jogo: a barra de vida é atualizada a cada ataque!

../../_images/lifebar_tutorial_LifeBar_health_update_no_anim.gif

O nó Number e o TextureProgress são atualizados quando o Player recebe um hit

Animar a perda de vida com o nó Tween

Our interface is functional, but it could use some animation. That’s a good opportunity to introduce the Tween node, an essential tool to animate properties. Tween animates anything you’d like from a start to an end state over a certain duration. For example, it can animate the health on the TextureProgress from its current level to the Player’s new health when the character takes damage.

A cena `` GUI`` já contém um nó filho `` Tween`` armazenado na variável `` tween``. Vamos agora usá-lo. Nós temos que fazer algumas mudanças em `` update_health``.

Nós usaremos o método `` interpolate_property`` do nó `` Tween``. São necessários sete argumentos:

  1. Uma referência ao nó que possui a propriedade para animar
  2. O identificador da propriedade como uma string
  3. O valor inicial
  4. O valor final
  5. A duração da animação em segundos
  6. O tipo da transição
  7. O alívio para usar em combinação com a equação.

The last two arguments combined correspond to an easing equation. This controls how the value evolves from the start to the end point.

Clique no ícone do script ao lado do nó `` GUI`` para abri-lo novamente. O nó `` Number`` precisa de texto para se atualizar, e a `` Bar`` precisa de um float ou um inteiro. Podemos usar `` interpolate_property`` para animar um número, mas não para animar o texto diretamente. Vamos usá-lo para animar uma nova variável `` GUI`` chamada `` animated_health``.

No topo do script, defina uma nova variável, nomeie-a como `` animated_health`` e defina seu valor como 0. Navegue de volta para o método `` update_health`` e limpe seu conteúdo. Vamos animar o valor `` animated_health``. Chame o método `` interpolate_property`` do nó `` Tween``:

func update_health(new_value):
    tween.interpolate_property(self, "animated_health", animated_health, new_value, 0.6, Tween.TRANS_LINEAR, Tween.EASE_IN)
// Add this to the top of your class.
private float _animatedHealth = 0;

public void UpdateHealth(int health)
{
    _tween.InterpolateProperty(this, "_animatedHealth", _animatedHealth, health, 0.6f, Tween.TransitionType.Linear,
        Tween.EaseType.In);
}

Vamos dividir a ligação:

tween.interpolate_property(self, "animated_health", ...

Nosso alvo é `` animated_health`` em `` self``, isto é, o nó `` GUI``. A interpolação de `` Tween`` _property toma o nome da propriedade como uma string. É por isso que nós escrevemos como `` “animated_health” ``.

... _health", animated_health, new_value, 0.6 ...

O ponto de partida é o valor atual em que a barra está. Nós ainda temos que codificar esta parte, mas ela será `` animated_health``. O ponto final da animação é `` health`` do `` Player`` depois do `` health_changed``: é `` new_value``. E `` 0.6`` é a duração da animação em segundos.

...  0.6, tween.TRANS_LINEAR, Tween.EASE_IN)

Os dois últimos argumentos são constantes da classe `` Tween``. `` TRANS_LINEAR`` significa que a animação deve ser linear. `` EASE_IN`` não faz nada com uma transição linear, mas devemos fornecer este último argumento ou obteremos um erro.

A animação não será reproduzida até que ativemos o nó `` Tween`` com `` tween.start () ``. Nós só temos que fazer isso uma vez se o nó não estiver ativo. Adicione este código após a última linha:

if not tween.is_active():
    tween.start()
if (!_tween.IsActive())
{
    _tween.Start();
}

Nota

Embora possamos animar a propriedade health no` Player`, não devemos. Personagens devem perder a vida instantaneamente quando são atingidos. Isso torna muito mais fácil gerenciar seu estado, como saber quando um morreu. Você sempre deseja armazenar animações em um contêiner de dados ou nó separado. O nó tween é perfeito para animações controladas por código. Para animações feitas à mão, confira AnimationPlayer.

Atribuir a _health animada para o LifeBar

Agora a variável `` animated_health`` anima, mas nós não atualizamos mais os nós `` Bar`` e `` Number``. Vamos consertar isso.

Até agora, o método update _health se parece com isto:

func update_health(new_value):
    tween.interpolate_property(self, "animated_health", animated_health, new_value, 0.6, Tween.TRANS_LINEAR, Tween.EASE_IN)
    if not tween.is_active():
        tween.start()
public void UpdateHealth(int health)
{
    _tween.InterpolateProperty(this, "_animatedHealth", _animatedHealth, health, 0.6f, Tween.TransitionType.Linear,
        Tween.EaseType.In);

    if(!_tween.IsActive())
    {
        _tween.Start();
    }
}

Neste caso específico, porque `` number_label`` recebe texto, nós precisamos usar o método `` _process`` para animá-lo. Vamos agora atualizar os nós `` Number`` e `` TextureProgress`` como antes, dentro de `` _process``:

func _process(delta):
    number_label.text = str(animated_health)
    bar.value = animated_health
public override void _Process(float delta)
{
    _numberLabel.Text = _animatedHealth.ToString();
    _bar.Value = _animatedHealth;
}

Nota

number_label e` bar` são variáveis que armazenam referências aos nós Number e` TextureProgress`.

Jogue o jogo para ver a barra animar suavemente. Mas o texto exibe o número decimal e parece uma bagunça. E considerando o estilo do jogo, seria bom para a barra de vida para animar de uma forma mais bonita.

../../_images/lifebar_tutorial_number_animation_messed_up.gif

The animation is smooth, but the number is broken

Podemos consertar ambos os problemas, completando `` animated_health``. Use uma variável local chamada `` round_value`` para armazenar o `` animated_health`` arredondado. Em seguida, atribua-o a `` number_label.text`` e `` bar.value``:

func _process(delta):
    var round_value = round(animated_health)
    number_label.text = str(round_value)
    bar.value = round_value
public override void _Process(float delta)
{
    var roundValue = Mathf.Round(_animatedHealth);
    _numberLabel.Text = roundValue.ToString();
    _bar.Value = roundValue;
}

Rode o jogo novamente para ver uma bela animação em blocos.

../../_images/lifebar_tutorial_number_animation_working.gif

By rounding out animated_health, we kill two birds with one stone

Dica

Toda vez que o jogador recebe um hit, o `` GUI`` chama `` _on_Player_health_changed``, que por sua vez chama `` update_health``. Isso atualiza a animação e o `` number_label`` e `` bar`` seguem em `` _process``. A barra de vida animada que mostra a saúde diminuindo gradualmente é um truque. Faz a GUI parecer viva. Se o `` Player`` receber 3 de dano, isso acontece em um instante.

Clareia a barra quando o jogador morre

Quando o personagem verde morre, ele executa uma animação de morte e desaparece. Neste ponto, não devemos mais mostrar a interface. Vamos desaparecer também quando o personagem morrer. Vamos reutilizar o mesmo nó `` Tween`` enquanto ele gerencia múltiplas animações em paralelo para nós.

Primeiro, o `` GUI`` precisa se conectar ao sinal `` died`` do `` Player`` para saber quando ele morreu. Pressione: kbd: F1 para voltar ao espaço de trabalho 2D. Selecione o nó `` Player`` na doca Scene e clique na aba Node ao lado do Inspector.

Encontre o sinal `` morreu``, selecione-o e clique no botão Conectar.

../../_images/lifebar_tutorial_player_died_signal_enemy_connected.png

O sinal já deve ter o inimigo conectado a ele

Na janela Connecting Signal, conecte-se ao nó `` GUI`` novamente. O caminho para o nó deve ser `` ../../ GUI`` e o método no nó deve mostrar `` _on_Player_died``. Deixe a opção Make Function ativada e clique em Connect na parte inferior da janela. Isso levará você ao arquivo `` GUI.gd`` no espaço de trabalho do Script.

../../_images/lifebar_tutorial_player_died_connecting_signal_window.png

Você deve obter esses valores na janela Conectando um sinal

Nota

Você deve ver um padrão agora: toda vez que a GUI precisar de uma nova informação, emitimos um novo sinal. Use-os com sabedoria: quanto mais conexões você adicionar, mais difícil será rastrear.

Para animar um fade em um elemento da interface do usuário, temos que usar sua propriedade `` modulate``. `` modulate`` é uma `` Cor`` que multiplica as cores de nossas texturas.

Nota

modulate vem da classe` CanvasItem`, todos os nós 2D e UI são herdados. Ele permite que você alterne a visibilidade do nó, atribua um sombreador a ele e modifique-o usando uma cor com modulate.

modulate takes a Color value with 4 channels: red, green, blue and alpha. If we darken any of the first three channels it darkens the interface. If we lower the alpha channel, our interface fades out.

Vamos interpor entre dois valores de cores: de um branco com um alfa de `` 1``, isto é, com opacidade total, a um branco puro com um valor alfa de `` 0``, completamente transparente. Vamos adicionar duas variáveis no topo do método `` _on_Player_died`` e nomea-las `` start_color`` e `` end_color``. Use o construtor `` Color () `` para construir dois valores `` Color``.

func _on_Player_died():
    var start_color = Color(1.0, 1.0, 1.0, 1.0)
    var end_color = Color(1.0, 1.0, 1.0, 0.0)
public void OnPlayerDied()
{
    var startColor = new Color(1.0f, 1.0f, 1.0f);
    var endColor = new Color(1.0f, 1.0f, 1.0f, 0.0f);
}

`` Color (1.0, 1.0, 1.0) `` corresponde ao branco. O quarto argumento, respectivamente `` 1.0`` e `` 0.0`` em `` start_color`` e `` end_color``, é o canal alfa.

Nós então temos que chamar o método `` interpolate_property`` do nó `` Tween`` novamente:

tween.interpolate_property(self, "modulate", start_color, end_color, 1.0, Tween.TRANS_LINEAR, Tween.EASE_IN)
_tween.InterpolateProperty(this, "modulate", startColor, endColor, 1.0f, Tween.TransitionType.Linear,
  Tween.EaseType.In);

This time, we change the modulate property and have it animate from start_color to the end_color. The duration is of one second, with a linear transition. Here again, because the transition is linear, the easing does not matter. Here’s the complete _on_Player_died method:

func _on_Player_died():
    var start_color = Color(1.0, 1.0, 1.0, 1.0)
    var end_color = Color(1.0, 1.0, 1.0, 0.0)
    tween.interpolate_property(self, "modulate", start_color, end_color, 1.0, Tween.TRANS_LINEAR, Tween.EASE_IN)
public void OnPlayerDied()
{
    var startColor = new Color(1.0f, 1.0f, 1.0f);
    var endColor = new Color(1.0f, 1.0f, 1.0f, 0.0f);

    _tween.InterpolateProperty(this, "modulate", startColor, endColor, 1.0f, Tween.TransitionType.Linear,
        Tween.EaseType.In);
}

E é isso. Agora você pode jogar o jogo para ver o resultado final!

../../_images/lifebar_tutorial_final_result.gif

O resultado, final Parabéns por chegar lá!

Nota

Usando exatamente as mesmas técnicas, você pode mudar a cor da barra quando o jogador é envenenado, transformar a barra em vermelho quando a sua saúde cai, sacudir a interface do usuário quando eles tomam um dano crítico … o princípio é o mesmo: emitir um sinal para encaminhar as informações do Player para o` GUI` e deixar o GUI processá-lo.