Menu

16 – Tutorial Intermediário – Cenário 2 – Lago

jogoJá completamos uma fase, já fizemos tela de loading, então basicamente nosso jogo já está pronto. Os próximos posts serão apenas formas diferentes de fazer inimigos e fases, mas basicamente segue tudo a mesma linha. Então serão posts beeem mais leve.

 Bom, como é uma fase aquática, o nosso background será a seguinte imagem disponibilizada por Game Developer Studio no site OpenGameArt:

http://opengameart.org/content/underwater-background-1

Você já pode adicionar esse background com o nome lago_background dentro da pasta Resources/sprites/tiles/fase2:

01- Tutorial Cenario 2

Quanto aos pisos, tetos e tudo mais, serão algo um pouco diferente de um lago… Na real não achei nenhum sprite legal para montar o cenário do lago, então se a vida te dá um limão, faça uma limonada! A fase aquática do sonic, senão me lembro é a ruina de um templo (Posso estar enganado, faz décadas desde que joguei e vi Sonic), então o nosso será uma base aquática com tecnologia avançada!

Então usaremos os sprites Sci-Fi Platform do GameArt2D :

Link para o site

Link para o download

Basicamente o que iremos usar são:

Objects/Saw.png — renomear para —> lago_serra (Sprite)
Objects/Switch (1).png — renomear para —> lago_interruptor1 (Sprite)
Objects/Switch (2).png — renomear para —> lago_interruptor2 (Sprite)
Tiles/Tile (2).png — renomear para —> lago_plataforma (Textura)
Tiles/Tile (5).png — renomear para —> lago_parede (Textura)
Tiles/Tile (13).png — renomear para —> lago_plataforma_one_way (Textura |Alpha Is Trasnparent)
Tiles/ BGTile (3).png.png — renomear para —> lago_background_interno (Textura)

Caso você queira deixar mais bonitinho pode usar os sprites das bordas da parede e plataforma. Aqui eu não usarei para não tornar o tutorial tão grande, afinal já vimos isso no primeiro cenário.

Ainda teremos mais alguns objetos, uma “porta”, baseada em eletricidade disponibilizado no OpenGameArt:

http://opengameart.org/content/animated-sci-fi-gateway-electric-trap

“Vish, Carlos, não tem nada a ver eletricidade na agua”

Concordo, eu queria mesmo aquelas lanças que ficam subindo e descendo, mas não achei nenhum sprite legal e isso é apenas um tutorial de um jogo não precisa fazer sentido .-., afinal um macaco com gravada nadando no mar sem respirar e pegando carona em um peixe espada, também não faz tanto sentido, sem falar em plataformas que voam e se mexem sozinhas na mesma direção O.o.

Ah e quanto ao que iremos usar desses sprites são eletricity 1, eletricity 2, electricity 3 com os nomes: lago_eletricidade1, lago_eletricidade2, lago_eletricidade3 e o “Lights on.png” com o nome “lago_portao”, todos sendo sprites.

Feito isso já podemos criar uma nova Scene com o nome Fase2. Nós já vimos como montamos o nosso mapa, então aqui basicamente eu não vou ensinar nada, apenas dizer o que vocês devem fazer para a gente ganhar tempo e ver as coisas novas, ok? Vamos a lista então:

1 – Criar um Material BackgroundLago (usar a textura lago_background) e BackgroundBase (lago_background_interno) com a opção Unlit/Texture.

02- Tutorial Cenario 2

2 – Na nossa aba Hierarchy criaremos dois objetos um Background e um BackgroundBase. Para ambos adicionaremos o Mesh Filter (Quad) e o Mesh Renderer com o material correspondente. O Background será a imagem mais ao fundo (posição Z = 10) e a mais larga. Já o BackgroundBase está mais na frente (posição Z = 5) e da metade para o final da fase. A ideia é que antes do portão de eletricidade seja um local aberto (lago) e após ele, seja a base (o local interno):

03- Tutorial Cenario 2

Use os Tillings dos materials para não deixar a imagem esticada.

