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 #03] Outer Space Invaders

Landstalker

Lenda da internet
Mensagens
19.355
Reações
39.658
Pontos
1.584
Outer_Space_Invaders.png

:: Tópicos

- Destruindo a Bullet depois de um tempo
- Criando novos Inimigos
- Waves
- Aprimorando as Waves

# Destruindo a Bullet depois de um tempo

Se repararmos quando atiramos com a nossa nave e as balas não acertam a um inimigo o que o ocorre com elas? Elas ficam lá, na memória ainda ocupando espaço. Em nossa Hierarchy, as balas vão se somando continuamente enquanto estamos atirando. Isso é ruim porque depois de um tempo, criamos vários objetos que não utilizamos mais, elas ficam infinitamente navegando pelo espaço e não deveremos permitir isso.

giphy.gif


Há várias maneiras de lidar com isso. Poderemos criar um sprite com um collider afastado da área da câmera cuja a colisão com a bala faça essa ser destruída. O problema dessa forma é que ficam as coisas mais enrijecidas, se eu projetar o meu jogo o tempo todo considerando que as balas do jogador são atiradas pra cima, enquanto a dos inimigos pra baixo, vá lá que eu resolva mudar isso, então terei que toda hora criar vários colliders para prever todas as situações de posição e impacto da bala, essa não é melhor prática possível, ao menos para essa situação.

Então, uma forma ainda mais simples e e eficiente de resolver esse problema é criar um temporizador. Depois de n segundos a bala automaticamente se destrói, permitindo a liberação de recursos.

Abra o arquivo Bullet.cs e adicione a seguinte variável de classe:

Código:
private float destroyTimeCouner;

Agora adicione o método Update que irá contar essa variável até alcançar o segundo desejado e então eliminar o nosso Game Object.

Código:
private void Update()
{
    destroyTimeCouner += Time.deltaTime;

    if (destroyTimeCouner >= 5)
    {
        Destroy(gameObject);
    }
}

Eu coloquei 5 segundos, mas poderemos tanto aumentar ou diminuir esse tempo. O ideal é colocar um tempo que você saiba que a bala já saiu da visão do jogador, porque seria inconveniente pôr um tempo muito baixo onde a bala estaria ainda na área da câmera e o jogador visse a sua bala sumir assim do nada.

Execute o jogo agora e repare que os Game Objects da bala simplesmente desaparecem depois de um tempo.

NOTA - Essa ainda não é a melhor maneira de tratar uma sucessão de objetos clonados na tela.

Pra tornar essa parte mais dinâmica e objetiva, estou destruindo os objetos criados ao invés de reutilizá-los, mas essa não é a melhor forma de lidar com isso. Lembra que lá atrás eu falei sobre o Pool de Objetos e como isso é importante para os jogos? Então, o modelo ideal é criar já uma quantidade inicial de balas e reaproveitá-las sem eliminá-las da memória. O pool funciona, como vimos brevemente antes, como um gerenciador de recursos, um gerenciador de objetos para reaproveitá-los durante uma instância do jogo. Criar e deletar Game Objects constantemente pode ser um trabalho penoso para a engine. A nossa sorte é que os nossos GOs são simples, demandando de poucos recursos do hardware e ocupando muito pouco do espaço em memória e processamento. Mas como via geral, é bom manter a prática de usar um pool. Ao término desse tutorial, farei uma parte explicando isso na prática.​

# Criando novos Inimigos

Como já diz o ditado: "É lamentável um homem de apenas um só livro", aqui eu falo: "É lamentável um jogo de apenas um só inimigo". Nada como um jogo recheado de inimigos com jogabilidades e mecânicas distintas, não é mesmo?

Vamos deixar o KongClassic sem munição, só para distinguir dos demais. Antes, deveremos criar o prefab dele assim como fizemos com a bala que o nosso Player atira. Arraste o KongClassic que está em sua Hierarchy até a pasta de Prefabs e teremos o prefab dele criado. Vamos precisar dos prefabs de cada um dos inimigos para "spawmá-los" durante o jogo quando formos criar as waves. Lembre-se que para se certificar que o seu prefab foi criado, o KongClassic ficará marcado como azul na Hierarchy e na pasta de Prefabs ele será marcado com uma caixa azul, igual a da bala.

Agora vamos criar um novo inimigo, o KongFeliz. Com o GO do KongClassic selecionado na Hierarchy, aperte CTRL+D e duplique o objeto, teremos algo como "KongClassic (1)". Esse kong servirá como molde para a criação do novo kong, mas antes de fazermos qualquer alteração, temos que desvinculá-lo ao seu prefab original, senão, qualquer alteração feita será repercutida em KongClassic ao invés do novo KongFeliz.

Com o "KongClassic (1)" selecionado, vá até ao menu "GameObject > Break Prefab Instance". Essa opção irá quebrar o vínculo de um GO com o seu prefab, ficaremos, portanto, seguros em alterar qualquer propriedade sem problemas. Verificamos que deu tudo certo quando ele deixou de ficar azul, indicando que esse GO não tem vínculo com quaisquer prefab do jogo criado.

SYibRf1.jpg


Dê um rename para KongFeliz, e no componente Sprite Renderer na propriedade Sprite deveremos mudar a imagem que ele faz referência, trocando de kClassic para kFeliz.

OgeuRYw.jpg


Aperte a bolinha que fica ao lado da caixa do Sprite e teremos a seguinte tela:

10js7YR.jpg


É só começar a digitar o nome da imagem e selecioná-la e o nosso sprite já estará com a imagem atualizada.

A outra forma de se alcançar isso seria arrastar diretamente a imagem do kFeliz para a propriedade Sprite.

Quase que não haverá diferenças entre os kongs, já que basicamente eles possuem quase as mesmas propriedades além da mudança do nome, da imagem e o tipo de bala que cada um pode atirar.

0KdzmxF.png


Note que você pode fazer um ajuste no BulletPoint do KongFeliz para ficar mais centralizado na boca.

MTZ6DTW.jpg


Agora que já temos o prefab do KongClassic criado, poderemos apagá-lo de nossa Hierarchy que não teremos problema pois iremos instanciá-lo futuramente quando tivermos às waves criadas.

Pegue agora o prefab da BulletPlayer e arraste até à cena na posição próxima ao KongFeliz.

Assim como fizemos entre o KongClassic e o KongFeliz, iremos fazer com a bala do jogador e a do KongFeliz nessa ordem:

- Duplique o objeto (CTRL+D);
- Quebre o seu vínculo com o prefab original via menu "GameObject > Break Prefab Instance";
- Renomeie o GameObject para "BulletFeliz";
- No Inspector vá até o componente Bullet e mude a propriedade Caster de "Player" para "Enemy" (assim será possível saber em quem a bala irá acertar).

Para a bala do jogador, definimos dois scripts, o Bullet.cs e o BulletPlayer.cs. O primeiro irá permanecer, já que ele é genérico e vai servir para qualquer bala do jogo, o segundo iremos descartar para o inimigo porque iremos criar um novo script para o controle do comportamento dessa bala.

Remova o componente BulletPlayer do GameObject BulletFeliz através do Inspector seguindo a imagem abaixo:

Su9zb08.jpg


