Menu

12 – Tutorial Intermediário – Cenário 1

E aê galerinha indie, tudo joia? Hoje vamos fazer o nosso primeiro tutorial de cenário e resolvi fazer o cenário pequeno… Isso significa que o post será pequeno? Não, pelo contrário, será o segundo maior post. Neste post vou informar a melhor forma de criar cenários, revisar o lance do material e tiling e mostrar como criar uma plataforma One Way em relação aos cenários. Porém teremos que criar também nesse tutorial, dois outros scripts importantes. Um que irá informar o local que o personagem será criado e quando a fase termina (Que no nosso caso será quando o Boss no final da fase for derrotado) e outro que irá cuidar do movimento da câmera, ou seja, esse tutorial será maiorzinho de fazer, ai ai u.u’.

Então vamos iniciar mais um post indicando qual o sprites que eu vou utilizar na construção do cenário. Para construir cenários, vocês podem procurar por Tilesets, esse é o nome dado aos sprites de construir cenários.

Os tilesets que usaremos são de onde? Chutem! Sim, são do site Game Art 2d:

Link Platform Game 
Link direto para o Download

Nossa primeira fase será uma floresta como já havia dito antes. Então após baixar, você podem importar os seguintes assets com os nomes:
floresta_background

floresta_background

floresta_plataforma1

floresta_plataforma1

floresta_plataforma2

floresta_plataforma2

floresta_plataforma3

floresta_plataforma3

floresta_plataforma_one_way1

floresta_plataforma_one_way1

floresta_plataforma_one_way2

floresta_plataforma_one_way2

floresta_plataforma_one_way3

floresta_plataforma_one_way3

floresta_moita

floresta_moita

floresta_placa

floresta_placa

O ideal mesmo aqui seria que vocês criassem todo o cenário através de uma ferramenta de edição de Imagem como o Photoshop, onde dividiriam o chão, as plataformas nuvens, backgrounds matos e tudo mais em layers separados para ficar fácil a exportação depois, porém como eu acho que muitos de vocês podem não ter experiência, então vamos fazer tudo pelo modo Hardcore no Unity mesmo.

Então exportem tudo para uma nova pasta: Resources/sprites/tiles/fase1:

01- Tutorial Cenario 1

Podemos deixar o background, floresta_plataforma2 e floresta_plataforma3 como Textura mesmo para que possamos transforma-los em materiais, porém com o Alpha Is Transparent para não perder a transparência da imagem. E se a opção Alpha from Grayscale tiver marcada, tire, pois sua imagem ficará toda transparente. Os demais ficam como sprites mesmo.

O próximo passo será criar uma nova Scene a qual chamaremos de Fase1:

02- Tutorial Cenario 1

Aqui a primeira coisa que faremos é adicionar o chão. Como viram nosso chão tem basicamente 3 partes: Começo, meio e fim. Então vamos criar um objeto vazio que daremos o nome de Chao e dentro dele criar três outros objetos Inicio, Meio, Fim:

03- Tutorial Cenario 1

Caso fosse feito já em um editor de imagem era só criar um Único Chao que poderia ser até mesmo um sprite, simplicando o processo e diminuindo o consumo de memória no jogo, mas vamos que vamos!

Após ter criado, aqui é bem simples. No Inicio e Fim, vamos adicionar o componente Sprite Renderer (Add Component >> Rendering >> Sprite Renderer ou apenas faz a busca pelo nome no Add Component) e selecionar os sprites floresta_plataforma1 (Para o objeto Inicio) e o floresta_plataforma2 (Para o objeto fim). Depois afaste os objetos:

04- Tutorial Cenario 1

Ao mover o objeto Horizontalmente, mexa direto no objeto Inicio e Fim para definir o tamanho da plataforma. Porém ao mexer verticalmente, mexa o objeto Chao, para mover todos os 3 objetos filhos igualmente:

05- Tutorial Cenario 1

O objeto Fim pode ficar fora do alcance da câmera sem problema. Digamos que ela vai ser o final da nossa fase. Já a altura eu recomendo muito que a parte de baixo da plataforma não fique sendo exibido na câmera, senão quiser ter que fazer mais sprites na parte de baixo:

06- Tutorial Cenario 1

E não se preocupe, que ao contrário do Canvas que tem os pivots, independente do tamanho da resolução da pessoa, a parte de baixo não é exibida, se mantem como você planejou a câmera. O que pode mudar são os espaços laterais dependendo da resolução, mas verticalmente, se mantem.

Próximo passo agora é criar um material chamada PlataformaFloresta, dentro da pasta Resources/materials (Botão direito na pasta >> Create >> Material):

08- Tutorial Cenario 1

Já inclusive podem marcar o Shader como Unlit/ exture. E a textura será a floresta_plataforma2:

09- Tutorial Cenario 1

Pronto, agora é só ir no objeto Meio na aba Hierarchy e adicionar o componente Mesh Filter e Mesh Renderer (Add Component >> Mesh >> Mesh Renderer e Mesh Filter) . Em Mesh Filter vamos adicionar o formato Quad:

11- Tutorial Cenario 1

E em Mesh Renderer vamos adicionar o Material PlataformaFloresta em Element0:

12- Tutorial Cenario 1

Agora é só esticar a imagem horizontalmente e deixar ela alinhada com os objetos Inicio e Fim:

13- Tutorial Cenario 1

Uma vez que tiver feito, mude o Tiling X quantas vezes achar necessário para sua imagem não ficar esticada:

14- Tutorial Cenario 1

Caso ainda sim sua imagem fique esticada, verifique se na sua textura na aba Project (floresta_plataforma2) e Inspector, se o seu Wrap Mode está como Repeat ao invés de Clamp.

Agora no Objeto Chao (O pai dos 3), vamos adicionar o Box Collider 2D e a Layer Piso (Para que nosso personagem pule, se lembra?). Caso no meu projeto que vocês baixem, não esteja com a Layer Piso, é só adicionar, é um “bug” do unity, como já expliquei antes.

