Aprendendo a programar jogos em Unity: detecção de objetos com raycast e adição de sons na fase

Vamos desenvolver estratégias para melhoria do sistema de pulos usando Raycast e realizar outros ajustes.

em 06/01/2024
Seja bem-vindo(a) ao GameDev: Aprendendo a programar jogos em Unity de hoje! Dando sequência ao processo de desenvolvimento de nosso game, iremos realizar alguns ajustes no sistema de pulos do personagem, para que sejam realizados apenas enquanto o carrinho estiver sobre alguma plataforma ou sobre o chão. Além disso, iremos acrescentar alguns efeitos e trilhas sonoras ao jogo.


Caso você esteja acessando esta série pela primeira vez, sinta-se convidado a juntar-se a nós nesta caminhada, que nos levará ao aprendizado dos processos de desenvolvimento de jogos divertidos e variados. A partir do primeiro tópico, você poderá aprender mais sobre as etapas de criação de um game utilizando a ferramenta Unity, desde a instalação da ferramenta em seu computador até o desenvolvimento em si, a codificação envolvida, lógicas e regras, utilização de multimídias e composição de cenas.

Nossa série é elaborada de forma a permitir, por meio de exemplos e projetos, que você possa criar “do zero” os games que sempre sonhou em tirar do papel. No momento, estamos desenvolvendo um platformer 2D que batizamos com o nome de Motorista da Pesada: o objetivo do jogador é coletar todas as caixas de presente espalhadas pelas fases no menor tempo possível, guiando um simpático carrinho, antes que o tempo se esgote. Além disso, o gamer tem que prestar atenção nas bombas espalhadas pelos caminhos, que podem abreviar sua aventura, levando ao fim do jogo.


No tópico anterior de nossa jornada, realizamos diversas correções envolvendo o sistema de fim de partida e aspectos relacionados à contagem de vidas e de tempo restante. Também introduzimos alguns códigos para permitir a interação do personagem com as bombas e, também, corrigimos elementos de exibição nos menus e na interface em geral. Venha conosco e vamos juntos nesta jornada rumo a novos conhecimentos!

Pulos pelo ar

Continuando com o processo de polimento das características de gameplay de nosso jogo, talvez você já tenha notado que nosso sistema de saltos respeita os limites superior e inferior da tela e, também, os posicionamentos das plataformas, mas não é tão “realista” pois permite, por exemplo, que o carrinho salte mesmo se estiver flutuando no ar:

Para corrigirmos esse comportamento, primeiramente temos de definir as regras sobre o que permitirá ou não a execução de um pulo pelo personagem. Podemos seguir a seguinte diretriz: caso o carrinho esteja com, ao menos, uma de suas rodas sobre o chão ou sobre uma plataforma, o pulo poderá ser realizado.

Porém, como podemos fazer para que o jogo identifique se o carrinho está sobre uma plataforma ou chão? Para isso, utilizaremos algumas funcionalidades do Unity que permitem, mesmo sem colisão direta, identificar objetos à distância, a partir de um determinado ponto no cenário: são as funções de raycast.

Funções de Raycast

As funções de raycast do Unity (Physics.Raycast e Physics2D.Raycast) são maneiras práticas de se identificar objetos pelo cenário, por meio de uma abordagem bem interessante: elas atuam como se fossem “raios laser” invisíveis, que partem de um determinado ponto no cenário (tridimensional ou bidimensional), em uma direção escolhida, por uma distância determinada.

Ao “colidir” com o Collider de algum objeto, a função recebe como retorno um objeto do tipo RaycastHit2D, contendo informações sobre o elemento alcançado, como seu nome, tipo, tags, dentre outros.

Iremos elaborar alterações no script Pulo para que dois raycasts sejam “disparados” para baixo, a partir da posição das rodas de nosso carrinho, em uma curta distância, conforme exemplificado a seguir:

Caso um desses raycast retorne informações sobre elementos classificados como chão ou como plataforma, significa que o carrinho está com uma das rodas sobre um elemento autorizado para salto, podendo então ser realizado o pulo desejado. A imagem a seguir exemplifica uma situação desse tipo:

Além dessa situação específica, Raycast são úteis para jogos em que é necessário mirar ou coletar objetos à distância, o que é o caso de shooters e, também, de games de qualquer natureza, nos momentos em que é necessário selecionar um objeto tridimensional da cena com o mouse, por exemplo.

Aperfeiçoando o sistema de pulos

Agora, vamos colocar a mão na massa: abra o Unity Hub e clique duas vezes sobre o item referente ao projeto. Na interface inicial do Unity, na aba Project, abra a pasta Assets, Scenes e, por fim, clique duas vezes no ícone da cena Fase01.

