Desenvolvendo o jogo Snake [parte 2]

8 minute read

Nesta parte nós iremos construir o grid, que é necessário para o movimento da cobra.

Posição e movimento

A primeira coisa que devemos definir é a posição da cobra. Para isso, podemos criar dois atributos na nossa classe Game.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import pyxel

class Game:
    def __init__(self):
        pyxel.init(208, 208)
        self.x = pyxel.width/2
        self.y = pyxel.height/2
        pyxel.run(self.update, self.draw)

      def update(self):
          self.x = (self.x + 1) % pyxel.width

      def draw(self):
          pyxel.rect(self.x, self.y, 8, 8, 7)
          pyxel.cls(0)

Game()

Definimos os atributos x e y para a posição da cabeça da cobra. Também definimos novos valores para a largura e altura. Baseado em alguns testes que eu fiz, 208 pixels é um bom tamanho, considerando o tamanho dos sprites que usaremos, que serão de 8x8 pixels. Este é o tamanho normal de um sprite no Pyxel. Mesmo que não estejamos usando sprites no momento, nós usaremos, então é bom já pensarmos nisso de antemão para podermos nos antecipar e evitar retrabalho no futuro. Já atualizei também a linha que desenha o quadrado na tela, para que ele desenhe um quadrado 8x8 na cor branca (cor 7).

Vamos tentar agora fazer com que a nossa cobra se movimente. Lembre que ainda não temos uma cobra de fato, apenas a sua cabeça, e sabemos onde ela está (x e y). Pode ser uma boa ideia criarmos variáveis para guardar valores comuns que usamos no código, promovendo um pouco mais de legibilidade, pois podemos nos referenciar aos valores através de nomes mais significativos no contexto do jogo. No nosso caso, eu optei por criar quatro variáveis para representar a direção que a cobra pode seguir. Também criei um novo atributo na classe Game para armazenar a direção atual da cobra. Veja como ficou:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import pyxel

#direcoes da cobra
CIMA = 0
DIREITA = 1
BAIXO = 2
ESQUERDA = 3

class Game:
  def __init__(self):
    pyxel.init(208, 208)
    self.x = pyxel.width/2
    self.y = pyxel.height/2
    self.direcao = DIREITA
    pyxel.run(self.update, self.draw)

  def update(self):
    self.x = (self.x + 1) % pyxel.width

  def draw(self):
    pyxel.cls(0)
    pyxel.rect(self.x, self.y, 8, 8, 7)

Game()

Definir a direção de movimento da cobra

Certo, desse jeito agora a nossa cobra vai se movimentar sempre para a direita. Mas ainda não temos duas coisas importantes, (1) o código que de fato movimenta a cobra frame a frame, e (2) o código que faz a cobra responder aos nossos comandos no teclado. Vamos atacar o item (2) primeiro, que é um pouco mais simples. O Pyxel nos fornece algumas funções utilitárias, para que possamos detectar entradas do usuário a partir do teclado. Ele permite que identifiquemos se alguma tecla está pressionada através da função pyxel.btn(<tecla>). Em <tecla> você informa qual tecla você quer saber se está pressionada. Encontre aqui a definição das teclas que você pode usar. No nosso caso estamos interessados, pelo menos por enquanto, nas setas do teclado: pyxel.KEY_UP, pyxel.KEY_DOWN, pyxel.KEY_RIGHT, pyxel.KEY_LEFT. Como essa lógica diz respeito a atualização de estado do jogo (posição da cobra), vamos colocar um trecho de código na função update.

Observe que a movimentação desejada pelo jogador nem sempre é permitida neste jogo. Tomemos como exemplo o if na linha 18: se a seta para cima está pressionada e a direção atual é diferente de BAIXO, alteramos a direção atual para CIMA. Em outras palavras, o jogador não pode mover a cobra para cima se ela estiver indo para baixo. Qualquer outra condição é permitida. Essa lógica se repete para cada posição, ajustando-se o teste correspondente.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import pyxel

#direcoes da cobra
CIMA = 0
DIREITA = 1
BAIXO = 2
ESQUERDA = 3

class Game:
  def __init__(self):
    pyxel.init(208, 208)
    self.x = pyxel.width/2
    self.y = pyxel.height/2
    self.direcao = DIREITA
    pyxel.run(self.update, self.draw)

  def update(self):
    #atualiza a direção atual de acordo com entrada do usuario
    if pyxel.btn(pyxel.KEY_UP) and self.direcao != BAIXO:
      self.direcao = CIMA
    elif pyxel.btn(pyxel.KEY_RIGHT) and self.direcao != ESQUERDA:
      self.direcao = DIREITA
    elif pyxel.btn(pyxel.KEY_DOWN) and self.direcao != CIMA:
      self.direcao = BAIXO
    elif pyxel.btn(pyxel.KEY_LEFT) and self.direcao != DIREITA:
      self.direcao = ESQUERDA

  def draw(self):
    pyxel.cls(0)
    pyxel.rect(self.x, self.y, 8, 8, 7)

