Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. SpriteKit

Criar Space Invaders com Swift e Sprite Kit: Finalizando o gameplay

by
Difficulty:BeginnerLength:LongLanguages:
This post is part of a series called Create Space Invaders with Swift and Sprite Kit.
Create Space Invaders with Swift and Sprite Kit: Implementing Gameplay

Portuguese (Português) translation by David Batista (you can also view the original English article)

Final product image
What You'll Be Creating

Na parte anterior desta série, nós fizemos os invasores se moverem, os invasores e o jogador dispararem e implementamos a detecção de colisão. Na quarta, e final, parte desta série, nós iremos adicionar a habilidade de movimento ao jogador usando o acelerômetro, gerenciar os níveis e garantir que o jogador morra quando receber um tiro. Vamos começar.

1. Terminando a classe Player

Passo 1: Adicionando propriedades

Adicione as seguintes propriedades à classe Player abaixo da propriedade canFire.

A propriedade invincible irá ser usada para tornar o jogar temporariamente invencível quando ele perder uma vida. A propriedade lives é o número de vidas que o jogador tem antes de ser morto.

Estamos usando um observador de propriedade na propriedade lives, que será chamada cada vez que ela tiver o valor alterado. O observador didSet é chamado imediatamente após o novo valor da propriedade ser definido. Feito isto, toda vez que nós decrementarmos a propriedade lives ele automaticamente verifica se a lives esta abaixo de zero, chamando o método kill se ela estiver. Se o jogador tiver vidas sobrando, o método respawn será chamado. Observadores de propriedade são muito convenientes e podem evitar um grande quantidade de código.

Passo 2: respawn

O método respawn torna o jogador invencível por um curto período de tempo e pisca o jogador para indicar que ele está temporariamente invencível. A implementação do método respawn ficará assim:

Nós definimos invincible para true e criamos um certo número de objetos SKActions. Agora, você deve estar familiarizado em como a classe SKAction funciona.

Passo 3: die

O método die é muito simples. Ele verifica se invincible está com false e, se ele estiver, decremente a variável lives.

Passo 4: kill

O método kill reinicia o invaderNum para 1 e leva o usuário de volta a StartGameScene, então ele pode começar um novo jogo.

Este código deve ser familiar para você já que ele é idêntico ao código que nós usamos para mover da GameScene para a StartGameScene. Note que nós forçamos o desempacotamento do scene para acessar as propriedade size e scaleMode do cenário.

Isto completa a classe Player. Nós agora precisamos chamar os métodos die e kill no método didBeginContact(_:).

Agora podemos testar tudo. Uma maneira rápida para testar o método die é comentando a chamada ao moveInvader no método update(_:). Após o jogador morrer e dar 3 respawns, você deverá ser lançado de volta a StartGameScene.

Para testar o método kill, certifique-se que o método moveInvaders não esteja comentado. Altere a propriedade invaderSpeed para um valor alto, por exemplo, 200. Os invasores devem chegar ao jogador muito rápido, o que resulta em uma morte instantanea. Altere invaderSpeed de volta para 2 quando finalizar os testes.

2. Finalizando os disparos dos invasores

Do jeito que o jogo está agora, apenas os invasores da parte de baixo podem disparar. Nós já temos a detecção de colisão pronta para quando um tiro do jogador acertar um invasor. Neste passo, nós iremos remover um invasor que receber um tiro e adicionar o invasor um linha acima no array de invasores que podem disparar. Adicione o seguinte ao método didiBeginContact(_:).

Removeremos a instrução NSLog e primeiro verificamos se contact.bodyA.node?.parent e contact.bodyB.node?.parent são diferente de nil. Eles serão igual a nil se já tivermos processado este contato. Neste caso, nós sairemos da função.

Nós calculamos o invadersPerRow como já havíamos feito antes e definimos firstBody.node? para theInvader, fazendo o casting para um Invader. Em seguida, pegamos a newInvaderRow subtraindo 1 e o newInvaderColumn, que permanece a mesma.

Nós só queremos habilitar invasores para disparar se o newInvaderRow for maior ou igual a 1, do contrário, nós tentaríamos definir um invasor na linha 0 para disparar. Não existe a linha 0 então isto causaria um erro.

Em seguida, não enumeramos entre os invasores, procurando pelo invasor que tem a linha e coluna correta. Uma vez achado, adicionamos ele ao array invadersWhoCanFire e chamamos stop.memory com true para então a enumeração parar imediatamente.

Nós precisamos achar o invasor que recebeu o tiro no array invadersWhoCanFire para podermos remove-lo. Normalmente, arrays tem algum tipo de funcionalidade como um método indexOf ou algo similar para esta função. No momento da escrita, não há nenhum método deste para arrays na linguagem Swift. A Library padrão do Swift define uma função find que nós podemos usar, mas eu encontrei um método na seção de genéricos no Guia de Programação da Linguagem Swift que irá servir para o que precisamos. A função é apropriadamente chamada findIndex. Adicione o seguinte no final do arquivo GameScene.swift.

