如何用Python实现游戏瓦片全遍历并优化pydirectinput控制代码
问题背景
我需要实现游戏内的全瓦片扫描功能,此处“扫描”指控制角色移动到对应瓦片上并保存其坐标。如果角色碰撞到障碍物,需要尝试向下移动一个瓦片后继续前进,目前已经可以正常获取角色的当前朝向。
我使用pydirectinput库实现按键控制,因为pyautogui在我的目标游戏中无法正常生效。
现有代码
def coordinates(): visited_coordinates = [] t_end = time.time() + 90 movements = [] while time.time() < t_end: for message in generated_object(): # generated object returns a list with 4 elements if [message[0], message[1]] in visited_coordinates: if "left" in movements: if "down" in movements: if "right" in movements: pydirectinput.press("up") if message[3] != 4: # 4 means it is facing up pydirectinput.press("up") # if it isn't facing up, it needs to be pressed twice movements.append("up") else: pydirectinput.press("right") if message[3] != 12: # 12 means it is facing right pydirectinput.press("right") # same with facing right, and so on movements.append("right") else: pydirectinput.press("down") if message[3] != 0: # 0 means it is facing down pydirectinput.press("down") movements.append("down") else: pydirectinput.press("left") if message[3] != 8: # 8 means it is facing left pydirectinput.press("left") movements.append("left") continue movements = [] if message[2] != 4: # this means it went beyond the border, and it must return pydirectinput.press("right", presses=2) visited_coordinates.append([message[0], message[1]]) return visited_coordinates
存在的问题
- 用命名管道通信,需要读取每一条收到的消息,导致代码执行动作耗时极长
- 嵌套判断过多属于典型的面条代码,可读性极差
- 角色一旦超出边界后就无法返回,需要限制角色不再向左移动,但因为会重置
movements列表,不知道该如何实现该逻辑
优化方案
1. 运行效率优化
- 把已访问坐标从列表改为集合存储,
in查询的时间复杂度从O(n)降到O(1),大幅提升重复坐标判断速度。注意集合不能存列表,所以可以把坐标转成元组存储:visited_coordinates = set(),添加时用visited_coordinates.add((message[0], message[1])),判断时直接查(message[0], message[1]) in visited_coordinates即可。 - 管道读取部分可以加缓冲区批量读取消息,不要逐次阻塞读取单条消息,减少IO等待耗时。如果
generated_object()是逐次返回消息的生成器,可以调整为每次批量拉取N条消息后统一处理,减少上下文切换开销。
2. 可读性优化:消除嵌套判断
把方向相关的配置抽成映射字典,避免多层if嵌套:
# 方向配置:方向名称 -> (朝向编码, 按键名) DIR_CONFIG = { "left": (8, "left"), "down": (0, "down"), "right": (12, "right"), "up": (4, "up") } # 尝试移动的优先级顺序 MOVE_PRIORITY = ["left", "down", "right", "up"]
原来的多层嵌套判断可以直接简化为:
current_coord = (message[0], message[1]) if current_coord in visited_coordinates: # 按优先级找第一个没试过的方向 for dir_name in MOVE_PRIORITY: if dir_name not in movements: target_facing, key = DIR_CONFIG[dir_name] press_count = 1 if message[3] == target_facing else 2 pydirectinput.press(key, presses=press_count) movements.append(dir_name) break continue
3. 边界限制逻辑实现
不需要依赖movements列表存左移限制状态,单独加一个全局的边界标记即可:
# 初始化时加一个禁止左移的标记 ban_left_move = False
在触发越界的逻辑里设置标记:
if message[2] != 4: # 触发越界 pydirectinput.press("right", presses=2) ban_left_move = True # 设置禁止左移标记
然后在判断移动方向的时候,优先排除左移选项:
for dir_name in MOVE_PRIORITY: # 禁止左移时直接跳过left方向 if ban_left_move and dir_name == "left": continue if dir_name not in movements: # 后续移动逻辑不变
优化后完整代码示例
import time import pydirectinput # 方向配置:方向名称 -> (朝向编码, 按键名) DIR_CONFIG = { "left": (8, "left"), "down": (0, "down"), "right": (12, "right"), "up": (4, "up") } # 尝试移动的优先级顺序 MOVE_PRIORITY = ["left", "down", "right", "up"] def coordinates(): visited_coordinates = set() t_end = time.time() + 90 movements = [] ban_left_move = False # 越界后禁止左移的标记 while time.time() < t_end: # 可根据需要调整generated_object为批量拉取消息 for message in generated_object(): current_coord = (message[0], message[1]) if current_coord in visited_coordinates: # 按优先级找合法的移动方向 for dir_name in MOVE_PRIORITY: if ban_left_move and dir_name == "left": continue if dir_name not in movements: target_facing, key = DIR_CONFIG[dir_name] press_count = 1 if message[3] == target_facing else 2 pydirectinput.press(key, presses=press_count) movements.append(dir_name) break continue movements = [] if message[2] != 4: # 触发越界 pydirectinput.press("right", presses=2) ban_left_move = True visited_coordinates.add(current_coord) # 如果需要返回列表格式可以转一下 return [list(coord) for coord in visited_coordinates]
内容的提问来源于stack exchange,提问作者comodin
相关产品推荐
相关产品推荐

