-
Notifications
You must be signed in to change notification settings - Fork 8
/
PicoGameBoy.py
196 lines (166 loc) · 6.47 KB
/
PicoGameBoy.py
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
# PicoGameBoy.py by Vincent Mistler for YouMakeTech
# A class to easily write games for the Raspberry Pi Pico Game Boy
from machine import Pin, PWM
from framebuf import FrameBuffer, RGB565
from st7789 import ST7789
from time import sleep
class PicoGameBoy(ST7789):
def __init__(self):
self.__up = Pin(2, Pin.IN, Pin.PULL_UP)
self.__down = Pin(3, Pin.IN, Pin.PULL_UP)
self.__left = Pin(4, Pin.IN, Pin.PULL_UP)
self.__right = Pin(5, Pin.IN, Pin.PULL_UP)
self.__button_A = Pin(6, Pin.IN, Pin.PULL_UP)
self.__button_B = Pin(7, Pin.IN, Pin.PULL_UP)
self.__buzzer = PWM(Pin(15))
super().__init__(width=240, height=240, id_=0, sck=18, mosi=19,
dc=20, rst=21, cs=17, bl=22, baudrate=62500000)
self.__fb=[] # Array of FrameBuffer objects for sprites
self.__w=[]
self.__h=[]
# center_text(s,color) displays a text in the middle of
# the screen with the specified color
def center_text(self, s, color = 1):
x = int(self.width/2)- int(len(s)/2 * 8)
y = int(self.height/2) - 8
self.text(s, x, y, color)
# center_text(s,color) displays a text in the right corner of
# the screen with the specified color
def top_right_corner_text(self, s, color = 1):
x = self.width - int(len(s) * 8)
y = 0
self.text(s, x, y, color)
# add_sprite(buffer,w,h) creates a new sprite from framebuffer
# with a width of w and a height of h
# The first sprite is #0 and can be displayed by sprite(0,x,y)
def add_sprite(self, buffer, w, h):
fb = FrameBuffer(buffer, w, h, RGB565)
self.__fb.append(fb)
self.__w.append(w)
self.__h.append(h)
# add_rect_sprite(color,w,h) creates a new rectangular sprite
# with the specified color, width and height
def add_rect_sprite(self, color, w, h):
buffer = bytearray(w * h * 2) # 2 bytes per pixel
# fill the buffer with the specified color
lsb = (color & 0b0000000011111111)
msb = (color & 0b1111111100000000) >> 8
for i in range(0,w*h*2,2):
buffer[i] = lsb
buffer[i+1] = msb
fb = FrameBuffer(buffer, w, h, RGB565)
self.__fb.append(fb)
self.__w.append(w)
self.__h.append(h)
# sprite(n,x,y) displays the nth sprite at coordinates (x,y)
# the sprite must be created first by method add_sprite
def sprite(self, n, x, y):
self.blit(self.__fb[n], x, y)
# sprite_width(n) returns the width of the nth sprite in pixels
def sprite_width(self,n):
return self.__w[n]
# sprite_height(n) returns the height of the nth sprite in pixels
def sprite_height(self,n):
return self.__h[n]
# button_up() returns True when the player presses the up button
def button_up(self):
return self.__up.value()==0
# button_down() returns True when the player presses the down button
def button_down(self):
return self.__down.value()==0
# button_left() returns True when the player presses the left button
def button_left(self):
return self.__left.value()==0
# button_right() returns True when the player presses the right button
def button_right(self):
return self.__right.value()==0
# button_A() returns True when the player presses the A button
def button_A(self):
return self.__button_A.value()==0
# button_B() returns True when the player presses the B button
def button_B(self):
return self.__button_B.value()==0
# any_button() returns True if any button is pressed
def any_button(self):
button_pressed=False
if self.button_up():
button_pressed = True
if self.button_down():
button_pressed = True
if self.button_left():
button_pressed = True
if self.button_right():
button_pressed = True
if self.button_A():
button_pressed = True
if self.button_B():
button_pressed = True
return button_pressed
# sound(freq) makes a sound at the selected frequency in Hz
# call sound(0) to stop playing the sound
def sound(self, freq, duty_u16 = 2000):
if freq>0:
self.__buzzer.freq(freq)
self.__buzzer.duty_u16(duty_u16)
else:
self.__buzzer.duty_u16(0)
if __name__ == "__main__":
pgb=PicoGameBoy()
# Colors (RGB)
# Note that color is a static method and can be called
# without creating an object (PicoGameBoy.color instead of pgb.color)
BLACK=PicoGameBoy.color(0,0,0)
WHITE=PicoGameBoy.color(255,255,255)
RED=PicoGameBoy.color(255,0,0)
GREEN=PicoGameBoy.color(0,255,0)
BLUE=PicoGameBoy.color(0,0,255)
# PicoGameBoy inherits all methods from the FrameBuffer class
# see https://docs.micropython.org/en/latest/library/framebuf.html
pgb.fill(BLACK)
# The Raspberry Pi Pico GameBoy uses a framebuffer
# The framebuffer is only transfered to the actual screen and become visible
# when calling show()
pgb.show()
# Drawing primitive shapes
# The screen resolution is 240x240 pixels but Python like many programming
# languages starts counting from zero, not one.
# The top left corner is (0,0) and bottom right is (239,239)
pgb.pixel(0,0,WHITE)
pgb.pixel(239,239,BLUE)
pgb.show()
sleep(1)
pgb.line(0,239,239,0,RED)
pgb.show()
sleep(1)
pgb.rect(10,10,220,220,WHITE)
pgb.show()
sleep(1)
pgb.fill_rect(10,10,220,220,WHITE)
pgb.show()
sleep(1)
# text
pgb.fill(BLACK)
pgb.text('Hello from Pi Pico GameBoy!',0,0,GREEN)
pgb.show()
sleep(1)
pgb.center_text('GAME OVER',WHITE)
pgb.show()
sleep(1)
# sprites
x = 120
y = 120
pgb.fill(BLACK)
pgb.add_rect_sprite(GREEN,12,12) # Create sprite #0
while True:
pgb.sprite(0,x,y)
pgb.show()
while not pgb.any_button():
sleep(0.020)
if pgb.button_right():
x=x+1
elif pgb.button_left():
x=x-1
elif pgb.button_up():
y=y-1
elif pgb.button_down():
y=y+1