简介

上一篇博文中,我们已经将棋子部署到地图上了,但是还远远不够,我们需要对棋子进行更多的操作。

玩过战棋游戏的小伙伴们应该都清楚,操作棋子有两种方式,一种是用鼠标控制,一种是用键盘控制,本次我们讲解如何用键盘来控制我们的棋子。

正文

通过键盘控制棋子移动,在屏幕上我们是通过光标看到键盘操作的效果的。因此我们需要制作一个光标类

这个光标类应该具备以下几个功能:

  1. 光标移动
  2. 光标选中棋子
  3. 光标取消选中
  4. 光标控制棋子移动

首先我们创建一个光标类,光标本质上也是一种棋子,但是和棋子不一样的是它具有的功能和棋子完全不同。因此我们单独将光标类拎出来。

class Cursor:def __init__(self,raw,col,map_obj):self.map_obj = map_objself.cursor_raw = rawself.cursor_col = colself.cursor = [pygame.image.load('../images/未选中光标.png'),pygame.image.load('../images/选中光标.png'),]self.status = 0self.current_obj = None

在光标类的初始化函数中,我们传入了地图对象,这是为了可以通过地图对象获取到光标选中棋子,同时也可以约束光标移动范围。

另外,光标有两种状态,一种为选中状态,一种选中状态,通过status控制

现在我们在屏幕中导入光标试试

def main():pygame.init()clock = pygame.time.Clock()             # 设置时钟clock.tick(10)                      # 每秒执行60次m = Map()m.load_map(3,5,Dogface())m.load_map(4,7,Store())b = Block()screen = pygame.display.set_mode((m.width,m.height))  # 显示窗口color = (255,255,0)screen.fill(color)c = Cursor(0,0,m)while True:# 轮询事件for event in pygame.event.get():if event.type == pygame.QUIT:   # 如果检测到事件是关闭窗口sys.exit()else:m.create(screen,b)screen.blit(c.cursor[c.status],(c.cursor_col*m.block,c.cursor_raw*m.block))pygame.display.update()pygame.quit()

现在我们看到的效果应该是这样的,在左上角有一个小小的光标:

效果图


一、光标移动

我们将光标设置到屏幕后,就可以开始实现第一个功能,光标移动了。

这个功能还是比较容易实现的,就是通过键盘上下左右四个按钮来改变光标的方位。

首先我们在光标类中添加上下左右的移动判断

class Cursor:def __init__(self,raw,col,map_obj):self.map_obj = map_objself.cursor_raw = rawself.cursor_col = colself.cursor = [pygame.image.load('../images/未选中光标.png'),pygame.image.load('../images/选中光标.png'),]self.status = 0self.current_obj = Nonedef move_up(self):if self.cursor_raw > 0:self.cursor_raw -= 1def move_down(self):if self.cursor_raw < self.map_obj.real_height:self.cursor_raw += 1def move_left(self):if self.cursor_col > 0:self.cursor_col -= 1def move_right(self):if self.cursor_col < self.map_obj.real_width:self.cursor_col += 1

然后我们需要监听键盘的按键事件。

pygame提供了event帮我们监听键盘的按键类型,我们只需要在主函数中添加判断即可

def main():pygame.init()clock = pygame.time.Clock()             # 设置时钟clock.tick(10)                      # 每秒执行60次m = Map()m.load_map(3,5,Dogface())m.load_map(4,7,Store())b = Block()screen = pygame.display.set_mode((m.width,m.height))  # 显示窗口color = (255,255,0)screen.fill(color)c = Cursor(0,0,m)while True:m.create(screen,b)screen.blit(c.cursor[c.status],(c.cursor_col*m.block,c.cursor_raw*m.block))pygame.display.update()# 轮询事件for event in pygame.event.get():if event.type == pygame.QUIT:   # 如果检测到事件是关闭窗口sys.exit()elif (event.type == pygame.KEYDOWN and event.key == pygame.K_UP):c.move_up()elif (event.type == pygame.KEYDOWN and event.key == pygame.K_DOWN):c.move_down()elif (event.type == pygame.KEYDOWN and event.key == pygame.K_LEFT):c.move_left()elif (event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT):c.move_right()else:passpygame.quit()

