Электронная игра на базе платы Microbit

Электронная игра на базе платы Microbit0

Было время, когда каждый ребенок СССР хотел портативную электронную игру типа «Ну погоди» или «Автогонки«». Игра сочетала в себе очень простой ЖК-экран с очень простой игровой механикой.
В западных странах тоже были такие электронные игрушки. Выпускались они фирмой Nintendo Game & Watch. Одной из первых была игра Fire, где игрок перемещал страховочную сетку пожарных влево или вправо, чтобы перебросить жителей из горящего здания слева в ожидавшую его машину скорой помощи справа

Свою игру он назвал Post (почта) и в ней нужно перебрасывать письма слева направо с помощью ракетки.

Инструменты и материалы:
-Microbit;
-Коммутационная плата;
-Макетная плата с шинами питания;
-1,3-дюймовый OLED-дисплей 128×64 с поддержкой I²C;
-Тактовые кнопки — 2 шт;
-Перемычки;
-Батарейка 3V;
-Держатель для батарейки;

Электронная игра на базе платы Microbit1

Шаг первый: программирование
Редактор Mu — это редактор для Micro: bit, по сути — MicroPython для Micro: bit. После установки Mu открываем редактор и подключаем Micro: bit к компьютеру. В левом нижнем углу окна редактора Mu, в строке состояния, должно прописаться подтверждение, что он «обнаружил новое устройство BBC micro: bit».
Затем нужно убедиться, что код Python может быть записан на бит Micro :. В редакторе Mu нажимаем кнопку New, чтобы создать новый файл Python, и вводим следующий простой код:
from microbit import *

display.scroll("Hello, World!")
Нажимаем кнопку Flash в редакторе Mu. Желтый светодиод на бите Micro: должен начать мигать, указывая на то, что выполняется файловая операция. Когда операция закончится, Micro: bit должен прокрутить «Hello, World!» на его светодиодном дисплее 5×5.

Электронная игра на базе платы Microbit2

Чтобы Micro: bit отправлял инструкции на OLED-дисплей, нужно установить некоторые библиотеки Python на Micro: bit. Редактор Mu делает это очень просто.
Сначала находим домашний каталог для Mu.
Далее копируем все файлы Python (все файлы с расширением .py) из репозитория fizban на Github и помещаем копии в домашний каталог Mu.
В редакторе Mu нажимаем кнопку Files. Это действие откроет две панели внизу редактора. Слева находятся файлы на Micro: bit — может быть один или несколько файлов, в зависимости от того, что вы делалось ранее с Micro: bit. Справа находятся файлы .py в домашнем каталоге, в том числе файлы ssd1306, которые были скопированы из репозитория fizban.
Перетаскиваем следующие три файла с правой панели ( домашний каталог) на левую панель (бит Micro:), которая ssd1306.py
ssd1306_px.py
ssd1306_text.py
Для этого проекта нам нужны только эти три файла, остальные могут понадобиться в других проектах.
Нажимаем кнопку «Files» еще раз, чтобы удалить нижние панели и снова сделать кнопку «Flash» доступной.
Чтобы узнать, все ли по-прежнему работает нормально, нужно обновить программу Hello.
from ssd1306 import initialize, clear_oled, draw_screen
from ssd1306_px import set_px
from ssd1306_text import add_text
from microbit import *

display.scroll("Import Successful!")
Загружаем код на Micro: bit. Если на экране прокручивается “Import Successful!”, можно перейти к следующему шагу.

Электронная игра на базе платы Microbit3

Шаг второй: подключение коммутационной платы
Внизу Micro: bit находятся 25 контактов (золотая полоска). Пять из них имеют размер, чтобы их можно было использовать с зажимами. Чтобы использовать остальные 20 контактов, нужно вставить Micro: bit в коммутационную плату. Для данного проекта OLED нужна коммутационная плата, которая обеспечивает доступ к контактам 19 и 20, поскольку это контакты, которые реализуют протокол связи I²C.
Можно использовать простую программу Python, чтобы проверить, работает ли коммутационная плата. Ниже представлен код, который просто показывает, была ли нажата кнопка A или кнопка B на бите Micro :.

