Un juego completo hecho por mí en una consola de Windows normal

¡Hola!



Hoy describiré en detalle cómo hice el juego en la línea de comandos y lo bueno que resultó.



¿De donde viene la idea?



Me inspiró la idea de hacer algo simple a primera vista, pero al mismo tiempo interesante en términos de desarrollo. Se me ocurrió la idea de hacer un juego en la consola, es interesante en términos de desarrollo, y será interesante verlo incluso desde fuera, como este juego.



Motor de juegos



Entonces, comencemos con cómo está organizado el juego en la raíz y cuál es su idea de trabajo.



Primero, decidí cómo se mostraría el mundo del juego en la consola. Me di cuenta de que para mostrar los objetos del juego, necesitamos una lista que almacene otras listas que almacenan personajes, que luego se muestran en el campo de juego en un bucle for.



Con este código:



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


Aquí dibujamos todos los caracteres de la lista y vamos a una nueva línea para dibujar la siguiente lista de caracteres.



Así es como se ve la variable que almacena las listas de símbolos:



imagen



Aquí inmediatamente tomamos una decisión sobre cómo mostrar los objetos en X e Y, ahora podemos especificar:



X - un símbolo en la lista

Y - una lista que contiene X

Por lo tanto, dibuje algún símbolo en el campo ... Usaremos esto al dibujar objetos del juego.



Podemos intentar dibujar una "bola" en el campo, sustituyendo la letra "O" por X e Y.



Para hacer esto, escriba el siguiente código:



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="")



imagen



Y así, hemos dibujado un objeto en nuestro campo de juego. Es cierto que las coordenadas X e Y no son clásicas. En primer lugar, indicamos primero Y, luego X, que no está del todo acorde con los clásicos, y en segundo lugar, la coordenada Y debe aumentar para subir el objeto, en nuestro caso, por el contrario, debe disminuir.



Gráfico de X e Y en el juego:



imagen



Esta característica también habrá que tenerla en cuenta más adelante cuando hagamos colisiones de objetos en la consola.



Ahora podemos intentar mover nuestro objeto por el campo de juego, es decir, crear movimiento.



Tendremos que limpiar la consola para borrar la imagen antigua del campo de juego.

Lo haremos con el comando:



os.system("cls||clear")


Además, necesitamos anular la variable OUTPUT_IMAGEpara borrar todos los objetos dibujados previamente en el campo de juego.



También necesitaremos incluir todo esto while True.



Agreguemos a la while Truefunción time.sleep(1)para limitar el FPS.



Y así, el código se dibujó ante nuestros ojos:



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 = [
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            ]


imagen



Ahora tenemos la capacidad de distribuir objetos por el campo.



Es cierto que estos objetos son demasiado primitivos y tendríamos que aprender a dibujar objetos complejos como jugadores, casas, comida ...



Para dibujar un objeto complejo, necesitamos entender y descubrir cómo dibujar un objeto especificando solo una vez su X e Y.



Para esto necesitamos una función que acepta una imagen (símbolos), X, Y;



Hagámoslo:



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


Ahora tenemos que implementarlo. Para hacer esto, debes decidir cómo dibujar una imagen que se extienda a lo largo de los ejes X e Y, se me ocurrió esto:

dibuja un objeto dividiéndolo en símbolos, y tan pronto como se encuentre el carácter "\ n", agrega el eje



Y. El eje Y, como dijimos, es incorrecto, invertido, por lo que le agregamos para bajar el objeto.



Un ejemplo de una imagen que se dibuja según mi principio:



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


Ahora describamos esto en nuestra función:



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


Agreguemos try: except()para evitar errores si el objeto tiene X e Y demasiado pequeños o demasiado grandes.



x_startEsta es la X, a partir de la cual debemos comenzar a dibujar al aumentar Y (con el carácter "\ n").



Ahora podemos usar nuestra función, colocar X e Y en ella, y la imagen que se debe dibujar:



el código
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 = [
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            ]




Y esto es lo que obtuvimos:



imagen



al igual que la bola que dibujamos, se puede mover a lo largo de los ejes X e Y.



el código
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 = [
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            [".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
            ]




imagen



Y ahora, el jugador ya se está moviendo por el mapa.



Aquí ya hemos hecho mucho, ya hay un jugador, ya hay un mapa, y parece que ya se puede hacer un juego, pero no. Necesitamos una función para calcular las colisiones de objetos, porque ¿qué tipo de juego es sin interacciones de objetos? Entonces empecemos.



Primero, necesitamos crear una función para obtener la latitud y la altura de un objeto para calcular su hitbox.



Entonces, decidí hacer la función de acuerdo con la siguiente lógica:



X - el hitbox del objeto en ancho X, este es el mayor número de caracteres entre los caracteres "\ n" en la imagen

Y - el hitbox en Y es el número de caracteres "\ n" en la imagen



Por esta lógica no es difícil haga una función que tome una fotografía, cuente todos los caracteres entre "\ n" y seleccione la mayor cantidad de caracteres de esta; se obtiene la latitud.

Y si cuentas los caracteres "\ n", como ya escribí, obtienes la altura.



La función resultó así:



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}