16- Tutorial Cenario 1

Lembrando que sua Box Collider 2D deve cobrir todos os 3 filhos, para que seu personagem não caia do nada.
Caso queira testar, é só arrastar o prefab de algum dos personagens para a aba Hierarchy:

17- Tutorial Cenario 1

Se o seu personagem ficou muito grande como o meu, basta ir na sua Main Camera e mudar o alcance da câmera.
Caso a projeção da sua câmera seja Orthographic, você muda o Size:

18- Tutorial Cenario 1

Caso ela Perspective você altera o campo de visão (Field of View):

19- Tutorial Cenario 1

“Carlos, qual é a diferença das duas?”

Bom, a Camera Orthographic é mais comum trabalhada com conteúdos 2D, onde ela considera todos como se tivessem tudo no mesmo plano. Ou seja, mesmo que um objeto esteja mais perto ou mais longe da câmera, ele vai continuar do mesmo tamanho. Já a Camera por Perspective, ela considera o tamanho de quem está mais próximo ou mais longe da câmera. Bora para um exemplo para ficar mais claro.

Vou deixar meu personagem mais próximo da câmera, ok?

21- Tutorial Cenario 1

Essa é a visão do nosso jogo de cima na aba Scene. Meu personagem não está mais com o Z igual ao da plataforma, está praticamente com a cara na câmera.

Agora vamos ver isso na aba Game com a Câmera Orthographic:

22- Tutorial Cenario 1

Continua exatamente igual. Mas se mudarmos a câmera para Perspective:

23- Tutorial Cenario 1
E lá está o nosso personagem com a cara na câmera de acordo com a sua distância. Se você quiser fazer uns efeitos Parallax sem script, só posicionando os objetos mais a frente ou mais atrás, use o Perspective. Do contrário use o Orthographic que é o padrão para jogos 2D ^^

Depois de escolher a sua câmera, ajuste novamente a posição do Objecto chão para que fique algo legal.

24- Tutorial Cenario 1

Com isso pronto, o próximo passo agora é repetir praticamente todo o processo para a Plataforma One Way. Então vou enumerar aqui:

1 – Crie um objeto chamado PlataformaVoadora com três objetos filhos (Inicio, Meio, Fim);
2 – No objeto Inicio adicione um Sprite Renderer com o Sprite floresta_plataforma_one_way1 e no objeto Fim adicione um Sprite Renderer com o Sprite floresta_plataforma_one_way3;
3 – Distancie horizontalmente os objetos Inicio e Fim;
4 – Crie um Material na pasta Resources/materials chamado PlataformaOneWayFloresta;
5 – No PlataformaOneWayFloresta escolha o Shader Unlit/Transparent e adicione a Textura ;
6 – No objeto Meio da PlataformaVoadora, adicione o Mesh Renderer e o Mesh Filter. O Mesh Filter será o Quad e o Mesh Renderer será o material PlataformaOneWayFloresta;
7 – Ajuste a distância das 3 imagens e o Tiling do PlataformaOneWayFloresta;
8 – Aplique o Box Collider 2D e o Layer Piso no objeto PlataformaVoadora.

Caso a sua Textura floresta_plataforma_one_way2 fique com um traço indesejado embaixo:

25- Tutorial Cenario 1

Troque o Filter Mode de Bilinear para Point (no filter):

26- Tutorial Cenario 1

Caso isso ainda não funcione tente trocar o Offset do material da posição Y para 0.01 (Fica ao lado da opção Tiling).
Prontinho.

Agora vamos para o que há de novo não é mesmo, que é o Platform One Way!

O importante é primeiro por essa plataforma em um local mais alto:

27- Tutorial Cenario 1

Depois iremos adicionar um componente chamado Platforme Effector 2D na plataforma com o Collider (Add Component >> Physics 2D >> Platform Effector 2D):

29- Tutorial Cenario 1

Logo de cara não vai acontecer nada. O motivo é bem simples. Caso repare na imagem acima, no nosso box Collider 2D, tem algo chamado Used By Effector que está desmarcado. Você precisa marca-lo se quiser usar algum tipo de Effector no Collider.

Depois de marcar você vai ver que já vai surgir um arco no seu Collider:

30- Tutorial Cenario 1

Esse arco ai já pertence ao Platform Effector 2D. Bora entender ele então:

31- Tutorial Cenario 1

O Use Collider Mask serve para informar caso você queira que esse Effector só funcione com um grupo de Layers. Caso ele vá funcionar com todo mundo, você pode deixar desmarcado. Caso você crie um Layer para o personagem e quer que esse effector só funcione com o personagem e em Collider Mask selecione apenas o layer do personagem.

Use One Way servem para fazer a grande mágica, permitir que seu personagem passe por de baixo do collider sem colidir e depois fique lá em pé. Você já pode testar isso:

32- Tutorial Cenario 1

33- Tutorial Cenario 1

Legal, né? No meu caso eu ainda aumentei o pulo do personagem no prefab na aba Project, para ele dar saltos mais altos.

Voltando… Use One Way Group, serve para que o sistema do Collider funcione com vários Colliders. Confesso que nunca vi isso em prática funcionando direito.

Surface Arc determina o local em que o Collider funciona normal, ou seja, se o objeto bater não irá conseguir passar pela plataforma. O padrão é 180, o que significa que se você bater na lateral da plataforma, você não vai conseguir passar. Caso você mude para 360, então nem por baixo você vai conseguir passar, porque o collider vai funcionar normal também na parte de baixo:

35- Tutorial Cenario 1

Mas se você por digamos que 90:

36- Tutorial Cenario 1

Você vai conseguir passar mesmo tocando pela lateral do collider sem ficar preso.

As opções Use Side Friction e Use Side Bounce servem para aplicar Fricção (Resistência) e a função de “quicar” dos lados da plataformas.

