序言

前排声明本文只是以KOF13为例介绍python脚本编写的应用,虽然格斗游戏圈小,但是并无意冒犯KOF13的玩家。

笔者本身是个空闲时间打打拳皇街机的键盘小白,偶尔会连线菜鸡互啄。KOF97至今连特瑞无限连都敲得不妥当,KOF98之后的各个版本再也不能按出反摇拉前的指令投,更不用说跑抓这种高难度动作,大学看了三年Abang的视频,去年入手KOF13,到现在除了能站桩打咬草的一套民工BC,实战BC从来都是用来凹NEOMAXKOF13里的蓄力,八稚女取消乃至最简单的HD取消都做不到,把键盘上的D键都敲坏了也没能按出一次波草的鬼胧取消,实在是悲惨。

昨晚难得打馆长的MISSION挑战,笔者其他角色正常都只能过一两关,熟练些的八神草剃能打通四关,然而第一次玩馆长竟然打到了第六关,那时候就特别想打通,结果第六关的跳AA目押站D敲了半天都确认不到接半月,心灰意冷下笔者决定开始用科技武装自己通关,这才引出了利用python脚本实现KOF13的连段。

但是在编写连段的过程中,笔者遇到一些python模拟键盘中的一些坑点,并且逐渐发现可以实现一键出招以及实现一些自己永远也打不出的连段,比如八稚女取消乃至无限葵花,于是写了这篇博客来分享。本身格斗游戏圈子就小,KOF13圈就更小了,本文主要是分享python在模拟键盘鼠标这类输入器上的方法。

序言的最后挂上自己的战绩,笔者真的是小白中的小白,编写脚本仅仅是自娱自乐,不会在实战里恶心其他玩家的,万望包涵👇

Java源代码 拳皇游戏 拳皇 脚本_模拟键盘

PS:笔者之所以对战完成率很低就是最近不知道怎么回事一连线就卡死在获取对手资料的界面,挂VPN也不管用,然后就不得不强退,还被封了几天号,实在是太冤枉了。

Python模拟键盘鼠标输入

目前就笔者所知,python可用于操控模拟键盘和鼠标的包有以下三个👇

  1. pymouse包与pykeyboard包;
  2. pynput包中的pynput.keyboardpynput.mouse模块;
  3. win32con包与win32api包;

12中的包都可以用pip简单安装,3中的两个包大概是python自带的两个包,或者可能是在pywin32包安装时附带安装的两个包。下面给出12两种包操控键盘鼠标的代码示例👇

pymouse包与pykeyboard包使用示例👇

from pykeyboard import PyKeyboard
 
k = PyKeyboard()
 
