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:

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

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:

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_IMAGE
para limpar todos os objetos desenhados anteriormente no campo de jogo.
Também precisaremos colocar tudo isso
while True
.
Vamos adicionar à
while True
funçã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 = [
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
]

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_start
Este é 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:

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

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:

Lógica em palavras
x — X
y — Y
h —
w —
x2 — X
y2 — Y
h2 —
w2 —
:
X, Y,
:
X
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
Y
X, Y,
y
— x
, h
— w
.
:
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:

Mas sem tocar:

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 -
name
nome do objeto (casa, grama, habitante, comida, etc.)
x
- objeto X - objeto
y
Y
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
d
self.WALK_RIGHT_PLAYER
True
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 jogador
self.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_E
em True
.
No loop
CheckAll()
, eu verifiquei se o objeto atual no loop era for
comida, 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 True
entã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 X
item, ele irá jogá-lo no chão, e também ao clicar no botão E
será chamada a função self.UseEat()
, que agora analisaremos.
Então a função
self.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
X
funçã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