Crie o script BulletFeliz.cs e o adicione ao GameObject, agora abra-o em sua IDE. O BulletFeliz ficará bem parecido com o BulletPlayer, ambos herdam de BulletAbstract e, como consequência iremos implementar um movimento para ele via método Movement; o que acontece quando a bala acerta algo, método Hit e como a bala é destruída via método Kill.

Código:
public class BulletFeliz : BulletAbstract
{
    void Start()
    {

    }
    void Update()
    {

    }

    public override void Movement()
    {
    }

    public override void Hit()
    {
    }

    public override void Kill()
    {
    }
}

O seu movimento ficará bem parecido com a bala do jogador, com a diferença que em vez de ela ir pra cima, ela irá se deslocar pra baixo. O sinal de negativo no eixo Y é que indicará o sentido que a bala irá percorrer nesse eixo. No método Movement temos:

Código:
public override void Movement()
{
    transform.Translate(0, - bullet.speed * Time.deltaTime, 0);
}

Dê Play e verá que a bala se move à mesma velocidade da bala do jogador, vamos deixá-la na metade dessa velocidade. Dê Stop (caso executou o jogo) e altere via Inspector o valor de sua propriedade Speed para "5".

No método Hit, o nosso target será sempre o jogador, já que definimos o jogador como alvo da nossa colisão para esse tipo de bala:

Código:
public override void Hit()
{
    var player = bullet.target.GetComponent<Player>();

    player.hp -= bullet.damage;

    if (player.hp <= 0)
        player.Kill();
}

Essa bala não irá atravessar alvos, portanto, uma vez que o alcance, ela irá ser destruída.

Código:
public override void Kill()
{
    Destroy(gameObject);
}


Agora o que deveremos fazer é pôr essa bala para ser usada no nosso KongFeliz. Vamos reutilizar algumas variáveis que já usamos no script do Player.cs, elas são as mesmas variáveis para controle das munições, coloque-as no Enemy.cs:

Código:
public GameObject bulletPrefab;
public GameObject firePoint;
public float fireRate = 0;

Coloque também a variável fireCounter pois será ela que irá armazenar o tempo até chegar no fireRate:

Código:
private float fireCounter;

Não quero que o KongFeliz tenha sempre o mesmo tempo de cast da bala, então defini um método para gerar um tempo aleatório para a variável fireRate, assim a cada tiro dado, o próximo tiro será lançado num tempo provavelmente diferente do anterior.

Código:
private void GenerateRandomFireRate()
{
    fireRate = Random.Range(0.5f, 2f);
}

O tempo dado varia entre meio segundo até dois, mas fique à vontade para aumentar ou diminuir esses tempos ou até mesmo deixá-lo fixo, sem precisar chamar esse método.

No método Start deveremos chamar o método GenerateRandomFireRate para que tenhamos o nosso primeiro tempo definido quando esse objeto for instanciado:

Código:
void Start()
{
    GenerateRandomFireRate();
}

Adicione o método Update, que fará um cast da bala a cada intervalo definido pelo fireRate:

Código:
void Update()
{
    //somente quando tem bala
    if (bulletPrefab != null && firePoint != null)
    {
        //conta o tempo em fireCounter
        fireCounter += Time.deltaTime;

        //caso o tempo de fireCounter seja maior ou igual ao de fireRate...
        if (fireCounter >= fireRate)
        {
            //reseta fireCounter, pega-se um novo tempo para fireRate e instancia a bala
            fireCounter = 0;
            GenerateRandomFireRate();
            Instantiate(bulletPrefab, firePoint.transform.position, Quaternion.identity);
        }
    }
}

O processo é repetido várias vezes enquanto o inimigo existir no jogo. Para concluirmos, deveremos ir ao Inspector do KongFeliz e configurar as variáveis de bulletPrefab e firePoint, lembre-se de criar agora o prefab da bala BulletFeliz de antemão. É já esperado que você nessa altura saiba fazer esse procedimento descrito nesse paragrafo sozinho. :kjoinha

Pronto, já temos o nosso segundo inimigo praticamente pronto. Para testar os seus conhecimentos, encorajo a você tentar criar novos inimigos com base das imagens já disponibilizadas tanto dos sprites dos inimigos quanto das balas.

meme-you-can-do-it.jpg


# Waves

Hordas de inimigos devem aparecer no espaço tentando ferozmente arrebatar o coração metálico de nossa pobre navezinha. O espaço sideral é um lugar perigoso, vasto e amplo não apenas em suas dimensões, mas nos mais diversos perigos que o assola. :)

Para termos uma sensação de fase, vamos criar várias waves de inimigos de forma aleatória, definiremos alguns pontos fora da visão da câmera onde essas naves surgirão. Como as estrelas do nosso background se deslocam para baixo, dando a sensação de que a nave está indo para cima (já que estamos usando a visão TOP-DOWN), portanto, os inimigos se deslocaram no sentido contrário, mas nem sempre em linha reta.

Ao olharmos para a dimensão de nossa câmera, em sua parte superior, as naves inimigas irão surgir. Dê um scroll segurando a tecla Ctrl + Mouse Wheel e aproxime à sua extremidade esquerda, se criarmos um Game Object qualquer apenas para testar e pegar a sua posição, iremos pegar em seu centro algo como (-4.5 , 4.5), conforme a figura abaixo ilustra:

kdGRnko.jpg


Cada quadrado desse representa uma 1 unidade na escala da Unity. Portanto, se esse quadrado está em (-4.5, 4.5), o próximo à sua direita está em (-3.5, 4.5), o seguinte em (-2.5, 4.5) e assim sucessivamente até chegarmos na ponta da outra extremidade que termina em:

EEa9FJy.jpg


Como estamos deslocando apenas no eixo X, os valores de Y continuam os mesmos. Temos agora 10 posições diferentes, que vai do X em -4.5 até 4.5.

Basicamente, essa primeira parte da wave será bem simples. De um spawn a outro, cria-se um número n de Kongs, instanciando eles numa dessas posições quaisquer. Deveremos só nos assegurar que quando instanciarmos mais de um Kong ao mesmo tempo, eles não acabem pegando a mesma posição que foi usada em um mesmo spawn. Quando um spawn termina, se reseta as posições, podendo novos Kongs pegá-las novamente.

Vamos criar dois novos scripts C#:

- Cell.cs
- WaveSpawner.cs


A struct Cell representará justamente as posições necessárias onde os Kongs irão ser instanciados. A classe WaveSpawner irá controlar tudo isso, "spawmando" nossos inimigos de tempo em tempo numa dessas posições. Abra em sua IDE o arquivo Cell.cs e remova a sua herança com MonoBehaviour e mude a keyword de class para struct removendo logo em seguida também os métodos Start e Update. Teremos algo assim:

Código:
public struct Cell
{

}

Cadê a classe? O que diabos é Struct?

Struct é uma outra forma de organização de dados, assim como as classes são. É muito complicado falar disso aqui onde envolve conceitos de programação que muitas vezes nem vemos em jogos e às vezes nem fora deles também. Basicamente um struct é usado para resolver coisas simples como posição em um mapa e armazenamento de valores simples sem referência. A nossa struct representará apenas isso, a posição X e Y em nossa Scene. Uma struct não pode herdar de nenhuma classe e o seu construtor não pode ser default.​

