Hello!
Today I will describe in detail how I made the game on the command line, and how good it turned out.
Where does the idea come from?
I was inspired by the idea of ββdoing something simple at first glance, but at the same time interesting in terms of development. The idea came to my mind to make a game in the console, it is interesting in terms of development, and it will be interesting to look at it even from the outside, such as this game.
Game engine
So, let's start with how the game is organized at the root, and what is its idea of ββwork.
First, I decided on how the game world would be displayed on the console. I realized that in order to display game objects, we need a list that stores other lists that contain symbols, which are subsequently displayed on the playing field in a loop
for
.
With this code:
for line_words in OUTPUT_IMAGE:
for word in line_words:
print(word, end="")
print("\n", end="")
Here we draw all the characters from the list, and go to a new line to draw the next list of characters.
This is how the variable that stores the lists of symbols looks like:
Here we immediately get a decision on how to display objects in X and Y, we can now specify:
X - a symbol in the list
Y - a list that contains X
Thus, draw some symbol on the field ... We will use this when drawing game objects.
We can try to draw a "ball" on the field, substituting the letter "O" for X and Y.
To do this, write the following code:
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="")
And so, we have drawn an object on our playing field. True, the X and Y coordinates are not classic. Firstly, we indicate first Y, then X, which is not quite according to the classics, and secondly, the Y coordinate should increase in order to raise the object, in our case, on the contrary, it should decrease.
Graph of X and Y in the game:
This feature will also have to be taken into account later when we do collisions of objects in the console.
Now we can try to move our object across the playing field, i.e. create movement.
We will need to clear the console in order to erase the old picture of the playing field.
We will do this with the command:
os.system("cls||clear")
Also, we need to override the variable
OUTPUT_IMAGE
in order to clear all objects previously drawn in the playing field.
We will also need to put all this in
while True
.
Let's add to the
while True
function time.sleep(1)
in order to limit the FPS.
And so, the code was drawn before our eyes:
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 = [
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
]
Now we have the ability to distribute objects across the field.
True, these objects are too primitive, and we would have to learn how to draw complex objects like players, houses, food ...
In order to draw a complex object, we need to understand and figure out how to draw an object by specifying only once its X and Y.
For this we need a function that accepts a picture (symbols), X, Y;
Let's do this:
def SetImage(image: str, x: int, y: int):
pass
Now we need to implement it. To do this, you need to decide how to draw an image that stretches along the X and Y axes, I came up with this:
draw an object by dividing it into symbols, and as soon as the "\ n" character is encountered, add the Y axis.
The Y axis, as we said, is incorrect, reversed, so we add to it to lower the object.
An example of an image that is drawn according to my principle:
image = " O\n'|'\n |"#
Now let's describe this in our function:
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
Let's add
try: except()
in order to avoid errors if the object has X and Y too small or too large.
x_start
This is the X, from which we need to start drawing when increasing Y (with the "\ n"
character ) Now we can use our function, drop X and Y into it, and the picture that needs to be drawn:
the code
from time import sleep
from os import system
OUTPUT_IMAGE = [
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
]
def SetImage(x: int, y: int, image: str):
x_start = x
x = x
y = y
for word in image:
if word == "\n":
x = x_start
y += 1
else:
x += 1
try:
OUTPUT_IMAGE[y][x] = word
except IndexError:
break
while True:
sleep(1)
system("cls||clear")
SetImage(x=3,y=4,image=" O\n'|'\n |")
for line_words in OUTPUT_IMAGE:
for word in line_words:
print(word, end="")
print("\n", end="")
OUTPUT_IMAGE = [
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
]
And this is what we got:
just like the ball we drew, it can be moved along the X and Y axes.
the code
from time import sleep
from os import system
OUTPUT_IMAGE = [
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
]
px = 0
py = 0
def SetImage(x: int, y: int, image: str):
x_start = x
x = x
y = y
for word in image:
if word == "\n":
x = x_start
y += 1
else:
x += 1
try:
OUTPUT_IMAGE[y][x] = word
except IndexError:
break
while True:
sleep(1)
system("cls||clear")
SetImage(x=px,y=py,image=" O\n'|'\n |")
for line_words in OUTPUT_IMAGE:
for word in line_words:
print(word, end="")
print("\n", end="")
px += 1
py += 1
OUTPUT_IMAGE = [
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
[".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".",],
]
And now, the player is already moving around the map.
Here we have already done a lot, there is already a player, there is already a map, and it would seem that it is already possible to make a game, but no. We need a function for calculating collisions of objects, because what kind of game is it without object interactions? So let's get started.
First, we need to make a function to get the latitude and height of an object in order to calculate its hitbox.
So, I decided to make the function according to the following logic:
X - the hitbox of the object in X width, this is the largest number of characters between the characters "\ n" in the picture
Y - the hitbox in Y is the number of characters "\ n" in the picture
By this logic it is not difficult make a function that takes a picture, counts all the characters between "\ n" for it, and selects the largest number of characters from this - the latitude is obtained.
And if you count the characters "\ n", as I already wrote, you get the height.
The function turned out like this:
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}
Why is ValueError except here?
It is here to prevent an error when starting the game.
So let's draw our player, and calculate its width and length.
code with drawing and calculating latitude and height
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! we have a function for calculating latitude and height, now we have to make a function for calculating the hitbox and collisions of objects.
Let's remember that our coordinate system is not a classical one, so, alas, we cannot use the classical function, we will have to make our own. To do this, I drew 2 squares on the graph that collide, and from this picture you can come up with a condition by which the collision will be calculated.
For ease of understanding, I drew hitboxes, i.e. squares:
Logic in words
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
Logic in code
, :
def IsClash(x: int, y: int, h: int, w: int,x2: int, y2: int, h2: int, w2: int):
if (y >= y2 - h2 + h and y - h <= y2 + h2 - h) or (y2 >= y - h + h2 and y2 - h2 <= y + h - h2):
if (x >= x2 - w2 + w and x - w <= x2 + w2 - w) or (x2 >= x - w + w2 and x2 - w2 <= x + w - w2):
return True
return False
True
, False
.
I additionally drew a cube on our playing field so that the player has someone to face.
And I tried how the collision calculation function works.
Here is a player touching a cube:
But no touching:
Contact code
/ :
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()
Now we have all the starting functions for the game, in fact, I wrote my game based on them.
A game
The idea of ββthe game is as follows:
There is a player, food appears around him, which he is forced to collect so as not to die. The game also has functions: pick up food, put it in the inventory, eat it from the inventory, put an item from the inventory on the
floor.I started by making a game loop in 3 lines, it's simple
While True
:
from time import sleep
while True:
sleep(0.1)
Then I considered it necessary to create a class in which all the functions of future objects will be stored. Therefore, I created a main.py file and a lib folder, into which I placed the lib.py file in which the game class was. THOSE. the game files looked like this:
+----game
| + --
| | -- main.py
| \ --lib
| +--lib.py -> class Game()
| \
|
+---
In the future, I worked mainly with the Game () class, in main.py I just called it, created starting objects, started the game.
In the game class, I made a run () function that starts the game loop. Also made the draw_all () function, it erases all past objects, draws new ones, and prints to the playing field.
And this is how the class looked like:
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)
I added all the basic functions of the type
set_image()
, size_object()
, is_clash()
, and all those who are game engine, and which I have described above.
Made a new function
create_object()
and a variable self.OBJECTS
, a function create_object()
I use to create objects, it takes the parameters img
, name
, x
, y
, up
, rigid
, data
.
img
- object picture - object
name
name (house, grass, inhabitant, food, etc.)
x
- object X - object
y
Y
up
- if this parameter is True, then the object is drawn above the player, otherwise the player overlaps it
rigid
- hardness, the player cannot go through this object (not yet implemented)
data
- personal data of the object, its personal characteristics
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}
)
At that time, I already added a player, a house, grass, and a villager.
And I decided to use the same parameter in the object
up
, use it in the object Home
, i.e. so that the house covers the player. To do this, I made the CheckAll () function, a for loop went through all the objects, and drew them on the outgoing picture, i.e. use the SetImage (x: int, y: int, img: str) function, supplying the X and Y of the object, and a picture.
Thus, he drew objects that the player could close himself. In the same cycle, I declared a list
up_of_payer_objects
, and if the object had up = True, then I added it to the list without drawing it on the field. After that I drew the player himself, and only then I went through the for loop over the objects in up_of_payer_objects, drawing them, thus they were above the player.
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"])
Then I started moving the player. For this, I created it as a separate object, which is not in the list
self.OBJECTS
, but which is stored in a variable self.PLAYER
.
All its parameters according to the type
X
, Y
, img
, itp You can get it using keys, in other words it is a dictionary (dict). With such a player and objects it was already possible to work, move, calculate collisions. I started by moving.
I started creating motion by making the CheckKeysObjects () function, which is responsible for tracking keystrokes, and which I call in the CheckAll () function at the very beginning
def CheckAll(self):
self.CheckKeysObjects()
....
To track keystrokes, I used the keyboard library , and 4 variables: And everything turned out to be simple, we track the keys, and if it is pressed , we make a variable . At the very beginning of the function, we declare all variables in , in order to reset all past results, otherwise the player will not stop.
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
After that, in the function, I
CheckAll()
check all the variables responsible for the movement, find out where the player is moving.
If any is in
True
, find out which one, and move the object in the opposite direction.
The resulting movement code
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
Yes, we move objects in the opposite direction in order to create the illusion of movement. If the player goes to the right, then all objects of the environment are shifted to the left.
Then I added more environmental items, and started spawning food, the player's goal is to collect food so as not to die.
For the countdown of the food spawn time, I used a simple one
time.sleep()
, and a library threading
- in order to run 2 functions at the same time, food spawn and the main game loop. The food spawn function SpawnEat()
is just a function that, when launched, generates food at random places, calling a function for each unit of food CreateObject()
.
Also, once I made the food spawn function, I made the player variable
self.PLAYER["hungry"]
, this is his hunger, at the very beginning it is equal to 100 units, I will decrease it if the player walks and spends energy (such as energy, it is not in the game) or increase it if the player ate something.
I also made a function
MinimizeHungry()
, it is called every 5 seconds, and it just takes 2 units of hunger from the player. I did this so that the player had to move, and not stand still.
Finally, in a function
Eat()
, this function is called on a separate thread from the game loop. She checks if there is too much food on the map, if food is more than 10 units. it does NOT call the function SpawnEat()
if less than 10 units. then calls SpawnEat()
.
Here's how it turned out:
Eat ()
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()
Function
Start()
to start the main loop:
Start ()
def Start(self):
while True:
self.CheckAll()
self.DrawAll()
sleep(0.01)
And a function
run()
that launches the entire game.
run ()
def run(self):
proc1 = threading.Thread(target=self.Start)
proc1.start()
proc2 = threading.Thread(target=self.Eat)
proc2.start()
The process of eating itself, I implemented simply in the function
CheckAll()
and CheckKeysObjects()
. Q CheckKeysObjects()
I checked to see if the player pressed the button E
. If pressed, then put the variable self.PRESS_E
in True
.
In the loop
CheckAll()
, I checked if the current object in the loop was for
food, if the food then checked if the player collided with it, if it collided, then checked the variable self.PRESS_E
, and if it True
then simply deleted the object and increased hunger, i.e. variable self.PLAYER["hungry"]
.
This is how it is in the code
for object_now in range(len(self.OBJECTS)):
....
if self.OBJECTS[object_now]["name"] == "meat":
items_objects.append(object_now)
is_clash = self.IsClash(
x=self.OBJECTS[object_now]["x"],
y=self.OBJECTS[object_now]["y"],
h=self.OBJECTS[object_now]["h"],
w=self.OBJECTS[object_now]["w"],
x2=self.PLAYER["x"],
y2=self.PLAYER["y"],
h2=self.PLAYER["h"],
w2=self.PLAYER["w"],
)
if is_clash:
if self.PRESS_E:
try:
self.PLAYER["hungry"] += self.HUNGRUY_ADD
del self.OBJECTS[object_now]
break
except IndexError:
pass
I will say in advance, I will need to rewrite all this when I do the inventory
Making inventory
So, it's hard, we need to make an inventory.
The difficulty is that all objects will need to be displayed, stored history, deleted, placed objects on the floor.
I started by adding a new key to the player, it was
self.PLAYER["inventory"]
, 4 cells are stored there, like this:
"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"},
}
Are just cell numbers.
status
- this key stores the value whether the egg cell is empty or not. If empty then "space", if there is an item, then the name of the item is stored there.
name
- stores the name of the item, it will be used when the player puts the item.
minimize_image
- this is a small picture of the item that is displayed in the player's inventory.
After, I made new checks in ours
CheckKeysObjects()
, when you click on the X
item, it will throw itself to the ground, and when you click on the button, the E
function will be called self.UseEat()
, which we will now analyze.
So the function
self.UseEat()
is a passage through all the cells of the inventory, in search of food, and if food is found, then it is removed from the inventory, and 10 units are added to hunger. To remove an item from the inventory, I made a function self.DestroyItem()
in which the cell index is supplied, and the entire cell simply becomes empty by default and without anything.
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))
Next is the function of throwing an object to the ground.
There is, however, nothing complicated, when you click on the
X
function is called self.QuitItem()
, the for loop goes through all the cells of the inventory, and if the key is ["status"]
not equal "space"
, then we delete this cell using the previously considered function self.DestroyItem()
, and create an object based on what was in the cell, X and Y puts the player as if he had thrown him next to him.
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
And yet all, many things I did not say how I did, T.K. they were not the main part of the game, albeit interesting. For example, messages about the possibility of picking up an item or not (when the inventory is full), that I added a walking animation, that I made a separate library of pictures, and other things.
That's all?
No, I'm going to add a neural network to the game, using a library that I wrote in Python,
I'm going to make the player's interaction with NPCs equipped with a neural network, a
small, but some kind of plot, and also some supplies for the player, such as armor, food. items, the ability to build in blocks.
Try the game
You can freely download it from my GitHub, you only need Python3 to run it, and the keyboard library . You need to run the file
main.py
.
A game