GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Python 3
  • Скачать Python
  • IDE
  • Скачать PyCharm
  • Django
  • Tkinter
  • Книги

This view

  • Original page
  • New address

GeezerVison is showing python-scripts.com — Создание игры с нуля на Tkinter Python - Земейка

Создание игры на Python Tkinter

В этой части изучения Tkinter мы создадим клон игры Nibbles (змейка). Nibbles – это первоначальная версия классической змейки. Впервые ее создали в конце 70х годов. Позже она была перенесена на ПК. В этой игре игрок управляет змейкой. Цель игры – съесть как можно больше яблок. После каждого съеденного яблока тело змеи увеличивается. Во время движения змейка должна уклоняться от стен и собственного тела.

Содержание курса

  1. Создание окна по центру и кнопка выхода в Tkinter
  2. Разметка виджетов в Tkinter — pack, grid и place
  3. Виджеты Checkbutton, Label, Scale и Listbox в Tkinter
  4. Меню, подменю и панель инструментов в Tkinter
  5. Диалоговые окна в Tkinter — Выбор цвета — Выбор файла
  6. Рисуем линии, прямоугольники, круг и текст в Tkinter
  7. Пишем игру змейка на Tkinter

Содержание статьи

  • Этапы разработки игры на Tkinter

Этапы разработки игры на Tkinter

Размер каждого соединения змейки – 10 пикселей. Змейка управляется стрелками на клавиатуре. Изначально у змейки есть всего три соединения. Игра начинается мгновенно. Когда игра заканчивается, на экране появляется надпись «Игра Закончена».

Для начало, мы создаем Canvas виджет. Объектами в игре являются изображения. Мы используем методы canvas для создания объектов изображения. Также, мы используем методы canvas для обнаружения объектов на холсте при помощи тегов, и для обнаружения столкновений с другими объектами.

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
import sys
import random
from PIL import Image, ImageTk
from tkinter import Tk, Frame, Canvas, ALL, NW
 
 
class Cons:
 
    BOARD_WIDTH = 300
    BOARD_HEIGHT = 300
    DELAY = 100
    DOT_SIZE = 10
    MAX_RAND_POS = 27
 
 
