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编写单人RPG小游戏(三) -> 正文阅读

[Python知识库]用pygame编写单人RPG小游戏(三)

介绍

上一讲 中介绍了如何绘制地图、加载英雄头像图片,并用鼠标控制英雄移动,在这一讲中将介绍用pandas加载自制的数据库(excel文件),在地图上行走遭遇怪时显示从数据表格中读取的怪的属性。

pandas 是基于NumPy 的一种工具,该工具是为解决数据分析任务而创建的。pandas 纳入了大量库和一些标准的数据模型,提供了高效地操作大型数据集所需的工具。pandas提供了大量能使我们快速便捷地处理数据的函数和方法1

安装 pandas 需要基础环境是 Python,如果已经安装了 Python 和 Pip,就可以使用 pip 安装 pandas2:

pip install pandas

接着用import导入pandas,查看版本

>>> import pandas
>>> pandas.__version__
'1.4.2'

如果出现这样的信息,就表示pandas安装成功了。

三、加载excel数据文件

在Excel中制作了若干表格,包括怪、技能、天赋、装备、任务物品、魔杖、任务、地图和NPC,如下图所示。唐小山的世界-图片7这个Excel文件已经上传到GitCode,链接见文末。

要读入这个Excel中的数据,可以使用pd.read_excel()方法。为此,在Game类的__init__()方法中添加如下代码:

import pandas as pd

class Game():
	def __init__(self, fps, window_width, window_height, caption):
		...
		self.data = GameConst.data
		self.Monster_data = pd.read_excel(self.data, sheet_name="怪")

由于Excel表格都是二维的矩阵,所以可以用字典进行存储。为此,在Game类中新建一个字典成员变量。

		# 读取怪属性矩阵
		print('loading monsters, please wait...')
		self.Monster = {} # 存储怪表格中的数据
		self.load_monsters()

pandas把Excel表格的第一行作为标题,存储在data.columns中。要读入这一行,可以在Monster字典中新建一个name_list列表变量,然后把这一行的内容存储在name_list中:

self.Monster['name_list'] = list(Monster_data.columns)
self.Monster['name_list']
['name', '绿史莱姆', '红史莱姆', '蝙蝠', '术士', '骷髅', '狼']
self.Monster['name_list'].pop(0) # 把'name'从列表中去除	

然后读入怪表格中的整个矩阵,方法是用双重for循环:

		for i in range(self.Monster_data.shape[0]):
			attr = str(self.Monster_data.iloc[i, 0])
			for j in range(1, self.Monster_data.shape[1]):
				name = self.Monster['name_list'][j-1]
				if i <= 1:
					b = str(self.Monster_data.iloc[i, j])
				else:
					b = int(self.Monster_data.iloc[i, j])
				self.Monster[name][attr] = b

Monster_data.shape是一个元组,存储了这个矩阵的行数和列数:

Monster_data.shape
(24, 7)

可以看到,怪表格中有24行(除去第一行标题),7列。

Monster_data.iloc[i, j]是表格中单元格中存放的数据,i是行,j是列,比如要提取第3行第2列的单元格:

Monster_data.iloc[2, 1]
1

注意行和列是从0开始计数的。

还有一点,就是在实践中发现,pandas在提取单元格的值时,有时会生成numpy格式的值,而在后续处理中希望这些值是字符串或整数。为此,可以用str()int()方法转换一下:

				if i <= 1:
					b = str(self.Monster_data.iloc[i, j])
				else:
					b = int(self.Monster_data.iloc[i, j])

这样,整个怪表格的数据就存放在Monster字典中了。可以用print()函数将整个Monster字典打印出来:

