Advertisement
  1. Code
  2. iOS

SpriteKit From Scratch: Restrições e actions

Scroll to top
This post is part of a series called SpriteKit From Scratch.
SpriteKit From Scratch: Fundamentals
SpriteKit From Scratch: Physics and Collisions

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

Introdução

Neste tutorial, a segunda edição do SpriteKit from Scratch, você aprenderá sobre restrições e actions. Estes recursos são usados para facilitar a adição de movimento e animações em seu jogo no SpriteKit enquanto limita a posição e orientação dos nós no jogo.

Para me seguir durante o tutorial, você pode usar o projeto que criou no primeiro tutorial desta série ou baixar uma nova cópia no GitHub.

1. Nó personalizado e Classes Scene

Antes de podermos adicionar restrições e actions a uma cena, primeiro precisamos criar algumas classes para que possamos trabalhar com nossos nós no código. Crie uma nova classe, PlayerNode, baseada no template iOS > Source > Cocoa Touch Class e certifique-se que ela seja uma subclasse da SKSpriteNode.

PlayerNode ClassPlayerNode ClassPlayerNode Class

Se o Xcode lançar um erro após criar a classe, adicione uma instrução de importação do framework SpriteKit abaixo da instrução import UIKit.

1
import UIKit
2
import SpriteKit

Em seguida, declare as três propriedades abaixo na classe PlayerNode. Estas propriedades manterão as restrições usadas para limitar o movimento horizontal do carro.

1
import UIKit
2
import SpriteKit
3
4
class PlayerNode: SKSpriteNode {
5
6
    var leftConstraint: SKConstraint!
7
    var middleConstraint: SKConstraint!
8
    var rightConstraint: SKConstraint!
9
10
}

Crie outra Classe Cocoa Touch e a chame de MainScene, tornando ela uma subclasse da SKScene.

MainScene ClassMainScene ClassMainScene Class

Na parte superior, adicione uma instrução de importação para o framework SpriteKit.

1
import UIKit
2
import SpriteKit

Com essas classes criadas, abra a MainScene.sks, clique no fundo cinza e selecione a cena, abra o Custom Class Inspector na direita e atribua MainScene para o Custom Class.

MainScene Custom ClassMainScene Custom ClassMainScene Custom Class

Selecione o carro e defina a ele a classe PlayerNode da mesma forma que você fez para a cena. Por fim, com o carro ainda selecionado, abra o Attributes Inspector e altere o Name para Player.

Player Sprite NamePlayer Sprite NamePlayer Sprite Name

Agora que temos as classes básicas configuradas, podemos começar criando algumas restrições no código.

2. Restrições

As restrições no SpriteKit são representadas pela classe SKConstraint e são usadas para limitar a posição e a orientação de nós específicos. A uma grande variedade de coisas que pode ser conseguida com restrições já que elas podem ser relacionadas a cena ou a outros nós. Restrições também funcionam com intervalos de valores, além de valores constantes para que os sprites dentro de sua cena possam ser fixados em um local específico ou permitir que se movam dentro de uma determinada área.

Vamos estar adicionando três restrições na classe PlayerNode. Estas restrições serão usadas para travar o carro nas três pistas no jogo.

Abra o MainScene.swift e crie uma propriedade para o jogador do tipo PlayerNode!. Esta propriedade armazenará uma referência do nó do jogador.

1
import UIKit
2
import SpriteKit
3
4
class MainScene: SKScene {
5
6
    var player: PlayerNode!
7
8
}

Em seguida, sobrepomos o método didMoveToView(_:) da classe MainScene:

1
override func didMoveToView(view: SKView) {
2
    super.didMoveToView(view)
3
4
    size = view.frame.size
5
6
    if let foundPlayer = childNodeWithName("Player") as? PlayerNode {
7
        player = foundPlayer
8
    }
9
10
    let center = size.width/2.0, difference = CGFloat(70.0)
11
12
    player.leftConstraint = SKConstraint.positionX(SKRange(constantValue: center - difference))
13
    player.middleConstraint = SKConstraint.positionX(SKRange(constantValue: center))
14
    player.rightConstraint = SKConstraint.positionX(SKRange(constantValue: center + difference))
15
16
    player.leftConstraint.enabled = false
17
    player.rightConstraint.enabled = false
18
19
    player.constraints = [player.leftConstraint, player.middleConstraint, player.rightConstraint]
20
}