Электронная игра на базе платы Microbit4

 Показать / Скрыть текстfrom ssd1306 import initialize, clear_oled, draw_screen
from ssd1306_px import set_px
from ssd1306_text import add_text
from microbit import *

while True:
display.show('.')
if button_a.was_pressed():
display.show('A')
sleep(500)
elif button_b.was_pressed():
display.show('B')
sleep(500)
Нужно загрузить эту программу в Micro: bit, прежде чем подключать ее к коммутационной плате. При нажатии кнопки A на светодиодном дисплее должно отображается «A», а при нажатии кнопки B на светодиодном дисплее отображается «B».
Дальше устанавливаем Micro: бит в коммутационную плату. В зависимости от платы может загореться светодиод, показывая, что она подключена и получает питание от Micro: bit.

Если мы посмотрим на схему контактов Micro: bit , то увидим, что контакт 5 используется совместно с кнопкой A на Micro: bit, а контакт 11 используется совместно с кнопкой B. Чтобы проверить, работает ли коммутационная плата, вставляем одну перемычку к контакту 5, одну перемычку к контакту 11 и одну перемычку к GND. Если теперь прикоснутся к контакту 5 и проводам GND, мы увидим «A» на светодиодном дисплее, потому что контакт 5 был «сброшен на GND», что аналогично нажатию кнопки A. Прикосновение к контакту 11 и GND покажут на светодиодном дисплее букву B. Это значит, что Micro: bit и коммутационная плата подключены правильно.

Электронная игра на базе платы Microbit5

Шаг третий: макетная плата
Хотя этот проект можно было бы реализовать без макетной платы, но ее использование упрощает монтаж.

Электронная игра на базе платы Microbit6

Для большинства проектов Micro: bit питание обеспечивается либо через USB-соединение с компьютером, либо, в качестве альтернативы, путем подключения батареи 3 В к разъему JST.

Для проекта, в котором плата Micro: bit работает с внешними компонентами, коммутационная плата может не обеспечивать достаточное количество соединений 3 В и GND для питания всех внешних компонентов. Можно решить эту проблему, подключив контакт 3V и контакт заземления (GND) к шинам питания макета. Сделать это просто.
-С помощью перемычки подключаем 3 В на коммутационной плате к + силовой шине на макетной плате.
-С помощью перемычки соединяем GND на коммутационной плате с шиной питания на макетной плате.

Электронная игра на базе платы Microbit7

Теперь есть коммутационная плата и макет, позволяющие подключать электронные компоненты к Micro: bit, сначала подключим наш OLED-дисплей.

Устанавливаем OLED-дисплей на макетную плату так, чтобы каждый из его контактов был подключен к отдельной пронумерованной строке. Например, вывод GND подключен к строке 10, а соседний вывод VCC подключен к строке 11 и так далее. Если все контакты OLED находятся в одном пронумерованном ряду, OLED вставлен неправильно и его необходимо повернуть на 90 градусов.
Затем нужно использовать перемычки для подключения каждого вывода OLED к определенным выводам Micro: bit на коммутационной плате:
OLED GND к Micro: бит GND
OLED VCC к Micro: бит 3 В
OLED SCL к Micro: бит, вывод 19
OLED SDA к Micro: бит, вывод 20
Дальше нужно загрузить следующий код Python на Micro: bit. Если все компоненты и все соединения работают, на дисплее будет отображаться “Hello!”.
from ssd1306 import initialize, clear_oled, draw_screen
from ssd1306_px import set_px
from ssd1306_text import add_text
from microbit import *

initialize()
clear_oled()
add_text(0, 0, "Hello!")
Дальше можно загрузить небольшую программу для проверки отображения пикселей. Хотя OLED имеет разрешение дисплея 128 на 64 пикселя, каждый пиксель крошечный, поэтому библиотека Python ssd1306 использует четыре пикселя каждый раз, когда получает указание рисовать «пиксель» на дисплее. То есть только сетка 64×32. Вот почему в игре такая рудиментарная графика. (Тем не менее, он намного лучше, чем светодиодный дисплей 5×5, который есть у Micro: bit.)
from ssd1306 import initialize, clear_oled, draw_screen
from ssd1306_px import set_px
from ssd1306_text import add_text
from microbit import *

