Um jogo completo feito por mim em um console normal do Windows

Olá!



Hoje vou descrever em detalhes como fiz o jogo na linha de comando e como ele ficou bom.



De onde vem a ideia?



Fui inspirado pela ideia de fazer algo simples à primeira vista, mas ao mesmo tempo interessante em termos de desenvolvimento. Tive a ideia de fazer um jogo para a consola, é interessante em termos de desenvolvimento, e vai ser interessante olhar para ele mesmo de fora, como este jogo.



Motor do jogo



Então, vamos começar com como o jogo está estruturado na raiz, e qual é a sua ideia de funcionamento.



Primeiro, decidi como o mundo do jogo seria exibido no console. Percebi que, para exibir objetos do jogo, precisamos de uma lista que armazene outras listas que armazenam personagens, que são subsequentemente exibidos no campo de jogo em um loop for.



Com este código:



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


Aqui, retiramos todos os caracteres da lista e vamos para uma nova linha para desenhar a próxima lista de caracteres.



É assim que a variável que armazena as listas de símbolos se parece:



imagem



Aqui nós imediatamente tomamos uma decisão sobre como exibir objetos em X e Y, podemos agora especificar:



X - um símbolo na lista

Y - uma lista que contém X

Assim, desenhe algum símbolo no campo ... Usaremos isso ao desenhar objetos do jogo.



Podemos tentar desenhar uma "bola" no campo, substituindo a letra "O" por X e Y.



Para fazer isso, escreva o seguinte 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="")



imagem



E assim, desenhamos um objeto em nosso campo de jogo. É verdade que as coordenadas X e Y não são clássicas. Em primeiro lugar, indicamos Y, depois X, o que não condiz com os clássicos e, em segundo lugar, a coordenada Y deve aumentar para elevar o objeto, no nosso caso, ao contrário, deve diminuir.



Gráfico de X e Y no jogo:



imagem



Esse recurso também terá que ser levado em consideração posteriormente quando fizermos colisões de objetos no console.



Agora podemos tentar mover nosso objeto pelo campo de jogo, ou seja, criar movimento.



Precisamos limpar o console para apagar a imagem antiga do campo de jogo.

Faremos isso com o comando:



os.system("cls||clear")


Além disso, precisamos substituir a variável OUTPUT_IMAGEpara limpar todos os objetos desenhados anteriormente no campo de jogo.



Também precisaremos colocar tudo isso while True.



Vamos adicionar à while Truefunção time.sleep(1)para limitar o FPS.



E assim, o código foi desenhado diante de nossos olhos:



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


imagem



Agora temos a capacidade de distribuir objetos pelo campo.



Verdade, esses objetos são muito primitivos, e teríamos que aprender a desenhar objetos complexos como jogadores, casas, comida ...



Para desenhar um objeto complexo, precisamos entender e descobrir como desenhar um objeto especificando apenas uma vez seu X e Y.



Para isso, precisamos uma função que aceita uma imagem (símbolos), X, Y;



Vamos fazer isso:



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


Agora precisamos implementá-lo. Para fazer isso, você precisa decidir como desenhar uma imagem que se estenda ao longo dos eixos X e Y, eu vim com isso:

desenhe um objeto dividindo-o em símbolos e, assim que o caractere "\ n" for encontrado, adicione o eixo



Y. O eixo Y, como dissemos, está incorreto, invertido, então nós adicionamos a ele para abaixar o objeto.



Um exemplo de imagem desenhada de acordo com o meu princípio:



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


Agora vamos descrever isso em nossa função:



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


Vamos adicionar try: except()para evitar erros se o objeto tiver X e Y muito pequeno ou muito grande.



x_startEste é o X, a partir do qual precisamos começar a desenhar ao aumentar Y (com o caractere "\ n").



Agora podemos usar nossa função, colocar X e Y nele, e a imagem que precisa ser desenhada:



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




E é isso que obtivemos:



imagem



assim como a bola que desenhamos, ela pode ser movida ao longo dos eixos X e Y.



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




imagem



E agora, o jogador já está se movendo pelo mapa.



Aqui já fizemos muito, já existe um jogador, já existe um mapa, e parece que já é possível fazer um jogo, mas não. Precisamos de uma função para calcular colisões de objetos, porque que tipo de jogo é sem interações de objetos? Então vamos começar.



Primeiro, precisamos fazer uma função para obter a latitude e a altura de um objeto para calcular seu hitbox.



Então, decidi fazer a função de acordo com a seguinte lógica:



X - o hitbox do objeto em largura X, este é o maior número de caracteres entre os caracteres "\ n" na imagem

Y - o hitbox em Y é o número de caracteres "\ n" na imagem



Por essa lógica não é difícil faz uma função que tira uma foto, conta todos os caracteres entre "\ n" para ela e seleciona o maior número de caracteres desta - a latitude é obtida.

E se você contar os caracteres "\ n", como eu já escrevi, você obtém a altura.



