Close Menu
    Facebook X (Twitter) Instagram
    Select Game
    • Notícias e Posts Recentes
    • Games
      • Fortnite
      • World of Warcraft
      • Honkai Star Rail
      • Elden Ring
      • Listas de Troféus
      • Listas de Conquistas
    • Cosplays e Animes
      • Cosplays
      • One Piece
    • Stories, Looks
      • Web Stories
      • Looks
    Facebook X (Twitter) Instagram
    Select Game
    Início » Gamedev » Tutorial: XNA Invasores – Parte 9

    Tutorial: XNA Invasores – Parte 9

    Diego Barboza15/06/2008

    Como prometido na última parte do tutorial, hoje vamos começar a implementar as colisões entre os objetos do jogo. Antes de fazer, vamos primeiro organizar melhor a classe JogoInvasores, implantando um sistema de estados para controlar o fluxo do jogo.
    Vamos criar um novo arquivo com o nome EstadoJogo.cs e dentro dele adicionar o seguinte código:

    namespace Tutorial_XNAInvasores
    {
        public enum EstadoJogo
        {
            Menu, TelaDeJogo, GameOver, Encerrar, Invasao, Vitoria
        }
    }
    

    Este arquivo irá definir os possíveis estados do jogo: Menu (o jogo está apresentando o menu principal), TelaDeJogo (a tela principal do jogo), GameOver (o jogador perdeu), Encerrar ( o jogador pediu para sair do jogo), Invasao (as naves invadiram a área do jogador) e Vitoria (o jogador venceu).
    Para começar a usar estes estados no jogo, primeiro é necessário declarar um atributo em JogoInvasores para armazenar o estado atual:

    private EstadoJogo estado;

    Feito isso, definimos o estado inicial do jogo no método Initialize.

    Estado = EstadoJogo.Menu;

    Repare que não estamos definindo o estado diretamente através do atributo, mas sim estamos usando uma propriedade “Estado”. Fazemos isso por que queremos que alguns métodos sejam chamados sempre que houver uma mudança de estado.
    A propriedade “Estado” basicamente altera o estado e executa alguma ação extra dependendo do estado anterior.

    private EstadoJogo Estado
    {
        get { return estado; }
        set
        {
            switch (value)
            {
                case EstadoJogo.Menu:
                    estado = value;
                    break;
                case EstadoJogo.TelaDeJogo:
                    estado = value;
                    IniciarJogo();
                    break;
                case EstadoJogo.GameOver:
                    if (estado == EstadoJogo.TelaDeJogo)
                    {
                        FinalizarJogo();
                    }
                    estado = value;
                    break;
                case EstadoJogo.Invasao:
                    if (estado == EstadoJogo.TelaDeJogo)
                    {
                        FinalizarJogo();
                    }
                    estado = value;
                    break;
                case EstadoJogo.Vitoria:
                    if (estado == EstadoJogo.TelaDeJogo)
                    {
                        FinalizarJogo();
                    }
                    estado = value;
                    break;
                case EstadoJogo.Encerrar:
                    estado = value;
                    break;
            }
        }
    }
    

    Conforme você já deve ter reparado, esta propriedade faz chamadas a dois métodos que ainda não foram criados: IniciarJogo e FinalizarJogo. Estes são os métodos responsáveis por iniciar e finalizar a parte central do jogo.
    Vamos então ver como fica IniciarJogo. Lembram que a gente adicionou os componentes NaveJogador e ControladorDeNaves dentro do método Update de JogoInvasor? Pois é, isto agora será feito por IniciarJogo, então podemos remover aquele código que marcamos como temporário. Para quem não se lembra, o código a ser removido é este:

        // Temporário: Cria e adiciona uma NaveJogador ao jogo
        if (naveJogador == null)
        {
            naveJogador = new NaveJogador(this, imagemJogador,
                new Vector2(300, 460));
            Components.Add(naveJogador);
        }
        // Temporário: Cria e adiciona um ControladorDeNaves ao jogo
        if (controladorDeNaves == null)
        {
            controladorDeNaves = new ControladorDeNaves(this);
            Components.Add(controladorDeNaves);
        }
    

    IniciarJogo vai fazer praticamente a mesma coisa, com a diferença que não será preciso ficar verificando se o atributo é nulo (já que iremos chamar este método uma vez só, ao contrário de Update que era chamado várias vezes por segundo) e mais pra frente vamos instanciar também os escudos usando este método.

    private void IniciarJogo()
    {
        naveJogador = new NaveJogador(this, imagemJogador, new Vector2(300, 460));
        Components.Add(naveJogador);
        controladorDeNaves = new ControladorDeNaves(this);
        Components.Add(controladorDeNaves);
    }
    

    Para encerrar o jogo usamos o método FinalizarJogo. Nele, armazenamos a pontuação do jogador e depois removemos os componentes do jogo. A remoção dos componentes é uma tarefa muito simples, basta passar uma instância do componente para o método Components.Remove.
    Novamente, neste método iremos remover também os escudos futuramente, mas por enquanto fica assim:

    private void FinalizarJogo()
    {
        ultimaPontuacao = naveJogador.Pontos;
        Components.Remove(naveJogador);
        naveJogador = null;
        Components.Remove(controladorDeNaves);
        controladorDeNaves = null;
    }
    

    O atributo ultimaPontuacao serve para armazenar a pontuação do jogador, mesmo depois que o componente for destruído. Ele é declarado assim:

    private int ultimaPontuacao;

    Agora voltamos ao método Update onde iremos definir o comportamento do jogo de acordo com seu estado atual. São três comportamentos distintos:

  • Se o estado for TelaDeJogo, chamamos o método AtualizarTelaDeJogo que contém a lógica central do jogo.
  • Se o estado for encerrar, finalizamos o jogo com o método Exit, da classe Game.
  • E se o estado for qualquer outro, chamamos o método ChecarOpcao que verifica a opção do usuário.
  • switch (estado)
    {
        case EstadoJogo.Menu:
        case EstadoJogo.GameOver:
        case EstadoJogo.Invasao:
        case EstadoJogo.Vitoria:
            ChecarOpcao();
            break;
        case EstadoJogo.TelaDeJogo:
            AtualizarTelaDeJogo();
            break;
        case EstadoJogo.Encerrar:
            Exit();
            break;
    }
    

    O método ChecarOpcao ficará responsável por verificar se o jogador deseja jogar novamente ou encerrar o jogo, mas nessa parte do tutorial não vamos nos preocupar com isto, portanto vamos apenas fazer com que este método altere o estado para “TelaDeJogo”.

    private void ChecarOpcao()
    {
        Estado = EstadoJogo.TelaDeJogo;
    }
    

    AtualizarTelaDeJogo é um método bem grande que verifica as colisões e checa o fim de jogo. Hoje vamos ver apenas algumas partes deste método. Já que ainda não estamos trabalhando com os escudos, algumas verificações ainda não serão feitas.
    Repare que vamos utilizar uma classe adicional, chamada GerenciadorDeColisao, para testar colisões entre os objetos do jogo. Esta é uma classe que contém apenas métodos estáticos e sua declaração será vista mais abaixo.

    private void AtualizarTelaDeJogo()
    {
    }

    Começamos o método testando se o tiro do jogador não é nulo e depois verificando sua colisão com as naves do ControladorDeNaves.

    if (naveJogador.Tiro != null)
    {
        if (GerenciadorDeColisao.ChecarColisao(controladorDeNaves,
            naveJogador.Tiro))
        {
            naveJogador.Pontos += 100;
            naveJogador.Tiro.Destruir = true;
        }
    }
    

    Depois fazemos o processo inverso, verificando a colisão do tiro das naves com a nave do jogador.

    if (controladorDeNaves.Tiro != null)
    {
        if (GerenciadorDeColisao.ChecarColisao(naveJogador,
            controladorDeNaves.Tiro))
        {
            naveJogador.Vidas--;
            controladorDeNaves.Tiro.Destruir = true;
        }
    }
    

    O resto do método diz respeito ao fim do jogo.

  • Se a quantidade de vidas restantes do jogador for menor que 0, mudamos o estado do jogo para “GameOver”.
  • Se as naves tiverem invadido o espaço do jogador, mudamos o estado para “Invasao”.
  • E se as naves inimigas acabaram, o jogador venceu e mudamos o estado para “Vitoria”.
  • if (naveJogador.Vidas < 0)
    {
        Estado = EstadoJogo.GameOver;
    }
    else if (controladorDeNaves.Invadiu)
    {
        Estado = EstadoJogo.Invasao;
    }
    else if (controladorDeNaves.QuantidadeDeNaves == 0)
    {
        Estado = EstadoJogo.Vitoria;
    }
    

    Por fim, se o jogador apertar ESC a partida atual termina com uma derrota. É claro que esta é uma opção muito simples, um aperfeiçoamento do jogo seria exibir uma tela de pause ou pelo menos de confirmação quando ESC for pressionado.

    if (Keyboard.GetState().IsKeyDown(Keys.Escape))
    {
        Estado = EstadoJogo.GameOver;
    }
    

    Agora já temos quase tudo pronto, falta apenas criar a classe GerenciadorDeColisao. Crie uma nova classe no projeto com este nome:

    namespace Tutorial_XNAInvasores
    {
        class GerenciadorDeColisao
        {
        }
    }
    

    Precisaremos de três versões diferentes do método VerificarColisao. A primeira irá verificar colisão entre as naves do ControladorDeNaves e um Tiro, a segunda irá testar se um Tiro colidiu com a NaveJogador e a última testa colisão entre um retângulo e um Tiro.
    Vamos à implementação do primeiro método:

    public static bool ChecarColisao(ControladorDeNaves controladorDeNaves,
        Tiro tiro)
    {
        NaveInvasor[,] naves = controladorDeNaves.Naves;
        for (int x = 0; x < ControladorDeNaves.NAVES_X; x++)
        {
            for (int y = 0; y < ControladorDeNaves.NAVES_Y; y++)
            {
                if (naves[x, y] != null)
                {
                    if (naves[x, y].ObterRetangulo().Intersects(
                        tiro.ObterRetangulo()))
                    {
                        naves[x, y] = null;
                        controladorDeNaves.QuantidadeDeNaves--;
                        return true;
                    }
                }
            }
        }
         return false;
    }
    

    Aqui estamos percorrendo todas as naves do ControladorDeNaves e verificando se elas não são nulas, ou seja, se já não foram destruídas. Caso não sejam, utilizamos o método Intersects da classe Rectangle para verificar se os retângulos da nave e do tiro se sobrepõem. Se isto acontecer, destruímos a nave em questão, reduzimos a contagem de naves restantes no controlador, e retornamos verdadeiro para que o jogo saiba que o tiro deve ser destruído e pontos devem ser adicionados ao total do jogador.
    A segunda implementação deste método verifica a colisão do Tiro dos invasores com a NaveJogador:

    public static bool ChecarColisao(NaveJogador naveJogador, Tiro tiro)
    {
        if (tiro.ObterRetangulo().Intersects(naveJogador.ObterRetangulo()))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    

    Temos um método bem mais simples aqui. Como há apenas uma NaveInvasor e um Tiro pra testar, não há necessidade de iterar por nenhuma lista. Apenas testamos então a interseção entre os retângulos dos dois objetos.
    Por fim, mais simples ainda, o teste que será utilizado futuramente para a colisão com os escudos:

    public static bool ChecarColisao(Rectangle caixa, Tiro tiro)
    {
        if (caixa.Intersects(tiro.ObterRetangulo()))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    

    Repare que o funcionamento é basicamente o mesmo da segunda versão, com a diferença que aqui recebemos diretamente uma instância de Rectangle, ao invés de obtê-lo dentro do método. Isto foi feito só para demonstrar algumas formas diferentes de trabalhar com a colisão.
    Para fechar, precisamos apenas adicionar umas propriedades em ControladorDeNaves para que seja possível acessar seus atributos “tiro”, “invadiu”, “quantidadeDeNaves” e “naves”. Feito isso, já podemos compilar e rodar o jogo.

    public Tiro Tiro
    {
        get { return tiro; }
    }
    
    public bool Invadiu
    {
        get { return invadiu; }
    }
    
    public int QuantidadeDeNaves
    {
        get { return quantidadeDeNaves; }
        set { quantidadeDeNaves = value; }
    }
    
    public NaveInvasor[,] Naves
    {
        get { return naves; }
    }
    

    Veja que agora você já pode destruir as naves inimigas e qualquer evento que leve ao fim do jogo (como destruir todos os inimigos ou pressionar ESC) simplesmente faz com que ele reinicie. É claro que vamos mudar isso em breve. Até a próxima.

    Não deixem de conferir o projeto completo aqui.

    Share. Facebook Twitter Pinterest LinkedIn Tumblr WhatsApp

    Posts Relacionados

    Roblox Campos de Batalha de Feiticeiro – Códigos ativos em julho de 2025 do game inspirado em Jujutsu Kaisen (Sorcerer Battlegrounds Codes)

    Roblox Anime Eterno – Códigos ativos em julho de 2025 (Anime Eternal Codes)

    Roblox Garden Tower Defense – Códigos ativos em julho de 2025 (Codes)

    Roblox Avenida Berry RP – Novos Códigos de Julho de 2025 (Berry Avenue Codes)

    GTA 6 – Saiba mais sobre o Jason Duval, um dos protagonistas

    Patch 11.2 de World of Warcraft terá mudanças no funcionamento dos bancos dos personagens

    Posts recentes
    • Roblox Campos de Batalha de Feiticeiro – Códigos ativos em julho de 2025 do game inspirado em Jujutsu Kaisen (Sorcerer Battlegrounds Codes)
    • Roblox Anime Eterno – Códigos ativos em julho de 2025 (Anime Eternal Codes)
    • Roblox Garden Tower Defense – Códigos ativos em julho de 2025 (Codes)
    • Roblox Avenida Berry RP – Novos Códigos de Julho de 2025 (Berry Avenue Codes)
    • GTA 6 – Saiba mais sobre o Jason Duval, um dos protagonistas
    • Patch 11.2 de World of Warcraft terá mudanças no funcionamento dos bancos dos personagens
    • Hearthstone ganha o Rei Mó como pet, mas ele é bem caro para obter (ou o jogador terá de ter muita sorte)
    • Roblox Adopt Me! – Novo código ativo para julho de 2025 (Codes)
    • Termos de Serviço e Política de privacidade
    • Sobre
    • Contato
    © 2025 Select Game - Todos os Direitos Reservados. Imagens e embeds tem foco em divulgação!

    Type above and press Enter to search. Press Esc to cancel.