pygame 入门实例教程 1 - 复古方块赛车游戏
作者自我介绍:大爽歌, b站小UP主 ,直播编程+红警三 ,python1对1辅导老师 。
本教程步骤明确,过程清晰简明,最终代码量250行上下,适合学习pygame的新手。
项目代码已上传到我的github: https://github.com/BigShuang/simple-brick-games-by-pygame
本文分7部分,每一部分最终代码均存到项目仓库下的1 car racing
游戏已录制成视频,投稿至本人b站:点击前往b站观看游戏视频
游戏运行效果,截图如下
〇 - 初始准备工作
- 本项目使用的python3版本*(如果你用python2,我不知会怎么样)*
- Ide推荐大家选择pycharm*(不同ide应该没影响)*
- 需要安装第三方库pygame,安装方法(windows电脑,mac系统本人实测与pygame不兼容,强行运行本项目卡成ppt)
电脑打开cmd命令窗口,输入pip install pygame
如果电脑上安的是pip3则是pip3 install pygame
补充说明:
由于众所周知的原因,安装过程中下载可能十分缓慢,甚至由此导致安装失败
此时建议大家尝试使用镜像下载
—国内源—
清华:https://pypi.tuna.tsinghua.edu.cn/simple
阿里云:http://mirrors.aliyun.com/pypi/simple/
中国科技大学: https://pypi.mirrors.ustc.edu.cn/simple/
华中理工大学:http://pypi.hustunique.com/
山东理工大学:http://pypi.sdutlinux.org/
豆瓣:http://pypi.douban.com/simple/
使用办法 pip install xxxx -i jinxiangurl
具体到pygame,则是:
pip install pygame -i https://pypi.tuna.tsinghua.edu.cn/simple
一、实现基础窗口
0 - 新建car_racing.py
文件,内容如下
import pygameWIN_WIDTH = 600 # 窗口宽度
WIN_HEIGHT = 900 # 窗口高度pygame.init() # pygame初始化,必须有,且必须在开头
# 创建主窗体
win=pygame.display.set_mode((WIN_WIDTH,WIN_HEIGHT))
此时运行car_racing.py
,会发现一个一闪而逝的窗口,
1 - 进一步,我们自然而然的就要思考这些问题
- 游戏窗口大小设置为多少合适?
由于是方块游戏,所以可以根据方格尺寸、行格字数、列格子数去计算出窗口的宽高 - 怎么维持住这个窗口?
通过while循环去实现 - 但是简单的循环只是单纯的将界面卡住,怎么实现刷新?
在循环体内使用pygame.display.update()
语句进行界面的更新 - 循环的刷新频率不做节制的话,界面会飞速刷新导致卡死,怎么办?
pygame有专门的对象pygame.time.Clock
用于去控制循环刷新的频率,创建pygame.time.Clock
对象后,调用该对象的tick()
方法,函数参数为每秒刷新次数,就可以设置循环每秒刷新频率,术语叫做帧率
可前往官方文档观看pygame.time.Clock的更多细节,https://www.pygame.org/docs/ref/time.html#pygame.time.Clock
- 根据上面的思路,修改
car_racing.py
后如下
import pygameC, R = 11, 20 # 11列, 20行
CELL_SIZE = 40 # 格子尺寸FPS=60 # 游戏帧率
WIN_WIDTH = CELL_SIZE * C # 窗口宽度
WIN_HEIGHT = CELL_SIZE * R # 窗口高度pygame.init() # pygame初始化,必须有,且必须在开头
# 创建主窗体
clock = pygame.time.Clock() # 用于控制循环刷新频率的对象
win = pygame.display.set_mode((WIN_WIDTH,WIN_HEIGHT))while True:clock.tick(FPS) # 控制循环刷新频率,每秒刷新FPS对应的值的次数pygame.display.update()
此时运行car_racing.py
, 就可以得到一个最最最基础的窗口了,
2 - 但是,这个窗口有个大问题, 此时窗口的关闭按钮很容易出bug(卡死)
所以需要自己去重新实现这个窗口关闭功能,需要在循环体内添加如下代码
# 获取所有事件
for event in pygame.event.get():if event.type == pygame.QUIT:# 判断当前事件是否为点击右上角退出键pygame.quit()sys.exit() # 需要提前 import sys
同时我们一般会希望能够设置下背景的颜色
比如,这个游戏的背景色是轻灰色(200, 200, 200)
那么设置背景的代码为
bg_color = (200, 200, 200)
win.fill(bg_color)
不过需要注意的是,这段代码放在不同的位置会产生不同的效果。
放在while循环之前,代表只绘制一次背景,会被后面绘制的东西遮住。
放在while循环中,则是每一帧都会绘制一次背景,一般用于去覆盖掉那些希望删掉的元素。
本阶段最后car_racing.py
如下
import pygame
import sysFPS=60 # 游戏帧率
WINWIDTH = 600 # 窗口宽度
WINHEIGHT = 900 # 窗口高度pygame.init() # pygame初始化,必须有,且必须在开头
# 创建主窗体
clock = pygame.time.Clock() # 用于控制循环刷新频率的对象
win = pygame.display.set_mode((WINWIDTH,WINHEIGHT))bg_color = (200, 200, 200)
win.fill(bg_color)while True:# 获取所有事件for event in pygame.event.get():if event.type == pygame.QUIT:# 判断当前事件是否为点击右上角退出键pygame.quit()sys.exit()clock.tick(FPS) # 控制循环刷新频率,每秒刷新FPS对应的值的次数pygame.display.update()
到这里,基础窗口就完成了~
二、实现基础方块绘制
1 最基础的绘制方法
pygame里面,绘制一个小方格实际上是很简单的。
新建一个方格色块(放在while循环之前)
area = pygame.Surface((CELL_SIZE, CELL_SIZE))
enemy_color = (50, 50, 50)
area.fill(enemy_color)
再将方格色块放在窗口对象上(放在while循环中,clock.tick(FPS)
之前)
win.blit(area, (CELL_SIZE, 0))
此时运行,效果如图
不过这个绘制方法的问题在于, 后面的移动操作管理起来颇为不便。
要在while循环中编写各种代码来实现area这个色块的位置变换的话,代码写起来麻烦,管理起来也乱。
pygame 给我们提供了一个Sprite
类,用于实现可以移动的二维图像对象。
我们将继承这个类,并封装一些需要的方法,方便移动以及管理。
2 使用pygame的Sprite类
In computer graphics, a sprite is a two-dimensional bitmap that is integrated into a larger scene, most often in a 2D video game.
在计算机图形学中,精灵是一种二维位图,它被集成到一个更大的场景中,通常在二维视频游戏中。
个人理解,sprite是一个计算机术语,代表界面中可以移动的二维位图。
继承Sprite
, 新建Block
类如下(在新建win对象,应该是14行,后面添加如下代码)
class Block(pygame.sprite.Sprite):def __init__(self, c, r, color):super().__init__()self.cr = [c, r]self.x = c * CELL_SIZEself.y = r * CELL_SIZEself.image = pygame.Surface((CELL_SIZE, CELL_SIZE))self.image.fill(color)self.rect = self.image.get_rect()self.rect.move_ip(self.x, self.y)
删掉前面 1 最基础的绘制方法中, area的相关代码
在while循环之前, 添加如下代码
enemy_color = (50, 50, 50)
block = Block(1, 1, enemy_color)
win.blit(block.image, block.rect)
此时运行效果和1中相同。
三 基础方块移动
1 给Block添加移动方法
首先,给Block
类添加移动到指定行列的类方法如下
def move_cr(self, c, r):self.cr[0] = cself.cr[1] = rself.x = c * CELL_SIZEself.y = r * CELL_SIZEself.rect.left = self.xself.rect.top = self.y
但是这个方法还是不够的的,
因为游戏中的移动,一般都是操作上下左右来移动。
那么,我们需要把上下左右,转换成c、r的变换。
所以建立字典如下
DIRECTIONS = {"UP": (0, -1), # (dc, dr)"DOWN": (0, 1),"LEFT": (-1, 0),"RIGHT": (1, 0),
}
然后再给Block
类添加按方向移动的方法如下
def move(self, direction):dc, dr = DIRECTIONS[direction]next_c, next_r = self.cr[0] + dc, self.cr[1] + drself.move_cr(next_c, next_r)
2 响应键盘按键移动
1 中只是添加了移动的方法,但是玩家要通过键盘来移动的话,
还需要程序中有能够响应处理键盘操作
所以在while
循环中的for event in pygame.event.get()
循环里,添加代码如下
if event.type == pygame.KEYDOWN:if event.key == pygame.K_LEFT or event.key == ord('a'):block.move("LEFT")if event.key == pygame.K_RIGHT or event.key == ord('d'):block.move("RIGHT")if event.key == pygame.K_UP or event.key == ord('w'):block.move("UP")if event.key == pygame.K_DOWN or event.key == ord('s'):block.move("DOWN")
同时移动后,需要再进行绘制才能看到在上面的for
循环后面(外面)添加
win.blit(block.image, block.rect)
但是这个时候会有一个问题,移动后原来的位置色块还在,没有消失掉(被清掉)。
多移动几下后,结果就像下图一样
3 清除之前的绘制
所以我们需要擦除之前绘制的色块。
pygame里面,一般采用重新绘制整个界面的方式擦除之前的绘制。
重新绘制界面后,再在界面上添加新的需要绘制的东西。
即在while
循环中,win.blit(block.image, block.rect)
之前添加代码:
win.fill(bg_color)
此时, while
循环之前的这两句代码删不删除没啥区别
win.fill(bg_color)
win.blit(block.image, block.rect)
(个人视为多余的,所以删除)
此时移动小方块就不会有之前的色块残留了
不过此时还有一个小小的问题,就是小方块可以移动到界面边界外
虽然可以再移动回来,但是这不符合我们这个程序的规则。
所以需要再进行边界处理,使其无法移到边界外。
4 边界处理
给Block
类添加检查能否移动的方法如下
def check_move(self, direction=""):move_c, move_r = DIRECTIONS[direction]next_c, next_r = self.cr[0] + move_c, self.cr[1] + move_rif 0 <= next_c < C and 0 <= next_r < R:return Truereturn False
再在while
循环中的for
循环中的每次调用move
方法前,使用check_move
检查是否 能移动。
修改后的while
循环如下
while True:# 获取所有事件for event in pygame.event.get():if event.type == pygame.QUIT:# 判断当前事件是否为点击右上角退出键pygame.quit() # 关闭窗口sys.exit() # 停止程序if event.type == pygame.KEYDOWN:if event.key == pygame.K_LEFT or event.key == ord('a'):if block.check_move("LEFT"):block.move("LEFT")if event.key == pygame.K_RIGHT or event.key == ord('d'):if block.check_move("RIGHT"):block.move("RIGHT")if event.key == pygame.K_UP or event.key == ord('w'):if block.check_move("UP"):block.move("UP")if event.key == pygame.K_DOWN or event.key == ord('s'):if block.check_move("DOWN"):block.move("DOWN")win.fill(bg_color)win.blit(block.image, block.rect)clock.tick(FPS) # 控制循环刷新频率,每秒刷新FPS对应的值的次数pygame.display.update()
当然这么写不够优雅,if block.check_move("RIGHT"):
重复了四次,
有的朋友可能会觉得把check_move
的调用放在move
方法里面开头更好。
这里之所以不这么做,是因为后面会在方块组成的赛车类里,进行这个check_move
的工作。
具体见下文。
四 多个方块组成方块赛车
1 使用方块组绘制赛车
无论是玩家的车,还是敌人的车,
都是由多个方块组成的。
所以首先要定义下,玩家车和敌人车的方格组成,代码如下
CARS = { # 车的形状,即格子位置"player": [[0, 1, 0],[1, 1, 1],[1, 0, 1],],"enemy": [[1, 0, 1],[1, 1, 1],[0, 1, 0],]
}
根据这个方格祖,绘制赛车方法如下:
(放在while
循环中,重置背景之后)
car_c, car_r = 2, 2
for ri, row in enumerate(CARS["enemy"]):for ci, cell in enumerate(row):if cell == 1:i_block = Block(car_c + ci, car_r + ri, enemy_color)win.blit(i_block.image, i_block.rect)
2 使用Group
管理方块
接下来要建立一个赛车类,用来管理这些方块。
pygame为我们提供了一个管理多个Sprite的工具pygame.sprite.Group
。
这个Group
类可以使用draw
方法,将内部的Sprite
一起绘制在屏幕窗口上。
这里赛车类就继承这个Group
类,如下。
class Car(pygame.sprite.Group):def __init__(self, c, r, car_kind, car_color):super().__init__()for ri, row in enumerate(CARS[car_kind]):for ci, cell in enumerate(row):if cell == 1:block = Block(c+ci, r+ri, car_color)self.add(block)
同时,给这个赛车类添加移动方法,用于统一移动内部的方块
def move(self, direction=""):if all(block.check_move(direction) for block in self.sprites()):self.free_move(direction)
然后删掉其他的建立方块与绘制方块的代码
此时while
循环上下代码如下
bg_color = (200, 200, 200)
enemy_color = (50, 50, 50)
player_color = (65, 105, 225) # RoyalBluebottom_center_c = (C - len(CARS["player"][0])) // 2
bottom_center_r = R - len(CARS["player"])
car = Car(bottom_center_c, bottom_center_r, "player", player_color)while True:# 获取所有事件for event in pygame.event.get():if event.type == pygame.QUIT:# 判断当前事件是否为点击右上角退出键pygame.quit() # 关闭窗口sys.exit() # 停止程序if event.type == pygame.KEYDOWN:if event.key == pygame.K_LEFT or event.key == ord('a'):car.move("LEFT")if event.key == pygame.K_RIGHT or event.key == ord('d'):car.move("RIGHT")if event.key == pygame.K_UP or event.key == ord('w'):car.move("UP")if event.key == pygame.K_DOWN or event.key == ord('s'):car.move("DOWN")win.fill(bg_color)car.draw(win)clock.tick(FPS) # 控制循环刷新频率,每秒刷新FPS对应的值的次数pygame.display.update()
此时效果如图,小车可以左右移动
五 刷新敌人赛车
敌人的赛车绘制,同上面第四部分。
不过敌人的赛车
- 需要隔一段时间就能自动生成一个
- 不能由玩家的键盘操作移动,而是自动地向下移动。
- 向下移动出边界后需要清理掉
1 为Car
添加新的方法
这里有几个问题需要先解决,
- 敌人的赛车是可以从上边界移动出来, 可以向下移动出下边界的。
所以需要修改下原有的赛车内
为Car
添加无边界限制的移动方法free_move
如下
def free_move(self, direction=""):for block in self.sprites():block.move(direction)
再修改原有的move
方法如下
def move(self, direction=""):if all(block.check_move(direction) for block in self.sprites()):self.free_move(direction)
- 敌人赛车完全移动出边界后要清理掉
所以要给赛车类添加一个方法判断是否完全出了边界
先给组成赛车的方格Block
添加方法is_out
如下
def is_out(self):if 0 <= self.cr[0] < C and 0 <= self.cr[1] < R:return Falsereturn False
再为Car
添加方法is_out
如下
def is_out(self):return all(block.is_out() for block in self.sprites())
2 添加敌人赛车管理类
我们需要一个管理敌人赛车的类EnemyManager
。
其代码如下
import random # 在代码文件开头部分添加# 在 enemy_color 声明之后的位置添加
class EnemyManager():def __init__(self):self.enemies = []self.move_count = 0def gen_new_enemies(self): # 生成敌人赛车# 设置敌人赛车的生成间隔, 隔两倍的敌人赛车行数+1if self.move_count % (2 * len(CARS["enemy"]) + 1) == 1:ec = random.randint(1, C - len(CARS["enemy"][0]))enemy = Car(ec, 0, "enemy", enemy_color)self.enemies.append(enemy)def move(self): # 自动向下移动敌人赛车# 超出边界后,自动清理掉to_delete = []for i, enemy in enumerate(self.enemies):enemy.free_move("DOWN")if enemy.is_out():to_delete.append(i)for di in to_delete[::-1]: # 倒着按序号来删除self.enemies.pop(di)self.move_count += 1self.gen_new_enemies()def draw(self, master):# 绘制敌人赛车for enemy in self.enemies:enemy.draw(master)
3 绘制敌人赛车
在while循环之前实例化EnemyManager
,代码如下
emg = EnemyManager()
在while
中对敌车进行绘制,即在win.fill(bg_color)
之后添加代码如下
emg.move()
emg.draw(win)
但是此时敌人赛车运动的太快了,是每帧一次移动。
所以需要设置一下,让敌人的赛车每过MOVE_SPACE
帧才进行一次移动
在开头添加代码如下
MOVE_SPACE = 5
再在while
循环之前添加帧计数器
frame_count = 0
在while
循环中的开头添加
frame_count += 1
再修改刚才的敌人赛车绘制代码如下
if frame_count % MOVE_SPACE == 0:emg.move()emg.draw(win)
此时运行效果如图
六 碰撞检测
1 - 添加检测方法
要实现碰撞检测,只需给敌人赛车管理类EnemyManager
添加方法
遍历其中现有的敌人赛车,检查是否与玩家赛车相撞即可。
赛车相撞的判断方法为两车的方块有重叠,即有相同位置的方块。
所以给类Car
添加check_collide
方法,代码如下
def check_collide(self, other_car):for block in self.sprites():bcr1 = tuple(block.cr)for other_block in other_car.sprites():bcr2 = tuple(other_block.cr)if bcr1 == bcr2:return Truereturn False
再给敌人赛车管理类EnemyManager
添加check_collide
方法,代码如下
def check_collide(self, player):for enemy in self.enemies:if enemy.check_collide(player):return Truereturn False
2 - 碰撞后结束游戏
在while
循环中,绘制完赛车后添加碰撞检测
即在clock.tick(FPS)
之前添加代码如下
if emg.check_collide(car):break
此时玩家赛车碰到敌人赛车,游戏就会结束。
不过,此时游戏结束后会直接关闭窗口。
玩家体验并不好,所以需要进一步的优化下。
最好是游戏结束后,窗口不关闭,展示Game Over
的提示,并告诉玩家得分。
七 综合优化
1 游戏流程优化
游戏流程上,运行代码后,敌人赛车就直接迎面而来,留给玩家的缓冲准备时间太短了。
所以这里修改为代码运行后,游戏处于等待状态,玩家按键盘任意键开始游戏。
同时游戏结束后,玩家可按键盘任意键重新开始。
首先需要在while
循环前,添加一个游戏运行状态变量,默认游戏未开始
running = False
再修改while
循环如下
while True:frame_count += 1# 获取所有事件for event in pygame.event.get():if event.type == pygame.QUIT:# 判断当前事件是否为点击右上角退出键pygame.quit() # 关闭窗口sys.exit() # 停止程序if event.type == pygame.KEYDOWN:if running: # 游戏开始时,响应上下左右按键if event.key == pygame.K_LEFT or event.key == ord('a'):car.move("LEFT")if event.key == pygame.K_RIGHT or event.key == ord('d'):car.move("RIGHT")if event.key == pygame.K_UP or event.key == ord('w'):car.move("UP")if event.key == pygame.K_DOWN or event.key == ord('s'):car.move("DOWN")else: # 游戏结束后,响应任意按键,开始游戏# reset game, 重置变量和游戏状态car = Car(bottom_center_c, bottom_center_r, "player", player_color)frame_count = 0emg = EnemyManager()running =Trueif running:win.fill(bg_color)if frame_count % MOVE_SPACE == 0:emg.move()emg.draw(win)car.draw(win)if emg.check_collide(car):running = False # 撞车后,游戏状态改变为结束clock.tick(FPS) # 控制循环刷新频率,每秒刷新FPS对应的值的次数pygame.display.update()
此时游戏流程就修改好了,但是没有文本提示,玩家会不明就里,感到迷惑,所以需要文字在开始时和结束时进行提示。
2 文本提示与得分展示
在游戏开头添加大中小三种字体(必须要在pygame.init()
之后,能后不能前)
# 大中小三种字体,48,36,24
FONTS = [pygame.font.Font(pygame.font.get_default_font(), font_size) for font_size in [48, 36, 24]
]
代码运行后,游戏尚未开始,需要文本提示玩家按任意键开始游戏
即在while
循环之前添加代码如下
score_color = (0,128,0)
start_info = FONTS[2].render("Press any key to start game", True, score_color)
text_rect = start_info.get_rect(center=(WIN_WIDTH / 2, WIN_HEIGHT / 2))
win.blit(start_info, text_rect)
游戏过程中,展示玩家得分(玩家坚持时间)
在while
循环中,car.draw(win)
之后添加代码如下
text_info = FONTS[2].render("Scores: %d" % (time_count / FPS), True, COLORS["score"])
win.blit(text_info, dest=(0, 0))
玩家撞车后,一轮游戏结束,需要展示Game Over
,玩家得分,与按任意键结束游戏
修改while
循环中,原有的代码
if emg.check_collide(car):running = False # 撞车后,游戏状态改变为结束
为
if emg.check_collide(car):running = False # 撞车后,游戏状态改变为结束over_color = (255,0,0)texts = ["Game Over", "Scores: %d" % (frame_count / FPS), "Press Any Key to Restart game"]for ti, text in enumerate(texts):over_info = FONTS[ti].render(text, True, over_color)text_rect = over_info.get_rect(center=(WIN_WIDTH / 2, WIN_HEIGHT / 2 + 48 * ti))win.blit(over_info, text_rect)
3 代码优化
我们注意到,关于颜色这个常量,都是直接写在用的地方。
后面多了后,容易找起来不方便,所以建议把所有的颜色声明都统一移到开头位置。
这里放在CELL_SIZE = 40
之后,移动后颜色设置代码如下
bg_color = (200, 200, 200)
enemy_color = (50, 50, 50)
player_color = (65, 105, 225) # RoyalBlue
score_color = (0,128,0) # SpringGreen
over_color = (255, 0, 0)
4 后续
- 其实,由于颜色值比较多,我更喜欢把颜色统一放在一个字典里面,根据键来取值,本文github项目下的
car_racing.py
就是这样写的 - 游戏中赛车移动,是按一下移动一个,可能有的人跟喜欢能够连续移动,这个已在项目下的
1 car racing
文件夹下的car_racing_plus1.py
中实现。
pygame 入门实例教程 1 - 复古方块赛车游戏相关推荐
- php页面get方法实现ajax,入门实例教程
ajax,入门实例教程 本例针对php页面,做了一个小的demo加深对ajax的理解 1.文档结构: 共有ajax.php 和action.php 2个页面. 2.源码如下: /*ajax.php页面 ...
- Web Components入门实例教程
代码示例 <!-- 定义组件模板 --> <template id="UserNameTemplate"><style>.user-name { ...
- [转]React 入门实例教程
React 入门实例教程 作者: 阮一峰 日期: 2015年3月31日 现在最热门的前端框架,毫无疑问是 React . 上周,基于 React 的 React Native 发布,结果一天之内,就获 ...
- 视频教程-Swift5语言入门实例教程-Swift
Swift5语言入门实例教程 15年以上IT行业工作经验.8年以上IT行业教学经验.丰富的项目经验和授课经验,授课形式不拘一格.熟悉iOS开发,网页开发.Java开发.平面设计等技术,是一名经验丰富的 ...
- 读阮一峰的React 入门实例教程有感
读阮一峰的React 入门实例教程有感 阮一峰的React入门实例教程其实我在一年前就读过,当时就想学习React,其实那个时候刚刚jQuery入门,啥也不懂,看得云里雾里,所以后来就没有继续研究下去 ...
- Phaser 3 入门实例教程
Phaser是什么? Phaser是一个HTML5游戏框架,目的是为了快速地制作跨浏览器的HTML5游戏. 这个框架,主要发掘了现代浏览器(兼及桌面和移动两类系统)的优点,所以对浏览器的唯一要求是,就 ...
- Very Good!!! - React 入门实例教程
现在最热门的前端框架,毫无疑问是 React . 上周,基于 React 的 React Native 发布,结果一天之内,就获得了 5000 颗星,受瞩目程度可见一斑. React 起源于 Face ...
- React 入门实例教程(原作者: 阮一峰)
转载(http://www.ruanyifeng.com/blog/2015/03/React.html) 现在最热门的前端框架,毫无疑问是 React . 上周,基于 react 的 React N ...
- mxGraph 入门实例教程
在上一篇文章 <记一次绘图框架技术选型: jsPlumb VS mxGraph> 中,提到了我为什么要去学习 mxGraph.在入门时我遇到了以下几个问题 官方文档偏向理论,没能较好地结合 ...
- Reactjs 入门实例教程
来自:http://www.ruanyifeng.com/blog/2015/03/react.html 现在最热门的前端框架,毫无疑问是 React . 上周,基于 React 的 React Na ...
最新文章
- windows下DOS命令关闭占用的端口
- C#中Ref和out的使用区别
- ITK:跟踪两个代码执行之间的内存费用
- java sqlmap_sqlmap 学习指南
- 24.JSP 客户端请求
- WPS Office文档未保存怎么恢复
- 前端工程师,会是一群高情商的程序猿
- 在Azure Data StudioSQL笔记本中使用Python脚本加密密码
- matlab 类型转换(类型判断)
- Java多线程 ReentrantLock、Condition 实现生产者、消费者协作模式
- linux 摄像头yuv,camera YUV格式
- C++primer第五版课后答案参考
- 一个完整的数据挖掘项目-纽约市建筑能源之星预测
- Layui Table格式化日期(表格)
- 我们都希望有一部属于自己的电脑。
- VCS(DVE)仿真波形的存储和打开.vpd
- 将r中的数据导出为excel文件
- 数字逻辑基础实验二—时序逻辑电路的设计
- 抖音SEO优化源码,企业号搜索排名系统,矩阵分发。
- 信管家源代码c语言,AK老唐信管家软件指标 宝塔线副图源码
热门文章
- tiny4412移植U-Boot 2020.07
- 原生开发什么意思_原生开发和混合开发的优劣势都是什么?
- QQ信任登陆注册开发者帐号
- 华为路由器Nasp设置 以及直连路由,静态路由,缺省路由,
- pkpm板按弹性计算还是塑性_PKPM楼板计算
- Java中Base64的解析
- 计算机触摸板设置方法,笔记本电脑触摸板设置
- html编辑中,出现报错 semi-.colon excepted css(………)
- quarkus数据库篇之二:无需数据库也能运行增删改查(dev模式)
- 嫡权法赋权法_组合赋权法确定权重的方法探讨