python run.py 
pygame 2.1.2 (SDL 2.0.20, Python 3.10.4)
Hello from the pygame community. https://www.pygame.org/contribute.html
loading game data, please wait...
loading monsters, please wait...
{'name_list': ['绿史莱姆', '红史莱姆', '蝙蝠', '术士', '骷髅', '狼'], 'name': {'name': 'name'}, '绿史莱姆': {'name': '绿史莱姆', 'drop_rate': '1. +小药草 110; 2. +中药草 105; 4. +攻击力提升药水 110; 5. +铁剑 101; 6. +皮盾 101', 'status': '无', 'lv': 1, 'HP': 40, 'HP_max': 40, 'attack': 10, '最小属性攻击力': 5, '最大属性攻击力': 15, 'defence': 1, '火焰伤害': 0, '寒冷伤害': 0, '闪电伤害': 0, '毒素伤害': 0, '火焰抵抗力': 0, '寒冷抵抗力': 0, '闪电抵抗力': 0, '毒素抵抗力': 0, 'Exp': 15, 'Exp_max': 15, 'evade_rate': 0, 'double_hit_rate': 0, 'critical_hit_rate': 1, 'coin': 13, 'status_times': 0}, '红史莱姆': {'name': '红史莱姆', 'drop_rate': '1. +小药草 110; 2. +中药草 105; 4. +攻击力提升药水 110; 5. +铁剑 130; 6. +皮盾 125', 'status': '无', 'lv': 1, 'HP': 150, 'HP_max': 150, 'attack': 15, '最小属性攻击力': 10, '最大属性攻击力': 20, 'defence': 2, '火焰伤害': 0, '寒冷伤害': 5, '闪电伤害': 5, '毒素伤害': 0, '火焰抵抗力': 0, '寒冷抵抗力': 5, '闪电抵抗力': 5, '毒素抵抗力': 0, 'Exp': 56, 'Exp_max': 56, 'evade_rate': 0, 'double_hit_rate': 1, 'critical_hit_rate': 0, 'coin': 52, 'status_times': 0}, '蝙蝠': {'name': '蝙蝠', 'drop_rate': '1. +小药草 110; 2. +中药草 105; 4. +攻击力提升药水 110; 5. +蝙蝠毛披风 110; 6. +布靴 105', 'status': '无', 'lv': 2, 'HP': 180, 'HP_max': 180, 'attack': 30, '最小属性攻击力': 25, '最大属性攻击力': 35, 'defence': 5, '火焰伤害': 0, '寒冷伤害': 0, '闪电伤害': 0, '毒素伤害': 9, '火焰抵抗力': 0, '寒冷抵抗力': 0, '闪电抵抗力': 0, '毒素抵抗力': 9, 'Exp': 70, 'Exp_max': 70, 'evade_rate': 1, 'double_hit_rate': 0, 'critical_hit_rate': 0, 'coin': 65, 'status_times': 0}, '术士': {'name': '术士', 'drop_rate': '1. +小药草 110; 2. +中药草 105; 3. +攻击力提升药水 110; 5. +蝙蝠毛披风 110; 6. +布靴 105', 'status': '无', 'lv': 2, 'HP': 350, 'HP_max': 350, 'attack': 35, '最小属性攻击力': 30, '最大属性攻击力': 40, 'defence': 10, '火焰伤害': 5, '寒冷伤害': 0, '闪电伤害': 0, '毒素伤害': 0, '火焰抵抗力': 5, '寒冷抵抗力': 0, '闪电抵抗力': 0, '毒素抵抗力': 0, 'Exp': 109, 'Exp_max': 109, 'evade_rate': 2, 'double_hit_rate': 3, 'critical_hit_rate': 2, 'coin': 88, 'status_times': 0}, '骷髅': {'name': '骷髅', 'drop_rate': '1. +小药草 110; 2. +中药草 105; 6. +攻击力提升药水 105; 5. +铁甲 110; 6. +铁盔 105', 'status': '无', 'lv': 3, 'HP': 600, 'HP_max': 600, 'attack': 56, '最小属性攻击力': 51, '最大属性攻击力': 61, 'defence': 20, '火焰伤害': 3, '寒冷伤害': 5, '闪电伤害': 5, '毒素伤害': 3, '火焰抵抗力': 3, '寒冷抵抗力': 5, '闪电抵抗力': 5, '毒素抵抗力': 3, 'Exp': 190, 'Exp_max': 190, 'evade_rate': 0, 'double_hit_rate': 0, 'critical_hit_rate': 1, 'coin': 153, 'status_times': 0}, '狼': {'name': '狼', 'drop_rate': '1. +小药草 110; 2. +中药草 105; 3. +攻击力提升药水 110; 5. +铁甲 110; 6. +铁盔 105', 'status': '无', 'lv': 4, 'HP': 2700, 'HP_max': 2700, 'attack': 66, '最小属性攻击力': 61, '最大属性攻击力': 71, 'defence': 33, '火焰伤害': 5, '寒冷伤害': 0, '闪电伤害': 5, '毒素伤害': 0, '火焰抵抗力': 5, '寒冷抵抗力': 0, '闪电抵抗力': 5, '毒素抵抗力': 0, 'Exp': 210, 'Exp_max': 210, 'evade_rate': 0, 'double_hit_rate': 15, 'critical_hit_rate': 2, 'coin': 161, 'status_times': 0}, 'attr_list': ['drop_rate', 'status', 'lv', 'HP', 'HP_max', 'attack', '最小属性攻击力', '最大属性攻击力', 'defence', '火焰伤害', '寒冷伤害', '闪电伤害', '毒素伤害', '火焰抵抗力', '寒冷抵抗力', '闪电抵抗力', '毒素抵抗力', 'Exp', 'Exp_max', 'evade_rate', 'double_hit_rate', 'critical_hit_rate', 'coin', 'status_times']}

