如何让游戏手柄joystick的按键映射键盘keyboard按键,方便不支持手柄的pygame游戏可以通过简单设置后用手柄进行操控

如何让游戏手柄joystick的按键映射键盘keyboard按键,方便不支持手柄的pygame游戏可以通过简单设置后用手柄进行操控

近日下载了一款Justin Armstrong在github中的马里奥第一关的python游戏,点击作者名字,可以到作者的Github中下载源码。虽然Justin只开发了第一关,但是通过简易的调整,也可以玩出新花样,比如可以设置漫天的加命蘑菇,把每个砖块都设为无敌星,或者变更游戏背景画面,把魂斗罗的地面移到马里奥中,当然还可以增加一些隐形的问号罐,调整地面水沟的距离等,总之和小朋友玩的很愉快,惊喜连连。
在这里插入图片描述

但是也有一个比较麻烦的问题,就是这款游戏的代码中不支持游戏手柄,通过键盘来玩,还是不太适应,怎么办呢? 当然是改代码,把游戏手柄连接进来喽。

于是我查了很多的资料,可惜就是没有找到如何能方便的映射游戏手柄的事件,一般都是讲游戏手柄的初始化,按钮的代号,定义,如何调用等内容,比如:小黑LLB 的译文 Pygame 官方文档 - pygame.joystick,写得非常详细,虽然没能解决最终的问题,也给了我很多启发,非常感谢。

另一方面,由于原作者Justin Armstrong的代码已经很完善,各种逻辑关系,也非常合理,对马里奥的操作,都是通过对Keys的状态进行获取并处理的,所以,如果要加入手柄的参数传递,一定会较大幅度的变更原游戏中的代码,为了不要太麻烦,有没有什么好的办法呢?我想到应该可以通过映射键盘事件,来完成这个功能的传递。可是找了好多资料,却没有找到合适的内容,有一篇文章也讲了对pygame.key.get_pressed() 函数的疑惑,Confused by pygame key.get_pressed() method 里面对于这个函数的功能讲得很清楚,就是会返回一系列的布什尔值,我试着打印了一下,可以看一下:

import pygame as pg
print(pg.key.get_pressed())
# 打印这个函数,会输出以下内容,共计512个布尔值,其中为True的表示该键被按下
# 比如说回车键,是第41个的值为True
"""
pygame.key.ScancodeWrapper(False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False)   
"""
# 与此同时,也通过打印event事件,查看当前键盘的状态:
print(event)
"""
<Event(768-KeyDown {'unicode': '\r', 'key': 13, 'mod': 4096, 'scancode': 40, 'window': None})>

# 可以看到 scancode = 40, 也就是当前按钮的定位,由于计数是从0开始的,所以前文提到的是第41个。
"""

虽然了解了这个函数的功能和状态,也知道了与keys的关系,但是直接把这个结果,赋值给keys是行不通的,看来还得再想想其它办法。那就是如何更直接的传递手柄的按键给键盘呢,有没有一个可以模拟键盘按键的模块呢?最后在https://pypi.org/中找到了一个keyboard模块,通过 pip install keyboard 进行导入。
可以直接模拟键盘按键,主要用到两个功能,keyboard.press 代表按下,keyboard.release 代表松开。
由于手柄的按键识别很多地方都有讲,我这里不再浪费大家的时间,以下是这部分是源代码tools.py中增加游戏手柄按键映射的代码,供参考:

# python 3.10
import pygame as pg
# pip install keyboard 
import keyboard

# 初始化joystick
pg.joystick.init()
# 这里是按一个手柄进行操作,所以对应的是 pg.joystick.Joystick(0)
joystick = pg.joystick.Joystick(0)
joystick.init()

"""
# 如果需要操作多个手柄,则需要循环进行检测,可以参考以下代码:
joystick_lists = [pg.joystick.Joystick(i) for i in range(pg.joystick.get_count())]
# 打印其中第一个手柄的名称
print(joystick_lists[0].get_name())
"""

"""# 这里是对键盘按键功能的定义,"""
keybinding = {
    
    
    'action':pg.K_j,  # 发射子弹
    'jump'  :pg.K_k,  
    'left'  :pg.K_a,
    'right' :pg.K_d,
    'down'  :pg.K_s
}