x_dim, y_dim = m.screen_size()
m.click(x_dim//2, y_dim//2, 1) #取整除 - 向下取接近除数的整数
k.type_string('Hello, World!')
 
# pressing a key
k.press_key('H')
# which you then follow with a release of the key
k.release_key('H')
# or you can 'tap' a key which does both
k.tap_key('e')
# note that that tap_key does support a way of repeating keystrokes with a interval time between each
k.tap_key('l',n=2,interval=5)
# and you can send a string if needed too
k.type_string('o World!')
 
#Create an Alt+Tab combo
k.press_key(k.alt_key)
k.tap_key(k.tab_key)
k.release_key(k.alt_key)
 
k.tap_key(k.function_keys[5]) # Tap F5
k.tap_key(k.numpad_keys['Home']) # Tap 'Home' on the numpad
k.tap_key(k.numpad_keys[5], n=3) # Tap 5 on the numpad, thrice
 
k.press_key(k.alt_key)
k.press_key(k.control_key)
 
####################################################
 
from pymouse import PyMouse
 
# instantiate an mouse object
m = PyMouse()
 
# move the mouse to int x and int y (these are absolute positions)
m.move(200, 200)
 
# click works about the same, except for int button possible values are 1: left, 2: right, 3: middle
m.click(500, 300, 1)
 
# get the screen size
m.screen_size()
# (1024, 768)
 
# get the mouse position
m.position()
# (500, 300)

pynput模拟键盘鼠标输入👇

# pynput模拟键盘
from pynput.keyboard import Key, Controller
 
k.press(Key.space)
k.release(Key.space)
 
k.press('a')
k.press('A')
 
with k.pressed(Key.shift):
k.press('a')
k.release('a')
 
k.type('Hello')
 
# pynput模拟鼠标
from pynput.mouse import Button, Controller
 
m = Controller()
m.position # 鼠标位置
m.position = (10,20) # 调整位置
m.move(5,-5) # 相对位置移动
m.press(Button.left)
m.release(Button.left)
m.click(Button.left,2)
m.scroll(0,2) # 滚两圈滚轮


 

可以发现pykeyboardpymouse以及pynput在操控鼠标键盘上的代码是极为相似且非常通俗易懂,笔者没有完善注释应该也可以很明白每一句代码是什么意思。如模拟键盘主要是模拟点击按键和输入字符串两种效果,模拟鼠标则是移动鼠标位置以及点击鼠标按键。事实上pynput应该相对功能更加完善,它可以实现键盘及鼠标事件的监听,这个在本文后面介绍如何编写一键出招时会介绍到事件监听的方法。

但是本文的脚本是基于3中的两个包来实现的。原因是12中的包都不能在KOF13运行的环境下成功模拟输入指令,笔者在测试过程中发现如果使用pynput或者pykeyboard来实现模拟键盘输入指令在KOF13中只会起到让角色挑衅一下的效果,虽然不是很明白这里面的机制,笔者猜想可能是因为3中的两个包更贴近硬件底层,而KOF13可能在控制器输入这块做了一些转换。

以下为利用win32conwin32api两个包编写的自定义模拟按键函数👇

# -*- coding: UTF-8 -*-
# @author: caoyang
# @email: lzwcy110@163.com
 
import time
import win32con
import win32api
 
key2code = { # 键盘上的每个按键对应的键码
"0": 49, "1": 50, "2": 51, "3": 52, "4": 53,
"5": 54, "6": 55, "7": 56, "8": 57, "9": 58,
"A": 65, "B": 66, "C": 67, "D": 68, "E": 69, "F": 70, "G": 71,
"H": 72, "I": 73, "J": 74, "K": 75, "L": 76, "M": 77, "N": 78,
"O": 79, "P": 80, "Q": 81, "R": 82, "S": 83, "T": 84,
"U": 85, "V": 86, "W": 87, "X": 88, "Y": 89, "Z": 90,
}
 
def key_down(key): # 按下键盘上的按键key
key = key.upper()
vk_code = key2code[key]
win32api.keybd_event(vk_code,win32api.MapVirtualKey(vk_code,0),0,0)
 
def key_up(key): # 抬起键盘上的按键key
key = key.upper()
vk_code = key2code[key]
win32api.keybd_event(vk_code, win32api.MapVirtualKey(vk_code,0),win32con.KEYEVENTF_KEYUP,0)
 
def key_press(key,interval=0.016): # 按下-->抬起键盘上的按键key, 停顿interval时间
key_down(key)
time.sleep(interval)
key_up(key)


 

注意到这里主要是涉及按键按下与按键松开两个动作,本质上只要有两个动作就完全足够了。核心函数是win32api.keybd_event,这个函数的详细用法可以参照polyhedronx的博客keybd_event模拟键盘输入,限于篇幅笔者不多作介绍。

有了必要的知识储备后笔者开始编写KOF13的连段脚本。注意以下代码中的key_presskey_upkey_down函数都已经在上述代码中写好了

一些简单的小连段

笔者的上下左右按键为WSADABCD的按键分别为JKUIAC组合键YBD组合键OAB组合键HBC组合键L

先以八神的葵花三段作为一个简单的例子。

注意KOF13对指令的要求是较为精确的,如果只是单纯敲击三遍 ↓ ← + LP 是不会有任何动作发生的👇

# 错误的葵花三段指令
 
interval = 0.016
for i in range(3):
key_press("s")
time.sleep(interval)
key_press("a")
time.sleep(interval)
key_press("j")
time.sleep(interval)
time.sleep(0.1)

代码执行效果👇

Java源代码 拳皇游戏 拳皇 脚本_模拟键盘_02

 

修正代码后我们再来测试一次葵花三段👇


# 正确的葵花三段指令 interval = 0.016for i in range(3):	key_down("s")	time.sleep(interval)	key_down("a")	time.sleep(interval)	key_up("s")	time.sleep(interval)	key_up("a")	time.sleep(interval)	key_press("j",interval=interval)	time.sleep(0.1)


代码执行效果👇

Java源代码 拳皇游戏 拳皇 脚本_模拟键盘_03

从左边的一列指令可以看到上述代码非常精准地实现了葵花三段的指令。其中每次敲击键盘的时间间隔interval设置为0.016是考虑到KOF全系列都是60帧的画面,每帧大约0.016秒,每段葵花间一定要间隔一定时间,上述代码中设置为0.1秒,否则连续一顿按反摇拳是出不了三段葵花的。

同理我们可以精确实现八稚女的指令👇

# 八稚女脚本
 
interval = 0.016
key_down("s")
time.sleep(interval)
key_down("d")
time.sleep(interval)
key_up("s")
time.sleep(interval)
key_down("s")
time.sleep(interval)
key_up("d")
time.sleep(interval)
key_down("a")
time.sleep(interval)
key_up("s")
time.sleep(interval)
key_up("a")
time.sleep(interval)
key_press("j")

代码执行效果👇

Java源代码 拳皇游戏 拳皇 脚本_Java源代码 拳皇游戏_04

接下来为了实现无限葵花,必不可少的是八稚女取消,即在两段小葵花后立刻输入八稚女指令,然后接着输入两段小葵花,为了测得八稚女取消的精准放帧时间,笔者采用两段小葵花→八稚女取消→大升龙的简易连段来测量,最终得到下面代码中的放帧时间👇

# 八稚女取消示例脚本
 
interval = 0.016
 
# 1. 两段小葵花
## 1.1 第一段
key_down("s")
time.sleep(interval)
key_down("a")
time.sleep(interval)
key_up("s")
time.sleep(interval)
key_up("a")
key_press("j",interval=interval)
time.sleep(0.25)
## 1.2 第二段
key_down("s")
time.sleep(interval)
key_down("a")
time.sleep(interval)
key_up("s")
time.sleep(interval)
key_up("a")
key_press("j",interval=interval)
time.sleep(0.03)
 
# 2. 八稚女取消
key_down("s")
time.sleep(interval)
key_down("d")
time.sleep(interval)
key_up("s")
time.sleep(interval)
key_down("s")
time.sleep(interval)
key_up("d")
time.sleep(interval)
key_down("a")
time.sleep(interval)
key_up("s")
time.sleep(interval)
key_up("a")
time.sleep(interval)
key_press("j")
time.sleep(0.4)
 
# 3. 大升龙
key_press("d")
time.sleep(interval)
key_down("s")
time.sleep(interval)
key_down("d")
time.sleep(interval)
key_up("s")
key_up("d")
key_press("u")

可知两段小葵花后放帧0.03秒,八稚女指令输入完后放帧0.4秒,调参过程中笔者发现这些放帧的秒数精确度要求几乎已经达到1/50秒,即差不多是1帧的级别,而且这还是笔者在零HD槽的情况下测试出的放帧秒数,有HD槽这段代码就直接会把八稚女放出来,真的难以想象这些能每次都能精确打出八稚女取消的大佬是怎么做到的。代码测试实际效果如下所示(两段小葵花→八稚女取消→大升龙)👇

Java源代码 拳皇游戏 拳皇 脚本_python脚本_05

注意可以看到八神在升龙时没有消耗HD槽,这表明八稚女取消是成功的,但是为了调出这个放帧时间,几乎是花了将近一个小时,因为靠人眼真的很难捕捉到指令的输入的速度是否是合理的。

最后笔者试图实现无限葵花,不过不知道是因为笔者对无限葵花指令的理解有偏差,还是说真的无限葵花对输入指令的节奏要求特别高,总之一直不能实现到无限葵花,即便是EX葵花起接大升龙取消的起手,也葵不出两循环以上,真的是佩服那些用手把无限葵花搓出来的巨佬,这里只能放上无限葵花的脚本代码,但是其中time.sleep()的参数是不能真正实现无限葵花的👇

# 无限葵花脚本
 
interval = 0.016
# 1. 无限葵花前先用EX葵花打出高浮空
key_down("s")
time.sleep(interval)
key_down("a")
time.sleep(interval)
key_up("s")
time.sleep(interval)
key_up("a")
key_press("y",interval=interval)
 
time.sleep(0.5)
 
key_down("s")
time.sleep(interval)
key_down("a")
time.sleep(interval)
key_up("s")
time.sleep(interval)
key_up("a")
key_press("j",interval=interval)
 
time.sleep(0.75)
 
# 2. 再用大升龙进一步提高浮空高度
key_press("d")
time.sleep(interval)
key_down("s")
time.sleep(interval)
key_down("d")
time.sleep(interval)
key_up("s")
key_up("d")
key_press("u")
time.sleep(0.4)
 
# 3. 无限葵花开始
 
for i in range(10):
 
# 3.1 第一段小葵花
key_down("s")
time.sleep(interval)
key_down("a")
time.sleep(interval)
key_up("s")
time.sleep(interval)
key_up("a")
key_press("j",interval=0.016)
 
time.sleep(0.05)
 
# 3.2 第二段小葵花
key_down("s")
time.sleep(interval)
key_down("a")
time.sleep(interval)
key_up("s")
time.sleep(interval)
key_up("a")
time.sleep(interval)
key_press("j",interval=0.016)
 
time.sleep(0.25)
 
# 3.3 八稚女取消
key_down("s")
time.sleep(interval)
key_down("d")
time.sleep(interval)
key_up("s")
time.sleep(interval)
key_down("s")
time.sleep(interval)
key_up("d")
time.sleep(interval)
key_down("a")
time.sleep(interval)
key_up("s")
time.sleep(interval)
key_up("a")
time.sleep(interval)
key_press("j")
 
time.sleep(2)