class Board(Canvas):
 
    def __init__(self):
        super().__init__(
            width=Cons.BOARD_WIDTH, height=Cons.BOARD_HEIGHT,
            background="blue", highlightthickness=0
        )
 
        self.initGame()
        self.pack()
 
    def initGame(self):
        """
        Инициализация игры.
        """
 
        self.inGame = True
        self.dots = 3
        self.score = 0
 
        # Переменные для передвижения замеи.
        self.moveX = Cons.DOT_SIZE
        self.moveY = 0
 
        # Изначальные стартовые координаты яблоки.
        self.appleX = 100
        self.appleY = 190
 
        self.loadImages()
 
        self.createObjects()
        self.locateApple()
        self.bind_all("<Key>", self.onKeyPressed)
        self.after(Cons.DELAY, self.onTimer)
 
    def loadImages(self):
        """
        Подгружаем нужные изображения для игры.
        """
 
        try:
            self.idot = Image.open("dot.png")
            self.dot = ImageTk.PhotoImage(self.idot)
            self.ihead = Image.open("head.png")
            self.head = ImageTk.PhotoImage(self.ihead)
            self.iapple = Image.open("apple.png")
            self.apple = ImageTk.PhotoImage(self.iapple)
 
        except IOError as e:
            print(e)
            sys.exit(1)
 
    def createObjects(self):
        """
        Создание объектов на холсте.
        """
 
        self.create_text(
            30, 10, text="Счет: {0}".format(self.score),
            tag="score", fill="white"
        )
 
        self.create_image(
            self.appleX, self.appleY, image=self.apple,
            anchor=NW, tag="apple"
        )
 
        self.create_image(50, 50, image=self.head, anchor=NW, tag="head")
        self.create_image(30, 50, image=self.dot, anchor=NW, tag="dot")
        self.create_image(40, 50, image=self.dot, anchor=NW, tag="dot")
 
    def checkAppleCollision(self):
        """
        Проверяем, не столкнулась ли голова змеи с яблоком.
        """
 
        apple = self.find_withtag("apple")
        head = self.find_withtag("head")
 
        x1, y1, x2, y2 = self.bbox(head)
        overlap = self.find_overlapping(x1, y1, x2, y2)
 
        for ovr in overlap:
 
            if apple[0] == ovr:
 
                self.score += 1
                x, y = self.coords(apple)
                self.create_image(x, y, image=self.dot, anchor=NW, tag="dot")
                self.locateApple()
 
    def moveSnake(self):
        """
        Меняем положение змеи на холсте.
        """
 
        dots = self.find_withtag("dot")
        head = self.find_withtag("head")
 
        items = dots + head
 
        z = 0
        while z < len(items)-1:
 
            c1 = self.coords(items[z])
            c2 = self.coords(items[z+1])
            self.move(items[z], c2[0]-c1[0], c2[1]-c1[1])
            z += 1
 
        self.move(head, self.moveX, self.moveY)
 
    def checkCollisions(self):
        """
        Проверка на столкновение змеи с другими объектами.
        """
 
        dots = self.find_withtag("dot")
        head = self.find_withtag("head")
 
        x1, y1, x2, y2 = self.bbox(head)
        overlap = self.find_overlapping(x1, y1, x2, y2)
 
        for dot in dots:
            for over in overlap:
                if over == dot:
                  self.inGame = False
 
        if x1 < 0:
            self.inGame = False
 
        if x1 > Cons.BOARD_WIDTH - Cons.DOT_SIZE:
            self.inGame = False
 
        if y1 < 0:
            self.inGame = False
 
        if y1 > Cons.BOARD_HEIGHT - Cons.DOT_SIZE:
            self.inGame = False
 
    def locateApple(self):
        """
        Распределяем яблоки по холсту (canvas).
        """
 
        apple = self.find_withtag("apple")
        self.delete(apple[0])
 
        r = random.randint(0, Cons.MAX_RAND_POS)
        self.appleX = r * Cons.DOT_SIZE
        r = random.randint(0, Cons.MAX_RAND_POS)
        self.appleY = r * Cons.DOT_SIZE
 
        self.create_image(
            self.appleX, self.appleY, anchor=NW,
            image=self.apple, tag="apple"
        )
 
    def onKeyPressed(self, e):
        """
        Управляем змеей через стрелки клавиатуры.
        """
 
        key = e.keysym
 
        LEFT_CURSOR_KEY = "Left"
        if key == LEFT_CURSOR_KEY and self.moveX <= 0:
            self.moveX = -Cons.DOT_SIZE
            self.moveY = 0
 
        RIGHT_CURSOR_KEY = "Right"
        if key == RIGHT_CURSOR_KEY and self.moveX >= 0:
            self.moveX = Cons.DOT_SIZE
            self.moveY = 0
 
        RIGHT_CURSOR_KEY = "Up"
        if key == RIGHT_CURSOR_KEY and self.moveY <= 0:
            self.moveX = 0
            self.moveY = -Cons.DOT_SIZE
 
        DOWN_CURSOR_KEY = "Down"
        if key == DOWN_CURSOR_KEY and self.moveY >= 0:
            self.moveX = 0
            self.moveY = Cons.DOT_SIZE
 
    def onTimer(self):
        """
        Создает игровой цикл для каждого события таймера
        """
 
        self.drawScore()
        self.checkCollisions()
 
        if self.inGame:
            self.checkAppleCollision()
            self.moveSnake()
            self.after(Cons.DELAY, self.onTimer)
        else:
            self.gameOver()
 
    def drawScore(self):
        """
        Рисуем счет игры
        """
 
        score = self.find_withtag("score")
        self.itemconfigure(score, text="Счет: {0}".format(self.score))
 
    def gameOver(self):
        """
        Удаляем все объекты и выводим сообщение об окончании игры.
        """
 
        self.delete(ALL)
        self.create_text(self.winfo_width() / 2, self.winfo_height()/2,
            text="Игра закончилась со счетом {0}".format(self.score), fill="white")
 
 
class Snake(Frame):
 
    def __init__(self):
        super().__init__()
        self.master.title('Змейка')
        self.board = Board()
        self.pack()
 
 
def main():
    root = Tk()
    nib = Snake()
    root.mainloop()
 
 
if __name__ == '__main__':
    main()

В первую очередь, мы расшифруем некоторые переменные, использующиеся в нашей игре:

  • BOARD_WIDTH и BOARD_HEIGHT обозначают размер игрового поля.
  • DELAY – скорость игры;
  • DOT_SIZE — размер яблока и соединения змейки;
  • MAX_RAND_POS – создание случайной точки для яблока.

Метод iniGame() инициализирует переменные, загружает изображения игры и запускает функцию таймера.

Python
1
2
self.createObjects()
self.locateApple()

Метод createObject() создает объекты на холсте, а locateApple() устанавливает яблоко на случайную точку на холсте.

Python
1
self.bind_all("<Key>", self.onKeyPressed)

Мы назначаем клавиши клавиатуры при помощи метода onKeyPressed(). Для управления замеей в игре используются стрелки на клавиатуре.

Python
1
2
3
4
5
6
7
8
9
10
11
try:
    self.idot = Image.open("dot.png")
    self.dot = ImageTk.PhotoImage(self.idot)
    self.ihead = Image.open("head.png")
    self.head = ImageTk.PhotoImage(self.ihead)
    self.iapple = Image.open("apple.png")
    self.apple = ImageTk.PhotoImage(self.iapple)
 
except IOError as e:
    print(e)
    sys.exit(1)

В этих строчках показана загрузка наших изображений. Это три изображения для нашей игры:

  • голова (head.png);
  • соединение змеи (dot.png);
  • яблоко (apple.png).

Есть вопросы по Python?

На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!

Python Форум Помощи

Telegram Чат & Канал

Вступите в наш дружный чат по Python и начните общение с единомышленниками! Станьте частью большого сообщества!

Чат
Канал

Паблик VK

Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!

Подписаться

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def createObjects(self):
    """
    Создание объектов на холсте.
    """
 
    self.create_text(
        30, 10, text="Счет: {0}".format(self.score),
        tag="score", fill="white"
    )
 
    self.create_image(
        self.appleX, self.appleY, image=self.apple,
        anchor=NW, tag="apple"
    )
 
    self.create_image(50, 50, image=self.head, anchor=NW, tag="head")
    self.create_image(30, 50, image=self.dot, anchor=NW, tag="dot")
    self.create_image(40, 50, image=self.dot, anchor=NW, tag="dot")

С помощью метода createObject() мы создаем игровые объекты на холсте. Это canvas объекты. У них есть стартовые x и y координаты.

Параметр image позволяет изображениям появиться на холсте. Параметр anchor установлен на NW (Север и Запад), что означает ориентир на верхнюю левую точку холста. И этот шаг очень важен, если мы хотим отображать изображения около границ корневого окна tkinter.

Создание игр на Python

Попробуйте удалить anchor и посмотрите, что произойдет. Параметр tag используется для идентификации объектов на холсте.

Вы можете прямо сейчас купить дешевых подписчиков на YouTube с помощью сервиса doctorsmm.com. Тут Вы найдете не только вкусный прайс, но и разнообразные критерии к каждой услуге, которые подойдут любому каналу. Кроме того, Вы можете получить большинство услуг со стабильной гарантией качества и защитой от списаний.

Один тег может использоваться для нескольких объектов на холсте.

Метод checkAppleCollision() позволяет нам узнать, съела ли змея яблоко или нет. Если да, мы добавляем одно соединение змейке и выполняем метод locateApple() для создания нового яблока на нашей игровой карте.

Python
1
2
apple = self.find_withtag("apple")
head = self.find_withtag("head")

Метод find_withtag() позволяет нам найти объект на холсте, используя имя данного тега.

Нам нужно два объекта: голова змеи и яблоко.

Стоит отметить, что даже если присутствует только один объект с указанным тегом, метод вернет кортеж. Это для случая с объектом яблока. Позже объект яблока станет доступен следующим образом: apple[0].

Python
1
2
x1, y1, x2, y2 = self.bbox(head)
overlap = self.find_overlapping(x1, y1, x2, y2)

Метод bbox() возвращает точки границ объекта. С помощью метода find_overlapping() мы найдем столкновение объектов с этими координатами.

Python
1
2
3
4
5
6
for ovr in overlap:
  
    if apple[0] == ovr:
        x, y = self.coords(apple)
        self.create_image(x, y, image=self.dot, anchor=NW, tag="dot")
        self.locateApple()

Если яблоко сталкивается с головой змеи, то мы создаем новое соединение для змейки на координатах яблока. Также, мы выполняем метод locateApple(), который удаляет старое яблоко из холста и создает новое в случайной точке на игровой карте.

