Menu

Tutorial de Unity 2D #10 – Ataque Especial

Chegando ao 10º tutorial de Unity 2D! Dessa vez vamos aprender e criar o ataque especial do nosso personagem principal!

Bom, vocês praticamente já viram como solta um ataque quando fizemos os nossos inimigos, todavia farei esse ataque especial de forma diferente para mudar um pouco nosso tutorial.

O que vamos precisar nele é o seguinte sprite:

Explicando para vocês como vai funcionar a nossa magia. Essa bola ai que vocês estão vendo se forma em volta do personagem e depois explode acertando a todos os inimigos que estiverem perto dessa esfera.

Se vocês buscarem na internet verão que existem duas formas de fazer uma animação de ataque. A mais comum e particularmente a que eu considero mais fácil, basta usar um programa de edição e fazer as montagens nas imagens. Nesta situação eu teria que utilizar o seguinte sprite (União do meu personagem e do ataque acima):

Porém se a gente fizer isso, vocês não vão aprender quase nada nesse tutorial, então vamos tentar aprender a fazer a animação desse ataque especial de forma mais hardcore, ou seja, a animação do ataque especial separado da animação do personagem.

O primeiro passo e mais simples, sem tanta dor de cabeça é criar uma novo objeto chamado AtaqueEspecial, que ficará dentro do objeto Player.

Agora importem o sprite da magia para o Unity, mudem seu Texture Type para Sprite (2D/uGUI) e Sprite Mode para Multiple.

Na hora de realizar um slice através do Sprite Editor, tenham um pouco de cuidado para que os cortes fiquem mais ou menos do mesmo tamanho e com possíveis pequenos cortes que apareçam:

Agora, de volta ao Objeto AtaqueEspecial, vamos adicionar o componente Sprite Renderer com o primeiro sprite que acabamos de cortar.

Ainda com o Objeto AtaqueEspecial selecionado, vá na aba Animations e adicione os sprites da bola se formando até a bola formada (Antes dela começar a explodir) e salva na pasta animations/player com o nome player_magia.

Se totalizou 16 sprites e você mudar o sample para 10, significa que sua animação tem cerca de 1,5 (1:05 = 1,5) segundos:

A animação em si já está legal, mas eu quero fazer diferente. Clicando sobre o ultimo sprite da animação (keyframe, o losango na linha do tempo, ficará azul), aumente a escala do ataque em X e Y para 4:

Caso repare, apareceu uma nova camada na barra de animação chamada AtaqueEspecial: Scale, com dois keyframes, um na posição 1 outro na ultima posição. Se você clicar no player, vai ver que o ataque começa a crescer desde o primeiro keyframe, mas não é isso que eu quero. Eu quero que ele cresça a partir do penúltimo, então vou puxar aquele primeiro keyframe para a penúltima posição:

E vou afastar o ultimo keyframe para o crescimento ser mais lento (Totalizando 1,7 segundos):

Agora, sim adicionem as ultimas partes que estavam faltando em sua animação, a da bola explodindo:

Totalizando 2,6 segundos nosso ataque já esta alcançando uma boa região. Precisamos agora criar um Circle Collider 2D, para que quando o inimigo toque no ataque, seja destruído. Para isso, volte a clicar sobre o primeiro keyframe da sua animação e adicione um Circle Collider 2D:

A opção Is Trigger deve estar marcada e diminua o raio do seu Circle Collider para ficar de acordo com o tamanho da animação:

Avance agora para o keyframe que aumentando o tamanho da nossa animação e diminui um pouco mais o raio, pois quando aumentamos a escala (scale), o raio aumento também:

Agora indo para as ultimas animações, onde a bola explode, aumente o raio, para cobrir toda a animação:

Com isso, o Circle Collider já vai alterar de tamanho de acordo com a nossa animação. E todo objeto com a tag Inimigo (Se lembram da aula passada?) que tocar nesse collider será destruído.

Com nossa animação feita, é hora de criamos o script.