现在我们的光标就可以自由的移动啦。(网上随便找了一个动图制作软件,请忽略水印,如果有更好的gif制作软件,请强推我,万分感谢!)

顺便提一下,从这个时候,我们将地图渲染移出了事件监听循环 ,目的是为了解决以后敌方棋子无法自动行动的bug。但是这又不可避免的增大了系统的消耗。


二、光标选中棋子

光标可以移动后,我我们就要选择我们的棋子。这个功能主要实现两个子功能:

  1. 状态改变:从未选中->选中
  2. 棋子显示可移动范围

我们先来实现第一个子功能,状态改变。

还记得前面初始化光标类的时候提到的status吗?我们就是通过status来控制光标状态的,0是未选中状态,1是选中状态。

所以我们只需要设置当键盘按在a键的时候,光标的状态发生改变

def main():pygame.init()clock = pygame.time.Clock()             # 设置时钟clock.tick(10)                      # 每秒执行60次m = Map()m.load_map(3,5,Dogface())m.load_map(4,7,Store())b = Block()screen = pygame.display.set_mode((m.width,m.height))  # 显示窗口color = (255,255,0)screen.fill(color)c = Cursor(0,0,m)while True:m.create(screen,b)screen.blit(c.cursor[c.status],(c.cursorX*m.block,c.cursorY*m.block))pygame.display.update()# 轮询事件for event in pygame.event.get():if event.type == pygame.QUIT:   # 如果检测到事件是关闭窗口sys.exit()elif (event.type == pygame.KEYDOWN and event.key == pygame.K_UP):c.move_up()elif (event.type == pygame.KEYDOWN and event.key == pygame.K_DOWN):c.move_down()elif (event.type == pygame.KEYDOWN and event.key == pygame.K_LEFT):c.move_left()elif (event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT):c.move_right()elif (event.type == pygame.KEYDOWN and event.key == pygame.K_a):c.status = 1else:passpygame.quit()

现在我们可以看到,按下键盘a键,光标变红了,但是我们的要求是选中棋子才会变红,所以我们要给选中的目标加个判断。

添加判断分三个步骤:

  1. 获取光标当前坐标
  2. 获取当前坐标对应的格子类别
  3. 比较获取格子类别

这时候,我们在光标类中导入的map_ob就起到作用了。

首先我们在光标类添加获取当前坐标对应对象方法

@property
def get_cursor_index_obj(self):return self.map_obj.empty_map[self.cursor_raw][self.cursor_col]

接着在光标类添加catch方法比对获取到的对象是不是我们的棋子类

def catch(self):if isinstance(self.get_cursor_index_obj,Dogface):self.status = 1

最后只需要在主函数的时间监听中调用catch方法即可

def main():pygame.init()clock = pygame.time.Clock()             # 设置时钟clock.tick(10)                      # 每秒执行60次m = Map()m.load_map(3,5,Dogface())m.load_map(4,7,Store())b = Block()screen = pygame.display.set_mode((m.width,m.height))  # 显示窗口color = (255,255,0)screen.fill(color)c = Cursor(0,0,m)while True:m.create(screen,b)screen.blit(c.cursor[c.status],(c.cursorX*m.block,c.cursorY*m.block))pygame.display.update()# 轮询事件for event in pygame.event.get():if event.type == pygame.QUIT:   # 如果检测到事件是关闭窗口sys.exit()elif (event.type == pygame.KEYDOWN and event.key == pygame.K_UP):c.move_up()elif (event.type == pygame.KEYDOWN and event.key == pygame.K_DOWN):c.move_down()elif (event.type == pygame.KEYDOWN and event.key == pygame.K_LEFT):c.move_left()elif (event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT):c.move_right()elif (event.type == pygame.KEYDOWN and event.key == pygame.K_a):c.catch()else:passpygame.quit()