Na aba Hierarchy, selecione todos os objetos subordinados ao GameObject Itens que sejam referentes a plataformas ou ao chão, conforme exemplo a seguir:


Na aba Inspector, iremos clicar sobre a caixa de seleção de rótulo Layer (Default) e, por fim, clicar na opção Add Layer

De forma semelhante às tags que utilizamos anteriormente, as layers são formas de identificação de objetos via código, mas que são detectáveis, também, pelo sistema de física do Unity.

Como as funções de raycast são derivadas da biblioteca Physics, iremos criar uma layer específica para o chão e as plataformas. Ainda na aba Inspector, em User Layer 6 preencha o campo vazio com a palavra “Piso”, sem as aspas.


Agora, na aba Hierarchy, selecione novamente os objetos referentes às plataformas e ao chão e, na aba Inspector, troque o valor padrão de Layer (Default) para o recém-criado (Piso), conforme exemplo a seguir:

Com todos os elementos categorizados com o layer Piso, já podemos ir no código do script Pulo e realizar as alterações para que sejam chamadas as funções de raycast nos momentos adequados.

Para isso, na aba Project, clique duas vezes sobre o ícone da pasta Assets, Scripts e, por fim, sobre o ícone do script Pulo, conforme exemplo a seguir:

Com o script aberto para edição no Visual Studio, altere o conteúdo presente dentro dos colchetes de void Update()  para o descrito abaixo:

        if (Input.GetKeyDown(KeyCode.Space))
        {
            RaycastHit2D hitRodaDir = 
                Physics2D.Raycast(transform.position + new Vector3(1, 0, 0), Vector3.down, 0.85f, LayerMask.GetMask("Piso"));
            RaycastHit2D hitRodaEsq = 
                Physics2D.Raycast(transform.position + new Vector3(-1, 0, 0), Vector3.down, 0.85f, LayerMask.GetMask("Piso"));

            if (hitRodaDir.collider != null || hitRodaEsq.collider != null)
            {
                Vector2 sentidoCima = new Vector2(0, 1);
                gameObject.GetComponent<Rigidbody2D>().AddForce(sentidoCima * forca);
            }                
        }

O novo código realiza o lançamento de dois raycasts (hitRodaDir e hitRodaEsq), cada um partindo da posição aproximada das rodas do carro (transform.position + new Vector3(±1, 0, 0)), com sentido para baixo (Vector3.down), percorrendo uma pequena distância (0.85f) e só retornando informações de colisão com objetos categorizados como Piso (LayerMask.GetMask("Piso")).

Caso um dos dois raycasts retorne uma colisão, autoriza-se o pulo; caso contrário, nada irá ocorrer.

Salve o script e minimize o Visual Studio, retornando ao editor do Unity para testar o jogo, indo à aba Game e clicando sobre o botão Play. Veja que, agora, tentar pular sobre o “vazio” já não é mais possível. Não se esqueça de, ao final dos testes, interromper a simulação, clicando novamente sobre o botão Play e retornando à aba Scene.

Trilha sonora e efeitos

Assim como já realizamos para a cena do menu, iremos acrescentar um fundo sonoro à nossa primeira fase. Para isso, na aba Hierarchy, clique com o botão direito em um espaço vazio (após a lista de GameObjects) e, no menu suspenso, selecione Audio e, por fim, Audio Source.


Dê o nome de “FundoSonoro”, sem as aspas, ao objeto. Na aba Inspector, altere o parâmetro AudioClip do componente Audio Source, apontando o arquivo de som de nome “musicaCeuAzul”, sem as aspas.

Altere também os parâmetros Play On Awake e Loop, deixando-os ativados, para que a trilha sonora de fundo toque constantemente.

Agora, iremos implementar um pequeno sistema para que o “ronco” do motor possa ser ouvido durante o deslocamento de nosso carrinho. Na aba Hierarchy, clique novamente com o botão direito em um espaço vazio, após o GameObject FundoSonoro, e, no menu suspenso, selecione Audio e, por fim, Audio Source.

O nome do novo GameObject será “Aceleracao”, sem as aspas. Na aba Inspector, altere o parâmetro AudioClip do componente Audio Source para um dos arquivos de áudio que tenham o nome iniciado por “motor”; escolha aquele que mais te agrade.

Assim como em relação ao feito para o objeto FundoSonoro, altere os parâmetros Play On Awake e Loop, deixando-os ativados, conforme exemplo a seguir:

Uma das características de um carro a combustão “normal” é o som de seu motor, que fica mais agudo conforme o motor é acelerado. Iremos simular, via script, esse comportamento, adicionando algumas linhas de código ao script MovFundo, responsável pelo controle de movimentação em nosso game.