遭遇怪时显示怪的属性

只有在地图上移动时才有可能会遭遇怪,所以在Player.move()方法中添加以下代码:

		if self.is_moved == 1:	
			self.print_location(hero, game)
			m1 = ['绿史莱姆', '术士', '蝙蝠']
			m2 = ['红史莱姆', '蝙蝠', '骷髅']
			self.generate_monsters_in_a_map(m1, m2, hero, game)

is_moved是一个标记,0表示英雄没有移动,1表示移动了,2表示无法移动(比如到了地图边缘)。

print_location()是打印英雄当前XY坐标、所处地点。

	def print_location(self, hero, game):
		game.message_1.append('你的位置: {0}'.format(hero.location))
		game.message_1.append('你的坐标(x, y): ({0}, {1})'.format(hero.x_coordinate, hero.y_coordinate))	

generate_monsters_in_a_map()是英雄在地图上行走时按一定概率发生的事件,如捡到药材,遇到怪。为了区分遇到的怪,设计了两个列表,m1是小怪,m2是首领怪。

	def generate_monsters_in_a_map(self, m1, m2, hero, game):
		if hero.x_coordinate == 9 and hero.y_coordinate == 0:
				self.combat_with_monster('狼', hero, game)
				return 0
		if hero.x_coordinate == 0 and hero.y_coordinate >= 0 and hero.y_coordinate <= 10:
			r1 = random.randrange(0, 100)
			if r1 < GameConst.prob_m1:
				self.combat_with_monster(m1[0], hero, game)
			elif r1 > 100 - GameConst.prob_m2:
				self.combat_with_monster(m2[0], hero, game)
		elif hero.y_coordinate == 9 and hero.x_coordinate > 0 and hero.x_coordinate < 10:
			r1 = random.randrange(0, 100)
			if r1 < GameConst.prob_m1:
				self.combat_with_monster(m1[1], hero, game)
			elif r1 > 100 - GameConst.prob_m2:
				self.combat_with_monster(m2[1], hero, game)
		elif hero.x_coordinate == 9 and hero.y_coordinate >= 0 and hero.y_coordinate <= 10:
			r1 = random.randrange(0, 100)
			if r1 < GameConst.prob_m1:
				self.combat_with_monster(m1[2], hero, game)
			elif r1 > 100 - GameConst.prob_m2:
				self.combat_with_monster(m2[2], hero, game)
		else:
			r1 = random.randrange(0, 100)
			if r1 < 20:
				self.obtained_item('小灵芝', 1, hero, game)
			elif r1 < 35:
				self.obtained_item('中灵芝', 1, hero, game)
			elif r1 < 65:
				self.obtained_item('小药草', 1, hero, game)
			elif r1 < 80:		
				self.obtained_item('中药草', 1, hero, game)
			elif r1 < 85:
				self.obtained_item('大药草', 1, hero, game)