只时候只有棋子类才能被我们光标选中了。 我们光标选中的第一个子功能到这就结束了,接下来我们讲解光标选中的第二个子功能——显示棋子移动范围。

首先我们需要新建一个格子类,叫做可移动格子类

class Removable(Block):def set_block(self):self.block = pygame.image.load('../images/blue.png')

一个很朴素的想法就是,一个棋子可以移动的范围就是他上下左右四个方向所能到的位置

于是我们新建一个算法类,在算法类中实现棋子可移动范围算法

removable = Removable()def movable(m,raw,col,step=5):for i in range(col-step,col+step+1):if i >= 0 and i <= m.real_height:left = raw - (step-abs(i-col)) if raw - abs(i-col) >= 0 else 0right = raw + (step-abs(i-col)) if raw + abs(i-col) <= m.real_width else m.real_widthfor j in range(left,right+1):if i == col and j == raw:passelse:if not m.empty_map[i][j]:m.empty_map[i][j] = removable 

在这个算法中我们首先要把棋子所在的行,列,步数传过来。然后从上到下显示可以移动的范围。

最上面一行肯定只有个格子可以到达,第二行从左到右有个格子,第三行从左到右有个格子,依次类推,一直到棋子所在行数从左到右的格子达到最大值——2*step,再往下就逐渐减小。直到最下面一行只有个格子可以到达。

然后我们在光标的catch类中调用这个方法,但是发现好像没有办法获取到棋子的当前坐标,所以我们要先在格子类中添加获取当前坐标方法,并且添加设置当前坐标方法

class Block:def __init__(self):self.block= Noneself.cur_raw = 0self.cur_col = 0self.set_block()def set_block(self):passdef get_cur_index(self):return self.cur_raw, self.cur_coldef set_cur_index(self,raw,col):self.cur_raw = rawself.cur_col = col

然后修改地图类的加载棋子方法

def load_map(self,status):raw, col = status.get_cur_index()self.empty_map[raw][col] = status

顺便修改主函数的加载棋子方式:

def main():pygame.init()clock = pygame.time.Clock()             # 设置时钟clock.tick(10)                      # 每秒执行60次m = Map()d = Dogface()d.set_cur_index(3,5)m.load_map(d)store = Store()store.set_cur_index(4,7)m.load_map(store)b = Block()screen = pygame.display.set_mode((m.width,m.height))  # 显示窗口color = (255,255,0)screen.fill(color)c = Cursor(0,0,m)while True:m.create(screen,b)screen.blit(c.cursor[c.status],(c.cursorX*m.block,c.cursorY*m.block))pygame.display.update()# 轮询事件for event in pygame.event.get():if event.type == pygame.QUIT:   # 如果检测到事件是关闭窗口sys.exit()elif (event.type == pygame.KEYDOWN and event.key == pygame.K_UP):c.move_up()elif (event.type == pygame.KEYDOWN and event.key == pygame.K_DOWN):c.move_down()elif (event.type == pygame.KEYDOWN and event.key == pygame.K_LEFT):c.move_left()elif (event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT):c.move_right()elif (event.type == pygame.KEYDOWN and event.key == pygame.K_a):c.catch()else:passpygame.quit()

最后在光标类的catch方法中调用显示可移动范围函数

def catch(self):if isinstance(self.get_cursor_index_obj,Dogface):self.status = 1self.curent_obj = self.get_cursor_index_objraw, col = self.get_cursor_index_obj.get_cur_index()movable(self.map_obj,raw,col)

现在我们来看看效果,目前设置棋子

看起来很完美了,但是这个算法还是有bug的,例如像这样:

乍一看好像没啥问题,但是仔细数数,最右边的格子能走到吗?很明显是走不到的,原因在于目前这个算法没有判断到遇到障碍的情况。

