[PYTHON] Créez un jeu à la Tetris!

introduction

Cette fois, j'ai fait un jeu de style Tetris en utilisant tkinter de Python. Le programme étant devenu assez long, j'omettrai l'explication de manière significative, mais je n'expliquerai que les points.

Types de tétrimino

Il y avait 4 types dans le programme du site auquel je faisais référence, mais j'ai essayé d'en faire 7 types avec un peu d'ingéniosité. Tetrimino comprend les éléments suivants. t.PNG  Tミノ    z.PNG  Zミノ      i.PNG  Iミノ        j.PNG  Lミノ l.PNG  Jミノ     o.PNG  Oミノ      s.PNG  Sミノ

Écran de jeu

テトリス.PNG  20200707_212249.gif

L'écran de jeu a 20 carrés verticalement et 10 carrés horizontalement. Si vous appuyez sur le bouton Démarrer sur le côté droit, Tetrimino descendra et le jeu commencera. Semblable au Tetris original, si une ligne horizontale est alignée, une ligne disparaît et si Tetrimino est empilé vers le haut, le jeu est terminé. gameover.PNG←ゲームオーバー時の表示

Cependant, il convient de noter que contrairement au Tetris original, Tetrimino ne peut pas être tourné. Par conséquent, la difficulté de survivre pendant longtemps a considérablement augmenté. Au fait, j'ai aussi essayé le jeu de test, mais j'ai eu du mal.

Exemple terminé du programme

# -*- coding:utf-8 -*-
import tkinter as tk
import random      

#constant
BLOCK_SIZE = 25  #Taille verticale et horizontale du bloc px
FIELD_WIDTH = 10  #Largeur du champ
FIELD_HEIGHT = 20  #Hauteur du champ

MOVE_LEFT = 0  #Constante indiquant de déplacer le bloc vers la gauche
MOVE_RIGHT = 1  #Constante indiquant de déplacer le bloc vers la droite
MOVE_DOWN = 2  #Constante indiquant de déplacer le bloc vers le bas

#Classe carrée qui compose le bloc
class TetrisSquare():
    def __init__(self, x=0, y=0, color="gray"):
        'Créer un carré'
        self.x = x
        self.y = y
        self.color = color

    def set_cord(self, x, y):
        'Définissez les coordonnées du carré'
        self.x = x
        self.y = y

    def get_cord(self):
        'Obtenez les coordonnées du carré'
        return int(self.x), int(self.y)

    def set_color(self, color):
        'Définir la couleur du carré'
        self.color = color

    def get_color(self):
        'Obtenez la couleur du carré'
        return self.color

    def get_moved_cord(self, direction):
        'Obtenez les coordonnées du carré après le déplacement'

        #Obtenez les coordonnées du carré avant de vous déplacer
        x, y = self.get_cord()

        #Calculez les coordonnées après le déplacement en tenant compte de la direction du déplacement
        if direction == MOVE_LEFT:
            return x - 1, y
        elif direction == MOVE_RIGHT:
            return x + 1, y
        elif direction == MOVE_DOWN:
            return x, y + 1
        else:
            return x, y