“Como eu posso fazer isso?”

Bom, nós não vamos usar isso nesse tutorial, mas vou mostrar como é possível. Primeiro você criaria na aba Project um Physics2D Material (Botão direito na aba Project >> Create >> Physics2D Material) lá você pode controlar o Friction (Ou o atrito que faz as coisas que foram empurradas andarem com mais facilidade ou mais dificuldade. No caso do nosso player, não vamos ver tanta diferença afinal não o empurramos e sim mudamos a sua posição, mas no caso de aplicar força/addForce ou cair na parte encostando algo, tem efeito mais lento dependendo do atrito) ou faz seu personagem quicar que nem uma bolinha alterando o Bounciness:

37- Tutorial Cenario 1

Caso você aplique esse Physics2D Material ao collider da PlataformaVoadora:

38- Tutorial Cenario 1

Seu personagem já deve ficar quicando toda vez que toca o collider, já que aumentamos o bounciness. Pode dá play e testar e ver seu personagem pulando como se estivesse em uma cama elástica. Isso é bom para objetos que você quer que façam seu personagem pular ou caso seu personagem seja uma bola, você pode adicionar isso no collider do seu personagem!

Mas pode apagar esse Physics2D Material que não iremos utilizar.

Voltando ao Platform Effector 2D, as opções Use Side Friction e Use Side Bounce, servem para aplicar esses dois efeitos que vimos na lateral do collider. O Side Arc, define o campo que essas duas opções terão efeitos. Mas como falei, basicamente só iremos utilizar o Use One Way e o Surface Arc em 180°, beleza?

Com isso terminamos mais um item, que o Platform One Way. Próximo passo agora é repetir mais uma vez o processo que fizemos, só que agora para o Background. Então:

1 – Crie um Material chamado BackgroundFloresta na pasta Resources/materials;
2 – Troque o Shader desse material para Unlit/Texture e escolham a textura floresta_background;
3 – Criem um objeto chamado Background na aba Hierarchy e adicionem o Mesh Filter (Quad) e Mesh Renderer (Com o material BackgroundFloresta)
4 – Aumente esse objeto para que ele cubra todo o cenário atrás e alterem o Tiling caso necessário:

39- Tutorial Cenario 1

E para finalizar de vez a construção do cenário, você pode baixar um áudio de floresta lá no freesound.org:

https://freesound.org/search/?q=forest

O que eu usei no projeto foi esse:

https://freesound.org/people/Slanesh/sounds/31766/

Após baixar o que você gostou, adicione a pasta Resources/sounds/BGS (Lembra-se para que serve essa pasta? Sons de fundo! )

Por fim adicione o áudio a sua Main Camera com a opção Loop marcada:

40- Tutorial Cenario 1

Pra finalizar de vez esse cenário, basta a gente criar dois objetos chamado LimiteEsquerdo, LimiteDireito. Neles iremos posiciona-los nas extremidades da fase e adicionaremos o Box Collider 2D, para que nossos personagens fujam da scene:

41- Tutorial Cenario 1

Isso já vai impedir que nosso personagem saia da scene.

Complico mais um pouquinho, ou já está bom?

É sempre bom complicar mais um pouco né? Então bora lá, vou pedir para que vocês criem outras duas layers:

42- Tutorial Cenario 1

Uma para os objetos limites da fase e outra para o personagem principal:

43- Tutorial Cenario 1

Depois definimos nos Objetos LimiteEsquerdo e LimiteDireito a Layer Limite

44- Tutorial Cenario 1

Já no prefab do Personagem1 e Personagem2, adicionamos a Layer Personagem:

45- Tutorial Cenario 1

Pronto, agora vá no menu Edit >> Project Settings >> Physics 2D e na parte inferior das configurações onde tem Layer Collision Matrix, desmarque todas as interações da Layer Limites, deixando apenas a interação entre Personagem e Limites:

46- Tutorial Cenario 1

Com isso o Collider não vai funcionar com mais ninguém que não tenha a Layer Personagem. Legal, né? Com isso se você tiver um pássaro voando pelo cenário ou uma plataforma que se mexe, ela não vai bater nos Colliders dos objetos que tiverem a Layer Limites. Caso queira criar layers para os inimigos e tirar a interação deles com outras coisas que não sejam o Piso e o Personagem é algo possível (lembrando que no caso tem que fazer outra layers para os ataques do personagem ou marcar os ataques do personagem com a layer Personagem). Em seus jogos é muito recomendado que vocês usem essas funções para aumentar a velocidade e processamento do seu jogo, porém no tutorial eu não vou fazer por um motivo que vocês já sabem. Ao copiar o projeto do Unity para outro pc, ele perde as tags e layers selecionadas, então vou evitar o máximo utilizar layers e tags sem ser via script.

Prontinho, mas estão sentindo falta de nada não nessa fase? A gente não adicione o Pause e nem o HUD. Então crie um objeto Canvas e adicione o prefab dos dois:

47- Tutorial Cenario 1

E com isso nós já terminamos a construção da nossa fase, onde tivemos uma revisão dos conteúdos passados e aprendemos sobre o Platform One Way e a interação dos colliders entre diferentes layers. Porém o post não termina por aqui, ainda temos coisas importantes a fazer:

1 – Um Script que controle a fase, criando o prefab do personagem correto fora outras coisas como verificar se a fase chegou ao fim ou não (Ela vai ser dada como completa, ao derrotar o Boss que terá em todas as fases);
2 – Um Script para a câmera seguir o personagem (Ou um objeto qualquer) e não fugir dos limites do cenário;
3 – Um Script para iniciar um evento de Boss (A câmera trava em uma posição e ativar os limitadores do cenário da batalha, para que o personagem não fuja do boss).

Ou seja, se tudo foi flores até agora sem mexer em nada de código, vamos puxar um pouco do raciocínio lógico de vocês para criar esses 3 scripts!