那么怎么解决这个问题呢?一番查找后,终于在某站找到了相关视频(别去找度娘了,度娘搜出来全都是网课教学)。

解决这个问题用到的算法是——洪水算法,个人理解是基于图算法的广度优先实现的。由于洪水算法这个在度娘可以搜到,所以我这就不班门弄斧了。当然如果感兴趣的小伙伴想要了解的话,也可以私聊或者评论我,我可以看情况另开一篇博文讲讲这个算法的实现原理。

现在,我就直接贴出已经实现好的算法:

# 洪水算法
def flood(m,cur_obj):next_d = deque([cur_obj.get_cur_index()])over = deque([])step = cur_obj.step+1while step:cur_d = deepcopy(next_d)next_d = deque([])while cur_d:index = cur_d.popleft()raw = index[0]col = index[1]if raw-1>=0 and (raw-1,col) not in over and m.empty_map[raw-1][col] == 0:next_d.append((raw-1,col))if raw+1<m.real_height and (raw+1,col) not in over and m.empty_map[raw+1][col] == 0:next_d.append((raw+1,col))if col-1>=0 and (raw,col-1) not in over and m.empty_map[raw][col-1] == 0:next_d.append((raw,col-1))if col+1<m.real_width and (raw,col+1) not in over and m.empty_map[raw][col+1] == 0:next_d.append((raw,col+1))over.append(index)step -= 1over.popleft()for raw,col in over:m.empty_map[raw][col] = removablefor raw,col in next_d:m.empty_map[raw][col] = attack

改一下catch方法

def catch(self):if isinstance(self.get_cursor_index_obj,Dogface):self.status = 1self.curent_obj = self.get_cursor_index_objflood(self.map_obj,self.get_cursor_index_obj)

注意,我这次是直接传了cur_obj过去,小伙伴们要看仔细哟。

现在我们来看看效果

效果图

现在看起来效果就好多了,遇到障碍会绕路减少可移动范围。

至于红色的部分就是棋子的可攻击范围,也就是flood方法中的attack。由于这个攻击范围还没有具体实现,所以暂时不在这篇文章的讲解范围内。

总结

又是么有评论,关注,收藏的一周,不过看到蹭蹭蹭上涨的阅读量还是很满足的,说明大家对战棋开发的求知欲还是很旺盛的。

但是,这篇文章还是阉割了。本来想把光标类的四个功能都写完了,但是发现要写的东西太多了。所以只能先分成上下两篇(其实就是想偷懒)。希望大家可以小小的原谅我。当然如果大家能给我点反馈的话,说不定今后的更新量会上升呢。

最后还是老样子,我们下周见咯。