Agora iremos criar os Materials: PlataformaLago (textura: lago_plataforma), PlataformaOneWayLago (textura: lago_plataforma_one_way), ParedeLago (textura: lago_parede). Todos com o Shader Unlit/Texture com exceção do PlataformaOneWayLago que será Unlit/Transparent.

09- Tutorial Cenario 2

Depois dos materials prontos, é hora e criar os objetos na nossa Scene. Vamos primeiro criar um objeto Piso, com o material PlataformaLago e o posicionaremos logo embaixo do cenário com o Box Collider 2D e com a Layer Piso e a posição de Z = 0 (Ou seja, na frente dos Background):

10- Tutorial Cenario 2

Agora vamos duplicar esse Piso e mudar seu nome para teto e o locaremos na parte superior do mapa, só que com a rotação no eixo Z invertida (180 graus):

11- Tutorial Cenario 2

Nosso cenário já está ficando mais ou menos. Vamos adicionar um novo GameObject chamado Parede com o Material ParedeLago. Esse objeto ficaria no inicio do BackgroundBase, mas não vai até o final, pois o final é onde vai acontecer a luta contra o boss (Ajuste os Tilings para um valor que achar necessário para ficar legal):

08- Tutorial Cenario 2

Nas paredes adicione também o Box Collider 2D, para que o personagem não consiga passar por dentro da parede.

12- Tutorial Cenario 2

Bom, já podemos criar um objeto chamado PlataformaOneWay, onde vamos adicionar o Mesh Filter (Quad), Mesh Renderer (PlataformaOneWayLago), Box Collider 2D (Com o Used By Effector), Platform Effector 2D (com Use One Way), a Layer Piso e o coloque ao lado da parede:

13- Tutorial Cenario 2

Caso o seu objeto fique com um tracinho embaixo:

14- Tutorial Cenario 2

Mude o Offset de Y do Material para 0.01 para que esse traço vá para cima com a parte superior da imagem:

15- Tutorial Cenario 2

Duplique essa plataforma duas vezes e a coloque lá no local onde será batalha contra o boss:

16- Tutorial Cenario 2

Com isso a base do nosso cenário já está toda feita! Agora vamos as novidades que terão exclusivamente nesse cenário.

Primeiro vou pedir para vocês baixarem essa imagem aqui:

lago_agua

Ela está disponível nesse link na OpenGameArt:

http://opengameart.org/content/water-0

Todavia, usem o meu, pois eu apliquei uma transparência na imagem com o photoshop. A ideia dessa imagem é que ela fique na frente de tudo, dando a impressão que o conteúdo realmente está embaixo d’agua e ele será um material. Há como criar um material como sprite para poder aplicar a transparência pelo próprio Unity? Há, mas se você usar um material com o Shader Sprite, você não vai conseguir usar o Tiling e nem o Offset como queremos fazer no nesse conteúdo, então usem a minha imagem já com a transparência aplicada, ok?

Após baixar, adicionem a imagem com o nome lago_agua.png na pasta Resources/sprites/tiles/fase2. Essa imagem será uma textura, ok? Em seguida, crie um Material chamado AguaLago com o Shader Unlit/Transparent e escolha a nossa imagem:

17- Tutorial Cenario 2

Agora é bem simples. Basta criar um Objeto chamado Foreground (Foreground é o posto do Background, ou seja, se background fica lá no fundo, o Foreground vai ficar logo na frente). Nesse objeto iremos adicionar o Mesh Filter (Quad), Mesh Renderer (AguaLago), o esticaremos para cobrir todo o cenário, e mudaremos a sua posição em Z para -5 (Assim, ele ficará na frente dos outros objetos, mais próximos da câmera):

19- Tutorial Cenario 2

Com isso o cenário já fica com uma cara melhor de que está embaixo d’agua. Para fazer um efeito melhor, altere os Tiling da imagem e adicione aquele Script Scrolling que serve para girar o material, que criamos no menu, lembra?

21- Tutorial Cenario 2