initialize()
clear_oled()
for y in range(31):
for x in range(63):
set_px(x, y, 1, 0)
draw_screen()

Электронная игра на базе платы Microbit8

Дальше переходим к кнопкам. Здесь можно использовать отдельные тактовые кнопки или кнопки на плате Micro: bit. Тактовые кнопки сделают игру более естественной, потому что их можно разместить по обе стороны от OLED-дисплея.
Располагаем каждую кнопку на макетной плате так, чтобы две ножки находились ниже среднего разделителя, а две ножки — выше среднего разделителя. Ножки под средним разделителем не имеют электрического контакта — они для того, чтобы кнопка аккуратно стояла на плате. Ножки над перегородкой подключаются следующим образом:
Для левой кнопки подключаем одну ногу к контакту 5, а другую — к GND;
Для правой кнопки подключаем одну ногу к контакту 11, а другую — к GND.
Если нажата левая кнопка, она закрывает соединение между контактом 5 и GND, что сигнализирует биту Micro: о том, что кнопка A была нажата. При нажатии правой кнопки соединение между контактом 11 и GND замыкается, что сигнализирует биту Micro: о том, что кнопка B была нажата.
Для тестирования используем следующий код.

Электронная игра на базе платы Microbit9

 Показать / Скрыть текстfrom ssd1306 import initialize, clear_oled, draw_screen
from ssd1306_px import set_px
from ssd1306_text import add_text
from microbit import *

initialize()

while True:
clear_oled()
add_text(0, 0, "…")
if button_a.was_pressed():
clear_oled()
add_text(0, 0, "LEFT button")
add_text(0, 1, "was pressed")
elif button_b.was_pressed():
clear_oled()
add_text(0, 0, "RIGHT button")
add_text(0, 1, "was pressed")
sleep(1000)

Электронная игра на базе платы Microbit10

Шаг четвертый: код
Изначально на экране отображаются два конверта, которые необходимо ловить. Конверты падают с определенной скорость. По мере игры, интервал между падением конвертов уменьшается и наступает момент, когда на экране находятся три конверта одновременно.
За каждый пойманный конверт начисляются очки, если пропустить три конверта, то игра заканчивается.

Электронная игра на базе платы Microbit11

Изначально предполагалось, что игра должна иметь гораздо более богатую графику. Какой бы объект ни отскакивал слева направо по экрану, он менял бы форму в каждой точке траектории — точно так же, как в оригинальных играх Nintendo Game & Watch.
Реализация такой графики усложнила код и в итоге мастер решил оставить одну форму.

Электронная игра на базе платы Microbit12

Полный код можно скачать ниже.

Электронная игра на базе платы Microbit13

 Показать / Скрыть текстimport math
import music
import random
from ssd1306 import initialize, clear_oled, draw_screen
from ssd1306_px import set_px
from ssd1306_text import add_text
from microbit import reset, button_a, button_b, display, sleep, i2c, Image

version = 0 # set to positive to show on title screen
limitX = 63 # the last visible pixel location at the right
limitY = 31 # the last visible pixel location at the bottom
letterW = 12 # the width of the letter sprite
letterH = 8 # the height of the letter sprite
delay = 150 # the delay after each play loop … slowly decreases
min_delay = 40 # the minimum delay after each play loop
paddle = 2 # the position of the paddle, either 0, 1, or 2
fails = 0 # the number of times the letter fell to the floor
score = 0 # the number of times the paddle hit a letter
chance = 40 # the 'range' of chances to launch a letter
traj = [] # the list of trajectory positions
bottoms = [] # the index of each bottom-of-trajectory positions
letter = "" # will hold the binary values for the letter sprite
lett_pos = [] # the list of positions of the three letters
titleI = 0 # Y position index of bouncing letter on title screen

# Expands each hex value into a single array of bits.
# Example: expand "909" into array of 100100001001
def hex_to_bits(definition):
bits = []
for line in definition:
for hex in line:
for char in '{:0>{w}}'.format(bin(int(hex, 16))[2:], w=4):
bit = 1 if char == '1' else 0
bits.append( bit )
return bits