#Classe de toile pour dessiner l'écran Tetris
class TetrisCanvas(tk.Canvas):
    def __init__(self, master, field):
        'Créez une toile pour dessiner Tetris'

        canvas_width = field.get_width() * BLOCK_SIZE
        canvas_height = field.get_height() * BLOCK_SIZE

        # tk.Initiation à la classe de toile
        super().__init__(master, width=canvas_width, height=canvas_height, bg="white")

        #Placez la toile sur l'écran
        self.place(x=25, y=25)

        #Créez un écran Tetris en dessinant des carrés de 10 x 20
        for y in range(field.get_height()):
            for x in range(field.get_width()):
                square = field.get_square(x, y)
                x1 = x * BLOCK_SIZE
                x2 = (x + 1) * BLOCK_SIZE
                y1 = y * BLOCK_SIZE
                y2 = (y + 1) * BLOCK_SIZE
                self.create_rectangle(
                    x1, y1, x2, y2,
                    outline="white", width=1,
                    fill=square.get_color()
                )

        #Définir le champ précédemment dessiné
        self.before_field = field

    def update(self, field, block):
        'Mettre à jour l'écran Tetris'

        #Créer un champ de dessin (champ + bloc)
        new_field = TetrisField()
        for y in range(field.get_height()):
            for x in range(field.get_width()):
                square = field.get_square(x, y)
                color = square.get_color()

                new_square = new_field.get_square(x, y)
                new_square.set_color(color)

        #Combiner les informations de carré de bloc avec le champ
        if block is not None:
            block_squares = block.get_squares()
            for block_square in block_squares:
                #Obtenez les coordonnées et la couleur du carré du bloc
                x, y = block_square.get_cord()
                color = block_square.get_color()

                #Mettre à jour la couleur du carré sur le champ des coordonnées acquises
                new_field_square = new_field.get_square(x, y)
                new_field_square.set_color(color)

        #Dessiner sur un canevas à l'aide de champs de dessin
        for y in range(field.get_height()):
            for x in range(field.get_width()):

                # (x,y)Obtenir la couleur du champ de coordonnées
                new_square = new_field.get_square(x, y)
                new_color = new_square.get_color()

                # (x,y)Ne dessinez pas si les coordonnées n'ont pas changé depuis le dernier dessin
                before_square = self.before_field.get_square(x, y)
                before_color = before_square.get_color()
                if(new_color == before_color):
                    continue

                x1 = x * BLOCK_SIZE
                x2 = (x + 1) * BLOCK_SIZE
                y1 = y * BLOCK_SIZE
                y2 = (y + 1) * BLOCK_SIZE
                #Dessinez un rectangle avec la couleur de chaque position dans le champ
                self.create_rectangle(
                    x1, y1, x2, y2,
                    outline="white", width=1, fill=new_color
                )

        #Mettre à jour les informations du champ dessiné la dernière fois
        self.before_field = new_field

#Classe de champ qui gère les informations des blocs empilés
class TetrisField():
    def __init__(self):
        self.width = FIELD_WIDTH
        self.height = FIELD_HEIGHT

        #Champ Initialiser
        self.squares = []
        for y in range(self.height):
            for x in range(self.width):
                #Gérez les champs sous forme de liste d'instances carrées
                self.squares.append(TetrisSquare(x, y, "gray"))

    def get_width(self):
        'Obtenez le nombre de carrés dans le champ (horizontal)'

        return self.width

    def get_height(self):
        'Obtenez le nombre de carrés dans le champ (vertical)'

        return self.height

    def get_squares(self):
        'Obtenez une liste des carrés qui composent le champ'

        return self.squares

    def get_square(self, x, y):
        'Obtenez le carré aux coordonnées spécifiées'

        return self.squares[y * self.width + x]

    def judge_game_over(self, block):
        'Déterminez si le jeu est terminé'

        #Créer un ensemble de coordonnées déjà renseignées sur le terrain
        no_empty_cord = set(square.get_cord() for square
                            in self.get_squares() if square.get_color() != "gray")

        #Créer un ensemble de coordonnées avec des blocs
        block_cord = set(square.get_cord() for square
                         in block.get_squares())

        #Avec un ensemble de coordonnées de bloc
        #Créer un ensemble de produits d'ensembles de coordonnées déjà renseignés dans le champ
        collision_set = no_empty_cord & block_cord

        #Si l'ensemble de produits est vide, le jeu n'est pas terminé
        if len(collision_set) == 0:
            ret = False
        else:
            ret = True

        return ret

    def judge_can_move(self, block, direction):
        'Déterminer si le bloc peut être déplacé dans la direction spécifiée'

        #Créer un ensemble de coordonnées déjà renseignées sur le terrain
        no_empty_cord = set(square.get_cord() for square
                            in self.get_squares() if square.get_color() != "gray")

        #Création d'un ensemble de coordonnées avec le bloc déplacé
        move_block_cord = set(square.get_moved_cord(direction) for square
                              in block.get_squares())

        #Déterminez s'il est hors du terrain
        for x, y in move_block_cord:

            #Ne peut pas bouger s'il dépasse
            if x < 0 or x >= self.width or \
                    y < 0 or y >= self.height:
                return False

        #Avec l'ensemble des coordonnées du bloc après le déplacement
        #Créer un ensemble de produits d'ensembles de coordonnées déjà renseignés dans le champ
        collision_set = no_empty_cord & move_block_cord

        #Mobile si l'ensemble de produits est vide
        if len(collision_set) == 0:
            ret = True
        else:
            ret = False

        return ret

    def fix_block(self, block):
        'Fixer le bloc et ajouter au champ'

        for square in block.get_squares():
            #Obtenez les coordonnées et les couleurs des carrés contenus dans le bloc
            x, y = square.get_cord()
            color = square.get_color()

            #Refléter les coordonnées et la couleur dans le champ
            field_square = self.get_square(x, y)
            field_square.set_color(color)

    def delete_line(self):
        'Supprimer une ligne'

        #Vérifiez si toutes les lignes peuvent être supprimées
        for y in range(self.height):
            for x in range(self.width):
                #Il ne peut pas être effacé s'il y en a même un vide dans la ligne
                square = self.get_square(x, y)
                if(square.get_color() == "gray"):
                    #À la ligne suivante
                    break
            else:
                #Si elle n'est pas interrompue, la ligne est pleine
                #Supprimez cette ligne et déplacez la ligne au-dessus de cette ligne d'une ligne vers le bas
                for down_y in range(y, 0, -1):
                    for x in range(self.width):
                        src_square = self.get_square(x, down_y - 1)
                        dst_square = self.get_square(x, down_y)
                        dst_square.set_color(src_square.get_color())
                #La ligne du haut est toujours vide
                for x in range(self.width):
                    square = self.get_square(x, 0)
                    square.set_color("gray")

#Classe de bloc Tetris
class TetrisBlock():
    def __init__(self):
        'Créer un bloc de Tetris'

        #Liste des carrés qui composent le bloc
        self.squares = []

        #Déterminez aléatoirement la forme du bloc
        block_type = random.randint(1, 7)

        #Déterminez les coordonnées et les couleurs des quatre carrés en fonction de la forme du bloc
        if block_type == 1:
            color = "aqua"
            cords = [
                [FIELD_WIDTH / 2, 0],
                [FIELD_WIDTH / 2, 1],
                [FIELD_WIDTH / 2, 2],
                [FIELD_WIDTH / 2, 3],
            ]
        elif block_type == 2:
            color = "yellow"
            cords = [
                [FIELD_WIDTH / 2, 0],
                [FIELD_WIDTH / 2, 1],
                [FIELD_WIDTH / 2 - 1, 0],
                [FIELD_WIDTH / 2 - 1, 1],
            ]
        elif block_type == 3:
            color = "orange"
            cords = [
                [FIELD_WIDTH / 2 - 1, 0],
                [FIELD_WIDTH / 2, 0],
                [FIELD_WIDTH / 2, 1],
                [FIELD_WIDTH / 2, 2],
            ]
        elif block_type == 4:
            color = "blue"
            cords = [
                [FIELD_WIDTH / 2, 0],
                [FIELD_WIDTH / 2 - 1, 0],
                [FIELD_WIDTH / 2 - 1, 1],
                [FIELD_WIDTH / 2 - 1, 2],
            ] 

        elif block_type == 5:
            color = "red"
            cords = [
                [FIELD_WIDTH / 2, 0],
                [FIELD_WIDTH / 2, 1],
                [FIELD_WIDTH / 2 - 1, 1],
                [FIELD_WIDTH / 2 - 1, 2],
            ]     

        elif block_type == 6:
            color = "green"
            cords = [
                [FIELD_WIDTH / 2 - 1, 0],
                [FIELD_WIDTH / 2 - 1, 1],
                [FIELD_WIDTH / 2, 2],
                [FIELD_WIDTH / 2, 1],
            ]      

        elif block_type == 7:
            color = "purple"
            cords = [
                [FIELD_WIDTH / 2, 1],
                [FIELD_WIDTH / 2 - 1, 0],
                [FIELD_WIDTH / 2 - 1, 1],
                [FIELD_WIDTH / 2 - 1, 2],
            ]     

        #Créez un carré avec la couleur et les coordonnées déterminées et ajoutez-le à la liste
        for cord in cords:
            self.squares.append(TetrisSquare(cord[0], cord[1], color))

    def get_squares(self):
        'Obtenez les carrés qui composent le bloc'

        # return [square for square in self.squares]
        return self.squares

    def move(self, direction):
        'Déplacer les blocs'

        #Déplacez les carrés qui composent le bloc
        for square in self.squares:
            x, y = square.get_moved_cord(direction)
            square.set_cord(x, y)