Se você está curioso sobre como esta função funciona, então eu recomento que você leia mais sobre genéricos no Guia de Programação da Linguagem Swift.

Agora que temos um método, nós podemos usa-lo para procurar o invasor, chama-lo passando o array invadersWhoCanFire e theInvader. Nós verificamos se o invaderIndex não é igual a nil e removemos o invasor do array invadersWhoCanFire usando o método removeAtIndex(index: int).

Você pode testar agora se ele funciona como deveria. Uma forma fácil é comentar onde player.die é chamado no método didBeginContact(_:). Certifique-se de remover o comentario após finalizar os testes. Note que o programa da fecha se você matar todos os invasores. Nós iremos corrigir isso no próximo passo.

O aplicativo fecha, por que temos uma SKAction repeatActionForever(_:) chamando invasores para disparar. Neste ponto, não temos invasores sobrando para disparar então o jogo buga. Nós podemos corrigir isto verificando a propriedade isEmpty no array invadersWhoCanFire. Se o array estiver vazio, o nível termina. Faça a seguinte alteração no método fireInvaderBullet.

O nível completo, significa que temos que incrementar invaderNum, que é usado para os níveis. Então chamamos levelComplete, que nós ainda precisamos criar no próximo passo.

3. Completando um nível

Precisamos ter um número definido de níveis. Se não tivermos, após algumas rodadas nós teremos tantos invasores que eles não irão caber na tela. Adiciona uma propriedade maxLevels na classe GameScene.

Agora adicione o método levelComplete no final do arquivo GameScene.swift.

Primeiro verificamos se o invaderNum é menor ou igual ao maxLevels que definimos. Se for, fazemos a transição para a LevelCompletScene, caso contrario reiniciamos a invaderNum com 1 e chamamos newGame. A LevelCompleteScene ainda não existe nem o método newGame então vamos cria-los um de cada vez nós próximos dois passos.

4. Implementando a classe LevelCompleteScene

Crie uma nova Cocoa Touch Class chamada de LevelCompleteScene que é uma subclasse da SKScene. A implementação da classe ficará como abaixo:

A implementação é idêntica a classe StartGameScreen, exceto que definimos a propriedade name do startGameButton com "nextlevel". Este código deverá ser familiar. Se não, então volte para a primeira parte deste tutorial para uma revisão.

5. newGame

O método newGame simplesmente faz uma transição de volta para a StartGameScene. Adicione o seguinte código no final do arquivo GameScene.swift.

Se você testar o aplicativo, você poderá jogar alguns níveis ou perder alguns jogos, mas o jogador não se moverá e isto torna o jogo chato. Vamos corrigir isto no próximo passo.

6. Movendo o jogador usando o acelerômetro

Nós usaremos o acelerômetro para mover o jogador.  Primeiro precisamos importar o framework CoreMotion. Adicione uma instrução de importação do framework no inicio do arquivo GameScene.swift.

Nós também precisamos de um par de novas propriedades.

Em seguida, adicionamos um método setupAccelerometer no final do arquivo GameScene.swift.

Aqui nós definimos o accelerometerUpdateInterval, que é o intervalo em segundos para fornecer atualizações ao manipulador.  Achei que 0.2 funciona bem, você pode tentar um valor diferente se você quiser. Dentro do manipulador; uma clousure, temos o accelerometerData.acceleration, que é uma estrutura do tipo CMAcceleration.

Nós estamos interessado apenas na propriedade x e usamos uma conversão de tipo numérico para fazer o cast para um CGFloat para nossa propriedade accelerationX.

Agora que temos a propriedade accelerationX definida, nós podemos mover o jogador. Nós fazemos isto no método didSimulatePhysics. Adicione o seguinte código no final do arquivo GameScene.swift.

Chame setupAccelerometer no método didMoveToView(_:) e você será capaz de mover o jogador com o acelerômetro. Mas há apenas um problema. O jogador pode ser mover para fora da tela em ambos os lados e isto faz ele levar alguns segundos para voltar. Nós podemos corrigir isto usando o motor de física e colisões. Faremos isso no próximo passo.

7. Restringindo o movimento do jogador

Como mencionado no passo anterior, o jogador pode ser mover para fora da tela. Isto é simples de corrigir usando o motor de física do Sprite Kit. Primeiro, adicionamos uma nova CollisionCategory chamada EdgeBody.

Defina ele como o collisionBitMask do jogador no seu método init.

Por ultimo, nós criamos uma physicsBody no seu próprio cenário.  Adicione o seguinte código no método didMoveToView(view: SKView) do arquivo GameScene.swift.

Nós inicializamos um corpo físico chamando init(edgeLoopFromRect:), passando o frame do cenário. O inicializador cria um laço na borda do frame do cenário. É importante notar que uma borda não tem volume ou massa e é sempre tratada como se a propriedade dynamic seja igual a false. Bordas também só podem colidir com corpos físicos baseados em volume, como nosso jogador.

