Skip to content

Instantly share code, notes, and snippets.

@samneggs
Last active June 8, 2023 21:27
Show Gist options
  • Save samneggs/c3dc7710438549c1fca93943f48d0ab3 to your computer and use it in GitHub Desktop.
Save samneggs/c3dc7710438549c1fca93943f48d0ab3 to your computer and use it in GitHub Desktop.
Breakout game on 3.5" display in MicroPython
from LCD_3inch5 import LCD_3inch5
import framebuf, gc, _thread
from micropython import const
from machine import Timer, UART, Pin
#constants
BRICK_WIDTH = const(20)
BRICK_HEIGHT = const(10)
BALL_SIZE = const(5)
BALL_X_START = const(390)
BALL_Y_START = const(160)
THREADED = const(0)
SOUNDS = const(1)
DEMO = const(0)
USE_UART = const(0)
# 7 Segment Chars
# Tony Goodhew 14th July 2021
nums =[0,1,1,1,1,1,1, # 0 # One row per digit
0,1,1,0,0,0,0, # 1
1,1,0,1,1,0,1, # 2
1,1,1,1,0,0,1, # 3
1,1,1,0,0,1,0, # 4
1,0,1,1,0,1,1, # 5
1,0,1,1,1,1,1, # 6
0,1,1,0,0,0,1, # 7
1,1,1,1,1,1,1, # 8
1,1,1,0,0,1,1, # 9
1,1,1,1,1,0,1, # a = 10 - HEX characters
0,0,1,1,1,1,1, # b = 11
0,0,0,1,1,0,1, # c = 12
0,1,1,1,1,0,1, # d = 13
1,1,0,1,1,1,1, # e = 14
1,0,0,0,1,1,1, # f = 15
1,1,1,1,0,1,1, # g needed for seg!
0,0,0,0,0,0,1, # -
0,0,0,0,0,0,0] # Blank
LCD = LCD_3inch5(200)
LCD.bl_ctrl(100)
LCD.FillRectangle(0,0, 480,320, LCD.BLACK)
colors = [LCD.RED,LCD.ORANGE,LCD.GREEN,LCD.YELLOW]
scoring = [7,5,3,1]
C = [LCD.BLACK,LCD.WHITE]
Buzz = machine.PWM(machine.Pin(14))
class Circle():
def __init__(self,x,y,w,h,buf):
self.x = x
self.y = y
self.ax = -1
self.ay = -1
self.w = w
self.h = h
self.buf = buf
class Brick():
def __init__(self,row,col,x,y,color,points):
self.row = row
self.col = col
self.x = x
self.y = y
self.color = color
self.points = points
class Score():
def __init__(self):
self.lives = 3
self.score = 0
self.bricks = 0
self.show = False
class Paddle():
def __init__(self):
self.x = 400
self.y = 160
self.ay = 1
self.oldy = 0
self.w = 40
self.h = 5
self.touchpos = 160
self.buf = 0
self.show = False
def init_paddle(self):
width = 5
height = self.w+10
display_buffer = bytearray(width * height * 2)
self.buf = framebuf.FrameBuffer(display_buffer,width,height, framebuf.RGB565)
self.buf.fill_rect(0,5,width,paddle.w,LCD.WHITE)
class Beep():
def __init__(self):
self.brick = False
self.wall = False
self.paddle = False
self.miss = False
class Uart:
def __init__(self,num,tx_pin,rx_pin):
self.uart = UART(num,baudrate=115200,tx=Pin(tx_pin),rx=Pin(rx_pin))
self.rx_buf = bytearray(6)
self.tx_buf = bytearray(6)
self.x = 0
self.y = 0
self.left_pushed = 0
self.right_pushed = 0
self.pot_raw = 0
self.pot_old = 0
def check(self):
if self.uart.any() != False:
self.uart.readinto(self.rx_buf)
print(self.rx_buf)
self.x = self.rx_buf[0]-self.rx_buf[1]
self.y = self.rx_buf[2]-self.rx_buf[3]
self.left_pushed = self.rx_buf[4] == 1
self.right_pushed = self.rx_buf[4] == 2
self.pot_raw = self.rx_buf[5]
print(self.pot_raw )
def send(self):
if joystick.x>0:
self.tx_buf[0] = joystick.x
else:
self.tx_buf[0] = 0
if joystick.x<0:
self.tx_buf[1] = abs(joystick.x)
else:
self.tx_buf[1] = 0
if joystick.y>0:
self.tx_buf[2] = joystick.y
else:
self.tx_buf[2] = 0
if joystick.y<0:
self.tx_buf[3] = abs(joystick.y)
else:
self.tx_buf[3] = 0
self.tx_buf[4] = joystick.left_pushed|joystick.right_pushed<<1
self.uart.write(self.tx_buf)
def seg(xx,yy,n,f): #,bg,fg):
global C
# (x, y, number, size-factor, background, foreground)
#c = [bg,fg]
p = n * 7
LCD.FillRectangle(xx+0*f,yy+1*f,1*f,3*f,C[nums[p+6]])
LCD.FillRectangle(xx+1*f,yy+4*f,3*f,1*f,C[nums[p+5]])
LCD.FillRectangle(xx+5*f,yy+4*f,3*f,1*f,C[nums[p+4]])
LCD.FillRectangle(xx+8*f,yy+1*f,1*f,3*f,C[nums[p+3]])
LCD.FillRectangle(xx+5*f,yy+0*f,3*f,1*f,C[nums[p+2]])
LCD.FillRectangle(xx+1*f,yy+0*f,3*f,1*f,C[nums[p+1]])
LCD.FillRectangle(xx+4*f,yy+1*f,1*f,3*f,C[nums[p]])
# 7 seg test
#for i in range(0,10):
# seg(400,290-i*30,i,5,LCD.BLACK,LCD.WHITE) # (x, y, number, size-factor, background, foreground)
def show_score():
score.show = False
num = score.score
#num = 123
places = 0
if num==0:
seg(420,200,0,5) #,LCD.BLACK,LCD.WHITE)
while num>0:
digit = num % 10
ypos = 200 + places
#draw_object2(score_list[digit],False)
seg(420,ypos,digit,5) #,LCD.BLACK,LCD.WHITE)
num //= 10
places += 30
def show_lives():
seg(420,50,score.lives,5) #,LCD.BLACK,LCD.WHITE)
def play_beep(freq,dur=0):
Buzz.freq(freq)
Buzz.duty_u16(2512)
for i in range(3000+dur):
pass
for i in range(2512,0,-1):
Buzz.duty_u16(i)
Buzz.duty_u16(0)
def fill_circle(obj, x0, y0, radius, color):
# From Adafruit GFX Arduino library
# Filled circle drawing function. Will draw a filled circule with
# center at x0, y0 and the specified radius.
obj.vline(x0, y0 - radius, 2*radius + 1, color )
f = 1 - radius
ddF_x = 1
ddF_y = -2 * radius
x = 0
y = radius
while x < y:
if f >= 0:
y -= 1
ddF_y += 2
f += ddF_y
x += 1
ddF_x += 2
f += ddF_x
obj.vline(x0 + x, y0 - y, 2*y + 1, color)
obj.vline(x0 + y, y0 - x, 2*x + 1, color)
obj.vline(x0 - x, y0 - y, 2*y + 1, color)
obj.vline(x0 - y, y0 - x, 2*x + 1, color)
return obj
def init_ball(radius):
width = radius * 2
height = radius *2
display_buffer = bytearray(width * height * 2)
ball_buffer=framebuf.FrameBuffer(display_buffer,width,height, framebuf.RGB565)
ball_buffer = fill_circle(ball_buffer,radius,radius,radius-2,LCD.WHITE)
return ball_buffer
def show_ball(obj):
if not score.show or not THREADED:
paddle.show = True
#LCD.FillRectangle(ball.x,ball.y,ball.h,ball.w,LCD.BLACK)
LCD.show_xy(obj.x,obj.y,obj.x+obj.w,obj.y+obj.h,obj.buf)
paddle.show = False
def move_ball(obj):
obj.x+=obj.ax
obj.y+=obj.ay
if obj.x > 400:
obj.x = 400
obj.ax = -obj.ax
if not THREADED:
play_beep(250,9000)
else:
beep.miss = True
miss()
return
if obj.x < 5:
obj.x = 5
obj.ax = -obj.ax
beep.wall = True
if obj.y > 310:
obj.y = 310
obj.ay = -obj.ay
beep.wall = True
if obj.y < 5:
obj.y = 5
obj.ay = -obj.ay
beep.wall = True
if obj.x+BALL_SIZE*2 > paddle.x and obj.x < paddle.x+paddle.h and obj.y>paddle.y and obj.y<paddle.y+paddle.w: # hit paddle
#LCD.FillRectangle(paddle.x,paddle.y,paddle.h,paddle.w,LCD.BLUE)
#LCD.FillRectangle(obj.x,obj.y,obj.h,obj.w,LCD.GREEN)
#exit()
obj.x = paddle.x-BALL_SIZE*2
obj.ax = -obj.ax
beep.paddle = True
if obj.x < 90:
for i in wall:
if i.color != LCD.BLACK:
brick_x1=i.x #(BRICK_HEIGHT+2)*i.col-10
brick_x2=i.x+BRICK_HEIGHT #(BRICK_HEIGHT+2)*i.col+10
brick_y1=i.y #(BRICK_WIDTH+2)*i.row-12
brick_y2=i.y+BRICK_WIDTH #(BRICK_WIDTH+2)*i.row+12
if obj.x < brick_x2 and obj.x+BALL_SIZE*2 > brick_x1 and obj.y < brick_y2 and obj.y+BALL_SIZE*2 > brick_y1:
#LCD.FillRectangle(brick_x1,brick_y1, BRICK_HEIGHT,BRICK_WIDTH, LCD.BLUE)
#LCD.FillRectangle(obj.x,obj.y, BALL_SIZE*2,BALL_SIZE*2, LCD.BLUE)
beep.brick = True
i.color = LCD.BLACK
score.bricks += 1
collx=obj.x-brick_x1 #i.col*BRICK_HEIGHT
colly=obj.y-brick_y1 - BALL_SIZE #i.row*BRICK_WIDTH
# print('Ball=',obj.x,obj.y)
# print('Brick=',i.x,i.
# print('Bricks=',score.bricks)
# print('coll x,y',collx,colly)
score.score+=i.points
if score.bricks==112:
reset_game()
return
#debug_coll(brick_x1,brick_y1,collx,colly)
if THREADED:
_thread.exit()
#exit()
LCD.FillRectangle(ball.x,ball.y,ball.h,ball.w,LCD.BLACK) # clear ball dropings
if abs(collx)>=abs(colly): # up/down collision
obj.ax=-obj.ax
if collx>=0:
obj.x=brick_x2
else:
obj.x=brick_x1
else: # left/right collision
obj.ay=-obj.ay
if colly>=0:
#debug_coll(brick_x1,brick_y1)
obj.y=brick_y2
else:
obj.y=brick_y1
#print(gc.mem_free())
LCD.FillRectangle(i.col*(BRICK_HEIGHT+2),i.row*(BRICK_WIDTH+2), BRICK_HEIGHT,BRICK_WIDTH, i.color)
score.show = True
if not THREADED:
pass
#show_score()
#LCD.FillRectangle(ball.x,ball.y,ball.h,ball.w,LCD.BLACK) # clear ball dropings
#show_ball(ball)
return
def miss():
LCD.FillRectangle(ball.x,ball.y,ball.h,ball.w,LCD.BLACK) # clear ball dropings
score.lives-=1
show_lives()
reset_ball()
if score.lives == 0:
score.lives = 3
score.score = 0
reset_game()
def debug_coll(brick_x1,brick_y1,collx,colly):
LCD.FillRectangle(paddle.x,paddle.y,paddle.h,paddle.w,LCD.BLUE)
LCD.FillRectangle(ball.x,ball.y,ball.h,ball.w,LCD.GREEN)
LCD.FillRectangle(brick_x1,brick_y1, BRICK_HEIGHT,BRICK_WIDTH, LCD.BLUE)
print(collx,colly)
exit()
def init_wall():
bricks = []
i=0
for row in range(0,8,2):
for col in range(0,14):
x=row*(BRICK_HEIGHT+2)
y=col*(BRICK_WIDTH+2)
bricks.append(Brick(col,row,x,y,colors[row//2],scoring[row//2]))
x=(row+1)*(BRICK_HEIGHT+2)
bricks.append(Brick(col,row+1,x,y,colors[row//2],scoring[row//2]))
i+=2
return (bricks)
def reset_wall():
i=0
for row in range(0,8,2):
for col in range(0,14):
wall[i].color = colors[row//2]
wall[i+1].color = colors[row//2]
i+=2
def reset_ball():
ball.x = BALL_X_START
ball.y = BALL_Y_START
ball.ax = -1
ball.ay = -1
def reset_game():
LCD.FillRectangle(0,0, 480,320, LCD.BLACK)
reset_wall()
show_wall()
reset_ball()
show_ball(ball)
score.bricks = 0
show_lives()
show_score()
LCD.show_xy(paddle.x,paddle.y,paddle.x+paddle.h,paddle.y+paddle.w,paddle.buf)
def show_wall():
for i in wall:
LCD.FillRectangle(i.x,i.y, BRICK_HEIGHT,BRICK_WIDTH, i.color)
def get_paddle():
get = LCD.touch_get()
#print(get)
if get != None:
paddle.touchpos = 290-int((get-430)*320//3270)
def move_paddle():
if paddle.y < paddle.touchpos+5:
paddle.y += paddle.ay
LCD.show_xy(paddle.x,paddle.y,paddle.x+paddle.h,paddle.y+paddle.w,paddle.buf)
elif paddle.y > paddle.touchpos+5:
paddle.y -=paddle.ay
LCD.show_xy(paddle.x,paddle.y,paddle.x+paddle.h,paddle.y+paddle.w,paddle.buf)
if paddle.y != paddle.touchpos:
pass
#LCD.FillRectangle(470,paddle.y, 20,5, LCD.WHITE)
#LCD.show_xy(paddle.x,paddle.y,paddle.x+paddle.h,paddle.y+paddle.w,paddle.buf)
def check_sound():
if beep.brick:
beep.brick = False
play_beep(2600)
if beep.wall:
beep.wall = False
play_beep(1000)
if beep.paddle:
beep.paddle = False
play_beep(500)
if beep.miss:
beep.miss = False
play_beep(250,9000)
def second_thread(): # sound
while(1):
check_sound()
if score.show and not paddle.show and 0 :
show_ball(ball)
score.show = False
show_score()
uart0 = Uart(0,0,1)
beep=Beep()
paddle = Paddle()
paddle.init_paddle()
score = Score()
wall = init_wall()
ball=Circle(BALL_X_START,BALL_Y_START,BALL_SIZE*2-1,BALL_SIZE*2-1,init_ball(BALL_SIZE))
reset_game()
def main(tim):
if THREADED:
wdt.feed()
elif SOUNDS:
check_sound()
if score.show==True and ball.x>90:
show_score()
if ball.x % 40 == 0 and not DEMO:
if USE_UART:
uart0.check()
paddle.y+=uart0.y
else:
get_paddle()
#print(uart0.y)
if DEMO:
paddle.y=ball.y-20
move_paddle()
move_ball(ball)
show_ball(ball)
try:
tim=Timer()
tim.init(mode=Timer.PERIODIC, freq=150, callback=main)
while(1):
pass
except KeyboardInterrupt:
LCD.bl_ctrl(0)
tim.deinit()
if THREADED:
gc.collect()
print('core 1')
#a_lock = _thread.allocate_lock()
wdt=machine.WDT(id=0, timeout=8000)
_thread.start_new_thread(second_thread, ())
scan = 0
while(0):
scan+=1
if THREADED:
wdt.feed()
elif SOUNDS:
check_sound()
if score.show==True and ball.x>90:
show_score()
if scan>49:
get_paddle()
move_paddle()
if scan >45 or ball.x<100:
move_ball(ball)
show_ball(ball)
#print(gc.mem_free())
if scan==50:
scan = 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment