Menu

18 – Tutorial Intermediário – Boss – Mago Azul

E então para quem estava achando ruim os inimigos que não atacavam, só andavam, hoje teremos um boss que só faz atacar e não se “move”. Por que as aspas? Bom, vocês logo entenderam.

Quem aqui já baixou os sprites do disciple-45×51.png no link abaixo:

http://opengameart.org/content/bosses-and-monsters-spritesheets-ars-notoria

Se não baixou, agora é a hora! Após baixar renomei o sprite para mago_azul.png e o adicione dentro da pasta Resources/sprites/inimigos/magoAzul. A imagem será do tipo Sprite e o Sprite Mode será Multiple:

01- Tutorial Fase 2 - Boss

Depois basta clicar no Sprite Editor logo abaixo e cortar as imagens (Slice) através da opção Grid By Cell Count sendo 4 colunas e 3 linhas:

02- Tutorial Fase 2 - Boss

Agora criei um objeto na Scene com o nome MagoAzul, adicionei o componente Sprite Renderer com a imagem do mago e o posicione lá no lado do Boss:

03- Tutorial Fase 2 - Boss

Você provavelmente vai precisar também ajustar a escala (Scale). Com isso pronto, agora vamos criar as animações. Então clique no objeto na aba Hierarchy e depois vá na aba animação criando as animações:

04- Tutorial Fase 2 - Boss

Pode remover o Loop Time de todas elas:

05- Tutorial Fase 2 - Boss

A animação escondido não terá nada.

A animação surgindo, você pode adicionar o primeiro sprite do personagem duas vezes, sendo que o primeiro você vai lá na opção color e deixar o personagem invisível (Alterando o Alpha Channel para 0) e no ultimo você vai deixar ele completamente visível (Tornando Alpha Channel = 255). (Quanto maior o Sample, acho que mais estiloso fica a entrada do boss surgindo do nada).

06- Tutorial Fase 2 - Boss

Na animação parado, basta adicionar o primeiro sprite. Sim só um, não vamos criar uma animação para dele ocioso, por não termos sprite e porque eu acho que cria um clima tenso legal pra o personagem imóvel, sombrio assim.

Na animação morrendo vamos adicionar todos os sprites:

07- Tutorial Fase 2 - Boss

Agora vamos para a aba Animator. Nela teremos apenas dois parâmetros do tipo Trigger: “surgiu” e “morreu”.

Já as transições serão:

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)
Any State -> morrendo (Condição: morreu | Has Exit Time: Não | Transition Duration: 0)

08- Tutorial Fase 2 - Boss

Pronto. Boa parte do nosso Inimigo, já está pronto. Porém antes de irmos para o Script, vamos precisar fazer outro objeto. Nosso mago irá soltar umas magias em direção ao personagem.

A animação do ataque será o mage-bullet-13×13.png que também está naquele link lá no inicio com os bosses. Após baixar adicione na pasta Resources/sprites/ataques/ com o nome magia_negra. A imagem será do tipo Sprite e o Sprite Mode será multiple. Na hora de cortar a imagem, corta 5 Colunas por 1 Linha:

09- Tutorial Fase 2 - Boss

Agora é só criar um Objeto com o nome MagiaNegra, adicionar o Sprite Renderer (para ter uma noção do tamanho da bola. Eu aumentei a sua escala para 10) e então criar uma animação.

10- Tutorial Fase 2 - Boss

A animação será criada na pasta Resources/animations/ataques com o nome magia_negra. Nela é só adicionar todos os sprites da imagem.

11- Tutorial Fase 2 - Boss

Como só temos uma única animação, não precisa fazer nada no Animator.
Por fim adicione um Circle Collider 2D com a opção Is Trigger:

13- Tutorial Fase 2 - Boss

