目录
- 导语
- 正文
- 一、环境准备
- 二、代码实现
- 三、效果实现
导语
今天小编要为大家介绍一款小编自己用代码码出来的赛车风格的打字小游戏
取名暂定为《极限车神》打字小游戏。
这款Pygame小游戏在玩法上可以说十分创新,不仅能游戏还能学到很多不同类型的编程代码关键字的语言,比如我就做了这几款类型:Python、Java等。
下面我们就来看一下这款赛车风格打字微信小游戏《极限车神》具体怎么玩吧!
正文
让我们先来看看这款Pygame的经典点评吧,让大家快速了解一下《极限车神》打字小游戏
**关键词:**休闲、赛车、竞技、竞速、操作、打字、闯关、限时。
**氪金指数:**0氪金。
推荐星数:4星(满星5星)
难度星数:5星(满星5星)
这是一款有点考验玩家打字速度操作水平的赛车游戏,玩起来还有点意思,考验大家手速的时候到啦。平常打字看键盘的小可爱你准备好了嘛?
一、环境准备
1)运行环境
本文用到的环境如下——
Python3、Pycharm社区版,第三方模块:Tkinter、Pygame。部分自带的库只要安装完Python就可以直接使用了
一般安装:pip install +模块名 镜像源安装:pip install -i https://pypi.douban.com/simple/+模块名 (之前有说过安装报错的几种方式跟解决方法,不会安装的可以去看下,还有很多国内镜像源也有文章的)
还有准备一些数据源素材等这些大家可以用自己准备的哦!
图片文本素材等如下——
小游戏当然要有音乐更配啦~
二、代码实现
1)导入库
import tkinter as tk | |
import threading,random,time,datetime | |
import inspect | |
import ctypes | |
from tkinter import ttk | |
from pygame import mixer |
2)主程序
FLAG = True # 定义一个信号量,用于判断最前面的小车是否到达终点,游戏结束变为False | |
CARFLAG = True # 定义小车真正在窗口上移动时的信号量,当小车在窗口上静止不动时变为False | |
ROAD = '' # 赛道(Python,Java,MySql赛道) | |
# 玩家类 | |
class Player: | |
def __init__(self,username): | |
self.username = '[玩家]'+username | |
# 创建小车 | |
def car_create(self,c,carposy_list): | |
self.car = Car('./sucai/car.png', carposy_list, 'img1', self.username) | |
self.car_img = tk.PhotoImage(file=self.car.filepath) | |
c.create_image(self.car.posx, self.car.posy, image=self.car_img, tag=self.car.cartag) | |
c.create_text(self.car.posx-, self.car.posy, text=self.car.username, font=("Purisa", 16), fill='white', tag=self.car.username) | |
# 电脑类 | |
class Computer: | |
def __init__(self,username='[电脑]'): | |
self.username = username | |
# 创建小车 | |
def car_create(self,c,filepath, posy, cartag, username): | |
self.car = Car(filepath, posy, cartag, username) | |
self.car_img = tk.PhotoImage(file=self.car.filepath) | |
c.create_image(self.car.posx, self.car.posy, image=self.car_img, tag=self.car.cartag) | |
c.create_text(self.car.posx-, self.car.posy, text=self.car.username, font=("Purisa", 16), fill='white',tag=self.car.username) | |
# 赛道类 | |
class Road: | |
def __init__(self,roadtype): | |
self.text = [] | |
with open(f'./sucai/{roadtype}关键字.txt', mode='r', encoding='utf-') as f: | |
for line in f: | |
self.text.append(line.strip().split('\t')[]) | |
self.road_number = int(len(self.text)*.2) if ROAD == 'Python' else int(len(self.text)*1.5) # 赛道的数量 | |
self.road_length = (self.road_number -) * 1000 # 需要跑完赛道的长度 | |
self.road_s =.0 | |
# 起点线 | |
def begin(self,c): | |
c.create_rectangle(, 50, 150, 352, fill='#FFF6EE', tag='begin') | |
c.create_text(, 200, text='R\ne\na\nd\ny\ni\nn\ng\n \nG\nO', font=("Purisa", 20), fill='black', tag='begin') | |
# 终点线 | |
def destination(self,c): | |
self.c_end = c.create_text(self.road_length -, 200, text='终\n点', font=("Purisa", 52), fill='white',tag='end') | |
# 路相对于车移动 | |
def roadmove(self,c,tag_road): | |
global FLAG,player | |
self.tag_road = tag_road | |
if FLAG: | |
self.road_s += player.car.speed | |
if self.road_s >: | |
self.road_s =.0 | |
if self.tag_road: | |
c.delete(self.tag_road.pop()) | |
for i in range(, self.road_number + 1): | |
c.move(i, -player.car.speed,) | |
c.move(self.c_end, -player.car.speed,) | |
# 小树类 | |
class Tree: | |
speed = | |
def __init__(self, posx,posy,filepath,treetag='tree'): | |
self.posx = posx | |
self.posy = posy | |
self.filepath = filepath | |
self.treetag = treetag | |
# 创建树 | |
def tree_create(self,c): | |
self.tree_img = tk.PhotoImage(file=self.filepath) | |
c.create_image(self.posx, self.posy, image=self.tree_img, tag=self.treetag) | |
# 树相对于车移动 | |
def treemove(self, c): | |
global FLAG,player | |
if FLAG: | |
if self.posx >: | |
self.speed = player.car.speed | |
self.posx -= self.speed | |
c.delete(self.treetag) | |
c.create_image(self.posx, self.posy, image=self.tree_img, tag=self.treetag) | |
else: | |
self.posx = random.randint(,950) | |
self.speed = player.car.speed | |
self.posx -= self.speed | |
c.create_image(self.posx, self.posy, image=self.tree_img, tag=self.treetag) | |
# 小车类 | |
class Car: | |
speed =.0 # 初始化小车的移动速度 | |
posx = # 初始化小车的初始位置,x轴方向 | |
car_move_distance = [] # 记录小车,以初始化速度speed =.0开始移动的路程 | |
def __init__(self, filepath, posy, cartag, username): | |
self.filepath = filepath | |
self.posy = posy | |
self.cartag = cartag | |
self.username = username | |
# 小车移动方法 | |
def car_move(self,c,car_img): | |
global FLAG | |
print(self.username,'准备就绪...') | |
self.car_img = car_img | |
def run(): | |
if FLAG: | |
if CARFLAG: | |
self.posx += self.speed | |
self.car_move_distance = [] | |
self.car_move_distance.append((self.cartag, self.posx)) | |
c.delete(self.cartag) | |
c.create_image(self.posx, self.posy, image=self.car_img, tag=self.cartag) | |
c.delete(self.username) | |
c.create_text(self.posx, self.posy, text=self.username, font=("Purisa",), fill='white',tag=self.username) | |
else: | |
c.delete(self.cartag) | |
c.create_image(self.posx, self.posy, image=self.car_img, tag=self.cartag) | |
c.delete(self.username) | |
c.create_text(self.posx, self.posy, text=self.username, font=("Purisa",), fill='white',tag=self.username) | |
c.after(, run) | |
else: print(f'{self.username} 小车停止') | |
run() | |
# 小车移动速度随时变化 | |
def car_speed_change(self): | |
global FLAG,player | |
time.sleep(.3) | |
while FLAG: | |
if player.car.speed <: self.speed = random.uniform(player.car.speed - 0.3, player.car.speed + 0.7) | |
elif < player.car.speed < 18: self.speed = random.uniform(player.car.speed - 0.8, player.car.speed + 0.5) | |
else: self.speed = random.uniform( - 1.1, 19 + 0.3) | |
time.sleep() | |
# 打字类 | |
class display_text: | |
def __init__(self, gw): | |
self.gw = gw | |
self.l_oop = [] # 存储提示标签对象 | |
self.l_oop = [] # 存储玩家打字时的标签对象 | |
self.cpm_list = [] #用正确的总输入字符次数除以总花费的时间(以分钟为单位) | |
self.typspeed =.0 # 记录打字的速度 | |
self.anwser = '' # 拼接答案 | |
self.text = self.gw.road.text # 单词内容 | |
self.text = self.text.copy() | |
self.text.extend(self.text) # 单词内容翻倍 x 2 | |
self.text_number = len(self.text) / # 单词个数 | |
self.loading_text() | |
# 加载打字内容 | |
def loading_text(self): | |
col = # 列数 | |
number = # 每页展示20个字 | |
for i, text in enumerate(self.text[:number]): | |
l = tk.Label(gw.text_frame, text=text, bg='white', font=('微软雅黑', 15)) | |
l = tk.Label(gw.text_frame, text='', bg='white', font=('微软雅黑', 15)) | |
if i < int(number /): | |
l.grid(row=1, column=col, ) | |
l.grid(row=2, column=col, ) | |
elif int(number /) <= i < number: | |
l.grid(row=3, column=col - int(number / 2), ) | |
l.grid(row=4, column=col - int(number / 2), ) | |
col += | |
l.focus_set() | |
l.bind("<Key>", self.l_bind) | |
self.l_oop.append(l1) | |
self.l_oop.append(l2) | |
# 标签绑定键盘事件 | |
def l_bind(self, event): | |
# print(f"事件触发键盘输入:{event.char},对应的ASCII码:{event.keycode}") | |
global FLAG | |
if not FLAG: return | |
if not self.text: return | |
if event.keycode ==: | |
self.anwser = '' | |
self.l_oop[0].configure(text='') | |
return | |
if not <= event.keycode <= 90: return | |
self.anwser += event.char | |
self.l_oop[0].configure(text=self.anwser, bg='#FFFAE3') | |
result = self.text[] | |
if self.anwser.strip() == result: | |
self.cpm_list.append(result) | |
self.l_oop[0].configure(fg='red') | |
self.text.remove(result) | |
self.l_oop.pop(0) | |
self.l_oop.pop(0) | |
self.anwser = '' | |
if not self.l_oop and len(self.text) != 0: | |
gw.text_frame.destroy() | |
gw.display_text_frame() | |
self.loading_text() | |
return | |
if len(self.anwser) >= len(result): | |
self.anwser = '' | |
# 检测打字速度, 每分钟输入了多少字符 | |
def typing_speed(self): | |
global FLAG | |
time_ = | |
while FLAG: | |
time_ += | |
try: | |
self.cpm = round(len(''.join(self.cpm_list)) / time_ *) | |
self.wpm = round(len(self.cpm_list) / (time_ /))# 用来表示打字速度,即每分钟打多少个字(或单词) | |
gw.cpm_label.configure(text=f'cpm:{self.cpm}') | |
gw.wpm_label.configure(text=f'wpm:{self.wpm}') | |
except: | |
self.cpm = | |
self.wpm = | |
self.typspeed = self.cpm / | |
time.sleep() | |
# 游戏窗口类 | |
class GameWindow: | |
bgcolor = '#E' # 画布颜色 | |
root = tk.Tk() | |
root.title('打字小游戏开发') | |
screenwidth = root.winfo_screenwidth() | |
screenheight = root.winfo_screenheight() | |
def __init__(self): | |
pass | |
# 窗口页面的大小 | |
def window_page_size(self,windowsize): | |
rootwidth, rootheight = windowsize | |
rootposx = int((self.screenwidth - rootwidth) /) | |
rootposy = int((self.screenheight - rootheight) /) - 100 | |
self.root.geometry('{}x{}+{}+{}'.format(rootwidth, rootheight, rootposx, rootposy)) | |
time.sleep(.1) | |
# 选择赛道 | |
def select_road(self): | |
def func(event): | |
global FLAG,CARFLAG,ROAD | |
FLAG = True | |
CARFLAG = True | |
road = event.widget["text"] | |
print(road) | |
ROAD = road[:-] | |
self.select_road_frame.destroy() | |
self.window_page_size((, 560)) | |
self.create_canvas() | |
configuration_project(ROAD) | |
self.recording_time() | |
self.display_typing_speed() | |
self.replay_button() | |
self.return_button() | |
self.window_page_size((, 300)) | |
self.select_road_frame = tk.Frame(self.root) | |
self.select_road_frame.place(x=, y=0, width=500, height=300) | |
for road in ['Python赛道','Java赛道','MySql赛道']: | |
b = tk.Button(self.select_road_frame, text=road,font=('华文行楷',),relief=tk.RAISED,cursor='hand2',width=12,height=1) | |
b.pack(pady=) | |
b.bind("<Button->", func) | |
# 创建一张画布 | |
def create_canvas(self): | |
self.canvas = tk.Canvas(self.root, width=, height=400, bg=self.bgcolor) | |
self.canvas.place(x=, y=0) | |
# 创建一条路——赛道 | |
def create_road(self,roadtype): | |
# self.road = Road('Java') # 实列化一个路的对象(Java类型的路) | |
self.road = Road(roadtype) # 实列化一个路的对象(Python类型的路) | |
self.road_img = tk.PhotoImage(file='./sucai/road.png') | |
self.c_road = [self.canvas.create_image(i,, image=self.road_img, tag=f'c_road{i}') for i in range(500, self.road.road_number * 1000, 1000)] | |
self.tag_road = [f'c_road{i}' for i in range(, (self.road.road_number - 2) * 1000, 1000)] | |
# 创建两棵树 | |
def create_tree(self): | |
self.tree = Tree(posx=160, posy=350,filepath='./sucai/tree1.png',treetag='tree1') | |
self.tree.tree_create(self.canvas) | |
self.tree = Tree(posx=230, posy=340,filepath='./sucai/tree2.png', treetag='tree2') | |
self.tree.tree_create(self.canvas) | |
# 创建跑程进度条 | |
def displayprogressbar(self,caroop_list): | |
maximum = self.road.road_length | |
self.progressbar_list = [] | |
for i in range(): | |
progressbar = ttk.Progressbar(self.canvas, length=, maximum=maximum) | |
progressbar.place(x=, y= 20 if i == 0 else i*40 + 20) | |
tk.Label(self.canvas,text=caroop_list[i].username,fg='#').place(x=215,y=20 if i == 0 else i*40 + 20) | |
self.progressbar_list.append(progressbar) | |
# 重新开始按钮 | |
def replay_button(self): | |
def function(): | |
global FLAG,CARFLAG,end_label_list,dt | |
FLAG = False | |
CARFLAG = False | |
for t in threading.enumerate()[:]: | |
stop_thread(t) | |
self.canvas.destroy() | |
self.text_frame.destroy() | |
if end_label_list: | |
for i in end_label_list: i.destroy() | |
del dt | |
FLAG = True | |
CARFLAG = True | |
self.create_canvas() | |
configuration_project(ROAD) | |
self.recording_time() | |
self.return_button() | |
self.replaybutton_img = tk.PhotoImage(file='./sucai/replay.png') | |
tk.Label(self.root,text='重新开始').place(x=,y=480) | |
replaybutton = tk.Button(self.root, image=self.replaybutton_img, command=function) | |
replaybutton.place(x=,y=400) | |
# 返回按钮 | |
def return_button(self): | |
def function(): | |
global FLAG,CARFLAG,ROAD,end_label_list,dt | |
FLAG = False | |
CARFLAG = False | |
ROAD = '' | |
for t in threading.enumerate()[:]: | |
stop_thread(t) | |
self.canvas.destroy() | |
self.text_frame.destroy() | |
self.cpm_label.destroy() | |
self.wpm_label.destroy() | |
if end_label_list: | |
for i in end_label_list: i.destroy() | |
del dt | |
self.select_road() | |
self.returnbutton_img = tk.PhotoImage(file='./sucai/return.png') | |
returnbutton = tk.Button(self.root,text='返回',image=self.returnbutton_img,command=function) | |
returnbutton.place(x=,y=50) | |
# 记录游戏时间 | |
def recording_time(self): | |
time_label = tk.Label(self.canvas, text='时长::00:00', font=("华文行楷", 15), background='#DAEFE6') | |
time_label.place(x=, y=20) | |
start_time = datetime.datetime.now() | |
def run(): | |
global FLAG | |
if FLAG: | |
time_label.after(, run) | |
update_time = datetime.datetime.now() - start_time | |
self.time_ = f'时长:{update_time}'.split('.')[] | |
time_label.configure(text=self.time_) # 不显示时长的毫秒值 | |
run() | |
# 展示文本 | |
def display_text_frame(self): | |
self.text_frame = tk.Frame(self.root, bg='white') | |
self.text_frame.place(x=, y=400, width=800, height=150) | |
# 显示打字速度 | |
def display_typing_speed(self): | |
self.cpm_label = tk.Label(self.root,text='cpm:', font=("微软雅黑", 13),fg='#A52A2A') | |
self.cpm_label.place(x=,y=410) | |
self.wpm_label = tk.Label(self.root, text='wpm:', font=("微软雅黑", 13),fg='#A52A2A') | |
self.wpm_label.place(x=, y=440) | |
# 播放背景音乐函数 | |
def playmusic(self,): | |
global FLAG | |
mixer.init() | |
mixer.music.load('./sucai/bgmusic.mp') | |
mixer.music.play() | |
while FLAG: | |
time.sleep() | |
mixer.music.stop() | |
# 显示窗口 | |
def displaywindow(self): | |
self.root.resizable(False, False) | |
self.root.mainloop() | |
def _async_raise(tid, exctype): | |
tid = ctypes.c_long(tid) | |
if not inspect.isclass(exctype): | |
exctype = type(exctype) | |
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype)) | |
if res ==: | |
raise ValueError("invalid thread id") | |
elif res !=: | |
ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None) | |
raise SystemError("PyThreadState_SetAsyncExc failed") | |
# 停止正在运行中的线程 | |
def stop_thread(thread): | |
_async_raise(thread.ident, SystemExit) | |
# 监测小车移动时的数据变化 | |
def monitoring_data(gw,caroop_list,dt): | |
global FLAG,CARFLAG,end_label_list | |
time.sleep(.5) # 休眠2.5秒后才开始 | |
gw.displayprogressbar(caroop_list) #显示完成跑道的进度条 | |
gw.canvas.delete('begin') # 删除起点线 | |
gw.road.destination(gw.canvas) # 创建终点线 | |
player_car_s = player.car.car_move_distance[][1] | |
timesleep_posx_s_list = {} | |
for i in caroop_list: | |
timesleep_posx_s_list[i.username] = i.car.car_move_distance[0][1] | |
print(i.username,'已跑起来') | |
while FLAG: | |
CARFLAG = False | |
for idx,p in enumerate(gw.progressbar_list): | |
p['value'] = timesleep_posx_s_list[caroop_list[idx].username] | |
if max(timesleep_posx_s_list.values()) > gw.road.road_length: | |
FLAG = False | |
ranking = sorted(timesleep_posx_s_list.items(), key=lambda x: x[1]) # 排名 | |
for idx,username in enumerate(ranking[::-]): | |
l = tk.Label(gw.root, text=username[] + f' 第{idx + 1}名', font=('微软雅黑', 28), bg='white') | |
l.pack(pady=) | |
end_label_list.append(l) | |
print('游戏结束') | |
return | |
# player.car.speed = | |
player.car.speed = dt.typspeed #使小车速度 = 打字速度 | |
player_car_s += player.car.speed #让小车此时已移动的路程加上小车速度 | |
timesleep_posx_s_list[player.username] = player_car_s | |
for i, caroop in enumerate(caroop_list[:]): | |
caroop.car.posx += caroop.car.speed - player.car.speed | |
timesleep_posx_s_list[caroop.username] = timesleep3_posx_s_list[caroop.username] + caroop.car.speed | |
gw.road.roadmove(gw.canvas, gw.tag_road) # 赛道相对小车的速度,开始移动 | |
gw.tree.treemove(gw.canvas) # 树1相对小车的速度,开始移动 | |
gw.tree.treemove(gw.canvas) # 树2相对小车的速度,开始移动 | |
time.sleep(.0095) | |
# 配置运行流程 | |
def configuration_project(roadtype): | |
print('游戏开始') | |
global player,gw,dt,end_label_list | |
tplaymusic = threading.Thread(target=gw.playmusic) # 开启一个播放背景音乐的线程 | |
tplaymusic.setDaemon(True) | |
tplaymusic.start() | |
gw.create_road(roadtype) # 创建赛道 | |
gw.create_tree() # 创建树 | |
gw.road.begin(gw.canvas) #创建一条起点线 | |
gw.display_text_frame() # 创建展示文本的frame | |
end_label_list = [] # 游戏结束时,存放显示名次的标签列表 | |
caroop_list = [] # 添加所有玩家(小车)对象 | |
carposy_list = [, 147, 250, 309] # 定义四个小车初始化的y轴坐标 | |
player = Player('yyds') # 创建一个真人玩家对象 | |
player.car_create(gw.canvas, carposy_list[]) # 生成一个属于真人玩家的小车 | |
caroop_list.append(player) | |
for i in range(): #创建三个电脑玩家 | |
computer = Computer(f'[电脑]{i+}') | |
computer.car_create(gw.canvas,f'./sucai/car{i+}.png', carposy_list[i+1], f'img{i+2}', computer.username)# 生成属于电脑玩家的小车 | |
caroop_list.append(computer) | |
for caroop in caroop_list: | |
tcm = threading.Thread(target=caroop.car.car_move,args=(gw.canvas,caroop.car_img)) # 为每一个玩家开启一个线程,用来启动各自的小车 | |
tcm.setDaemon(True) | |
tcm.start() | |
tcsc = threading.Thread(target=caroop.car.car_speed_change) #为每一辆小车定时随机改变小车的移动速度 | |
tcsc.setDaemon(True) | |
tcsc.start() | |
dt = display_text(gw) # 实例化一个打字所用的文本内容对象 | |
tdt = threading.Thread(target=dt.typing_speed) # 开启线程来记录打字的速度 | |
tdt.setDaemon(True) | |
tdt.start() | |
tmd = threading.Thread(target=monitoring_data, args=(gw, caroop_list, dt)) # 开启线程来记录每一辆小车移动时的数据变化 | |
tmd.setDaemon(True) | |
tmd.start() | |
def start(): | |
global gw | |
gw = GameWindow() # 实例化一个游戏窗口对象 | |
gw.select_road() # 选择赛道 | |
gw.displaywindow() | |
if __name__ == '__main__': | |
start() |
三、效果实现
这款《极限车神打字小游戏》顾名思义就是一个打字的游戏,只有打字速度快,你的赛车才能
跑起来哦。游戏分为三个赛道,都是关于不同编程的关键字英文,边玩游戏还能提升自己哦,
两全其美哦!
1)游戏界面
2)Python赛道游戏界面
3)赛道起飞