Разработка игрPython

Как сделать игру-кликер на Python: кнопка, очки и личный рекорд

Пишем игру-кликер на Python и Tkinter: счётчик, таймер на 15 секунд, кнопка старта, личный рекорд в JSON и проверка игрового цикла.

Кодик

Автор

5 мин чтения

Кликер можно собрать на Tkinter без игрового движка. За 15 секунд игрок набирает очки, а программа обновляет таймер, блокирует кнопку после финала и сохраняет лучший результат.

Главная задача кликера не в большой кнопке, а в управлении состоянием. До старта клики не считаются, во время раунда каждый клик добавляет одно очко, а после нуля кнопка отключается. Флаг running явно отделяет эти три фазы.

1Стартуем

Обнуляем score, ставим 15 секунд и включаем кнопку.

2Считаем

Обработчик click добавляет очко только при running.

3Финишируем

after вызывает tick каждую секунду, а в конце сохраняет best.

Из каких состояний состоит кликер

В окне нужны Label с очками, Label с остатком времени, большая кнопка клика и маленькая кнопка старта. Числа хранятся в обычных атрибутах класса, а StringVar используются только для подписей. Рекорд лежит в JSON рядом с файлом игры.

ФазаrunningКнопка
До стартаfalsedisabled
Раундtruenormal
После 0falsedisabled
Новый стартtruenormal

После этого шага у каждой части есть одна роль. Интерфейс собирает действие, логика меняет состояние, а вывод показывает признак успеха. Это проще отлаживать, чем один большой обработчик.

Один раунд кликера
Три фазы и один флаг running. Новый раунд можно начать сразу после нуля

Сначала соберите минимальный сценарий. Не добавляйте оформление, хранение и дополнительные режимы одновременно. Добейтесь одного правильного прохода от входных данных до результата, а затем проверяйте каждое улучшение отдельно.

Пишем окно, таймер и счётчик

Tkinter нельзя замораживать time.sleep, иначе окно перестанет рисоваться и принимать клики. Метод after ставит tick в очередь событий через 1000 мс. После каждого tick окно снова готово к клику.

import json
import tkinter as tk
from pathlib import Path

SAVE_FILE = Path("clicker_record.json")

def load_best():
    try:
        return int(json.loads(SAVE_FILE.read_text())["best"])
    except (FileNotFoundError, KeyError, ValueError, json.JSONDecodeError):
        return 0

class Clicker:
    def __init__(self, root):
        self.root = root
        self.score = 0
        self.seconds = 15
        self.best = load_best()
        self.running = False
        self.info = tk.StringVar()
        self.click = tk.Button(root, text="КЛИК!", font=("Arial", 28),
                               width=12, height=4, command=self.add_point,
                               state="disabled")
        self.click.pack(padx=30, pady=20)
        tk.Label(root, textvariable=self.info, font=("Arial", 16)).pack()
        tk.Button(root, text="Начать 15 секунд",
                  command=self.start).pack(pady=16)
        self.render()

    def render(self):
        self.info.set(f"Очки: {self.score}  |  Время: {self.seconds}  |  Рекорд: {self.best}")

    def start(self):
        if self.running: return
        self.score, self.seconds, self.running = 0, 15, True
        self.click.config(state="normal")
        self.render(); self.root.after(1000, self.tick)

    def add_point(self):
        if self.running:
            self.score += 1; self.render()

    def tick(self):
        if not self.running: return
        self.seconds -= 1; self.render()
        if self.seconds > 0:
            self.root.after(1000, self.tick)
        else:
            self.running = False; self.click.config(state="disabled")
            if self.score > self.best:
                self.best = self.score
                SAVE_FILE.write_text(json.dumps({"best": self.best}))
            self.render()

root = tk.Tk(); root.title("Кликер")
Clicker(root); root.mainloop()
Что должно работать после первого запуска

after сохраняет отзывчивость окна. running не даёт запустить два таймера. Кнопка не считает очки до старта и после финала. Битый или отсутствующий JSON даёт безопасный рекорд 0.

sleep или after
Для GUI ответ один. Сравнение показывает, какой вариант легче поддерживать и проверять.

Как after обновляет игру без зависания

after не гарантирует идеальную точность до миллисекунды: обработчик попадёт в очередь не раньше указанного времени. Для учебного кликера этого достаточно. Для соревновательной игры считайте дедлайн по monotonic-времени, а не только число тиков.

СобытиеМеняетПоследствие
startscore, seconds, runningНовый раунд
add_pointscoreНовая подпись
ticksecondsСледующий after
Финалrunning, bestБлокировка и JSON

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

Смотрите на состояние, а не только на красивый экран. Перезагрузка, повторный клик, пустой ввод и медленный ответ часто открывают ошибки, которые не видны в идеальном демонстрационном проходе.

Практика: проверяем раунд и рекорд

Таймер легко ломается двойным стартом. Если кнопку «Начать» нажать трижды, без running появятся три независимых цепочки after, и секунды полетят втрое быстрее. Проверьте этот сценарий, а затем испортите JSON вручную. Игра должна восстановиться с нулевым рекордом.

Проверка своими руками
  1. Запустите clicker.py и нажмите большую кнопку до старта.
  2. Начните раунд и наберите не меньше 10 очков.
  3. Нажмите «Начать» ещё раз во время раунда.
  4. Дождитесь нуля и попробуйте добавить очко.
  5. Перезапустите файл и сверьте рекорд.
  6. Замените содержимое JSON на мусор и запустите игру.
Готово, если выполняются все пункты

Очки считаются только во время раунда. Таймер уменьшается раз в секунду и не ускоряется. Кнопка отключается на нуле. Рекорд переживает перезапуск и битый JSON не роняет игру.

Что проверить
До того, как добавлять улучшения. Пройдите все четыре сценария перед публикацией проекта.

Доводим кликер до готовой мини-игры

Когда базовый цикл работает, добавьте цену клика, улучшение или комбо-серию. Добавляйте по одному правилу и оставляйте один источник истины для score. Если кнопка и клавиша Space добавляют очки, оба события должны вызывать add_point, а не копировать её логику.

time.sleep в интерфейсе

Окно зависнет. Используйте after.

Двойной старт

Без running появятся несколько таймеров.

Клик после финала

Отключайте Button и дополнительно проверяйте running.

JSON читается без except

Первый запуск или обрыв записи уронят программу.

Сверьтесь с первичным источником. Поведение используемых функций и ограничения примера проверяйте по документацией Python по Tkinter. Документация особенно важна, когда меняются версии, политика платформы или формат ответа API.

Что получится в итоге

Готовый результат проекта: Как сделать игру-кликер на Python: кнопка, очки и личный рекорд
Готовая мини-игра показывает очки, 15-секундный таймер и рекорд, а большая кнопка работает только во время раунда.

Короткие ответы
Почему не time.sleep?

Он блокирует главный цикл Tkinter, и окно перестаёт отвечать.

Зачем running?

Флаг не даёт запустить два раунда и не считает клики вне игры.

Где хранится рекорд?

В маленьком JSON-файле рядом с игрой.

Как понять, что проект готов?

Он повторяет целевой сценарий несколько раз подряд, понятно реагирует на ошибочный ввод и не теряет состояние при обычных действиях.

Можно ли копировать код целиком?

Можно как стартовую точку, но сначала замените значения из примера, затем запустите проект по шагам. Если вы не можете объяснить одну строку, её лучше разобрать до следующего шага.

Превратите счётчик в игровой цикл

Классы, файлы и события можно закрепить в курсе Python. Если игра не находит JSON, сверьте случай со статьё про FileNotFoundError.

Сравните игровой цикл с крестиками-ноликами и камнем, ножницами, бумагой. В разделе «Практика» Кодика можно хранить каждую версию мини-игры отдельно.