Adicione as seguintes variáveis públicas em nossa Cell. Repare que uma delas é estática para acesso rápido, caso quisermos usar:

Código:
public float x;
public float y;
public static Cell zero = new Cell(0, 0);

Vamos criar um construtor (Constructor) para o nossa Cell. Um construtor é como se fosse a porta de entrada de uma classe. Não precisávamos usar nenhum deles antes porque as nossas classes herdavam de MonoBehaviour que por si só, na Unity, tem a sua própria forma de instanciá-las como já vimos. Todo construtor tem que ter exatamente o nome da classe ou struct ao qual faz parte e não pode ter retorno de nenhum tipo, mesmo o "void".

Dentro de nossa struct e abaixo das variáveis declaradas, faça:

Código:
public Cell(float x, float y)
{
    this.x = x;
    this.y = y;
}

Simples, não? Agora deveremos desenvolver a WaveSpawner que usaremos a Cell para instanciar os Kongs criados. Abra o arquivo WaveSpawner.cs e adicione as seguintes variáveis como variáveis de classe:

Código:
public List<Cell> cells;
public List<GameObject> kongs;

private Cell baseCell;
private float spawnRate = 2f;
private float timeCounter;

A variável cells irá definir a cada spawn quais células já foram usadas. Fazemos isso para não permitir criar kongs em uma mesma posição de uma mesma wave. A variável kongs comporta os prefabs dos Kongs criados (KongClassic, KongFeliz), deveremos via Inspector arrastar os prefabs para a nossa lista. A variável baseCell representa a nossa primeira posição base, que é na posição (-4.5, 4.5), teremos 10 posições possíveis, cada uma com uma unidade de diferença no eixo X. Variável spawnRate determina o tempo necessário de um spawn para outro. timerCounter, por sua vez, determina o tempo percorrido até chegar ou ultrapassar o spawnRate e, então, resetar sua variável para um próximo spawn.

Implemente os métodos Awake e Start. De costume, usamos o Awake para instanciar elementos no nosso jogo e o Start para inicializá-los.

Código:
void Awake()
{
    cells = new List<Cell>();
}

void Start()
{
    baseCell = new Cell(-4.5f, 4.5f);
}

Vamos criar um método que irá nos retornar a primeira posição válida de um spawn. Para isso, ele deverá verificar a lista de células e ver se a célula que ele criou não se encontra nela, caso esteja presente na lista, então ele cria uma nova posição e executa o mesmo processo até ter certeza que não há célula usada.

Código:
public Cell GetRandomCell()
{
    Cell cell;

    do
    {
        var x = baseCell.x + Random.Range(1, 9);
        var y = baseCell.y;

        cell = new Cell(x, y);
    }
    while (cells.Contains(cell));

    cells.Add(cell);

    return cell;
}

A nova célula (cell) usa como base a célula baseCell, onde tem sua posição fixada em x:-4.5 e y:4.5. Como vamos apenas variar no eixo x, a variável y fica inalterada, pegando sempre o valor definido em baseCell. A variável x pega o valor base e soma com um número aleatório entre 1 e 9. Com a cell criada, ele testa while (cells.Contains(cell)) se essa célula não foi usada. Leia-se: "Faça ...isso.. enquanto cells contém cell", caso não tenha, ele procede normalmente com o restante do código, adicionando a nova célula na lista de células.

Agora com o Update, teremos a seguinte situação:

- contar timeCounter até chegar em spawnRate;
- chegando no tempo do spawnRate, faça: resetar o contador, limpar a lista de células (já que é um novo spawn), escolher a quantidade de kongs a serem instanciados, pegar uma posição válida qualquer (GetRandomCell), escolher algum Kong aleatoriamente e, por fim, instancia o Kong na posição obtida.

Código:
void Update()
{
    timeCounter += Time.deltaTime;

    if (timeCounter >= spawnRate)
    {
        timeCounter = 0;
        cells.Clear();
        var kongNumber = Random.Range(1, 5);

        for (var i = 0; i < kongNumber; i++)
        {
            var cell = GetRandomCell();

            Instantiate(kongs[Random.Range(0, kongs.Count)], new Vector3(cell.x, cell.y), Quaternion.identity);
        }
    }
}

Nomenclaturas

Eu usei Cell para simbolizar a posição (x e y) como representação da posição 2D na Scene. Mas é comum usar para as mesmas coisas outras nomenclaturas como Tile ou Node. Como essa informação é apenas das coordenadas X e Y, não senti necessidade de tratar isso como se fosse um outro termo, mesmo podendo caber qualquer outro termo citado. Tiles é comumente usado para representar "azulejos" ou pequenas posições retangulares no mapa. Node é um termo usado para também representar uma posição 2D ou 3D mas, mais voltado às técnicas de Pathfinding (técnicas avançadas para pesquisa de nós num espaço).​

Temos que criar o Game Object WaveSpawner e adicionar o nosso script correspondente. Lembre-se de popular também a lista de Kongs via Inspector conforme a imagem abaixo.

A6GEHBe.jpg


Execute o jogo e teremos hordas de kongs aparecendo querendo comer a sua banana.

:coolface

giphy.gif


# Aprimorando as Waves

Se repararmos, as waves não soam totalmente naturais, inimigos surgindo sempre na mesma posição e velocidade horizontal. Mesmo para tentar desempenhar um jogo retrô, temos que deixar as coisas mais orgânicas, deixando o jogo com uma experiência ainda melhor de se jogar.

Para isso, vamos deixar as waves sendo ainda mais variáveis. Criando formações fixas de inimigos e "spawmando" com velocidades distintas.

Para criarmos algumas formações fixas, dando as formas e posições que quisermos, vamos trabalhar diretamente na Scene do nosso jogo. Arraste, por exemplo, o KongClassic até a posição acima da câmera, de preferência na x:4.5, y:4.5. Agora vamos no item de menu: "Edit > Snap Settings...", uma pequena janela como a seguinte irá abrir:

kfBptnU.jpg


A janela de snap, permite-nos configurar o snap em forma de grid em uma Scene, alinhando os objetos conforme os seus parâmetros. Como cada Kong cabe bem no centro de cada unidade, então vamos deixá-los lado a lado para formar formações interessantes. Em Move X, Y e Z, definimos a posição média que cada objeto ficará em relação a nossa unidade. Exemplo, se colocar dois objetos em Snap com 1 unit, então a distância de um para outro ficará sendo 1 unit. Como o pivot (lembra dele?) de cada Kong está em seu centro, então definiremos o snap em x:0.5 e y:0.5 ao invés de 1 e 1. Deixe essa janela aperta durante todo o processo.

Agora vamos colocar um Kong em (4.5, 4.5), com o Kong ainda selecionado, aperte CTRL+D para duplicá-lo, repare que na duplicação, o objeto criado se encontra na mesma posição do objeto de origem, basta arrastá-lo pro lado que veremos o seu deslocamento e a sua sobreposição (overlap). A ideia aqui é criar vários Kongs em sequência e um ao lado do outro. O snap serve para que a distância de um para outro seja simétrica.

