Как запустить динозаврика Google на тачбаре? Пишем игру на Python

Не знаете, чем заняться на новогодних праздниках? Предлагаем отложить нарезку оливье и взяться за новый проект — написать свою игру для тачбара. В тексте показываем, как это сделать на Python всего за час.

Вот что получится, если будете следовать инструкции.

С 2016 года у некоторых моделей MacBook Pro есть сенсорная OLED-панель. Она заменяет функциональные клавиши, закладки и элементы настроек.

Для программирования тачбара не обязательно погружаться в Swift и AppKit — достаточно знания Python и библиотеки PyTouchBar. С последним познакомимся подробней: напишем игру с динозавриком и освоим встроенные инструменты модуля.

Используйте навигацию, если нужно изучить конкретные шаги, а не читать инструкцию целиком:

Делаем меню и знакомимся с элементами библиотеки

Кнопка «играть»

PyTouchBar работает в связке с Tkinter. Для начала нужно установить и первый, и второй модули. А после — подготовить GUI-окно, которое будет отображаться на тачбаре. И добавить, например, кнопку «играть».

from tkinter import * import PyTouchBar # создание окна Tkinter root = Tk() # параметризация кнопки starter = PyTouchBar.TouchBarItems.Button(title='играть', color=(PyTouchBar.Color.green)) # добавление элемента PyTouchBar.set_touchbar([starter]) # подготовка окна PyTouchBar.prepare_tk_windows(root) root.mainloop()

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

Для окрашивания кнопки можно использовать встроенную константу PyTouchBar.Color. Или передать в функцию кортеж (r, g, b, a), где r, g, b, a — значения от 0 до 1.

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

# PyTouchBar, добавление функции для action. ... def start(button): # здесь будет запуск игровой сцены game print('Hello world') starter = PyTouchBar.TouchBarItems.Button(title='играть', color=(PyTouchBar.Color.green), action=start) ...

Подпрограмма start нужна для запуска игровой сцены game.

Настройка скорости динозаврика

Библиотека поддерживает не только простые кнопки, но и так называемые «степперы» — с помощью них можно вводить числовые значения. В нашем случае — скорость динозаврика.

# PyTouchBar, добавление степпера. ... # минимальная скорость speed = 2 def set_speed(stepper): global speed size = int(speed.value) print ('Скорость динозавра:', speed) speed_p = PyTouchBar.TouchBarItems.Stepper(min = 2, max = 7, action = set_speed) # параметризация степпера # добавление кнопки и степпера PyTouchBar.set_touchbar([speed_p, starter]) ...

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

На самом деле PyTouchBar поддерживает больше элементов. Некоторые из них мы добавим в игру ниже. Полный список ищите в официальной документации.

Загружаем игровую сцену из кактусов

Представление 2D-сцены

После нажатия кнопки «играть» должна сработать специальная функция game, которая нужна для отрисовки сцены — динозаврика и кактусов. Один из вариантов — добавить их с помощью кнопок, которые мы будем называть чанками.

Каждый чанк — целочисленное значение:

  • -2 — динозавр врезался в кактус,
  • -1 — кактус,
  • 0 — плоскость,
  • 1 — динозаврик,
  • 2 — динозавр перепрыгнул через кактус.

Карту можно представить так:

map = [1, 0, 0, -1, 0, -1, 0]

Визуализация элементов

Кнопки поддерживают параметр image: на фон можно поставить любое статическое изображение.

Так, например, можно «пройтись» циклом по списку map и наполнить список buttons кнопками с изображениями, выбранными по значениям чанков.

# Отрисовка карты map с помощью кнопок. ... for chunk in range(len(map)): if map[chink] == 1: buttons[chunk] = PyTouchBar.TouchBarItems.Button(image='assets/trex.png', color=(PyTouchBar.Color.black), action=jumping) elif map[chunk]== 0: buttons[chunk] = PyTouchBar.TouchBarItems.Button(image='assets/platform.png', color=(PyTouchBar.Color.black)) else: buttons[chunk] = PyTouchBar.TouchBarItems.Button(image='assets/cactus.png', color=(PyTouchBar.Color.black)) ...

В результате схема карты преобразится в игровую сцену.