pygame战棋游戏制作之战棋光标设置上(三)相关推荐

  1. pygame战棋游戏制作之战棋地图绘制(一)

    前言 本来有好多篇博文想写的,但是目前在做的是这款战棋游戏的开发,所以其他版块只能先咕咕咕了.或者有关注的小伙伴们可以留言希望下一个更新的版块是啥子?postgresql数据库底层原理?网络编程?计算 ...

  2. pygame战棋游戏制作之战棋回合切换(五)

    简介 前面我们已经完成了战棋地图的绘制,棋子的设置,光标的设置,接下来我们将要开始实现rpg游戏的灵魂--回合切换. 正文 回合切换主要是通过棋子列表判断,当列表为空时,触发回合切换的函数.因此我们创 ...

  3. pygame战棋游戏制作之战棋棋子设置(二)

    简介 在上一篇推文中,我们成功地将地图绘制出来,但是只有一个网格,十分单调,战棋游戏肯定是要有棋子的. 这一篇主要讲解棋子是如何设置的. 正文 棋子本质就是地图二维数组中的一个下标对应的值.在上一篇地 ...

  4. 暗棋单机版_中国暗棋游戏下载-中国暗棋下载v1.0.0 安卓版-单机手游网

    中国暗棋游戏去棋牌类象棋手游,游戏玩法是将象棋到放,然后不断的反面获得游戏的进程,玩法市面简单方便可以和线上线下朋友一起玩耍哦!小编也给你们带来了中国暗棋手游下载地址!此版本为安卓最新完整版,感兴趣的 ...

  5. python井字棋游戏人机对战_用Python做一个井字棋小游戏

    井字棋是一个经典的小游戏,在九宫格上玩家轮流画OXO,当每列或每行或是两个对角成一线时便是获胜. 今天就用Python编写一个井字棋小游戏,与电脑对战. 程序执行画面如下图所示: 程序提供了两种人工智 ...

  6. 三子棋游戏(支持多子棋)

    目录 前言 一.创建三子棋的思路过程? 二.游戏模块 1.棋盘初始化 2.开始下棋 3.判断输赢 三.测试模块 四.源码 总结 前言 用c语言实现一个三子棋的游戏,当然也可以实现多子棋,修改一点参数即 ...

  7. 计算机爱恩斯坦棋游戏,爱恩斯坦棋计算机博弈系统的研究与实现

    爱恩斯坦棋计算机博弈系统的研究与实现 [摘要]:计算机博弈,也称机器博弈,是一个极具挑战与发展前景的计算机研究领域,其作为人工智能领域一个极其重要的课题,素有人工智能领域"果蝇"之 ...

  8. 三子棋游戏(井字棋)

    编写三子棋所需的步骤: 1)首先,我们得先要有一个棋盘,那我们就得先编写一个函数来将棋盘初始化,再编写一个函数负责打印我们的棋盘. 我们打印的棋盘为: 2)有了棋盘后,就可以下棋了.我们可以编写两个函 ...

  9. pygame入门小游戏(外星人入侵(2)设置背景颜色和添加背景图)

    上一节介绍了游戏窗口的创建,今天我们来给游戏添加一个有颜色的背景吧!也可以添加一张你喜欢的图片作为背景^_^ 我们创建一种背景色,并把它存储在bg_color中.颜色只需指定一次,因此在while循环 ...

最新文章

  1. 使用 EF Core 的 EnableRetryOnFailure 解决短暂的数据库连接失败问题
  2. php mysql table_关于php:MySQL Table不存在错误,但确实存在
  3. VirtualBox Linux Samba 设置
  4. 敏捷有效执行的关键软技能
  5. linux是只读添加 来覆盖,Linux之指令 重定向 文件覆盖和文件追加
  6. java integer常量池_为什么Integer常量池的行为在127发生变化?
  7. 二级指针、数组指针、二维数组、指针数组作为函数形参时可传入的实参
  8. vue js 和原生app调用回调方法问题
  9. 生成.properties文件(bat文件生成)
  10. Java 面试—乐/悲观锁,wait/notify/notifyAll对比
  11. 2019年三峡大学计算机考研名单,三峡大学2019硕士研究生复试录取方案
  12. 【数据结构C++】哈希表(三)
  13. PHP完全自学手册01.pdf
  14. word文件怎么压缩?
  15. pgsql 一键修改PGSQL表名、字段名为小写
  16. doc转docx文件会乱吗_docx怎么转换成doc?docx转doc方法汇总
  17. 打怪升级之小白的大数据之旅(六十一)<Hive旅程第二站:Hive安装>
  18. 彭于晏都在看的博客,你还在犹豫什么
  19. 定位教程3---固定相机,先拍后抓
  20. 手机上淘宝教育视频倍速播放方法教学

热门文章

  1. 今日分享-自定义返回按钮(与系统按钮位置一致)
  2. 生产者与消费者的实现
  3. HTML5游戏开发实战
  4. uniapp切换中英文
  5. 2014工作总结与2015展望
  6. 【BUCT数据结构类库】1.2--链表的基本操作
  7. AD绘制怎么画3D封装库?
  8. 其实真正的互联网公司是很少的很少
  9. Arduino基础项目十四:红外对管模块
  10. uniapp踩坑(五):监听手机物理返回键和滑动返回事件