Menu

20 – Tutorial Intermediário – Vampiro

Chegou a hora de criar mais um monstrinho e minha criatividade para diferentes monstros está acabando, mas pensando aqui em criar um inimigo imortal, ou seja, mesmo que você cause dano a ele, a sua regeneração é mais rápido.

 Para facilitar, esse inimigo não vai andar, apenas ficar girando de um lado para o outro. E caso jogador apareça na sua frente ele atacará.

E o que melhor do que um vampiro para interpretar um inimigo em uma caverna com poder de regeneração?

Os sprites que vamos utilizar são os disponibilizados gratuitamente pelo irmirx no site OpenGameArt:

http://opengameart.org/content/vampire-animations

Vamos utilizar os 3 tipos de animação. Então na pasta Resources/sprites/inimigos/vampiro crie mais 3 pastas: surgindo (os sprites appear), parado (sprites walk-idle) e atacando (sprites attack):

03- Tutorial Fase 3 - Inimigo

Todas as imagens como Texture Type Sprite, ok?

Bom, agora a gente precisa criar um objeto no cenário com o nome Vampiro. Adicionamos o Sprite Renderer com uma imagem qualquer do vampiro para posiciona-lo no mapa em um tamanho legal (Lembrando que depois vamos remover):

05- Tutorial Fase 3 - Inimigo

Após ajustar a posição e o tamanho do Vampiro, já podemos adicionar o Box Collider 2D e o Rigidbody2D. No Rigidbody2D marque a opção Freeze Rotation e Freeze Position X (ou Is Kinematic) para o objeto não ser empurrado no Sleeping Mode para Never Sleep.

06- Tutorial Fase 3 - Inimigo

Agora já podemos criar as animações. Então clicando sobre o Vampiro, vá na aba Animation e crie 4 animações na pasta Resources/animations/inimigos/vampiro:

07- Tutorial Fase 3 - Inimigo

atacando, escondido e surgindo terão o Loop Time na aba Inspector desativados, uma vez que a animação não vai precisar se repetir.

A animação escondido como sempre não há nada.

A animação surgindo, iremos colocar os sprites do vampiro surgindo (Só cuidado que eles estão na ordem invertida, como se estivesse desaparecendo):

08- Tutorial Fase 3 - Inimigo

Na animação atacando usaremos o sprites atacando e na animação parado iremos usar os sprites da pasta parado.

Na animação atacando, talvez seja preciso ajustar o Pivot da animação para que ela fique alinhada com as demais:

09- Tutorial Fase 3 - Inimigo

Eu não lembro se eu explique o que é o Pivot/Anchor/Ancora dos sprites. Mas é o que define a posição central da imagem. Ou seja, se as imagens não tiverem bem alinhadas, ela vão ficar saltando para diferentes direções.

Com as animações prontas, é hora de editar o Animator. Lá criaremos 2 parametros do tipo Trigger: surgiu e atacou.

As transições serão simples:

Entry -> escondido
escondido -> surgindo (Condição: surgiu | Has Exit Time: Não | Transition Duration: 0)
surgindo -> parado (Condição: nada | Has Exit Time: Sim | Transition Duration: 0)
parado -> atacando (Condição: atacou | Has Exit Time: Não | Transition Duration: 0)
atacando -> parado (Condição: nada | Has Exit Time: Sim | Transition Duration: 0)

10- Tutorial Fase 3 - Inimigo

Pronto, agora podemos adicionar uma luz um pouco clara no vampiro para iluminar o chão e o trazer um pouco a frente (Position Z).

11- Tutorial Fase 3 - Inimigo

12- Tutorial Fase 3 - Inimigo

