| 
 
| 本帖最后由 NeloKin 于 2023-1-5 14:58 编辑 
 项目背景:
 行空板UNIHIKER,作为一个带有LCD触控屏,自带linux环境和python库的设备,非常适合开发一些有关触控类的小游戏。作为一个节奏反应类游戏爱好者,我对“别踩白块”这款游戏十分喜爱,然而这家游戏的制作公司随后在游戏里加入了大量的广告,让我苦不堪言。借着这个机会,我将使用pygame库在行空板上重新复现这一经典游戏,帮助广大被无孔不入的广告恶心到的玩家。
 别踩白块,游戏正如他的名字所示,我们只需要不断踩着黑色方块前进即可,画面分为4行4列,每一行都只有一个黑色色块,玩家的任务就是按对他。本项目复刻了游戏的禅模式,在规定的时间内尽可能点击最多的黑块来取得高分。注意千万别点错了哦,点到白块可就直接游戏结束了。
 
 
 硬件介绍:
 
 
 
 软件清单:
 
 
 
 
 
 
 项目文件下载:
 
  别踩白块_行空.zip 
 由于背景音乐过大,无法上传,各位可以选择自己喜欢的bgm,改名为bgm.mp3后添加入sounds文件夹即可。
 
 代码详解:
 
 
 本项目使用pygame库实现。由于需要随机变量,计时器和蜂鸣器,还需添加time,random,pinpong等库。
 初始化传感器,设定蜂鸣器频率
 
 复制代码import pygame  # 导入pygame库
import random  # 导入random库
import numpy as np  # 导入numpy库
import time  # 导入time库
from pinpong.extension.unihiker import *#导入pinpong库(蜂鸣器使用)
from pinpong.board import Board,Pin
Board().begin() #初始化,选择板型和端口号,不输入则进行自动识别
buzzer.set_tempo(8,220) #设定蜂鸣器为8/8拍,BPM为220
 
 初始化pygame游戏窗口,初始化参数
 
 复制代码pygame.init()  # 初始化pygame
width =240     # 定义宽
height=320     # 定义高
size=(240,320)  # 定义尺寸
screen = pygame.display.set_mode(size) # 创建游戏窗口,尺寸为(240,320)
global start_page   #开始页定义
global game_page    #游戏页定义
start_page = True   #初始化开始页为真
game_page = False   #初始化游戏页为否
 
 将游戏窗口划分为一个个小方格,每个方格的宽为60,高为80,共计4行,4列。创建point类表示方块,rect方法绘制方块
 
 
 载入背景音乐,准备文字字体,已经初始黑块排列的生成。复制代码# 将游戏窗口划分为一个个小方格,每个方格的宽为60,高为80,共计4行,4列
ROW=4 # 定义行数,
COL=4 # 定义列数
cell_width=width/COL # 定义格子的宽
cell_height=height/ROW # 定义格子的高
class Point:
    row=0 # 行
    col=0 # 列
    def __init__(self,row,col): # 行 列
        self.row=row 
        self.col=col
point_color = (0,0,0)   #块颜色为黑
def rect(point,color):
    left=point.col*cell_width # 定义方格距离左边缘的距离
    top=point.row*cell_height # 定义方格距离上边缘的距离
    pygame.draw.rect(screen,color,(left,top,cell_width,cell_height)) # 在窗口上绘制矩形,颜色为color
 这里使用list类作为黑块的地址存储方式,存储四个point类,表示屏幕上显示的四个黑块,每行一个。初始黑块横坐标位置随机生成。
 
 复制代码# 载入背景音乐
