基于树莓派RP2040实现复古游戏“打砖块”的移植项目
这是我参加“硬禾学堂2022寒假在家练”活动所完成的项目,项目使用的是树莓派RP2040开发板。 项目实现的总体功能是将复古游戏“打砖块”移植到树莓派RP2040开发板上。
标签
树莓派
MicroPython
游戏
RP2040
2022寒假在家练
emmmmmm
更新2022-03-03
中央民族大学
779

一、项目介绍

这是我参加“硬禾学堂2022寒假在家练”活动所完成的项目,项目使用的是树莓派RP2040开发板。

项目实现的总体功能是将复古游戏“打砖块”移植到树莓派RP2040开发板上,具体的需求如下所示:

  • 能够在按下按钮A后发射小球,实现正常的开始游戏。
  • 能够正确判断小球和边界、砖块以及球拍之间的碰撞,并能正确反射。
  • 能够在小球与砖块的碰撞后,让砖块消失,并统计分数。
  • 能够实现用摇杆控制球拍的左右移动。
  • 能够在球拍没有成功接到小球之时结束游戏,并打出Game over提示。
  • 能够在成功打完所有砖块之时结束游戏,并打出You win提示。
  • 能够在按下按钮B后开始播放背景音乐,并且再次按下停止播放。

二、设计框图

FkpSdbTo4JP3e7AqnPnfGtk81-lU

三、简单的软硬件介绍

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、游戏初始化界面

Fgi1Q5LM8y6xjTTSLSN0ajMy_ASo

2、点击RP2040的按钮A开始游戏

FhRRQx2qN03my_3SWZATaqH-_nit

3、点击RP2040的按钮B播放背景音乐,再次点击将会停止播放

4、左右移动摇杆实现游戏中球拍的左右移动

FsrmcpV70xRvGtfIeBDSDRL5Yx6FFjZ8F1HiIW6GcbolSqB8-jJmpCvG

5、小球遇到砖块的碰撞,以及分数的统计

FltDIJ688SCqHvehOpEynnZZupYbFnVdrApQ6SMTvutex9gOtve_hJPy

6、小球遇到边界以及球拍的碰撞

FnVdrApQ6SMTvutex9gOtve_hJPyFvyUUwzuXxS5uG--7y8HgkEtDzTWFtLz1xo-9GSqK3Cbu6IzJrWKV3NA

FvhbIPbKCoQk7aW0Dbys26tpkEtE

7、球拍未成功接到小球,游戏结束

Fn4ExkWh2eIbOLsgtgQgsS-QYIPDFpiwcPoHPArMj_RnmMAvz7JWkj9F

8、成功打完所有小球,游戏结束

Fl39q8FXOu27zRBDZ_ojPp_txKVd

五、主要代码片段及说明

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开发板有了更加深入的了解,在项目开发的过程中,也充分锻炼了我的代码能力。同时一个完整的项目经历,让我对于项目开发的基本流程有了新的认识。

附件下载
code.rar
源码
团队介绍
缪淼-中央民族大学
团队成员
emmmmmm
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号