¿Por qué ValueError excepto aquí?
.



Así que dibujemos nuestro jugador y calculemos su ancho y largo.



código con dibujo y cálculo de latitud y altura
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()





¡Hurra! tenemos una función para calcular la latitud y la altura, ahora tenemos que hacer una función para calcular el hitbox y las colisiones de objetos.



Recordemos que nuestro sistema de coordenadas no es clásico, por lo que, lamentablemente, no podemos usar la función clásica, tendremos que hacer la nuestra. Para hacer esto, dibujé 2 cuadrados en el gráfico que colisionan, y a partir de esta imagen puedes llegar a una condición por la cual se calculará la colisión.



Para facilitar la comprensión, dibujé casillas de impacto, es decir cuadrícula:



imagen



Lógica en palabras




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



Lógica en código
, :



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 .



Además, dibujé un cubo en nuestro campo de juego para que el jugador tenga a quién enfrentar.



Y probé cómo funciona la función de cálculo de colisiones.



Aquí hay un jugador tocando un cubo:



imagen



pero sin tocar:



imagen



Código de contacto
/ :



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()





Ahora tenemos todas las funciones iniciales del juego, de hecho, escribí mi juego basándome en ellas.



Un juego



La idea del juego es la siguiente:



hay un jugador, aparece comida a su alrededor, que se ve obligado a recoger para no morir. El juego también tiene funciones: recoger comida, ponerla en el inventario, comerla del inventario, poner un artículo del inventario en el



suelo. Empecé haciendo un bucle de juego en 3 líneas, es simple While True:



from time import sleep
while True:
    sleep(0.1)


Luego consideré necesario crear una clase en la que se almacenarán todas las funciones de los objetos futuros. Por lo tanto, creé un archivo main.py y una carpeta lib, en la que coloqué el archivo lib.py en el que estaba la clase del juego. AQUELLOS. los archivos del juego se veían así:



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


En el futuro, trabajé principalmente con la clase Game (), en main.py simplemente lo llamé, creé objetos iniciales, comencé el juego.



En la clase de juego, hice una función run () que inicia el ciclo del juego. También hizo la función draw_all (), borra todos los objetos pasados, dibuja otros nuevos e imprime en el campo de juego.



Y así es como se veía la clase:



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)



He añadido todas las funciones básicas del tipo set_image(), size_object(), is_clash()y todos aquellos que son motor de juego, y que he descrito anteriormente.



Hecho una nueva función create_object()y una variable self.OBJECTS, una función create_object()que utilizo para crear objetos, que toma los parámetros img, name, x, y, up, rigid, data.



img- imagen del objeto -

namenombre del objeto (casa, césped, habitante, comida, etc.)

x- objeto X - objeto

yY

up- si este parámetro es Verdadero, entonces el objeto se dibuja sobre el jugador; de lo contrario, el jugador lo superpone

rigid- dureza, el jugador no puede pasar por este objeto (aún no implementado)

data- datos personales del objeto, sus características personales



Crear objeto ()
:



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}
    )




En ese momento, ya agregué un jugador, una casa, césped y un aldeano.



Y decidí usar el mismo parámetro en el objeto up, usarlo en el objeto Home, es decir, para que la casa cubra al jugador. Para hacer esto, hice la función CheckAll (), un bucle for recorrió todos los objetos y los dibujé en la imagen saliente, es decir use la función SetImage (x: int, y: int, img: str), proporcionando la X e Y del objeto y una imagen.



Por lo tanto, dibujó objetos que el jugador podría cerrar. En el mismo ciclo, declaré una lista up_of_payer_objects, y si el objeto tenía up = True, lo agregué a la lista sin dibujarlo en el campo. Después de eso, dibujé al jugador él mismo, y solo entonces pasé por el bucle for sobre los objetos en up_of_payer_objects, dibujándolos, por lo que estaban por encima del jugador.



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"])


Luego comencé a mover el reproductor. Para esto, lo creé como un objeto separado, que no está en la lista self.OBJECTS, pero que está almacenado en una variable self.PLAYER.



Todos los parámetros de acuerdo con el tipo X, Y, img, ITP Puedes conseguirlo usando claves, es decir, es un diccionario (dict). Con un jugador y objetos así, ya era posible trabajar, moverse, calcular colisiones. Empecé moviéndome.

Comencé a crear movimiento haciendo la función CheckKeysObjects (), que es responsable de rastrear las pulsaciones de teclas, y que llamo en la función CheckAll () desde el principio



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


Para rastrear las pulsaciones de teclas, usé la biblioteca del teclado y 4 variables: Y todo resultó ser simple, rastreamos las teclas, y si se presiona , hacemos una variable . Al comienzo de la función, declaramos todas las variables para restablecer todos los resultados anteriores, de lo contrario, el reproductor no se detendrá.



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




Después de eso, en la función, CheckAll()verifico todas las variables responsables del movimiento, averiguo hacia dónde se mueve el jugador.



Si hay alguno dentro True, averigüe cuál y mueva el objeto en la dirección opuesta.