Para adiantar no MagoAzul podemos adicionar também logo o Box Collider 2D e o Rigidbody2D, só que no Rigidbody2D no Sleeping Mode troque para Never Sleep (Isso fará com que o personagem receba dano através do OnTriggerEnter2D ou OnCollisionEnter2D mesmo que não esteja se movendo).

14- Tutorial Fase 2 - Boss

Agora iremos criar um Script genérico de ataques que sigam o jogador. Então na pasta scripts/inimigos crie um script chamado AtaqueSeguirJogador. Esse script será genérico para atender tanto a MagiaNegra quanto a outros tipos de ataques que terão mais na frente.

Então basicamente o que teremos que ter as seguintes variáveis:

1 -> Uma para armazenar a posição do jogador
2 -> Uma para determinar a velocidade que o ataque segue
3 -> Uma para determinar o dano do ataque
4 -> Uma para determinar em quanto tempo o ataque vai se auto destruir. (Vamos por 0 para não vai se auto destruir)

Essa variáveis serão:
Script: AtaqueSeguirJogador.cs

using UnityEngine;
using System.Collections;

public class AtaqueSeguirJogador : MonoBehaviour {

    private Transform jogador;       //Alvo que irá seguir
    public float velocidade;        //Velocidade que o ataque se move
    public int dano;                //Dano que o ataque causa
    public float tempoDestruir;     //Tempo que levará para o ataque se auto destruir
}

No Start iremos pegar o Player e verificar se o objeto vai se auto destruir em um determinado tempo, caso sim informamos para isso ocorrer:

Script: AtaqueSeguirJogador.cs

	void Start () {
        jogador = GameObject.FindGameObjectWithTag("Player").transform; //Recupera o transform do jogador
        if (tempoDestruir > 0)
            Destroy(this.gameObject, tempoDestruir);
	}

Para seguir o personagem será igual ao script da medusa. Ou seja vamos usar o Vector3.MoveTowards para ir em direção ao jogador e vamos rotacional-lo com o Quarternion.LookRotation:

Script: AtaqueSeguirJogador.cs

	void Update () {
        transform.position = Vector3.MoveTowards(transform.position, jogador.position, velocidade * Time.deltaTime);

        //Rotaciona
        var direcao = jogador.transform.position - transform.position;
        var rotacao = Quaternion.LookRotation(direcao, transform.TransformDirection(Vector3.up));
        transform.rotation = new Quaternion(0, 0, rotacao.z, rotacao.w);
    }

Por sim, nesse script vamos adicionar um script OnTriggerEnter2D para verificar caso tenha tocado no personagem. Caso sim, então iremos causar dano ao personagem e destruir o objeto:

Script: AtaqueSeguirJogador.cs

    void OnTriggerEnter2D(Collider2D colisor) {
        if (colisor.gameObject.tag.Equals("Player")) {                      
            var personagem = colisor.gameObject.GetComponent();
            personagem.recebeDano(dano);
            Destroy(this.gameObject);
        }
    }

Bem simples, né? Agora é só adicionar esse script ao nosso objeto MagiaNegra lá no editor do Unity:

15- Tutorial Fase 2 - Boss

Ou seja a MagiaNegra vai seguir o jogador numa velocidade = 3, caso acerte vai causar 3 de dano e vai se auto destruir em 4 segundos.

Após adicionar o script, transforme esse objeto MagiaNegra em um prefab dentro da pasta Resources/prefabs/inimigos:

16- Tutorial Fase 2 - Boss

Pode removê-lo da aba Hierarchy também. Próximo passo será fazer o script do MagoAzul, então crie um script chamado MagoAzul dentro da pasta scripts/inimigos. Esse script vai herdar de Inimigos, então sua estrutura será:

Script: MagoAzul.cs

using UnityEngine;
using System.Collections;
using System;

public class MagoAzul : Inimigo {

    protected override void atacar() {
        
    }

    protected override void mover() { }
}

Qual será a desse boss. Bom, como falei, ele não vai se mover, mas ficar parado é muito fácil, né? Então a ideia é que ele se teletransporte para diferentes posições do cenário ao receber algum dano. E ele também irá soltar 3 de tempo em tempo.