Se quiser você pode adicionar um prefab do seu personagem ai no cenário e ir andando com ele para testar o tamanho e posição dos objetos, para ver se ficarem legais.

Se você perceber que sua aba Hierarchy está ficando muito grande, você pode criar um objeto vazio com um nome qualquer (No meu caso Cenario), para guardar todos os itens que montam o cenário:

22- Tutorial Cenario 2

Se nós tivéssemos criado o cenário pelo Photoshop ou outro editor qualquer, aqui a gente só precisaria basicamente colocar os colliders, o que ternário o processo um pouco mais fácil. Os próximos objetos que eu vou criar não são exatamente a parte visual do cenário e sim objetos de interação, por isso vou deixa-los fora do objeto (Cenario) que criei para organizar, ok?

]O primeiro objeto será chamado de Interruptor, que terá o componente Sprite Renderer com o Sprite lago_interruptor2, posicionado na primeira PlataformaOneWay e com o Box Collider 2D com a opção Is Trigger selecionada:

24- Tutorial Cenario 2

A ideia é que quando o personagem encoste esse interruptor, ele seja ativado, liberando o caminho para dentro da base interna.

Agora vamos precisar criar o portão! Então crie um objeto chamado Portao, com o componente Sprite Renderer e o sprite lago_portao. O posicione logo na entrada da base e se preciso ajuste a sua escala sem estragar a imagem:

25- Tutorial Cenario 2

Agora dentro desse objeto crie outro objeto chamado Eletricidade. Nele adicione o Sprite Renderer com o primeiro Sprite da eletricidade e adicione o Box Collider 2D:

26- Tutorial Cenario 2

Bom, nossa eletricidade não pode ficar parada, então vamos criar uma animação para ela. Clicando sobre o objeto Eletricidade, vá na aba Animation e peça para criar uma nova animação dentro da pasta Resources/animations/objetos/eletricidade:

27- Tutorial Cenario 2

Nesta animação, basta joga os 3 sprites da animação e definir a velocidade na opção Sample:

28- Tutorial Cenario 2

Prontinho. Estamos quase terminando de montar nosso cenário, para depois irmos para os scripts.

Vamos agora criar um objeto chamado Serra e nele adicionaremos o Sprite Renderer com o sprite lago_serra e o posicionaremos dentro da entrada da base:

29- Tutorial Cenario 2

Reparem que também diminui o tamanho (Scale) do objeto, pois nosso personagem vai precisar pular por cima dessa serra. Por fim adicione um Circle Collider 2D:

30- Tutorial Cenario 2

E pronto, terminamos de montar nosso cenário.

Agora precisaremos dá vida a tudo que criamos, não é mesmo? O primeiro será o interruptor, então vamos criar um script chamado Interruptor dentro da pasta scripts/cenarios:

31- Tutorial Cenario 2

Neste script vamos precisar uma variável publica do tipo Sprite, para mudar o sprite do interruptor quando ficar ativo. Precisaremos também de outra variável publica que irá receber o objeto eletricidade, para desativa-la quando o interruptor for encostado:

Script: Interruptor.cs

using UnityEngine;
using System.Collections;

public class Interruptor : MonoBehaviour {

    public Sprite interruptorAtivo;     // Sprite do Interruptor ativo
    public GameObject bloqueio;         // Desativa o objeto que bloqueia a passagem
}

Agora através do método OnTriggerEnter2D, vamos verificar se o personagem tocou. Caso sim, liberamos a passagem e mudamos o sprite do interruptor:

Script: Interruptor.cs

using UnityEngine;
using System.Collections;

public class Interruptor : MonoBehaviour {

    public Sprite interruptorAtivo;     // Sprite do Interruptor ativo
    public GameObject bloqueio;         // Desativa o objeto que bloqueia a passagem 

    void OnTriggerEnter2D (Collider2D colisor) {
        if (colisor.gameObject.tag.Equals("Player")) {
            GetComponent().sprite = interruptorAtivo; //Muda o Sprite
            bloqueio.SetActive(false);                                //Desativa o objeto que está bloqueando passagem
        }
    }
}