El código de movimiento resultante
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




Sí, movemos objetos en la dirección opuesta para crear la ilusión de movimiento. Si el jugador va hacia la derecha, todos los objetos del entorno se desplazan hacia la izquierda.



Luego agregué más elementos ambientales y comencé a generar comida, el objetivo del jugador es recolectar comida para no morir.



Para la cuenta regresiva del tiempo de generación de alimentos, utilicé time.sleep()uno simple y una biblioteca threading, para ejecutar 2 funciones al mismo tiempo, la generación de alimentos y el ciclo principal del juego. La función de SpawnEat()generación de alimentos es solo una función que, cuando se inicia, genera alimentos en lugares aleatorios, llamando a una función para cada unidad de alimento CreateObject().



Además, una vez que hice la función de generación de alimentos, hice que el jugador fuera variableself.PLAYER["hungry"], esta es su hambre, al principio es igual a 100 unidades, la disminuiré si el jugador camina y gasta energía (como energía, no está en el juego) o la aumentaré si el jugador comió algo.



También hice una función MinimizeHungry(), se llama cada 5 segundos y solo toma 2 unidades de hambre del jugador. Hice esto para que el jugador tuviera que moverse y no quedarse quieto.



Finalmente, en una función Eat(), esta función se llama en un hilo separado del bucle del juego. Comprueba si hay demasiada comida en el mapa si hay más de 10 unidades de comida. NO llama a la función SpawnEat()si tiene menos de 10 unidades. luego llama SpawnEat().



Así es como resultó:



Comer ()
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()




Función Start()para iniciar el bucle principal:



Comienzo ()
def Start(self):
    while True:  
        self.CheckAll()
        self.DrawAll()
        sleep(0.01)




Y una función run()que lanza todo el juego.



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




El proceso de comer en sí, lo implementé simplemente en la función CheckAll()y CheckKeysObjects(). P CheckKeysObjects()Revisé para ver si el jugador presionó el botón E. Si presiona, coloque la variable self.PRESS_Een True.



En el bucle CheckAll(), verifiqué si el objeto actual en el bucle era forcomida, si la comida no verificó si el jugador chocó con él, si chocó, luego verifiqué la variable self.PRESS_E, y si Trueluego simplemente eliminó el objeto y aumentó el hambre, es decir, variable self.PLAYER["hungry"].



Así es en el código
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




Diré de antemano que tendré que reescribir todo esto cuando haga el inventario.


Haciendo inventario



Entonces, es difícil, necesitamos hacer un inventario.



La dificultad es que todos los objetos deberán mostrarse, almacenarse el historial, eliminarse y colocarse objetos en el suelo.



Comencé agregando una nueva clave al reproductor, era self.PLAYER["inventory"], 4 celdas se almacenan allí, así:



"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"},
}


Son solo números de celda.



status- esta clave almacena el valor si el óvulo está vacío o no. Si está vacío, entonces "espacio", si hay un elemento, entonces el nombre del elemento se almacena allí.



name- almacena el nombre del elemento, se utilizará cuando el jugador ponga el elemento.



minimize_image- esta es una pequeña imagen del elemento que se muestra en el inventario del jugador.



Luego, hice nuevas comprobaciones en la nuestra CheckKeysObjects(), al hacer clic en el Xítem se tira al suelo, y al hacer clic en el botón Ese llamará a la función self.UseEat(), que ahora analizaremos.



Entonces la funciónself.UseEat()Es un paso por todas las celdas del inventario, en busca de comida, y si se encuentra comida, se retira del inventario, y se suman 10 unidades al hambre. Para eliminar un artículo del inventario, hice una función self.DestroyItem()en la que se proporciona el índice de celda, y toda la celda simplemente se vacía de forma predeterminada y sin nada.



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))




Luego está la función de arrojar un objeto al suelo.



Sin embargo, no hay nada complicado, al hacer clic en Xse llama a la función self.QuitItem(), el bucle for recorre todas las celdas del inventario, y si la clave ["status"]no es igual "space", entonces borramos esta celda usando la función considerada anteriormente self.DestroyItem(), y creamos un objeto en base a lo que había en la celda, X y Y pone al jugador como si lo hubiera arrojado a su lado.



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




Y sin embargo, todas, muchas cosas que no dije como lo hice, T.K. no eran la parte principal del juego, aunque interesantes. Por ejemplo, mensajes sobre la posibilidad de recoger un artículo o no (cuando el inventario está lleno), que agregué una animación andante, que hice una biblioteca separada de imágenes y otras cosas.



¿Eso es todo?



No, voy a agregar una red neuronal al juego, usando una biblioteca que escribí en Python,

voy a hacer la interacción del jugador con los NPC equipados con una red neuronal, una

trama pequeña, pero de algún tipo, y también algunos suministros para el jugador, como armaduras, comida. elementos, la capacidad de construir en bloques.



Prueba el juego



Se puede descargar libremente desde mi GitHub, solo necesita Python3 para ejecutarse y la biblioteca de teclado . Necesita ejecutar el archivo main.py.



Un juego



All Articles