Vamos percorrer o código passo a passo. O método didMoveToView(_:) é chamado sempre que a cena for apresentada por uma view. Após chamar o método didMoveToView(_:) da superclasse, redimensionamos a cena para o mesmo tamanho da view atual. Isso garante que a cena sempre se estenda devidamente ao tamanho e dimensão da tela do dispositivo atual.

Acessamos o sprite do jogador que adicionamos no editor de cena do Xcode, procurando por ele pelo nome que demos anteriormente. Então atribuímos este valor para a propriedade player.

Após calcular o centro da cena e especificar uma restrição diferente de 70.0, criamos as restrições do sprite. Usando o método de classe positionX(_:) da classe SKConstraint, criamos as restrições da esquerda, meio e direita para o sprite do jogador. Este método requer uma instância SKRange como parâmetro que, no nosso caso, é um range com um valor constante. Se você quer dar uma olhada nas restrições e intervalos possíveis em SpriteKit, eu recomento dar uma olhada na referência das classes SKRange e SKConstraint.

Desabilitamos as restrições da esquerda e da direita, pois não queremos que elas atuem quando o jogo começar. Por último, atribuímos estas restrições á propriedade constraints do nó do jogador. Esta propriedade é definida na classe SKNode.

Compile e rode seu jogo em qualquer simulador ou dispositivo fisico. Você poderá ver agora que a sua cena está dimensionada corretamente com o carro centralizado na parte inferior.

Centered Car in SceneCentered Car in SceneCentered Car in Scene

Você pode ver que o carro está restringido ao centro horizontal da cena e é capaz de ser restringido nas pistas da direita e da esquerda uma vez que adicionarmos algum movimento ao jogo.

2. Actions

Actions em SpriteKit são representadas pela poderosa classe SKAction. As actions nos permitem animar e mover facilmente os sprites em uma cena. Elas são executadas por nós e calculadas pelas APIs do SpriteKit e funcionam ao lado das restrições e simulações de física.

Além de especificar o que uma action faz, você também pode configurar e programar como a action funciona. Você pode, por exemplo, pausar e retomar uma action ou configurar um comportamento ateanuado da action. Isto dá um maior grau de controle para que você possa facilmente acelerar ou desacelerar determinadas actions para produzir alguns elementos de jogabilidade interessantes.

Da mesma forma que os nós podem ter nós filhos, existem três tipos de actions que podem ter actions filhas:

  • sequence actions, que executa um array de actions, uma após a outra
  • group actions, que executa um array de actions, todas de uma vez
  • repeating actions, que repete uma única action por uma quantidade de vezes ou indefinitivamente

Você pode criar actions via programação ou no editor de cena do Xcode, que foi usado no tutorial anterior. Vamos usar ambas as técnicas neste tutorial.

Abra o MainScene.sks e clique no ícone próximo ao botão Animate na parte inferior esquerdo da cena para abrir o Action Editor View.

Open Action Editor ViewOpen Action Editor ViewOpen Action Editor View
Action Editor ViewAction Editor ViewAction Editor View

Em seguida, navegue para o final do Object Library na direita e ache o item Move Action. Clique e arraste ele para a timeline do Action Editor View e o coloque no canto esquerdo como mostrado abaixo.

Add Action to TimelineAdd Action to TimelineAdd Action to Timeline

Isso faz com que a action comece a execução no 0:00, isso é, quando a cena for apresentada. Se coloca-la em outro lugar, a action começará a execução após o intervalo de tempo mostrado na parte superior da timeline.

Passe o mouse sobre a action e clique no ícone pequeno de seta no canto inferior esquerdo. Um popup irá aparecer, clique no botão infinity na esquerda. Isso faz com que a action se repita para sempre.

Repeating Move Action ForeverRepeating Move Action ForeverRepeating Move Action Forever

Com a action ainda selecionada, abra o Attributes Inspector na direita e altere o valor do Y Offset para 100.

Set Y Offset ValueSet Y Offset ValueSet Y Offset Value

Os outros valores especificam que o carro começará a animação imediatamente (Start Time) e a cada 1 segundo (Duration) moverá 0 pontos na direção X e 100 na direção Y (Offset). A propriedade Timing Function pode ser usada para iniciar e/ou parar gradativamente uma action. Neste caso, estamos usando Linear, o que significa que o carro sempre se move na mesma velocidade.