Agora é só voltar ao Editor do Unity e adicionar o script Interruptor ao objeto Interruptor, informa o sprite do interruptor ativo (lago_interruptor1) e o objeto que está bloqueando a passagem:

32- Tutorial Cenario 2

Joia! Agora vamos para o próximo script, o da Serra Assassina! Criem um Script chamado Serra dentro da pasta scripts/cenario. Nesse script vamos definir o dano causado ao player ao encostar-se a serra, a posições de onde ele inicia até onde ela vai, a direção para onde ela está indo:

Script: Serra.cs

using UnityEngine;
using System.Collections;

public class Serra : MonoBehaviour {

    public float velocidade;        //Velocidade da movimentação
    public int dano;                //Dano causado ao encostar
    public Vector3 posicaoInicial;  //Posição inicial do objeto
    public Vector3 posicaoFinal;    //Posição até o objeto vai
    public bool inicio;             //Direção que está indo, se é pro inicio ou pro fim
}

No método Update, vamos verificar para qual lado ele está indo e fazer ele se mover até chegar lá através do método MoveToward:

Script: Serra.cs

	void Update () {
        //Move a Serra
	    if (inicio) {
            transform.position = Vector3.MoveTowards(transform.position, posicaoInicial, velocidade * Time.deltaTime);

            if (transform.position == posicaoInicial)
                inicio = false;
        } else {
            transform.position = Vector3.MoveTowards(transform.position, posicaoFinal, velocidade * Time.deltaTime);

            if (transform.position == posicaoFinal)
                inicio = true;
        }
    }

É basicamente o que já fizemos várias vezes. O script verifica o lado; move para direção; e depois verifica se chegou ao destino para inverter a direção. Porém uma serra que não gira, não é uma serra é? Então vamos pegar a rotação através do eulerAngles, e girar a rotação no eixo Z:

var rotacao = transform.eulerAngles;
rotacao.z += velocidade * 100 * Time.deltaTime;
transform.eulerAngles = rotacao;

Multiplicamos a velocidade vezes 100 para a rotação não ser tão lenta. Com isso já deve funcionar, porém para ficar mais fácil da gente ver a rotação e não estourar o limite da rotação, podemos verificar que se a rotação passou de 360 (Deu um giro completo), removemos 360 do seu valor. Com isso método Update fica sendo:

Script: Serra.cs

	void Update () {
        //Move a Serra
	    if (inicio) {
            transform.position = Vector3.MoveTowards(transform.position, posicaoInicial, velocidade * Time.deltaTime);

            if (transform.position == posicaoInicial)
                inicio = false;
        } else {
            transform.position = Vector3.MoveTowards(transform.position, posicaoFinal, velocidade * Time.deltaTime);

            if (transform.position == posicaoFinal)
                inicio = true;
        }

        //Gira a Serra
        var rotacao = transform.eulerAngles;
        rotacao.z += velocidade * 100 * Time.deltaTime;
        if (rotacao.z > 360)
            rotacao.z -= 360;
        transform.eulerAngles = rotacao;
    }

Simples, né? Por fim, algo que já fizemos várias e várias vezes. Criar um método OnCollisionStay2D, que verifique se encostou no player. Caso tenha encostado, então causa dano:

Script: Serra.cs

    void OnCollisionStay2D(Collision2D colisor) { 
        if (colisor.gameObject.tag.Equals("Player")) {
            var personagem = colisor.gameObject.GetComponent();
            personagem.recebeDano(dano);
        }
    }

Já acabou o script da serra, agora é só voltar ao editor do Unity, adicionar o script ao objeto Serra informando os valores:

34- Tutorial Cenario 2

Interruptor, portão e serra já estão ok. Caso queira usar em outros cantos, pode criar um prefab para eles.

Com quase tudo pronto, agora só precisamos dá o toque final na fase. Vamos criar um Objeto canvas e dentro dele adicionar os prefabs HUD e Pause:

35- Tutorial Cenario 2