pygame.mixer.music.load('sounds/bgm.mp3')
pygame.mixer.music.play(-1)     #循环播放
pygame.mixer.music.set_volume(1)    #设定音量可调整
# 文本准备
scorefont = pygame.font.SysFont('Arial', 40)  # 创建计分器Font字体对象
timerfont = pygame.font.SysFont('Arial', 20)  # 创建计时器Font字体对象
titlefont = pygame.font.Font('chinese.ttf', 40)    # 创建标题Font字体对象
#计时器准备
TIMER1_EVENT = pygame.USEREVENT
#创建初始黑块队列,在0到3内随机,每行一块
lists = [Point(row = 0, col = random.randint(0,COL-1)),
        Point(row = 1, col = random.randint(0,COL-1)),
        Point(row = 2, col = random.randint(0,COL-1)),
        Point(row = 3, col = random.randint(0,COL-1))
]  
 
 定义计时器功能,使用pygame的time库内set——time函数,作为时间事件触发,每次触发减少倒计时,并刷新时间显示。归零后跳转结算界面。
 
 复制代码#初始化计时器,设定倒计时秒数
def start_timer1():
    global seconds
    seconds = 60
    pygame.time.set_timer(TIMER1_EVENT, 1000)
#计时器触发时,复写秒数显示,并减少计时秒数。
def on_timer1():
    global seconds
    global game_over
    global game_page
    print (seconds)
    pygame.draw.rect(screen,(255,255,255),(210,0,25,22))
    timer_dis = timerfont.render(str(seconds),True,(255,0,0))
    screen.blit(timer_dis,(210,0))
    pygame.display.flip()
    seconds -= 1
    if seconds < 0:     #若归零,则跳转到结算页面,计时器停止
        pygame.time.set_timer(TIMER1_EVENT, 0)
        game_over = True
        game_page = False
 
 定义分割线绘制功能,美化游戏界面。
 
 复制代码def draw_line():    #绘制分割线
    pygame.draw.line(screen,(0,0,0),(0,80),(240,80))
    pygame.draw.line(screen,(0,0,0),(0,160),(240,160))
    pygame.draw.line(screen,(0,0,0),(0,240),(240,240))
    pygame.draw.line(screen,(0,0,0),(60,0),(60,320))
    pygame.draw.line(screen,(0,0,0),(120,0),(120,320))
    pygame.draw.line(screen,(0,0,0),(180,0),(180,320))
 接下来为主程序循环部分,接在while True函数内。
 
 开始界面部分:加载背景,加载标题,加载开始游戏图片。若鼠标移动到开始游戏图片,替换同字异色图片,点击跳转至游戏界面。
 
 复制代码while start_page: # 当进入开始页面
        for event in pygame.event.get():  # 遍历所有事件
            if event.type == pygame.QUIT:  # 如果单击关闭窗口,则退出
                pygame.quit()  # 退出pygame
            bg = pygame.image.load("pic/bg.png").convert_alpha()    #载入背景图片
            bg = pygame.transform.scale(bg, (240, 320))             #修改背景图片大小,以匹配屏幕
            title1 = titlefont.render('别踩', True, (0, 0, 0))
            title2 = titlefont.render('白块', True, (255, 255, 255))    #生成双色标题
            screen.blit(bg,(0,0))   #绘制背景
            screen.blit(title1, (40, 40))
            screen.blit(title2, (120, 40))  #绘制标题
            screen.blit(pygame.image.load("pic/start-5.png"), (30, 190))  # 在(30,190)显示图片start-5.png
            pygame.display.update()     #显示开始界面到屏幕
            
            
            
            global t_x, t_y # 定义两个全局变量t_x, t_y
            t_x, t_y = pygame.mouse.get_pos() # 获取鼠标的x和y坐标位,存储到变量t_x, t_y中
            if 30 <= t_x <= 200 and 190 <= t_y <= 250:  # 18*50 # 如果鼠标移动到“开始游戏”的图片范围内
                screen.blit(pygame.image.load("pic/start-6.png"),(30, 190))  # 在(30,190)切换图片为start-6.png
            if event.type == pygame.MOUSEBUTTONUP and 30 <= t_x <= 200 and 190 <= t_y <= 250: # 如果鼠标被释放且横纵坐标在“开始游戏”图片的范围内
                game_page = True    # 定义游戏页面状态为True,进入游戏页面
                start_page = False  # 定义开始页面状态为False,退出开始页面    
        pygame.display.flip()  # 更新全部显示
 
 游戏界面部分。
 初始化参数,绘制初始游戏界面以及初始黑块布局。倒计时开始。
 核心逻辑是判断鼠标点击事件,判定是否为最后一行。
 若在最后一行,并在黑块区域,则得分+1,刷新黑块位置,蜂鸣器震动反馈。若不在黑块位置,则游戏结束。
 (由于行空板使用的电容屏精度较低,容易发生误触或者错误判定,本代码中已默认屏蔽点错结束的分支,若有兴趣者可自行取消注释开启)
 
 复制代码if game_page:   #游戏界面
        game_over = False   #游戏结束为否
        start_page = False  #开始界面为否
        screen.fill((255,255,255))  # 填充白色,刷新背景
        Score = 0   #初始化分数
        start_timer1()  #开始计时器
        draw_line()     #分割线绘制
        for sub_list in lists:      #绘制初始黑块
             rect(sub_list,point_color)
        while game_page:
            pygame.display.flip()  # 更新全部显示
        
            for event in pygame.event.get():
                if event.type == pygame.QUIT:  # 如果单击关闭窗口,则退出
                    pygame.quit()  # 退出pygame
                elif event.type == TIMER1_EVENT:    #计时器触发,每秒一次
                    on_timer1() #触发倒计时程序
                if event.type == pygame.MOUSEBUTTONUP and 240 <= event.pos[1] <= 320:   #判定鼠标点击是否为最后一行
                    
                    if lists[3].col*60 <= event.pos[0] <= lists[3].col*60+60:   #判定鼠标点击位置是否有黑块
                        i = 3
                        for i in range(3,0,-1):
                            lists[i].col = lists[i-1].col   #将每行黑块的列数传递给下一行黑块,实现黑块的下降
                        lists[0].col =  random.randint(0,COL-1)     #最上方黑块在随机列重新生成
                        Score = Score + 1   #分数加一
                        print (Score)
                        #重新绘制游戏画面,改变黑块位置并重绘计时和分数
                        screen.fill((255,255,255))
                        for sub_list in lists:
                            rect(sub_list,point_color)
                        pygame.draw.rect(screen,(255,255,255),(210,0,25,22))
                        timer_dis = timerfont.render(str(seconds),True,(255,0,0))   #计时重绘
                        screen.blit(timer_dis,(210,0))
                        pygame.draw.rect(screen,(255,255,255),(0,0,25,22))
                        Score_dis = timerfont.render(str(Score),True,(255,0,0))     #计分绘制
                        screen.blit(Score_dis,(0,0))
                        buzzer.pitch(988,1)     #蜂鸣器触发
                        draw_line()             #分割线绘制
                        pygame.display.flip()   #显示刷新
                    '''else:    #若按到了白块
                        game_over = True
                        game_page = False'''#跳转到结算页面
 
 结算界面部分:
 当游戏跳出游戏界面后,显示最终得分,并显示重新开始标题。
 若判定到鼠标点击标题部分,则跳转回游戏部分,重新计时计分。
 
 复制代码if game_over:   #结算页面
            screen.fill((255,255,255))  #覆盖背景
            Score_fin = scorefont.render("Score:"+ str(Score),True,(255,0,0))
            screen.blit(Score_fin,(25,150))     #显示最终得分
            retry = titlefont.render('重新开始?', True, (0, 0, 0)) 
            screen.blit(retry,(40,200))     #绘制重新开始标题
            pygame.display.flip()   显示刷新
            while game_over:
                 for event in pygame.event.get():
                        if event.type == pygame.QUIT:  # 如果单击关闭窗口,则退出
                            pygame.quit()  # 退出pygame
                        if event.type == pygame.MOUSEBUTTONUP and 40 <= event.pos[0] <= 200 and 190 <= event.pos[1] <= 240: # 如果鼠标被释放且横纵坐标在“重新开始”标题的范围内
                            game_page = True    #重新返回游戏界面
                            game_over = False
 
 结果展示:
 
 
 
 
 
 
 
 
 
 
 
 
 | 
 |