一、项目介绍
基于RP2040实现小车游戏,通过A,B按键来控制小车左右移动来躲避障碍物,每过一个障碍物就会得一分,碰到障碍物游戏结束。
二、硬件介绍
-
采用树莓派Pico核心芯片RP2040:
-
双核Arm Cortex M0+内核,可以运行到133MHz
- 264KB内存
-
性能强大、高度灵活的可编程IO可用于高速数字接口
-
片内温度传感器、并支持外部4路模拟信号输入,内部ADC采样率高达500Ksps、12位精度
-
支持MicroPython、C、C++编程
-
-
板上功能:
-
240*240分辨率的彩色IPS LCD,SPI接口,控制器为ST7789
-
四向摇杆 + 2个轻触按键 + 一个三轴姿态传感器MMA7660用做输入控制
-
板上外扩2MB Flash,预刷MicroPython的UF2固件
-
一个红外接收管 + 一个红外发射管
- 一个三轴姿态传感器MMA7660
-
一个蜂鸣器
-
双排16Pin连接器,有SPI、I2C以及2路模拟信号输入
-
可以使用MicroPython、C、C++编程
- USB Type C连接器用于供电、程序下
-
三、设计思路
设计流程图如下:
首先进行硬件的初始化,然后简单的搭建外框架def out_frame(self):
,然后分别定义一个小车和障碍物的函数def obstacle_frame(self):
并且将小车和障碍物的坐标保存到一个数组中,为接下来的是否发生碰撞提供数据进行判断,通过调用这个函数可以将小车和障碍物显示在屏幕上,然后定义一个函数来移动小车和障碍物,具体就是通过不断的擦除和写入来实现方块的移动,小车移动函数为def car_move(self,yg_direction):
其后面的参数用来控制小车移动的方向。障碍物移动函数为:def obstacle_move(self):
碰撞检测函数为def car_obstacle(self):
在项目中遇到的一个问题就是障碍物移动到底部时的图像处理,一开始因为没有考虑好,导致直接把底部边框覆盖掉了;还有一个就是小车移动到边缘处的时候的处理,因为一开始先进行的图片渲染,然后才是数据的处理,导致小车移动到边缘处的时候,再按动AB两个键的时候导致无法移动,通过不间断打印含有小车信息的列表,观察数据的变化,最终排除了困难。
主模块如下:
import uos
import machine
import st7789 as st7789
from fonts import vga2_8x8 as font1
from fonts import vga1_16x32 as font2
import random
import framebuf
from time import sleep
from button import button
xAxis = machine.ADC(machine.Pin(28))
yAxis = machine.ADC(machine.Pin(29))
st7789_res = 0
st7789_dc = 1
disp_width = 240
disp_height = 240
CENTER_Y = int(disp_width/2)
CENTER_X = int(disp_height/2)
print(uos.uname())
spi_sck=machine.Pin(2)
spi_tx=machine.Pin(3)
spi0=machine.SPI(0,baudrate=4000000, phase=1, polarity=1, sck=spi_sck, mosi=spi_tx)
#
#print(spi0)
display = st7789.ST7789(spi0, disp_width, disp_width,
reset=machine.Pin(st7789_res, machine.Pin.OUT),
dc=machine.Pin(st7789_dc, machine.Pin.OUT),
xstart=0, ystart=0, rotation=0)
#car_image=open("/carar.bin", 'rb')
display.fill(st7789.BLACK)
display.text(font2, "Hello!", 72, 10)
display.text(font2, "Player", 72, 40)
display.text(font1, "Press any key to", 56, 100)
display.text(font2, "START", 80, 140)
display.text(font1, "www.luxiansheng.ltd", 44, 200)
#buttton Initialization
key_a=button(6)
key_b=button(5)
key_start=button(7)
key_select=button(8)
def any_key():
while True:
sleep(0.02)
if key_a.value()==True or key_b.value()==True or key_start.value()==True or key_select.value()==True:
print(key_a.value())
return True
else:
return False
while True:
display.text(font2, " ", 80, 140)
sleep(0.2)
display.text(font2, "START", 80, 140)
sleep(0.2)
keytf=any_key()
if keytf==True:
break
class car:
speed=8
score=0
obstacle_rect=[]
car_rect=[64,176,88,216]
wobs=0#随机数
collision=False
car_speed=8
def __init__(self,speed=1):
car.speed=speed
def out_frame(self):
for i in range(0,240,8):
for j in range(0,240,8):
display.fill_rect(j,i,8,8,st7789.BLACK)
display.fill_rect(0,0,8,240,st7789.color565(0xff,0,0))
display.fill_rect(0,0,240,8,st7789.color565(0xff,0,0))
display.fill_rect(0,232,240,8,st7789.color565(0xff,0,0))
display.fill_rect(232,0,8,240,st7789.color565(0xff,0,0))
display.fill_rect(144,0,8,240,st7789.color565(0xff,0,0))
display.text(font2, "Score", 152, 20)
display.text(font2,str(car.score),176,52)
display.text(font2,"Speed",152,84)
display.text(font2,str(car.speed),176,116)
display.text(font2,"Auth",160,148)
display.text(font2,"L&L",168,180)
def obstacle_frame(self):
car.wobs=random.randint(8,104)
car.obstacle_rect=[8,8,car.wobs,23,car.wobs+39,8,143,23]
def obstacle_move(self):
if car.obstacle_rect[3]>=231 :
if car.obstacle_rect[1]<232:
display.fill_rect(car.obstacle_rect[0],car.obstacle_rect[1],car.wobs-8,car.speed,st7789.BLACK)
display.fill_rect(car.obstacle_rect[4],car.obstacle_rect[5],104-car.wobs,car.speed,st7789.BLACK)
else:
return -1
else:
display.fill_rect(car.obstacle_rect[0],car.obstacle_rect[3]+1,car.wobs-8,car.speed,st7789.YELLOW)
display.fill_rect(car.obstacle_rect[4],car.obstacle_rect[7]+1,104-car.wobs,car.speed,st7789.YELLOW)
display.fill_rect(car.obstacle_rect[0],car.obstacle_rect[1],car.wobs-8,car.speed,st7789.BLACK)
display.fill_rect(car.obstacle_rect[4],car.obstacle_rect[5],104-car.wobs,car.speed,st7789.BLACK)
for i in range(1,8,2):
car.obstacle_rect[i]+=car.speed
def car_move(self,yg_direction):
if yg_direction>0:
car.car_rect[0]+=car.car_speed
car.car_rect[2]+=car.car_speed
if car.car_rect[0]<16 or car.car_rect[2]>136:
car.car_rect[0]=120
car.car_rect[2]=144
if car.car_rect[0]>=8 and car.car_rect[2]<=144:
display.fill_rect(car.car_rect[0]-car.car_speed,car.car_rect[1],24,40,st7789.BLACK)
display.fill_rect(car.car_rect[0],car.car_rect[1],24,40,st7789.GREEN)
'''display.fill_rect(car.car_rect[2],car.car_rect[1],car.car_speed,40,st7789.GREEN)
display.fill_rect(car.car_rect[0],car.car_rect[1],car.car_speed,40,st7789.BLACK)
'''
elif yg_direction<0:
car.car_rect[0]-=car.car_speed
car.car_rect[2]-=car.car_speed
if car.car_rect[0]<8 or car.car_rect[2]>144:
car.car_rect[0]=8
car.car_rect[2]=31
if car.car_rect[0]>=8 and car.car_rect[2]<=144:
display.fill_rect(car.car_rect[0]+car.car_speed,car.car_rect[1],24,40,st7789.BLACK)
display.fill_rect(car.car_rect[0],car.car_rect[1],24,40,st7789.GREEN)
#print(car.car_rect)
'''
if car.car_rect[0]<(8+car.car_speed):
car.car_rect[0]=8+car.car_speed
car.car_rect[2]=31+car.car_speed
if car.car_rect[2]>(144-car.car_speed):
car.car_rect[0]=120-car.car_speed
car.car_rect[2]=144-car.car_speed
'''
def car_obstacle(self):#小车与障碍物碰撞检测
if car.obstacle_rect[1]<216 and car.obstacle_rect[3]>176:
if car.car_rect[0]<car.wobs or car.car_rect[0]>(16+car.wobs):
car.collision=True
if __name__=="__main__":
pause=False
ad=car()
ad.out_frame()
display.fill_rect(car.car_rect[0],car.car_rect[1],24,40,st7789.GREEN)
while True:
if car.collision==True:
print(4545)
break
ad.obstacle_frame()
display.text(font2," ",176,52)
display.text(font2,str(car.score),176,52)
while True:
if car.collision==True:
break
ad.car_obstacle()
sleep(0.02)
if key_a.value()==True:
ad.car_move(5)
if key_b.value()==True:
ad.car_move(-5)
ad.obstacle_move()
#print(car.obstacle_rect)
if car.obstacle_rect[1]>231:
car.score+=1
break
while True:
display.text(font2,"GameOver",10,104)
sleep(0.2)
display.text(font2," ",10,104)
button模块如下:#对按键进行初始化
import time
from machine import Pin
class button:
def __init__(self, pin, callback=None, trigger=Pin.IRQ_RISING, min_ago=200):
#print("button init")
self.callback = callback
self.min_ago = min_ago
self._next_call = time.ticks_add(time.ticks_ms(), self.min_ago)
self.pin = Pin(pin, Pin.IN, Pin.PULL_UP)
self.pin.irq(trigger=trigger, handler=self.debounce_handler)
self._is_pressed = False
def call_callback(self, pin):
#print("call_callback")
self._is_pressed = True
if self.callback is not None:
self.callback(pin)
def debounce_handler(self, pin):
#print("debounce")
if time.ticks_diff(time.ticks_ms(), self._next_call) > 0:
self._next_call = time.ticks_add(time.ticks_ms(), self.min_ago)
self.call_callback(pin)
def value(self):
p = self._is_pressed
self._is_pressed = False
return p
然后需要导入st7789驱动
最终运行主模块,实现游戏的成功运行。
四、成果展示
1、开始界面如下,按任意键进入游戏,本项目没有加入速度难度等变量的自定义:
2、进入游戏后,障碍物会随机刷新,通过AB两个按键来实现小车的左右移动,当触碰到边缘上时,通过对小车列表数据的检测,小车将无法向边缘方向移动,当小车每次通过障碍得分Score上的数据将会加一,如果小车触碰到障碍物,游戏结束。
此项目只是实现一个比较简单的游戏,还有许多细节需要打磨,例如加入难度的控制,游戏结束跳转到主页面,历史最高分数的记录等等,我会接着去进行优化。