combat_with_monster()方法表示英雄进入战斗状态,为此,使用2个变量game_processstatus进行存储(一个标记,一个作为对照)。

	def combat_with_monster(self, m, hero, game):
		self.status = GameConst.HERO_ATTACKING
		self.game_process = GameConst.HERO_ATTACKING
		hero.current_monster = m
		game.message_2 = []
		game.message_2.append("遇到敌人: {0}。".format(m))
		game.message_2.append('开始战斗: ')

然后,用一个计时器3产生怪的攻击事件。

	# 设置怪攻击事件
	MYEVENT01 = pygame.USEREVENT + 1
	pygame.time.set_timer(MYEVENT01, 1000)	
	# 主循环
	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 == MYEVENT01:
				if player.game_process == GameConst.HERO_ATTACKING:
					player.Monster_do_melee_attack(hero, game)	
			elif event.type == pygame.KEYDOWN:
				keys = pygame.key.get_pressed()
				game.respond_to_keys(keys, player, hero, game)

而英雄的攻击则由玩家控制,设计的是按a就进行攻击。为此,在pygame.KEYDOWN事件中记录玩家按下的按键,然后用game.respond_to_keys()方法进行处理。

def respond_to_keys(self, keys, player, hero, game):
		''' 对按键输入的响应。'''
		if keys[K_a]:
			if player.game_process == GameConst.HERO_ATTACKING:
				result = player.Hero_do_melee_attack(hero, game)
				if result == 1:
					player.Hero_wins_combat(hero, game)

显示英雄的属性面板

为了查看英雄的详细属性,查看英雄的背包,并且使用背包里的物品,需要设计一个属性面板,如图所示。
唐小山的世界-图片8
为此,在Game类中增加一个方法,打印这个面板。

	def print_panels(self, player, hero, game):		
		# 显示英雄属性面板
		if player.game_process == GameConst.HERO_PROPERTIES_PANEL:	
			self.screen.fill(RGB.Black)	
			# 英雄属性信息框
			pygame.draw.rect(self.DISPLAYSURF, RGB.Black, GameConst.CharBox, 0)	
			pygame.draw.rect(self.DISPLAYSURF, RGB.White, GameConst.CharBox, 1)			
			# 英雄装备信息框
			pygame.draw.rect(self.DISPLAYSURF, RGB.Black, GameConst.EquipBox, 0)	
			pygame.draw.rect(self.DISPLAYSURF, RGB.White, GameConst.EquipBox, 1)
			# 英雄背包信息框
			pygame.draw.rect(self.DISPLAYSURF, RGB.Black, GameConst.BackpackBox, 0)	
			pygame.draw.rect(self.DISPLAYSURF, RGB.White, GameConst.BackpackBox, 1)

然后要实现鼠标移动时消息框高亮,原理与之前讲的一样,请参考:用pygame编写单人RPG小游戏(一)_下唐人的博客-CSDN博客

完整的程序运行结果如图所示。

唐小山的世界-图片12

唐小山的世界-图片9唐小山的世界-图片10
唐小山的世界-图片11唐小山的世界-图片13
完整的源代码已经上传到GitCode:class3 · master · 下唐人 / magic_tower_chapter_0 · GitCode


  1. pandas,百度百科 ??

  2. pandas安装,菜鸟教程 ??

  3. Pygame(十七)定时器,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-16 11:18:33  更:2022-05-16 11:18:37 
 
开发: 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年12日历 -2024/12/27 15:52:40-

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