Com isso nosso personagem está praticamente pronto. Logo já pode deixar ele sem nada no Sprite Renderer para que fique invisível. E então iremos criar seu script (C#) com o nome Vampiro dentro da pasta scripts/inimigos. Esse script assim como os outros de inimigo, vai herdar a classe Inimigo, então sua estrutura será:

Script: Vampiro.cs

using UnityEngine;
using System.Collections;
using System;

public class Vampiro : Inimigo {

    protected override void atacar() {
        
    }

    protected override void mover() {
        
    }
}

Bom, a primeira coisa que farei logo é definir os pontos de vida do vampiro:

Script: Vampiro.cs

    protected override void Awake() {
        base.Awake(); //Chama o Awake da classe Inimigo
        status = new Status(999, 0, 0); //HP = 999 | MP = 0 | Ataque = 0
    }

A ideia agora é criar as variáveis do ataque e movimento. Como falei, o Vampiro não vai sair do lugar, mas ele vai girar, então precisamos saber a direção que está e o tempo que levará para ele girar de um lado para o outro:

Script: Vampiro.cs

    public bool direita = false;        //Está virado para direita
    public float tempoVirar;            //De quanto em quanto tempo ele vira
    private float contadorTempoVirar;   //O contador que verifica o tempo para virar

Temos que iniciar o contador no método Awake, certo?

Script: Vampiro.cs

    protected override void Awake() {
        base.Awake(); //Chama o Awake da classe Inimigo
        status = new Status(999, 0, 0); //HP = 999 | MP = 0 | Ataque = 0
        contadorTempoVirar = tempoVirar;
    }

Com isso o método Mover é bem simples:

Script: Vampiro.cs

protected override void mover() {
        contadorTempoVirar -= Time.deltaTime;
        if (contadorTempoVirar <= 0) {
            direita = !direita;         //Direita vai receber a negação dela, ou seja se era false vira true, se era true vira false
            inverter();                 //inverte o lado 
            contadorTempoVirar = tempoVirar; //reinicia a contagem
        }
    }

Vamos reduzindo o tempo, quando o tempo zerar, informamos para a variável direita o seu valor inverso (Se era true vira false. Se era false vira true), invertemos o objeto através do método que já foi implementado na classe Inimigo e reiniciamos a contagem.

Tudo tranquilo, ok?

Agora precisamos fazer o ataque. Então criaremos uma variável publica para o dano e outra para a distância do alcance do ataque:

Script: Vampiro.cs

using UnityEngine;
using System.Collections;
using System;

public class Vampiro : Inimigo {

    public bool direita = false;        //Está virado para direita
    public float tempoVirar;            //De quanto em quanto tempo ele vira
    private float contadorTempoVirar;   //O contador que verifica o tempo para virar

    public int dano;        //Dano causado no jogador
    public float alcance;   //Alcance do ataque

E agora iremos verificar se o personagem está ou não dentro do alcance. Algum chute de como podemos fazer isso? Nada, nada, nadinha? E se eu disser que a gente já fez isso? Como nós sabemos que nosso personagem está no chão? Verificando se da sua posição até outra, existe um objeto com uma layer especifica, não é? Aqui será a mesma coisa, vamos verificar se da posição do inimigo até o alcance, existe algum objeto com a Layer personagem. Caso tenha e já não esteja realizando um ataque, atacamos e causamos dano ao personagem.

Só que ao invés de usarmos o Physics2D.Linecast para não ficar algo repetitivo (Afinal a ideia é mostrar o máximo de coisas diferente para vocês), iremos usar o Physics2D.Raycast.

Qual é a diferença de um para o outro? Bom o Linecast como o próprio nome diz, vai criar uma linha de um ponto a outro, ou seja, precisamos saber o ponto de inicio e fim. Já o Raycast ele vai criar uma linha de um ponto, a uma determinada direção com um alcance X.

Exemplificando.
Linecast -> Podemos dizer que cria uma linha da posição 1 a 10
Raycast -> Podemos dizer que cria uma linha da posição 1 a uma direção (Exemplo X + 1 | Vector3.right) com um alcance de 10 casas.

Ou seja, precisamos saber 3 coisas. O ponto de origem, a direção que a linha vai seguir e qual será seu alcance. Então nosso código será:

Script: Vampiro.cs

    protected override void atacar() {
        var direcao = (direita ? Vector3.right : -Vector3.right); 
        var personagemPerto = Physics2D.Raycast(transform.position, direcao, alcance, 1 << LayerMask.NameToLayer("Personagem"));
        
        if (personagemPerto && !animator.GetCurrentAnimatorStateInfo(0).IsName("atacando")) {
            animator.SetTrigger("atacou");
            var personagem = GameObject.FindGameObjectWithTag("Player").GetComponent();
            personagem.recebeDano(dano);
        }
    }

Primeiro pegamos a direção que o Raycast vai apontar. A gente já viu a estrutura ( CONDICAO ? RESPOSTA SE TRUE : RESPOSTA SE FALSE), não é? Então caso direita seja verdade, a direção será Vector3.right (X = 1, Y = 0, Z = 0) e caso direita seja false então a direção ser -Vector3.right (X = -1, Y = 0, Z = 0).

Depois no Raycast, verificamos se da posição atual do inimigo (transform.position), em direção a variável direcao, e com um alcance informado lá no inicio, existe algum objeto com a Layer “Personagem”.

Formas alternativas disso seria criar um objeto alcanceVerificador e pegar a sua posição igual ao chaoVerificador e usar o Linecast. Ou ainda pior, gerar a posição do alcance no código e usar o Linecast (O que é mais complicado, sendo mais interessante usar o Raycast).

Já no if, verificamos se personagemPerto possui um valor true (resultado do Raycast) e se o inimigo já não está executando a animação de ataque. Nós já vimos o método animator.GetCurrentAnimatorStateInfo(0).IsName nos nossos personagens não é mesmo? Mas para quem não lembra o GetCurrentAnimatorStateInfo(0), busca a animação atual que está sendo executada e o IsName(“atacando”), verifica se essa é a animação atacando.

Caso o inimigo esteja no alcance e o vampiro não esteja atacando, então ativamos a animação de ataque (animator.SetTrigger(“atacou”)), buscamos o personagem e o causamos dano.

“Carlos, eu estava aqui pensando, como eu posso saber o alcance ideal já que não existe mais um objeto para eu ter uma noção desse alcance?”

Calma, calma, calma. Se o alcance é 5, então é só você ver a posição do seu vampiro no eixo X e substrair ou adicionar 5. Mas existe outra forma que é o Debug.DrawRay. Que cria uma linha de uma posição a outro alcance.
Ou seja é só adicionar a seguinte linha:

Debug.DrawRay(transform.position, direcao * alcance, Color.red);

Onde a função DrawRay vai criar uma linha vermelha (Color.red) da posição atual (transform.position), até um determinado alcance (direcao * alcance). Caso você não defina a cor, ela irá formar uma linha branca.

Outra coisa que podemos adicionar no método de ataque ou mover é a recuperação da vida do nosso vampiro, a sua regeneração. Ou seja, o método ataque fica sendo:

Script: Vampiro.cs

	    protected override void atacar() {
        //Verifica se o personagem está no alcance
        var direcao = (direita ? Vector3.right : -Vector3.right); 
        var personagemPerto = Physics2D.Raycast(transform.position, direcao, alcance, 1 << LayerMask.NameToLayer("Personagem"));
        
        //Cria a linha na aba Scene
        Debug.DrawRay(transform.position, direcao * alcance, Color.red);

        //Verifica se pode atacar
        if (personagemPerto && !animator.GetCurrentAnimatorStateInfo(0).IsName("atacando")) {
            animator.SetTrigger("atacou");
            var personagem = GameObject.FindGameObjectWithTag("Player").GetComponent();
            personagem.recebeDano(dano);
        }

        //Regenera
        status.recuperaHP(999); //Recupera tudo
    }

E no nosso método lá de mover, ainda podemos adicionar também a verificação se o personagem está ou nõa atacando, para que ele não vire enquanto realiza um ataque, né?

Script: Vampiro.cs

    protected override void mover() {
        if (!animator.GetCurrentAnimatorStateInfo(0).IsName("atacando")) { 
            contadorTempoVirar -= Time.deltaTime;
            if (contadorTempoVirar <= 0) {
                direita = !direita;         //Direita vai receber a negação dela, ou seja se era false vira true, se era true vira false
                inverter();                 //inverte o lado 
                contadorTempoVirar = tempoVirar; //reinicia a contagem
            }
        }
    }

Pronto! Agora sim nosso script está feito. Só basta a gente voltar no editor do Unity e adicionar o script ao Vampiro:

13- Tutorial Fase 3 - Inimigo

Após adicionar, pode transformar seu Vampiro em um prefab na pasta Resources/prefabs/inimigos e testar seu jogo.

16- Tutorial Fase 3 - Inimigo

Oh a nossa linha vermelha ali do Debug.DrawRay. Caso a gente entre nesse alcance, ele irá atacar:

17- Tutorial Fase 3 - Inimigo

Um inimigo simples, com uma mecânica diferente. Vimos como deixa-lo imortal, aprendemos um pouco sobre o Debug.DrawRay e o Physics2D.Raycast. Tanto é que se você não quiser usar o chaoVerificador, pode usar o Physics2D.Raycast com a direção sendo “–Vector3.up” para apontar para baixo.

Então chegamos ao fim desse tutorial! Qualquer coisa, é só deixar nos comentários o/

Criador do Jogos Indie, amante de jogos, terror, música, anime e programação. Estudante de mestrado com foco em jogos na educação. Louco por Resident Evil e... sei lá, acho que é isso O.o

2 comments

  1. PedroPW disse:

    Otimo tutorial amigo, tenho uma duvida aki porque o meu jogo está tao grande, passando de 30MB? O jogo nao tem tanta coisa assim, por enquanto só tem as plataformas e nem o inimigo coloquei ainda, posteriormente gostaria de colocar ele pra android com as devidas modificações mais com 30MB fica meio dificil, gostaria de saber como “diminuir” se for possivel. Achei esse site seu na net bem legal nao pare com os tutos 😉

    • Quando você gera o jogo no standalone (desktop), ele também cria dois outros arquivos, que esqueci o nome deles. Mas você pode excluir, são arquivos com 120mb senão me engano e ficam juntos do .exe.

      Porém, quanto aos demais conteúdos, é preciso sim otimizá-los pra diminuir o tamanho principalmente par celular. Ao clicar nas imagens e áudios do seu jogo dentro do Unity, na aba Inspecto aparece algumas opções de compressão do conteúdo. Ali você pode diminuir a qualidade dos recursos na aplicação final, com a intenção de ganhar desempenho e ocupar menos espaço.

Deixe uma resposta

Parceiros

Steam Brasil LoboLimão Centro RPG Lab Indie
Mundo Gamer PodTerror

Anunciantes

Aglomerando - Agregador de conteúdo
Uêba - Os Melhores Links GeraLinks - Agregador de links Piadas Idiotas - São idiotas mas o faz rir Tedioso: Os melhores links LinkLog MeusLinks.com - Informação e conteúdo todos os dias para você! Agregador de Links - Madruga Links 4Blogs - Agregador de conteúdo Está no seu momento de descanso né? Entao clique aqui!