Também definimos a categoryBitMask para CollisionCategories.EdgeBody. Se você testar o aplicativo, você pode notar que sua nave não poderá se mover para fora da tela, mas as vezes ela rodará. Quando um corpo físico colide com outro corpo físico, é possível que isto resulte em uma rotação Isto é um comportamente padrão. Para resolver isso, nós definimos allowsRotation para false no arquivo Player.swift.

8. Campo de estrela

Passo 1: Criando o campo de estrelas

O jogo tem um campo de estrelas se movendo no fundo. Nós podemos criar o campo de estrelas usando o motor de partículas do Sprite Kit.

Cria um novo arquivo e selecione Resource na seção iOS. Escolha SpriteKit Particle File como template e clique em Next. Para o Particle template escolha rain e salve como StarField. Clique em Create para abrir o arquivo no editor. Para ver as opções, abra o SKNode Inspector na barra lateral direita.


Ao invés de passar por cada configuração aqui, o que levaria muito tempo, seria melhor ler a documentação para saber mais sobre cada configuração individual. Também não irei entrar em detalhes sobre as configurações do campo de estrelas. Se você estiver interessado, abra o arquivo no Xcode e de uma olhada em cada configuração que eu usei.

Passo 2: Adicionando o campo de estrela no cenário

Adicione o seguinte código no método didMoveToView(_:) no arquivo StartGameScene.swift.

Nós usamos um SKEmitterNode para carregar o arquivo StarFiled.sks, definimos sua position e demos a ele um baixo zPosition. A razão para o baixo zPosition é ter a certeza que não vamos impedir que o usuário toque no botão Iniciar. O sistema de partículas gera centenas de partículas, assim definindo-a muito baixa, podemos superar esse problema. Você também deve saber que você pode configurar manualmente todas as propriedades de partícula em um SKEmitterNode, embora seja muito mais fácil usar o editor para criar um arquivo de .sks e carregá-lo em tempo de execução.

Agora adicione o campo de estrela nos arquivos GameScene.swift e LevelCompleteScene.swift. O código é exatamente o mesmo do acima.

9. Implementando a classe PulsatingText

Passo 1: Criar a classe PulsatingText

A StartGameScene e LevelCompleteScene têm texto que aumenta e diminui repetidamente. Nós usaremos uma subclasse SKLabelNode e um par de instâncias da SKAction para alcançar este efeito.

Criamos uma nova Cocoa Touch Class que é uma subclasse da SKLabelNode, chamamos ela de PulsatingText, e adicionamos o seguinte código à ela.

A primeira coisa que você deve notar é que não há inicializador. Se sua subclasse não define um inicializador designado, ela automaticamente herda todos os inicializadores designados da sua superclasse.

Nós temos um método, setTextFontSizeAndPulsate(theText:theFontSize:), que faz exatamente o que ela diz. Ele define as propriedades text e fontSize da SKLabelNode e cria algumas instâncias SKAction para fazer a escala do texto subir e descer, criando um efeito de pulsação.

Passo 2: Adicionar PulsatingText em StartGameScene

Adicione o código abaixo no arquivo StartGameScene.swift no método didiMoveToView(_:).

Nós inicializamos uma instância PulsatingText, invaderText, e chamamos setTextFontSizeAndPulsate(theText:theFontSize:). Nós também definimos sua position e o adicionamos ao cenário.

Passo 3: Adicionar PulsatingText em LevelCompleteScene

Adicione o código abaixo no arquivo LevelCompleteScene.swift no método didiMoveToView(_:).

É exatamente o mesmo do passo anterior. Apenas definimos um texto diferente.

10. Expandindo o jogo

Isto completa o jogo. Eu tenho algumas sugestões de como você poderia expandir o jogo. Dentro da pasta images, tem três imagens diferentes de invasor. Quando você for adicionar os invasores ao cenário, escolha randomicamente uma entre as três imagens. Você precisará atualizar o inicializador do invasor para aceitar uma imagem como parâmetro. Uma dica, consulte a classe Bullet.

Há também uma imagem UFO. Tenta fazer ela aparecer e mover pela tela a cada 50 segundos. Se o jogador acerta-la, dê a ele uma vida extra. Você pode querer limitar o número de vidas que eles podem ter se você fizer isso. Por ultimo, tente criar um HUD para as vidas do jogador.

Estas são apenas algumas sugestões. Tente fazer seu próprio jogo.

Conclusão

Isto trás esta serie a um final. Você deve ter um jogo semelhando ao jogo original do Space Invaders. Eu espero que você tenha achado este tutorial muito útil e tenha aprendido coisas novas. Obrigado pela leitura.

Seja o primeiro a saber sobre novas traduções–siga @tutsplus_pt no Twitter!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.