Что нужно учитывать

Для запуска сцены нужно закрыть root-окно и запустить новое. PyTouchBar не поддерживает обновление Tkinter-программ.

def game(map): # отрисовка карты map ... def prepare(): root = Tk() def start(button): # запускается по клику кнопки «играть» root.destroy() ... # генерируем карту и передаем в функцию игровой сцены game(map) ... PyTouchBar.set_touchbar([starter]) PyTouchBar.prepare_tk_windows(root) root.mainloop() prepare()

Но как тогда заставить динозаврика бежать?

Заставляем динозаврика бежать

Чтобы динозаврик побежал, нужно как-то обновлять сцену без root.update(). То есть уничтожать настоящую сцену с помощью root.destroy() и запускать новую с обновленными параметрами map. Получается некое обновление кадров.

Удаление старых кадров

Автоматизировать удаление старых кадров с помощью root.destroy() можно с помощью встроенного метода root.after(). Он умеет запускать отложенные функции.

# root.after(), пример вызова метода destroy. ... # после запуска Tkinter удалит кадр через 1 секунду root.after(1000, start.destroy) root.mainloop() ...

1000 — время, спустя которое удалится root-окно. Чем меньше значение, тем быстрее бежит динозаврик.

Создание новых кадров

Сам динозаврик не двигается. Обновляется только задний план — ландшафт сцены.

По сути, генерировать новые кадры можно разными способами. Выберем один из самых простых: будем делать срез списка на один элемент и добавлять рандомный чанк в конец. А после — запускать на новой карте игровую сцену game.

# Генерация новых кадров. Одна итерация — один новый кадр и чанк. ... # бесконечная генерация новых чанков и кадров while True: new_map = map[1:] # первый чанк — всегда динозавр (1) new_map[0] = 1 # условие, чтобы не генерировать два кактуса подряд if map[-1] == 0: new_map.append(random.choice([0, -1])) else: new_map.append(0) game(new_map) ...

На тачбаре это выглядит вот так:

Покадровое обновление игровой сцены.

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

Добавление событий

Предпоследний этап — то, ради чего играют в Google-динозаврика, — «паркур по кактусам».

Чтобы добавить прыжок, нужно завести переменную jump — к ней мы будем прибавлять единицу при нажатии на кнопку с динозавром. И модифицировать список map таким образом, чтобы в первом чанке кактус и динозавр могли встретиться несколькими способами.

Динозаврик врезался в кактус — чанк -2. Комбинация [1, -1], jump = 0.

Динозаврик перепрыгнул кактус — чанк 2. Комбинация [1, -1], jump = 1.

Вот как «сценарии» записаны в программе:

# Ситуативная генерация новых кадров. ... jump = 0 # первый элемент — комбинация значений, где второе значение — следующий чанк map = [[1,0], 0, 0, -1, 0, -1, 0] ... while True: # итоговое значение для комбинации: # [1,0] → 1 кактуса нет, динозаврик бежит # [1,-1] → кактус есть: если jump = 1, все хорошо zero_chunk = 1 # записываем комбинацию map[0] = [1,map[1]] # динозаврик не прыгнул на прошлом чанке и врезался if jump == 0 and map[0] == [1, -1]: zero_chunk = -2 # на чанке без кактуса jump обнуляется elif jump >= 1 and map[0] == [1, 0]: jump = 0 zero_chunk = 1 # динозаврик прыгнул на прошлом чанке, все хорошо elif jump == 1 and map[0] == [1,-1]: zero_chunk = 2 points += 1 # настраиваем формат карты для передачи в функцию new_map = map[1:] new_map[0] = zero_chunk if map[-1] == 0: new_map.append(random.choice([0, -1])) else: new_map.append(0) map = new_map game(new_map)

В коде отрисовки сцены также нужно добавить новые события:

# Отрисовка игровой сцены. def game(buttons) ... for b in range(len(map)): # если динозаврик не перепрыгнул, загружаем ассет со столкновением if map[b] == -2: buttons[b] = PyTouchBar.TouchBarItems.Button(image='assets/loss.png', color=(PyTouchBar.Color.black)) # если динозаврик перепрыгнул, загружаем ассет с прыжком elif map[b] == 2: buttons[b] = PyTouchBar.TouchBarItems.Button(image='assets/lucky.png', color=(PyTouchBar.Color.black)) ...