A função resultou assim:



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 que ValueError está exceto aqui?
.



Então, vamos desenhar nosso jogador e calcular sua largura e comprimento.



código com desenho e cálculo de latitude e 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()





Hooray! temos uma função para calcular latitude e altura, agora temos que fazer uma função para calcular o hitbox e colisões de objetos.



Vamos lembrar que nosso sistema de coordenadas não é clássico, então, infelizmente, não podemos usar a função clássica, teremos que fazer a nossa própria. Para fazer isso, desenhei 2 quadrados no gráfico que colidem, e a partir desta imagem você pode chegar a uma condição pela qual a colisão será calculada.



Para facilitar a compreensão, desenhei hitboxes, ou seja, quadrados:



imagem



Lógica em palavras




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 no 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 .



Além disso, desenhei um cubo em nosso campo de jogo para que o jogador tenha alguém para enfrentar.



E eu tentei como funciona a função de cálculo de colisão.



Aqui está um jogador tocando um cubo:



imagem



Mas sem tocar:



imagem



Código de contato
/ :



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





Agora que temos todas as funções iniciais do jogo, de fato, escrevi meu jogo baseado nelas.



Um jogo



A ideia do jogo é a seguinte:



há um jogador, a comida aparece ao seu redor, que ele é forçado a recolher para não morrer. O jogo também tem funções: pegar comida, colocar em seu inventário, comê-la de seu inventário, colocar um item de seu inventário no



chão. Comecei fazendo um loop de jogo em 3 linhas, é simples While True:



from time import sleep
while True:
    sleep(0.1)


Então considerei necessário criar uma classe na qual todas as funções dos objetos futuros serão armazenadas. Portanto, criei um arquivo main.py e uma pasta lib, na qual coloquei o arquivo lib.py que contém a classe do jogo. ESSA. os arquivos do jogo eram assim:



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


No futuro, trabalhei principalmente com a classe Game (), em main.py eu simplesmente a chamei, criei objetos iniciais e iniciei o jogo.



Na aula de jogo, criei uma função run () que inicia o loop do jogo. Também fez a função draw_all (), ela apaga todos os objetos anteriores, desenha novos e imprime no campo de jogo.



E é assim que a classe parecia:



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)



Eu adicionei todas as funções básicas do tipo set_image(), size_object(), is_clash(), e todos aqueles que são o motor de jogo, e que eu descrevi acima.



Fez uma nova função create_object()e uma variável self.OBJECTS, uma função create_object()que eu uso para criar objetos, que leva os parâmetros img, name, x, y, up, rigid, data.



img- imagem do objeto -

namenome do objeto (casa, grama, habitante, comida, etc.)

x- objeto X - objeto

yY

up- se este parâmetro for True, então o objeto é desenhado sobre o jogador, caso contrário, o jogador o sobrepõe

rigid- dureza, o jogador não pode percorrer este objeto (ainda não implementado)

data- dados pessoais do objeto, suas características pessoais



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




Naquela época, já adicionei um jogador, uma casa, grama e um morador.



E decidi usar o mesmo parâmetro no objeto up, usá-lo no objeto Home, ou seja, para que a casa cubra o jogador. Para fazer isso, criei a função CheckAll (), um loop for percorreu todos os objetos e os desenhei na imagem de saída, ou seja, use a função SetImage (x: int, y: int, img: str), fornecendo o X e Y do objeto, e uma imagem.



Assim, ele desenhou objetos que o próprio jogador poderia fechar. No mesmo ciclo, declarei uma lista up_of_payer_objects, e se o objeto tivesse até = True, adicionei-o à lista sem desenhá-lo no campo. Depois disso, desenhei o próprio jogador, e só então passei pelo loop for sobre os objetos em up_of_payer_objects, desenhando-os, portanto, eles estavam acima do jogador.



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


Então comecei a mover o jogador. Para isso, criei-o como um objeto separado, que não está na lista self.OBJECTS, mas que fica armazenado em uma variável self.PLAYER.



Todos os seus parâmetros de acordo com o tipo X, Y, img, itp Você pode obtê-lo usando as teclas, ou seja, é um dicionário (dict). Com tal jogador e objetos já era possível trabalhar, mover, calcular colisões. Comecei me movendo.

Comecei a criar movimento fazendo a função CheckKeysObjects (), que é responsável por rastrear pressionamentos de tecla e que chamo na função CheckAll () logo no início



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


Para controlar as teclas digitadas, usei a biblioteca de teclado e 4 variáveis: E tudo acabou por ser simples, rastreamos as teclas e, se for premido , criamos uma variável . No início da função, declaramos todas as variáveis ​​em , a fim de redefinir todos os resultados anteriores, caso contrário, o player não irá parar.



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




Depois, na função, CheckAll()verifico todas as variáveis ​​responsáveis ​​pelo movimento, descubro para onde o jogador está se movendo.



