Un jeu à part entière créé par moi dans une console Windows standard

salut!



Aujourd'hui, je vais décrire en détail comment j'ai créé le jeu sur la ligne de commande et à quel point il s'est avéré bon.



D'où vient l'idée?



J'ai été inspiré par l'idée de faire quelque chose de simple à première vue, mais en même temps intéressant en termes de développement. J'ai eu l'idée de faire un jeu dans la console, c'est intéressant en termes de développement, et ce sera intéressant de le regarder même de l'extérieur, comme ce jeu.



Moteur de jeu



Alors, commençons par comment le jeu est organisé à la racine, et quelle est son idée de travail.



Tout d'abord, j'ai décidé de la façon dont le monde du jeu serait affiché sur la console. J'ai réalisé que pour afficher les objets du jeu, nous avons besoin d'une liste qui stocke d'autres listes qui stockent des personnages, qui sont ensuite affichés sur le terrain de jeu en boucle for.



Avec ce code:



for line_words in OUTPUT_IMAGE:
       for word in line_words:
           print(word, end="")
       print("\n", end="")


Ici, nous dessinons tous les caractères de la liste et allons à une nouvelle ligne pour dessiner la liste de caractères suivante.



Voici à quoi ressemble la variable qui stocke les listes de symboles:



image



Ici, nous prenons immédiatement une décision sur la façon d'afficher les objets en X et Y, nous pouvons maintenant spécifier:



X - un symbole dans la liste

Y - une liste qui contient X

Ainsi, dessinez un symbole sur le champ ... Nous l'utiliserons pour dessiner des objets de jeu.



Nous pouvons essayer de dessiner une "boule" sur le terrain, en remplaçant la lettre "O" par X et Y.



Pour ce faire, écrivez le code suivant:



import os
OUTPUT_IMAGE = [
        [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
        [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
        [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
        [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
        [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
        [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
        [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
        [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
        [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
        [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
        [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
        ]

OUTPUT_IMAGE[4][6] = "O"
os.system("cls||clear")
for line_words in OUTPUT_IMAGE:
       for word in line_words:
           print(word, end="")
       print("\n", end="")



image



Et ainsi, nous avons dessiné un objet sur notre terrain de jeu. Certes, les coordonnées X et Y ne sont pas classiques. Premièrement, nous indiquons d'abord Y, puis X, ce qui n'est pas tout à fait conforme aux classiques, et deuxièmement, la coordonnée Y doit augmenter pour élever l'objet, dans notre cas, au contraire, elle doit diminuer.



Graphique de X et Y dans le jeu:



image



Cette fonctionnalité devra également être prise en compte ultérieurement lors de collisions d'objets dans la console.



Maintenant, nous pouvons essayer de déplacer notre objet sur le terrain de jeu, c'est-à-dire créer du mouvement.



Nous devrons vider la console pour effacer l'ancienne image du terrain de jeu.

Nous allons faire cela avec la commande:



os.system("cls||clear")


De plus, nous devons remplacer la variable OUTPUT_IMAGEafin d'effacer tous les objets précédemment dessinés dans le champ de jeu.



Nous devrons également intégrer tout cela while True.



Ajoutons à la while Truefonction time.sleep(1)afin de limiter le FPS.



Et ainsi, le code a été dessiné sous nos yeux:



from time import sleep
from os import system
OUTPUT_IMAGE = [
        [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
        [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
        [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
        [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
        [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
        [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
        [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
        [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
        [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
        [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
        [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
        ]

x = 0
y = 0
while True:
      sleep(1)
      system("cls||clear")
      OUTPUT_IMAGE[y][x] = "O"
      for line_words in OUTPUT_IMAGE:
             for word in line_words:
                 print(word, end="")
             print("\n", end="")
      y += 1
      x += 1
      OUTPUT_IMAGE = [
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            ]


image



Nous avons maintenant la possibilité de distribuer des objets sur le terrain.



Certes, ces objets sont trop primitifs, et il faudrait apprendre à dessiner des objets complexes comme des joueurs, des maisons, de la nourriture ...



Pour dessiner un objet complexe, il faut comprendre et comprendre comment dessiner un objet en ne spécifiant qu'une seule fois ses X et Y.



Pour cela, il nous faut une fonction qui accepte une image (symboles), X, Y;



Faisons cela:



def SetImage(image: str, x: int, y: int):
    pass


Nous devons maintenant le mettre en œuvre. Pour ce faire, vous devez décider comment dessiner une image qui s'étire le long des axes X et Y, j'ai trouvé ceci:

dessinez un objet en le divisant en symboles, et dès que le caractère "\ n" est rencontré, ajoutez l'axe



Y. L'axe Y, comme nous l'avons dit, est incorrect, inversé, nous y ajoutons donc pour abaisser l'objet.



Un exemple d'image dessinée selon mon principe:



image = " O\n'|'\n |"#


Décrivons maintenant cela dans notre fonction:



def SetImage(x: int, y: int, image: str):
  x_start = x
  x = x
  y = y
  for word in image:
      if word == "\n":
          x = x_start
          y += 1
      else:
          x += 1
          try:
            OUTPUT_IMAGE[y][x] = word
          except IndexError:
              break


Ajoutons try: except()pour éviter les erreurs si l'objet a X et Y trop petits ou trop grands.



x_startC'est le X, à partir duquel nous devons commencer à dessiner lors de l'augmentation de Y (avec le caractère "\ n")



Maintenant, nous pouvons utiliser notre fonction, y déposer X et Y, et l'image qui doit être dessinée:



le code
from time import sleep
from os import system
OUTPUT_IMAGE = [
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      ]

def SetImage(x: int, y: int, image: str):
  x_start = x
  x = x
  y = y
  for word in image:
      if word == "\n":
          x = x_start
          y += 1
      else:
          x += 1
          try:
            OUTPUT_IMAGE[y][x] = word
          except IndexError:
              break
while True:
      sleep(1)
      system("cls||clear")
      SetImage(x=3,y=4,image=" O\n'|'\n |")
      for line_words in OUTPUT_IMAGE:
             for word in line_words:
                 print(word, end="")
             print("\n", end="")
      OUTPUT_IMAGE = [
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            ]




Et voici ce que nous avons:



image



tout comme la balle que nous avons dessinée, elle peut être déplacée le long des axes X et Y.



le code
from time import sleep
from os import system
OUTPUT_IMAGE = [
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      ]
px = 0
py = 0
def SetImage(x: int, y: int, image: str):
  x_start = x
  x = x
  y = y
  for word in image:
      if word == "\n":
          x = x_start
          y += 1
      else:
          x += 1
          try:
            OUTPUT_IMAGE[y][x] = word
          except IndexError:
              break
while True:
      sleep(1)
      system("cls||clear")
      SetImage(x=px,y=py,image=" O\n'|'\n |")
      for line_words in OUTPUT_IMAGE:
             for word in line_words:
                 print(word, end="")
             print("\n", end="")
      px += 1
      py += 1
      OUTPUT_IMAGE = [
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            ]




image



Et maintenant, le joueur se déplace déjà sur la carte.



Ici on a déjà beaucoup fait, il y a déjà un joueur, il y a déjà une carte, et il semblerait qu'il soit déjà possible de faire un jeu, mais non. Nous avons besoin d'une fonction pour calculer les collisions d'objets, car de quel genre de jeu s'agit-il sans interactions d'objets? Alors, commençons.



Tout d'abord, nous devons créer une fonction pour obtenir la latitude et la hauteur d'un objet afin de calculer sa hitbox.



J'ai donc décidé de faire la fonction selon la logique suivante:



X - la hitbox de l'objet en largeur X, c'est le plus grand nombre de caractères entre les caractères "\ n" dans l'image

Y - la hitbox en Y est le nombre de caractères "\ n" dans l'image



Par cette logique ce n'est pas difficile créer une fonction qui prend une image, compte tous les caractères entre "\ n" pour elle, et sélectionne le plus grand nombre de caractères à partir de cela - la latitude est obtenue.

Et si vous comptez les caractères "\ n", comme je l'ai déjà écrit, vous obtenez la hauteur.



La fonction s'est avérée comme ceci:



def GetSizeObject(img: str):
    w = 0
    weights = []
    h = [word for word in img if word == "\n"]

    for word in img:
      if word == "\n":
          weights.append(w)
          w = 0
      else:
          w += 1
      try:
          return {"w": max(weights), "h":len(h)}
      except ValueError:
            return {"w": 0, "h":0}



Pourquoi ValueError sauf ici?
.



Alors dessinons notre lecteur et calculons sa largeur et sa longueur.



code avec dessin et calcul de la latitude et de la hauteur
from time import sleep
from os import system
OUTPUT_IMAGE = [
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      ]
px = 3
py = 3
def SetImage(x: int, y: int, image: str):
    global OUTPUT_IMAGE      
    x_start = x
    x = x
    y = y
    for word in image:
        if word == "\n":
            x = x_start
            y += 1
        else:
            x += 1
            try:
              OUTPUT_IMAGE[y][x] = word
            except IndexError:
                break

def GetSizeObject(img: str):
    w = 0
    weights = []
    h = [word for word in img if word == "\n"]
    h.append(1)

    for word in img:
        if word == "\n":
            weights.append(w)
            w = 0
        else:
            w += 1
    try:
        return {"w": max(weights), "h":len(h)}
    except ValueError:
        return {"w": 0, "h":0}

player_image = " O\n'|'\n |"
def draw():
      global OUTPUT_IMAGE
      sleep(1)
      system("cls||clear")
      for line_words in OUTPUT_IMAGE:
             for word in line_words:
                 print(word, end="")
             print("\n", end="")
      OUTPUT_IMAGE = [
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            ]
while True:
    SetImage(x=px,y=py,image=player_image)
    print(GetSizeObject(img=player_image))
    draw()





Hourra! nous avons une fonction pour calculer la latitude et la hauteur, maintenant nous devons créer une fonction pour calculer la hitbox et les collisions d'objets.



Rappelons-nous que notre système de coordonnées n'est pas classique, donc, hélas, nous ne pouvons pas utiliser la fonction classique, nous devrons créer le nôtre. Pour ce faire, j'ai dessiné 2 carrés sur le graphique qui entrent en collision, et à partir de cette image, vous pouvez trouver une condition par laquelle la collision sera calculée.



Pour faciliter la compréhension, j'ai dessiné des hitbox, c.-à-d. carrés:



image



La logique en mots




x — X

y — Y

h —

w —

x2 — X

y2 — Y

h2 —

w2 —



:





y y2 - h2 + h y - h y2 + h2 - h



y2 y - h + h2 y2 - h2 y + h - h2

2 ?
2 , - / .



Y



X, Y, yx, hw.



:



x x2 - w2 + w x - w x2 + w2 - w







x2 x - w + w2 x2 - w2 x + w - w2



X



Logique dans le code
, :



def IsClash(x: int, y: int, h: int, w: int,x2: int, y2: int, h2: int, w2: int):
    if (y >= y2 - h2 + h and y - h <= y2 + h2 - h) or (y2 >= y - h + h2 and y2 - h2 <= y + h - h2):
        if (x >= x2 - w2 + w and x - w <= x2 + w2 - w) or (x2 >= x - w + w2 and x2 - w2 <= x + w - w2):
            return True

    return False


True , False .



J'ai également dessiné un cube sur notre terrain de jeu pour que le joueur ait quelqu'un à affronter.



Et j'ai essayé le fonctionnement de la fonction de calcul de collision.



Voici un joueur touchant un cube:



image



mais pas de contact:



image



Code de contact
/ :



from time import sleep
from os import system
OUTPUT_IMAGE = [
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
      ]

def SetImage(x: int, y: int, image: str):
    global OUTPUT_IMAGE      
    x_start = x
    x = x
    y = y
    for word in image:
        if word == "\n":
            x = x_start
            y += 1
        else:
            x += 1
            try:
              OUTPUT_IMAGE[y][x] = word
            except IndexError:
                break

def GetSizeObject(img: str):
    w = 0
    weights = []
    h = [word for word in img if word == "\n"]
    h.append(1)

    for word in img:
        if word == "\n":
            weights.append(w)
            w = 0
        else:
            w += 1
    try:
        return {"w": max(weights), "h":len(h)}
    except ValueError:
        return {"w": 0, "h":0}

def IsClash(x: int, y: int, h: int, w: int,x2: int, y2: int, h2: int, w2: int):
    if (y >= y2 - h2 + h and y - h <= y2 + h2 - h) or (y2 >= y - h + h2 and y2 - h2 <= y + h - h2):
        if (x >= x2 - w2 + w and x - w <= x2 + w2 - w) or (x2 >= x - w + w2 and x2 - w2 <= x + w - w2):
            return True

    return False

player_image = " O\n'|'\n |"
cube_image = "____\n|  |\n----"
cx = 5#
cy = 4  #          
px = 10  #
py = 3#
def draw():
      global OUTPUT_IMAGE
      sleep(1)
      system("cls||clear")
      for line_words in OUTPUT_IMAGE:
             for word in line_words:
                 print(word, end="")
             print("\n", end="")
      OUTPUT_IMAGE = [
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            ]
while True:
    SetImage(x=px,y=py,image=player_image)
    SetImage(x=cx,y=cy,image=cube_image)
    print("is clash: ",IsClash(
      x=px,
      x2=cx,
      y=py,
      y2=cy,
      h=GetSizeObject(img=player_image)["h"],
      h2=GetSizeObject(img=cube_image)["h"],
      w=GetSizeObject(img=player_image)["w"],
      w2=GetSizeObject(img=cube_image)["w"],
      ))
    draw()





Maintenant, nous avons toutes les fonctions de départ pour le jeu, en fait, j'ai écrit mon jeu basé sur elles.



Un jeu



L'idée du jeu est la suivante:



il y a un joueur, de la nourriture apparaît autour de lui, qu'il est obligé de collecter pour ne pas mourir. Le jeu a aussi des fonctions: ramasser de la nourriture, la mettre dans l'inventaire, la manger de l'inventaire, mettre un objet de l'inventaire sur le



sol J'ai commencé par faire une boucle de jeu en 3 lignes, c'est simple While True:



from time import sleep
while True:
    sleep(0.1)


Ensuite, j'ai jugé nécessaire de créer une classe dans laquelle toutes les fonctions des futurs objets seront stockées. Par conséquent, j'ai créé un fichier main.py et un dossier lib, dans lesquels j'ai placé le fichier lib.py dans lequel se trouvait la classe de jeu. CEUX. les fichiers du jeu ressemblaient à ceci:



+----game
|    + -- 
|    | -- main.py
|    \ --lib
|         +--lib.py -> class Game()
|         \
|
+---


Dans le futur, j'ai principalement travaillé avec la classe Game (), dans main.py je l'ai simplement appelée, créé des objets de départ et lancé le jeu.



Dans la classe de jeu, j'ai créé une fonction run () qui démarre la boucle de jeu. Également créé la fonction draw_all (), il efface tous les objets passés, en dessine de nouveaux et imprime sur le terrain de jeu.



Et voici à quoi ressemblait la classe:



from time import sleep


class Game():
    def __init__(self):
        self.OUTPUT_IMAGE = []  #   

    def draw_all(self):
        for line_words in self.OUTPUT_IMAGE:
            for word in line_words:
                print(word, end="")
            print("\n", end="")

    def run(self):
        while True:
            self.draw_all()
            sleep(0.1)



J'ai ajouté toutes les fonctions de base du type set_image(), size_object(), is_clash()et tous ceux qui sont le moteur de jeu, et que je l' ai décrit ci - dessus.



Fait une nouvelle fonction create_object()et une variable self.OBJECTS, une fonction que create_object()j'utilise pour créer des objets, il faut les paramètres img, name, x, y, up, rigid, data.



img- image de l'objet -

namenom de l' objet (maison, herbe, habitant, nourriture, etc.)

x- objet X - objet

yY

up- si ce paramètre est vrai, alors l'objet est dessiné au-dessus du joueur, sinon le joueur le chevauche

rigid- dureté, le joueur ne peut pas passer par cet objet (pas encore mis en œuvre)

data- données personnelles de l'objet, ses caractéristiques personnelles



create_object ()
:



def CreateObject(self,x: int, y: int, img: str, name: str = None, up: bool = False, rigid: bool = False, data: dict = {}):
    size_object = self.GetSizeObject(img=img)
    self.OBJECTS.append(
        {"name": name,
         "x": x,
         "y": y,
         "up": up,
         "rigid": rigid,
         "h":size_object["h"],
         "w":size_object["w"],
         "id":uuid4().hex,
         "data":data,
         "img": img}
    )




A cette époque, j'ai déjà ajouté un joueur, une maison, de l'herbe et un villageois.



Et j'ai décidé d'utiliser le même paramètre dans l'objet up, de l'utiliser dans l'objet Home, c'est-à-dire de sorte que la maison couvre le joueur. Pour ce faire, j'ai créé la fonction CheckAll (), une boucle for a parcouru tous les objets et les ai dessinés sur l'image sortante, c'est-à-dire utilisez la fonction SetImage (x: int, y: int, img: str), en fournissant les X et Y de l'objet et une image.



Ainsi, il a dessiné des objets que le joueur pouvait fermer lui-même. Dans le même cycle, j'ai déclaré une liste up_of_payer_objects, et si l'objet avait up = True, alors je l'ai ajouté à la liste sans le dessiner sur le terrain. Après cela, j'ai dessiné le joueur lui-même, et alors seulement j'ai parcouru la boucle for sur les objets dans up_of_payer_objects, les dessinant, donc ils étaient au-dessus du joueur.



def CheckAll(self):
    up_of_payer_objects = []
    for object_now in range(len(self.OBJECTS)):
        if object_now["up"]:
            up_of_payer_objects.append(object_now)
            continue
        self.SetImage(x=object_now["x"],y=object_now["y"],image=object_now["img"])


Puis j'ai commencé à déplacer le lecteur. Pour ce faire, je l'ai créé en tant qu'objet séparé, qui n'est pas dans la liste self.OBJECTS, mais qui est stocké dans une variable self.PLAYER.



Tous ses paramètres en fonction du type X, Y, img, ITP Vous pouvez l'obtenir en utilisant des clés, en d'autres termes c'est un dictionnaire (dict). Avec un tel joueur et des objets, il était déjà possible de travailler, de se déplacer, de calculer les collisions. J'ai commencé par bouger.

J'ai commencé à créer du mouvement en exécutant la fonction CheckKeysObjects (), qui est responsable du suivi des frappes au clavier, et que j'appelle dans la fonction CheckAll () au tout début



def CheckAll(self):
    self.CheckKeysObjects()
    ....


Pour suivre les frappes, j'ai utilisé la bibliothèque de clavier et 4 variables: Et tout s'est avéré simple, nous suivons les touches, et si elle est enfoncée , nous créons une variable . Au tout début de la fonction, nous déclarons toutes les variables dans , afin de réinitialiser tous les résultats passés, sinon le lecteur ne s'arrêtera pas.



self.WALK_LEFT_PLAYER

self.WALK_RIGHT_PLAYER

self.WALK_UP_PLAYER

self.WALK_DOWN_PLAYER



dself.WALK_RIGHT_PLAYERTrue



False



CheckKeysObjects ()
def CheckKeysObjects(self):
    #    False,    
    self.WALK_LEFT_PLAYER = False
    self.WALK_RIGHT_PLAYER = False
    self.WALK_UP_PLAYER = False
    self.WALK_DOWN_PLAYER = False
    #    
    if keyboard.is_pressed("a"):
        self.WALK_LEFT_PLAYER = True
    elif keyboard.is_pressed("d"):
        self.WALK_RIGHT_PLAYER = True
    if keyboard.is_pressed("w"):
        self.WALK_UP_PLAYER = True
    elif keyboard.is_pressed("s"):
        self.WALK_DOWN_PLAYER = True




Après cela, dans la fonction, je CheckAll()vérifie toutes les variables responsables du mouvement , je découvre où se déplace le joueur.



S'il y en a un True, trouvez lequel et déplacez l'objet dans la direction opposée.



Le code de mouvement résultant
def CheckAll(self):
    self.CheckKeysObjects()  # check moves
    up_of_payer_objects = []
    for object_now in range(len(self.OBJECTS)):
        self.PLAYER["img"] = self.PLAYER["image_normal"]
        if self.WALK_LEFT_PLAYER:
            self.OBJECTS[object_now]["x"] += 1

        elif self.WALK_RIGHT_PLAYER:
            self.OBJECTS[object_now]["x"] -= 1


        if self.WALK_UP_PLAYER:

            self.OBJECTS[object_now]["y"] += 1
        elif self.WALK_DOWN_PLAYER:

            self.OBJECTS[object_now]["y"] -= 1




Oui, nous déplaçons des objets dans la direction opposée afin de créer l'illusion du mouvement. Si le joueur va vers la droite, tous les objets de l'environnement sont décalés vers la gauche.



Ensuite, j'ai ajouté plus d'objets environnementaux et j'ai commencé à engendrer de la nourriture, le but du joueur est de collecter de la nourriture pour ne pas mourir.



Pour le compte à rebours de la période d'apparition de la nourriture, j'ai utilisé une simple time.sleep()et une bibliothèque threading- afin d'exécuter 2 fonctions en même temps, la nourriture et la boucle de jeu principale. La fonction d'apparition de nourriture SpawnEat()est simplement une fonction qui, lorsqu'elle est lancée, génère de la nourriture à des endroits aléatoires, appelant une fonction pour chaque unité de nourriture CreateObject().



De plus, une fois que j'ai créé la fonction d'apparition de nourriture, j'ai créé une variable pour le joueurself.PLAYER["hungry"], c'est sa faim, au tout début elle est égale à 100 unités, je la diminuerai si le joueur marche et dépense de l'énergie (comme de l'énergie, ce n'est pas dans le jeu) ou l'augmentera si le joueur a mangé quelque chose.



J'ai aussi créé une fonction MinimizeHungry(), elle est appelée toutes les 5 secondes, et cela ne prend que 2 unités de faim du joueur. J'ai fait cela pour que le joueur doive bouger et ne pas rester immobile.



Enfin, dans une fonction Eat(), cette fonction est appelée sur un thread distinct de la boucle de jeu. Elle vérifie s'il y a trop de nourriture sur la carte, si la nourriture est supérieure à 10 unités. il n'appelle PAS la fonction SpawnEat()si moins de 10 unités. puis appelle SpawnEat().



Voici comment cela s'est avéré:



Manger ()
def Eat(self):
    while True:
        sleep(4)
        if len([i for i in self.OBJECTS if i["name"] == "meat"]) < 10:
            self.SpawnEat()
        sleep(1)
        self.MinimizeHungry()




Fonction Start()pour démarrer la boucle principale:



Début ()
def Start(self):
    while True:  
        self.CheckAll()
        self.DrawAll()
        sleep(0.01)




Et une fonction run()qui lance tout le jeu.



courir ()
def run(self):
    proc1 = threading.Thread(target=self.Start)
    proc1.start()
    proc2 = threading.Thread(target=self.Eat)
    proc2.start()




Le processus de manger lui-même, je l'ai implémenté simplement dans la fonction CheckAll()et CheckKeysObjects(). Q CheckKeysObjects()J'ai vérifié si le joueur avait appuyé sur le bouton E. Si elle est pressée, puis mettre la variable self.PRESS_Edans True.



Dans la boucle CheckAll(), j'ai vérifié si l'objet actuel dans la boucle était de la fornourriture, si la nourriture a ensuite vérifié si le joueur est entré en collision avec elle, si elle est entrée en collision, puis vérifié la variable self.PRESS_E, et si elle a Truesimplement supprimé l'objet et augmenté la faim, c'est-à-dire variable self.PLAYER["hungry"].



C'est comme ça dans le code
for object_now in range(len(self.OBJECTS)):
    ....
    if self.OBJECTS[object_now]["name"] == "meat":
        items_objects.append(object_now)
        is_clash = self.IsClash(
            x=self.OBJECTS[object_now]["x"],
            y=self.OBJECTS[object_now]["y"],
            h=self.OBJECTS[object_now]["h"],
            w=self.OBJECTS[object_now]["w"],
            x2=self.PLAYER["x"],
            y2=self.PLAYER["y"],
            h2=self.PLAYER["h"],
            w2=self.PLAYER["w"],
        )

        if is_clash:
            if self.PRESS_E:
                try:
                    self.PLAYER["hungry"] += self.HUNGRUY_ADD
                    del self.OBJECTS[object_now]
                    break

                except IndexError:
                    pass




Je dirai à l'avance, je devrai réécrire tout ça quand je ferai l'inventaire


Faire l'inventaire



Donc, c'est dur, il faut faire un inventaire.



La difficulté est que tous les objets devront être affichés, l'historique stocké, supprimés, placés des objets sur le sol.



J'ai commencé par ajouter une nouvelle clé au lecteur, c'était self.PLAYER["inventory"], 4 cellules y sont stockées, comme ceci:



"inventory":{
    "0":{"status":"space","name":"#0", "minimize_image":"#0"},
    "1":{"status":"space","name":"#1", "minimize_image":"#1"},
    "2":{"status":"space","name":"#2", "minimize_image":"#2"},
    "3":{"status":"space","name":"#3", "minimize_image":"#3"},
}


Ne sont que des numéros de cellule.



status- cette clé stocke en elle-même la valeur, l'ovule est vide ou non. S'il est vide, alors "espace", s'il y a un élément, le nom de l'élément y est stocké.



name- stocke le nom de l'objet, il sera utilisé lorsque le joueur met l'objet.



minimize_image- ceci est une petite image de l'objet qui est affiché dans l'inventaire du joueur.



Après, j'ai fait de nouveaux contrôles dans le nôtre CheckKeysObjects(), lorsque vous cliquerez sur l' Xélément, il se jettera au sol, et lorsque vous cliquerez sur le bouton, la Efonction sera appelée self.UseEat(), que nous allons maintenant analyser.



Donc la fonctionself.UseEat()est un passage à travers toutes les cellules de l'inventaire, à la recherche de nourriture, et si de la nourriture est trouvée, alors elle est retirée de l'inventaire, et 10 unités sont ajoutées à la faim. Pour supprimer un élément de l'inventaire, j'ai créé une fonction self.DestroyItem()dans laquelle l'index de cellule est fourni, et la cellule entière devient simplement vide par défaut et sans rien.



self.DestroyItem ()
def DestroyItem(self,index_item: str):
    item = self.PLAYER["inventory"][index_item]
    self.PLAYER["inventory"][index_item] = self.PLAYER["default_inventory_item"](index_item)
    self.PLAYER["inventory_must_update"] = True
    return item




self.CheckKeysObjects ()
def CheckKeysObjects(self):
    self.WALK_LEFT_PLAYER = False
    self.WALK_RIGHT_PLAYER = False
    self.WALK_UP_PLAYER = False
    self.WALK_DOWN_PLAYER = False
    if key("a"):
        self.WALK_LEFT_PLAYER = True
    elif key("d"):
        self.WALK_RIGHT_PLAYER = True
    if key("w"):
        self.WALK_UP_PLAYER = True
    elif key("s"):
        self.WALK_DOWN_PLAYER = True
    if key("f"):
        self.KEY_F = True
    else:
        self.KEY_F= False
    if key("e"):
        self.UseEat()




self.UseEat ()
def UseEat(self):
    for inventory_item in range(len(self.PLAYER["inventory"])):
        if self.PLAYER["inventory"][str(inventory_item)]["name"] == "meat":
            if self.PLAYER["hungry"] + self.ADD_HUNGRY_COUNT < 100.0:
                self.PLAYER["hungry"] += self.ADD_HUNGRY_COUNT
                self.DestroyItem(index_item=str(inventory_item))




Vient ensuite la fonction de jeter un objet au sol.



Il n'y a, cependant, rien de compliqué, lorsque vous cliquez sur la Xfonction est appelée self.QuitItem(), la boucle for parcourt toutes les cellules de l'inventaire, et si la clé n'est ["status"]pas égale "space", alors nous supprimons cette cellule en utilisant la fonction précédemment considérée self.DestroyItem(), et créons un objet basé sur ce qui était dans la cellule, X et Y met le joueur comme s'il l'avait jeté à côté de lui.



self.Quititem ()
def QuitItem(self):
    for inventory_item in range(len(self.PLAYER["inventory"])):
        if self.PLAYER["inventory"][str(inventory_item)]["status"] != "space":
            self.CreateObject(
                img=self.PLAYER["inventory"][str(inventory_item)]["img"],
                x=self.PLAYER["x"],
                y=self.PLAYER["y"],
                name=self.PLAYER["inventory"][str(inventory_item)]["name"],
                data=self.PLAYER["inventory"][str(inventory_item)]["data"],
            )
            self.DestroyItem(index_item=str(inventory_item))
            break




Et pourtant tout, beaucoup de choses, je n'ai pas dit comment je l'ai fait, T.K. ils n'étaient pas la partie principale du jeu, bien qu'intéressants. Par exemple, des messages sur la possibilité de ramasser un objet ou non (lorsque l'inventaire est plein), que j'ai ajouté une animation de marche, que j'ai fait une bibliothèque séparée d'images, et d'autres choses.



C'est tout?



Non, je vais ajouter un réseau de neurones au jeu, en utilisant une bibliothèque que j'ai écrite en Python,

je vais faire l'interaction du joueur avec des PNJ équipés d'un réseau de neurones, un

petit, mais une sorte d'intrigue, et aussi des fournitures pour le joueur, telles que l'armure, la nourriture. objets, la possibilité de construire des blocs.



Essayez le jeu



Il peut être téléchargé gratuitement depuis mon GitHub, vous n'avez besoin que de Python3 pour fonctionner et de la bibliothèque de clavier . Vous devez exécuter le fichier main.py.



Un jeu



All Articles