Adicionaremos os objetos Limites no cenário (LimiteEsquerdo, LimiteDireito, LimiteBoss) como fizemos no cenário 1 (Com o Box Collider 2D e o Layer Limites):

36- Tutorial Cenario 2

Lembrando que o LimiteBoss tem o EventoBoss para evitar que o personagem volte e seu Box Collider 2D tem que estar com a opção Is Trigger marcada:

37- Tutorial Cenario 2

Na Camera, vamos adicionar o Script GCCamera definindo os limites do cenário e a velocidade da câmera:

38- Tutorial Cenario 2

Por fim crie um objeto GC para receber o script que gerenciar a fase, mas antes vamos fazer uns ajustes. Eu estava aqui pensando, em algum tutorial passado eu coloquei que quando o player morresse, reiniciava a fase, correto? Mas eu fiz isso dentro do Player, sendo que isso é uma responsabilidade do controlador da fase verificar isso. Então… vamos alterar (Desculpa, mas é que eu não planejo o jogo antes, apenas faço no tutorial >.<).

No Script Jogador no método recebeDano, vamos remover aquele conteúdo dentro do status.estaMorto(),deixando apenas assim:

Script: Jogador.cs

    public void recebeDano(int dano) {
        if (invencibilidade <= 0f) {
            status.sofrerDano(dano);

            if (status.estaMorto()) 
                animator.SetTrigger("morreu");
            else                
                invencibilidade = 3f;
        }   
    }

Na Interface IPersonagem, vamos criar um método para retornar o animator:

Script: IPersonagem.cs

using UnityEngine;
using System.Collections;

public interface IPersonagem {
    Status getStatus();
    void recebeDano(int dano);
    Sprite getSprite();
    string getNomePrefab();
    IHabilidade getHabilidade1();
    IHabilidade getHabilidade2();
    IHabilidade getHabilidade3();
    Animator getAnimator();
}

Agora novamente no script Jogador, vamos criar o método que retorna o Animator:

Script: Jogador.cs

    public Animator getAnimator() {
        return animator;
    }

Agora sim vamos alterar o Script GCFase. Nele vamos adicionar uma nova variável booleana chamada reiniciar para verificar se a fase já vai reiniciar:

Script: GCFase.cs

using UnityEngine;
using UnityEngine.SceneManagement;
using System.Collections;

public class GCFase : MonoBehaviour {

    public Vector3 posicaoInicial;
    public int nivel;
    private Transform player;
    public GameObject boss;
    private bool reiniciar = false;
...

Agora no Update, vamos verificar se o personagem morreu. Caso sim, buscamos o tempo da animação e invocamos o método para reiniciar fase de forma que ele não invoque o método outras vezes no Update:

Script: GCFase.cs

    void Update() {
        if (boss == null)
            finalizarFase();

        var personagem = GameObject.FindGameObjectWithTag("Player").GetComponent();
        if (personagem.getStatus().estaMorto() && !reiniciar) {
            var duracaoAnimacao = personagem.getAnimator().GetCurrentAnimatorStateInfo(0).length;
            Invoke("reiniciarFase", duracaoAnimacao);
            reiniciar = true;   //Evita que entre nesse if outras vezes
        }
    }

“Pronto Carlos, já posso adicionar esse script ao objeto GC da fase?”

Ééééé, não. Tipo, nossa fase já está com cara de agua por causa do Background e Foreground, porém na agua seu movimento é mais lento, não é? Então que tal criar outro script só para diminuir a velocidade do nosso personagem? Quem não quiser fazer tem problema não, pode adicionar o GCFase ao objeto GC e já estará pronto, mas quem quiser, então cria um outro objeto chamado GCFaseLago dentro da pasta scripts/controllers.

Esse script vai herdar GCFase, então ele será bem pequeno, pois todo o trabalho tudo já estará sendo feito pelo GCFase. Aqui no método Start apenas vamos dizer que a gravidade e a velocidade do personagem vão cair pela metade:

Script: GCFaseLago.cs

using UnityEngine;
using System.Collections;

public class GCFaseLago : GCFase {