"""# 接下来的部分,就是获取手柄按键,并对应以上的键盘按键。"""
    def event_loop(self):
        for event in pg.event.get():
            if event.type == pg.QUIT:
                self.done = True
            elif event.type == pg.KEYDOWN:
                self.keys = pg.key.get_pressed()
                self.toggle_show_fps(event.key)    # event.key 表示键盘按键的值,比如k 的值是107, 回车键的值是13,等 
            elif event.type == pg.KEYUP:
                self.keys = pg.key.get_pressed()

            """# 以下增加游戏手柄的控制检测"""
            elif event.type == pg.JOYBUTTONDOWN:  # 检测到手柄上的键按下
                # print(event)
                if joystick.get_button(7)==1:	# get_button(7) 是我这个手柄上的start键
                    keyboard.press('enter')		# 调用键盘中的回车键。
                if joystick.get_button(0)==1:	# get_button(0) 是我这个手柄上的A键
                    keyboard.press('k')			# 对应键盘的k键,也就是对应跳的功能。              
                if joystick.get_button(2)==1:   # get_button(2) 是我这个手柄上的X键
                    keyboard.press('j')
                if joystick.get_button(1)==1:   # get_button(1) 是我这个手柄上的B键
                    keyboard.press('k')
                if joystick.get_button(3)==1:	# get_button(3) 是我这个手柄上的Y键
                    keyboard.press('j')

            elif event.type ==pg.JOYBUTTONUP:	 # 检测到手柄上的键放开
                if joystick.get_button(7)==0:	 # get_button(7) 是我这个手柄上的start键
                    keyboard.press('enter')		 # 手柄按键放天的时候,键盘中的回车键也要放开。
                if joystick.get_button(0)==0:
                    keyboard.release('k')
                if joystick.get_button(2)==0:  
                    keyboard.release('j')
                if joystick.get_button(1)==0:
                    keyboard.release('k')
                if joystick.get_button(3)==0:  
                    keyboard.release('j')                    

            elif event.type == pg.JOYHATMOTION:	# 检测到手柄上的十字方向键按下
                #该事件的返回值是一个元组类型的数据,有两个元素,分别表示左右,和上下
                if joystick.get_hat(0)[0]==-1:  # 检测到手柄上的十字方向键 左键按下
                    keyboard.press('a')			# 调用键盘中按下a键的函数
                else:							# 如果没有检测到手柄上的十字方向键 左键按下
                    keyboard.release('a')		# 调用键盘中释放a键的函数

                if joystick.get_hat(0)[0]==1:  # 检测到手柄上的十字方向键 右键按下
                    keyboard.press('d')
                else:
                    keyboard.release('d')

                if joystick.get_hat(0)[1]==-1:  # 检测到手柄上的十字方向键 往下的按键按下
                    keyboard.press('s')
                else:
                    keyboard.release('s')

            elif event.type == pg.JOYAXISMOTION: # 检测到手柄上的方向轴按下
                if joystick.get_axis(0)<-0.3:    # 方向轴往左的偏移值get_axis(0)从0 到-1,这里设定到-0.3的时候调用按键。
                    keyboard.press('a')
                else:
                    keyboard.release('a')
                
                if joystick.get_axis(0)>0.3:	# 方向轴往右的偏移值get_axis(0)从0 到 1,这里设定到0.3的时候调用按键。
                    keyboard.press('d')
                else:
                    keyboard.release('d')

                if joystick.get_axis(1)>0.7:    # 方向轴往下的偏移值get_axis(1)从0 到 1,这里设定到0.7的时候调用按键。
                    keyboard.press('s')
                else:
                    keyboard.release('s')

            self.state.get_event(event)

以上就是关于游戏手柄与键盘映射的一个初步解决方案,供参考。如果您有更好的解决方案,望能分享。
若需要马里奥第一关的源代码请到作者的Github中下载,有任何疑问,请留言,谢谢!

猜你喜欢

转载自blog.csdn.net/lemiaokai/article/details/127189255