Desta forma vamos precisar criar uma variável para armazenar as posições que o inimigo pode se teletransportar e outras duas variáveis para armazenar o tempo que ele irá realizar o ataque e um para verificar a contagem desse tempo:

Script: MagoAzul.cs

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;

public class MagoAzul : Inimigo {

    public List posicoes;
    public float duracaoAtaque;
    private float contagemAtaque;

    protected override void atacar() {
        
    }

    protected override void mover() { }
}

Opa! Tem algo novo ai para vocês. Uma lista. Para não termos que criar variáveis como posicao1, posicao2, posicao3… Podemos criar uma lista de posições. Ou seja, uma única variável que armazena várias posições. As listas são sempre definidas como List. Para usar o List você precisar chamar no inicio do seu script o namespace: using System.Collections.Generic;.

A variável duracaoAtaque determinar de quanto em quanto tempo ele irá realizar seus ataques, exemplo a cada 4 segundos ele lança um novo ataque. E o contagemAtaque serve para verificar se já chegou o momento de lançar o ataque.

Qual é o próximo passo? Definir o Status do boss no método Awake. Também já iremos passar para a variável contagemAtaque o tempo definido na duracaoAtaque.

Script: MagoAzul.cs

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

        contagemAtaque = duracaoAtaque;
    }

A gente poderia por MP e ir usando, porém eu acho que chegaria um tempo em que o Boss não poderia mais usar magia e isso seria chato, ele lá parado só apanhando. Em relação ao ataque é 0, porque ele também não irá causar dano ao jogador. Ou seja, não teremos o OnCollisionStay2D. E com isso eu pensei que o OnCollisionStay2D poderia sair das outras 3 classes dos inimigos que fizemos e lança-la na classe global Inimigo, o que acham? Deixa nossos códigos mais enxutos, né?

Então antes de continuar esse código, vamos fazer o ajuste. No Script inimigo, adicione o método OnCollisionStay2D, só que vamos verificar se o inimigo possui ataque ou não, caso não possua, ignora o resto do código. Também vamos adicionar aquela variável encostado e o método OnCollisionExit2D:

Script: Inimigo.cs

    void OnCollisionStay2D(Collision2D colisor) {
        if (!status.estaMorto() && status.getAtaque() > 0) { 
            if (colisor.gameObject.tag.Equals("Player")) {
   encostado = true;
                var personagem = colisor.gameObject.GetComponent();
                personagem.recebeDano(status.getAtaque());
                animator.SetTrigger("atacou");
            }
        }
    }
    void OnCollisionExit2D(Collision2D colisor) {
        if (colisor.gameObject.tag.Equals("Player"))
            encostado = false;
    }

Agora no inicio do script Inimigo.cs, adicionem a variável encostado como protected:

Script: Inimigo.cs

    protected bool encostado = false; //Está encostado no alvo

E depois remove esses mesmos métodos e a variável dos scripts Medusa, Zumbi e Andromalius. Pronto com essa modificação, já iremos reduzir bastante linhas de códigos.

Voltando ao script do MagoAzul, iremos criar um método para criar o prefab da MagiaNegra e nela ainda vamos alterar a velocidade da magia para ser de forma aleatória!

Script: MagoAzul.cs

    void lancarMagia() {
        var prefab = Resources.Load("prefabs/inimigos/MagiaNegra");
        var obj = Instantiate(prefab, transform.position, Quaternion.identity) as GameObject;
        var magia = obj.GetComponent();
        magia.velocidade = UnityEngine.Random.Range(1f, 10f);
    }

O que esse script faz? Bom, primeiro ele pega o prefab lá da magia. Depois nos criamos um objeto na mesma posição do boss, para então recuperar o script AtaqueSeguirJogador que tem nesse GameObject. Por fim mudamos a velocidade da magia usando a classe Random do Unity.