	void Start () {
        var objPlayer = GameObject.FindGameObjectWithTag("Player");
        //Diminui a gravidade
        var rigidbody2D = objPlayer.GetComponent();
        rigidbody2D.gravityScale *= 0.5f; //Cai pela metade
	}
}

O que fazemos nesse script é buscar o personagem e depois o componente Rigidbody2D dentro dele. Por fim dizemos que a gravidade cairá pela metade através do *=. Eu já expliquei isso eu acho, mas para caso você não lembre isso nada mais é do que igual a isso:

valor *= 2;
valor = valor * 2;

Ou seja, é o próprio valor multiplicado pelo que vem do lado direito. E qualquer número multiplicado por 0.5 é a sua metade.

Para a gente conseguir diminuir a velocidade do personagem, teremos que fazer mais alterações na Interface IPersonagem para buscar e setar a velocidade do personagem:

Script: IPersonagem.cs

using UnityEngine;
using System.Collections;

public interface IPersonagem {
    Status getStatus();
    void recebeDano(int dano);
    Sprite getSprite();
    string getNomePrefab();
    IHabilidade getHabilidade1();
    IHabilidade getHabilidade2();
    IHabilidade getHabilidade3();
    Animator getAnimator();
    float getVelocidade();
    void setVelocidade(float velocidade);
}

E no script Jogador, vamos adicionar esses dois métodos:

Script: Jogador.cs

    public float getVelocidade() {
        return velocidade;
    }

    public void setVelocidade(float velocidade) {
        this.velocidade = velocidade;
    }

Prontinho, agora podemos voltar para o método Start do script GCFaseLago, buscar a velocidade do personagem e diminui-la pela metade:

Script: GCFaseLago.cs

using UnityEngine;
using System.Collections;

public class GCFaseLago : GCFase {

	void Start () {
        var objPlayer = GameObject.FindGameObjectWithTag("Player");
        //Diminui a gravidade
        var rigidbody2D = objPlayer.GetComponent();
        rigidbody2D.gravityScale *= 0.5f; //Cai pela metade

        //Velocidade
        var personagem = objPlayer.GetComponent();
        var velocidade = personagem.getVelocidade();
        velocidade *= 0.5f;
        personagem.setVelocidade(velocidade);
	}
}

Com a alteração feita, basta a gente voltar ao Editor do Unity e adicionar o script GCFaseLago ao objeto GC, informando o nivel (2) e a posição inicial do personagem. Vai ter o parâmetro Boss lá também que não pode ficar nulo, então enquanto a gente não cria o nosso boss, pode colocar qual objeto do cenário que não será destruído:

39- Tutorial Cenario 2

Salve tudo e volta a Scene MenuFases e no objeto Fase2, informe o nome da Scene que será carregada:

40- Tutorial Cenario 2

Adicione a fase ao Scene Build e teste o jogo.

41- Tutorial Cenario 2

42- Tutorial Cenario 2

Se tiver tudo certo, então acaba de terminar mais um post o/
Espero que tenham gostado e qualquer dúvida deixe 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

5 comments

  1. Fabio Pimenta disse:

    Ufa, terminei a Fase 2 !!! Esperando pelo mob e Boss dessa Fase e ansioso pelo que vem nas Fases 3 e 4.

    Só uma dúvida e sugestão Carlos : As próximas Fases continuarão no esquema de plataforma ou serão outro tipo de Gameplay ?

    Tenho curiosidade e vontade de fazer um mix de gêneros tendo uma fase plataforma, outra puzzle, outra infinit runner, outra estratégia ou tower defense ou até mesmo City Building.

    É tranquilo misturar os tipos cada Scene de uma forma não é ?

    Parabéns por mais um excelente tutorial !!!

  2. Boa Noite Carlos,

    essas (Platform Effector 2D (com Use One Way) dão muitos erros. as pontas esbarram com os coliders do personagem.

    tem outra forma de fazer isso que funcione melhor?

    obrigado.

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!