Então com o Kong criado e arrastado ao lado esquerdo, aperte o botão X e depois Y para ajustá-lo corretamente na nossa grid, gerando uma distância equidistante ao Kong anterior. Faremos isso várias vezes até completarmos a nossa formação de Kongs malignos e cruéis.

Recapitulando...

- Posicione algum Kong em (4.5, 4.5);
- Com o Kong selecione, duplique-o usando CTRL+D;
- Arraste-o à esquerda (ou direita se você começou numa outra posição) até se encaixar mais ou menos dentro de uma unidade;
- Aperte os botões X e Y para ajustar o Kong selecionado na posição correta;
- Repita todo o processo até formar um grupo de Kongs malignos e cruéis;


giphy.gif


Complete a formação até ficar algo como a imagem abaixo:

EVxIYnE.jpg


Você não precisa fazer o processo de duplicação e snapping passo a passo, ao completar uma fileira, selecione ela por completo, dê um CTRL+D e arraste-a pra cima, usando as opções de snapping em toda a fileira. Repita o procedimento até formar 3 fileiras como mostrado na imagem acima.

Agora temos que organizar essa bando de macaquitos siderais num grupo único, dessa forma movendo ou posicionando um único objeto, todos os objetos filhos dele serão movimentos uniformemente.

Crie, portanto, um novo Game Object (CTRL+SHIFT+N) e o posicione em (-4.5, 4.5), da qual é a mesma posição do Kong da extrema esquerda de baixo.

Vamos selecionar pela Hierarchy todos os Kongs criados e arrastá-los para dentro desse novo Game Object:

giphy.gif


Renomeie o GameObject para KongGroup01, arraste esse Game Object para a nossa pasta de Prefabs. e poderemos apagá-lo de nossa Hierarchy e reutilizá-lo posteriormente em nosso jogo instanciando-o via WaveSpawner.

Temos a nossa primeira formação personalidade de Kongs do mal criada.

2xhs22D.png


Boa pergunta, jovem Pandoin. Sim, poderemos criar prefabs que contém outros prefabs sem problema algum. Porém, contudo, todavia, entretanto, mas... temos que fazer uma ressalva aqui. Uma vez que eu criei um prefab que contém outros prefabs dentro dele, os prefabs contidos perdem os seus vínculos originais com os prefabs de origem. Ou seja, em nosso exemplo aqui, Os KongsClassics que estão dentro de KongGroup01 não fazem mais parte do prefab KongClassic, portanto, se eu atualizar o prefab KongClassic, adicionando, por exemplo, um novo GameObject e apertando "Apply", isso não irá repercutir para os Kongs que estão dentro de KongGroup01, porque na verdade, criamos um único prefab que continha dados de um prefab anterior. KongGroup01 é um prefab, e os seus GameObjects fazem parte dele.

A imagem abaixo ilustra bem isso, eu criei como teste (você não precisa repetir esse exemplo, se quiser) um GameObject dentro de KongClassic e o chamei de "Teste", ao apertar "Apply", esse novo GameObject não apareceu nos KongsClassic contidos dentro de KongGroup01, mas aparecerá em qualquer outro KongClassic normalmente:

uGO3BND.jpg


Então, fique ciente de como usar esses recursos de forma a não cometer erros que irão lhe prejudicar lá na frente. Retire o "Teste" e aperte "Apply" novamente, caso você tem feito esse mesmo teste.

Agora vamos mexer na nossa classes Enemy e WaveSpawner à permitir as nossas novas alterações.

Em Enemy.cs, faremos que a sua velocidade seja variável a partir de uma velocidade base já definida chamada speed. Assim, todos os inimigos que serão instanciados de uma mesma wave mas que não fazem parte de um grupo (a exemplo do KongGroup01) terão suas velocidades alteradas para mais ou menos da velocidade base definida.

Em Enemy.cs, adicione o seguinte método privado:

Código:
private void SwingSpeed()
{
    speed += Random.Range(-1f, 1f);
}

O SwingSpeed será chamado no Start, mas apenas na condição que o Kong atual não faça parte de nenhum grupo, e para justamente checar essa configuração de grupo, basta vermos se o Kong não tem objeto pai. Crie o seguinte getter logo abaixo de todas as variáveis, mas acima de todos os métodos:

Código:
public bool withinGroup
{
    get { return (gameObject.transform.parent != null); }
}

Exemplo: KongGroup01 é o Game Object pai, porque cada um daqueles Kongs que estão dentro dele (KongClassic, KongClassic (1), KongClassic (2), ...) são filhos de KongGroup01. A variável parent de cada Kong aponta para o transform do Game Object pai, que é o próprio KongGroup01. Se tentarmos saber qual é o pai de KongGroup01, o valor de seu "transform.parent" ficará nulo, porque ele não faz parte de nenhum outro Game Object.

Por que então criamos o KongGroup01? Porque assim será mais fácil de mover e instanciar todos aqueles outros Kongs que criamos.

Agora voltando ao código... No método Start, adicione à chamada ao SwingTeste apenas quando o Kong não fizer parte de nenhum grupo.

Código:
void Start()
{
    GenerateRandomFireRate();

    if (!withinGroup)
        SwingSpeed();
}

Teste o nosso jogo e veremos que os Kongs descem numa velocidade variável com base da velocidade que você definiu pela variável speed.

giphy.gif


Pronto, agora para finalizar, temos que fazer a WaveSpawner instanciar a KongGroup01 também.

Quero poder ensinar algo novo que ainda não foi abordado em nenhuma das partes desse tutorial e que agora criei a oportunidade para tal que é o conceito de carregamento de recursos da Unity.

Como instanciamos um prefab até agora? Via Inspector arrastamos um prefab para a propriedade Game Object de algum componente definido em algum código e dentro dele usamos o método Instanciate para criar instâncias desse prefab, certo?

Certo! Mas agora para o exemplo dos grupos que criamos, como o KongGroup01, faremos de uma forma um pouco diferente. Dentro da pasta Prefab em nossa janela de Project. Crie a pasta "Resources" e arraste KongGroup01 para dentro dela. Teremos algo assim:

KhARDEC.jpg


Lembra que lá atrás eu falei que algumas pastas criadas em Project poderiam ter significados diferentes à Unity? Então, a pasta Resources é uma delas. Quando o seu jogo é gerado e compilado para distribuição, todo o conteúdo que está dentro de Resources é indexado pela Unity de uma forma única e agregada, possibilitado todos os seus assets serem acessados vai código como usando Resources.Load.

Resources é, sem dúvida, uma mão na roda para quem trabalha com a Unity. Pois uma vez assets como imagens, sons, fontes, ou Game Objects (oriundos de Prefabs) são acessados de forma simples, prática e única, independente, inclusive, do sistema operacional ao qual o jogo será destinado. Em meu jogo, eu posso criar várias pastas Resources, no final, tudo que estará contido nas Resources, serão indexados e agrupados como sendo de uma única pasta resource, isso é automático feito pela Unity e não teremos controle sobre isso, portanto, cabendo aqui uma ressalva: "Não deixe Prefabs com nome duplicados em Resources". Para ilustrar essa situação, poderemos ter várias pastas Resources em nosso jogo sem problema algum. Vamos supor que tenhamos as seguintes pastas:

- Textures
- Resources
* GrassTile
* Enemy
- Music
- Resources
* ExplosionFX
* Enemy
- Prefabs
- Resources
* Bullet
* Enemy
- Other Prefabs
- Resources
* Enemy

Cada uma dessas pastas Resources em diferentes outras pastas de cada tipo de recurso, apresentam em comum o arquivo "Enemy" que pode significar tanto uma textura, um áudio, quanto mesmo um prefab. A Unity tem como identificar tranquilamente, mesmo os recursos com nomes iguais, quais são eles através da natureza/tipo do arquivo. Exemplo:

Código:
var texture2D = Resources.Load<Texture2D>("Enemy");
var audioClip = Resources.Load<AudioClip>("Enemy");

Eu identifico à Unity que estou carregando "Enemy" cujo o arquivo será uma textura, ela automaticamente me retorna para a variável texture2D um arquivo cujo o nome é "Enemy" e o seu tipo é de Textura 2D, assim como estou identificando também um arquivo "Enemy" cujo o tipo é de áudio e retornando-o propriamente. A Unity consegue distinguir quem é quem, nesse caso. Repare que eles NÃO são Game Objects, quando uso Resources.Load para carregar fontes, texturas, áudio, arquivos textos, xml, etc. Esses recursos não são tratados como Game Object, são conhecidos como raw data e preciso criar um Game Objects ou associá-los a Game Objects existentes se eu quiser executá-los "perceptivelmente" em meu jogo.

Para carregarmos um Prefab, a história muda um pouco e preciso me assegurar que não exista mais de um prefab com o mesmo nome no meu jogo que estejam em pastas Resources. Isso porque um prefab é um prefab, que é na verdade um GameObject que pode ser reutilizado à vontande. Então mesmo que eu criei um prefab para comportar um arquivo de audio e outro para comportar um arquivo de textura, antes de tudo, para a Unity, eles são prefabs e não audio ou textura, que são carregados como componentes individuais para cada prefab. Dessa forma, quando se trata de prefabs, você tem que garantir que os nomes não podem ser duplicados.

Para instanciar um prefab, como o exemplo fictício de nossas pastas acima, faça algo como a amostra a seguir:

Código:
var enemyPrefab = Resources.Load("Enemy");
var enemyGO = Instantiate(enemyPrefab, Vector3.zero, Quaternion.identity);

Só por medida de curiosidade, se você quiser utilizar arquivos como de textura e áudio no Resources.Load, deveremos criar ou instanciar GOs para eles, assim como eu disse antes.

Então façamos algo como o do exemplo a seguir:

Código:
var texture2D = Resources.Load<Texture2D>("Enemy");
var audioClip = Resources.Load<AudioClip>("Enemy");

//instanciando uma textura
var textureGO = new GameObject();
textureGO.AddComponent<SpriteRenderer>();
textureGO.GetComponent<SpriteRenderer>().sprite =
    Sprite.Create(texture2D, new Rect(0, 0, texture2D.width, texture2D.height), new Vector2(0.5f, 0.5f), 30);

//instanciando um áudio
var audioGO = new GameObject();
audioGO.AddComponent<AudioSource>();
audioGO.GetComponent<AudioSource>().clip = audioClip;
audioGO.GetComponent<AudioSource>().Play();

Desculpe pela longa explanação, é apenas para evitar dúvidas que acabam sendo meio que frequentes para quem vai começar a programar em Unity e acaba populando seus fóruns e answers da vida com esse tipo de pergunta. Há mais coisas sobre Resources, mas não cabe a explicar isso aqui para não ficar mais extenso o assunto, por hora, essas informações são mais que suficientes para o que pretendemos fazer que é instanciar o nosso grupo de Kongs. Antes de irmos aos Kongs direto, o meu último alerta sobre Resources:

Resources é uma mão na roda, mas CUIDADO!!

É muito prático e conveniente usar Resources. Basta criar essas pastinhas onde seja o que for, que a Unity indexa todos os arquivos nelas contidas e poderemos acessá-los via código sem problema. Porém, essa conveniência toda tem um preço, e esse preço se chama espaço e performance. Diferente de tratarmos GameObjects diretamente numa Scene e seus componentes e propriedades via Inspector e código. Todos os arquivos que você deixar em Resources, serão colocados no seu jogo, mesmo que entre eles sejam arquivos que você não vai precisar usar numa Scene de seu jogo. Então poderemos ocupar o espaço com arquivos desnecessários. A outra coisa, porém, são excessivas chamadas de arquivos via Resources durante a execução do jogo, o que não pode ser muito aconselhado porque diminui a performance uma vez que estamos carregando os arquivos via HD (se o jogo for para PC ou mesmo consoles) ou via SD Card quando é mobiles e outros periféricos que fazem uso disso. Então durante uma sessão de jogo, durante uma mesma fase, ficar puxando às vezes dezenas ou centenas de arquivos, mesmo com os recursos de threads, o seu jogo poderá cair em drops de fps. O recomendável nesse caso, quando for carregar muitos arquivos que serão tratados via código durante a execução de uma Scene de seu jogo é carregá-los antes da fase, estágio do jogo propriamente aparecer pronta para o usuário, dessa forma, trazemos arquivos do HD para a memória RAM do sistema via Resources e reaproveitamos os recursos já carregados na memória para instanciar novos GameObjects, deixando o jogo com menos callback e menos possíveis slowdowns.​

Ufa, agora vamos instanciar o bagulho lá que ainda quero jogar hoje. :ksafado

No arquivo WaveSpawner.cs vamos fazer...

Crie a variável de classe:

Código:
private List<KongGroup> kongGroups;

Essa lista irá armazenar todo o grupo de Kongs que criarmos para o jogo. Adicione o seguinte struct logo após as variáveis privadas:

Código:
private struct KongGroup
{
    public Vector3 position;
    public GameObject prefab;
}

Esse struct tem o prefab a ser instanciado na posição informada. No método Awake de WaveSpawner crie a instância da lista de kongs:

Código:
kongGroups = new List<KongGroup>();

No método Start, vamos usar o Resource.Load para carregar todos os grupos de kongs em nossa lista. Eles serão chamados de forma aleatória depois em Update.

Código:
//carregando os grupos de kongs (formação fixa)
var kongGroup01 = new KongGroup
{
    prefab = Resources.Load("KongGroup01") as GameObject,
    position = new Vector3(baseCell.x, baseCell.y)
};

kongGroups.Add(kongGroup01);

No método Update, temos a maior mudança. Agora além da geração aleatória de kongs individuais em cada uma das 10 posições definidas, vamos verificar se a cada spawn vamos chamar a formação aleatória (que é o código atual) ou vamos chamar um dos grupos que criamos (no caso só criei um KongGroup, criei outros também!!! I trust u!).

