发布时间:2025-12-09 16:17:06 浏览次数:4
https://v.qq.com/x/page/m09169jorj0.html?vuid24=UQ4gZ9NuNjFsyWtJ%2FXiQNw%3D%3D&ptag=2_7.3.6.19976_copy
在此声明,本博客仅作学习交流使用,不可用于任何商业途径,对产生的任何影响,本人概不负责。如有侵权,请联系删除。
本文参考自https://blog.csdn.net/chijiandi/article/details/85276661#_1
玩了好几年dnf,虽然脱坑了,正好有个兴趣,就做了一个简陋的脚本。代码水平有限,正在努力学习中。
语言:python
工具:pycharm
win10使用键盘模拟时,开启测试模式。
bcdedit /set testsigning on
首先需要定位人物和怪物,本文修改了npk文件,怪物大小设置25*25,颜色为纯色。通过找色块找到位置。
author = 'Yanxiang'version = '1.0'date = '10/06/2019'import cv2import numpy as npimport timedef detective(img_path,flag):kernel_4 = np.ones((4, 4), np.uint8) # 4x4的卷积核def separate_color_yellow(hsv):# 怪物颜色提取lower_hsv = np.array([0, 0, 255]) # 提取颜色的低值..high_hsv = np.array([0, 0, 255]) # 提取颜色的高值mask = cv2.inRange(hsv, lowerb=lower_hsv, upperb=high_hsv)print("怪物坐标提取完成")return maskdef separate_color_door(hsv):# 门颜色提取lower_hsv = np.array([120, 255, 255]) # 提取颜色的低值high_hsv = np.array([120, 255, 255]) # 提取颜色的高值mask = cv2.inRange(hsv, lowerb=lower_hsv, upperb=high_hsv)print("门的坐标提取完成")return maskdef erocode_dilate(mask):erosion = cv2.erode(mask, kernel_4, iterations=1)erosion = cv2.erode(erosion, kernel_4, iterations=1)dilation = cv2.dilate(erosion, kernel_4, iterations=1)dilation = cv2.dilate(dilation, kernel_4, iterations=1)return dilation# flag = 0 表示找到怪物的平均坐标# flag = 1 表示找到人物坐标def contours(dilation, flag):# target是把原图中的非目标颜**域去掉剩下的图像target = cv2.bitwise_and(Img, Img, mask=dilation)# 将滤波后的图像变成二值图像放在binary中ret, binary = cv2.threshold(dilation, 127, 255, cv2.THRESH_BINARY)# 在binary中发现轮廓,轮廓按照面积从小到大排列img_contours, contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)p = 0inumber = 1# 新建数组存放所有怪物 X,Y坐标的中心值monster_x = []monster_Y = []# pos_x =0# pos_y =0if(len(contours)!=0):for i in contours: # 遍历所有的轮廓x, y, w, h = cv2.boundingRect(i) # 将轮廓分解为识别对象的左上角坐标和宽、高if w * h > 100:if flag == 0:p += 1print('第 %d 个怪物的底中的位置为' % (p), x + w / 2, y + h )monster_x.append(x + w / 2)monster_Y.append(y + h )return monster_x,monster_Yif flag == 2:p += 1print('第 %d 个的中心像素位置为' % (p), x + w / 2, y + h / 2)pos_xx = xpos_yy = yreturn pos_xx,pos_yyelse:if flag == 0:return monster_x,monster_Yif flag == 2:return 0,0Img = cv2.imread(img_path)#读入一幅图像if Img is not None:#判断图片是否读入HSV = cv2.cvtColor(Img, cv2.COLOR_BGR2HSV)#把BGR图像转换为HSV格式# 提取颜色if flag==0:mask_yellow = separate_color_yellow(HSV)dilation_yellow = erocode_dilate(mask_yellow)pos_x,pos_y = contours(dilation_yellow,flag)return pos_x,pos_yelif flag==2:mask_door = separate_color_door(HSV)dilation_red = erocode_dilate(mask_door)pos_x,pos_y = contours(dilation_red,flag)return pos_x,pos_y和上面差不多,不贴了。还有另外一种方法定位的人物。
首先将屏幕置于最前窗口。
class setf():def __init__(self):self.gamename = '地下城与勇士'self.shell = win32com.client.Dispatch("WScript.Shell")self.dll = CDLL("user32.dll")def setfocus(self):# pid = self.get_pid_for_pname(self.gamename)# if pid:# print(222)# for hwnd in self.get_hwnds_for_pid(pid):# 获得窗口句柄hwnd = win32gui.FindWindow(None, "地下城与勇士")# 将dnf窗口保持焦点self.shell.SendKeys('%')self.dll.LockSetForegroundWindow(2)if self.dll.IsIconic(hwnd):win32gui.SendMessage(hwnd, win32con.WM_SYSCOMMAND, win32con.SC_RESTORE, 0)self.dll.SetWindowPos(hwnd, win32con.HWND_TOPMOST, 0, 0, 0, 0,win32con.SWP_NOSIZE | win32con.SWP_NOMOVE)self.dll.SetForegroundWindow(hwnd)self.dll.SetActiveWindow(hwnd)移动 参考模拟按键
完整的按键模拟方法包引用自网络。
import rabird.winioimport timeimport atexit# KeyBoard Commands# Command portKBC_KEY_CMD = 0x64# Data portKBC_KEY_DATA = 0x60__winio = Nonedef __get_winio():global __winioif __winio is None:__winio = rabird.winio.WinIO()def __clear_winio():global __winio__winio = Noneatexit.register(__clear_winio)return __winiodef wait_for_buffer_empty():'''Wait keyboard buffer empty'''winio = __get_winio()dwRegVal = 0x02while (dwRegVal & 0x02):dwRegVal = winio.get_port_byte(KBC_KEY_CMD)def key_down(scancode):winio = __get_winio()wait_for_buffer_empty();winio.set_port_byte(KBC_KEY_CMD, 0xd2);wait_for_buffer_empty();winio.set_port_byte(KBC_KEY_DATA, scancode)def SPkey_down(scancode):winio = __get_winio()wait_for_buffer_empty();winio.set_port_byte(KBC_KEY_CMD, 0xd2);wait_for_buffer_empty();winio.set_port_byte(KBC_KEY_DATA, 0xe0)wait_for_buffer_empty();winio.set_port_byte(KBC_KEY_CMD, 0xd2);wait_for_buffer_empty();winio.set_port_byte(KBC_KEY_DATA, scancode)def key_up(scancode):winio = __get_winio()wait_for_buffer_empty();winio.set_port_byte( KBC_KEY_CMD, 0xd2);wait_for_buffer_empty();winio.set_port_byte( KBC_KEY_DATA, scancode | 0x80);def SPkey_up(scancode):winio = __get_winio()wait_for_buffer_empty();winio.set_port_byte(KBC_KEY_CMD, 0xd2);wait_for_buffer_empty();winio.set_port_byte(KBC_KEY_DATA, 0xe0)wait_for_buffer_empty();winio.set_port_byte(KBC_KEY_CMD, 0xd2);wait_for_buffer_empty();winio.set_port_byte(KBC_KEY_DATA, scancode | 0x80)def mouse_down():winio = __get_winio()wait_for_buffer_empty();winio.set_port_byte(KBC_KEY_CMD, 0xd3);wait_for_buffer_empty();winio.set_port_dword(KBC_KEY_DATA, 0x09)def mouse_up():winio = __get_winio()wait_for_buffer_empty();winio.set_port_byte(KBC_KEY_CMD, 0xd3);wait_for_buffer_empty();winio.set_port_dword(KBC_KEY_DATA, 0x08)def key_press(scancode, press_time = 0.05):key_down( scancode )time.sleep( press_time )key_up( scancode )time.sleep(0.05)def SPkey_press(scancode, press_time = 0.05):SPkey_down( scancode )time.sleep( press_time )SPkey_up( scancode )time.sleep(press_time)def mouse_clicked(clicked_time = 0.05):mouse_down()time.sleep( clicked_time )mouse_up()time.sleep( clicked_time )https://blog.csdn.net/qq_37232329/article/details/79926440
上面的链接为扫描码
从上面得知人物、怪物、门的坐标,可以移动。在移动之前,先计算移动速度。
主要代码如下:
def moveTo(monsters_x,monsters_y,person_x,person_y,flag = 0):''' flag = 1 时,为 人物 向 门口 移动flag = 0 时,为 人物 向 怪物 移动 , 距离怪物 80 个像素时 , 释放技能'''if(flag==0):# 释放技能的位置,在怪物前方60像素skill_pos = 60else:skill_pos = 0# 1 怪物 在 人物 的 右上if((person_x - monsters_x)<0 and (person_y - monsters_y) >0):if(monsters_x - person_x <skill_pos):print('距离右边怪物挺近,直接往上移动')key_press(0x34)key_press(0x25, abs(person_y - monsters_y) / y_speed)else:move_time = abs(monsters_x - person_x - skill_pos) / x_speed - abs(person_y - monsters_y) / y_speedif (move_time > 0):print('跑啊跑,往右上移动')key_press(0x34)key_down(0x34)time.sleep(0.1)key_press(0x25, abs(person_y - monsters_y) / y_speed)time.sleep(move_time)key_up(0xb4)else:print('不值得跑,先往右,后往上')key_press(0x34)key_press(0x34, abs(monsters_x -skill_pos- person_x) / x_speed)key_press(0x25, abs(person_y - monsters_y) / y_speed)剑魂的技冷却时间,释放技能时间。此方法计算的是可用的技能。可以继续优化,如:加入技能释放范围;或者按照一定顺序计算冷却时间最短的技能。
list_to_time = [0,0,0,0,0,0,0,0,0,0,0]list_skill = ['q', 'e', 'w', 'd','t','y','a','s','f','g','h']list_skill_cooling = [ 6.8, 9.6, 32, 9, 37, 103, 22, 0, 17.5,36, 120]skill_release_time = [ 0.5, 0.5, 3, 0.5, 0.5, 7, 5, 0.3, 1, 1.5, 2]list_key = [0x10,0x12,0x11,0x20,0x14,0x15,0x1e,0x1f,0x21,0x22,0x23]def rel_skill(skill,ini_time):for i in list_skill:while(skill==i):list_to_time[list_skill.index(i)] = ini_time + list_skill_cooling[list_skill.index(i)] + skill_release_time[list_skill.index(i)]f = open('data.txt','w')for j in range(len(list_skill)):f.write(str(list_to_time[j])+'\n')f.close()key_press(list_key[list_skill.index(i)])time.sleep(skill_release_time[list_skill.index(i)]+0.8)breakreturn
截图
import win32gui,win32ui,win32con,win32apifrom key import *def window_capture(filename,flag = 0):hwnd = win32gui.FindWindow(None, "地下城与勇士")# 根据窗口句柄获取窗口的设备上下文DC(Divice Context)hwndDC = win32gui.GetWindowDC(hwnd)win32gui.SetForegroundWindow(hwnd)# 根据窗口的DC获取mfcDCmfcDC = win32ui.CreateDCFromHandle(hwndDC)# mfcDC创建可兼容的DCWsaveDC = mfcDC.CreateCompatibleDC()# 创建bigmap准备保存图片saveBitMap = win32ui.CreateBitmap()# 获取监控器信息left, top, right, bottom = win32gui.GetWindowRect(hwnd)width = right - leftheight = bottom - topsaveBitMap.CreateCompatibleBitmap(mfcDC, width, height)# 高度saveDC,将截图保存到saveBitmap中saveDC.SelectObject(saveBitMap)# 截取从左上角(0,0)长宽为(w,h)的图片if flag==0:saveDC.BitBlt((0, 0), (width, height), mfcDC, (0, 0), win32con.SRCCOPY)saveBitMap.SaveBitmapFile(saveDC, filename)else:saveDC.BitBlt((720, 795), (75, 60), mfcDC, (0, 0), win32con.SRCCOPY)saveBitMap.SaveBitmapFile(saveDC, './pos.png')return width, height水平有限,花了5天时间修改npk文件,10几天写代码。初学python,我所会的就只是一些基础以及一丁点制作思路,没有后续的成品,谢谢。