一、项目介绍
这是我参加“硬禾学堂2022寒假在家练”活动所完成的项目,项目使用的是树莓派RP2040开发板。
项目实现的总体功能是将复古游戏“打砖块”移植到树莓派RP2040开发板上,具体的需求如下所示:
- 能够在按下按钮A后发射小球,实现正常的开始游戏。
- 能够正确判断小球和边界、砖块以及球拍之间的碰撞,并能正确反射。
- 能够在小球与砖块的碰撞后,让砖块消失,并统计分数。
- 能够实现用摇杆控制球拍的左右移动。
- 能够在球拍没有成功接到小球之时结束游戏,并打出Game over提示。
- 能够在成功打完所有砖块之时结束游戏,并打出You win提示。
- 能够在按下按钮B后开始播放背景音乐,并且再次按下停止播放。
二、设计框图
三、简单的软硬件介绍
1、元器件
项目所使用的元器件为树莓派RP2040开发板,该开发板是由树莓派基金会推出的双核Arm Cortex M0+微控制器,有133MHz的时钟速率以及264KB的SRAM,支持C/C++、MicroPython编程。
2、软件
项目所使用的开发语言为MicroPython,MicroPython是一款编程语言兼容Python3的软件,用C写成的,能够运行在微控制器的硬件上并进行了相应的优化。
3、工具
项目所使用的开发工具为Thonny。Thonny是一款针对初学者的Python IDE工具。
Thonny的官方网站:https://thonny.org/
四、实现的功能及图片展示
1、游戏初始化界面
2、点击RP2040的按钮A开始游戏
3、点击RP2040的按钮B播放背景音乐,再次点击将会停止播放
4、左右移动摇杆实现游戏中球拍的左右移动
5、小球遇到砖块的碰撞,以及分数的统计
6、小球遇到边界以及球拍的碰撞
7、球拍未成功接到小球,游戏结束
8、成功打完所有小球,游戏结束
五、主要代码片段及说明
1、硬件接口模块类(hardware)
主要实现一些涉及硬件接口的初始化。
- RP2040对于屏幕显示控制的初始化。(SPI、st7789)
- RP2040对于摇杆坐标读取的初始化。(ADC)
- RP2040对于A、B两个按钮状态读取的初始化。(Pin)
- RP2040对于音乐模块的初始化。(p_music)
class hardware():
def init():
# screen
spi = SPI(0, baudrate=31250000, polarity=1, phase=0,
sck=Pin(game_kit.lcd_sck, Pin.OUT),
mosi=Pin(game_kit.lcd_sda, Pin.OUT))
tft = st7789.ST7789(
spi,
240,
240,
reset=Pin(game_kit.lcd_rst, Pin.OUT),
dc=Pin(game_kit.lcd_dc, Pin.OUT),
rotation=0)
tft.fill(st7789.color565(135, 206, 250))
tft.fill_rect(0, 232, 240, 8, st7789.color565(135, 206, 250))
hardware.tft = tft
hardware.xAxis = ADC(Pin(28))
hardware.yAxis = ADC(Pin(29))
hardware.buttonB = Pin(5,Pin.IN, Pin.PULL_UP) #B
hardware.buttonA = Pin(6,Pin.IN, Pin.PULL_UP) #A
# bgm
hardware.bgm = p_music(p_music.song0, tempo=1, duty=500, pins=[
Pin(game_kit.buzzer, Pin.OUT)])
2、绘图和文本所使用模块(st7789)
绘图和文本具体功能的实现。采用了st7789模块。
主要运用了st7789模块中color565、fill、fill_rect、text等几个函数。
另外还在游戏中实现了setcolor和outtextxy两个函数。
(1)color565:将红色、绿色和蓝色值(0-255)转换为16位565编码。
def color565(red, green=0, blue=0):
"""
Convert red, green and blue values (0-255) into a 16-bit 565 encoding.
"""
try:
red, green, blue = red # see if the first var is a tuple/list
except TypeError:
pass
return (red & 0xf8) << 8 | (green & 0xfc) << 3 | blue >> 3
(2)fill: 用指定的颜色填充整个帧缓冲区。
参数:
- color(int):用一个int类型表示颜色(具体来说就是将红色、绿色和蓝色值(0-255)转换为16位565编码)。
def fill(self, color):
"""
Fill the entire FrameBuffer with the specified color.
Args:
color (int): 565 encoded color
"""
self.fill_rect(0, 0, self.width, self.height, color)
(3)fill_rect:在给定的位置、大小和颜色绘制一个矩形。
参数:
- x(int):矩形左上角的x坐标。
- y(int):矩形左上角的y坐标。
- width(int):矩形的宽(左右边x坐标的差)。
- heigh(int)t:矩形的高(上下边y坐标的差)。
- color(int):用一个int类型表示颜色(具体来说就是将红色、绿色和蓝色值(0-255)转换为16位565编码)。
def fill_rect(self, x, y, width, height, color):
"""
Draw a rectangle at the given location, size and filled with color.
Args:
x (int): Top left corner x coordinate
y (int): Top left corner y coordinate
width (int): Width in pixels
height (int): Height in pixels
color (int): 565 encoded color
"""
self.set_window(x, y, x + width - 1, y + height - 1)
chunks, rest = divmod(width * height, _BUFFER_SIZE)
pixel = _encode_pixel(color)
self.dc.on()
if chunks:
data = pixel * _BUFFER_SIZE
for _ in range(chunks):
self.write(None, data)
if rest:
self.write(None, pixel * rest)
(4)text:以指定的字体和颜色绘制显示的文本,支持8和16位宽字体。
参数:
- font(module):要使用的字体模块。
- text(str):要写的文本。
- x0(int):开始绘制的列。
- y0(int):开始绘制的行。
- color(int):用于字符的565编码颜色。
- background(int):用于背景的565编码颜色。
def text(self, font, text, x0, y0, color=WHITE, background=BLACK):
"""
Draw text on display in specified font and colors. 8 and 16 bit wide
fonts are supported.
Args:
font (module): font module to use.
text (str): text to write
x0 (int): column to start drawing at
y0 (int): row to start drawing at
color (int): 565 encoded color to use for characters
background (int): 565 encoded color to use for background
"""
if font.WIDTH == 8:
self._text8(font, text, x0, y0, color, background)
else:
self._text16(font, text, x0, y0, color, background)
(5)setcolor:设定pen_color的565编码颜色。
参数:
- c(int):用于pen_color的565编码颜色。
def setcolor(c):
global pen_color
pen_color = c
(6)outtextxy:在指定的位置绘制显示的文本。
参数:
- x(int):开始绘制的列。
- y(int):开始绘制的行。
- c(str):要写的文本。
def outtextxy(x, y, c):
global pen_color
hardware.tft.text(font, c, x*8, y*8, pen_color, GameWindow.window_color)
3、音乐播放类(p_music)
用于音乐播放的模块。引用了buzzer_music中的music类。
以music为父类创建了p_music类,如下面代码所示。
song0和song1分别为两首bgm的乐谱,在handware类中可以看到对于音乐模块的初始化,将song0作为该游戏运用的bgm。
class p_music(music):
song1 = '0 A5 2 26 0.6299212574958801;2 B5 2 26 0.6299212574958801;4 C6 6 26 0.6299212574958801;10 B5 2 26 0.6299212574958801;12 C6 4 26 0.6299212574958801;16 E6 4 26 0.6299212574958801;20 B5 8 26 0.6299212574958801;32 E5 4 26 0.6299212574958801;36 A5 6 26 0.6299212574958801;42 G5 2 26 0.6299212574958801;44 A5 4 26 0.6299212574958801;48 C6 4 26 0.6299212574958801;52 G5 8 26 0.6299212574958801;64 F5 2 26 0.6299212574958801;66 E5 2 26 0.6299212574958801;68 F5 6 26 0.6299212574958801;74 E5 2 26 0.6299212574958801;76 F5 4 26 0.6299212574958801;80 C6 4 26 0.6299212574958801;84 E5 8 26 0.6299212574958801;94 C6 2 26 0.6299212574958801;96 C6 2 26 0.6299212574958801;98 C6 2 26 0.6299212574958801;100 B5 6 26 0.6299212574958801;106 F#5 2 26 0.6299212574958801;108 F#5 4 26 0.6299212574958801;112 B5 4 26 0.6299212574958801;116 B5 8 26 0.6299212574958801;128 A5 2 26 0.6299212574958801;130 B5 2 26 0.6299212574958801;132 C6 6 26 0.6299212574958801;138 B5 2 26 0.6299212574958801;140 C6 4 26 0.6299212574958801;144 E6 4 26 0.6299212574958801;148 B5 8 26 0.6299212574958801;160 E5 2 26 0.6299212574958801;162 E5 2 26 0.6299212574958801;164 A5 6 26 0.6299212574958801;170 G5 2 26 0.6299212574958801;172 A5 4 26 0.6299212574958801;176 C6 4 26 0.6299212574958801;180 G5 8 26 0.6299212574958801;192 E5 4 26 0.6299212574958801;196 F5 4 26 0.6299212574958801;200 C6 2 26 0.6299212574958801;202 B5 2 26 0.6299212574958801;204 B5 4 26 0.6299212574958801;208 C6 4 26 0.6299212574958801;212 D6 4 26 0.6299212574958801;216 E6 2 26 0.6299212574958801;218 C6 2 26 0.6299212574958801;220 C6 8 26 0.6299212574958801;228 C6 2 26 0.6299212574958801;230 B5 2 26 0.6299212574958801;232 A5 4 26 0.6299212574958801;236 B5 4 26 0.6299212574958801;240 G#5 4 26 0.6299212574958801;244 A5 11 26 0.6299212574958801;256 C6 2 26 0.6299212574958801;258 D6 2 26 0.6299212574958801;260 E6 6 26 0.6299212574958801;266 D6 2 26 0.6299212574958801;268 E6 4 26 0.6299212574958801;272 G6 4 26 0.6299212574958801;276 D6 8 26 0.6299212574958801;288 G5 2 26 0.6299212574958801;290 G5 2 26 0.6299212574958801;292 C6 6 26 0.6299212574958801;298 B5 2 26 0.6299212574958801;300 C6 4 26 0.6299212574958801;304 E6 4 26 0.6299212574958801;308 E6 11 26 0.6299212574958801;324 A5 2 26 0.6299212574958801;326 B5 2 26 0.6299212574958801;328 C6 4 26 0.6299212574958801;332 B5 2 26 0.6299212574958801;334 C6 2 26 0.6299212574958801;336 D6 4 26 0.6299212574958801;340 C6 6 26 0.6299212574958801;346 G5 2 26 0.6299212574958801;348 G5 8 26 0.6299212574958801;356 F6 4 26 0.6299212574958801;360 E6 4 26 0.6299212574958801;364 D6 4 26 0.6299212574958801;368 C6 4 26 0.6299212574958801;372 E6 15 26 0.6299212574958801;388 E6 11 26 0.6299212574958801;400 E6 4 26 0.6299212574958801;404 A6 8 26 0.6299212574958801;412 G6 8 26 0.6299212574958801;420 E6 4 26 0.6299212574958801;424 D6 2 26 0.6299212574958801;426 C6 2 26 0.6299212574958801;428 C6 8 26 0.6299212574958801;436 D6 4 26 0.6299212574958801;440 C6 2 26 0.6299212574958801;442 D6 2 26 0.6299212574958801;444 D6 4 26 0.6299212574958801;448 G6 4 26 0.6299212574958801;452 E6 11 26 0.6299212574958801;464 E6 4 26 0.6299212574958801;468 A6 8 26 0.6299212574958801;476 G6 8 26 0.6299212574958801;484 E6 4 26 0.6299212574958801;488 D6 2 26 0.6299212574958801;490 C6 2 26 0.6299212574958801;492 C6 8 26 0.6299212574958801;500 D6 4 26 0.6299212574958801;504 C6 2 26 0.6299212574958801;506 D6 2 26 0.6299212574958801;508 D6 4 26 0.6299212574958801;512 B5 4 26 0.6299212574958801;516 A5 11 26 0.6299212574958801;528 A5 2 26 0.6299212574958801;530 B5 2 26 0.6299212574958801;532 C6 6 26 0.6299212574958801;538 B5 2 26 0.6299212574958801;540 C6 4 26 0.6299212574958801;544 E6 4 26 0.6299212574958801;548 B5 8 26 0.6299212574958801;560 E5 4 26 0.6299212574958801;564 A5 6 26 0.6299212574958801;570 G5 2 26 0.6299212574958801;572 A5 4 26 0.6299212574958801;576 C6 4 26 0.6299212574958801;580 G5 8 26 0.6299212574958801;592 F5 2 26 0.6299212574958801;594 E5 2 26 0.6299212574958801;596 F5 6 26 0.6299212574958801;602 E5 2 26 0.6299212574958801;604 F5 4 26 0.6299212574958801;608 C6 4 26 0.6299212574958801;612 E5 8 26 0.6299212574958801;622 C6 2 26 0.6299212574958801;624 C6 2 26 0.6299212574958801;626 C6 2 26 0.6299212574958801;628 B5 6 26 0.6299212574958801;634 F#5 2 26 0.6299212574958801;636 F#5 4 26 0.6299212574958801;640 B5 4 26 0.6299212574958801;644 B5 8 26 0.6299212574958801;656 A5 2 26 0.6299212574958801;658 B5 2 26 0.6299212574958801;660 C6 6 26 0.6299212574958801;666 B5 2 26 0.6299212574958801;668 C6 4 26 0.6299212574958801;672 E6 4 26 0.6299212574958801;676 B5 8 26 0.6299212574958801;688 E5 2 26 0.6299212574958801;690 E5 2 26 0.6299212574958801;692 A5 6 26 0.6299212574958801;698 G5 2 26 0.6299212574958801;700 A5 4 26 0.6299212574958801;704 C6 4 26 0.6299212574958801;708 G5 8 26 0.6299212574958801;720 E5 4 26 0.6299212574958801;724 F5 4 26 0.6299212574958801;728 C6 2 26 0.6299212574958801;730 B5 2 26 0.6299212574958801;732 B5 4 26 0.6299212574958801;736 C6 4 26 0.6299212574958801;740 D6 4 26 0.6299212574958801;744 E6 2 26 0.6299212574958801;746 C6 2 26 0.6299212574958801;748 C6 8 26 0.6299212574958801;756 C6 2 26 0.6299212574958801;758 B5 2 26 0.6299212574958801;760 A5 4 26 0.6299212574958801;764 B5 4 26 0.6299212574958801;768 G#5 4 26 0.6299212574958801;772 A5 11 26 0.6299212574958801'
song0 = '0 E5 2 14;4 B4 2 14;6 C5 2 14;8 D5 2 14;10 E5 1 14;11 D5 1 14;12 C5 2 14;14 B4 2 14;16 A4 2 14;20 A4 2 14;22 C5 2 14;24 E5 2 14;28 D5 2 14;30 C5 2 14;32 B4 2 14;38 C5 2 14;40 D5 2 14;44 E5 2 14;48 C5 2 14;52 A4 2 14;56 A4 2 14;64 D5 2 14;68 F5 2 14;70 A5 2 14;74 G5 2 14;76 F5 2 14;78 E5 2 14;84 C5 2 14;86 E5 2 14;90 D5 2 14;92 C5 2 14;94 B4 2 14;98 B4 2 14;100 C5 2 14;102 D5 2 14;106 E5 2 14;110 C5 2 14;114 A4 2 14;118 A4 2 14;248 E5 8 14;256 C5 8 14;264 D5 8 14;272 B4 8 14;280 C5 8 14;288 A4 8 14;296 G#4 8 14;304 B4 8 14;313 E5 8 14;321 C5 8 14;329 D5 8 14;337 B4 4 14;341 B4 4 14;345 C5 4 14;349 E5 4 14;353 A5 8 14;361 G#5 8 14;248 C5 8 14;256 A4 8 14;264 B4 8 14;272 G#4 8 14;280 A4 8 14;288 E4 8 14;0 B4 2 14;4 G#4 2 14;6 A4 2 14;8 B4 2 14;12 A4 2 14;14 G#4 2 14;28 B4 2 14;30 A4 2 14;32 G#4 4 14;40 B4 2 14;38 A4 2 14;44 C5 2 14;52 E4 2 14;64 F4 2 14;94 G#4 2 14;100 A4 2 14;102 B4 2 14;106 C5 2 14;110 A4 2 14;114 E4 2 14;114 E4 2 14;118 E4 2 14;98 G#4 2 14;296 E4 8 14;304 G#4 8 14;313 C5 8 14;321 A4 8 14;329 B4 8 14;337 G#4 4 14;341 G#4 4 14;345 A4 4 14;349 C5 4 14;353 E5 8 14;353 E5 8 14;361 E5 8 14;48 A4 2 14;56 E4 2 14;68 D5 2 14;70 F5 2 14;74 E5 2 14;76 D5 2 14;80 E5 2 14;78 C5 2 14;80 C5 2 14;22 A4 2 14;24 C5 2 14;84 A4 2 14;86 C5 2 14;92 A4 2 14;90 B4 2 14;0 E5 2 14;4 B4 2 14;6 C5 2 14;8 D5 2 14;10 E5 1 14;11 D5 1 14;12 C5 2 14;14 B4 2 14;16 A4 2 14;20 A4 2 14;22 C5 2 14;24 E5 2 14;28 D5 2 14;30 C5 2 14;32 B4 2 14;38 C5 2 14;40 D5 2 14;44 E5 2 14;48 C5 2 14;52 A4 2 14;56 A4 2 14;64 D5 2 14;68 F5 2 14;70 A5 2 14;74 G5 2 14;76 F5 2 14;78 E5 2 14;84 C5 2 14;86 E5 2 14;90 D5 2 14;92 C5 2 14;94 B4 2 14;98 B4 2 14;100 C5 2 14;102 D5 2 14;106 E5 2 14;110 C5 2 14;114 A4 2 14;118 A4 2 14;0 B4 2 14;4 G#4 2 14;6 A4 2 14;8 B4 2 14;12 A4 2 14;14 G#4 2 14;28 B4 2 14;30 A4 2 14;32 G#4 4 14;40 B4 2 14;38 A4 2 14;44 C5 2 14;52 E4 2 14;64 F4 2 14;94 G#4 2 14;100 A4 2 14;102 B4 2 14;106 C5 2 14;110 A4 2 14;114 E4 2 14;114 E4 2 14;118 E4 2 14;98 G#4 2 14;48 A4 2 14;56 E4 2 14;68 D5 2 14;70 F5 2 14;74 E5 2 14;76 D5 2 14;80 E5 2 14;78 C5 2 14;80 C5 2 14;22 A4 2 14;24 C5 2 14;84 A4 2 14;86 C5 2 14;92 A4 2 14;90 B4 2 14;124 E5 2 14;128 B4 2 14;130 C5 2 14;132 D5 2 14;134 E5 1 14;135 D5 1 14;136 C5 2 14;138 B4 2 14;140 A4 2 14;144 A4 2 14;146 C5 2 14;148 E5 2 14;152 D5 2 14;154 C5 2 14;156 B4 2 14;162 C5 2 14;164 D5 2 14;168 E5 2 14;172 C5 2 14;176 A4 2 14;180 A4 2 14;188 D5 2 14;192 F5 2 14;194 A5 2 14;198 G5 2 14;200 F5 2 14;202 E5 2 14;208 C5 2 14;210 E5 2 14;214 D5 2 14;216 C5 2 14;218 B4 2 14;222 B4 2 14;224 C5 2 14;226 D5 2 14;230 E5 2 14;234 C5 2 14;238 A4 2 14;242 A4 2 14;124 B4 2 14;128 G#4 2 14;130 A4 2 14;132 B4 2 14;136 A4 2 14;138 G#4 2 14;152 B4 2 14;154 A4 2 14;156 G#4 4 14;164 B4 2 14;162 A4 2 14;168 C5 2 14;176 E4 2 14;188 F4 2 14;218 G#4 2 14;224 A4 2 14;226 B4 2 14;230 C5 2 14;234 A4 2 14;238 E4 2 14;238 E4 2 14;242 E4 2 14;222 G#4 2 14;172 A4 2 14;180 E4 2 14;192 D5 2 14;194 F5 2 14;198 E5 2 14;200 D5 2 14;204 E5 2 14;202 C5 2 14;204 C5 2 14;146 A4 2 14;148 C5 2 14;208 A4 2 14;210 C5 2 14;216 A4 2 14;214 B4 2 14;124 E5 2 14;128 B4 2 14;130 C5 2 14;132 D5 2 14;134 E5 1 14;135 D5 1 14;136 C5 2 14;138 B4 2 14;140 A4 2 14;144 A4 2 14;146 C5 2 14;148 E5 2 14;152 D5 2 14;154 C5 2 14;156 B4 2 14;162 C5 2 14;164 D5 2 14;168 E5 2 14;172 C5 2 14;176 A4 2 14;180 A4 2 14;188 D5 2 14;192 F5 2 14;194 A5 2 14;198 G5 2 14;200 F5 2 14;202 E5 2 14;208 C5 2 14;210 E5 2 14;214 D5 2 14;216 C5 2 14;218 B4 2 14;222 B4 2 14;224 C5 2 14;226 D5 2 14;230 E5 2 14;234 C5 2 14;238 A4 2 14;242 A4 2 14;124 B4 2 14;128 G#4 2 14;130 A4 2 14;132 B4 2 14;136 A4 2 14;138 G#4 2 14;152 B4 2 14;154 A4 2 14;156 G#4 4 14;164 B4 2 14;162 A4 2 14;168 C5 2 14;176 E4 2 14;188 F4 2 14;218 G#4 2 14;224 A4 2 14;226 B4 2 14;230 C5 2 14;234 A4 2 14;238 E4 2 14;238 E4 2 14;242 E4 2 14;222 G#4 2 14;172 A4 2 14;180 E4 2 14;192 D5 2 14;194 F5 2 14;198 E5 2 14;200 D5 2 14;204 E5 2 14;202 C5 2 14;204 C5 2 14;146 A4 2 14;148 C5 2 14;208 A4 2 14;210 C5 2 14;216 A4 2 14;214 B4 2 14;248 C5 2 13;252 C5 2 13;254 E5 2 13;250 E5 2 13;256 C5 2 13;260 C5 2 13;258 A4 2 13;262 A4 2 13;264 D5 2 13;268 D5 2 13;266 B4 2 13;270 B4 2 13;272 G#4 2 13;276 G#4 2 13;274 B4 2 13;278 B4 2 13;280 A4 2 13;284 A4 2 13;282 C5 2 13;286 C5 2 13;347 C5 2 13;351 C5 2 13;313 C5 2 13;317 C5 2 13;319 E5 2 13;315 E5 2 13;321 C5 2 13;325 C5 2 13;323 A4 2 13;327 A4 2 13;329 D5 2 13;333 D5 2 13;331 B4 2 13;335 B4 2 13;337 G#4 2 13;341 G#4 2 13;339 B4 2 13;343 B4 2 13;345 A4 2 13;349 A4 2 13;349 E5 2 13;353 E5 2 13;355 A5 2 13;359 A5 2 13;357 E5 2 13;361 E5 2 13;363 G#5 2 13;367 G#5 2 13;365 E5 2 14;292 A4 2 13;288 A4 2 13;294 E4 2 13;290 E4 2 13;300 G#4 2 13;296 G#4 2 13;302 E4 2 13;298 E4 2 13;308 G#4 2 13;304 G#4 2 13;310 B4 2 13;306 B4 2 13'
该类包含以下几个函数。
(1)__init__:初始化。
def __init__(self, songString='0 D4 8 0', looping=True, tempo=3, duty=2512, pin=None, pins=[Pin(0)]):
super().__init__(songString, looping, tempo, duty, pin, pins)
self.pause = True
(2)toggle_pause:反转当前播放状态。
def toggle_pause(self):
self.pause = not self.pause
if self.pause:
print("mute\n")
self.mute()
else:
print("unmute\n")
self.unmute()
(3)mute:静音状态设置。
def mute(self):
for pwm in self.pwms:
pwm.duty_u16(0)
(4)unmute:结束静音状态设置(即正常播放)。
def unmute(self):
for pwm in self.pwms:
pwm.duty_u16(self.duty)
(5)tick:每一时刻播放。
def tick(self):
if self.pause:
pass
else:
super().tick()
3、游戏窗口类(GameWindow)
主要实现了游戏窗口的相关参数的初始化以及游戏窗口背景绘制。
包含以下两个函数。
(1)init:游戏窗口的相关参数的初始化。设置游戏窗口的宽和高都为240,并且设置有窗口背景颜色的参数为st7789.color565(135, 206, 250)。
def init():
GameWindow.window_length = 240
GameWindow.window_wide = 240
# 定义游戏窗口背景颜色参数
GameWindow.window_color = st7789.color565(135, 206, 250)
(2)backgroud:绘制游戏窗口背景颜色。
def backgroud():
# 绘制游戏窗口背景颜色
hardware.tft.fill_rect(0, 0, GameWindow.window_length, GameWindow.window_wide, GameWindow.window_color)
4、球类(Ball)
主要实现了球相关参数的初始化,以及游戏开始前后球分别在静止和移动的时候的绘制。
包含以下三个函数。
(1)init:球相关参数的初始化。
def init():
#设置球的半径、颜色、移动速度参数
Ball.ball_color = st7789.color565(255,215,0)
Ball.move_x = 2
Ball.move_y = 2
Ball.radius = 5
(2)ballready:游戏未开始时,球静止时的绘制。
def ballready():
#设置球的初始位置、
Ball.ball_x = Rect.mouse_x
Ball.ball_y = GameWindow.window_wide-Rect.rect_wide-Ball.radius
#绘制球,设置反弹触发条件
hardware.tft.fill_rect(int(Ball.ball_x-Ball.radius), int(Ball.ball_y-Ball.radius), Ball.radius*2, Ball.radius*2, Ball.ball_color)
(3)ballmove:游戏开始后,计算球当前时刻的坐标,并绘制球。以此同时,通过球的位置判断是否进入失败条件。
def ballmove():
#设置游戏失败条件
if Ball.ball_y+Ball.radius >= 240:
setcolor(st7789.BLACK)
outtextxy(11,17, "GAME OVER")
GameOver.over_sign = 1
#绘制球,设置反弹触发条件
hardware.tft.fill_rect(int(Ball.ball_x-Ball.radius), int(Ball.ball_y-Ball.radius), Ball.radius*2, Ball.radius*2, Ball.ball_color)
Ball.ball_x += Ball.move_x
Ball.ball_y -= Ball.move_y
#调用碰撞检测函数
Collision.ball_window()
Collision.ball_rect()
#每接5次球球速增加一倍
if Collision.distance < Ball.radius:
Score.frequency += 1
if Score.frequency == 5:
Score.frequency = 0
Ball.move_x += Ball.move_x
Ball.move_y += Ball.move_y
Score.point += Score.point
5、球拍类(Rect)
主要实现了球拍相关参数的初始化,以及用摇杆操控球拍移动后球拍的重新绘制。
包含以下两个函数。
(1)init:球拍相关参数的初始化。
def init():
#设置球拍颜色参数
Rect.rect_color = st7789.color565(255,0,0)
Rect.rect_length = 50
Rect.rect_wide = 10
Rect.mouse_x = 120
Rect.mouse_y = 0
(2)rectmove:用摇杆操控球拍移动后球拍的重新绘制。
def rectmove():
#获取位置参数
xValue = hardware.yAxis.read_u16()
yValue = hardware.xAxis.read_u16()
rect_v = 0.001
if xValue < 30000 or xValue > 35000:
Rect.mouse_x = Rect.mouse_x + rect_v*(xValue - 32500)
#绘制球拍,限定横向边界
if Rect.mouse_x >= GameWindow.window_length-Rect.rect_length//2:
Rect.mouse_x = GameWindow.window_length-Rect.rect_length//2
if Rect.mouse_x <= Rect.rect_length//2:
Rect.mouse_x = Rect.rect_length//2
hardware.tft.fill_rect(int(Rect.mouse_x-Rect.rect_length//2),GameWindow.window_wide-Rect.rect_wide, Rect.rect_length, Rect.rect_wide, Rect.rect_color)
6、砖块类(Brick)
主要实现了砖块相关参数的初始化,以及随游戏进行砖块的实时刷新,同时判断是否满足游戏胜利条件(即所有的砖块全部被打完)。
包含以下两个函数。
(1)init:砖块相关参数的初始化。
def init():
#设置砖块颜色参数
Brick.brick_color = st7789.color565(139,126,102)
Brick.brick_list = [[1,1,1,1,1,1],[1,1,1,1,1,1],[1,1,1,1,1,1],[1,1,1,1,1,1],[1,1,1,1,1,1]]
Brick.brick_length = 35
Brick.brick_wide = 20
(2)brickarrange:刷新砖块,并判断是否满足游戏胜利条件。
def brickarrange():
for i in range(5):
for j in range(6):
Brick.brick_x = j*(Brick.brick_length+5)
Brick.brick_y = i*(Brick.brick_wide+5)+5
if Brick.brick_list[i][j] == 1:
#绘制砖块
hardware.tft.fill_rect(Brick.brick_x,Brick.brick_y,Brick.brick_length,Brick.brick_wide,Brick.brick_color)
#调用碰撞检测函数
Collision.ball_brick()
if Collision.distanceb < Ball.radius:
Brick.brick_list[i][j] = 0
Score.score += Score.point
#设置游戏胜利条件
if Brick.brick_list == [[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0]]:
setcolor(st7789.BLACK)
outtextxy(11,17, "You Win")
Win.win_sign = 1
7、得分类(Score)
主要实现了得分相关参数的初始化,以及实时在屏幕上绘制玩家分数。
(1)init:得分相关参数的初始化。
def init():
#设置初始分数
Score.score = 0
#设置初始加分点数
Score.point = 1
#设置初始接球次数
Score.frequency = 0
(2)countscore:绘制玩家分数。
def countscore():
#绘制玩家分数
my_score = str(Score.score)
setcolor(st7789.BLACK)
outtextxy(10,20, "you score:"+str(my_score))
8、游戏结束类(GameOver)
用于初始化游戏结束标志。
class GameOver():
'''创建游戏结束类'''
def init():
#定义GameOver标识
GameOver.over_sign = 0
9、游戏胜利类(Win)
用于初始化游戏胜利标志。
class Win():
'''创建游戏胜利类'''
def init():
#定义Win标识
Win.win_sign = 0
10、碰撞检测类(Collision)
用于球分别与窗口边界、球拍以及砖块之间的碰撞检测。
主要包含以下三个函数。
(1)ball_window:球与窗口边框的碰撞检测。
#球与窗口边框的碰撞检测
def ball_window():
if Ball.ball_x <= Ball.radius or Ball.ball_x >= (GameWindow.window_length-Ball.radius):
Ball.move_x = -Ball.move_x
if Ball.ball_y <= Ball.radius:
Ball.move_y = -Ball.move_y
(2)ball_rect:球与球拍的碰撞检测。
#球与球拍的碰撞检测
def ball_rect():
#定义碰撞标识
Collision.collision_sign_x = 0
Collision.collision_sign_y = 0
if Ball.ball_x < (Rect.mouse_x-Rect.rect_length//2):
Collision.closestpoint_x = Rect.mouse_x-Rect.rect_length//2
Collision.collision_sign_x = 1
elif Ball.ball_x > (Rect.mouse_x+Rect.rect_length//2):
Collision.closestpoint_x = Rect.mouse_x+Rect.rect_length//2
Collision.collision_sign_x = 2
else:
Collision.closestpoint_x = Ball.ball_x
Collision.collision_sign_x = 3
if Ball.ball_y < (GameWindow.window_wide-Rect.rect_wide):
Collision.closestpoint_y = (GameWindow.window_wide-Rect.rect_wide)
Collision.collision_sign_y = 1
elif Ball.ball_y > GameWindow.window_wide:
Collision.closestpoint_y = GameWindow.window_wide
Collision.collision_sign_y = 2
else:
Collision.closestpoint_y = Ball.ball_y
Collision.collision_sign_y = 3
#定义球拍到圆心最近点与圆心的距离
Collision.distance = math.sqrt(math.pow(Collision.closestpoint_x-Ball.ball_x,2)+math.pow(Collision.closestpoint_y-Ball.ball_y,2))
#球在球拍上左、上中、上右3种情况的碰撞检测
if Collision.distance < Ball.radius and Collision.collision_sign_y == 1 and (Collision.collision_sign_x == 1 or Collision.collision_sign_x == 2):
if Collision.collision_sign_x == 1 and Ball.move_x > 0:
Ball.move_x = - Ball.move_x
Ball.move_y = - Ball.move_y
if Collision.collision_sign_x == 1 and Ball.move_x < 0:
Ball.move_y = - Ball.move_y
if Collision.collision_sign_x == 2 and Ball.move_x < 0:
Ball.move_x = - Ball.move_x
Ball.move_y = - Ball.move_y
if Collision.collision_sign_x == 2 and Ball.move_x > 0:
Ball.move_y = - Ball.move_y
if Collision.distance < Ball.radius and Collision.collision_sign_y == 1 and Collision.collision_sign_x == 3:
Ball.move_y = - Ball.move_y
#球在球拍左、右两侧中间的碰撞检测
if Collision.distance < Ball.radius and Collision.collision_sign_y == 3:
Ball.move_x = - Ball.move_x
(3)ball_brick:球与砖块的碰撞检测。
#球与砖块的碰撞检测
def ball_brick():
#定义碰撞标识
Collision.collision_sign_bx = 0
Collision.collision_sign_by = 0
if Ball.ball_x < Brick.brick_x:
Collision.closestpoint_bx = Brick.brick_x
Collision.collision_sign_bx = 1
elif Ball.ball_x > Brick.brick_x+Brick.brick_length:
Collision.closestpoint_bx = Brick.brick_x+Brick.brick_length
Collision.collision_sign_bx = 2
else:
Collision.closestpoint_bx = Ball.ball_x
Collision.collision_sign_bx = 3
if Ball.ball_y < Brick.brick_y:
Collision.closestpoint_by = Brick.brick_y
Collision.collision_sign_by = 1
elif Ball.ball_y > Brick.brick_y+Brick.brick_wide:
Collision.closestpoint_by = Brick.brick_y+Brick.brick_wide
Collision.collision_sign_by = 2
else:
Collision.closestpoint_by = Ball.ball_y
Collision.collision_sign_by = 3
#定义砖块到圆心最近点与圆心的距离
Collision.distanceb = math.sqrt(math.pow(Collision.closestpoint_bx-Ball.ball_x,2)+math.pow(Collision.closestpoint_by-Ball.ball_y,2))
#球在砖块上左、上中、上右3种情况的碰撞检测
if Collision.distanceb < Ball.radius and Collision.collision_sign_by == 1 and (Collision.collision_sign_bx == 1 or Collision.collision_sign_bx == 2):
if Collision.collision_sign_bx == 1 and Ball.move_x > 0:
Ball.move_x = - Ball.move_x
Ball.move_y = - Ball.move_y
if Collision.collision_sign_bx == 1 and Ball.move_x < 0:
Ball.move_y = - Ball.move_y
if Collision.collision_sign_bx == 2 and Ball.move_x < 0:
Ball.move_x = - Ball.move_x
Ball.move_y = - Ball.move_y
if Collision.collision_sign_bx == 2 and Ball.move_x > 0:
Ball.move_y = - Ball.move_y
if Collision.distanceb < Ball.radius and Collision.collision_sign_by == 1 and Collision.collision_sign_bx == 3:
Ball.move_y = - Ball.move_y
#球在砖块下左、下中、下右3种情况的碰撞检测
if Collision.distanceb < Ball.radius and Collision.collision_sign_by == 2 and (Collision.collision_sign_bx == 1 or Collision.collision_sign_bx == 2):
if Collision.collision_sign_bx == 1 and Ball.move_x > 0:
Ball.move_x = - Ball.move_x
Ball.move_y = - Ball.move_y
if Collision.collision_sign_bx == 1 and Ball.move_x < 0:
Ball.move_y = - Ball.move_y
if Collision.collision_sign_bx == 2 and Ball.move_x < 0:
Ball.move_x = - Ball.move_x
Ball.move_y = - Ball.move_y
if Collision.collision_sign_bx == 2 and Ball.move_x > 0:
Ball.move_y = - Ball.move_y
if Collision.distanceb < Ball.radius and Collision.collision_sign_by == 2 and Collision.collision_sign_bx == 3:
Ball.move_y = - Ball.move_y
#球在砖块左、右两侧中间的碰撞检测
if Collision.distanceb < Ball.radius and Collision.collision_sign_by == 3:
Ball.move_x = - Ball.move_x
12、主程序(main)
首先对各个模块进行初始化,接着进入一个死循环,每循环一次表示游戏经过了一帧,游戏画面整体刷新一次。具体的流程可以看设计框图。
def main():
hardware.init()
GameWindow.init()
Rect.init()
Ball.init()
Brick.init()
Score.init()
Win.init()
GameOver.init()
# 定义游戏开始标识
start_sign = 0
while True:
GameWindow.backgroud()
Rect.rectmove()
Score.countscore()
buttonValueA = hardware.buttonA.value()
buttonValueB = hardware.buttonB.value()
if buttonValueA == 0:
start_sign = 1
if buttonValueB == 0:
hardware.bgm.toggle_pause()
if start_sign == 0:
Ball.ballready()
else:
Ball.ballmove()
Brick.brickarrange()
if GameOver.over_sign == 1 or Win.win_sign == 1:
break
# 控制游戏窗口刷新频率
time.sleep(0.00001)
hardware.bgm.tick()
#print(Ball.ball_x,Ball.ball_y)
六、项目总结
通过这次寒假一起练活动,我对于RP2040开发板有了更加深入的了解,在项目开发的过程中,也充分锻炼了我的代码能力。同时一个完整的项目经历,让我对于项目开发的基本流程有了新的认识。