На тачбаре это выглядит вот так:

Итоговый результат.

Выводим количество очков

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

Подсчет очков

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

Вывод сообщения

Чтобы вывести сообщение, нужно создать новое окно Tkinter, подготовить его и добавить текстовое поле.

# PyTouchBar, добавление элемента Label. def finish(points): ... # объявление элемента label label = PyTouchBar.TouchBarItems.Label(text = f'Упс:( Вы набрали: {points}') ...

Элемент Label поддерживает разные шрифты, цвета и масштабы. Полный список параметров есть в официальной документации.

Притом вызвать функцию finish нужно после отрисовки сцены.

# Обработка окончания игры. while True: points = 0 ... # динозаврик не прыгнул на прошлом чанке и врезался if jump == 0 and map[0] == [1, -1]: zero_chunk = -2 elif jump == 1 and map[0] == [1, -1]: zero_chunk = 2 points += 1 ... game(new_map) # если динозаврик врезался — вызвать finish() if zero_chunk == -2: finish(points)

Как закрыть сообщение и программу?

У PyTouchBar есть особенность: с помощью библиотеки можно модифицировать встроенную кнопку escape. Например, сделать ее кнопкой для закрытия программы.

... def exit_f(button): exit() esc = PyTouchBar.TouchBarItems.Button(title = "exit", action = exit_f) PyTouchBar.set_touchbar(… , esc_key = esc) ...

Полная версия кода доступна на GitHub. Подключайтесь и предлагайте свои улучшения.

На что еще способна библиотека?

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

Но Doom с помощью PyTouchBar запустить будет сложно: не понятно, как «вытягивать» отдельные кадры и управлять игрой через панель. Для более сложных проектов лучше «прыгнуть в нору за кроликом» и программировать тачбар с помощью Objective-C.

Напишите в комментариях, какую еще программу или игру можно написать для тачбара. И подпишитесь на блог Selectel, чтобы не пропустить обзоры, новости, кейсы и полезные гайды из мира IT.

Читайте также:

0
13 комментариев
Написать комментарий...
Пётр Загребельный

Вы молодец но опоздали на 5 лет )

Ответить
Развернуть ветку
Mila Mashkova

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

Ответить
Развернуть ветку
Алексей Альев

Мде, игра для тачбара на макбуке... Звучит как интересный опыт, но охват аудитории у такого проекта будет околонулевой

Ответить
Развернуть ветку
Selectel
Автор

А какая программа для тачбара могла бы найти своего пользователя? Поделитесь идеями — необязательно, чтобы это была игра)

Ответить
Развернуть ветку
Даниил

Да уже никакая, отказались от него в новых моделях

Ответить
Развернуть ветку
Министерство Коррупции

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

Ответить
Развернуть ветку
Артурас Лапинскас

Что можно вывести на полоску 0,5 см? ряд кастомных кнопок, больше ничего, для игр нормальный экран же есть, зачем извращаться...

Ответить
Развернуть ветку
Mark Kats

суть-то в опыте. я не делал такого, мне интересно, и сделаю)

Ответить
Развернуть ветку
Владислав Ефименко

Можно, например, вывести Doom, если правильно сделать раскадровку и с высокой частотой обновлять бэкграунд кнопок)

Ответить
Развернуть ветку
Dmitriy Subko

TouchBar требует привыкания. Лично я плевался полгода, пока случайно не узнал, как его можно настроить полностью под себя с помощью утилиты MTMR. Я вывел туда док, запуск ланчпада, локальное и московское время, погоду, нагрузку, заряд аккумулятора, таймер-помидор.

Ответить
Развернуть ветку
Selectel
Автор

Спасибо за рекомендацию!

Ответить
Развернуть ветку

Комментарий удален модератором

Развернуть ветку
Максим Глызин

чет не заводится игра, вообще ни как

Ответить
Развернуть ветку
Selectel
Автор

Напишите, пожалуйста, какую ошибку / исключение выводит Python?

Ответить
Развернуть ветку
Читать все 13 комментариев
null