IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> 用pygame实现滑动拼图(Slide Puzzle)小游戏 -> 正文阅读

[Python知识库]用pygame实现滑动拼图(Slide Puzzle)小游戏

用pygame实现滑动拼图(Slide Puzzle)小游戏

介绍

滑动拼图(Slide Puzzle) 是一款策略类游戏1,把一幅图案分为相等的若干正方型小方块,取走其中一块制造出一块空位,利用多余的空格滑动其他小方块,把打乱的图案重新形成拼图。

这个游戏可以用Python语言和Pygame框架来开发,如下图所示。
滑动拼图(Slide Puzzle)小游戏在这里,用阿拉伯数字代替了拼图,只要把数字按顺序排列,就算解谜成功2。但是,由于打乱拼图时采用了随机打乱的方式,有可能无解(一般情况下都是无解,小概率有解),所以增加了一点魔法,可以互换两个滑块上的内容,但是只能使用3次,如果用了3次魔法还是解不出来,那就只能New Game了。

文件目录

编写这个小游戏用到的代码和素材文件如下(在文末附有下载链接):
myDirectory/

  • sound/
    • bg_music.mp3 # 背景音乐
    • click.ogg # 点击音效
    • hit.wav # 解谜成功音效
  • run.py # 游戏运行程序
  • Game.py # 游戏模块,生成游戏板、处理玩家鼠标点击事件等
  • Player.py # 玩家模块,记录玩家得分、点击次数等
  • RGB.py # 颜色模块,定义了所有RGB颜色常量
  • GameConst.py # 常量模块,定义了游戏用到的常量

现在分别介绍一下.py文件中的代码。

游戏运行程序

运行游戏用的脚本run.py的全部代码如下:

import pygame, sys
import Game, GameConst, Player
from pygame.locals import *

def main():
	# 建立游戏对象
	game = Game.Game(GameConst.fps, GameConst.window_width, GameConst.window_height, 'Slide Puzzle')
	
	# 建立玩家对象,追踪玩家相关操作,记录玩家数据
	player = Player.Player()

	# 初始化游戏板
	game.init_board(player)
	
	# 主循环
	while True:
		# 事件处理
		for event in pygame.event.get():
			if event.type == QUIT:	# 退出
				pygame.quit()
				sys.exit()
			elif event.type == MOUSEMOTION:	# 记录玩家鼠标位置
				player.mouseX, player.mouseY = event.pos
			elif event.type == pygame.KEYDOWN:
				keys = pygame.key.get_pressed()
				game.respond_to_keys(keys)

		# 显示游戏板
		game.display_map(player)
	
		# 处理鼠标移动事件
		game.respond_to_mouse_motion(player)

		# 处理鼠标单击事件
		b1, b2, b3 = pygame.mouse.get_pressed()	
		game.respond_to_click(b1, b2, b3, player)		
		
		# 将game.DISPLAYSURF对象绘制到屏幕上
		pygame.display.update()

		# 生成游戏时间控制
		game.tick()

if __name__ == '__main__':
	while True:
		main()

在这里显示了游戏的主要逻辑,包括定义一个main()函数,建立game对象和player对象,用game.init_board()方法初始化游戏板,建立游戏循环,在游戏循环中用game.display_map()方法绘制游戏板,用game.respond_to_mouse_motion()方法处理鼠标移动事件,用game.respond_to_click()方法处理鼠标点击事件,最后将绘制好的游戏板打印到屏幕上,以及生成游戏的时间控制。

游戏模块

游戏模块Game.py的全部代码如下:

import pygame, sys, random
import RGB, GameConst, Player
from pygame.locals import *