Na realidade existem duas classes Random. A do C# que é o System.Random e a do Unity que é a UnityEngine.Random, como nós temos tanto o System quanto o UnityEngine lá nos namespaces no inicio do nosso script, precisamos informar qual dos dois Random é que estamos usando. O Random do Unity é voltado a valores do tipo Float. E nesse Random estamos dizendo para pegar um valor aleatório entre 1 e 10 para atribuir a velocidade.

Bom, já fizemos o método lancarMagia, agora no método ataque temos eu verificar quando vamos chama-lo. Eu particularmente penso em chama-lo 3 vezes por ataque! Então os passos são fazer a contagem. Caso chegou no tempo determinado, iremos chamar o lancarMagia 3 vezes , porém com um atraso de um para o outro (O atraso podemos fazer através do método Invoke) e por fim reiniciamos a contagem:

Script: MagoAzul.cs

    protected override void atacar() {
        contagemAtaque -= Time.deltaTime;   //Reduz o tempo da contagem
        if (contagemAtaque <= 0) {          //Caso tenha completado o tempo, solta o ataque
            Invoke("lancarMagia", 0.1f);    //Lança uma mágia após 0.1 segundo    
            Invoke("lancarMagia", 1f);      //Lança uma mágia após 1 segundo    
            Invoke("lancarMagia", 2f);      //Lança uma mágia após 2 segundos    
            contagemAtaque = duracaoAtaque; //Reinicia a contagem
        }
    }

Com isso nosso inimigo já vai lançar 3 ataques em velocidade diferentes em direção ao jogador.

Para finalizar esse script, só precisamos fazer uma coisa. Fazer com que nosso boss se teletransporte! E isso vai acontecer quando o inimigo sofrer dano. Desta forma, vamos precisar alterar o método recebeDano lá da classe Inimigo, dizendo que ele é do tipo virtual, portanto, pode ser escrito de forma diferente em uma classe filha:

Script: Inimigo.cs

public virtual void recebeDano(int dano) {
...
}

Só adicionar o virtual após o public, não precisa alterar nada de dentro. Agora no script MagoAzul, iremos dizer que após o método recebeDano do Inimigo for executado (base.recebeDano), nosso inimigo vai se teletransportar para uma posição aleatória que definimos na variável posições! E para isso vamos precisar da classe Random do system, que trabalha com valores inteiros.

Script: MagoAzul.cs

    public override void recebeDano(int dano) {
        base.recebeDano(dano);

        if (!status.estaMorto()) { 
            var random = new System.Random();
            var tamanhoDaLista = posicoes.Count;
            var posicao = random.Next(0, (tamanhoDaLista - 1));
            transform.position = posicoes[posicao];
        }
    }

Vamos tentar entender o código. Primeiro a gente diz para ele receber o dano igualzinho como é na classe Inimigo (base.recebeDano(dano)). Depois no if verificamos se o personagem não está morto. Caso ele não esteja, criamos a classe Random (Do System) para gerar valores aleatórios inteiros! Lembram-se da nossa lista posições? Pegamos o total de itens nessa lista através do atributo “Count” (posicoes.Count). Ai vai algo que pode confundir a cabeça de algum de vocês.

O Count ele sempre vai contar o total de itens na lista! Mas ao tentar acessar o valor, a contagem começa do zero. Exemplo:

Item 1 da Lista – Posição 0
Item 2 da Lista – Posição 1
Item 3 da Lista – Posição 2

No total temos 3 itens, porém o Item 2 da lista nós o buscamos na posição 1, entenderam?

Desta forma a linha random.Next(0, tamanhoDaLista-1)), vai retornar um valor aleatório começa do zero, até um valor abaixo do tamanho da lista. No exemplo acima, retornaria um valor entre 0 e 2. Tendo esse valor, então é só lançar a posição da lista na posição do mago:

transform.position = posicoes[posicao];