Finalmente, teste a action, clique no botão Animate na parte inferior esquerda do editor de cena. A toolbar na parte inferior ficará azul e o carro começará a se mover para cima.

Action in Xcode Scene EditorAction in Xcode Scene EditorAction in Xcode Scene Editor

Com a action de movimento implementada, é hora de criar as actions horizontais via programação. Antes de fazer isso, precisamos adicionar alguma lógica para que os botões do jogo possam controlar o carro.

Crie um novo arquivo escolhendo o template iOS > Source > Swift File e nomeie de LaneStateMachine.

Swift File TemplateSwift File TemplateSwift File Template

Adicione o seguinte código no novo arquivo:

1
import GameplayKit
2
3
class LaneStateMachine: GKStateMachine {
4
5
}
6
7
class LaneState: GKState {
8
    var playerNode: PlayerNode
9
10
    init(player: PlayerNode) {
11
        playerNode = player
12
    }
13
}
14
15
class LeftLane: LaneState {
16
    override func isValidNextState(stateClass: AnyClass) -> Bool {
17
        if stateClass == MiddleLane.self {
18
            return true
19
        }
20
21
        return false
22
    }
23
24
    override func didEnterWithPreviousState(previousState: GKState?) {
25
        playerNode.moveInDirection(.Left, toLane: self)
26
    }
27
}
28
29
class MiddleLane: LaneState {
30
    override func isValidNextState(stateClass: AnyClass) -> Bool {
31
        if stateClass == LeftLane.self || stateClass == RightLane.self {
32
            return true
33
        }
34
35
        return false
36
    }
37
38
    override func didEnterWithPreviousState(previousState: GKState?) {
39
        if previousState is LeftLane {
40
            playerNode.moveInDirection(.Right, toLane: self)
41
        } else if previousState is RightLane {
42
            playerNode.moveInDirection(.Left, toLane: self)
43
        }
44
    }
45
}
46
47
class RightLane: LaneState {
48
    override func isValidNextState(stateClass: AnyClass) -> Bool {
49
        if stateClass == MiddleLane.self {
50
            return true
51
        }
52
53
        return false
54
    }
55
56
    override func didEnterWithPreviousState(previousState: GKState?) {
57
        playerNode.moveInDirection(.Right, toLane: self)
58
    }
59
}

O que todo esse código faz, é utilizar o novo framework GameplayKit para criar uma máquina de estado que represente as três pistas e o movimento entre elas no jogo. Se você quer entender melhor sobe o que este código está fazendo, verifique meu tutorial sobre o GameplayKit.

Em seguida, abra o PlayerNode.swift e adicione os dois métodos a seguir na classe PalyerNode.

1
func disableAllConstraints() {
2
    leftConstraint.enabled = false
3
    middleConstraint.enabled = false
4
    rightConstraint.enabled = false
5
}
6
7
func moveInDirection(direction: ButtonDirection, toLane lane: LaneState) {
8
    disableAllConstraints()
9
10
    let changeInX = (direction == .Left) ? -70.0 : 70.0
11
    let rotation = (direction == .Left) ? M_PI/4 : -M_PI/4
12
13
    let duration = 0.5
14
    let moveAction = SKAction.moveByX(CGFloat(changeInX), y: 0.0, duration: duration)
15
    let rotateAction = SKAction.rotateByAngle(CGFloat(rotation), duration: duration/2)
16
    rotateAction.timingMode = .EaseInEaseOut
17
    let rotateSequence = SKAction.sequence([rotateAction, rotateAction.reversedAction()])
18
    let moveGroup = SKAction.group([moveAction, rotateSequence])
19
20
    let completion = SKAction.runBlock { () -> Void in
21
        switch lane {
22
        case is LeftLane:
23
            self.leftConstraint.enabled = true
24
        case is MiddleLane:
25
            self.middleConstraint.enabled = true
26
        case is RightLane:
27
            self.rightConstraint.enabled = true
28
        default:
29
            break
30
        }
31
    }
32
    
33
    let sequenceAction = SKAction.sequence([moveGroup, completion])
34
    runAction(sequenceAction)
35
}