#Classe qui contrôle le jeu Tetris
class TetrisGame():

    def __init__(self, master):
        'Instanciation de Tetris'

        #Initialiser la liste de gestion des blocs
        self.field = TetrisField()

        #Réglez le bloc de chute
        self.block = None

        #Définir l'écran Tetris
        self.canvas = TetrisCanvas(master, self.field)

        #Mise à jour de l'écran Tetris
        self.canvas.update(self.field, self.block)

    def start(self, func):
        'Démarrez Tetris'

        #Définir la fonction à appeler à la fin
        self.end_func = func

        #Initialiser la liste de gestion des blocs
        self.field = TetrisField()

        #Nouveau bloc d'automne ajouté
        self.new_block()

    def new_block(self):
        'Ajouter un nouveau bloc'

        #Créer une instance de bloc tombant
        self.block = TetrisBlock()

        if self.field.judge_game_over(self.block):
           self.end_func()
           print("Game Over!")

            #Mettre à jour l'écran Tetris
        self.canvas.update(self.field, self.block)
        
    def move_block(self, direction):
        'Déplacer les blocs'

        #Bouge seulement si tu peux bouger
        if self.field.judge_can_move(self.block, direction):

            #Déplacer les blocs
            self.block.move(direction)

            #Écran de mise à jour
            self.canvas.update(self.field, self.block)

        else:
            #Si le bloc ne peut pas descendre
            if direction == MOVE_DOWN:
                #Réparez le bloc
                self.field.fix_block(self.block)
                self.field.delete_line()
                self.new_block()

#Une classe qui accepte les événements et contrôle Tetris en fonction des événements
class EventHandller():
    def __init__(self, master, game):
        self.master = master

        #Jeu à contrôler
        self.game = game

        #Minuterie qui publie régulièrement des événements
        self.timer = None

        #Installer le bouton de démarrage du jeu
        button = tk.Button(master, text='START', command=self.start_event)
        button.place(x=25 + BLOCK_SIZE * FIELD_WIDTH + 25, y=30)

    def start_event(self):
        'Traitement lorsque le bouton de démarrage du jeu est enfoncé'

        #Démarrez Tetris
        self.game.start(self.end_event)
        self.running = True

        #Jeu de minuterie
        self.timer_start()

        #Commencer à accepter l'entrée d'opération de touche
        self.master.bind("<Left>", self.left_key_event)
        self.master.bind("<Right>", self.right_key_event)
        self.master.bind("<Down>", self.down_key_event)

    def end_event(self):
        'Traitement en fin de partie'
        self.running = False

        #Arrêtez d'accepter des événements
        self.timer_end()
        self.master.unbind("<Left>")
        self.master.unbind("<Right>")
        self.master.unbind("<Down>")

    def timer_end(self):
        'Minuterie de fin'

        if self.timer is not None:
            self.master.after_cancel(self.timer)
            self.timer = None

    def timer_start(self):
        'Démarrer la minuterie'

        if self.timer is not None:
            #Annuler la minuterie une fois
            self.master.after_cancel(self.timer)

        #Le minuteur démarre uniquement lorsque Tetris est en cours d'exécution
        if self.running:
            #Démarrer la minuterie
            self.timer = self.master.after(1000, self.timer_event)

    def left_key_event(self, event):
        'Traitement lors de l'acceptation de l'entrée de la touche gauche'

        #Déplacez le bloc vers la gauche
        self.game.move_block(MOVE_LEFT)

    def right_key_event(self, event):
        'Traitement lors de l'acceptation de l'entrée de la touche droite'

        #Déplacez le bloc vers la droite
        self.game.move_block(MOVE_RIGHT)

    def down_key_event(self, event):
        'Traitement lors de l'acceptation de l'entrée de la touche inférieure'

        #Déplacer le bloc vers le bas
        self.game.move_block(MOVE_DOWN)

        #Redémarrez la minuterie d'automne
        self.timer_start()

    def timer_event(self):
        'Traitement à l'expiration du minuteur'

        #Effectue le même traitement que lors de l'acceptation de l'entrée de la touche bas
        self.down_key_event(None)


