Aula 02 – Criando games em python – Objetos Básicos do Jogo

Aula 02 – Criando games em python – Objetos Básicos do Jogo

Games em Python - Objetos Básicos do Jogo

Games em Python – Objetos Básicos do Jogo

Voltar para página principal do blog

Todas as aulas desse curso

Aula 01

Aula 03

Se gostarem do conteúdo dêem um joinha 👍 na página do Código Fluente no
Facebook

Esse é o link do código fluente no Pinterest

Meus links de afiliados:

Hostinger

Digital Ocean

One.com

Para baixar o código acesse o link abaixo:

https://github.com/toticavalcanti/curso_python_games/Jogo_Breakout/aula_02/

Link da documentação oficial do Tkinter:

https://tkdocs.com/

Objetos Básicos do Jogo

Antes de começar a desenhar todos os itens do nosso jogo, vamos definir uma classe base com as funcionalidades que todos terão em comum, armazenando uma referência da tela do jogo(Canvas) e seus itens, com métodos para obter informações sobre a posição de um objeto do jogo, assim como excluir o item da tela:


class GameObject:
    def __init__(self, canvas, item):
        self.canvas = canvas
        self.item = item
    def get_position(self):
        return self.canvas.coords(self.item)
    def move(self, x, y):
        self.canvas.move(self.item, x, y)
    def delete(self):
        self.canvas.delete(self.item)

Na antiga sintaxe do python, para declarar uma classe como a que declaramos acima, era necessário fazer assim:

class GameObject(Object):

Isto é, passar o Object como parâmetro para a classe, agora não é mais necessário, já que todas as classes ou herdam da classe pai, e aí sim é necessário passar como parâmetro a classe pai, ou herdam da classe Object, já que Object é pai de todas as classes python, veja a aula:

https://www.codigofluente.com.br/aula-15-python-orientacao-a-objeto-01/

Dicas de livros relacionados:

                                   

Assumindo que nós criamos um widget Canvas como mostrado nos exemplos de código anteriores, um uso básico desta classe e seus atributos seria assim:


import tkinter as tk
root = tk.Tk()
frame = tk.Frame(root)
canvas = tk.Canvas(frame, width=600, height=400, bg='#aaaaff')
item = canvas.create_rectangle(10,10,100,80, fill='green')
game_object = GameObject(canvas, item) #cria nova instância
print(game_object.get_position())
# [10, 10, 100, 80]
game_object.move(20, -10)
print(game_object.get_position())
# [30, 0, 120, 70]
game_object.delete()

Neste exemplo, criamos um retângulo verde e instanciamos um GameObject passando o item(retângulo) para a criação do objeto jogo.

Em seguida, recuperamos a posição do item na tela(canvas), depois movemos o item e calculamos a posição novamente.

Finalmente, excluímos o item.

Os métodos que a classe GameObject oferece serão reutilizados nas subclasses que iremos ver mais tarde, portanto, essa abstração evita a duplicação de código desnecessária.

Agora que você aprendeu a trabalhar com essa classe base, podemos definir classes filhas separadas para: bola, rebatedor e os tijolos.

Classe Bola

A classe Ball armazenará informações sobre a velocidade, direção e raio da bola.

Nós simplificaremos o movimento da bola, uma vez que o vetor de direção sempre será um desses:

movimento da bola

movimento da bola

Portanto, alterando o sinal de um dos componentes do vetor, nós mudaremos a direção em 90 graus.

Isso acontecerá quando a bola bater contra a borda da tela, quando acertar um tijolo ou o rebatedor do jogador:


class Ball(GameObject):
    def __init__(self, canvas, x, y):
        self.radius = 10
        self.direction = [1, -1]
        self.speed = 10
        item = canvas.create_oval(x-self.radius, y-self.radius,
                                  x+self.radius, y+self.radius,
                                  fill='white')
        super(Ball, self).__init__(canvas, item)

Por enquanto, a inicialização do objeto é suficiente para entender os atributos que a classe possui.

O método speed() define a velocidade do movimento, ou seja, da animação do objeto, 0 será o mais rápido e 10 será a mais lenta.

Vamos tratar a lógica da recuperação da bola mais tarde, quando os outros objetos do jogo tiverem sido definidos e colocados no canvas do jogo.

Classe Rebatedor

A classe Hitter representa a raquete ou rebatedor do jogador e tem dois atributos para armazenar a largura e altura.

Um método set_ball será usado para armazenar uma referência para a bola, que pode ser movido com a bola antes do jogo começar:


class Hitter(GameObject):
    def __init__(self, canvas, x, y):
        self.width = 80
        self.height = 10
        self.ball = None
        item = canvas.create_rectangle(x - self.width / 2,
                                       y - self.height / 2,
                                       x + self.width / 2,
                                       y + self.height / 2,
                                       fill='blue')
        super(Hitter, self).__init__(canvas, item)
    def set_ball(self, ball):
        self.ball = ball
    def move(self, offset):
        coords = self.get_position()
        width = self.canvas.winfo_width()
        if coords[0] + offset >= 0 and \
            coords[2] + offset <= width:
            super(Hitter, self).move(offset, 0)
            if self.ball is not None:
                self.ball.move(offset, 0)

O método move é responsável pelo movimento horizontal do rebatedor.

