内容介绍
内容介绍
- 项目介绍:使用Thonny IDE连接RP2040 Game Kit,基于MicroPython语言实现了一款经典的推箱子小游戏,将地图上所有箱子推到指定的标记点处即为游戏成功。
- 硬件介绍:本项目使用树莓派Pico核心芯片RP2040。板卡正面由一个240*240分辨率的彩色IPS LCD、一个四向摇杆、两个轻触按键构成。此外,板卡上还有SPI接口、一个三轴姿态传感器MMA7660用做输入控制、一个红外接收管 + 一个红外发射管、双排16Pin连接器,有SPI、I2C以及2路模拟信号输入。片内温度传感器、并支持外部4路模拟信号输入,内部ADC采样率高达500Ksps、12位精度,支持MicroPython、C、C++编程。
- 项目设计思路:
- 文字说明:
- 程序开始运行,出现初始界面,显示项目名称及游戏方法,并提示用户按下A键触发游戏。
- 用户按下A键开始游戏,出现初始地图。初始地图中静态部分有板墙、通道及标记点位置,动态部分有箱子和小人。
- 用户使用摇杆控制小人方向。这里以“上”为例,其它三个方向(下、左、右)同理。(设目前所处位置坐标为(x,y))
- 若所处位置上方(x,y-1)为板墙:则返回,小人停留在目前的位置不变。
- 若所处位置上方(x,y-1)为箱子:
- 若所处位置上方两格(x,y-2)为板墙或另一个箱子:返回,小人停留在目前的位置不变
- 否则,小人纵坐标-1(变为(x,y-1)),小人正上方的箱子纵坐标-1(变为(x,y-2)),当前位置(x,y)变为初始地图静态部分规定的样子(通道或标记点)。
- 若以上两种情况都不是,则直接将小人纵坐标位置-1,当前位置(x,y)变为初始地图静态部分规定的样子(通道或标记点)。
- 判断两个箱子所在的位置是否与标记点位置一致
- 一致:游戏成功,显示成功字样
- 不一致:继续游戏,用户继续使用摇杆控制
- 若用户需要游戏中断重来:按B键,则游戏界面回复初始地图样式。
- 流程图说明:
- 文字说明:
- 代码片段说明:
- 设置引脚,A键和B键均为输入端口,会使电平升高。
xAxis = ADC(Pin(28)) yAxis = ADC(Pin(29)) buttonB = Pin(5,Pin.IN, Pin.PULL_UP) buttonA = Pin(6,Pin.IN, Pin.PULL_UP) led = Pin(4, Pin.OUT)
- 背景音乐数据,使用贪吃蛇背景音乐中的一个。
song = '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'
- 作图颜色样式设定。板墙设置为白色,小人设置为浅蓝色,箱子设置为蓝色(其上有黄色图案),标记点设置为红色,整个游戏的背景设置为紫色。
class color_cfg: #颜色设定 wall = st7789.WHITE box = st7789.YELLOW blue = st7789.BLUE man = st7789.CYAN sym = st7789.RED background = st7789.color565(128, 0, 128) def outtextxy(x, y, c, color): #作图 global pen_color hardware.tft.text(font3, c, x*16, y*16, pen_color, color) def setcolor(c): #设置作图颜色 global pen_color pen_color = c
- 初始化屏幕驱动
- 初始化
class hardware(): #初始化屏幕驱动 def init(): # screen st7789_res = 0 st7789_dc = 1 spi_sck=machine.Pin(2) spi_tx=machine.Pin(3) spi = machine.SPI(0,baudrate=4000000, phase=1, polarity=1, sck=spi_sck, mosi=spi_tx) tft = st7789.ST7789( spi, 240, 240, reset=machine.Pin(st7789_res, machine.Pin.OUT), dc=machine.Pin(st7789_dc, machine.Pin.OUT), #xstart=0, #ystart=0, rotation=0) tft.fill(color_cfg.background) tft.fill_rect(0, 232, 240, 8, color_cfg.blue) hardware.tft = tft
- 静态地图设置。静态地图即不随用户输入电平而改变的部分,在本游戏中为板墙、通道和标记点。用户每操作一次摇杆,地图都需按照此静态地图的初始设置重新绘制。
#将地图绘制为一个凸字形 m = [ 0,0,0,0,-1,-1,-1,-1, 0,1,3,0,0,0,0,0, 0,1,1,1,1,1,1,0, 0,1,1,1,1,1,1,0, 0,1,1,3,1,1,1,0, 0,1,1,1,1,1,1,0, 0,1,1,0,0,0,0,0, 0,0,0,0,-1,-1,-1,-1,]
- 初始化
- 游戏主体部分
- 绘制地图。此函数用于绘制初始地图,且用户每操作一次摇杆,地图都需依赖此函数重新绘制。
class game(): ma = [] d = 0 x = 1 y = 1 mySong = music(song, pins=[Pin(23)]) def draw(self): #根据上述设置绘制地图 for i in range(8): for n in range(8): if self.ma[n*8+i] == 0: #绘制板墙 setcolor(color_cfg.wall) outtextxy(i+1, n+1, '0',color_cfg.wall) if self.ma[n*8+i] == 1: #绘制通道 setcolor(color_cfg.background) outtextxy(i+1, n+1, '0',color_cfg.background) if self.ma[n*8+i] == 2: #绘制箱子,放到m中设置的初始位置 setcolor(color_cfg.box) outtextxy(i+1, n+1, 'b',color_cfg.blue) if self.ma[n*8+i] == 3: #绘制终点 setcolor(color_cfg.sym) outtextxy(i+1, n+1, 'o',color_cfg.background) if self.ma[n*8+i] == 4: #绘制人,初始位置为(1,1) setcolor(color_cfg.background) outtextxy(i+1, n+1, 'm',color_cfg.man)
- 初始界面设置。第一行使用头文件中设置好的font2从屏幕的第2列起显示游戏名称,第3-5行从第1列起显示游戏的玩法指南,第6行从第2列起提示用户游戏的触发机制。
def title(self): #初始界面 hardware.tft.text(font2, 'BOXMAN', 2*16, 1*32, color_cfg.wall, color_cfg.background) #标题 hardware.tft.text(font1, 'Note:', 1*16, 3*32, color_cfg.wall, color_cfg.background) #游戏说明 hardware.tft.text(font1, 'Push boxes to the red flag,', 1*16, 4*32, color_cfg.wall, color_cfg.background) hardware.tft.text(font1, 'and then you win the game.', 1*16, 5*32, color_cfg.wall, color_cfg.background) hardware.tft.text(font1, 'START:buttonA on the right', 2*16, 6*32, color_cfg.wall, color_cfg.background)
- 初始化游戏数据。相比于上面设置好的静态地图,此处增加了动态部分——箱子、小人。箱子的位置根据矩阵设置,小人的初始位置设定为地图的左上角。此处的地图才是游戏开始时在界面上真正显示的地图。
def init_run(self): #初始化游戏数据 self.ma = [ 0,0,0,0,-1,-1,-1,-1, 0,1,3,0,0,0,0,0, 0,1,1,1,1,1,1,0, 0,1,2,1,1,0,1,0, 0,1,1,3,1,0,1,0, 0,1,1,1,1,2,1,0, 0,1,1,0,0,0,0,0, 0,0,0,0,-1,-1,-1,-1,] self.d = 0 #移动方向标记,上1下3,左2右4 #人所在初始位置为(1,1) self.x = 1 self.y = 1 self.ma[self.y*8+self.x] = 4 self.draw() #显示游戏画面
- 游戏主进程。首先设置通过按键A触发游戏开始的机制,然后进入游戏的主循环。这是一个死循环,保障游戏一直运行。主循环中调用了判断行动方向、根据行动方向修改地图数据、根据新的地图数据重新绘制地图、判断游戏胜利四个主要函数。
async def process(self): #游戏主进程 bgm = asyncio.create_task(self.bgm_process()) #播放音乐(此处使用实例贪吃蛇的音乐) self.title() #显示标题界面 while True: buttonValueA = buttonA.value() if buttonValueA == 0: #按下A键时进入游戏 hardware.init() self.init_run() print('go') while True: #游戏主循环 self.d = 0 self.dir_select() #判断小人行动方向 self.run() #根据移动修改地图数据 self.draw() #重绘地图 self.judge() #判断游戏胜利 await self.blink() #led灯闪烁
- 判断行动方向。通过x和y方向数值的感知判断用户操作的方向,并将每个行动方向标记为一个数字,便于之后程序的编写。
def dir_select(self): xValue = xAxis.read_u16() yValue = yAxis.read_u16() buttonValueB = buttonB.value() #摇杆方向感知 if xValue <1000: #上,d标记为1 self.d = 1 elif xValue >40000: #下,d标记为3 self.d = 3 if yValue <1000: #左,d标记为4 self.d = 4 elif yValue >40000: #右,d标记为2 self.d = 2 if buttonValueB == 0: #按B键,游戏重新开始 self.init_run()
- 根据行动方向修改地图数据,具体算法与上述“项目设计思路”中所写相同。分别按照上述算法写好四个方向的代码即可。
def run(self): if self.d == 1: #上 if self.ma[(self.y-1)*8 + self.x] == 0: return elif self.ma[(self.y-1)*8 + self.x] == 2: if self.ma[(self.y-2)*8 + self.x] == 0 or self.ma[(self.y-2)*8 + self.x] == 2: return else: self.ma[(self.y-2)*8 + self.x] = 2 self.ma[(self.y-1)*8 + self.x] = 4 self.ma[self.y*8 + self.x] = m[self.y*8 + self.x] self.y = self.y - 1 else: self.ma[(self.y-1)*8 + self.x] = 4 self.ma[self.y*8 + self.x] = m[self.y*8 + self.x] self.y = self.y - 1 elif self.d == 3: #下 if self.ma[(self.y+1)*8 + self.x] == 0: return elif self.ma[(self.y+1)*8 + self.x] == 2: if self.ma[(self.y+2)*8 + self.x] == 0 or self.ma[(self.y+2)*8 + self.x] == 2: return else: self.ma[(self.y+2)*8 + self.x] = 2 self.ma[(self.y+1)*8 + self.x] = 4 self.ma[self.y*8 + self.x] = m[self.y*8 + self.x] self.y = self.y + 1 else: self.ma[(self.y+1)*8 + self.x] = 4 self.ma[self.y*8 + self.x] = m[self.y*8 + self.x] self.y = self.y + 1 elif self.d == 4: #左 if self.ma[self.y*8 + self.x-1] == 0: return elif self.ma[self.y*8 + self.x-1] == 2: if self.ma[self.y*8 + self.x-2] == 0 or self.ma[self.y*8 + self.x-2] == 2: return else: self.ma[self.y*8 + self.x-2] = 2 self.ma[self.y*8 + self.x-1] = 4 self.ma[self.y*8 + self.x] = m[self.y*8 + self.x] self.x = self.x - 1 else: self.ma[self.y*8 + self.x-1] = 4 self.ma[self.y*8 + self.x] = m[self.y*8 + self.x] self.x = self.x - 1 elif self.d == 2: #右 if self.ma[self.y*8 + self.x+1] == 0: return elif self.ma[self.y*8 + self.x+1] == 2: if self.ma[self.y*8 + self.x+2] == 0 or self.ma[self.y*8 + self.x+2] == 2: return else: self.ma[self.y*8 + self.x+2] = 2 self.ma[self.y*8 + self.x+1] = 4 self.ma[self.y*8 + self.x] = m[self.y*8 + self.x] self.x = self.x + 1 else: self.ma[self.y*8 + self.x+1] = 4 self.ma[self.y*8 + self.x] = m[self.y*8 + self.x] self.x = self.x + 1
- 判断是否胜利
def judge(self): if self.ma[1*8+2] == 2 and self.ma[4*8+3] == 2: #根据终点(sym)所在位置计算游戏是否胜利 self.win()
- 胜利后出现文字与灯的设定
def win(self): hardware.tft.text(font1, 'Win!Press B to RESTART!', 2*16, 6*32, color_cfg.wall, color_cfg.background) async def blink(self): led.value(1) await asyncio.sleep_ms(50) led.value(0) await asyncio.sleep_ms(50)
- 播放背景音乐
async def bgm_process(self): while True: self.mySong.tick() await asyncio.sleep(0.04)
- 绘制地图。此函数用于绘制初始地图,且用户每操作一次摇杆,地图都需依赖此函数重新绘制。
- 项目主函数调用上面所写函数,运行游戏。
def main(): hardware.init() s = game() asyncio.run(s.process()) main()
- 设置引脚,A键和B键均为输入端口,会使电平升高。
- 实现的功能及成果展示
- 初始界面
- 最上面一行为游戏名称,而后为游戏指南,最后为触发游戏机制提醒。
- 初始地图
- 动态+静态。
- 游戏成功
- 两个箱子均被移动到了标记点位置上。
- 成功后重置游戏
- 游戏返回初始地图位置。
- 初始界面
- 主要难题及解决方法
-
问题:起初想要使用python库直接使用python实现游戏编程,遇到树莓派默认关闭SSH的问题,尝试打开SSL但没有成功。
解决:选择直接使用电子森林中已有的库文件进行编程
-
问题:地图过大产生溢出
解决:缩小地图,使用8×8大小即可
-
问题:给出的贪吃蛇程序中,st7789c程序没有给出,导致贪吃蛇程序无法正常运行,继而无法根据贪吃蛇程序进行修改
解决:不使用st7789c,直接使用文件st7789中的函数进行初始化即可
-
问题:课程中使用Online Sequencer网站生成音乐序列,在实际操作过程中,网站的加载速度非常慢,无法生成音乐序列
解决:直接使用贪吃蛇程序中已经给出的音乐序列即可
-
附件下载
WangXueyi.zip
团队介绍
王雪怡
团队成员
王雪怡
评论
0 / 100
查看更多