Código:
void Update()
{
    //vai contando o tempo até chegar em spawnRate
    timeCounter += Time.deltaTime;

    if (timeCounter >= spawnRate)
    {
        //reseta no contador
        timeCounter = 0;
        //o cooldown do spawnRate variará entre 1s até 2s.
        spawnRate = Random.Range(1f, 2f);

        //formação aleatória? (quando for 0)
        if (Random.Range(0, 9) >= 5)
        {
            cells.Clear();
            var kongNumber = Random.Range(1, 5);

            for (var i = 0; i < kongNumber; i++)
            {
                //pega uma das 10 possiveis posições
                var cell = GetRandomCell();

                //instancia a formação aleatória de kongs
                Instantiate(kongs[Random.Range(0, kongs.Count)], new Vector3(cell.x, cell.y), Quaternion.identity);
            }
        }
        //formação fixa? (quando for diferente de 0).
        else
        {
            //pega um grupo qualquer...
            var group = kongGroups[Random.Range(0, kongGroups.Count)];

            //instancia o prefab do grupo para criar o seu Game Object
            Instantiate(group.prefab, group.position, Quaternion.identity);

            //depois de executar uma formação fixa e não random, o tempo para o próximo spawn será de 3s.
            spawnRate = 3f;
        }

    }
}

Para finalizar, deveremos destruir depois de um tempo os inimigos que foram criados só que não foram destruídos pelo jogador. Mas vou deixar isso ao seu encargo. :kjoinha

Temos ao fim desse capitulo, os seguintes arquivos modificados ou criados:

Enemy_02.cs
WaveSpawner_01.cs
Cell_01.cs
Bullet_02.cs
BulletFeliz_01.cs

No próximo capítulo, vamos ver coisas bem interessantes como deixar efeitos de morte tanto do jogador quanto dos kongs, criar uma HUD para identificar coisas como life, colocar músicas, etc. :)

TO BE CONTINUED
 

XINTSUAI2

Mil pontos, LOL!
Mensagens
42.172
Reações
33.361
Pontos
1.029
Já viu a extensão PoolManager? Já usei ela em um projeto pessoal, diz que é uma forma bem eficiente e mais leve de spawnar e despawnar objetos.
 

Landstalker

Lenda da internet
Mensagens
19.355
Reações
39.658
Pontos
1.584
Já viu a extensão PoolManager? Já usei ela em um projeto pessoal, diz que é uma forma bem eficiente e mais leve de spawnar e despawnar objetos.

Eu na verdade tento usar o mínimo possível de assets feitos por outros. Porque eu mesmo gosto de entender como todo o processo de um jogo funciona. Assim eu sinto que eu fico mais completo e preparado para mais desafios e possíveis problemas que se encontra num desenvolvimento. Eu mesmo, tempos atrás, senti necessidade de importar um TileMap da ferramenta http://www.mapeditor.org/ para uma demo na Unity. Então eu mesmo falei com o criador dessa ferramenta e conseguir fazer um Asset que importasse o mapa feito nela diretamente na Unity. Demorei quase uma semana fazendo isso, mas foi bem bacana.

Tempos depois eu criei a minha própria ferramente de tilemap que roda internamente na Unity, deu um put* TRABALHO, pesquisando muito tomando muito pau, mexendo na parte avançada da engine, interceptando até comandos e cliques dela, enfim, deu bastante trabalho e consegui me virar para fazer um tilemap framework dentro da Unity.

Mas claro, nem tudo é 100% possível e vai de tempo e limitação de cada pessoa. Eu certamente vou precisar de assets que eu não teria como programar. Eu já gastei mais de 300 dólares em Assets na Unity, dede 2014.
 

Landstalker

Lenda da internet
Mensagens
19.355
Reações
39.658
Pontos
1.584
Já viu a extensão PoolManager? Já usei ela em um projeto pessoal, diz que é uma forma bem eficiente e mais leve de spawnar e despawnar objetos.

Eu criei uma solução de Pool que funciona de forma seamlessly, eu até a utilizo no Switch Hype Train - The Game.
 

XINTSUAI2

Mil pontos, LOL!
Mensagens
42.172
Reações
33.361
Pontos
1.029
Eu na verdade tento usar o mínimo possível de assets feitos por outros. Porque eu mesmo gosto de entender como todo o processo de um jogo funciona. Assim eu sinto que eu fico mais completo e preparado para mais desafios e possíveis problemas que se encontra num desenvolvimento. Eu mesmo, tempos atrás, senti necessidade de importar um TileMap da ferramenta http://www.mapeditor.org/ para uma demo na Unity. Então eu mesmo falei com o criador dessa ferramenta e conseguir fazer um Asset que importasse o mapa feito nela diretamente na Unity. Demorei quase uma semana fazendo isso, mas foi bem bacana.

Tempos depois eu criei a minha própria ferramente de tilemap que roda internamente na Unity, deu um put* TRABALHO, pesquisando muito tomando muito pau, mexendo na parte avançada da engine, interceptando até comandos e cliques dela, enfim, deu bastante trabalho e consegui me virar para fazer um tilemap framework dentro da Unity.

Mas claro, nem tudo é 100% possível e vai de tempo e limitação de cada pessoa. Eu certamente vou precisar de assets que eu não teria como programar. Eu já gastei mais de 300 dólares em Assets na Unity, dede 2014.
Por isso passei a usar c# no lugar do Playmaker, mas sou bem mais limitado enquanto dev, minha área é design e ideação mesmo.
 

XINTSUAI2

Mil pontos, LOL!
Mensagens
42.172
Reações
33.361
Pontos
1.029
Eu criei uma solução de Pool que funciona de forma seamlessly, eu até a utilizo no Switch Hype Train - The Game.
Olha interessante, no momento não tenho condições de dizer que sou capaz de criar algo, vou na base de tutoriais mesmo.

Agora uma dica seria fazer tutoriais em. Youtube mas fracionados em videos abordando coisas menores parte a parte, mesmo que seja de um todo. Porque o cara pode tanto assistir em sequência para fechar um proj, como pode usar só um dos vídeos para solucionar algo específico... E põe numa. Playlist.
 


Landstalker

Lenda da internet
Mensagens
19.355
Reações
39.658
Pontos
1.584
Esse joguete do curso que tô fazendo aqui, é bastante simples, mesmo que o código pareça meio complexo. A minha ideia não é ensinar o jogo, mas sim, técnicas que vai servir ou ajudar qualquer um no desenvolvimento de jogos.

Quem terminar o curso, espero que já entenda coisa comumente usadas como ininite scroller, parallax e talvez alguma outra coisa que eu coloque até o final. Ainda não terminei de fazer tudo.

Por isso passei a usar c# no lugar do Playmaker, mas sou bem mais limitado enquanto dev, minha área é design e ideação mesmo.

Eu nunca usei o Playmaker, eu não sei o quão potente ele pode ser. Mas eu já usei o blueprints da Unreal e, sinceramente, não curti não. Sei que de certa forma eles são parecidos, já que a ideia é ser uma linguagem visual e não de scripts, mas isso não é a minha praia. Eu sempre curti muito programar e, infelizmente, o meu fraco é justamente é o design.
 

Landstalker

Lenda da internet
Mensagens
19.355
Reações
39.658
Pontos
1.584
Olha interessante, no momento não tenho condições de dizer que sou capaz de criar algo, vou na base de tutoriais mesmo.

Agora uma dica seria fazer tutoriais em. Youtube mas fracionados em videos abordando coisas menores parte a parte, mesmo que seja de um todo. Porque o cara pode tanto assistir em sequência para fechar um proj, como pode usar só um dos vídeos para solucionar algo específico... E põe numa. Playlist.

Eu sei mas eu não quis fazer no YT por algumas razões:

- Privacidade;
- Não gostei do meu áudio para gravação de vídeo, talvez comprar um mic melhor;
- Talvez usar o vimeo ao invés do YT;
- Usar os posts para mostrar o tutorial e deixar registrado na própria Outerspace, já que a minha ideia era ser um curso apenas para cá, com assets daqui (Emotikongs) e que qualquer um também poderia acessar sem barreiras, já que o YT pode ser bloqueado do trabalho, etc.

Então acabei decidindo usar texto em detrimento ao vídeo. Mas claro que sei que videos são menos cansativos e são até mais empolgantes, ao menos, para a maioria.

Não que eu tenha descartado essa possibilidade, mas é algo que eu tenho que ter uma estrutura melhor para fornecer depois.
 

XINTSUAI2

Mil pontos, LOL!
Mensagens
42.172
Reações
33.361
Pontos
1.029
Eu sei mas eu não quis fazer no YT por algumas razões:

- Privacidade;
- Não gostei do meu áudio para gravação de vídeo, talvez comprar um mic melhor;
- Talvez usar o vimeo ao invés do YT;
- Usar os posts para mostrar o tutorial e deixar registrado na própria Outerspace, já que a minha ideia era ser um curso apenas para cá, com assets daqui (Emotikongs) e que qualquer um também poderia acessar sem barreiras, já que o YT pode ser bloqueado do trabalho, etc.

Então acabei decidindo usar texto em detrimento ao vídeo. Mas claro que sei que videos são menos cansativos e são até mais empolgantes, ao menos, para a maioria.

Não que eu tenha descartado essa possibilidade, mas é algo que eu tenho que ter uma estrutura melhor para fornecer depois.
A outerspace era bloqueada na empresa que trabalhava anteriormente. Mas youtube não hehe
 

thiagoencd

Bam-bam-bam
Mensagens
165
Reações
181
Pontos
469
Enfim, terminada esta parte!
Algumas coisas que ocorreram, as vezes vinham os personagens aleatoriamente e as vezes vinha o grupo com todos os kongs do konggroup de uma vez só, e quando eles vinham novamente, não apareciam os que matei anteriormente. As vezes o grupão vem com alguns kongs a esquerda da área de jogo, e por fim, quando eu morro devido a uma colisão, dá crash no jogo com essa mensagem de erro:

Destroying GameObjects immediately is not permitted during physics trigger/contact, animation event callbacks or OnValidate. You must use Destroy instead.
UnityEngine.Object:DestroyImmediate(Object)
Player:Kill() (at Assets/Scripts/Player.cs:47)
Player:Hit(Int32) (at Assets/Scripts/Player.cs:55)
Enemy:OnTriggerEnter2D(Collider2D) (at Assets/Scripts/Enemy.cs:70)
 

Landstalker

Lenda da internet
Mensagens
19.355
Reações
39.658
Pontos
1.584
Enfim, terminada esta parte!
Algumas coisas que ocorreram, as vezes vinham os personagens aleatoriamente e as vezes vinha o grupo com todos os kongs do konggroup de uma vez só, e quando eles vinham novamente, não apareciam os que matei anteriormente. As vezes o grupão vem com alguns kongs a esquerda da área de jogo, e por fim, quando eu morro devido a uma colisão, dá crash no jogo com essa mensagem de erro:

Destroying GameObjects immediately is not permitted during physics trigger/contact, animation event callbacks or OnValidate. You must use Destroy instead.
UnityEngine.Object:DestroyImmediate(Object)
Player:Kill() (at Assets/Scripts/Player.cs:47)
Player:Hit(Int32) (at Assets/Scripts/Player.cs:55)
Enemy:OnTriggerEnter2D(Collider2D) (at Assets/Scripts/Enemy.cs:70)

Use Destroy() ao invés de DestroyImmediate().

O ideal, na verdade, é usar Pool ao invés de destruir os objetos, principalmente os objetos que vão se repetir.
 

thiagoencd

Bam-bam-bam
Mensagens
165
Reações
181
Pontos
469
Então, consegui sim, só aconteceu esse negócio de as vezes vir toda a horda de kongs de uma vez, mas de resto está funfando 100%! Vou tirar print do que acontece nessa parte. Não sei se era pra ser assim mesmo, achei que as 3 fileiras de kongs fosse para pegar eles em ordem aleatória, não todos de uma vez.

Edit: Seguem os prints:

wwIJ9xH

SZmulob

jpU9oOr

yjz6CDu
 
Ultima Edição:

Landstalker

Lenda da internet
Mensagens
19.355
Reações
39.658
Pontos
1.584
Não entendi bem a sua dúvida sobre os KongGroups, se for sobre a posição, a posição do grupo é diferente da posição de cada um dos seus kongs, porque a posição de todos agora passa a ser a posição do grupo e não mais individual (mesmo ela ainda existindo).

Verifique se você colocou em WaveSpawner.cs a variável:

Código:
baseCell = new Cell(-4.5f, 4.5f);

Ela é usada mais adiante no método Start dessa mesma classe:

Código:
var kongGroup = new KongGroup
{
    prefab = Resources.Load("KongGroup0" + i) as GameObject,
    position = new Vector3(baseCell.x, baseCell.y)
};

É partir daí que o programa sabe onde instanciar o grupo na posição desejada.
 

thiagoencd

Bam-bam-bam
Mensagens
165
Reações
181
Pontos
469
Sim, minha dúvida era mais sobre se eles vem todos juntos assim mesmo, porque achei estranho vir toda essa horda de uma vez. Fazendo com kongs que atiram então fica impossível não morrer.
 

Landstalker

Lenda da internet
Mensagens
19.355
Reações
39.658
Pontos
1.584
Sim, minha dúvida era mais sobre se eles vem todos juntos assim mesmo, porque achei estranho vir toda essa horda de uma vez. Fazendo com kongs que atiram então fica impossível não morrer.

Mas se você reparar, esse paredão de kongs eu usei apenas os KongClassics, justamente que não atiram para assim o player conseguir eliminá-los, furando essa barreira.

:kfeliz
 

thiagoencd

Bam-bam-bam
Mensagens
165
Reações
181
Pontos
469
Ah tá, porque fazer isso com tiros é pedir pra ter a mãe xingada pelos jogadores! :klol:klol:klol:klol
 

Landstalker

Lenda da internet
Mensagens
19.355
Reações
39.658
Pontos
1.584
Ah tá, porque fazer isso com tiros é pedir pra ter a mãe xingada pelos jogadores! :klol:klol:klol:klol

Mas eu gostei que você fez algo diferente, a ideia do tutorial é nem sempre forçar os alunos a fazer exatamente igual o que está escrito, é usar a sua criatividade para mudar algumas coisas, trazer com o seu próprio toque.
 

thiagoencd

Bam-bam-bam
Mensagens
165
Reações
181
Pontos
469
Sim, queria até ter feito mais, mas já era de madrugada. Agora que as obras lá na minha sogra terminaram eu devo ter mais tempo pra ver isso um pouco melhor. Mas no geral está tudo rodando legal conforme o previsto até agora.
 

Landstalker