Game()

Movimentar a cobra

Agora o item (1) da seção anterior. Antes usávamos a instrução self.x = (self.x + 1) % pyxel.width para atualizar a posição da cobra, mas agora nós precisamos fazer uma movimentação ligeiramente mais complexa. Se a direção é CIMA, a cobra precisa ir pra cima, e de maneira análoga para as outras direções. Também não queremos que ela se movimente pixel a pixel, mas de 8 em 8 pixels, que é o tamanho do nosso quadro (e futuro sprite). Isso faz a gente criar a ideia de um grid por onde ela se movimenta, pois ela não pode ficar em qualquer posição na tela, mas em múltiplos de 8, para poder casar com o tamanho dos nossos sprites. Utilizamos a variável pyxel.frame_count do Pyxel, que armazena a contagem de pixels desde o início da execução. Quando colocamos no if a condição pyxel.frame_count % 5 == 0, o seu bloco de código só executará quando pyxel.frame_count for um múltiplo de 5.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import pyxel

#direcoes da cobra
CIMA = 0
DIREITA = 1
BAIXO = 2
ESQUERDA = 3

class Game:
  def __init__(self):
    pyxel.init(208, 208)
    self.x = pyxel.width/2
    self.y = pyxel.height/2
    self.direcao = DIREITA
    pyxel.run(self.update, self.draw)

  def update(self):
    #atualiza a direção atual de acordo com entrada do usuario
    if pyxel.btn(pyxel.KEY_UP) and self.direcao != BAIXO:
      self.direcao = CIMA
    elif pyxel.btn(pyxel.KEY_RIGHT) and self.direcao != ESQUERDA:
      self.direcao = DIREITA
    elif pyxel.btn(pyxel.KEY_DOWN) and self.direcao != CIMA:
      self.direcao = BAIXO
    elif pyxel.btn(pyxel.KEY_LEFT) and self.direcao != DIREITA:
      self.direcao = ESQUERDA

    #atualizar a posicao da cobra a cada 5 frames
    if pyxel.frame_count % 5 == 0:
      if self.direcao == CIMA:
        self.y -= 8
      elif self.direcao == DIREITA:
        self.x += 8
      elif self.direcao == BAIXO:
        self.y += 8
      elif self.direcao == ESQUERDA:
        self.x -= 8

  def draw(self):
    pyxel.cls(0)
    pyxel.rect(self.x, self.y, 8, 8, 7)

Game()

Ainda falta um pequeno detalhe: no jogo original, quando a cobra sai por alguma das bordas do jogo, ela entra novamente na borda oposta. Se saiu pela direita, reentra com o mesmo valor de y do lado esquerdo. Se saiu por cima, reentra com o mesmo valor de x na borda inferior. Vamos alterar o código que temos para implementar esse comportamento.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import pyxel

#direcoes da cobra
CIMA = 0
DIREITA = 1
BAIXO = 2
ESQUERDA = 3

class Game:
    def __init__(self):
      pyxel.init(208, 208)
      self.x = pyxel.width/2
      self.y = pyxel.height/2
      self.direcao = DIREITA
      self.tamanho_sprite = 8
      pyxel.run(self.update, self.draw)

    def update(self):
      #atualiza a direção atual de acordo com entrada do usuario
      if pyxel.btn(pyxel.KEY_UP) and self.direcao != BAIXO:
        self.direcao = CIMA
      elif pyxel.btn(pyxel.KEY_RIGHT) and self.direcao != ESQUERDA:
        self.direcao = DIREITA
      elif pyxel.btn(pyxel.KEY_DOWN) and self.direcao != CIMA:
        self.direcao = BAIXO
      elif pyxel.btn(pyxel.KEY_LEFT) and self.direcao != DIREITA:
        self.direcao = ESQUERDA

      if pyxel.frame_count % 15 == 0:
        if self.direcao == CIMA:
            if self.y - self.tamanho_sprite < 0:
                self.y =  pyxel.height - self.tamanho_sprite
            else:
                self.y -= 8
        elif self.direcao == DIREITA:
            if self.x + self.tamanho_sprite > pyxel.width - self.tamanho_sprite:
                self.x = 0
            else:
                self.x += 8
        elif self.direcao == BAIXO:
            if self.y + self.tamanho_sprite > pyxel.height - self.tamanho_sprite:
                self.y = 0
            else:
                self.y += 8
        elif self.direcao == ESQUERDA:
            if self.x - self.tamanho_sprite < 0:
                self.x = pyxel.width - self.tamanho_sprite
            else:
                self.x -= 8

    def draw(self):
        pyxel.cls(0)
        pyxel.rect(self.x, self.y, self.tamanho_sprite, self.tamanho_sprite, 7)

Game()

O código que temos aqui pode ser encontrado no repositório GitHub do projeto, sob tag v0.2.

Leave a comment