class Application(tk.Tk):
    def __init__(self):
        super().__init__()

        #Paramètres de la fenêtre de l'application
        self.geometry("400x600")
        self.title("Tetris")

        #Génération Tetris
        game = TetrisGame(self)

        #Génération de gestionnaire d'événements
        EventHandller(self, game)


def main():
    'fonction principale'

    #Génération d'applications GUI
    app = Application()
    app.mainloop()


if __name__ == "__main__":
    main()

finalement

C'est dommage que nous n'ayons pas pu reproduire la rotation de Tetrimino, qui est le vrai frisson de Tetris, mais je pense que c'est un jeu que tout le monde peut apprécier. J'ai créé un jeu en utilisant Python pour la première fois, et quand je l'ai fait, le sentiment d'accomplissement était merveilleux. Ensuite, nous nous vengerons pour pouvoir fabriquer un Tetris rotatif!

Site de référence

https://daeudaeu.com/programming/python/tkinter/tetris/

Recommended Posts

Créez un jeu à la Tetris!
Faisons un jeu de squash
Faisons un jeu de shiritori avec Python
Je veux faire un jeu avec Python
Créez un jeu cocos2d avec une fenêtre à double angle de pixels
Faire une matrice de distance
Créez un jeu Janken en une seule ligne (python)
Je vais créer un mot de passe!
Créer un bouton Nyan
J'ai créé un jeu ○ ✕ avec TensorFlow
Créer un décodeur Base64
Essayez de créer un jeu simple avec Python 3 et iPhone
Créer un backend Blueqat ~ Partie 1
Créer un backend Blueqat ~ Partie 2
[Django] Créer un menu déroulant
Créer un LINE BOT (chat)
Faites une loterie avec Python
Faire de Responder un démon (service)
Faire un feu avec kdeplot
Faire une impression de forage de calcul
Comment créer un jeu d'action multijoueur en ligne avec Slack
Comment faire un simple jeu Flappy Bird avec Pygame
Faisons une rumba distante [Matériel]
Comment faire une traduction japonais-anglais
Créer un identifiant Santa à partir d'une image du Père Noël
Créer une boîte à Tweet pour Pepper
Faisons une interface graphique avec python.
Faites un son avec le notebook Jupyter
Faisons un service de vente au comptant 2
Créer un outil de reconnaissance de visage à l'aide de TensorFlow
Faisons une rupture de bloc avec wxPython
Faisons un service de vente au comptant 1
Comment créer un robot - Avancé
Comment créer une fonction récursive
Rendre la compilation C un peu plus facile
python / Créer un dict à partir d'une liste.
[Python] Faire de la fonction une fonction lambda
Créer un système de recommandation avec python
[Blender] Comment créer un plug-in Blender
Créer un filtre avec un modèle django
Zura fait comme un jeu de la vie
Faisons un graphe avec python! !!
Faisons un spacon avec xCAT
Comment créer un robot - Basic
Créer un itérateur de modèle avec PySide
Faire un joli graphique avec plotly
Créez un générateur de rideaux avec Blender
Faisons un service de vente au comptant 3
Créer un lecteur vidéo avec PySimpleGUI + OpenCV
[Python] Comment rendre une classe itérable
Faisons un noyau jupyter
J'ai essayé de jouer au jeu ○ ✕ en utilisant TensorFlow
Créer un diagramme de relations des modules Python
Créez un simulateur de gacha rare avec Flask