Entenderam?

E com isso, terminamos esse script. Agora salve e volte ao Editor do Unity. Nele adicione o script MagoAzul ao objeto MagoAzul, informando o Animator do próprio MagoAzul, a distância do personagem para o mago surgir e a duração entre um ataque e outro:

17- Tutorial Fase 2 - Boss

Está vendo ali embaixo a nossa variável Posicoes? Clique na setinha e vai aparecer o tamanho (Size) da lista. Troque para 3 e aperte Enter:

19- Tutorial Fase 2 - Boss

3 Elementos do tipo Vector3 vão aparecer. Agora é só definir os locais que você quer que o Boss esteja.

20- Tutorial Fase 2 - Boss

Com isso, ao receber algum dano, ele irá se mover aleatoriamente para uma das 3 posições. Se quisesse adicionar outras posições, era só aumentar o tamanho da lista ^^.

Para finalizar esse script, salve o MagoAzul como um prefab.

21- Tutorial Fase 2 - Boss

E no Script GC Fase Lago informe o real Boss:

Agora é só voltar a Scene Main e testar seu jogo!

Hoje é um dia de chuva aqui na minha cidade que faz um calor do inferno quase o ano todo e também é um sábado. Então estou aqui fazendo esse tutorial com bastante disposição e me dá aquela vontade de pegar pesado nesse tutorial, adicionando uma música exclusiva para o boss ou uma barra de vida gigante na parte inferior, mas vou conter minha empolgação aqui. Caso alguém queira fica como desafio para vocês, mas a dica é. A barra de vida do Boss é igual ao do personagem, só mudaria a largura da barra e pegaria o Status do inimigo ao invés do jogador. E apenas ativaria o objeto junto com a música quando cruzasse a barra LimiteBoss.

Então quem quiser fica isso ai como desafio, porque o nosso tutorial termina por aqui! Qualquer dúvida, deixem nos comentários.

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

One comment

  1. Caio disse:

    Bom, como você deixou um desafio, tentei fazer ele!! Deu certo, por isso quero compartilhar como fiz e te perguntar o que achou e como você faria, para que possamos ter uma ideia de alguém que tem experiencia maior que a minha e que faria, de certa forma, da maneira correta, ou pelo menos mais correta possível ^^’
    A primeira coisa que fiz foi criar um novo script que o chamei de BossHUD e coloquei dentro dele isso:

    using UnityEngine;
    using System.Collections;
    using UnityEngine.UI;

    public class BossHUD : MonoBehaviour {

    public Slider sliderHP;
    private Status status;
    public GameObject limiteBoss;
    public GameObject painelHPBoss;
    public bool lutaBoss = false;

    void Start () {

    var boss = GameObject.FindWithTag(“Boss”).GetComponent();
    status = boss.getStatus();
    sliderHP.maxValue = status.getHPMax();
    painelHPBoss.SetActive(false);
    }

    void Update () {

    sliderHP.value = status.getHP();
    if (!limite()) {
    painelHPBoss.SetActive(true);
    }

    }

    public bool limite() {

    var collider = limiteBoss.GetComponent();
    return collider.isTrigger;

    }
    }

    Vendo ele da para ver que fiz algumas mudanças em varias coisas, como coloquei a tag Boss para ter mais facilidade de pegar o boss no Start e nao um inimigo qualquer, fiz o HUD do boss a la boss de Megaman, na lateral e grande, coloquei dentro do canvas e deixei ele desativado. Se, de alguma forma. ele estiver ativado antes de encontrar o boss, ele vai ser desativado no Start. Então, para ativa-lo usei o limiteBoss como referencia para saber se o personagem passou dele ou nao, através do isTrigger ativado/desativado. Bom tirando o que disse, é praticamente o que voce fez durante o tutorial, que por sinal eu adorei e digo muito obrigado por faze-lo, ele sem sombras de duvidas esta me ajudando muito a entender o Unity2D.

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!