From 88c820100988111233801e858eba3c1926651109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E9=B9=8F?= Date: Wed, 25 Mar 2026 16:09:10 +0800 Subject: [PATCH] =?UTF-8?q?=E9=94=AE=E7=9B=98=E6=8C=89=E9=94=AE=E4=BB=8E?= =?UTF-8?q?=20`pyautogui`=20=E8=BF=81=E7=A7=BB=E5=88=B0=20`pydirectinput`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __pycache__/combat_engine.cpython-311.pyc | Bin 7552 -> 7562 bytes __pycache__/coordinate_patrol.cpython-311.pyc | Bin 23629 -> 23662 bytes __pycache__/death_manager.cpython-311.pyc | Bin 2320 -> 2696 bytes __pycache__/player_movement.cpython-311.pyc | Bin 14431 -> 14420 bytes __pycache__/stuck_handler.cpython-311.pyc | Bin 3482 -> 3460 bytes auto_bot_move.py | 2 +- combat_engine.py | 13 +-- coordinate_patrol.py | 82 +++++++++--------- death_manager.py | 6 +- docs/history.md | 5 ++ player_movement.py | 44 +++++----- stuck_handler.py | 18 ++-- 12 files changed, 88 insertions(+), 82 deletions(-) diff --git a/__pycache__/combat_engine.cpython-311.pyc b/__pycache__/combat_engine.cpython-311.pyc index 0eba7470e5b9fefa5dc23f21477d9a5a9a2a476c..71b3c276e1e41939d281e8f6c46c5aee9f1468be 100644 GIT binary patch delta 693 zcmZp$?y}}x&dbZi00axB9nL&IkynzjVWPT9G*b>s6iW(Y3Udx?E?X2EBLk4llEa?M z5yb&!v*vK-az$~0*=#x7xja!kU^aUWZ!TXHA0q=3gF8bCM+-v==Q1V+hSfj}0a5%L z=j~x;`Xw;An#GV&YV%r_W=2Mp&4z3@82NAU7F4EW7NsVaWabr=mQ3d3nBkxa)KJO(^d4@A2>Ozl$!r`3c7ZCOwcT zMOq+22t;Uu2z3yl0U~rjgf58C0}=X@KXKc!Spu2GoRiIX^jI`+h)GRe$Rn(*dsjs9 ziir9O=8GaaS44C!i0C{;7nnSUN5V(<0|T=lI`Nf{K~V0Bfbs(8ivk)~1T-!PXxtSL znZh;0cq0E4{t5hd1!PfWfeL_V^FN+b%<^C-g5v2GZ(?alempRc^2<_-DvKaKoBUkB zg3(~If}jJV-{wR?V1&6&-XreHV-3>5Qe0S6Qe-%pSHeWV3FJX%5a9(PTqg%gtYR$R z{72#kqjwNUgc(Fgg9rg2af>A>KR>6)5X3hD5sn~1*0h}b#FC;oAbTZ4kqk&&3Pf!|T((k1OZn4M5r{pKc$3t8x3X!KuoyqUmP~M`6;D2sdh!_KnX@5 VF1|f^rHl!e$OQ%{ntWSkDFA#`u;Ks! delta 655 zcmeCOZm{NE&dbZi00f<_%QF{E1-7fTAW%`9P_ky`pfy7 zV_Z^;vrF;|V#@Q&V*;1jt5M7ATx?IL4**9 z&;k)43yRc1gf@r(xv@wWMCeU^&TYqL0b~}lPS)npt5>}tCUr$j>$;fvB{B1hVwP9L zEI%+X3u@jKk)7hd!1n z$^`+Hy82qu3Z-CT|sY<*@?kU@0yvDk(CU%qC$X;0W@*6NvBx z5iXOxBvvt&ZT>3pgOM?C@;fO>aRZRJF^F&g61P~>a`F>Piee|rNlP;FO}3DJFUEh1 zJw84qKRG@gV!J3vG0$Wt87Jjr5K{(3NCAmo95%W6DWy57c13AGE+Y^ZpP#&3#$@ty GnZ*E!1GKCF diff --git a/__pycache__/coordinate_patrol.cpython-311.pyc b/__pycache__/coordinate_patrol.cpython-311.pyc index 231199908bdc578af56aed23f5f1ac0cf7cdb55e..1eb05b5cd9f0c307824b6000ad751e50d9b748db 100644 GIT binary patch delta 1269 zcmb_bUr19?7~kF0?#6ewGny^`<#n5LyW8ES(~3|;R-{hLrw}wERB{Mn=0Kk+dRb8* z90~~^Cd}$7iVNj?y$0vRNBJ`7q4gB8Afw0bu2XVZ1bH}o58wHHzwlccgfAO{ z^jMN?CVicM@GABCx%30<(%jNyYJO^BAvHUfUZ4(XNvNi`r5Da4C^oH3uFi5%MNws< zN=NLq&RP^3R!*;;<6;QK5EDaM+KvrER2x)23l5`Zf{Pv$Jxuh_GvS`jCM4eMSx&-8 z!i0Z*wQ%qu_!#)|l?gpZwQ%S4`V!8{K|xR$D8(An(Z=`guD9;4XVcu>i|*b` z|5lAj(s3=>0EZAlh(V|T}i|U zeKJ6VOWIU_P#Dl#+OqD=&Tvl$dOA475#kKu`BE$Hr&*KA-4S$0I7AVm45HgTS+``9 zT#lg}5Dg#w}IP9~H4C(*;hUtS-E}E*WZAQrSZ92$u;?81bu`K-Zu1H;Hq4XGUwvQ7( z5eYD=}(+g^<%Ym4y_0r!1?Hqe# db1_qS(~o0nw+>8-!anhn9JL8&dtqx@9yp!WVa5Wf*WyAM`POrAP8`UWK`*Npi3J5cMR#*rng-S3@PqT6@BF^s_vd_C5#Fo_ zwkI~5)nu%O%b7;*@FpxRO6yze{ z^l)Zo2h$*f4h|g@I*O9IJo#v9c`7x)!B4^e)oQYpY4;AU-wuChFn=_Y)2d6q)6*t+ z{_Iayd(Z8bbJt(zyhc{FBX_qaU8*-7biWgO-ikd>Cz#mJ#s1WwQNRB!L;YT5K$}g` z&m2>*FsjcD1_iMkZ|Q12#5@u1i7@Eo&`F__*kditMlYA5`F+f-aks`G%ppu6OxA}8 zF1L9gcZxxXLx@6%WQNE8h3iC2a=1A5<^2px97_~SBpI9cv@Rs_QRY;*Q=v|!;M{&D z?IUAty`-4V1;{+%yA? zgGNCsln3xZGJA=MAufh~v2T3`0OjWXPOP?XJh|7`%hTA!Ar?MYq;4$SXE?@?unT^!*uU&? Jd(&&^`~jDsh*AIm diff --git a/__pycache__/death_manager.cpython-311.pyc b/__pycache__/death_manager.cpython-311.pyc index 0afb86dd7bdcfa6f54d302475cc6b6c008ab5328..5d37ec3934d4936f33d71a35180a3e0897018f16 100644 GIT binary patch delta 1278 zcmZuwUuYaf7@yhQ+ne0&Hlg{~OWO2OHYOKhOnnf<(khk|6!TI`p)bdg-L_ZK^O)VJ zk>FiQW3O!rImUVsDWo8kY9oQz68gtH_~3&t7r2LG!BS{$HTd93?Mv~&@7pA6tbRND zoBhrA&CIvIZ)SF9p>@Ysx*j25na_SmzaB2`;B=rk)f+iaPJ~VMC`p8iw+wo)xH{BM zCrMJqP&AW(PZ}9cC?Z1l54bH!JDcWdC&{fbD`i_r+sZfz$yID?>?9Av3|j(-$DcKc zEPdtWULbi=?dU2fGmX_f{RO4MI;utj`|hp2%(|gHflx9^i}yQom;y52ObHkb82WGv z-%JV8lgEz%sV&TT-`(>{cf93kZ~j7UrS$v#nf0la+VqvR<;&i!>)yh>wY%57(!95H zwf4ys7+Jq?$@}#CU#4!aFPwYn*KZeV53bf0m)9QTMD5xvtnoO1Zg?ai^9ER+s|XC> zCgrH4jVZ->6wqzrmTfu74^CT|WNIw^zV*?b!&b^U{Z1;AI%V;}=iNHa-wTi@kG8bf zln7}QwAs*v>*6OG%RPk95}71tpx-bqne-^}rP&%Hlhl-B+nMqYJNa)Bkp&_;Shh_w zL$FIA_7}udugN#WobmzAH$ea;CKX>wZ?TbgUHYL*C&W|f?cM}?PBJ%? z<>`#$>e&f1&8<-cVHW}_#5)kMv`}lzhj#+Fk+hvmaSqiqZM+ZhI8K;$ zi|E1B^5m&X8rtx`kE}AI!i@4(fTmS$7 delta 921 zcmZva&ui2`6vtIQWPO?`W|^8M9%>ViY>y4hT6K0BXc58$asN4cl~b~?I?s8qkx)n7zg zxML?_i8)^J$1azENf9(?3SY?l+5N<^0v4eS+XJ7lq<_|wv^PKq$j!_;oE|+)xycYBmQBiV}-)jSp#d`Rfc5@WK zo(lAqZG7Z2YtUnwxG=9FJFX{FqTZxUpi3am@fHxFAmUFZ;#>I;1*cay3SqBFveI7z zAtI{MQUf6x*rwih)(<73SjaDqhi236oxc_HgC|9XFyU`JGMlyYGtN2E$Bspk=Nl?u Vf7DbHCTrjzeu}+c|8e+X{{Vgoy$t{W diff --git a/__pycache__/player_movement.cpython-311.pyc b/__pycache__/player_movement.cpython-311.pyc index f16c297c4819643ebfd3512ce1d65fd71a8b6088..2843aaffe2fc5252d35e1a06992ee293e1d23881 100644 GIT binary patch delta 789 zcmZ{g&ubGw6vubARbJw3x3Mqj#w4Vh?k0x(Xg49HSP;a6QV|a-E#gID!CXp^dgvi| z&_Klq#Sx9ScrrJYy?OCa|A3h?(9>dXf){fsNKdY@f=$xlG0frnKHu+rztv@3d#`Ch z#Xs!zPVMO@t*NSqXI)pl7M#%PoA+xE*Qy(}_4=a?2HI!x)7{aYllgO!nw?oe(~_n+ zO?z~*wY=RB)Rxrd)b?mp(qv~$P)kyaQ_F4WQI*_dZ$ia|u!KarDj+2x#UbSZZ3VX% z1;i!9ImA8i1+n&J0W%V2_`h)vLtEp-WKSZwi;?4k{>Ct}{7i4@Tlx@>{fOir_d`4o zkdct#kU1g5RROjHn}dCDS_$~J$6fkCuL!UtSRAaskdtip{ePa>17jBq9(7+%Pas)x z-{m?aHo)DFB{b%g|McDHaV?3ew2mwugp8vR;m65WovQGuiif7eOm|FRUSghOo_(Ktn;p4+aDyn}l*DXzR$x(Lkz06vL*aT?`5_q@Qe#4^XShsLYr_NKtB@5eEF6cYqG5*?0?|6uE- oM0(ig*Nf{zUAZ4hq48<6bGxSmPMHIjySI=dLFCHOFuHj2H&}WIGynhq delta 770 zcmZ{g&ubGw6vuaB)yXpJHf6fql5E(`x{#l{ZA2;JEef?Ba?nzcJy=4~>LJmzlpb;n zRNO#e0#ZI37_vCBD6&E{?=}4fLpnz~MS9SZ7gOK? z>1FJz84eeHlfgWPc?$C(M4ouK!eEZW9EG_MW<1$H_pgqgq6ofb4rV}(JXR_<=1%=b z{Yk(p&t*?OkmEG+QK`011Vsi-4owQp}>Jx~Y5PS|7*;S4&4GQ*Ex=RJF-1 zXCtwjj2-2YHf)X>3>P^rQd}gToL9vp@}vob`8>zgsKs!Z<1)o%;xF~%`s(uia-!Zk zBab*_yxgmwP#-buaO_a*gjgM}`TGo)I4)6K3h{KsRfcOE*C?(<3E8Y`m(NW3HMkwO gBzRlT%q(c5^IOb{Z^q2ED$GD^xXP4v` z#FXck$82U{n##__Sx}i+T9Th$nmKtnXSWXzBLhPN!&4643w&M|IlQlMcwgY~{=mi{ zr1pV>> 未上马,先按 {self.mount_key} {self.mount_hold_sec:.1f}s 上马" ) - pyautogui.keyDown(self.mount_key) + pydirectinput.keyDown(self.mount_key) time.sleep(self.mount_hold_sec) - pyautogui.keyUp(self.mount_key) + pydirectinput.keyUp(self.mount_key) self._next_mount_allowed = time.time() + self.mount_retry_after_sec return False @@ -235,16 +235,16 @@ class CoordinatePatrol: # --- [平滑移动核心:边走边转控制层] --- # A. 始终保持前进动力 - pyautogui.keyDown("w") + pydirectinput.keyDown("w") # B. 转向决策逻辑 if abs_diff <= self.angle_deadzone_deg: if random.random() < self.random_jump_prob: # 频率建议设低,约每 200 帧跳一次 self.logger.info(">>> 随机跳跃:模拟真人并辅助脱困") - pyautogui.press("space") + pydirectinput.press("space") # 在死区内:绝对直线行驶,松开所有转向键 - pyautogui.keyUp("a") - pyautogui.keyUp("d") + pydirectinput.keyUp("a") + pydirectinput.keyUp("d") elif abs_diff > self.angle_threshold_deg: # 超出阈值:执行平滑脉冲转向 @@ -252,24 +252,24 @@ class CoordinatePatrol: other_key = "a" if angle_diff > 0 else "d" # 确保不会同时按下左右键 - pyautogui.keyUp(other_key) + pydirectinput.keyUp(other_key) # 获取针对平滑移动优化的短脉冲时长 duration = self._turn_duration_from_angle(abs_diff) # 关键:执行短促转向脉冲 - pyautogui.keyDown(turn_key) + pydirectinput.keyDown(turn_key) # 这里的 sleep 极短 (建议 < 0.1s),不会造成移动卡顿 time.sleep(duration) - pyautogui.keyUp(turn_key) + pydirectinput.keyUp(turn_key) self.last_turn_end_time = time.time() # self.logger.debug(f"修正航向: {turn_key} | 角度差: {angle_diff:.1f}°") else: # 在死区与阈值之间:为了平滑,不进行任何按键动作,仅靠 W 前进 - pyautogui.keyUp("a") - pyautogui.keyUp("d") + pydirectinput.keyUp("a") + pydirectinput.keyUp("d") return @@ -306,8 +306,8 @@ class CoordinatePatrol: if dist < threshold: # 到点后保持平滑过渡:不松开 W,只松开左右修正键 # 避免 patrol 连续多点时出现“刹一下再走”的抖动。 - pyautogui.keyUp("a") - pyautogui.keyUp("d") + pydirectinput.keyUp("a") + pydirectinput.keyUp("d") self.reset_stuck() return True @@ -319,37 +319,37 @@ class CoordinatePatrol: # --- 平滑移动核心逻辑 --- # 只要没到终点,始终保持前进 W 键按下 - pyautogui.keyDown("w") + pydirectinput.keyDown("w") # 4. 根据偏角决定转向动作 if abs_diff <= self.angle_deadzone_deg: if random.random() < self.random_jump_prob: # 频率建议设低,约每 200 帧跳一次 self.logger.info(">>> 随机跳跃:模拟真人并辅助脱困") - pyautogui.press("space") + pydirectinput.press("space") # 在死区内,确保转向键松开,直线前进 - pyautogui.keyUp("a") - pyautogui.keyUp("d") + pydirectinput.keyUp("a") + pydirectinput.keyUp("d") elif abs_diff > self.angle_threshold_deg: # 超出阈值:执行平滑脉冲转向 turn_key = "d" if angle_diff > 0 else "a" other_key = "a" if angle_diff > 0 else "d" # 松开反方向键 - pyautogui.keyUp(other_key) + pydirectinput.keyUp(other_key) # 获取针对平滑移动优化的短脉冲时长 duration = self._turn_duration_from_angle(abs_diff) # 关键:执行短促转向脉冲 - pyautogui.keyDown(turn_key) + pydirectinput.keyDown(turn_key) # sleep 极短 (建议 < 0.1s),不会造成移动卡顿 time.sleep(duration) - pyautogui.keyUp(turn_key) + pydirectinput.keyUp(turn_key) self.last_turn_end_time = time.time() else: # 在死区与阈值之间:为了平滑,不进行任何按键动作,仅靠 W 前进 - pyautogui.keyUp("a") - pyautogui.keyUp("d") + pydirectinput.keyUp("a") + pydirectinput.keyUp("d") return False @@ -405,9 +405,9 @@ class CoordinatePatrol: ok = self._ensure_mounted(state) if not ok: return False - pyautogui.keyDown(str(takeoff_key)) + pydirectinput.keyDown(str(takeoff_key)) time.sleep(float(takeoff_hold_sec)) - pyautogui.keyUp(str(takeoff_key)) + pydirectinput.keyUp(str(takeoff_key)) return False # 计算距离 @@ -419,23 +419,23 @@ class CoordinatePatrol: if f_signal < 10: # 退化:直接按 mount_key 一次并等待 self.logger.info(">>> 飞行阶段检测:未上马,先按 %s", self.mount_key) - pyautogui.press(self.mount_key) + pydirectinput.press(self.mount_key) time.sleep(float(mount_wait_sec)) return False # --- 2. 起飞阶段(兼容你的阈值写法:100~200) --- if 100 < f_signal < 200: self.logger.info(">>> 起飞:按住 %s %.1f 秒", takeoff_key, float(takeoff_hold_sec)) - pyautogui.keyDown(str(takeoff_key)) + pydirectinput.keyDown(str(takeoff_key)) time.sleep(float(takeoff_hold_sec)) - pyautogui.keyUp(str(takeoff_key)) + pydirectinput.keyUp(str(takeoff_key)) return False # --- 3. 巡航与降落判定(兼容你的阈值写法:>240) --- if f_signal > 240: if dist > float(arrival_threshold): # A. 始终按住 W 前进 - pyautogui.keyDown("w") + pydirectinput.keyDown("w") # B. 修正方向(使用与本模块一致的朝向计算) target_heading = self.get_target_heading_deg((float(curr_x), float(curr_y)), (float(target_x), float(target_y))) @@ -444,8 +444,8 @@ class CoordinatePatrol: # 在死区内:确保转向键松开,减少左右抖动 if abs_diff <= float(turn_deadzone_deg): - pyautogui.keyUp("a") - pyautogui.keyUp("d") + pydirectinput.keyUp("a") + pydirectinput.keyUp("d") return False # 防止“上一个修正刚结束,下一帧立刻反向再修正”造成摆头 @@ -456,8 +456,8 @@ class CoordinatePatrol: key = "d" if angle_diff > 0 else "a" other_key = "a" if key == "d" else "d" - pyautogui.keyUp(other_key) - pyautogui.keyDown(key) + pydirectinput.keyUp(other_key) + pydirectinput.keyDown(key) # 修正脉冲时长随角度误差变化,避免固定 0.05s 过冲/欠冲 duration = self._turn_duration_from_angle(abs_diff) @@ -467,17 +467,17 @@ class CoordinatePatrol: safe_duration = min(safe_duration, 0.04) time.sleep(safe_duration) - pyautogui.keyUp(key) + pydirectinput.keyUp(key) setattr(self, "_last_simple_flight_turn_time", now) return False # C. 到达目标,执行降落 - pyautogui.keyUp("w") + pydirectinput.keyUp("w") self.logger.info(">>> 到达目标,执行降落:按住 %s %.1f 秒", land_key, float(land_hold_sec)) - pyautogui.keyDown(str(land_key)) + pydirectinput.keyDown(str(land_key)) time.sleep(float(land_hold_sec)) - pyautogui.keyUp(str(land_key)) - pyautogui.press(dismount_key) + pydirectinput.keyUp(str(land_key)) + pydirectinput.press(dismount_key) self.logger.info(">>> 降落完成:已按 %s", dismount_key) return True @@ -532,10 +532,10 @@ class CoordinatePatrol: """只按下指定键,抬起其他移动键。""" for k in ("w", "a", "d"): if k == key: - pyautogui.keyDown(k) + pydirectinput.keyDown(k) else: - pyautogui.keyUp(k) + pydirectinput.keyUp(k) def stop_all(self): for key in ["w", "a", "s", "d"]: - pyautogui.keyUp(key) + pydirectinput.keyUp(key) diff --git a/death_manager.py b/death_manager.py index f8abd1b..cff5dac 100644 --- a/death_manager.py +++ b/death_manager.py @@ -1,6 +1,6 @@ import time import math -import pyautogui +import pydirectinput class DeathManager: @@ -24,7 +24,7 @@ class DeathManager: self.corpse_pos = (state['x'], state['y']) self.is_running_to_corpse = True print(f">>> [系统] 记录死亡坐标: {self.corpse_pos},准备释放灵魂...") - pyautogui.press('9') # 绑定宏: /run RepopMe() + pydirectinput.press('9') # 绑定宏: /run RepopMe() time.sleep(5) # 等待加载界面 def run_to_corpse(self, state): @@ -36,7 +36,7 @@ class DeathManager: # 如果距离尸体很近(0.005 约等于 10-20 码) if is_arrived: print(">>> 已到达尸体附近,尝试复活...") - pyautogui.press('0') # 绑定宏: /run RetrieveCorpse() + pydirectinput.press('0') # 绑定宏: /run RetrieveCorpse() time.sleep(5) self.is_running_to_corpse = False self.corpse_pos = None diff --git a/docs/history.md b/docs/history.md index 180611c..cb19c6e 100644 --- a/docs/history.md +++ b/docs/history.md @@ -59,3 +59,8 @@ - **轮询间隔**:`coordinate_patrol.py` 的 `navigate_path` 阻塞等待轮询由固定 `time.sleep(0.05)` 调整为局部 `poll_sleep_sec = 0.02`,让阻塞模式控制频率更接近 `navigate`,减少航点/转向后的抖动。 +### 键盘按键从 `pyautogui` 迁移到 `pydirectinput` + +- 将项目中所有键盘按键调用(`pyautogui.keyDown/keyUp/press`)替换为对应的 `pydirectinput.keyDown/keyUp/press` +- 涉及文件:`coordinate_patrol.py`、`stuck_handler.py`、`death_manager.py`、`player_movement.py`、`combat_engine.py` + diff --git a/player_movement.py b/player_movement.py index 6276d23..4990e2c 100644 --- a/player_movement.py +++ b/player_movement.py @@ -1,7 +1,7 @@ import math import time import logging -import pyautogui +import pydirectinput from player_position import PlayerPosition # 游戏朝向约定:正北=0°,正西=90°,正南=180°,正东=270°(逆时针递增) @@ -81,9 +81,9 @@ class PlayerMovement: f"[转向 {attempt + 1}] 当前 {current_heading:.1f}° → 目标 {target_heading:.1f}°," f"按 '{key}' {turn_duration:.2f}s(需转 {angle_to_turn:.1f}°)" ) - pyautogui.keyDown(key) + pydirectinput.keyDown(key) time.sleep(turn_duration) - pyautogui.keyUp(key) + pydirectinput.keyUp(key) time.sleep(0.15) # 等待游戏刷新朝向 self.logger.warning(f"转向失败:超过最大尝试次数 {max_attempts}") @@ -96,9 +96,9 @@ class PlayerMovement: duration: 移动时间(秒) """ self.logger.info(f"向前移动 {duration:.2f}s") - pyautogui.keyDown('w') + pydirectinput.keyDown('w') time.sleep(duration) - pyautogui.keyUp('w') + pydirectinput.keyUp('w') def _escape_stuck(self): """卡死脱困组合拳:后退 + 随机转向 + 跳跃。 @@ -109,23 +109,23 @@ class PlayerMovement: self.logger.warning("检测到角色卡死,执行脱困动作") # 1. 松开前进键,后退 0.8s - pyautogui.keyUp('w') - pyautogui.keyDown('s') + pydirectinput.keyUp('w') + pydirectinput.keyDown('s') time.sleep(0.8) - pyautogui.keyUp('s') + pydirectinput.keyUp('s') # 2. 随机左转或右转 0.3~0.5s turn_key = random.choice(['a', 'd']) turn_dur = random.uniform(0.3, 0.5) - pyautogui.keyDown(turn_key) + pydirectinput.keyDown(turn_key) time.sleep(turn_dur) - pyautogui.keyUp(turn_key) + pydirectinput.keyUp(turn_key) # 3. 跳跃(按空格)同时向前冲 0.5s - pyautogui.keyDown('w') - pyautogui.press('space') + pydirectinput.keyDown('w') + pydirectinput.press('space') time.sleep(0.5) - pyautogui.keyUp('w') + pydirectinput.keyUp('w') self.logger.info("脱困动作完成,重新开始前进") @@ -172,7 +172,7 @@ class PlayerMovement: return False # 按住 w 开始持续前进 - pyautogui.keyDown('w') + pydirectinput.keyDown('w') # 卡死检测:记录最近 STUCK_CHECK_COUNT 次坐标,用于判断是否停滞 recent_positions = [] try: @@ -204,7 +204,7 @@ class PlayerMovement: if moved < self.STUCK_MOVE_THRESHOLD: self._escape_stuck() recent_positions.clear() - pyautogui.keyDown('w') + pydirectinput.keyDown('w') # x 和 y 都在容差范围内即视为到达 if abs(dx) <= position_tolerance and abs(dy) <= position_tolerance: @@ -213,7 +213,7 @@ class PlayerMovement: # 接近目标时松开 w 停下,再做一次静止判定,避免冲过头后死循环 if distance <= position_tolerance * 2: - pyautogui.keyUp('w') + pydirectinput.keyUp('w') time.sleep(0.2) final_pos = self.player_position.get_position_with_retry() if final_pos is not None: @@ -223,7 +223,7 @@ class PlayerMovement: self.logger.info(f"已到达目标位置 ({target_x}, {target_y})") return True # 静止后仍未到达,重新按住 w 继续前进 - pyautogui.keyDown('w') + pydirectinput.keyDown('w') # 每隔 HEADING_CHECK_INTERVAL 次读一次朝向,减少 OCR 频率 if iteration % self.HEADING_CHECK_INTERVAL != 0: @@ -239,12 +239,12 @@ class PlayerMovement: if abs_diff > self.COARSE_TURN_THRESHOLD: # 偏差过大:停步原地粗修正,再重新按住 w - pyautogui.keyUp('w') + pydirectinput.keyUp('w') self.logger.info(f"偏差 {abs_diff:.1f}° 过大,停步原地修正") if not self.turn_to_heading(required_heading): self.logger.warning("修正转向失败,移动中止") return False - pyautogui.keyDown('w') + pydirectinput.keyDown('w') elif abs_diff > self.ANGLE_TOLERANCE: # 小幅偏差:边走边转,同时按住 w 和转向键 @@ -254,15 +254,15 @@ class PlayerMovement: self.logger.info( f"边走边转:按 '{turn_key}' {turn_time:.2f}s(偏差 {abs_diff:.1f}°)" ) - pyautogui.keyDown(turn_key) + pydirectinput.keyDown(turn_key) time.sleep(turn_time) - pyautogui.keyUp(turn_key) + pydirectinput.keyUp(turn_key) else: # 方向已对齐,让角色多走一段再重新检测,减少 OCR 频率 time.sleep(1) finally: - pyautogui.keyUp('w') + pydirectinput.keyUp('w') self.logger.warning(f"移动失败:超过最大迭代次数 {max_iterations}") return False diff --git a/stuck_handler.py b/stuck_handler.py index 6ca69e3..f231e77 100644 --- a/stuck_handler.py +++ b/stuck_handler.py @@ -1,7 +1,7 @@ import math import time import random -import pyautogui +import pydirectinput class StuckHandler: # 针对 0.xxxx 坐标系优化 @@ -61,27 +61,27 @@ class StuckHandler: # 1. 全停 for k in ("w", "a", "d", "s"): - pyautogui.keyUp(k) + pydirectinput.keyUp(k) # 2. 倒车并转向(组合动作更有效) - pyautogui.keyDown("s") + pydirectinput.keyDown("s") turn_key = random.choice(["a", "d"]) - pyautogui.keyDown(turn_key) + pydirectinput.keyDown(turn_key) # 倒车时间稍长一点,离开障碍物 time.sleep(0.5) # 3. 尝试跳跃脱离地形卡位 - pyautogui.press("space") + pydirectinput.press("space") time.sleep(0.5) - pyautogui.keyUp(turn_key) - pyautogui.keyUp("s") + pydirectinput.keyUp(turn_key) + pydirectinput.keyUp("s") # 4. 往前稍微走一步,重新锁定坐标 - pyautogui.keyDown("w") + pydirectinput.keyDown("w") time.sleep(0.5) - pyautogui.keyUp("w") + pydirectinput.keyUp("w") self.reset() print(">>> 脱困尝试结束")