class Game():
	def __init__(self, fps, window_width, window_height, caption):
		pygame.init()

		# 设置帧率
		self.__f = fps
		self.__c = pygame.time.Clock()

		# 设置窗口和字体
		self.window_width = window_width
		self.window_height = window_height
		self.DISPLAYSURF = pygame.display.set_mode((self.window_width, self.window_height))
		self.FONT1 = pygame.font.SysFont('stzhongsong', 18)
		self.FONT2 = pygame.font.SysFont('Arial', 26)
		
		# 设置窗口标题
		self.__caption = caption
		pygame.display.set_caption(caption)

		# 消息框
		self.message_F1 = []
		self.message_1 = []
		self.message_4 = []		
		
		# 游戏板
		self.tiles = list(range(1, 16))
		self.answer = [[1 for i in range(5)] for j in range(5)]	# 增大1格,周围全是1,简化can_move代码
		self.answer_bak = [[1 for i in range(5)] for j in range(5)] # 副本
		
		# 记录互换的滑块
		self.rect1 = 0
		self.rect2 = 0
		
	# 设置一些get/set方法,作为对象的属性	
	def _getd(self): return self.DISPLAYSURF
	def _setd(self, value): self.DISPLAYSURF = value
	screen = property(_getd, _setd)

	def _getf(self): return self.__f
	def _setf(self, value): self.__f = value
	FPS = property(_getf, _setf)

	def _getc(self): return self.__c
	def _setc(self, value): self.__c = value
	fpsClock = property(_getc, _setc)

	def _getcaption(self): return self.__caption
	def _setcaption(self, value): self.__caption = value
	caption = property(_getcaption, _setcaption)

	def init_board(self, player):
		''' 初始化游戏板。'''
		self.clear_message()
		self.add_message_F1('Generating new puzzle...')
		self.add_message_1('Reset')
		self.add_message_1('New Game')
		self.add_message_1('魔法:互换')	
		player.game_process = GameConst.GAMING
		player.slide_times = 0
		player.magic = 3
		
		# 将15个数字随机打乱
		random.shuffle(self.tiles)		
		
		#填到self.answer中作为初始状态,并在self.answer_bak中保留一个副本
		for i in range(GameConst.MapBlock_X):
			for j in range(GameConst.MapBlock_Y):
				if (i, j) == (3, 3): # 最后一个滑块留空
					continue
				self.answer[i][j] = self.tiles[i*GameConst.MapBlock_X + j]
				self.answer_bak[i][j] = self.tiles[i*GameConst.MapBlock_X + j]
		self.answer[3][3] = 0
		self.answer_bak[3][3] = 0		
	
	def add_message_F1(self, s):
		''' 往消息队列中添加信息。'''
		self.message_F1.append(s)
		if len(self.message_F1) == 5:
			self.message_F1 = []
			self.message_F1.append(s)
			
	def add_message_1(self, s):
		''' 往功能按钮队列中添加内容。'''
		self.message_1.append(s)
		
	def tick(self):
		''' 自动的暂停,控制帧速率 '''
		self.fpsClock.tick(self.FPS) 

	def fill(self, color):
		self.DISPLAYSURF.fill(color)

	def print_text(self, font, x, y, text, color=RGB.White, shadow=True):
		''' 使用选定的字体font,在(x, y)位置开始以颜色color打印文本text。'''
		imgText = font.render(text, True, color)
		self.DISPLAYSURF.blit(imgText, (x, y))

	def print_text_from_center(self, font, centerx, centery, text, color=RGB.White, shadow=True):
		''' 使用选定的字体font,在(x, y)位置开始以颜色color打印文本text。'''
		imgText = font.render(text, True, color)
		imgRect = imgText.get_rect()
		(imgRect.centerx, imgRect.centery) = (centerx, centery)
		(x, y) = imgRect.topleft
		self.DISPLAYSURF.blit(imgText, (x, y))		
		
	def print_message_F1(self):
		''' 在过场动画信息框内打印消息。'''
		s = self.message_F1
		left, top, width, height = GameConst.MsgBoxF1
		for i, v in enumerate(s):			
			self.print_text(self.FONT1, left+GameConst.TextMargin, top+GameConst.TextMargin+i*GameConst.FontHeight, v, RGB.White)

	def print_message_1(self):
		''' 在位置信息框内打印消息。'''
		s = self.message_1
		left, top, width, height = GameConst.MsgBox1
		for i, v in enumerate(s):	
			self.print_text(self.FONT1, left, top+i*GameConst.FontHeight, v, RGB.White)
			
	def print_message_4(self):
		''' 在位置信息框内打印消息。'''
		s = self.message_4
		left, top, width, height = GameConst.MsgBox4
		for i, v in enumerate(s):	
			self.print_text(self.FONT1, left, top+i*GameConst.FontHeight, v, RGB.White)			

	def print_slide_times(self, player):
		''' 打印滑块移动次数。'''
		left, top, width, height = GameConst.MsgBox5
		self.print_text(self.FONT1, left, top+GameConst.FontHeight, '已滑动次数:{}'.format(player.slide_times), RGB.White)		

	def print_magic_times(self, player):
		''' 打印滑块移动次数。'''
		left, top, width, height = GameConst.MsgBox6
		self.print_text(self.FONT1, left, top+GameConst.FontHeight, '剩余魔法次数:{}'.format(player.magic), RGB.White)		
				
	def display_map(self, player):
		self.fill(RGB.Black)

		# 游戏板的边框
		pygame.draw.rect(self.DISPLAYSURF, RGB.DarkSlateGray1, GameConst.MapBox, 1)
		
		# 15个滑块
		for i in range(0, GameConst.MapBlock_X):
			for j in range(0, GameConst.MapBlock_Y):
				if self.answer[i][j] == 0: # 跳过留空的方块
					continue
				left, top, width, height = GameConst.MapBox
				# 边框
				position = left + j * GameConst.MapBlockSize + 1, top + i * GameConst.MapBlockSize + 1, GameConst.MapBlockSize-2, GameConst.MapBlockSize-2
				pygame.draw.rect(self.DISPLAYSURF, RGB.Gainsboro, position, 1)
				# 滑块
				position = left + j * GameConst.MapBlockSize + 5, top + i * GameConst.MapBlockSize + 5, GameConst.MapBlockSize-10, GameConst.MapBlockSize-10
				pygame.draw.rect(self.DISPLAYSURF, RGB.White, position, 0)
				# 数字
				self.print_text_from_center(self.FONT2, left + j * GameConst.MapBlockSize + 50, top + i * GameConst.MapBlockSize + 50, str(self.answer[i][j]), RGB.Black)
				
		# 打印游戏信息
		self.print_message_F1()
			
		# 显示功能按钮
		self.print_message_1()			
			
		# 如果玩家胜利,显示消息
		self.print_message_4()	
			
		# 打印滑块移动次数	
		self.print_slide_times(player)			
			
		# 打印剩余魔法次数	
		self.print_magic_times(player)
			
	def respond_to_click(self, b1, b2, b3, player):
		''' 对鼠标点击的响应。'''
		x, y = player.mouseX, player.mouseY 
		if b3: # 右键点击无效
			pass
		if b1: # 左键点击 
			if player.game_process == GameConst.GAMING: # 如果在Gaming,则可滑动
				# 在游戏板上点击
				MapBox = pygame.Rect(GameConst.MapBox)
				if MapBox.collidepoint(x, y):
					for i in range(0, GameConst.MapBlock_X):
						for j in range(0, GameConst.MapBlock_Y):
							if self.answer[i][j] == 0: # 跳过留空的方块
								continue
							left, top, width, height = GameConst.MapBox
							# 滑块
							position = left + j * GameConst.MapBlockSize + 5, top + i * GameConst.MapBlockSize + 5, GameConst.MapBlockSize-10, GameConst.MapBlockSize-10
							rect = pygame.Rect(position)
							if rect.collidepoint(x, y):
								left, top, width, height = rect
								# 判断是否可以滑动
								judge = self.can_move(i, j)
								self.move(judge, i, j, player)
				# 点击Reset		
				MsgBox1 = pygame.Rect(GameConst.MsgBox1)
				if MsgBox1.collidepoint(x, y):
					left, top, width, height = GameConst.MsgBox1
					for i in range(4):
						for j in range(4):
							self.answer[i][j] = self.answer_bak[i][j]
				
				# 点击New Game
				MsgBox2 = pygame.Rect(GameConst.MsgBox2)
				if MsgBox2.collidepoint(x, y):
					left, top, width, height = GameConst.MsgBox2
					self.init_board()
					
				# 点击“互换”
				MsgBox3 = pygame.Rect(GameConst.MsgBox3)
				if MsgBox3.collidepoint(x, y):
					left, top, width, height = GameConst.MsgBox3
					if player.magic > 0:
						player.magic -= 1
						self.add_message_F1('互换两块板子,用鼠标在两块板子上依次点击:')
						player.game_process = GameConst.CHANGING
					else:
						self.add_message_F1('魔法次数已经用完,无法交换。')
				
			elif player.game_process == GameConst.CHANGING: # 如果游戏状态是Changing,则交换鼠标点击的滑块
				# 在游戏板上点击
				MapBox = pygame.Rect(GameConst.MapBox)
				if MapBox.collidepoint(x, y):
					for i in range(0, GameConst.MapBlock_X):
						for j in range(0, GameConst.MapBlock_Y):
							if self.answer[i][j] == 0:
								continue
							left, top, width, height = GameConst.MapBox
							# 交换滑块
							position = left + j * GameConst.MapBlockSize + 5, top + i * GameConst.MapBlockSize + 5, GameConst.MapBlockSize-10, GameConst.MapBlockSize-10
							rect = pygame.Rect(position)
							if rect.collidepoint(x, y):
								if self.rect1 == 0:
									self.rect1 = (i, j)
								else:
									self.rect2 = (i, j)
									self.change(self.rect1, self.rect2)
									self.rect1 = 0
									self.rect2 = 0
									player.game_process = GameConst.GAMING
			
			elif player.game_process == GameConst.HERO_WIN: # 如果游戏状态是Hero_win,则只响应点击New Game 	 							
				# 点击New Game
				MsgBox2 = pygame.Rect(GameConst.MsgBox2)
				if MsgBox2.collidepoint(x, y):
					left, top, width, height = GameConst.MsgBox2
					self.init_board(player)									
					
	def change(self, rect1, rect2):
		''' 交换两个滑块的内容。'''
		tmp = self.answer[rect1[0]][rect1[1]]
		self.answer[rect1[0]][rect1[1]] = self.answer[rect2[0]][rect2[1]]
		self.answer[rect2[0]][rect2[1]] = tmp
					
	def move(self, judge, i, j, player):
		''' 移动滑块,即,交换当前滑块和空滑块的内容。'''
		if judge == 'left':
			player.get_sounds()['click'].play()
			self.answer[i-1][j] = self.answer[i][j]
			self.answer[i][j] = 0
			player.slide_times += 1
		elif judge == 'right':
			player.get_sounds()['click'].play()
			self.answer[i+1][j] = self.answer[i][j]
			self.answer[i][j] = 0
			player.slide_times += 1
		elif judge == 'top':
			player.get_sounds()['click'].play()
			self.answer[i][j-1] = self.answer[i][j]
			self.answer[i][j] = 0	
			player.slide_times += 1
		elif judge == 'down':
			player.get_sounds()['click'].play()
			self.answer[i][j+1] = self.answer[i][j]
			self.answer[i][j] = 0	
			player.slide_times += 1
		else:
			self.add_message_F1('can not move.')
		# 检查当前的游戏板中的滑块是否已经按顺序排列好
		if self.check_answer():
			player.get_sounds()['hit'].play()
			player.game_process = GameConst.HERO_WIN
			self.message_4 = ['恭喜,你赢了!按Esc退出...']
			
	def check_answer(self):
		''' 检查滑块是否顺序排列。'''
		for i in range(GameConst.MapBlock_X):
			for j in range(GameConst.MapBlock_Y):
				a = j + i * GameConst.MapBlock_X + 1
				if (i, j) == (3, 3):
					a = 0
				if self.answer[i][j] != a:
					return False
		return True
			
	def can_move(self, i, j):
		''' 判断滑块是否能够移动。'''
		if self.answer[i-1][j] == 0:
			return 'left'
		elif self.answer[i+1][j] == 0:
			return 'right'
		elif self.answer[i][j-1] == 0:
			return 'top'
		elif self.answer[i][j+1] == 0:
			return 'down'
		else:
			return False
		
	def respond_to_mouse_motion(self, player):
		''' 对鼠标移动的响应。'''
		x, y = player.mouseX, player.mouseY 
		# 移动到滑块板上,则高亮显示滑块板
		MapBox = pygame.Rect(GameConst.MapBox)
		if MapBox.collidepoint(x, y):
			left, top, width, height = GameConst.MapBox
			pygame.draw.rect(self.DISPLAYSURF, RGB.GhostWhite, (left, top, width+5, height+5), 4)	
			# 移动到某个滑块上,则高亮显示该滑块
			for i in range(0, GameConst.MapBlock_X):
				for j in range(0, GameConst.MapBlock_Y):
					if self.answer[i][j] == 0:
						continue
					left, top, width, height = GameConst.MapBox
					# 滑块
					position = left + j * GameConst.MapBlockSize + 5, top + i * GameConst.MapBlockSize + 5, GameConst.MapBlockSize-10, GameConst.MapBlockSize-10
					rect = pygame.Rect(position)
					if rect.collidepoint(x, y):
						left, top, width, height = rect
						pygame.draw.rect(self.DISPLAYSURF, RGB.GhostWhite, (left, top, width+3, height+3), 2)	
		
		# 移动到Reset按钮上,高亮该按钮
		MsgBox1 = pygame.Rect(GameConst.MsgBox1)
		if MsgBox1.collidepoint(x, y):
			left, top, width, height = GameConst.MsgBox1
			pygame.draw.rect(self.DISPLAYSURF, RGB.GhostWhite, (left, top, width+2, height+2), 1)						
		
		# 移动到New Game按钮上,高亮该按钮
		MsgBox2 = pygame.Rect(GameConst.MsgBox2)
		if MsgBox2.collidepoint(x, y):
			left, top, width, height = GameConst.MsgBox2
			pygame.draw.rect(self.DISPLAYSURF, RGB.GhostWhite, (left, top, width+2, height+2), 1)
			
		# 移动到Change按钮上,高亮该按钮	
		MsgBox3 = pygame.Rect(GameConst.MsgBox3)
		if MsgBox3.collidepoint(x, y):
			left, top, width, height = GameConst.MsgBox3
			pygame.draw.rect(self.DISPLAYSURF, RGB.GhostWhite, (left, top, width+2, height+2), 1)			

	def clear_message(self):
		# 清空信息框
		self.message_1 = []
		self.message_4 = []
		self.message_F1 = []		
		
	def respond_to_keys(self, keys):
		''' 对按键输入的响应。'''
		if keys[K_ESCAPE]:
			pygame.quit()
			sys.exit()

