O que há de Novo?
Fórum Outer Space - O maior fórum de games do Brasil

Registre uma conta gratuita hoje para se tornar um membro! Uma vez conectado, você poderá participar neste site adicionando seus próprios tópicos e postagens, além de se conectar com outros membros por meio de sua própria caixa de entrada privada!

  • Anunciando os planos GOLD no Fórum Outer Space
    Visitante, agora você pode ajudar o Fórum Outer Space e receber alguns recursos exclusivos, incluindo navegação sem anúncios e dois temas exclusivos. Veja os detalhes aqui.


[Unity #01] Outer Space Invaders

Landstalker

Lenda da internet
Mensagens
19.356
Reações
39.661
Pontos
1.584
Outer_Space_Invaders.png

:: Tópicos

- Estrutura de Pastas
- Jogos 2D na Unity
- Programando o BackgroundScroller

Antes de começarmos com o projeto, baixe os arquivos de texturas por esse link.

# Estrutura de Pastas

Na parte 1 vimos rapidamente como criar um arquivo C# e colocá-lo numa pasta, agora vamos organizar as nossas pastas para o nosso projeto ficar organizado.

Com o botão direito do mouse em cima da janela de Project, selecione Create > Folder e crie as seguintes pastas:

- Scripts (caso não foi criada na parte 1)
- Scenes (caso não foi criada na parte 1)
- Textures
- Audio
- Prefabs


Em Scripts, caso não tenha, crie o arquivo GameManger.cs via Creater > C# Script. É nessa pasta que todos os nossos scripts ficarão. Em Scenes vão ficar as nossas cenas, caso você não a salvou, pressione CTRL + S e uma caixa de diálogo irá aparecer perguntando onde você gostaria de salvar a sua cena atual.

Em Textures ficarão todos as nossas texturas, sejam elas texturas para background, para sprites dos inimigos e nave, ou para botões e HUB, tudo envolvendo imagem ficará nessa pasta.

Audio contará com os arquivos de áudio, como sons FX e música.

E por último, a pasta Prefabs (que pode soar um pouco estranho para quem não está acostumado com o termo) será usada para armazenar todos os nossos prefabs do jogo. Prefabs são objetos "pré-fabricados", ou seja, são objetos que servem como template para serem usados repetidamente no jogo. Eles funcionam como templates e poderemos criar praticamente tudo como um prefab.

Um NPC pode ser um prefab, um tiro de uma nave também, a própria nave, enfim, quase qualquer coisa.

Question.png


A resposta é NÃO!!!

Um prefab pode ser uma junção de vários GameObjects que fazem parte de um objeto maior. E digamos que queremos reutilizá-lo várias vezes durante o jogo, então aí criamos esses componentes como prefab e poderemos instanciá-los à vontade em qualquer parte do jogo. Uma vez que alteramos a propriedade de um prefab, todos as instâncias desse prefab também são alteradas, fazendo com que todos tenham as mesmas propriedades, afinal, já que é isso que se espera de um template. Com o decorrer do curso, o conceito de prefab ficará mais fácil a ser entendido, espero eu. :kvergonha

# Jogos 2D na Unity

Eu meio que já abordei esse assunto de forma leviana na primeira parte, aqui voltarei a falar um pouco mais. A Unity é uma engine 3D, portanto mesmo que programemos em 2D na verdade estamos usando o ambiente 3D disfarçado. Mas não se preocupe que isso não quer dizer necessariamente que um programador 2D terá que aprender modelagem 3D e o escambau para conseguir usar essa engine. Nada disso!

Com o passar do tempo, a Unity vem se aperfeiçoando e trazendo recursos que facilitam os desenvolvedores de jogos que queiram criar jogos 2D. Hoje a coisa é bem mais fácil quando foi há 3-4 anos atrás, onde se obrigou muitos desenvolvedores criar assets específicos para facilitar o desenvolvimento de um jogo 2D nela.

Eu mesmo já comprei alguns desses Assets, cheguei a gastar em 2014 quase 300 dólares em assets na Unity, até desenvolvi por minhas próprias mãos um editor de tileset porque a Unity não dá suporte a um editor desses (até tem um projeto interno deles para isso mas ainda não foi oficialmente lançado). Embora haja o que melhorar, a Unity é versátil e poderosa e poderemos ter jogos 2D nela tranquilamente sem muito alardes.

Outras engines 2D trabalham com as unidades em pixel. A Unity não trabalha com pixel diretamente, a medida usada se chama simplesmente "unit" e mesmo uma unidade dela representando um metro, esse um metro pode ser, na verdade, qualquer tamanho. Então como diabos uma box feita em pixel art de tamanho 32x32 pode ser convertida para essa unidade?

É simples, é só você se perguntar com um metro cabe quantos pixels? Daí damos o significado de por exemplo 1 metro = 32 pixels então a cada metro eu posso ter uma box/crate de um jogo sendo renderizada na tela, conseguiram sacar? Esse termo na Unity é chamado de PPU (Pixels per Unit), uma vez que eu defino o meu PPU eu acabo tendo a noção dos tamanhos e posições dos objetos 2D na tela.

Para quem ainda não está convencido das vantagens de se trabalhar com esse ambiente, pense agora que você pode usar todo o mundo 3D à sua disposição. Shaders, iluminação volumétrica, objetos 3D, enfim, recursos que estariam disponíveis apenas em engines 3D e que podem ser usados num jogo 2D para deixá-lo ainda mais bonito e moderno.

Para não ficar extensivo e massivo uma explicação direta sobre isso, vou soltando essas informações ao longo do desenvolvimento do jogo, assim fica mais fácil e natural de assimilar esse assunto com à prática em questão.

# Programando o BackgroundScroller

Amiguinhos, agora vamos pôr a mão na massa. Simbora!!!

Empolgadao2.png


Com o arquivo de texturas baixados, copie as imagens de background SiderialSpaceXX para a nossa pasta de texturas do projeto.

NOTA - Há três formas de se copiar conteúdo para as pastas do projeto

Você pode arrastar um asset (imagem, objeto 3D, áudio, etc.) diretamente da pasta windows/mac para a pasta que criamos no área de Project. Você pode copiar o conteúdo via explorer/finder e colar na pasta física do projeto, ou ainda você pode clicar com o botão do mouse direito e selecionar a opção "Import New Asset". Qualquer uma das três opções é válida e fazem a mesma coisa.​

Feito isso, elas já estarão adicionadas automaticamente no projeto. Antes de usá-las deveremos fazer uma pequena alteração. Selecione as seis imagens ao mesmo tempo e no Inspector altere a propriedade Filter Mode para "Point (no filter)". O que isso necessariamente que nos dizer? Isso significa que usaremos a imagem como ela foi concebida, sem filtro algum da Unity trabalhando em cima dela. Essa é a melhor opção, por exemplo, para quem quer trabalhar com pixel art na Unity, outras opções podem aplicar filtro que dão efeitos de distorção como blur e AA, e não é isso que vamos querer para essas imagens.

01_001.jpg


NÃO ESQUEÇA de apertar em "Apply" para salvar a alteração.

Essas imagens serão os nossos backgrounds do espaço sideral, serão as nossas estrelas. Arraste as três primeiras imagens para a nossa cena. Faça isso de uma em uma senão a Unity vai lhe pedir para criar uma animação. Feito isso, selecione as três imagens que já estão em nossa Hierarchy e altere no Inspector a propriedade de posição delas para (0, 0, 0).

Selecione agora a nossa câmera, ou seja, a "Main Camera". no Inspector veremos que na propriedade background temos um azul escuro, um azul tipo marinho selecionado, vamos substitui-lo para totalmente preto. Ainda nesse objeto, faça a alteração da propriedade size para "3.8". Verifique também se a sua câmera se encontra na posição inicial de (0, 0, 0).

01_002.jpg


Nossa Scene deve ficar mais ou menos com essa cara:

01_003.jpg


Note que, se você estiver com a Main Camera selecionada, teremos na Scene a Camera Preview, mostrando de ante-mão mais ou menos como a nossa cena ficará em execução.

Agora vá na janela Game e selecione a resolução para 1024x768, a opção Standalone como mostra a imagem logo a seguir:

01_004.jpg


Nada como começar do início, não? Voltamos à raiz dos jogos para criar, por que não, um Invaders temático da Outerspace. :ksafado

Ao apertar play (atalho Ctrl + P), você verá que a tela do jogo já se encontra em cima das imagens que colocamos, tudo com cara de espaço sideral, porém, temos um probleminha aí, tá tudo muito parado e estático, e não é isso que queremos, certo?

Vamos colocar um pouco de ação nessas imagens e fazê-las se mover infinitamente, dando uma sensação de continuidade do espaço. É por isso que temos 3 imagens colocadas em cena, cada uma ficará uma do lado da outra e alinhadas fora da área de nossa câmera, dessa forma quando uma imagem sair completamente da nossa viewport ela será reposicionada novamente após a última imagem da fila e assim sucessivamente com todas as outras imagens. É assim também que os jogos do tipo Runners funcionam, eles apresentam uma parte do cenário para o jogador e quando essa parte vira "passado" fora da visão do jogador ele reposiciona ela novamente ao final da fila. Bom, eu confesso que nunca programei nenhum runner em minha vida, nem ao menos vi um código de um, mas posso assegurar que é mais ou menos assim que eles funcionam.

NOTA - Reaproveitamento de Recursos é a Chave da Performance

Jogos bem otimizados costumam reaproveitar recursos que foram criados e jogados em cena. Isso é uma prática para TODA e QUALQUER programação de jogos e isso não depende da engine que você estiver trabalhando. Jogos bem programados e otimizados tendem a economizar espaço e, principalmente processamento de sua CPU e GPU. No caso do exemplo acima, poderíamos muito bem criar centenas de clones dessas imagens na nossa cena, mas estaríamos gastando recursos desnecessariamente, mesmo que isso fosse a maneira mais simples de se fazer. Na programação de jogos um conceito que, infelizmente não é comumente usado (principalmente por iniciantes) é de Pool de Objetos, que é um design pattern para reaproveitar recursos de objetos de um jogo. No meu joguete do Switch Hype Train, eu aplico vastamente essa técnica personalizada por mim. Dê uma conferida nela para entender melhor como funciona o pool na programação de jogos.​

Agora faça o seguinte, na pasta Scripts crie um novo script C# chamado de BackgroundScroller. Esse script será responsável em mover as imagens de background e reposicioná-las conforme o sentido que apontamos. Abra o arquivo e teremos inicialmente algo parecido com o link abaixo:

BackgroundScroller_01.cs

ATENÇÃO: Infelizmente o fórum não tem suporte a indentação e highlighting de código fonte, portanto, estou usando uma conta minha no GitHub para hospedá-los. Não se preocupe que os códigos apresentados estão sendo cadenciados parte a parte, você não irá se espantar em ver tudo pronto logo de uma vez, os códigos serão gradualmente criados um a um e parte a parte, assim como é o nosso curso. Até falei com um moderador para ver se ajustava a tag code daqui, mas nem sequer resposta obtive, paciência...​

Toda classe criada através da Unity irá herdar de MonoBehaviour como demonstra o template de criação de scripts que mexemos na parte 1 desse tutorial. MonoBehavior é uma classe importante porque ela nos dará suporte a como manipular os GameObjects e todos os outros recursos necessários à nossa programação em cena. São elas que viram componentes de um GameObject dos quais poderemos mexer suas propriedades via Inspector.

Para a nossa classe BackgroundScroller herdar de MonoBehaviour, importamos a namespace UnityEngine (linha #1). No escopo da nossa classe, temos dois métodos são eles o Start e Update. Start é executado uma única vez quando o nosso jogo é inicializado, para cada objeto que tiver um script anexado à ele (componente) o método Start será chamado uma vez. O método Update, por sua vez, é executado inicialmente após a execução de Start e, diferente do primeiro, o Update será executado toda vez de acordo com a frequência do seu jogo. Exemplo, se o seu jogo roda a 30 FPS, o seu Update provavelmente será executado 30x num intervalo de 1s, se o jogo roda a 60 FPS, provavelmente será executado esse método 60x em um 1s.

Método Start é comumente usado para inicializado de valores dos seus componentes e varíaveis enquanto o Update é comumente usado para input do usuário, lógica, I.A, etc. Há o método FixedUpdate que é bem semelhante ao Update, com a diferença que ele é executado ainda mais vezes e, por isso mesmo, é indicado para tratamento de física. Todos esses métodos que falei são executados automaticamente pela Unity, não cabendo a nós chamá-los arbitrariamente.

NOTA - Estude quando puder a ordem de execução dos eventos em Unity

É importante saber ao menos os eventos básicos que são chamados automaticamente pela Unity. Assim evitamos problemas na hora de onde e como organizar os nossos códigos e não sofrer com execução indesejadas de código ou até mesmo deadlocks. Essa página oficial é bem completa e mostra detalhadamente os eventos chamados e a ordem de execução deles. Não se preocupe em decorar tudo, eu mesmo não sei todos eles de cabeça, os que mais são comumente utilizados é bom aprender.​

Voltando ao código...

Retorna a sua janela de Hierarchy e crie um novo GameObject usando CTRL + SHIFT + N (ou botão direito na área de Hierarchy e selecione "Create Empty"). Renomeie de "GameObject" para "BackgroundScroller". Com esse objeto selecionado, na área do Inspector você verá escrito "Add Component" no final em um botão, clique sobre ele e comece a escrever o nome do script que acabamos de criamos (BackgroundScroller) e na própria lista ele será mostrado, selecione e já teremos adicionado o nosso componente a esse GO.

01_005.jpg


Pronto, agora o objeto BackgroundScroller tem o componente BackgroundScroller associado a ele.

Precisamos informar ao nosso script as imagens que iremos movimentar. Para tornar o código mais flexível possível para o maior número de situações vamos usar uma Lista (System.Collections.Generic.List). que irá nos permitir colocar a quantidade de background que desejarmos. Escreva alinha abaixo acima do método Start:

Código:
public List<GameObject> backgrounds;

Se a sua IDE reclamar que não está identificando o código é porque precisa importar a referência à lista. Escreva logo abaixo de using UnityEngine;

Código:
using System.Collections.Generic;

NOTA - Uma lista é um recurso de coleção em linguagem de programação

Listas são usadas para armazenas dados seguindo alguma ordem de armazenamento. Elas fazem parte de algo maior chamado coleção de dados. É amplamente usado em programação. Há outras formas de coleção de dados como arrays, queues, dictionaries, etc. Cada um tem suas próprias funcionalidades para lidar com coleção de dados. Para entender um pouco mais acesse esses links: 1 , 2.​


NOTA - A lista de backgrounds é pública para permitir adição de seus elementos via Inspector.

Campos (fields) como ints, float, bool, etc., e classes como strings, listas, etc. são visíveis ao Inspector se deixarmos essas propriedades como públicas ou serializadas. Dessa forma poderemos mexer suas propriedades independente do código que foi escrito.​

Adicione agora os seguintes campos:

Código:
public float speed = 1;
public GameObject swapBar;
public Direction targetDirection = Direction.None;

Embaixo de todos os campos que você já digitou, escreva a Enumeration abaixo:

Código:
public enum Direction
{
    Up,
    Down,
    Right,
    Left,
    None
}

O seu código nesse exato momento deve estar parecido com esse:

BackgroundScroller_02.cs

Na variável speed, determinaremos a velocidade de deslocamento das imagens, a variável swapBar será o objeto que quando uma imagem tocá-lo fará o reposicionamento dela para o final da fila das imagens, targetDirection irá apontar em qual direção essas estrelas irão se locomover, para isso, definimos uma enumeração indicando Up, Down, Right e Left.

O processo final dessa parte teremos algo assim, a cada imagem se deslocando e se reposicionando conforme toca no SwapBar (quadrado branco). Repare que a área da câmera fica meio que no centro, então pro jogador o cenário é sempre infinito porque ele não consegue ver o que está atrás da "cortina".

3o7bu1A6D538pupt0k.gif


Para criar o nosso SwapBar, vamos até a área de Project na pasta Textures e dentro dela com o botão direito do mouse siga esse caminho: "Create > Sprites > Square". Uma quadrado branco 2D será criado, araste-o à nossa cena em qualquer posição (Renomeio para "SwapBar"). Nesse momento deveremos colocar colliders em nossos objetos para que o comportamento deles sejam como nós esperamos. Cada uma das três imagens assim como o swapBar irá ter um collider associado. Temos vários tipos de colliders diferentes na Unity, sejam eles 2D ou 3D e com formas diferentes (quadrado, esférico, triangular, amorfo, etc.) Para as nossas imagens e SwapBar, deveremos ter um collider 2D chamado de Box Collider 2D.

Clique no SwapBar e no Inspector clique no botão Add Component e escreva "Box Collider 2D". Novamente ainda nele, clique no Add Component e escreva agora "Rigidbody 2D". Não se importe com a posição que ele fique, ela será calculada dinamicamente pelo nosso script BackgroundScroller.

Ainda no Inspector do SwapBar, no componente Rigidbody 2D vá até a propriedade Body Type e mude para "Kinematic". Dessa forma, o nosso objeto do SwapBar não irá sofrer ação da gravidade e cair no espaço quando o jogo for executado.

01_007.jpg


Selecione agora as três imagens do espaço sideral (SiderialSpace...) e clicando no Add Component coloque Box Collider 2D. Com as três selecionadas marque a propriedade Is Trigger, conforme é apresentado na imagem abaixo:

01_008.jpg


Para que a nossa colisão funcione adequadamente, vamos definir uma tag que será usada para cada uma dessas imagens de background. Para adicionar uma tag, vá em qualquer imagem e clique em Tag (geralmente fica marcado como "Untagged") e no final clique em Add Tag, conforme a imagem a seguir:

01_009.jpg


Agora é só expandir a lista de Tags e adicionar a tag "Space" apertando o ícone de "+".

01_010.jpg


NOTA - Passos repetidos serão minimizados

Como provavelmente é a primeira vez que você está fazendo esses passos, eu estou mostrando passo a passo como fazê-los, incluindo até imagens. Na medida que o tutorial avança, não irei repeti-los da mesma forma como foram feitos já que não necessidade para tal. Portanto, se precisar criar passos que já foram executados antes, irei enxugar a explicação porque já subentenderá que você sabe como fazer. Se, por exemplo, eu pedir para criar uma nova Tag de nome "Enemy", você já tem que saber como criar uma. Assim o tutorial ficará também mais sucinto e objetivo, sem necessidades de repetição de explicações.​

Com a tag criada, certifique-se de para cada uma das 3 imagens selecione a tag "Space".

Selecione agora na Hierarchy o BackgroundScroller. Repare que as propriedades que definimos antes via script aparem no Inspector, isso graças ao poder de binding da Unity com a C#, possibilitando a nós alterar seus valores sem ter que ficar entrando toda hora no script. De antemão adianto que os valores alterados via Inspector prevalecem sobre os valores que constam no código fonte. Se por exemplo eu alterar a speed no Inspector para 2 e o código fonte estiver marcado como 1, o jogo levará em consideração o valor alterado pelo Inspector. Não esqueça disso, isso é bem importante.

Na propriedade de Backgrounds, coloque o valor 3 (esse será o tamanho da nossa lista) e arraste cada uma das imagens para o um dos espaços formados na lista. Arraste também o SwapBar para a propriedade SwapBar e teremos finalmente uma tela como a seguir:

01_011.jpg


Agora estamos pronto para fazer o restante do código de BackgroundScroller.

Vamos colocar 6 novas variáveis:

Código:
private float x, y;
private float sizeX, sizeY;
private const float offset = 0.1f;
private static BackgroundScroller _instance;

Repare que todas elas são marcadas como "private", variáveis com escopo de visibilidade privado são restritas à classe que a definiu. Nem o Inspector conseguirá enxergar assim como outras classes que farão acesso a instância da BackgroundScroller terão acesso. Portanto, definimos variáveis como pública caso queiramos trocar seus valores fora do escopo da sua classe de origem.

NOTA - Para saber mais sobre os modificadores de acesso

Palavras chaves como "public", "protected", "private" e "internal" são modificadores de acesso que podem restringir a visibilidade dos membros (variáveis e métodos) de uma classe. Para saber um pouco mais a respeito deles, acesse esse link.​

Voltando ao código...

As variáveis x e y irão dizer a direção e sentido que as imagens de background irão se deslocar. Ser "x" ou "y" informa a direção e o sinal de cada um delas ("+" ou "-") informará o sentido.

As variáveis sizeX e sizeY informam o tamanho de uma imagem. Como todas as imagens de background que definimos possuem o mesmo tamanho (1024x768), basta pegar o tamanho de uma que você já sabe o tamanho de todas. O tamanho vai ser importante para sabermos posicioná-las uma ao lado da outra, ou uma em cima da outra, a depender da direção que escolhemos.

A variável offset será meio que opcional, já que ela que definirá o gap ou melhor, a lacuna de distância que ficará uma imagem da outra.

A variável _instance é para garantir que só haja uma única instância de BackgroundScroller ativa em nossa cena. Isso faz parte do padrão de projeto conhecido como singleton é uma prática de programação que não é restrita a nenhuma linguagem em específico. Futuramente explicarei em detalhes esses conceitos mas para esse tutorial iria ficar muito extensivo.

NOTA - Deixe o seu código organizado

Em uma classe deixe a definição das suas variáveis públicas aparecendo primeiro, das com modificadores de acesso mais restritivos aparecendo logo depois, depois de pôr as variáveis da classe, coloque a outra parte de seus membros como os getters e setters (se tiver), depois o construtor da classe ("método" com o mesmo nome da classe sem a palavra chave "void") e, por último, os métodos que fazem parte dela. Tudo seguindo também a ordem dos modificadores de acesso.​

Por último antes de partimos ao código dos métodos, vamos pôr um getter que dará acesso à BackgroundScroller em qualquer outra classe via singleton. Não procure entendê-lo ainda agora, em outros tutoriais explicarei melhor o seu conceito.

Código:
public static BackgroundScroller instance
{
    get
    {
        return _instance ?? (_instance = FindObjectOfType<BackgroundScroller>());
    }
}

Nesse momento, o nosso código deverá ficar assim:

BackgroundScroller_03

Agora vamos implementar os métodos. Como já sabemos o Start será executa uma vez logo o jogo ser inicializado. É nele que vamos arrumar a posição inicial das imagens, o Update iremos renomeá-lo para FixedUpdate e ele será executado continuamente enquanto o jogo estiver ativo. Vamos usar o FixedUpdate porque vamos mexer com componentes de física da Unity, como o SwapBar usa um RigidBody 2D, é recomendável usá-lo.

No método Start, defina as variáveis amountX e amountY, vamos calcular a posição de cada imagem e armazenar nelas temporariamente.

Código:
var amountX = 0f;
var amountY = 0f;

A variável targetDirection modificará os valores de x e y para sabermos onde exatamente cada imagem vai ficar.

Código:
//calcula o sentido do deslocamento das imagens conforme a direção informada
if (targetDirection == Direction.Up)
    y = -1f;
else if (targetDirection == Direction.Down)
    y = 1f;
else if (targetDirection == Direction.Left)
    x = 1f;
else if (targetDirection == Direction.Right)
    x = -1f;

As nossas coordenadas seguem o padrão de um plano cartesiano, mas especificamente do primeiro quadrante. Então da esquerda para direita o valor de "x" irá aumentar, enquanto o contrário diminui. De baixo pra cima o valor de "y" irá aumentar, enquanto o contrário diminui.

Para cada imagem da lista de background, iremos fazer esses cálculos, para percorre-las defina um foreach.

Código:
foreach (var iBackGround in backgrounds)
{
}

iBackground será temporariamente uma imagem de backgrounds. Ele começa do primeiro elemento e vai ao último associado. Como nossa lista é composta de 3 imagens, pegamos a primeira, depois a segunda e por último a terceira; terminando esse procedimento, o condigo continua depois do escopo do "foreach".

Dentro do "foreach" coloque:

Código:
var sr = iBackGround.GetComponent<SpriteRenderer>();

Leia-se: pegue o componente SpriteRenderer que está associado ao GameObject iBackGround.

Se voltarmos rapidinho ao Inspector de alguma das imagem, veremos que temos esse componente associado a cada uma delas:

01_012.jpg


Então usamos meu game object.GetComponent<meu componente>(); para obter um componente. Exemplo, para obter o componente Box Collider 2D da imagem seria só substituir o ".GetComponent<SpriteRenderer>();" para ".GetComponent<BoxCollider2D>();". Mas como queremos a imagem para sabermos o seu tamanho, então será o SpriteRenderer mesmo.

l4FGKpSInybjPEd20.gif


Agora que você aprendeu como pega os componentes de um GameObject. Vamos alterar a posição de cada imagem. Como a primeira imagem ficará logo à vista sob a câmera, a posição dela será a inicial (0, 0), igual a da câmera, as demais ficarão ou acima ou abaixo dela.

Escreva o seguinte código ainda dentro do "foreach":

Código:
if (x > 0 || x < 0)
    iBackGround.transform.position = new Vector3((amountX + offset) * x, 0);
else if (y > 0 || y < 0)
    iBackGround.transform.position = new Vector3(0, (amountY + offset) * y);

Lembra que alteramos os valores de x ou y de acordo com a targetDirection? Se por exemplo, "x" for diferente de zero, implica dizer que iremos alterar os valores do eixo X, se "y" for diferente de zero, será o eixo Y. Repare que não estamos usando o comparativo como (x != 0), porque x não é inteiro, assim como y também não, são números de ponto flutuante (float).

Lembra do Transform?

nH6Vgy6.jpg


Acessamos as suas propriedades de posição, rotação e escala usando meu game object.transform.propriedade. Queremos mover as imagens, então a propriedade do Transform necessária para isso é a position.

Qualquer uma das três propriedades do Transform são imutáveis, por causa disso, toda vez que iremos mexer com elas precisamos criá-las novamente (por isso usamos o modificador new). Todas elas usam Vector3. Que é um vetor que guarda as propriedades de X, Y e Z no espaço.

Coloque o seguinte código:

Código:
amountX += sr.bounds.size.x;
amountY += sr.bounds.size.y;

Leia-se: incremente amoutX e amountY com a largura e a altura respectivamente da imagem.

Agora temos o código do foreach finalizado:

Código:
foreach (var iBackGround in backgrounds)
{
    var sr = iBackGround.GetComponent<SpriteRenderer>();

    if (x > 0 || x < 0)
        iBackGround.transform.position = new Vector3((amountX + offset) * x, 0);
    else if (y > 0 || y < 0)
        iBackGround.transform.position = new Vector3(0, (amountY + offset) * y);

    amountX += sr.bounds.size.x;
    amountY += sr.bounds.size.y;
}

Como tudo isso funciona? A partir das coordenadas, saberemos se vamos colocar a imagem na ordem vertical ou horizontal, e com o sinal saberemos se ficará à esquerda ou direita da câmera, porque iremos deslocar essas imagens. Execute o jogo e você verá que as imagens se arrumaram na ordem adequada, mude os valores de targetDirection e execute novamente o jogo, observará que as imagens mudaram de ordem. Elas ainda estão estáticas, mas já estão na posição certa a partir da direção informada.

Agora vamos tratar do SwapBar, essa barrinha que irá fazer uma imagem mudar de posição quando tocá-la. O SwapBar tem que ficar do lado oposto às imagens, se as imagens se postam em cima, então o SwapBar ficará abaixo de todas elas, se as imagens se postam à direita, então o SwapBar ficará sempre à sua esquerda.

Código:
//calcula a posição do SwapBar
var background = backgrounds[0];
sizeX = background.GetComponent<SpriteRenderer>().bounds.size.x;
sizeY = background.GetComponent<SpriteRenderer>().bounds.size.y;

swapBar.transform.position = new Vector3(
    (Camera.main.gameObject.transform.position.x + (sizeX * 2)) * x * -1,
    (Camera.main.gameObject.transform.position.y + (sizeY * 2)) * y * -1
);

As variáveis sizeX e sizeY contém o valor do tamanho da dimensão de uma imagem, como todas as três imagens apresentam a mesma dimensão, então não importa qual eu pegue. Multiplicamos o sizeX e sizeY por 2 para darmos um espaço ainda maior entre a SwapBar e as imagens. Estamos alterando a posição do SwapBar de acordo com a posição da câmera. Como queremos que o sentido dela seja diferente das imagens, então multiplicamos as variáveis de x e y por -1.

ATENÇÃO - NÃO altere a propriedade Z de cada imagem e objeto da cena, deixe tudo como zero. Alterando essa propriedade, os objetos estarão em nível de profundidade diferentes e os colliders não conseguirão identificá-los. Lembre-se, nosso jogo é 2D e não precisamos mexer com essa propriedade. A câmera, quando iniciamos um jogo, ela começa na posição (0, 0, -10), deixe-a do jeito que está. Durante o nosso jogo, não iremos mexer em sua posição e ela se encontrará totalmente estática.​

Até dado momento, nosso código está assim:

BackgroundScroller_04

Para continuar o nosso código, é necessário entender essa breve explicação sobre reposicionamento. Quando uma imagem toca o SwapBar, ela volta a se anexar a última imagem da fila e assim sucessivamente com todas as imagens. Para isso, temos que saber o índice da imagem que acabou de se esbarrar no SwapBar e o índice da imagem mas distante, para saber onde iremos posicioná-la.

Como temos 3 imagens, temos os seguintes índices:

0, 1, 2

A imagem de índice 0 é a primeira, a de índice 2 é a última ou terceira imagem. Quando a de índice 0 toca o SwapBar, teremos o seguinte resultado:

1, 2, 0.

E depois...

2, 0, 1

Até chegarmos novamente no...

0, 1, 2

E assim ficará em loop eterno.

Conseguiram perceber a mudança de ordem? Para a de índice 0 saber onde se reposicionar, ela tem que saber onde a de índice 2 está, para a de índice 1 saber onde ela deve se reposicionar, ela tem que saber onde a de índice 0 está, e assim por diante. Se tivéssemos mais de 3 imagens, a lógica aplicada seria exatamente a mesma.

Crie agora o seguinte método:

Código:
private int GetBackgroundIndex(GameObject background)
{
    for (var i = 0; i < backgrounds.Count - 1; i++)
    {
        if (background == backgrounds[i])
            return i;
    }

    return -1;
}

O método acima percorrerá a lista de backgrounds e quando encontrar o mesmo objeto retorna o seu índice, ou seja, a sua posição na lista.

Já temos agora o índice da imagem que tocou, agora temos que saber o índice da última imagem. Vamos criar o método que a partir do tamanho da lista e do índice atual, calcular o último índice correto e já reposicionar a imagem:

Código:
public void RepositionSpace(GameObject go)
{
    //a partir do índice atual do GO
    var index = GetBackgroundIndex(go);

    //pega o índice da última imagem
    for (var i = 0; i < backgrounds.Count - 1; i++)
    {
        index++;

        if (index > backgrounds.Count - 1)
            index = 0;
    }

    //calcula a nova posição da imagem a partir do seu índice
    var lastBackgroundPosition = backgrounds[index].transform.position;
    go.transform.position = new Vector3(lastBackgroundPosition.x + sizeX * x, lastBackgroundPosition.y + sizeY * y);
}

Esse método será chamado pelo SwapBar quando a imagem tocá-lo. O GameObject passado como parâmetro é justamente o GO da imagem que o tocou. Ele pega o índice atual da imagem pelo método GetBackgroundIndex e a partir daí procura o índice válido para a sua reposição. A variável lastBackgroundPosition é justamente a posição da imagem que queremos, uma vez com essa posição, acrescentamos o tamanho da imagem (usando sizeX ou sizeY a depender da orientação escolhida) e multiplicamos pelo sentido (x ou y).

Sei que realmente parece confuso tudo isso, mas com a prática e esforço as coisas começam a ficar mais claras. Até se acostumar com a lógica de programação de games, com C# e a própria Unity, leva-se um tempo. Não se desespere.

DON'T BE AFRAID!!!

scurrd.gif


Agora vamos criar na pasta de Scripts um novo script C# chamado de SpawBar, de mesmo nome do nosso quadradinho lá na cena. Antes de abrir esse script, lembre-se de adicioná-lo ao nosso objeto.

A classe SpawBar será muito simples, implementa o método OnTriggerEnter2D e verifica se o objeto que colidiu é o com a tag "Space", caso for, ele chamará o método RepositionSpace que se encontra na classe BackgroundScroller.

Código:
public class SwapBar : MonoBehaviour
{
    private void OnTriggerEnter2D(Collider2D other)
    {
        if (other.gameObject.CompareTag("Space"))
        {
            BackgroundScroller.instance.RepositionSpace(other.gameObject);
        }
    }
}

Então você pode estar se perguntando... Quem diabos é que chama o método OnTriggerEnter2D? Na verdade ele é chamado automaticamente pela Unity quando um objeto colide com outro. No caso para isso funcionar, definimos que cada collider das imagens fossem marcadas como trigger, lembra? Dessa forma a Unity sabe qual método usar quando a colisão ocorrer.

Há vários outros métodos de colisão, mas não cabe falar deles por agora.

Nosso SwapBar ficou assim:

SpawnBar_01

Voltemos à nossa classe BackgroundScroller e faremos a seguinte alteração no método FixedUpdate.

Código:
void FixedUpdate()
{
    foreach (var iBackground in backgrounds)
    {
        iBackground.transform.Translate(Time.deltaTime * speed * x * -1, Time.deltaTime * speed * y * -1, 0);
    }
}

Como você já sabe, esse método será chamado sucessivas vezes durante a execução do jogo. O método Translate serve para mover o objeto a partir de uma posição espacial (x, y, z). Lembre-se que TUDO que se refere à posição no espaço, rotação no espaço, e escala faz parte de Transform. A variável deltaTime é calculada automaticamente pela Unity a cada chamada do método FixedUpdate. Tudo que envolve física, movimento, animação, etc, tudo que dê a sensação de mover alguma coisa, ela deverá ser chamada. Ela é necessária para fazer seu jogo ter um framerate independente.

Multiplicamos o deltaTime pela velocidade (speed) pelo sentido inverso e pela direção (x ou y) e temos agora as nossas imagens se movimentando.

NOTA - Experimente

Mude os valores das nossas variáveis, altere a velocidade (speed), altere os sentidos, mude a direção pelo Inspector e vê como os componentes do jogo se comportam, essa é também uma forma de aprender e entender melhor o código. Veja que se as variáveis x e y não fossem iguais a zero em nenhum momento, as nossas imagens se movimentam de forma diagonal ao invés de ortogonal. É fazendo experimentos que entendemos melhor o funcionamento de um jogo.​

O nosso código até dado momento deve ser:

BackgroundScroller_05

Nosso jogo já está com o background pronto, no próximo capitulo iremos abordar novos assuntos. ;)

PARTE III
 
Ultima Edição:

Preses

Mil pontos, LOL!
Mensagens
10.918
Reações
7.198
Pontos
1.189
Marcando pois pretendo ler depois.

Parabéns pela iniciativa!
 

Perekopeko

Bam-bam-bam
Mensagens
1.113
Reações
2.137
Pontos
453
Agora vamos criar na pasta de Scripts um novo script C# chamado de SpawBar, de mesmo nome do nosso quadradinho lá na cena. Antes de abrir esse script, lembre-se de adicioná-lo ao nosso objeto.

A classe SpawBar será muito simple...

Imagino que os dois que negritei seriam SpawnBar o nome, e tem vários outros SwapBar pelo texto.
 
Ultima Edição:

Landstalker

Lenda da internet
Mensagens
19.356
Reações
39.661
Pontos
1.584
Imagino que os dois que negritei seriam SpawnBar o nome, e tem vários outros SwapBar pelo texto.

O nome certo é SwapBar, swap de troca. Eu tinha alterado durante o jogo e não alterei tudo no documento, valeu por informar.
 

Landstalker

Lenda da internet
Mensagens
19.356
Reações
39.661
Pontos
1.584
Dei uma olhada, no texto em si não tem mais SpawnBar só swap...
 

LucianoBraga

Operador de Marreta
Membro STAFF
Mensagens
50.279
Reações
175.734
Pontos
2.234
Muito legal. Vou dar uma olhada com mais calma depois.
 


Landstalker

Lenda da internet
Mensagens
19.356
Reações
39.661
Pontos
1.584
Muito legal. Vou dar uma olhada com mais calma depois.

Espero que goste.

-------------------

O intuito é auferir um jogo simples e que já traz algumas técnicas usadas em programação de jogos 2D. Pessoal, quero o feedback de vocês, se tá fácil de entender, se há partes que ficou confuso, etc.
 

Perekopeko

Bam-bam-bam
Mensagens
1.113
Reações
2.137
Pontos
453
Outra coisa, na hora que você fala pra mexer na MainCamera, você fala pra colocar a position em (0, 0, 0), mas nesse caso o background fica na frente de tudo. Deixando em (0, 0, -10) o background continua só de fundo.

No mais, ótimo tuto sempre quis aprender a fuçar na Unity mas ficava de preguiça.
 

Landstalker

Lenda da internet
Mensagens
19.356
Reações
39.661
Pontos
1.584
Main Camera fica na (0, 0, -10), é o único objeto que não deve ser mexido, para esse jogo ela sempre ficará fixa. Se olharmos nessa imagem:

zRM0eVB.jpg


A posição zero é proposital para fazer com que a câmera renderize tudo que estiver acima disso (-9, -8, ...0, 1, ...). Se realmente a deixarmos em Zero (ou seja: Vector3.Zero;) ela não conseguirá renderizar os objetos que estão na posição 0 em z).