Embora sejam três Scripts, podem ficar tranquilo que os 3 são relativamente pequenos. Então bora para o primeiro script.

Peço que criem um script (C#) chamado GCFase dentro de scrtips/controllers. Neste script por hora só iremos ter duas variáveis, sendo uma variável publica posicaoInicial do tipo Vector3 e outra do tipo inteiro que guarda qual é o nível fase (floresta =1, lago =2, caverna=3 e montanha = 4). Também vamos criar um código no método Awake e um método publico chamado finalizaFase, mas como diria o jack, vamos por parte.

Primeiro criem a variável publica:

Script: GCFase.cs

    public Vector3 posicaoInicial;
    public int nivel;

Em seguida vamos pegar o nome do prefab do personagem que o jogador selecionou e que está registrado no GameStatus, lembram? Depois daqui, é só revisão de como instanciar um objeto no cenário igual como fizemos os ataques:

Script: GCFase.cs

    void Awake() {
        //Cria o personagem na fase
        var nomePrefab = FindObjectOfType().getGameStatus().prefabPersonagem; //Busca o nome do prefab
        var prefabPlayer = Resources.Load("prefabs/jogador/" + nomePrefab);           //Busca o prefab
        var objPersonagem = Instantiate(prefabPlayer) as GameObject;                  //Cria o personagem
        objPersonagem.transform.position = posicaoInicial;
    }

Sem complicação, não é mesmo? Só pegamos o nome do prefab que temos registrado. Buscamos na pasta Resources/prefabs/jogador e criamos esse objeto. Depois definimos a sua posição no cenário. Se quiséssemos, poderíamos até deixar esse código com menos linha trocando as duas ultimas linhas por:

Instantiate(prefabPlayer, posicaoInicial, Quaternion.identity);

Onde o primeiro parâmetro é o prefab, o segundo parâmetro é a posição e o terceiro parâmetro é a rotação. O Quaternion.identify cuida de trazer o prefab sem rotação alguma, ou seja, alinhado com o cenário.

Mais simples, impossível. Agora vamos criar só mais um método público nesse script, que será chamado quando o jogador completar a fase, onde iremos liberar a próxima fase caso ainda não tenha liberada, recuperarmos a vida do nosso personagem, para que nas próximas fases ele comece com tudo cheio, atualizar a informação do jogador no GameStatus. Por fim chamar a tela de seleção de fases:

Script: GCFase.cs

    public void finalizarFase() {
        var gameStatus = FindObjectOfType().getGameStatus();                                    //Busca o GameStatus
        var status = GameObject.FindGameObjectWithTag("Player").GetComponent().getStatus();//Busca o Status do personagem ativo
        if (nivel == gameStatus.faseLiberada)                                                           //Verifica se esse nível é ultimo liberado
            gameStatus.faseLiberada++;                                                                  //Libera o próximo nivel

        status.recuperaHP(999);                     //Recupera todo HP
        status.recuperaMagia(999);                  //Recupera toda MP
        gameStatus.status = status;                 //Informa para o gameStatus o novo status do jogador.

        SceneManager.LoadScene("MenuFases");                   //Chama a scene de selação de fases
    }

Explicando, primeiro a gente buscou o GameStatus do script GCJogo. Ao alterar ele aqui, já irá alterar automaticamente lá no GCJogo, já que é passado como referencia, ok?

Sem seguida a gente buscou o Status do personagem ativo na fase.

Sem seguida verificamos o nível atual é o ultimo nível liberado. Caso sim, a gente libera o próximo nível.

Recuperamos o HP e MP do nosso personagem e passamos esse status atualizado para o gameStatus. Por fim chamamos a seleção de fases! Se lembrem que para usar o SceneManager, precisamos chamar o “using UnityEngine.SceneManagement;”.

Tudo tranquilo, então é só salvar e voltar o Unity, onde você vai criar um novo objeto com o nome GC e adicionar esse script a ele, informando uma posição que você achar legal para seu personagem começar e informar o nível da fase (1):

48- Tutorial Cenario 1

A posição do personagem você pode identificar usando o prefab, todavia você tem que remover o prefab da aba Hierarchy depois, pois o script GCFase já irá adicionar um. Do contrário ficará duplicado.

Pronto nosso script GCFase está finalizado por hora.

Vamos para o script da câmera. Nele iremos utilizar o GCCamera que já criamos anteriormente para controlar o som, lembra? Então já pode abrir esse script e adicionar uma nova variável, que irá receber o Transform de um objeto no cenário e outra variável do tipo float que irá definir a velocidade com a qual a câmera se mexer:

Script: GCCamera.cs

    public Transform alvo;      //Quem a camera segue
    public float velocidade;    //Com qual velocidade a camera segue

Depois disso no método Update vamos verificar se existe algum alvo. Caso sim, iremos pegar o destino que a câmera deve ir. O destino será a posição de X e Y do alvo, porém mantendo a mesma distância que a câmera foi definida (Ou seja, o Z continua sendo o camera). Depois usaremos o método Vector3.Lerp para fazer a câmera sair da sua posição original para o destino, numa velocidade que definimos:

Script: GCCamera.cs

    void Update() {
        if (alvo != null) {
            var destino = new Vector3(alvo.position.x, alvo.position.y, transform.position.z);
            transform.position = Vector3.Lerp(transform.position, destino, velocidade * Time.deltaTime);
        }
    }

“Epa, epa, epa, epa!!! No script da fase, para definir que um objeto saísse da sua posição para um destino, você usou o Vector3.MoveTowards e agora está usando o Vector3.Lerp? Qual é a diferença de cada um? Quando eu devo usar cada um?! Por que existem dois se eles fazem a mesma coisa que é sair de uma posição para ir a um destino??!! Explica ai por favor, Carlos!!!”

Calma, calma, calma. Pelo menos eu vi que você entendeu o transform.Translate. O Translate serve para sair de uma posição a outra, sem a gente saber o destino. Ou seja, eu sei que ele quer ir para a esquerda, agora o destino não sei, então uso o transform.Translante(Vector3.right * Time.deltaTime)! Agora se você sabe o destino, então você usa o Vector3.Lerp ou Vector3.MoveTowards.

Agora explicar os dois você me complica um pouco, mas vou tentar explicar de forma clara.

O Vector3.MoveTowards define:
(Posição Inicial, Posição Final, Distância real caminhada da Posição Inicial para a Posição Final)

Já o Vector3.Lerp define:
(Posição Inicial, Posição Final, Distância caminhada em porcentagem da Posição Inicial para a Final)

Não entendeu né? Vamos transformar isso em algo prático. Ao invés de um Vector3, vamos pensar em um valor linear onde temos um objeto que inicia na posição 0 e que ir pra posição 2. No caso do MoveTowards ele vai sempre andar o mesmo valor fixo:

Vector3.MoveTowards(transform.position, destino, 0.5)
Frame 1 (Executa o Update)

Transform.position: 0
destino: 2
Distância: 2
Percorre: 0.5
Posição final do objeto percorrido: 0.5

Frame 2 (Executa o Update)

Transform.position: 0.5
destino: 2
Distância: 1.5
Percorre: 0.5
Posição final do objeto percorrido: 1

Frame 3 (Executa o Update)

Transform.position: 1
destino: 2
Distância: 1
Percorre: 0.5
Posição final do objeto percorrido: 1.5

Frame 4 (Executa o Update)

Transform.position: 1.5
destino: 2
Distância: 0.5
Percorre: 0.5
Posição final do objeto percorrido: 2

Ou seja, o MoveTowards percorre uma distância real e “fixa”, entendeu? Agora vamos ver o Lerp que percorrer uma porcentagem sendo 0 (0% do caminho percorrido ou seja fica na mesma posição de origem) e 1 (100% anda o caminho todo de uma vez, ou seja fica já no destino):

Vector3.Lerp(transform.position, destino, 0.5)
Frame 1 (Executa o Update)

Transform.position: 0
destino: 2
Distância: 2
Percorre: 1 (0.5 = 50% do caminho | 50% da distância = 1)
Posição final do objeto percorrido: 1

Frame 2 (Executa o Update)

Transform.position: 1
destino: 2
Distância: 1
Percorre: 0.5 (0.5 = 50% do caminho | 50% da distância = 0.5)
Posição final do objeto percorrido: 1.5

Frame 3 (Executa o Update)

Transform.position: 1.5
destino: 2
Distância: 0.5
Percorre: 0.25 (0.5 = 50% do caminho | 50% da distância = 0.25)
Posição final do objeto percorrido: 1.75


E vai assim infinitamente até chegar perto do valor. Com isso podemos observar que o Lerp ele é mais “rápido” (percorre uma distância maior por frame) quando a distância é maior e mais “lento” (percorre uma distância menor por frame) quando a distância é menor. Com isso ele acelera no inicio e quando chega perto do destino ele vai mais lento, gerando um efeito legal de suavização.

movetowards vs lerp

Então podemos entender que MoveToward mantem a mesma velocidade independente da distância. Já o Lerp ele é mais rápido quando a distância é maior e mais suave quando está chegando ao destino. Basicamente é essa a diferença. Deu para entender melhor agora?

Bom, voltando ao nosso script vamos usar o Lerp, para fazer um movimento legal na câmera ao seguir o personagem. Agora de volta ao nosso script GCFase, lá no Awake, vamos definir o alvo sendo o personagem adicionando duas linhas ao final do método:

Script: GCFase.cs

    void Awake() {
        //Cria o personagem na fase
        var nomePrefab = FindObjectOfType().getGameStatus().prefabPersonagem; //Busca o nome do prefab
        var prefabPlayer = Resources.Load("prefabs/jogador/" + nomePrefab);           //Busca o prefab
        var objPersonagem = Instantiate(prefabPlayer) as GameObject;                  //Cria o personagem
        objPersonagem.transform.position = posicaoInicial;
        player = objPersonagem.transform;

        //Ajusta a camera
        var camera = FindObjectOfType();
        camera.alvo = objPersonagem.transform;
    }

Buscamos o script da câmera e depois dizemos quem é o alvo ^^

Já podemos até fazer um teste. Volta ao Unity e adiciona o Script GC Camera a Main Camera e informa o valor da velocidade que você quer:

49- Tutorial Cenario 1

Agora volte a Scene MenuFases e lá no objeto Fase1 diga para ir para a Fase1:

50- Tutorial Cenario 1

(Estou pensando em mudar isso depois, acrescendo mais um post a esse tutorial. Ensinando a vocês a fazer uma tela de Loading de fases, mas por hora, vamos fazer o botão chamar direto a fase).

Agora volte a Scene Main e faça todo o percurso até chegar na fase e testar sua câmera. Ai já deverá ver que a câmera se move de forma suave. Se a câmera fica muito para trás quando seu personagem anda, aumente a velocidade, assim a porcentagem gerada pela velocidade * Time.deltaTime será maior, o que significa que ele vai estar mais próximo do destino do que da posição de origem ao mover ^^.

51- Tutorial Cenario 1

Porém, a câmera ainda mostra conteúdos que a gente não quer que seja exibido, não é mesmo? Então precisaremos definir os limites que a câmera pode ir.

Para fazer o limite, bem… eu pesquisei, pesquisei, pesquisei, pesquisei e não achei nenhum script legal para servir de modelo para vocês, então eu mesmo criei o meu modelo de limitação da câmera que acho que seja mais fácil de entender. Mas relaxem, que eu vou desenhar para vocês entenderem e não, não estou zoando, vou desenhar mesmo.
Bom, o grande problema da limitação da câmera, é que dependendo da resolução do pc, o alcance da nossa câmera muda correto?

52- Tutorial Cenario 1

53- Tutorial Cenario 1

Porém se você analisar essas duas imagens acima vai ver que o problema é apenas horizontalmente. Verticalmente o alcance da câmera não diminui ou aumenta.

O script que vou explicar, serve apenas para câmera Orthographic, para fazer uma com perspective, terei que pensar em outro script.

Bom, vocês se lembram do Size da nossa Câmera Orthographic

55- Tutorial Cenario 1

Esse 10 ai que eu usei, nada mais é do que dizer, do ponto central da nossa câmeras, tem 10 posições para cima e 10 posições pra baixo. Ou seja, imaginando que nossa câmera está na posição 0, então ela vai enxergar tudo da Y= 10 a Y = -10. Já o X, vai mudar de acordo com a resolução da pessoa.

No nosso script nós vamos definir as posições limite de X e Y:

Script: GCCamera.cs

using UnityEngine;
using System.Collections;

public class GCCamera : MonoBehaviour {

    public Transform alvo;      //Quem a camera segue
    public float velocidade;    //Com qual velocidade a camera segue

    public float minX = -9999;
    public float maxX = 9999;
    public float minY = -9999;
    public float maxY = 9999;

Eu adicionei os valores 9999 e -9999 como default. É um valor alto, para caso sua scene seja grande, ela dificilmente entre nesse limite, deixando a câmera sem limitações pros lados. Caso a gente queira limitar, então a gente define um valor real, ok?

Bom, se tratando de Y, a gente já viu que nós não temos problema em relação a resolução, então aqui é bem simples. Basta você posicionar a sua camera na parte inferior e superior no Inspector e guardar os valores de Y:

Inferior (Y = 1):

56- Tutorial Cenario 1

Superior (Y = 14):

57- Tutorial Cenario 1

Bom, agora no nosso script vamos verificar no método Update após ele ter ser movido, se a camera é do tipo Orthographic. Caso sim, vamos recuperar os valores de X, Y, Z e garantir que eles estejam dentro do limite:

    void Update() {

        if (alvo != null) {
            var destino = new Vector3(alvo.position.x, alvo.position.y, transform.position.z);
            transform.position = Vector3.Lerp(transform.position, destino, velocidade * Time.deltaTime);
        }

        //Limite
        var camera = GetComponent();
        if (camera.orthographic) { 
            var x = transform.position.x;
            var y = transform.position.y;
            var z = transform.position.z;

            x = Mathf.Clamp(x, minX, maxX); 
            y = Mathf.Clamp(y, minY, maxY);

            transform.position = new Vector3(x, y, z);
        }
    }

Após o comentário //Limite, buscamos o Script Camera, e verificamos se ele é orthographic (camera.orthographic == true). Caso sim, recuperas as informações de X, Y e Z da posição da camera. E dizemos que tanto X quanto Y, ele tem que estar dentro do limite informado. Para definir o limite usamos o método Mathf.Clamp, que nada mais faz do que verificar se o valor do primeiro parâmetro é maior que o valor mínimo (segundo parametro) e menor que o valor máximo (terceiro parâmetro). Caso seja menor, então o valor retornado será o valor mínimo, caso seja maior, o valor retornado será o valor máximo. Caso esteja entre os dois, o valor retornar é o informado no primeiro parâmetro.

Exemplo:
Mathf.Clamp(10, 0, 20) = Retorna 10
Mathf.Clamp(-10, 0, 20) = Retorna 0
Mathf.Clamp(30, 0, 20) = Retorna 20

Tranquilo? Com isso só então precisamos dizer que a nova posição da camera com o X e Y atualizados.

Desta forma O Y já deve funcionar com as informações que pegamos antes (Min = 1 e Max = 14), afinal ele é igual para qualquer resolução:

59- Tutorial Cenario 1

60- Tutorial Cenario 1

A câmeras não vai exibir nada abaixo do chão e nem acima do background, vai respeitar a posição que informamos antes. O grande problema aqui é o X, pois ele muda conforme a resolução:

61- Tutorial Cenario 1

62- Tutorial Cenario 1

Então aqui temos que pegar o valor do alcance horizontal. Já vimos que o alcance Vertical é o mesmo que o Size da camera Orthographic, correto? Então o alcance horizontal vai ser o Size * a resolução da pessoa LARGURA/ALTURA:

var alcanceHorizontal = camera.orthographicSize * Screen.width / Screen.height;

Mas então com isso já sabemos que independente da resolução do jogador, o alcanceHorizontal vai retornar a distância do ponto central da câmera para um dos lados.

Agora pense comigo, se já o alcance horizontal da nossa câmera, para definir a posição dela, é só pegar a posição limite do cenário do lado esquerdo + o alcance horizontal. E a posição limite do lado direito – o alcance horizontal. Vai um desenho ai para vocês entenderem:

63- Tutorial Cenario 1

Digamos que a gente sabe que a posição limite da nossa camera do lado esquerdo (minX) é 0 e que a posição máxima do limite do lado direito horizontalmente é 80 (maxX). A gente também já descobriu o alcance da nossa camera horizontalmente. Então só precisamos pegar a posição do limite esquerdo + o alcance horizontal para achar a posição correta da camera. Ou seja, se a resolução do jogador for menor, o alcance também será menor, consequentemente a posição da câmera será mais próxima do Limite. Deu para entender? É meio difícil essa parte mesmo, exige um pouco do raciocínio da pessoa. Mas se você não entendeu e não quer entender tudo bem, basta entender que em Y a gente pega a posição da câmera mesmo e X, nós pegamos a posição dos objetos no limite da nossa scene.

Arrumando isso via código ficaria:

Script: GCCamera.cs

    void Update() {

        if (alvo != null) {
            var destino = new Vector3(alvo.position.x, alvo.position.y, transform.position.z);
            transform.position = Vector3.Lerp(transform.position, destino, velocidade * Time.deltaTime);
        }

        //Limite
        var camera = GetComponent();
        if (camera.orthographic) { 
            var x = transform.position.x;
            var y = transform.position.y;
            var z = transform.position.z;

            var alcanceHorizontal = camera.orthographicSize * Screen.width / Screen.height;

            var realMinX = minX + alcanceHorizontal;
            var realMaxX = maxX - alcanceHorizontal;

            x = Mathf.Clamp(x, realMinX, realMaxX); 
            y = Mathf.Clamp(y, minY, maxY);

            transform.position = new Vector3(x, y, z);
        }
    }

Para saber quem é a posição limite dos lados, basta a gente pegar as posições dos objetos LimiteEsquero e LimiteDireito:

64- Tutorial Cenario 1

65- Tutorial Cenario 1

Então agora é só adicionar as posições ao script GCCamera:

67- Tutorial Cenario 1

Pronto, agora já está tudo ok independente da resolução do jogador:

68- Tutorial Cenario 1

69- Tutorial Cenario 1

Agora só falta uma besteira para acabar esse tutorial gigante u.u. É um script simples. Que iremos chamar de EventoBoss e o salvaremos dentro de scripts/cenarios, beleza? Esse script é super simples:
Script: EventoBoss.cs

using UnityEngine;
using System.Collections;

public class EventoBoss : MonoBehaviour {

    void OnTriggerExit2D(Collider2D colisor) {
        if (colisor.tag.Equals("Player")) {
            if (colisor.transform.position.x > transform.position.x) {

                var collider = GetComponent();
                collider.isTrigger = false;

                var camera = FindObjectOfType();
                camera.minX = colisor.transform.position.x – 2f;
            }
        }
    }
}

Aqui estamos dizendo que se o personagem cruzar um collider com a opção Is Trigger marcada, esse collider não será mais do tipo Is Trigger, ou seja, o personagem vai bater essa parede invisível sem poder voltar e a camera também não vai poder ir para trás desse collider.

Para isso fazemos duas verificação antes. Se foi realmente o player que passou e se ele passou para o lado direito (a posição dele em X é maior do que a do objeto atual).

O -2f que adiciono junto com a posição do objeto colisor, é apenas para o personagem não ficar colado na tela.
Para por isso em prática, vamos criar um objeto na nossa aba Hierarchy a qual chamarei de LimiteBoss. E nele adicionarei o script EventoBoss e o BoxCollider2D com a opção Is Trigger marcada:

70- Tutorial Cenario 1

Com isso quando o personagem cruzar esse Box Collider 2D não poderá mais voltar. Isso é o que normalmente os jogos Beat’em Up antigos faziam. Se você quisesse poderia até mesmo adicionar também nesse script uma variável publica para o Audio de uma música só para boss e botava para tocar na câmera.

Bom, agora é só salvar tudo e ficar feliz porque esse tutorial acabou. Ooooh trabalho, viu u.u. E o próximo também será um pouco trabalhoso, pois vamos criar a Interface e a classe abstrata mãe das classes dos inimigos.
Mas por hoje paramos por aqui mesmo. Valeu pessoal e até o próximo post 😉

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

21 comments

  1. Fernando disse:

    Grande Carlos, parabéns pelo tutorial, na época que me aventurei a tentar fazer jogo as coisas eram mais difíceis, com essa engine a coisa fica mais fácil, mas nem tanto assim… hehehe.
    Seguinte, meu foco de desenvolvimento é mobile, então estou fazendo alguns ajustes no tutorial para os controles serem touch. Criei um botão direcional que substitui o movimento Horizontal, mantendo a funcionalidade via teclado, montei um prefab de nome VirtualJoystick, mas para funcionar alterei o script jogador, criando a variavel publica joystick do tipo VirtualJoystick. Agora meu personagem1 e 2 precisam que referencie essa variavel, quando uso cenário de testes onde o personagem1 já está na Hierarquia e o VirtualJoystick também faço a referencia, e tudo funciona.
    Problema: Como nossos personagens são criados dinamicamente, por mais que o VirtualJoystick não seja, essa referencia entre dois prefabs distintos (ex: personagem1 e VirtualJoystick) é perdida, em execução está marcado no campo o nome do VirtualJoystick, mas não funciona, então apenas para testes em execução mesmo refaço essa referencia e funciona. Sabe como contornar isso? Acredito que o problema esteja em referenciar um prefab. No tutorial ate agora não vi nenhuma situação semelhante.
    Pensei na possibilidade de já referenciar esse joystick no script Jogador (não sei como fazer), ou inserir o joystick no prefab dos personagens, mas acho que vai zoar tudo…. hehehehe

    • Rapaz, eu não entendi exatamente o que é feito, mas se for o que eu estou pensando, basta você buscar esse script no método Start do personagem:

      void Start() {
      joystick = FindObjectOfType<ScriptDoJoystick>();

      }

  2. Fernando disse:

    Era isso mesmo Carlos, solução simples hein, eu estava tentando fazer isso na classe Jogador, enfim, só fazendo muito para pegar o jeito. Depois que tem a solução na mão parece fácil, mas o difícil é saber o que fazer e como fazer em cada situação.
    Agora vou fazer os botões que ativam as habilidades e o ataque. Obrigado.

    • é verdade. Muitas vezes gasto mais tempo pensando em como organizar e fazer algo que fique legal, do que propriamente fazendo.

      Mas depois de fazer várias coisas você vai pegando um ritmo legal 😉

  3. Fabio Pimenta disse:

    Carlos, mais um caso Arquivo X : Se eu arrasto o Sprite de Project para a Scene ele aparece na aba Game mas se eu crio ele como Empty (seguindo seu tutorial para ter Inicio Meio e Fim para a plataforma) ele não aparece, fica invisível #.#

    Funcionou para o Chao mas para a PlataformaVoadora está acontecendo isso. Alguma idéia ?

    • Após cria-lo pelo Create Empty, você adicionou o componente Sprite Renderer?

      Verifique também a posição do objeto (Position z)

      • Fabio Pimenta disse:

        Sim, o Início e Fim estavam com o Sprite Renderer e o Meio com Mesh Filter e Mesh Renderer tudo certo.

        O mesmo aconteceu agora com o Background que ficou invisível. Estava colocando na cena, mas aí fui checar o Z e estava -10, coloquei 0 e ele passou a aparecer na aba Game.

        A partir de agora vou prestar mais atenção no Z, primeiro achei que o eixo Z não teria importância para jogos 2D, no máximo para rotacionar objetos, mas parece que quando envolve textura algo misterioso ocorre 🙂

        Vlw Santo Carlos \o/

        • Sim, sim. Seja 2D ou 3D a camera funciona da mesma forma. Tendo duas opções que é a pespectiva, que funciona como se fosse nos olhos normais, indicando o que está perto ou longe e o ortografico, que ignora a terceira dimensão (Z), mas ainda sim, qualquer coisa que estiver atrás da camera, não será enxergado por ela.

  4. João Carlos disse:

    Carlos, estou tentando fazer o comando para o minimo e maximo que a camera vai mas nao está funcionando. quando voce faz o codigo para verificar se a camera é do tipo Orthographic:

    var camera = GetComponent();
    if (camera.orthographic) {
    var x = transform.position.x;
    var y = transform.position.y;
    var z = transform.position.z;

    x = Mathf.Clamp(x, minX, maxX);
    y = Mathf.Clamp(y, minY, maxY);

    transform.position = new Vector3(x, y, z);
    }

    essa linha “camera.orthographic” nao esta sendo aceita.

    Tambem a linha “camera.orthographicSize do script” não esta funcionando:
    var alcanceHorizontal = camera.orthographicSize * Screen.width / Screen.height;

    dizem que teve mudança nos scripts a partir da versao 5.3 e alterou varios comandos.

    como a forma certa de escrever para funcionar?

  5. João Carlos disse:

    Boa noite Carlos qual a explicação para essa parte “camera.orthographic” não estar pegando? aparece um traço vermelho e o script não compila..

    • Opa João. No caso para estar dando erro, significa que o camera deve ser nulo. Você precisa buscar o componente camera:
      camera = GetComponent<Camera>

      E certifique também que você está colocando esse script no objeto Main Camera e que ele está com o script Camera ativado.

  6. wellington disse:

    mano como faço pra mudar a camera estilo megaman/super metroid, eu imaginei q seria dando um LoadScene, e se for LoadScene como faço pra ficar salvo os itens quando trocar a cena, e se tiver como deixa um script da camera pra eu analiza ou uma explicação agradeço mt, pq n to achando em nem um lugar

  7. wellington disse:

    tranquilo é que ta meio puxado o tutorial, esse negocio de {get e set} ta dificil entender, mais eu consegui o bang da camera nao sei se da melhor forma, tiver q fazer 3 script um no player q salva a posicao dele na cena passada quando ele colidi com uma linha,outro q da o lodscene quando colide com outra linha logo na frente, e demora 1 seg pra mudar de cena e enquando tiver contando pra mudar a cena ele deixa a tela preta. ou seja 3 objeto e 3 scripts no total n sei se e a melhora forma

    • Então, esse get e set é o básico usado na maioria as linguagens de programação, então não tera para onde correr :/

      Pode guarda todas essas informações no script GCJogo, como método estático. Mas se funcionou da forma que você fez, tem bronca não o/

  8. wellington disse:

    tranquilo vou estuda um pouco mais dps volto aqui pra entender melhor e completa esse tutorial

  9. Daniel H.S. disse:

    Oi Carlos W. Gama, estou com um problema ao jogar o personagem no mapa pelo scripta segue oque aparece no unity

    ArgumentException: The Object you want to instantiate is null.
    UnityEngine.Object.CheckNullArgument (System.Object arg, System.String message) (at C:/buildslave/unity/build/Runtime/Export/UnityEngineObject.cs:196)
    UnityEngine.Object.Instantiate (UnityEngine.Object original) (at C:/buildslave/unity/build/Runtime/Export/UnityEngineObject.cs:163)
    GCFase.Awake () (at Assets/assets/script/controllers/GCFase.cs:14)

    estou a 3 dias pensando oque pode ser, ja mexi e remexi no código, vou ficar doido, me ajuda por favor

  10. Daniel H.S. disse:

    Outro erro que ta enchendo o saco é o estouro de pilha no script status, segue a baixo o erro

    StackOverflowException: The requested operation caused a stack overflow.
    Status..ctor () (at Assets/assets/script/jogador/Status.cs:14)
    …..muitas linhas como esta acima e depois essa debaixo
    Status..ctor () (at Assets/assets/sc

  11. Daniel H.S. disse:

    Essa do status acabei de arrumar, erro besta :v eu tava dando um new status toda hora no codigo, então por enquanto o unico erro chato mesmo é o

    ArgumentException: The Object you want to instantiate is null.
    UnityEngine.Object.CheckNullArgument (System.Object arg, System.String message) (at C:/buildslave/unity/build/Runtime/Export/UnityEngineObject.cs:196)
    UnityEngine.Object.Instantiate (UnityEngine.Object original) (at C:/buildslave/unity/build/Runtime/Export/UnityEngineObject.cs:163)
    GCFase.Awake () (at Assets/assets/script/controllers/GCFase.cs:14)

    • Erros de Overflow é normalmente quando você faz algo muito repetitivo ou até mesmo infinitamente, ai dá erro de overflow (exemplo, tarefas recursivas gigantes ou sem fim ou loop infinito)

  12. Daniel H.S. disse:

    Descobri oque era, eu tinha que ter criado uma pasta chamada Resources(os recursos tem que estar la) dentro teria o prefabs, dentro do prefabs, coloca uma pasta com o prefab do personagem sozinho, se tiver mais de um ele buga.

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!