O método disableAllConstraints() é um método conveniente para desabilitar as restrições do nó do jogador.

No moveInDirection(_:toLane:), determinamos qual direção o carro irá se mover na horizontal, -70.0 quando se mover para a esquerda e +70.0 quando mover para a direita. Então calculamos o angulo correto (em radianos) para rodar o carro, quando em movimento. Note que o número positivo representa uma rotação no sentido anti-horário.

Após especificar uma duração constante, criamos as actions de movimento e rotação usando os métodos de classes moveByX(_:duration:) e rotateByAngle(_:duration). Criamos uma sequência de rotação para rodar o carro de volta a como estava antes do movimento. O método reversedAction() cria automaticamente o inverso de uma action para você.

Em seguida, criamos um grupo de actions de movimento para executar o movimento horizontal e rodar ao mesmo tempo. Finalmente, criamos uma action complementar para executar uma closure quando executada. Nesta closure, identificamos em qual pista o carro esta e ativamos a restrição correta para esta pista.

Abra o ViewController.swift e adicione uma propriedade, stateMachine, do tipo LaneStateMachine! à classe ViewController.

1
class ViewController: UIViewController {
2
3
    var stateMachine: LaneStateMachine!
4
5
    ...
6
7
}

Substitua as implementações dos métodos viewDidLoad() e didPressButton(_:) na classe ViewController conforme abaixo:

1
override func viewDidLoad() {
2
    super.viewDidLoad()
3
4
    let skView = SKView(frame: view.frame)
5
    let scene = MainScene(fileNamed: "MainScene")!
6
    skView.presentScene(scene)
7
    view.insertSubview(skView, atIndex: 0)
8
9
    let left = LeftLane(player: scene.player)
10
    let middle = MiddleLane(player: scene.player)
11
    let right = RightLane(player: scene.player)
12
13
    stateMachine = LaneStateMachine(states: [left, middle, right])
14
    stateMachine.enterState(MiddleLane)
15
}
16
17
@IBAction func didPressButton(sender: UIButton) {
18
    switch sender.tag {
19
    case ButtonDirection.Left.rawValue:
20
        switch stateMachine.currentState {
21
        case is RightLane:
22
            stateMachine.enterState(MiddleLane)
23
        case is MiddleLane:
24
            stateMachine.enterState(LeftLane)
25
        default:
26
            break
27
        }
28
    case ButtonDirection.Right.rawValue:
29
        switch stateMachine.currentState {
30
        case is LeftLane:
31
            stateMachine.enterState(MiddleLane)
32
        case is MiddleLane:
33
            stateMachine.enterState(RightLane)
34
        default:
35
            break
36
        }
37
    default:
38
        break
39
    }
40
}

No viewDidLoad(), adicionamos o objeto SKView no índice 0, de modo que os botões de controles se tornam visíveis e também inicializamos a máquina de estado.

No didPressButton(_:), identificamos qual botão o usuário pressiona, baseado na tag do botão, e entramos na pista correta a partir de onde o carro esta.

Compile e rode o jogo. Pressione ambos os botões, esquerdo e direito, no fundo da tela para fazer o carro se mover. Você pode ver o carro se virar e se mover na direção do botão que você pressionou.

Turning and Moving CarTurning and Moving CarTurning and Moving Car

Note que os ícones de botão podem ser incompatíveis, como mostrado abaixo.

Mismatched ButtonsMismatched ButtonsMismatched Buttons

Para corrigir isso, abra o catalogo de ativos (Image.xcassets) e para ambas as imagens (Left Arrow e Right Arrow) mude o Rendering Mode para Original Image.

Original Image Rendering ModeOriginal Image Rendering ModeOriginal Image Rendering Mode

Conclusão

Agora você deve estar confiante usando restrições e actions no SpriteKit. Como você pode ver, estes recursos do framework torna muito fácil adicionar animações e movimentos em um jogo no SpriteKit.

No próximo tutorial desta série, iremos dar uma olhada nos nós de câmera do SpriteKit, assim nosso carro não saíra pelo topo da tela. Após isso, iremos nos aprofundar no sistema de simulação de física do SpriteKit com foco nos corpos de física e na detecção de colisão.

Como sempre, deixe seus comentários e feedback nos comentários abaixo.

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

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
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.