Veja a lógica por trás deste método(move) passo a passo:

  • O self.get_position() calcula as coordenadas atuais do rebatedor.
  • O self.canvas.winfo_width() recupera a largura da tela.
  • Se as coordenadas mínima e máxima do eixo x, mais o deslocamento produzido pelo o movimento, estão dentro dos limites da tela, é isso que acontece:
    • O super(Hitter, self).move(offset, 0) chama o método com o mesmo nome na classe pai da classe Hitter, que move o item dentro da tela.
    • Se o rebatedor ainda tem uma referência para a bola (isso acontece quando o jogo não foi iniciado), a bola é movida também.

Este método será ligado às teclas de entrada para que o jogador possa usá-las para controlar o movimento do rebatedor.

Nós veremos mais tarde como podemos usar o Tkinter para processar a chave de entrada de eventos.

Por enquanto, vamos passar para a implementação do último componente do nosso jogo.

Classe Tijolo

Cada tijolo do nosso jogo será uma instância da classe Brick.

Esta classe contém a lógica que é executada quando os tijolos são atingidos e destruídos:


class Brick(GameObject):
    COLORS = {1: '#999999', 2: '#555555', 3: '#222222'}
    def __init__(self, canvas, x, y, hits):
        self.width = 75
        self.height = 20
        self.hits = hits
        color = Brick.COLORS[hits]
        item = canvas.create_rectangle(x - self.width / 2,
                                       y - self.height / 2,
                                       x + self.width / 2,
                                       y + self.height / 2,
                                       fill=color, tags='brick')
        super(Brick, self).__init__(canvas, item)
    def hit(self):
        self.hits -= 1
        if self.hits == 0:
            self.delete()
        else:
            self.canvas.itemconfig(self.item,
                                   fill=Brick.COLORS[self.hits])

Como você deve ter notado, o método __init__ é muito parecido com o da classe Hitter, uma vez que desenha um retângulo e armazena a largura e a altura da forma.

Nesse caso, o valor da tag option passada como um argumento de palavra-chave é ‘brick‘.

Com esta tag, podemos verificar se o jogo acabou quando o número de itens restantes com esta tag é zero.

Outra diferença da classe Hitter é o método hit e os atributos usados.

A variável de classe chamada COLORS é um dicionário, uma estrutura de dados que contém pares chave / valor com o número de impactos que o tijolo pode receber e a cor correspondente.

Quando um tijolo é atingido, a execução do método ocorre da seguinte forma:

  • O número de ocorrências da instância do brick é diminuído em 1.
  • Se o número de ocorrências restantes for 0, self.delete() excluirá o tijolo da tela.
  • Caso contrário, self.canvas.itemconfig() altera a cor do tijolo.

Por exemplo, se chamarmos esse método para um bloco com dois golpes(hits) restantes, diminuiremos o contador em 1 e a nova cor será # 999999, que é o valor de Brick.COLORS[1].

Se o mesmo tijolo for atingido novamente, o número de golpes(hits) restantes se tornará zero e o item será deletado.

O código completo dessa aula é:

exemplo_02/exemplo_02.py


import tkinter as tk


class GameObject(object):
    def __init__(self, canvas, item):
        self.canvas = canvas
        self.item = item

    def get_position(self):
        return self.canvas.coords(self.item)

    def move(self, x, y):
        self.canvas.move(self.item, x, y)

    def delete(self):
        self.canvas.delete(self.item)


class Ball(GameObject):
    def __init__(self, canvas, x, y):
        self.radius = 10
        self.direction = [1, -1]
        self.speed = 10
        item = canvas.create_oval(x-self.radius, y-self.radius,
                                  x+self.radius, y+self.radius,
                                  fill='white')
        super(Ball, self).__init__(canvas, item)


class Hitter(GameObject):
    def __init__(self, canvas, x, y):
        self.width = 80
        self.height = 10
        self.ball = None
        item = canvas.create_rectangle(x - self.width / 2,
                                       y - self.height / 2,
                                       x + self.width / 2,
                                       y + self.height / 2,
                                       fill='blue')
        super(Hitter, self).__init__(canvas, item)
    def set_ball(self, ball):
        self.ball = ball
    def move(self, offset):
        coords = self.get_position()
        width = self.canvas.winfo_width()
        if coords[0] + offset >= 0 and \
            coords[2] + offset <= width:
            super(Hitter, self).move(offset, 0)
            if self.ball is not None:
                self.ball.move(offset, 0)


class Brick(GameObject):
    COLORS = {1: '#999999', 2: '#555555', 3: '#222222'}
    def __init__(self, canvas, x, y, hits):
        self.width = 75
        self.height = 20
        self.hits = hits
        color = Brick.COLORS[hits]
        item = canvas.create_rectangle(x - self.width / 2,
                                       y - self.height / 2,
                                       x + self.width / 2,
                                       y + self.height / 2,
                                       fill=color, tags='brick')
        super(Brick, self).__init__(canvas, item)
    def hit(self):
        self.hits -= 1
        if self.hits == 0:
            self.delete()
        else:
            self.canvas.itemconfig(self.item,
                                   fill=Brick.COLORS[self.hits])

Ficamos por aqui e até a próxima.

Aula 01

Aula 03

Todas as aulas desse curso

Voltar para página principal do blog

Para baixar o código acesse o link abaixo:

https://github.com/toticavalcanti/curso_python_games/Jogo_Breakout/aula_02/

Se gostarem do conteúdo dêem um joinha 👍 na página do Código Fluente no
Facebook

Link do código fluente no Pinterest

Novamente deixo meus link de afiliados:

Hostinger

Digital Ocean

One.com

Obrigado, até a próxima e bons estudos. 😉

About The Author
-

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>