Crie um novo script (C# Script) na pasta scripts chamado AtaqueEspecial e abra o MonoDeveloper. Aqui será bem simples, um daqueles scripts que vocês já conseguem fazer sem minha ajuda.

Vamos apenas adicionar um Collider do tipo Trigger e verificar se o objeto tocado for do tipo Inimigo, então o destruímos:

	void OnTriggerEnter2D(Collider2D colisor) {
		if (colisor.gameObject.tag == "Inimigo") {
			Destroy(colisor.gameObject);
		}
	}

De volta ao Unity, clique no seu objeto AtaqueEspecial (Sem estar com nenhum keyframe selecionado, o AtaqueEspecial tem que estar azul, selecionado, e não cinza, ok? Pois se você estiver com um keyframe selecionado, você vai adicionar um script SÓ naquele keyframe em diante e nós queremos para todo o objeto) e adicione o Script AtaqueEspecial.

Dê play e teste o ataque especial (Ele vai estar sempre ativo), veja se ele já está destruindo os inimigos.

OBS: Se o seu inimigo não destruir, verifique se você colocou a tag Inimigo (Eu me esqueci de colocar no Skeleton).

Se você reparar, em alguns momentos a animação fica atrás do personagem e nós a queremos sempre na frente, então precisamos organizar isso, trocando a ordem do layer. Clique sobre o heroi_sprites e troque o Order in Layer no componente Sprite Renderer para zero, se já não estiver.

E no AtaqueEspecial, troque o valor para 1:

Quem tiver o maior valor sempre vai estar na frente de quem tiver o menor valor.

Se estiver tudo certo, agora eu vou pedir para que vocês desabilitem o item AtaqueEspecial. Para desabilitar basta clicar sobre ele na aba Hierarchy e na aba Inspector desmarcar o Checkbox no topo:

Se você fizer isso, ele desaparece até de cena. Dentro do Script do Player, vamos ativa-lo e desativa-lo sempre que apertamos o botão Ctrl.

Então de volta ao script do player adicione as seguintes propriedades:

	public GameObject ataqueEspecial;
	public float duracaoAtaque;
	private float contagemAtaque;

ataqueEspecial – Será o nosso objeto AtaqueEspecial, precisamos dele no nosso script para ativar e desativa-lo.

duracaoAtaque – O ataque ficará ativo enquanto a pessoa estiver clicando no botão OU até a animação completar, por isso passamos aqui o tempo da duração do ataque/animação. (Existe outra forma de fazer, mas estou tentando usar menos script possível, em outro tutorial eu posso até explicar, quando vocês estiverem mais maduros em programação de Unity, que ainda é a maior dificuldade de todos que me pedem ajuda).

contagemAtaque – Irá verificar se já atingiu o tempo da animação e caso tenha completado, desativa o objeto AtaqueEspecial.

Após adicionar essas três propriedades no inicio do script, vamos atualizar o método Update. Adicione um simples método chamado Ataque.

void Update () {
		Movimentacao();
		Ataque ();
	}

E agora vamos criar o método Ataque:

void Ataque() {
		if (Input.GetButtonDown ("Fire1")) {
			ataqueEspecial.SetActive(true);
			contagemAtaque = 0f;
			PerdeVida(20);
		}

		if (Input.GetButton ("Fire1")) {
			contagemAtaque += Time.deltaTime;
			if (contagemAtaque >= duracaoAtaque) {
				ataqueEspecial.SetActive(false);
			}
		}

		if (Input.GetButtonUp ("Fire1")) {
			ataqueEspecial.SetActive(false);
		}
	}

Eu poderia dizer que nosso ataque vai executar até o final uma vez que o botão for clicado, mas eu quero que vocês vejam a usabilidade do GetButtonDown, GetButton, GetButtonUp que já os expliquei no tutorial de programação para Unity.

Vamos lá:

GetButtonDown – O ocorre no exato momento em que se clica no botão. E só ocorre uma única vez por clique.

GetButton – Ocorre enquanto se estiver pressionando o botão.

GetButtonUp – Ocorre quando se deixa de clicar no botão.

O botão “Fire1” é o nosso botão Ctrl do lado esquerdo ou botão esquerdo do mouse. Eu mostrei para vocês acho que no tutorial de movimentação e no tutorial de programação, onde você ver e gerencia os botões do seu jogo, mas para quem não se lembra, basta ir Edit >> Project Settings >> Input:

Agora explicando o nosso código:

if (Input.GetButtonDown (“Fire1”)) {
      ataqueEspecial.SetActive(true);
      contagemAtaque = 0f;
      PerdeVida(20);
}

Aqui estamos dizendo que no exato momento que clicar no botão Fire1, iremos ativar o nosso objeto AtaqueEspecial, iremos perder 20 de vida (É, nesse jogo não tem Mana, então para executar uma magia você pagará com a vida igual ao jogo Street of Rage) e iniciamos a contagem da duração da animação do zero, pois o objeto acabou de ser ativado.

if (Input.GetButton (“Fire1”)) {
      contagemAtaque += Time.deltaTime;
      if (contagemAtaque >= duracaoAtaque) {
            ataqueEspecial.SetActive(false);
      }
}

Enquanto nós estivermos pressionando o botão, iremos fazer a verificação da contagem do tempo, para que quando atingir o limite (Duracao da animação do Ataque Especial), ele seja desativado, para não ficar repetindo a animação infinitamente.

if (Input.GetButtonUp (“Fire1”)) {
      ataqueEspecial.SetActive(false);
}

E caso o jogador deixe de apertar o botão, o Ataque Especial será cancelado (desativado), mesmo que ainda não tinha sido completado.
Bom, basicamente é isso, agora volte a o Unity e no script do Player, adicione o Ataque Especial e Duracao Ataque.

Agora é dar play e se divertir com mais uma etapa do seu jogo concluído.

Dúvidas, sugestões e reclamações, deixem nos comentários. 😉

E até semana que vem com o tutorial de Áudio, onde vamos por uma música no jogo e um no nosso ataque especial e no ataque dos monstros!

Player.cs

using UnityEngine;
using System.Collections;

public class Player : MonoBehaviour {

	public float velocidade;
	public float forcaPulo;
	private bool estaNoChao;
	public Transform chaoVerificador;
	public Transform spritePlayer;
	private Animator animator;

	//Ataque Especial
	public GameObject ataqueEspecial;
	public float duracaoAtaque;
	private float contagemAtaque;

	//Vida
	public GameObject vida;
	public int maxVida;
	private int vidaAtual;

	//Tudo que ocorre quando o personagem e criado
	void Start () {
		animator = spritePlayer.GetComponent ();

		//Vida
		vidaAtual = maxVida;
		vida.guiText.color = new Vector4(0.25f, 0.5f, 0.25f, 1f);
		vida.guiText.text = "HP: " + vidaAtual + "/" + maxVida;
	}
	
	//Tudo que ocorre enquanto o personagem existe
	void Update () {
		Movimentacao();
		Ataque ();
	}

	//Responsavel pela movimentacao do personagem
	void Movimentacao() {

		//Seta no paramentro movimento, um valor 0 ou maior que 0. Ira ativar a condicao para mudar de animacao
		animator.SetFloat("movimento", Mathf.Abs (Input.GetAxisRaw ("Horizontal")));

		//Anda para a direita
		if (Input.GetAxisRaw("Horizontal") > 0 ) {
			transform.Translate (Vector2.right * velocidade * Time.deltaTime);
			transform.eulerAngles = new Vector2(0, 0);
		}

		//Anda para a esquerda
		if (Input.GetAxisRaw("Horizontal") < 0 ) {
			transform.Translate (Vector2.right * velocidade * Time.deltaTime);
			transform.eulerAngles = new Vector2(0, 180);
		}

		//Verifica se esta no chao
		estaNoChao = Physics2D.Linecast(transform.position, chaoVerificador.position, 1 << LayerMask.NameToLayer("Piso"));
		animator.SetBool ("chao", estaNoChao);

		//Responsavel pelo pulo
		if (Input.GetButtonDown("Jump") && estaNoChao) {
			rigidbody2D.AddForce(transform.up * forcaPulo);
		}
	}

	public void PerdeVida(int dano) {
		vidaAtual -= dano;

		if (vidaAtual <= 0) {
			Application.LoadLevel(Application.loadedLevel);
		} 

		if ((vidaAtual * 100 / maxVida) < 30) {
  			vida.guiText.color = Color.red;
  		}
  		vida.guiText.text = "HP: " + vidaAtual + "/" + maxVida;
  	}

  	public void RecuperaVida(int recupera) {
  		vidaAtual += recupera;
  		if (vidaAtual > maxVida) {
			vidaAtual = maxVida;
		}

		if ((vidaAtual * 100 / maxVida) >= 30) {
			vida.guiText.color = new Vector4(0.25f, 0.5f, 0.25f, 1f);
		}

		vida.guiText.text = "HP: " + vidaAtual + "/" + maxVida;
	}

	void Ataque() {
		if (Input.GetButtonDown ("Fire1")) {
			ataqueEspecial.SetActive(true);
			contagemAtaque = 0f;
			PerdeVida(20);
		}

		if (Input.GetButton ("Fire1")) {
			contagemAtaque += Time.deltaTime;
			if (contagemAtaque >= duracaoAtaque) {
				ataqueEspecial.SetActive(false);
			}
		}

		if (Input.GetButtonUp ("Fire1")) {
			ataqueEspecial.SetActive(false);
		}
	}
}

AtaqueEspecial.cs

using UnityEngine;
using System.Collections;

public class AtaqueEspecial : MonoBehaviour {

	// Use this for initialization
	void Start () {
	
	}
	
	// Update is called once per frame
	void Update () {
	
	}

	void OnTriggerEnter2D(Collider2D colisor) {
		if (colisor.gameObject.tag == "Inimigo") {
			Destroy(colisor.gameObject);
		}
	}
}

Parte 9 – Inimigo parte 2

Indice Unity 2D

Parte 11 – Áudio

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

23 comments

  1. Paulo disse:

    Ola Carlos!
    É possível dares uma dica de como fazer o player pular em cima de um inimigo e ele morrer, tipo Mario e outros clássicos de plataformas?
    Gd Abraço

    ps: Parabéns por mais um ótimo tutorial, adorei…

    • e aê paulo, belezinha?

      É mais simples do que parece. Não tem o chaoVerificador? Basta você adicionar um collider nele, que quando tocar em um objeto com a tag inimigo, destrua esse objeto (UM script quase igual ao ataque especial. só que ao invés de ser um OnTriggerEnter2D seria um OncollisionEnter2D ^^

  2. Paulo disse:

    Pois é com o OnCollisionEnter2D, não deu certo porque entra em conflito com o chão e as animações, mas funcionou com o OnTriggerEnter2D, no entanto também levo dano, looool.
    Não consigo consertar….

    • perdão é trigger mesmo, confundi aqui.

      Bom, se você levou dano significa que o inimigo ainda tocou em você, neste caso, pode tentar aumentar o collider ou afastar um pouco mais o chaoVerificador do personagem

  3. Marcos Cruz disse:

    Fala Carlos mais uma vez você faz um ótimo trabalho, obrigadão mesmo, com esta excelente aula da até pra arriscar fazer, um script onde o player possa se transforma em um SS1, Bankai(Bleach) ou no incrível Hulk xD

    E seguindo a linha de raciocínio do paulo, pra alguem como eu que ainda esta engatinhando neste mundo de programação, fazer um sistema de combo para o player, e os inimigos com vida, tipo os classicos “hack and slash” do super nintendo,

    EU consegui alguns Script de foruns gringos, mas tanto minha barra de vida quanto a do inimigo fica um embaixo uma da outra, e ainda tem o problema de que, se eu adicionar, digamos 5 inimigo a mais as 5 barra de vida, a minha e a dos inimigos ficam uma em baixa da outra, tipo elas não some mesmo que eu zere a vida deles.

    O outro problema é que, todos os inimigos tem a tag “Inimigo”, então supondo que tem 2 inimigos um na frente e o outro atrás do player, mas ambos estão estão do raio do ataque, mas o ataque é um simples movimento frontal, ou seja deveria causar dano quem estar a sua frente né? Mas o inimigo infeliz que esta atrás sofre com o dano também. Bem bizarro e muito LOKO xD

    • vixe, vocês gostam de texto grande, querem se vingar dos meus posts gigantes =P

      Bora lá, o primeiro problema:
      1 – Possivelmente terá seu script terá que fazer referência ao objeto da barra de vida (Pode ser um public ou realizar a busca pelo script mesmo). Se ele já faz o papel de diminuir a barra, então já está praticamente feito, só vai precisar adicionar isso aqui:

      void OnDestroy() {
      Destroy(barraVida);
      }

      OU seja, quando o inimigo for destruído, ele também vai destruir a barra de vida que o representa

      2 – Nesse tutorial ai que eu fiz, o ataque realmente é para todos os lados. Mas pelo que eu entendi, você quer um que seja só frontal correto? Então ao invés de usar um Circle Collider 2D, pode usar um Box Collider 2D e posiciona-lo a frente do personagem (Usando a opção trigger também para não chocar em nada)

      Traaanquilo? o.o7

  4. Victor Henrique Magalhães Fernande disse:

    Olá Carlos, mais uma vez, um excelente tutorial, parabéns!!
    É o seguinte, eu estou cursando a matéria de Orientação a objetos na faculdade, e no final do curso agora estou fazendo um projeto final, e pensei, por que não fazer um jogo?? Sempre gostei dessa área e estava com ótimas ideias para o jogo. Coincidentemente pesquisando na internet achei o seu perfeito tutorial que fazia justamente o que eu queria, que era um jogo de plataforma ao estilo de super Mario, então segui a fundo seus tutoriais até conseguir tomar meus próprios passos. Como eu tinha apenas algumas semanas pra esse projeto, pensei num jogo pequeno de apenas duas fases, então com o decorrer do tempo fui me adiantando, hoje meu jogo já tem toda a interface gráfica, menu , transição das cenas, game over, toda essa parte gráfica e claro, usei minha criatividade nas fases para espalhar os objetos, os inimigos, cenários novos e poder criar duas fases bastante legais. Porém o que eu realmente eu to precisando agora e não tenho ideia de como fazer, é o sistema de save/Load, que como você colocou será o tutorial do dia 12/12, e eu tenho que apresentar esse projeto no dia 02/12, então não poderei esperar até o dia 12. Aí eu queria saber se você não pode me dar uma força, me adiantar o conteúdo pra eu pelo menos ter uma noção de como eu faço pra salvar um cena e carregar a mesma de onde foi salva.
    Grato desde já, e mais uma vez Parabéns pelos Tutoriais!

    • Vocês é quem mandam aqui!

      Manda teu email, que eu envio para ti 😉

      Vai se surpreender como é facil. O problema é que o tutorial usa como base a parte de gráfico e mudar de fase, todavia você disse que já fez isso, então deve conseguir entender o que eu escrevi nessa parte. Qualquer coisa que não entender no tutorial é só me enviar por e-mail [email protected]

    • Ah e desculpa a demora na resposta, é que estou meio que com a corda no pescoço no meu mestrado.

      Três seminários e uma lista de exercício com tempo muuuuito curto para fazer e o professor é do tipo que cobra pesado (Não que isso seja ruim, mas tem me deixado bastante ocupado).

      E outro fator é o Jogos Indie trocou de servidor na madrugada de segunda pra terça, mas apenas hoje foi que a NET resolveu atualizar o DNS e eu poder ver o site no novo servidor.

      Normalmente respondo vocês no mesmo dia, mas por causa desses motivos complicou bastante

    • Victor, não estou conseguindo enviar nada para o seu email. Já tentei de dois diferentes e nada. Existe outro email que eu possa enviar?

  5. Deidara Sama disse:

    Olá Carlos! \o/ exelente tutorial cara! \o/ e bem,eu gostaria de esclarecer uma duvida….como seria basicamente o script que eu teria que fazer para quando o meu player colidisse com um objeto por exemplo,um cubo,e ele instanciar por exemplo uma moeda,em outro lugar que não seja esse cubo…por exemplo,o player colidir como cubo A e instanciar a ”moeda” no cubo B?…grato desde já! \o/ mais uma vez,parabéns pela serie de tutoriais! \o/

  6. Cara, você ta de parabéns…seus tutorials são muito bons! 😀

  7. matheus63518 disse:

    ola eu gostaria de saber como seria se o ataque fosse uma animaçao do personagem que vc controla, que no caso estou fazendo um jogo em que o personagem faria uma especíe de combo de ataque fisico

  8. carloswgama disse:

    Opa Matheus.

    Acho que o que você quer é o mesmo caso do inimigo esqueleto, não?
    http://jogosindie.com/tutorial-de-unity-2d-inimigo-parte-1/

  9. Quando eu adiciono um novo animator – atacando – no personagem, a câmera não fica mais focado nele, quando o personagem se move, ela fica parada. Como resolve isso?

    • E junto está dando esse erro aqui:
      UnassignedReferenceException: The variable Atacando of Player has not been assigned.
      You probably need to assign the Atacando variable of the Player script in the inspector.
      Player.Ataque () (at Assets/Scripts/Player.cs:131)
      Player.Update () (at Assets/Scripts/Player.cs:40)

      • Consegui ajustar, fala só arrumar a merda da câmera que não quer seguir o personagem.. Wtf.. Talvez o erro seja no animator, mas não sei mexer nessa merda. Criei um novo parâmetro de valor booleano atacando e com as condições e pa, mas não funfa a câmera nem o ataque. Quero fazer uma bola de magia sair do personagem quando ele clica no botão esquerdo do mouse.
        Se puder dar uma força pra arrumar esse bug da câmera e do ataque.

  10. rafoso disse:

    Coisa linda esse tutorial!!!

  11. wellington disse:

    como faço pra magia sair uma só vezes e para, já tentei algumas variações mais nd deu certo

    • Como assim só uma vez? Só poder atacar uma vez e não poder atacar mais?

      Se for, cria uma variavel do tipo booleana para checar se a já atacou ou não. Quando ataca, verifica se já atacou. Caso não, libera o ataque e muda o valor da variavel para já atacou.

  12. wellington disse:

    pode escrever esse script da variavel booleana pra eu entender melhor

    • wellington você vai criar uma nova variavel do tipo booleana:

      private bool podeAtacar = true;

      e no update você coloca a condição:

      void Update () {
      Movimentacao();
      if (podeAtacar) {
      Ataque ();
      podeAtacar = false;
      }
      }

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!