Eu acho que não deixe claro, vou editar e fazer observação.

Continue dando feedback, às vezes acaba deixando alguma informação passar.
 

Landstalker

Lenda da internet
Mensagens
19.356
Reações
39.661
Pontos
1.584
Eu vi a informação apesar de eu ter deixado claro que eram as imagens, os sprites que deveriam permanecer sem profundidade, acabei dando uma atualização na parte de "ATENÇÃO" que me referi à posição dos objetos. Veja lá.
 

thiagoencd

Bam-bam-bam
Mensagens
165
Reações
181
Pontos
469
Muito legal, deu quase tudo certo, só que as imagens passam uma vez e não voltam mais. E quando vejo na Scene só tem o quadrado do jogo mesmo, não aparece o quadrado da imagem se deslocando pra baixo e depois voltando para o inicio da fila como no seu.

Edit: Consegui resolver em partes, esqueci de colocar uma parte importante do código, só que agora, depois do primeiro looping, eles começam a se embolar ficando um em cima do outro, ai começa a aparecer uma parte com a tela totalmente preta.
 
Ultima Edição:

Landstalker

Lenda da internet
Mensagens
19.356
Reações
39.661
Pontos
1.584
Verifique se:

- Todos os quadrados da imagem do SiderialSpace estão com as mesmas propriedades (mesma posição ("0, 0, 0"), mesmo Sorting Layer ("Background"), mesmo Order in Layer ("5"). Se todos os SiderialSpace estão com um componente BoxCollider 2D e a propriedade IsTrigger está marcada. Se tambérm a a Tag "Space" está definida para cada um deles;
- Se o SwapBar está com um componente Box Collider 2D e um componente RigidBody 2D marcado como "Kinematic" na propriedade Body Type;
- Se no Game Object BackgroundScroller você setou a SwapBar e cada uma das imagens.
 

thiagoencd

Bam-bam-bam
Mensagens
165
Reações
181
Pontos
469
Verifique se:

- Todos os quadrados da imagem do SiderialSpace estão com as mesmas propriedades (mesma posição ("0, 0, 0"), mesmo Sorting Layer ("Background"), mesmo Order in Layer ("5"). Se todos os SiderialSpace estão com um componente BoxCollider 2D e a propriedade IsTrigger está marcada. Se tambérm a a Tag "Space" está definida para cada um deles;
- Se o SwapBar está com um componente Box Collider 2D e um componente RigidBody 2D marcado como "Kinematic" na propriedade Body Type;
- Se no Game Object BackgroundScroller você setou a SwapBar e cada uma das imagens.
Faltaram essas em negrito, alterei mas não mudou muita coisa.
To tentando fazer uns debugs aqui, tentando ver esses index mas já to com a cabeça meio fervendo, vou dar um tempinho e depois eu vejo.
 

Landstalker

Lenda da internet
Mensagens
19.356
Reações
39.661
Pontos
1.584
Faltaram essas em negrito, alterei mas não mudou muita coisa.
To tentando fazer uns debugs aqui, tentando ver esses index mas já to com a cabeça meio fervendo, vou dar um tempinho e depois eu vejo.

Você tinha terminado toda essa parte 01? Eu vou revisar o tutorial dessa parte e ver se faltou algo de colocar aqui. É porque eu costumo escrever o tutorial depois de fazer uma parte do jogo, e meu medo e ter deixado alguma info importante de lado.
 

thiagoencd

Bam-bam-bam
Mensagens
165
Reações
181
Pontos
469
Você tinha terminado toda essa parte 01? Eu vou revisar o tutorial dessa parte e ver se faltou algo de colocar aqui. É porque eu costumo escrever o tutorial depois de fazer uma parte do jogo, e meu medo e ter deixado alguma info importante de lado.
Não precisa não, já achei! Foi algum erro no código do BackgroundScroller.cs, copiei o seu código em cima do meu e funcionou direito. Agora estou vendo o teu e o meu lado a lado linha por linha pra tentar achar meu erro. O tutorial tá ótimo, só não achei aquelas partes que coloquei em negrito e nem sei se fez alguma diferença eu mudar aquilo lá.
Valeu!!!

PQP!!!!!!!!!!!!!!!!!!!!!!!!
Era isso aqui:

private int GetBackgroundIndex(GameObject background)
{
for(var i = 0; i < backgrounds.Count - 1; i++)
{

if (background = backgrounds)
{
return i;
}
}
return -1;
}
 
Ultima Edição:

Landstalker

Lenda da internet
Mensagens
19.356
Reações
39.661
Pontos
1.584
É porque backgrounds é uma lista, e toda lista no C# se usarmos o for, deveremos acessar pelo seu índice. Portanto acessamos assim:

Código:
background == backgrounds[i]

considerando o "i" o índice corrente.

Caso use o foreach, o código fica mais enxuto e objetivo:

Código:
foreach(iBackground in backgrounds)

Sendo o "iBackground" a varriável corrente da lista backgrounds.
 

thiagoencd

Bam-bam-bam
Mensagens
165
Reações
181
Pontos
469
Sim, o código eu até entendi legal, foi só erro de sintaxe mesmo, coloquei = ao invés de ==
 

Jor-el

Bam-bam-bam
Mensagens
925
Reações
4.069
Pontos
433
Se besta, criar um jogo é difícil, explicar então......parabéns.
 

j0kk3r

Mil pontos, LOL!
VIP
Mensagens
12.352
Reações
16.925
Pontos
1.284
Ô saco. Parece que é de propósito, tô com uma cacetada de coisa pra fazer e não consegui abrir a unity ainda.
 

sparcx86_GHOST

Ei mãe, 500 pontos!
Mensagens
26.774
Reações
18.254
Pontos
784
Para os que estão começando agora não se intimidem, parece complicado mas quando você faz o passo a passo e está com o programa aberto seguindo as instruções não é tão complicado. Precisa apenas se focar muito nos conceitos pois são essenciais para aprendizado, se perguntar porque isso ou daquilo não apenas fazer porque está escrito.
Bom projeto.
2D0B0AAD44270F265BDC15604249A7E2
 
Topo Fundo