在这个文件中建立了Game类,增加了一些方法,其中重要的有:

  • display_map(),用来绘制游戏板。
  • respond_to_mouse_motion(),对鼠标移动的响应,在这里主要是移动到滑块或按钮上高亮显示。
  • respond_to_click(),对鼠标点击的响应。

屏幕上消息的打印在这里采用了队列的方式管理,就是建立一个数组:

self.message_F1 = []

然后增加在这个消息框内打印文字的方法:

	def print_message_F1(self):
		left, top, width, height = GameConst.MsgBoxF1
		for i, v in enumerate(self.message_F1):			
			self.print_text(self.FONT1, left+GameConst.TextMargin, top+GameConst.TextMargin+i*GameConst.FontHeight, v, RGB.White)

GameConst.py中定义了MsgBoxF1常量,标记出了这个消息框的左上角坐标和宽高:

MsgBoxF1 = (0, 0, 800, 600)

然后增加在消息框内添加文本的方法:

	def add_message_F1(self, s):
		''' 往消息队列中添加信息。'''
		self.message_F1.append(s)
		if len(self.message_F1) == 5:
			self.message_F1 = []
			self.message_F1.append(s)

在这里设置为只显示4行消息,如果文本超过4行,则覆盖之前的。

然后在display_map()中调用print_message_F1()方法:

	def display_map(self, player):
		self.fill(RGB.Black)
		...
		# 打印游戏信息
		self.print_message_F1()