Lenda da internet
Mensagens
19.355
Reações
39.658
Pontos
1.584
Pior que eu nem sei se vou continuar e um dos motivos foi a aceitação do tutorial aqui. Eu escolhi um formato que não ajuda à divulgação e é complicado dar-se continuidade quando não se tem praticamente ninguém para acompanha-lo. Não que eu esteja culpando alguém, claro que não, mas por não querer me expor e querer restringir o tutorial apenas à Outerspace, então escolhi um formato que não ajudou muito, além do estilo do jogo não ser o que a maioria queria (não que isso importe muito, já que a ideia do tutorial é dar os conceitos básicos da Unity).

O outro motivo é por falta de tempo, eu consigo programar o jogo rapidamente, mais levo 6x-10x a mais de tempo em transformá-lo em tutorial, já que tenho que pensar como vou escrever, delimitar os códigos, diagramar tudo, tirar screens, enfim, fazer várias coisas necessárias para ser um tutorial teoricamente fácil para a maioria. E essa falta de tempo resulta em outros projetos meus que um deles é a DE2D para Unity que pretendo ter a primeira versão pronta antes de Setembro. Como ela será free e paga, a ideia é criar novos tutoriais com base nela e com um formato que seja bem melhor que o escolhido.

De qualquer forma, é um dos meus desenhos que essa área do fórum não morra, e fico feliz que mais gente comente aqui, poste projetos, dúvidas, etc. Se tiver qualquer dúvida, ficarei feliz em tentar ajudar quando eu puder, dessa forma é comunidade cresce e se ajuda também.
 

Zatt

Bam-bam-bam
Mensagens
410
Reações
506
Pontos
278
(LANDINHO VOU FAZER O PRÓXIMO THE WITCHER X :kjoinha)
Noh que lindo esse tutorial.
Vou fazer! e posto os resultados!
 

Gentilhomem

Bam-bam-bam
Mensagens
3.759
Reações
11.171
Pontos
303
Vim parabenizar o OP por sua iniciativa, pretendo voltar a estudar python e seu tópico me inspirou bastante, obrigado pelo bom serviço ao recinto, a OS agradece.:kjoinha
 

j0kk3r

Mil pontos, LOL!
VIP
Mensagens
12.352
Reações
16.925
Pontos
1.284
@Landstalker

Ei Land você não terminou o tutorial?

Vim aqui na pasta procurar esse tutorial seu pra estudar e só achei esses 3

https://forum.outerspace.com.br/index.php?threads/unity-00-introdução-à-unity.479462/
https://forum.outerspace.com.br/index.php?threads/unity-02-outer-space-invaders.479460/
https://forum.outerspace.com.br/index.php?threads/unity-03-outer-space-invaders.480633/

Semana passada eu arrumei um tempinho e resolvi mexer com isso de vez. Baixei a unity e comecei um projeto seguindo umas aulas no youtube. 3 dias brincando consegui fazer um side scroller infinito aqui e to pensando em incluir mais algumas coisas e lançar na play store pro meu filho brincar hehehe. Só que eu to ficando empolgado demais e quero aprender mais coisas.

Vou dar uma lida no que você já postou até aqui

Abraço!
 

Landstalker

Lenda da internet
Mensagens
19.355
Reações
39.658
Pontos
1.584
@j0kk3r

Coringuinha, eu não completei esse tutorial por falta de tempo na época e de um público interessado, só uma pessoa realmente o acompanhou até o último episódio, e agora que várias imagens foram postadas no postimage e o site saiu do ar há um tempo, só piorou a situação desse material que fiz.

Então, o que eu pretendo fazer é criar um site de programação de jogos e usar o YT para publicar os vídeos, a minha proposta é fazer as aulas totalmente gratuitas via YT e o material dos cursos disponíveis apenas pelo site para quem pagar, além de ter a versão textual do curso (há pessoas que preferem assim também) disponível pelo site, além de algumas outras regalias. É uma coisa que estou pensando já há um tempo em fazer, mas como estou sem computador isso está dificultando de eu conseguir colocar em prática.

Esse curso que fiz aqui na OS, já fiz sem explicar programação, e acho que isso resultou para muitas pessoas que não trabalham com programação e não conhecem a conseguir também entender melhor o curso. Conseguindo completar o site, farei aulas completas de como se programar em pelo menos nas linguagens C# (para usar na Unity) e Lua (para usar na Corona e outras engines), havendo demanda, poderei ensinar Java e, talvez, até mesmo C/C++, já que poderemos brincar com essas duas últimas linguagem e acessar o baixo nível para procurar entender APIs gráficas como OGL, DirectX e Vulkan.

Depois das aulas de linguagem de programação, focar nos cursos específicos para engines como a Unity. Eu já tenho códigos e moldes que desenvolvi aqui que servem para montar cursos, como Platforma, Runners, Tower Defense com A* Pathfinder, Tetris, 3 Match, navinha e FPS 3D.
 

j0kk3r

Mil pontos, LOL!
VIP
Mensagens
12.352
Reações
16.925
Pontos
1.284
@j0kk3r

Coringuinha, eu não completei esse tutorial por falta de tempo na época e de um público interessado, só uma pessoa realmente o acompanhou até o último episódio, e agora que várias imagens foram postadas no postimage e o site saiu do ar há um tempo, só piorou a situação desse material que fiz.

Então, o que eu pretendo fazer é criar um site de programação de jogos e usar o YT para publicar os vídeos, a minha proposta é fazer as aulas totalmente gratuitas via YT e o material dos cursos disponíveis apenas pelo site para quem pagar, além de ter a versão textual do curso (há pessoas que preferem assim também) disponível pelo site, além de algumas outras regalias. É uma coisa que estou pensando já há um tempo em fazer, mas como estou sem computador isso está dificultando de eu conseguir colocar em prática.

Esse curso que fiz aqui na OS, já fiz sem explicar programação, e acho que isso resultou para muitas pessoas que não trabalham com programação e não conhecem a conseguir também entender melhor o curso. Conseguindo completar o site, farei aulas completas de como se programar em pelo menos nas linguagens C# (para usar na Unity) e Lua (para usar na Corona e outras engines), havendo demanda, poderei ensinar Java e, talvez, até mesmo C/C++, já que poderemos brincar com essas duas últimas linguagem e acessar o baixo nível para procurar entender APIs gráficas como OGL, DirectX e Vulkan.

Depois das aulas de linguagem de programação, focar nos cursos específicos para engines como a Unity. Eu já tenho códigos e moldes que desenvolvi aqui que servem para montar cursos, como Platforma, Runners, Tower Defense com A* Pathfinder, Tetris, 3 Match, navinha e FPS 3D.

Que pena que não terminou o tutorial T.T

Mas boa sorte na empreitada! Se precisar de alguma coisa e quando tiver novidades sobre o site/yt dá um toque que eu quero dar uma olhada e quem sabe até participo de um curso. Tô brincando aqui pra fazer o joguinho e to gostando muito. É muito fácil mexer com Unity, mas sempre tem técnicas, boas práticas e coisas que a gente só conhece depois de um bom tempo de surra.

Agora por exemplo preciso pensar em alguma gambiarra pra mudar o personagem durante o jogo hehehe. Acho que com isso aqui eu consigo:



Mas vamos que vamos
 
Topo Fundo