环境准备
操纵键鼠
驱动安装 链接库加载 代码准备和游戏外测试
toolkit.py
游戏内测试
键鼠监听
武器识别
如何大略且高效判断是否在游戏内
如何大略且高效判断背包状态 无武器/1号武器/2号武器
如何大略且高效判断武器子弹种别
如何大略且高效判断武器名称
如何大略且高效判断武器模式 全自动/连发/单发
何时触发识别
压枪思路
组织数据
第一阶段实现 能自动识别出所有武器
cfg.py
toolkit.py
apex.py
第二阶段实现 能自动识别出所有武器并采取对应压枪参数实行压枪
第三阶段实现 放弃抖枪术 转常规后座抵消法
本文为下面参考文章的学习与实践
[原文] FPS游戏自动枪械识别+压枪(以PUBG为例)
环境准备Python Windows 开拓环境搭建
解释
根本环境从 python 官网下载安装包并安装, 配置环境变量后, 在命令行内可以实行 python 命令
包管理工具pip: 一个当代的,通用的 Python 包管理工具。供应了对 Python 包的查找、下载、安装、卸载的功能。注:pip 已内置于 Python 3.4 和 2.7 及以上版本,其他版本需另行安装。虚拟环境python 根本环境下, 不同的依赖只能存在一个版本, 而不同的项目可能依赖了同一个包的不同版本, 这样的项目就可能无法在同一个 python 根本环境下运行. 基于根本环境创建的虚拟环境是相互隔离的, 第三方依赖包可根据项目哀求自行下载, 不同项目运行在不同的虚拟环境几下就可以避免以来冲突等问题
虚拟环境只能基于本地存在的根本环境来创建, 会继续根本环境自带的库, 可以选择是否继续根本环境的已安装的第三方包
我以为可以借鉴学习 java maven 的依赖管理理念, 告别虚拟环境
虚拟环境管理工具virtualenv: virtualenv可以为每个项目创建一套隔离的Python环境, 再利用pip进行包管理venv: python 3.3 起自带的虚拟环境管理工具pipenv:virtualenvwrapper:virtualenvwrapper-win:virtualenv-burrito:autoenv:pyvenv:pyenv:CondaCondaCondaMinicondaAnaconda
Conda 是一个开源的 环境和包管理系统, 它可以创建并管理完备隔离的不同版本的 python 环境, 也可以创建并管理某 python 版本的完备隔离的虚拟环境, 用了它就不必再安装根本环境了
默认配置下, Conda 可以安装和管理由 Anaconda® 构建、审查和掩护的数千个包。版本常日低于最新版
Anaconda: Anaconda是一个打包的凑集,里面预装好了 Conda、Python、浩瀚数据科学和机器学习干系的包、科学打算工具等等,以是也称为Python的一种发行版。Miniconda: Miniconda 是一个免费的 conda 最小安装程序。 它是 Anaconda 的一个小型勾引版本,仅包含 Conda、Python、它们所依赖的包以及少量其他有用的包,包括 pip、zlib 和其他一些包。Anaconda Navigator: Anaconda 的 GUI 管理工具根本环境搭建Python 官网Python Windows 下载
到官网找到 Windows 最新版下载并安装
pip 是 Python 包管理工具,该工具供应了对Python 包的查找、下载、安装、卸载的功能。
什么是 Python Launcher?
python 安装程序会自动在 path 环境变量中添加这两条款次
目录构造解释
vc dll 构造体_python的安装目录构造
python.exe: python 阐明器, 运行时会弹出掌握台窗口pythonw.exe: 无窗口的python可实行程序, 代码在后台运行DLLs: 包含 python 的 .pyd(Python动态模块)文件与几个Windows的 .dll(动态链接库)文件pyd 文件是由 D 措辞编写的一种 dll 文件, 可以保护 python 文件的源码不被暴露Doc: 帮助文档include: python 的 C 措辞接口头文件(.h结尾), 当在 C 程序中集成 python 时, 会用到这个目录下的头文件C措辞中, 后缀为 .h 的文件是头文件, 内含函数声明、宏定义、构造体定义等内容。 后缀为 .c 的文件是源文件, 内含函数实现,变量定义等内容。 为什么要有头文件? C/C++编译的时候先扫描全体文件有没有语法缺点, 然后将C语句转化为汇编, 当碰到不认识的变量、类、函数、工具的命名时, 首先查找它有没有声明, 如果没有声明直接报错, 如果有,则根据对应的定义空出一定的存储空间并进行干系的指令转化。Lib: python 自带的标准库/包/测试套件等Lib/site-packages: 存放安装的第三方库, pip install 安装的第三方库就放在这里libs: python 的 C 措辞接口库文件Scripts: 脚本文件, 如 pip.exe 包管理器等tcl: python 与 TCL 的结合Tools: 一些工具Miniconda 环境搭建Miniconda
红字提示, 不推举勾选添加环境变量, 由于可能会导致因路径被添加到靠前的位置而造成问题. 如果是首次安装 python 干系环境, 可以选择添加到环境变量选项, 如果已经有在用的其他配置了 PATH 的 Conda 或者 Python 则不建议
确实在用户环境变量 PATH 里加了很多目录, 查看这些目录下都有哪些 exe, 根目录下有 python.exe
利用办法
安装完成后, 从开始菜单中找到并打开 [Anaconda Prompt], 运行 [conda list] 命令, 如果精确安装, 则会涌现已安装的包列表
常用命令
Command referenceconda常用命令:安装,更新,创建,激活,关闭,查看,卸载,删除,清理,重命名,换源,问题Anaconda /Miniconda 常用命令CONDA凑集
查看帮助conda -hconda --helpconda install -hconda install --helpconda env -h12345
查看信息
conda info # 包含 conda, python, pip 等, 还有当前在 conda 命令行中激活的环境1
conda env listconda info -e12
新安装的 Conda 只有 base 根本环境, 没有虚拟环境
配置源windows环境下conda改换为海内清华镜像源
编辑用户目录下的 .condarc 文件即可改换 conda 默认源。
# Windows 用户无法直接创建名为 .condarc 的文件,须要先实行如下命令,天生该文件后再修正。C:\Users\用户名\.condarc# 设置搜索时显示通道地址conda config --set show_channel_urls yes123
修正文件内容
channels: - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ - https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/menpo/ - https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/bioconda/ - https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/ - https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/ - https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/ - defaultsshow_channel_urls: true12345678910
运行 conda clean -i 打消索引缓存,担保用的是镜像站供应的索引。
运行 conda config --show 确认源信息
虚拟环境# 创建虚拟环境conda create -hconda create -n testenvconda create -n testenv2 python=3.8conda create -n testenv3 python=3.10.7 # 貌似不能下 Anaconda 库中没有的 python 版本, 表现便是转圈良久conda create -p C:\mrathena\develop\workspace\pycharm\yolov5-6.2\venv# 查看环境包conda list # 查看当前激活环境的包, 默认激活的是 base 根本环境conda list -n testenv # 查看指定虚拟环境的包# 激活虚拟环境conda activate testenvconda activate C:\mrathena\develop\workspace\pycharm\yolov5-6.2\venv# 反激活conda deactivate # 退出虚拟环境, 重新激活 base 根本环境# 删除虚拟环境conda remove -n testenv --allconda remove -p C:\mrathena\develop\workspace\pycharm\yolov5-6.2\venv --all1234567891011121314151617
如果报错如下, 检讨是否有开代理工具, 关闭代理, 重开工具就可以了
CondaHTTPError: HTTP 000 CONNECTION FAILED for url <https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/win-64/current_repodata.json>1
创建虚拟环境的把稳点
创建虚拟环境时, 一定要指定一个不同于 base python 版本的 python 版本
不然的话, 新的虚拟环境基本等同于没有创建, 利用的仍旧是 base 环境, 实行 pip install 会污染 base 环境, 真是恶心
创建了一个不同于 base python 版本的虚拟环境后, 在虚拟环境中会实打实包含类似 base 的目录构造, 也包含对应的 pip.exe, 这时候再实行 pip install 就不会影响到 base 环境了
IDE PyCharmpycharm的virtualenv、pipenv、conda详解
下载最新版如 pycharm-professional-2021.2.3.exe
以下选项自选
Create Desktop Shortcut, 64-bit launcher, 创建64位启动器的桌面快捷办法, 非常建议Update context menu, Add “Open Folder as Project”, 在高下文菜单(文件夹右键)添加"以项目的办法打开该文件夹"选项, 可选Create Associations, .py, 创建 .py 文件的关联, 默认利用 PyCharm 打开 .py 文件, 非常建议Download and install JRE x86 by JetBrains, 下载 JRE? 不愿定做什么, 不选Update PATH variable(restart needed), Add “bin” folder to the PATH, 更新 PATH 环境变量, 将启动器目录添加到 PATH, 不选创建工程时, 建议每个工程都创建新的虚拟环境, 通过 Conda
测试
在 conda 命令行中也能看到 pycharm 中创建的虚拟环境, 但是没有名字
插件Chinese (Simplified) Language Pack / 中文措辞包
探求文档Pypi
在官网输入包名, 找到包, 点进去, 里面一样平常都会有项目解释, GitHub, 文档等内容
conda create -n apex python=3.91
操纵键鼠
由于绝地求生屏蔽了硬件驱动外的其他鼠标输入,因此我们无法直接通过py脚本来掌握游戏内鼠标操作。为了实现游戏内的鼠标下移,我利用了罗技鼠标的驱动(ghub),而py通过调用ghub的链接库文件,将指令操作通报给ghub,终极实现利用硬件驱动的鼠标指令输入给游戏,从而绕过游戏的鼠标输入限定。值得一提的是,我们只是通过py代码调用链接库的接口将指令通报给罗技驱动的,跟实际利用的是何种鼠标没有关系,以是即便用户利用的是雷蛇、卓威、双飞燕等鼠标,对下面的代码并无任何影响。
驱动安装 链接库加载 代码准备和游戏外测试罗技驱动利用 LGS_9.02.65_X64(请自行找资源安装,官网新版罗技驱动没找到对应的链接库文件),链接库文件在项目链接里面可以找到。下面是载入链接库的代码。
罗技驱动分LGS(老)和GHub(新), 必须装指定版本的LGS驱动(如已安装GHub可能须要卸载), 不然要么报未安装, 要么初始化成功但调用无效
LGS_9.02.65_x64_Logitech.exe, 网盘下载其他地址1其他地址2
try: gm = CDLL(r39;./ghub_device.dll') gmok = gm.device_open() == 1 if not gmok: print('未安装ghub或者lgs驱动!!!') else: print('初始化成功!')except FileNotFoundError: print('短缺文件')123456789
装了该驱动后, 无需重启电脑, 当下就生效了. 遗憾的是, 没有对应的文档, 只能预测参数了
toolkit.pyimport timefrom ctypes import CDLLimport win32api # conda install pywin32try: driver = CDLL(r'mouse.device.lgs.dll') # 在Python的string前面加上‘r’, 是为了见告编译器这个string是个raw string(原始字符串),不要转义backslash(反斜杠) '\' ok = driver.device_open() == 1 if not ok: print('初始化失落败, 未安装lgs/ghub驱动')except FileNotFoundError: print('初始化失落败, 短缺文件')class Mouse: @staticmethod def move(x, y, absolute=False): if ok: mx, my = x, y if absolute: ox, oy = win32api.GetCursorPos() mx = x - ox my = y - oy driver.moveR(mx, my, True) @staticmethod def down(code): if ok: driver.mouse_down(code) @staticmethod def up(code): if ok: driver.mouse_up(code) @staticmethod def click(code): """ :param code: 1:左键, 2:中键, 3:右键, 4:侧下键, 5:侧上键, 6:DPI键 :return: """ if ok: driver.mouse_down(code) driver.mouse_up(code)class Keyboard: @staticmethod def press(code): if ok: driver.key_down(code) @staticmethod def release(code): if ok: driver.key_up(code) @staticmethod def click(code): """ :param code: 'a'-'z':A键-Z键, '0'-'9':0-9, 其他的没猜出来 :return: """ if ok: driver.key_down(code) driver.key_up(code)12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
游戏内测试
在游戏里口试过后, 管用, 但是不准, 预测可能和游戏内鼠标灵敏度/FOV等有关系
from toolkit import Mouseimport pynput # conda install pynputdef onClick(x, y, button, pressed): if not pressed: if pynput.mouse.Button.x2 == button: Mouse.move(100, 100)mouseListener = pynput.mouse.Listener(on_click=onClick)mouseListener.start()mouseListener.join()123456789101112
键鼠监听
前面说到,要实现压枪就要对各种配件、状态做出识别。那么在写识别的函数之前,我们先要办理的是何时识别的问题。如果识别利用多线程\多进程的一贯持续检测,无疑是一种巨大的开销,因此就须要对键盘、鼠标的状态进行监听。只有按下特定按键时,才触发特定相应的识别要求。
这里我利用的钩子是Pynput,其他可利用的库还有Pyhook3
Pynput 解释
def onClick(x, y, button, pressed): print(f'button {button} {"pressed" if pressed else "released"} at ({x},{y})') if pynput.mouse.Button.left == button: return False # 正常不要返回False, 这样会结束监听并停滞监听线程, 在关闭程序前返回False就好了listener = pynput.mouse.Listener(on_click=onClick)listener.start()def onRelease(key): print(f'{key} released') if key == pynput.keyboard.Key.end: return False # 正常不要返回False, 这样会结束监听并停滞监听线程, 在关闭程序前返回False就好了listener = pynput.keyboard.Listener(on_release=onRelease)listener.start()1234567891011121314
Listener中绑定on_press和on_release的函数( on_key_press、on_key_release),它们返回False的时候是结束监听,下文鼠标监听的函数同理,以是不要随便返回False
键盘的分外按键采取keyboard.Key.tab这种写法,普通按键用keyboard.KeyCode.from_char(‘c’)这种写法
这里有一点非常坑,on_press和on_release的参数只能有一个key,这个key便是对应键盘按下的哪颗按键。但这是不敷以知足我们的需求的,由于我们该当在钩子函数内部,在按下指定按键时对旗子暗记量做出修正,但由于参数的限定,我们无法把旗子暗记量传进函数内部,这里我也是想了良久,末了才想到用嵌套函数的写法办理这个问题。
其余,钩子函数本身是壅塞的。也便是说钩子函数在实行的过程中,用户正常的键盘/鼠标操作是无法输入的。以是在钩子函数里面必须写成有限的操作(即O(1)韶光繁芜度的代码),也便是说像背包内配件及枪械识别,还有下文会讲到的鼠标压枪这类韶光开销比较大或者持续韶光长的操作,都不适宜写在钩子函数里面。这也阐明了为什么在检测到Tab(打开背包)、鼠标左键按下时,为什么只是改变旗子暗记量,然后把这些任务丢给别的进程去做的缘故原由。
武器识别如何大略且高效判断是否在游戏内找几个特色点取色判断, 血条左上角和生存物品框左下角
一样平常能用于取色的点, 它的颜色RGB都是相同的, 这种点的颜色非常稳定
我原来以为屏幕点取色该当不会超过1ms的耗时, 结果切切没想到, 取个色居然要1-10ms, 效率奇低, 暂无其他优雅方法
如何大略且高效判断背包状态 无武器/1号武器/2号武器看武器边框上赤色圈住的部分颜色, 灰色解释没有武器, 高下不同色, 解释利用2号武器, 高下同色解释利用1号武器
如何大略且高效判断武器子弹种别可以和上面的放在一起, 同一个点直接判断出背包状态和武器子弹种别
如何大略且高效判断武器名称在分类后的根本上, 通过 背包状态 确定要检讨颜色的位置(1号位/2号位), 通过 武器子弹种别 缩小判断范围, 在每个武器的名字上找一个纯白色的点, 确保这个点只有这把武器是纯白色, 然后逐个比拟
如何大略且高效判断武器模式 全自动/连发/单发须要压枪的只有全自动和半自动两种模式的武器, 单发不须要压枪(后面有可能做自动单发, 到时候在考虑), 喷子和狙不须要压枪
以是须要找一个能区分三种模式的点(不同模式这个点的颜色不同但是稳定), 且这个点不能受和平和三重的分外标记影响
收起武器, 部分武器可以通过[V]标判断, 放弃
何时触发识别键盘 1/2/3/E/V 开释, 鼠标 右键 按下, 这个如果不影响开枪就这个了, 影响的话就改成侧下键. 键位和键在游戏内的功能不冲突的
压枪思路apex 的压枪有两个思路, 由于 apex 不同武器的弹道貌似是固定的, 其他游戏也是??
旁边抖动抵消水平后坐力, 下拉抵消垂直后坐力. 这种方法大略, 但是画面会抖动, 效果也不是很好根据武器配件等测试不同情形下的武器后坐力数据, 然后做反向抵消.可以通过取巧的办法, 只做无配件状态下的反向抵消, 还省了找配件的麻烦这种方法太难太麻烦了, 但是做的好的话, 基本一条线, 强的离谱我先试试 抖枪大法
组织数据武器数据, 通过子弹类型分组, 组里的每个成员指定序号, 名称, 压枪参数等信息
配置数据, 按分辨率分组, 再按是否在游戏中, 是否有武器, 武器位置, 武器子弹类型, 武器索引等信息分类
旗子暗记数据, 程序运行时, 进程线程间通讯
第一阶段实现 能自动识别出所有武器目前测试下来, 一波识别大概六七十毫秒的样子, 最多也不超过一百毫秒, 紧张耗时在取色函数(1-10ms), 性能已经够用了
cfg.pymode = 'mode'name = 'name'game = 'game'data = 'data'pack = 'pack' # 背包color = 'color'point = 'point'index = 'index'bullet = 'bullet' # 子弹differ = 'differ'positive = 'positive' # 肯定的negative = 'negative' # 否定的# 检测数据detect = { "3440:1440": { game: [ # 判断是否在游戏中 { point: (236, 1344), # 点的坐标, 血条左上角 color: 0x00FFFFFF # 点的颜色, 255, 255, 255 }, { point: (2692, 1372), # 生存物品右下角 color: 0x959595 # 149, 149, 149 } ], pack: { # 背包状态, 有无武器, 选择的武器 point: (2900, 1372), # 两把武器时, 1号武器上面边框分边界的上半部分, y+1 便是1号武器上面边框分边界的下半部分 color: 0x808080, # 无武器时, 灰色, 128, 128, 128 '0x447bb4': 1, # 轻型弹药武器, 子弹类型: 1/2/3/4/5/6/None(无武器) '0x839b54': 2, # 重型弹药武器 '0x3da084': 3, # 能量弹药武器 '0xce5f6e': 4, # 偷袭弹药武器 '0xf339b': 5, # 霰弹子弹药武器 '0x5302ff': 6, # 空投武器 }, mode: { # 武器模式, 全自动/半自动/单发/其他 point: (3148, 1349), '0xf8f8f8': 1, # 全自动 '0xfefefe': 2 # 半自动 }, name: { # 武器名称判断 color: 0x00FFFFFF, '1': { # 1号武器 '1': [ # 轻型弹药武器 (2959, 1386), # 1: RE-45 自动手枪 (2970, 1385), # 2: 转换者冲锋枪 (2972, 1386), # 3: R-301 卡宾枪 (2976, 1386), # 4: R-99 冲锋枪 (2980, 1386), # 5: P2020 手枪 (2980, 1384), # 6: 喷火轻机枪 (2987, 1387), # 7: G7 侦查枪 (3015, 1386), # 8: CAR (轻型弹药) ], '2': [ # 重型弹药武器 (2957, 1385), # 1: 赫姆洛克突击步枪 (2982, 1385), # 2: 猎兽冲锋枪 (2990, 1393), # 3: 平行步枪 (3004, 1386), # 4: 30-30 (3015, 1386), # 5: CAR (重型弹药) ], '3': [ # 能量弹药武器 (2955, 1386), # 1: L-STAR能量机枪 (2970, 1384), # 2: 三重式偷袭枪 (2981, 1385), # 3: 电能冲锋枪 (2986, 1384), # 4: 专注轻机枪 (2980, 1384), # 5: 哈沃克步枪 ], '4': [ # 偷袭弹药武器 (2969, 1395), # 1: 哨兵偷袭步枪 (2999, 1382), # 2: 充能步枪 (2992, 1385), # 3: 赞助手枪 (3016, 1383), # 4: 长弓 ], '5': [ # 霰弹子弹药武器 (2957, 1384), # 1: 和平守卫者霰弹枪 (2995, 1382), # 2: 莫桑比克 (3005, 1386), # 3: EVA-8 ], '6': [ # 空投武器 (2958, 1384), # 1: 克雷贝尔偷袭枪 (2983, 1384), # 2: 敖犬霰弹枪 (3003, 1383), # 3: 波塞克 (3014, 1383), # 4: 暴走 ] }, '2': { differ: 195 } } }, "2560:1440": { }, "2560:1080": { }, "1920:1080": { }}# 武器数据weapon = { '1': { # 轻型弹药武器 '1': { name: 'RE-45 自动手枪', }, '2': { name: '转换者冲锋枪', }, '3': { name: 'R-301 卡宾枪', }, '4': { name: 'R-99 冲锋枪', }, '5': { name: 'P2020 手枪', }, '6': { name: '喷火轻机枪', }, '7': { name: 'G7 侦查枪', }, '8': { name: 'CAR (轻型弹药)', } }, '2': { # 重型弹药武器 '1': { name: '赫姆洛克突击步枪', }, '2': { name: '猎兽冲锋枪', }, '3': { name: '平行步枪', }, '4': { name: '30-30', }, '5': { name: 'CAR (重型弹药)', } }, '3': { # 能量弹药武器 '1': { name: 'L-STAR能量机枪', }, '2': { name: '三重式偷袭枪', }, '3': { name: '电能冲锋枪', }, '4': { name: '专注轻机枪', }, '5': { name: '哈沃克步枪', }, }, '4': { # 偷袭弹药武器 '1': { name: '哨兵偷袭步枪', }, '2': { name: '充能步枪', }, '3': { name: '赞助手枪', }, '4': { name: '长弓', }, }, '5': { # 霰弹弹药武器 '1': { name: '和平守卫者霰弹枪', }, '2': { name: '莫桑比克', }, '3': { name: 'EVA-8', }, }, '6': { # 空投武器 '1': { name: '克雷贝尔偷袭枪', }, '2': { name: '敖犬霰弹枪', }, '3': { name: '波塞克', }, '4': { name: '暴走', }, }}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
toolkit.py
import mss # pip install mssimport ctypesfrom ctypes import CDLLimport cfgfrom cfg import detect, weapon# 全局 dlluser32 = ctypes.windll.user32gdi32 = ctypes.windll.gdi32hdc = user32.GetDC(None)try: driver = CDLL(r'mouse.device.lgs.dll') # 在Python的string前面加上‘r’, 是为了见告编译器这个string是个raw string(原始字符串),不要转义backslash(反斜杠) '\' ok = driver.device_open() == 1 if not ok: print('初始化失落败, 未安装lgs/ghub驱动')except FileNotFoundError: print('初始化失落败, 短缺文件')class Mouse: @staticmethod def point(): return user32.GetCursorPos() @staticmethod def move(x, y, absolute=False): if ok: mx, my = x, y if absolute: ox, oy = user32.GetCursorPos() mx = x - ox my = y - oy driver.moveR(mx, my, True) @staticmethod def moveHumanoid(x, y, absolute=False): """ 仿真移动(还没做好) """ if ok: ox, oy = user32.GetCursorPos() # 原鼠标位置 mx, my = x, y # 相对移动间隔 if absolute: mx = x - ox my = y - oy tx, ty = ox + mx, oy + my print(f'({ox},{oy}), ({tx},{ty}), x:{mx},y:{my}') # 以绝对位置办法移动(防止相对位置丢失精度) adx, ady = abs(mx), abs(my) if adx <= ady: # 水平方向移动的间隔短 for i in range(1, adx): ix = i if mx > 0 else -i temp = int(ady / adx abs(ix)) iy = temp if my > 0 else -temp Mouse.move(ox + ix, oy + iy, absolute=True) # time.sleep(0.001) else: # 垂直方向移动的间隔短 for i in range(1, ady): iy = i if my > 0 else -i temp = int(adx / ady abs(iy)) ix = temp if mx > 0 else -temp Mouse.move(ox + ix, oy + iy, absolute=True) # time.sleep(0.001) @staticmethod def down(code): if ok: driver.mouse_down(code) @staticmethod def up(code): if ok: driver.mouse_up(code) @staticmethod def click(code): """ :param code: 1:左键, 2:中键, 3:右键, 4:侧下键, 5:侧上键, 6:DPI键 :return: """ if ok: driver.mouse_down(code) driver.mouse_up(code)class Keyboard: @staticmethod def press(code): if ok: driver.key_down(code) @staticmethod def release(code): if ok: driver.key_up(code) @staticmethod def click(code): """ 键盘按键函数中,传入的参数采取的是键盘按键对应的键码 :param code: 'a'-'z':A键-Z键, '0'-'9':0-9, 其他的没猜出来 :return: """ if ok: driver.key_down(code) driver.key_up(code)class Monitor: """ 显示器 """ sct = mss.mss() @staticmethod def grab(region): """ region: tuple, (left, top, width, height) pip install mss """ left, top, width, height = region return Monitor.sct.grab(monitor={'left': left, 'top': top, 'width': width, 'height': height}) @staticmethod def pixel(x, y): """ 效率很低且不稳定, 单点检测都要耗时1-10ms 获取颜色, COLORREF 格式, 0x00FFFFFF 结果是int, 可以通过 print(hex(color)) 查看十六进制值 可以通过 print(color == 0x00FFFFFF) 进行颜色判断 """ # hdc = user32.GetDC(None) return gdi32.GetPixel(hdc, x, y) class Resolution: """ 分辨率 """ @staticmethod def display(): """ 显示分辨率 """ w = user32.GetSystemMetrics(0) h = user32.GetSystemMetrics(1) return w, h @staticmethod def virtual(): """ 多屏幕组合的虚拟显示器分辨率 """ w = user32.GetSystemMetrics(78) h = user32.GetSystemMetrics(79) return w, h @staticmethod def physical(): """ 物理分辨率 """ # hdc = user32.GetDC(None) w = gdi32.GetDeviceCaps(hdc, 118) h = gdi32.GetDeviceCaps(hdc, 117) return w, hclass Game: """ 游戏工具 """ @staticmethod def game(): """ 是否在游戏内 太耗时了, 以是不能调的多了 """ w, h = Monitor.Resolution.display() data = detect.get(f'{w}:{h}').get(cfg.game) for item in data: x, y = item.get(cfg.point) if Monitor.pixel(x, y) != item.get(cfg.color): return False return True @staticmethod def index(): """ 武器索引和子弹类型索引 :return: 武器位索引, 1:1号位, 2:2号位, None:无武器, 拳头(这个暂时无法判断) 子弹类型索引, 1:轻型, 2:重型, 3:能量, 4:偷袭, 5:霰弹, 6:空投, None:无武器 """ w, h = Monitor.Resolution.display() data = detect.get(f'{w}:{h}').get(cfg.pack) x, y = data.get(cfg.point) color = Monitor.pixel(x, y) if data.get(cfg.color) == color: return None, None else: bullet = data.get(hex(color)) return (1, bullet) if color == Monitor.pixel(x, y + 1) else (2, bullet) @staticmethod def weapon(index, bullet): """ 通过武器位和子弹类型识别武器, 参考:config.detect.name :param index: 武器位, 1:1号位, 2:2号位 :param bullet: 子弹类型, 1:轻型, 2:重型, 3:能量, 4:偷袭, 5:霰弹, 6:空投 :return: """ w, h = Monitor.Resolution.display() data = detect.get(f'{w}:{h}').get(cfg.name) color = data.get(cfg.color) if index == 1: lst = data.get(str(index)).get(str(bullet)) for i in range(len(lst)): x, y = lst[i] if color == Monitor.pixel(x, y): return i + 1 elif index == 2: differ = data.get(str(index)).get(cfg.differ) lst = data.get(str(1)).get(str(bullet)) for i in range(len(lst)): x, y = lst[i] if color == Monitor.pixel(x + differ, y): return i + 1 return None @staticmethod def mode(): """ 武器模式 :return: 1:全自动, 2:半自动, None:其他 """ w, h = Monitor.Resolution.display() data = detect.get(f'{w}:{h}').get(cfg.mode) x, y = data.get(cfg.point) color = Monitor.pixel(x, y) return data.get(hex(color)) @staticmethod def detect(): """ 决策是否须要压枪, 向旗子暗记量写数据 """ if Game.game() is False: print('not in game') return index, bullet = Game.index() if (index is None) | (bullet is None): print('no weapon') return if Game.mode() is None: print('not in full auto or semi auto mode') return arms = Game.weapon(index, bullet) if arms is None: print('detect weapon failure') return # 检测通过, 须要压枪 print(weapon.get(str(bullet)).get(str(arms)).get(cfg.name)) return weapon.get(str(bullet)).get(str(arms)).get(cfg.name)123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
apex.py
import timeimport pynput # conda install pynputimport toolkitExitFlag = Falsedef down(x, y, button, pressed): global ExitFlag if ExitFlag: print(ExitFlag) return False # 结束监听线程 if pressed: # 按下 if pynput.mouse.Button.right == button: toolkit.Game.detect()mouseListener = pynput.mouse.Listener(on_click=down)mouseListener.start()def release(key): if key == pynput.keyboard.Key.end: print('end') global ExitFlag ExitFlag = True return False if key == pynput.keyboard.KeyCode.from_char('1'): toolkit.Game.detect() elif key == pynput.keyboard.KeyCode.from_char('2'): toolkit.Game.detect() elif key == pynput.keyboard.KeyCode.from_char('3'): toolkit.Game.detect() elif key == pynput.keyboard.KeyCode.from_char('e'): toolkit.Game.detect() elif key == pynput.keyboard.KeyCode.from_char('v'): toolkit.Game.detect()keyboardListener = pynput.keyboard.Listener(on_release=release)keyboardListener.start()keyboardListener.join()123456789101112131415161718192021222324252627282930313233343536373839404142434445
1