Na aba Project, clique duas vezes sobre o ícone da pasta Assets, Scripts e, por fim, sobre o ícone do script MovFundo, para abri-lo no Visual Studio:

O primeiro acréscimo que iremos fazer é em relação à declaração de duas variáveis novas, audioAceleracao e audioPitch, que serão utilizadas para simular o efeito sonoro de aceleração do veículo ao haver movimentação no cenário. Acrescente as seguintes linhas de código logo após a declaração de velocidade, limiteMin e limiteMax:

    public AudioSource audioAceleracao;
    private float audioPitch;

Agora, acrescente ao final das linhas de código que estão dentro dos colchetes da função void Update() o seguinte conteúdo:

        //Modificar som
        if (audioAceleracao)
        {
            if (direcao != 0)
                audioPitch = Mathf.Min(3, audioPitch + Time.deltaTime);
            else
                audioPitch = Mathf.Max(1, audioPitch - Time.deltaTime);

            audioAceleracao.pitch = audioPitch;
        }

O código inserido realiza as seguintes operações, caso haja um objeto com componente AudioSource referenciado na variável pública audioAceleracao:
  • Se houver movimentação no cenário (direcao != 0), a variável numérica audioPitch terá seu valor gradativamente aumentado até o limite máximo determinado (3);
  • Se não houver movimentação no cenário (direcao == 0), a variável audioPitch terá seu valor reduzido gradativamente até o limite mínimo determinado (1).
  • Ao final, o AudioSource audioAceleracao terá seu parâmetro pitch alterado para o valor calculado, armazenado em audioPitch.
O parâmetro pitch, referente a um AudioSource, é o responsável por alterar o tom de um determinado som sem modificar seu volume. Quanto maior o valor, mais agudo o som será reproduzido. No editor do Unity, ele é representado pela barrinha em destaque na imagem ilustrativa a seguir:


Como o script MovFundo é utilizado em diversos objetos de nosso cenário (chão, plataformas e fundo), não queremos que todos os objetos alterem ao mesmo tempo o som do motor do carro; por isso, foi utilizada a condição if (audioAceleracao) no código, pois iremos apontar a apenas um dos objetos que contenham MovFundo o objeto correto relacionado à variável audioAceleracao.

Salve o script e minimize o Visual Studio, retornando ao editor do Unity. Selecione um dos objetos que contenham o componente MovFundo, por exemplo, Parte1 (subordinado ao objeto Fundo, de Cenario) e, na aba Inspector, indique ao parâmetro Audio Aceleracao do componente Mov Fundo o objeto de nome “Aceleracao”, sem as aspas, conforme exemplo a seguir:

Agora teste o jogo e veja o efeito sonoro do motor ser alterado ao se realizar movimentações no cenário. Note, no canto inferior da imagem a seguir, a barra de seleção de Pitch do componente Audio Source se movimentar dinamicamente:

Caso queira ouvir com mais clareza o som do motor do carrinho, recomendo, após a interrupção da simulação, alterar o parâmetro Volume do componente Audio Source do objeto FundoSonoro para valores mais baixos, por exemplo, 0.8, conforme ilustrado a seguir. Modifique a gosto e teste novamente o game.

Finalize a simulação da execução, clicando novamente sobre o botão Play. Não se esqueça de salvar a cena (menu File, Save) e o projeto (menu File, Save Project) antes de fechar o Unity.

Próximos passos

Hoje pudemos aprender um pouco mais e aplicar em nosso projeto a poderosa funcionalidade de raycast do Unity; em nosso caso específico, para detectar chão e plataformas de nossa fase e regulamentar o uso da funcionalidade de pulo. Também conseguimos implementar o fundo sonoro de nossa primeira fase e um sistema de feedback sonoro da aceleração (simulada) de nosso carrinho pelos caminhos dessa aventura.

Em nosso próximo encontro, vamos abordar uma funcionalidade muito importante, que permite a reutilização de objetos e códigos entre fases de um game, diminuindo “dores de cabeça” ao se realizar alterações posteriores: os prefabs.

Nosso próximo texto já encontra-se disponível, continue conosco nessa jornada de conhecimento e fique ligado sempre aqui no GameBlast!
Revisão: Ives Boitano

Entendo videogames como sendo uma expressão de arte e lazer e, também, como uma impactante ferramenta de educação. No momento, doutorando em Sistemas da Informação pela EACH-USP, desenvolvendo jogos e sistemas desde 2020. Se quiser bater um papo comigo, nas redes sociais procure por @RodrigoGPontes.
Este texto não representa a opinião do GameBlast. Somos uma comunidade de gamers aberta às visões e experiências de cada autor. Escrevemos sob a licença Creative Commons BY-SA 3.0 - você pode usar e compartilhar este conteúdo desde que credite o autor e veículo original.