由于display_map()在游戏循环中不断调用,就保证了message_F1中的文本的持续显示。

这样,在后续要打印消息的时候就可以直接往数组中添加文本,而不必考虑在屏幕的哪个位置上显示等细节。

if player.magic > 0:
	player.magic -= 1
	self.add_message_F1('互换两块板子,用鼠标在两块板子上依次点击:')
	player.game_process = GameConst.CHANGING
else:
	self.add_message_F1('魔法次数已经用完,无法交换。')

有了这些方法,在游戏屏幕上打印文本就像在控制台使用print()函数一样方便。

也可以增加清空消息的方法,在适当的时候(比如初始化游戏板)调用:

	def clear_message(self):
		# 清空信息框
		self.message_1 = []
		self.message_4 = []
		self.message_F1 = []

玩家模块

玩家模块Player.py的全部代码如下:

import pygame, os, GameConst

class Player():
	def __init__(self):		
		# 记录鼠标的位置		
		self.__x = 0
		self.__y = 0
		# 设置一个变量,跟踪游戏进度
		self.__p = GameConst.GAMING # game process
		# 已经移动滑块的次数
		self.__slide_times = 0
		# 剩余魔法的次数
		self.__magic = 3
		# 开启音效
		pygame.mixer.init()
		self.sounds = {}
		self.load_sounds()

	def get_sounds(self):
		return self.sounds

	def _getmagic(self): return self.__magic
	def _setmagic(self, value): self.__magic = value
	magic = property(_getmagic, _setmagic)

	def _getslide_times(self): return self.__slide_times
	def _setslide_times(self, value): self.__slide_times = value
	slide_times = property(_getslide_times, _setslide_times)

	def _getp(self): return self.__p
	def _setp(self, value): self.__p = value
	game_process = property(_getp, _setp)

	def _getmousex(self): return self.__x
	def _setmousex(self, value): self.__x = value
	mouseX = property(_getmousex, _setmousex)

	def _getmousey(self): return self.__y
	def _setmousey(self, value): self.__y = value
	mouseY = property(_getmousey, _setmousey)

	def load_sounds(self):
		''' 搜索当前目录下的sound文件夹,然后载入背景音乐(.mp3)和音效(.wav, .ogg)。'''
		sound_files = os.listdir('sound')
		for x in sound_files:
			t = x.split(".")
			if t[1] == 'mp3':
				pygame.mixer.music.load('sound/{}.{}'.format(t[0], t[1]))
				pygame.mixer.music.set_volume(0.1875)
				pygame.mixer.music.play(-1, 0.0) 
			else:
				self.sounds[t[0]] = pygame.mixer.Sound('sound/{}.{}'.format(t[0], t[1]))

在这里默认当前目录下有一个名叫sound的文件夹,其中包括了音效文件(.mp3, .wav, .ogg)。用os.listdir()方法3列出了这个文件夹内的文件,如果是.mp3格式则作为背景音乐直接播放,其他的文件以文件名作为键值存在self.sounds这个字典里,要用的时候直接引用:

player.get_sounds()['click'].play()

颜色模块

游戏模块RGB.py的部分代码如下:

Black = (0, 0, 0)
White = (255, 255, 255)
DarkSlateGray1 = (151, 255, 255)
Gainsboro = (220, 220, 220)
GhostWhite = (248, 248, 255)

以上是本游戏用到的颜色,全部代码请见RGB.py

源代码已经上传到GitCode: 下唐人 / slide_puzzle · GitCode


  1. 滑动拼图,百度百科 ??

  2. [美] Al Sweigart 著,李强 译. Python和Pygame游戏开发指南. 人民邮电出版社. 2015.12 ??

  3. python列出文件夹下所有的文件,CSDN博客 ??

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-05-18 17:35:34  更:2022-05-18 17:35:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 13:47:15-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码