Метод moveSnake() устанавливаем механизм управлениями змеей через клавиатуру. Чтобы понять, нужно посмотреть на то, как двигается змейка. Мы управляем ее головой. Мы можем изменить ее направление при помощи клавиш стрелок на клавиатуре. Остальные соединения двигаются за головой как одна единая цепочка, прямо как блокчейн на python который мы создали в другой статье.

Python
1
2
3
4
5
6
z = 0
while z < len(items)-1:
    c1 = self.coords(items[z])
    c2 = self.coords(items[z+1])
    self.move(items[z], c2[0]-c1[0], c2[1]-c1[1])
    z += 1

Этот код позволяет соединениям (туловище змеи) двигаться одной цепью.

Python
1
self.move(head, self.moveX, self.moveY)

Этот код меняет направление движения головы змеи. Значение атрибутов класса self.moveX и self.moveY меняются после каждого нажатия по стрелкам клавиатуры для изменения траектории движения змеи.

С помощью метода checkColission() мы определяем, когда змея удариться головой об свой хвост либо об стену.

Python
1
2
3
4
5
6
7
x1, y1, x2, y2 = self.bbox(head)
overlap = self.find_overlapping(x1, y1, x2, y2)
 
for dot in dots:
    for over in overlap:
        if over == dot:
          self.inGame = False

Игра заканчивается, если голова змеи ударяется об свой хвост.

Python
1
2
if y1 > HEIGHT - DOT_SIZE:
    self.inGame = False

Игра заканчивается, если змея ударяется о край игрового поля.

Метод locateApple() устанавливает новое яблоко в случайной точке на холсте и удаляет старое яблоко с игровой карты.

Python
1
2
apple = self.find_withtag("apple")
self.delete(apple[0])

В этой части кода мы находим и удаляем яблоко, которое было съедено змейкой.

Python
1
r = random.randint(0, Cons.MAX_RAND_POS)

Мы получаем случайное число от 0 до MAX_RAND_POS — 1

Python
1
2
3
self.appleX = r * Cons.DOT_SIZE
...
self.appleY = r * Cons.DOT_SIZE

Здесь мы устанавливаем x и y координаты объекта яблока.

В методе onKeyPressed(), мы реагируем на какие клавиши нажал игрок.

Python
1
2
3
4
5
LEFT_CURSOR_KEY = "Left"
 
if key == LEFT_CURSOR_KEY and self.moveX <= 0:
    self.moveX = -Cons.DOT_SIZE
    self.moveY = 0

Если мы нажали на стрелку влево, то обновляем значения из атрибутов self.moveX и self.moveY в соответствии с положением змеи на карте. Эти атрибуты используется в методе moveSnake(), который изменяет координаты объекта змеи. Стоит отметить, что если змея поворачивается направо, мы не можем сразу же развернуться влево.

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def onTimer(self):
    """
    Создает игровой цикл для каждого события таймера
    """
 
    self.drawScore()
    self.checkCollisions()
 
    if self.inGame:
        self.checkAppleCollision()
        self.moveSnake()
        self.after(Cons.DELAY, self.onTimer)
    else:
        self.gameOver()

После каждого DELAY запускается метод onTimer(). Если мы в игре, мы запускаем три метода, которые являются основой логики игры. В противном случае игра заканчивается.

Таймер основан на методе after(), который запускает метод после DELAY только однажды. Чтобы повторно запускать таймер, мы рекурсивно запускаем метод onTimer().

Python
1
2
3
4
5
6
7
def drawScore(self):
    """
    Рисуем счет игры
    """
 
    score = self.find_withtag("score")
    self.itemconfigure(score, text="Счет: {0}".format(self.score))

Метод drawScore рисует счет игры на нашей игровой карте в верхнем левом углу.

Python
1
2
3
4
5
6
7
8
def gameOver(self):
    """
    Удаляем все объекты и выводим сообщение об окончании игры.
    """
 
    self.delete(ALL)
    self.create_text(self.winfo_width() / 2, self.winfo_height()/2,
        text="Игра закончилась со счетом {0}".format(self.score), fill="white")

Если игра закончена, мы удаляем все объекты на холсте. Затем мы выводим по центру экрана финальный счет игрока.

Tkinter Игра

В этой части мы разобрали простую компьютерную игру «змейка», созданную в Tkinter.

Vasile Buldumac

Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.

E-mail: [email protected]

Образование
Universitatea Tehnică a Moldovei (utm.md)

  • 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
  • 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»
You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.