# Shows or hides the letter sprite.
def update_sprite(sprite, x, y, width, height, visible):
for offy in range(height):
for offx in range(width):
if sprite[offy*letterW + offx] == 1:
if x+offx <= limitX and y+offy <= limitY:
set_px(x+offx, y+offy, visible, 0)

# Shows or hides the paddle.
def update_paddle(index, visible):
x = [ 8, 28, 48 ][index]
y = limitY — 6
for offx in range(letterW+4):
set_px(x + offx, y, visible, 0)
set_px(x + offx, y+1, visible, 0)

# Builds the trajectory of bounces, and records bottom positions
# (where the player's paddle will need to be).
def build_traj():
global traj, bottoms, traj_count
append_bounce(-4)
bottoms.append(len(traj)-1)
append_bounce(6)
bottoms.append(len(traj)-1)
append_bounce(16)
bottoms.append(len(traj)-1)
append_bounce(26)
traj_count = len(traj)

# Adds an up and down arc to the trajectory.
def append_bounce(x):
if x >= 0:
traj.extend( [ [x, 10], [x+1, 6], [x+2, 2] ] )
traj.extend( [ [x+4, 0], [x+6, 2], [x+7, 6], [x+8, 10], [x+9, 16] ] )

# To launch a new letter, the following must be true:
# 1) The letters at pos1 and pos2 are either:
# a) not launched, or
# b) are at least 8 steps away, and not at the top of the arc
# (so no two letters at the same pos in the arc)
# 2) The number 2 is chosen randomly from within a range.
# The range decreases from 40 to 3, so the game starts launching
# letters slowly, a later launches letters more often.
def can_launch(pos1, pos2):
global chance
if is_away(pos1) and is_away(pos2):
if chance > 3.2:
chance = chance — .2
return random.randrange(0, math.floor(chance)) == 2
return False

# Determines if the position means another letter can be launched.
def is_away(pos):
if pos < 0:
return True
if pos >= 100:
return False
if pos > 8:
return traj[pos][1] != 0 # not at top of bounce
return False

# Shows or hides the title screen
def set_title(state):
title = "POST"
left = 2
if version > 0:
title = title + " v." + str(version)
left = 0
add_text(left, 2, title)
update_paddle(2, 1)

def update_title(y):
bounce = [16,10,6,2,0,2,6,10]
last = y-1
if last < 0:
last = len(bounce) — 1
update_sprite(letter, 50, bounce[last], letterW, letterH, 0)
update_sprite(letter, 50, bounce[y], letterW, letterH, 1)
next = y + 1
if next > len(bounce) — 1:
return 0
return next

# Shows or hides the message to "Get ready".
def set_ready(state):
if state == 1:
add_text(2, 0, "Get ready")
else:
for x in range(64):
for y in range(10):
set_px(x, y, 0, 0)

# Moves the player's paddle either left or right.
def move_paddle(offset):
global paddle
update_paddle(paddle, 0)
paddle = paddle + offset
update_paddle(paddle, 1)

# Called when the player misses a letter.
def on_fail():
global fails
music.pitch(200, 500, wait=False)
fails = fails + 1
show_fails(fails)
if fails >= 3:
game_over()
return True
return False

# Called when the player has missed three letters.
def game_over():
clear_oled()
add_text(2, 0, "GAME OVER!")
add_text(2, 2, "Score: " + str(score))

# Called when a letter hits the player's paddle.
def on_success():
global score, delay
score = score + 1
if delay > min_delay:
delay = delay — 2
music.stop()
music.pitch(600, 1)

# Uses Microbit's LED to show the number of fails.
def show_fails(count):
leds = "00000:00000:0"
for i in range (count):
leds = leds + "9"
for i in range (count, 3):
leds = leds + "0"
leds = leds + "0:00000:00000"
display.show(Image(leds))

# Shows a dotted line when the player misses a letter
def show_splat(pos):
x = traj[pos-100][0] * 2
for i in range (0, letterW, 2):
set_px(x+i, limitY, 1, 0)

# Removes any dotted lines indicating a missed letter
def hide_splats():
for i in range (64):
set_px(i, limitY, 0, 0)

# Moves the letter to the next position in the trajectory
def move_letter(index):
global lett_pos
index1 = get_index(index, -1)
index2 = get_index(index, 1)
if lett_pos[index] >= 0 and lett_pos[index] < 100:
lett_pos[index] = get_next(lett_pos[index])