Se houver algum True, descubra qual deles e mova o objeto na direção oposta.



O código de movimento 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




Sim, movemos objetos na direção oposta para criar a ilusão de movimento. Se o jogador for para a direita, todos os objetos do ambiente serão deslocados para a esquerda.



Então eu adicionei mais itens ambientais e comecei a desovar comida, o objetivo do jogador é coletar comida para não morrer.



Para a contagem regressiva do tempo de desova de comida, usei um simples time.sleep()e uma biblioteca threading- para executar 2 funções ao mesmo tempo, desova de comida e o loop principal do jogo. A função de geração de alimento SpawnEat()é apenas uma função que, quando lançada, gera alimento em locais aleatórios, chamando uma função para cada unidade de alimento CreateObject().



Além disso, depois de fazer a função de geração de comida, fiz uma variável para o jogadorself.PLAYER["hungry"], essa é a fome dele, logo no início é igual a 100 unidades, vou diminuir se o jogador andar e gastar energia (como energia, não está no jogo) ou aumentar se o jogador comer alguma coisa.



Também fiz uma função MinimizeHungry(), ela é chamada a cada 5 segundos, e só tira 2 unidades de fome do jogador. Eu fiz isso para que o jogador tivesse que se mover, e não ficar parado.



Finalmente, em uma função Eat(), essa função é chamada em um thread separado do loop do jogo. Ela verifica se há comida demais no mapa, se a comida tem mais de 10 unidades. ele NÃO chama a função SpawnEat()se menos de 10 unidades. então liga SpawnEat().



Foi assim que aconteceu:



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




Função Start()para iniciar o loop principal:



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




E uma função run()que inicia todo o jogo.



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




O próprio processo de comer, implementei simplesmente na função CheckAll()e CheckKeysObjects(). Q CheckKeysObjects()Verifiquei se o jogador pressionou o botão E. Se pressionado, coloque a variável self.PRESS_Eem True.



No loop CheckAll(), eu verifiquei se o objeto atual no loop era forcomida, se a comida então verifiquei se o jogador colidiu com ela, se colidiu, então verifiquei a variável self.PRESS_E, e se Trueentão simplesmente apagou o objeto e aumentou a fome, ou seja, variável self.PLAYER["hungry"].



É assim que está no 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




Direi com antecedência, vou precisar reescrever tudo isso quando fizer o inventário


Fazendo inventário



Então, é difícil, precisamos fazer um inventário.



A dificuldade é que todos os objetos precisarão ser exibidos, históricos armazenados, excluídos, objetos colocados no chão.



Comecei adicionando uma nova chave ao player, era self.PLAYER["inventory"], 4 células são armazenadas lá, assim:



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


São apenas números de células.



status- esta chave armazena o valor se a célula-ovo está vazia ou não. Se estiver vazio, "espaço", se houver um item, o nome do item será armazenado lá.



name- armazena o nome do item, ele será usado quando o jogador colocar o item.



minimize_image- esta é uma pequena imagem do item que é exibido no inventário do jogador.



Depois, fiz novas verificações na nossa CheckKeysObjects(), ao clicar no Xitem, ele irá jogá-lo no chão, e também ao clicar no botão Eserá chamada a função self.UseEat(), que agora analisaremos.



Então a funçãoself.UseEat()é uma passagem por todas as células do inventário, em busca de alimento, e se o alimento for encontrado, ele é retirado do inventário e 10 unidades são adicionadas à fome. Para remover um item do estoque, criei uma função self.DestroyItem()em que o índice da célula é fornecido, e a célula inteira simplesmente fica vazia por padrão e sem 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))




Em seguida, é a função de jogar um objeto no chão.



Não há, no entanto, nada de complicado, quando você clica na Xfunção é chamada self.QuitItem(), o loop for percorre todas as células do inventário, e se a chave ["status"]não for igual "space", excluímos esta célula usando a função considerada anteriormente self.DestroyItem()e criamos um objeto baseado no que estava na célula, X e Y coloca o jogador como se o tivesse jogado ao lado dele.



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




E ainda assim todas, muitas coisas eu não disse como disse, T.K. eles não eram a parte principal do jogo, embora fossem interessantes. Por exemplo, mensagens sobre a possibilidade de pegar um item ou não (quando o estoque estiver cheio), que acrescentei uma animação ambulante, que fiz uma biblioteca separada de fotos e outras coisas.



Isso é tudo?



Não, vou adicionar uma rede neural ao jogo, usando uma biblioteca que escrevi em Python,

vou fazer a interação do jogador com NPCs equipados com uma rede neural, uma

pequena, mas de algum tipo de trama, e também alguns suprimentos para o jogador, como armadura, comida. itens, a capacidade de construir em blocos.



Experimente o jogo



Ele pode ser baixado gratuitamente do meu GitHub, você só precisa do Python3 para rodar e da biblioteca do teclado . Você precisa executar o arquivo main.py.



Um jogo



All Articles