Arquitetura baseada em eventos para SpriteKit
Este artigo é um pedaço do que apresentei na talk que dei no CocoaHeads ES em Vitória, realizado na Brooder, em novembro de 2019, “Criando o seu primeiro jogo com SpriteKit”. Lá, falo um pouco mais sobre os objetos, funcionalidades e lifecycle do SpriteKit em si, além de introduzir a arquitetura baseada em eventos.
O SpriteKit é um framework desenvolvido pela Apple para a criação de jogos de alta performance em 2D. Como ex-desenvolvedor de jogos, este framework me atraiu naturalmente assim que comecei a estudar Swift e desenvolver apps, mas como desenvolvia com a Unity, que é bastante opinativa em sua arquitetura, fiquei um pouco perdido ao tentar criar experiências com ele. A talk que fiz e por consequência este artigo são o resultado de uma tentativa de deixar o desenvolvimento com SpriteKit mais estruturado para facilitar o seu entendimento.
O jogo de exemplo que desenvolvi para expor esta arquitetura é um infinite runner bem simples, que conta com todos os elementos básicos que se espera de um jogo: uma personagem que tem movimentos, ações, animações, pontos de vida, sons, que existe em um ambiente animado com inimigos que reagem a personagem e tentam acabar com a existência dela, e claro, com uma boa trilha sonora de fundo também.
O objeto básico do SpriteKit é o SKNode
, todos os objetos que vão ser utilizados no contexto dele são do mesmo tipo, e quando juntos, formam uma árvore de nós pais e filhos, com métodos prontos para acessá-los.
var scene: SKScene?
The scene node that contains this node.
var parent: SKNode?
The node's parent node.
var children: [SKNode]
The node's children.
Quando um nó qualquer é criado, o SpriteKit automaticamente organiza a sua cadeia com essa estrutura, e podemos assim criar uma forma de comunicação padronizada entre os nós. Com um objeto base e uma simples extension, nosso nó pode receber um evento e escolher propagar, modificar ou impedir que este evento continue pela cadeia:
Com estes dois métodos, quando criarmos um nó qualquer, ele automaticamente propaga o evento passado para baixo ou para cima na cadeia e quando quisermos causar alguma mudança no evento, podemos lidar com eles assim:
Também podemos criar elementos na árvore que não fazem parte do SpriteKit, que não são necessariamente um SKNode
, para lidar com elementos externos, como uma view do UIKit
, um objeto que controle o haptic feedback do device pra gente, etc. Para isso, utilizo o protocolo EventHandler
, que meus SKNodes
implementam:
Agora que temos uma base, podemos começar a criar nossos objetos, eventos e a interação que pode acontecer entre eles. Para começar, estabeleço toda a criação dos nós de UI, da fase e do personagem no começo do ciclo de vida de minha SKScene
Cada evento que será passado pelo sistema é um objeto completo que pode apenas indicar alguma coisa que aconteceu, ou carregar dados adicionais sobre o evento em si, como o SpeedEvent
, que define a velocidade do jogo, e cada objeto meu lida com ele da forma que desejar:
Para lidar com eventos que apenas indicam algum acontecimento no sistema, podemos simplificar o processo:
E por fim, para mostrar a facilidade de se criar objetos que interagem com o sistema como um todo e causam efeitos na cadeia, mesmo que não sejam SKNodes, para adicionar suporte aos controles de Xbox One e PlayStation 4 no nosso jogo, podemos simplesmente adicionar uma classe como esta:
A talk que fiz tem um hands-on no final que mostra essa arquitetura toda funcionando, incluindo um live code que mostra o quão fácil é incluir novas funcionalidades e criar objetos que interagem com o sistema de forma dinâmica, sem a necessidade de objetos que conhecem uns aos outros diretamente. O projeto completo do jogo pode ser visto no meu GitHub