# Gets the next position in the trajectory.
def get_next(t):
if t >= 0 and t < traj_count-1:
return t + 1
return -1

# If the letter is not in a trajectory, it may be launched
def launch_letter(index):
global lett_pos, started
index1 = get_index(index, -1)
index2 = get_index(index, 1)
if lett_pos[index] < 0 or lett_pos[index] >= 100:
if can_launch(lett_pos[index1], lett_pos[index2]):
lett_pos[index] = 0
if not started:
set_ready(0)
started = True

# Gets the index of the previous or next letter.
def get_index(index, offset):
index = index + offset
if index < 0:
return 2
if index > 2:
return 0
return index

# Restarts the program, returning to the title screen
def restart():
a = button_a.was_pressed()
b = button_b.was_pressed()
sleep(200)
reset()

# Initialize the game
letter = hex_to_bits( "fff:a05:909:891:861:801:801:fff".split(':'))
lett_pos = [ -1, -1, -1 ]
build_traj()
initialize()
clear_oled()
set_title(1)
opened = False # has the user pressed a button on the title screen?
started = False # has the game started? (used to clear "Get ready" prompt)
playing = True # is the game underway; if not, the game has finished
move_letters = False # do we move the letters in the current game turn?

# Show the title screen
while not opened:
titleI = update_title(titleI)
draw_screen()
sleep(delay)
if button_a.was_pressed() or button_b.was_pressed():
opened = True
clear_oled()
set_ready(1)
update_paddle(paddle, 1)
draw_screen()

# Start playing the game, where the player can move the paddle each turn,
# whereas the letters move every second turn. (The paddle must move twice
# as fast as the letters, so the player can save every letter.)
while playing:
refresh = False # whether a screen redraw is needed

if button_a.was_pressed():
if paddle > 0:
move_paddle(-1)
refresh = True;
elif button_b.was_pressed():
if paddle < 2:
move_paddle(1)
refresh = True;

if move_letters:
for i in range(len(lett_pos)):
pos = lett_pos[i]
if pos >= 100: # indicates that the letter was missed by the player
lett_pos[i] = -1 # available to be launched again later
update_sprite(letter, traj[pos-100][0] * 2, traj[pos-100][1], letterW, letterH, 0)
refresh = True
if on_fail():
playing = False
refresh = False
else:
show_splat(pos)
else:
if pos > 0: # the sprite needs to be 'removed' from its last position
update_sprite(letter, traj[pos-1][0] * 2, traj[pos-1][1], letterW, letterH, 0)
refresh = True
if pos >= 0: # the sprite needs to be drawn on the screen
refresh = True
update_sprite(letter, traj[pos][0] * 2, traj[pos][1], letterW, letterH, 1)
if pos in bottoms: # is the letter at the bottom?
if bottoms.index(pos) == paddle: # is the paddle at the same position?
on_success()
else:
lett_pos[i] = 100 + pos # player missed the letter

if playing:

if refresh:
draw_screen()

if move_letters:
move_letter(0)
move_letter(1)
move_letter(2)
launch_letter(0)
launch_letter(1)
launch_letter(2)

sleep(delay)

if not move_letters:
hide_splats()
draw_screen()

move_letters = move_letters == False

else:

while not button_a.was_pressed() and not button_b.was_pressed():
sleep(150)
restart()
microbit-post-game.py

Электронная игра на базе платы Microbit14

Шаг пятый: печатная плата и корпус
Макетная плата позволяет очень легко монтировать и тестировать схему, но она не идеальна для готового продукта. Чтобы получить меньший форм-фактор с большей стабильностью, можно сделать схему на макете печатной платы (PCB).
Для этого необходимо припаять компоненты и провода к плате. На изображении ниже, слева показана исходная макетная версия, а справа — версия печатной платы.

Электронная игра на базе платы Microbit15

Последнее что нужно сделать — это корпус. Для корпуса можно приспособить любую подходящую коробку или сделать ее самому.

Электронная игра на базе платы Microbit16

Источник

Если вам понравилась статья, поделитесь ей в соц.сетях !