本帖最后由 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
复制代码
结果展示:
|
|