如何用AutoHotkey查找屏幕最近血条并定位至其最左侧?
AutoHotkey 实现屏幕血条识别与最近血条定位
核心思路
要解决这个问题,不能仅依赖单次PixelSearch,需要完成三个关键步骤:
- 识别屏幕上所有符合颜色特征的连续血条区域(而非单个像素)
- 计算每个血条与屏幕中心的距离,筛选出最近的血条
- 定位到目标血条的最左侧像素
解决方案代码示例
方法1:逐行扫描合并血条区域(逻辑清晰,适合新手)
通过逐行扫描屏幕像素,将连续的同色像素段合并为血条区域,再筛选最近目标:
; 配置参数(根据实际需求调整) TargetColor := 0xFF0000 ; 血条颜色(RGB格式,可通过Window Spy取色) Variation := 5 ; 颜色误差容忍度 MinBarWidth := 20 ; 血条最小宽度(过滤小像素块) MinBarHeight := 5 ; 血条最小高度(过滤小像素块) ; 获取屏幕中心坐标 ScreenCenterX := A_ScreenWidth // 2 ScreenCenterY := A_ScreenHeight // 2 ; 存储所有识别到的血条 Bars := [] ; 逐行扫描屏幕 Loop, % A_ScreenHeight { Y := A_Index CurrentBarStart := 0 Loop, % A_ScreenWidth { X := A_Index PixelGetColor, Color, %X%, %Y%, RGB ; 检查当前像素颜色是否匹配 if (Abs(Color - TargetColor) <= Variation) { if (CurrentBarStart = 0) { CurrentBarStart := X ; 标记血条起始X坐标 } } else { if (CurrentBarStart != 0) { ; 计算当前行的血条段宽度 BarWidth := X - CurrentBarStart if (BarWidth >= MinBarWidth) { ; 尝试合并到已有的血条区域 FoundExisting := false for _, Bar in Bars { if (Y >= Bar.Top && Y <= Bar.Bottom && CurrentBarStart <= Bar.Right && X >= Bar.Left) { ; 更新血条的边界范围 Bar.Left := Min(Bar.Left, CurrentBarStart) Bar.Right := Max(Bar.Right, X - 1) Bar.Bottom := Y FoundExisting := true break } } ; 若未找到可合并的血条,新增一个 if (!FoundExisting) { Bars.Push({Left: CurrentBarStart, Right: X - 1, Top: Y, Bottom: Y}) } } CurrentBarStart := 0 } } } ; 处理行尾未闭合的血条段 if (CurrentBarStart != 0) { BarWidth := A_ScreenWidth - CurrentBarStart + 1 if (BarWidth >= MinBarWidth) { FoundExisting := false for _, Bar in Bars { if (Y >= Bar.Top && Y <= Bar.Bottom && CurrentBarStart <= Bar.Right && A_ScreenWidth >= Bar.Left) { Bar.Left := Min(Bar.Left, CurrentBarStart) Bar.Right := Max(Bar.Right, A_ScreenWidth) Bar.Bottom := Y FoundExisting := true break } } if (!FoundExisting) { Bars.Push({Left: CurrentBarStart, Right: A_ScreenWidth, Top: Y, Bottom: Y}) } } } } ; 过滤高度不足的血条,并计算中心坐标与距离 FilteredBars := [] for _, Bar in Bars { BarHeight := Bar.Bottom - Bar.Top + 1 if (BarHeight >= MinBarHeight) { Bar.CenterX := (Bar.Left + Bar.Right) // 2 Bar.CenterY := (Bar.Top + Bar.Bottom) // 2 ; 计算距离平方(避免开根号,提升效率) Bar.DistanceSq := (Bar.CenterX - ScreenCenterX)**2 + (Bar.CenterY - ScreenCenterY)**2 FilteredBars.Push(Bar) } } ; 定位最近的血条 if (FilteredBars.Length() > 0) { ClosestBar := FilteredBars[1] for _, Bar in FilteredBars { if (Bar.DistanceSq < ClosestBar.DistanceSq) { ClosestBar := Bar } } ; 移动鼠标到血条最左侧的垂直中心位置 MouseMove, %ClosestBar.Left%, %ClosestBar.CenterY%, 0 ToolTip, 已定位到最近血条最左侧 SetTimer, ToolTip, -1000 } else { ToolTip, 未识别到符合条件的血条 SetTimer, ToolTip, -1000 }
方法2:循环PixelSearch扩展区域(速度更快)
通过循环调用PixelSearch找到初始像素,再向四周扩展得到完整血条区域,避免逐行扫描的低效:
; 配置参数 TargetColor := 0xFF0000 Variation := 5 MinBarWidth := 20 MinBarHeight := 5 ScreenCenterX := A_ScreenWidth // 2 ScreenCenterY := A_ScreenHeight // 2 Bars := [] SearchX1 := 1, SearchY1 := 1 SearchX2 := A_ScreenWidth, SearchY2 := A_ScreenHeight Loop { ; 查找下一个匹配像素 PixelSearch, FoundX, FoundY, %SearchX1%, %SearchY1%, %SearchX2%, %SearchY2%, %TargetColor%, %Variation%, Fast RGB if ErrorLevel { break ; 无更多匹配像素,退出循环 } ; 扩展得到血条的完整边界 ; 向左扩展到边缘 LeftX := FoundX while (LeftX > 1) { PixelGetColor, C, %LeftX-1%, %FoundY%, RGB if (Abs(C - TargetColor) > Variation) { break } LeftX-- } ; 向右扩展到边缘 RightX := FoundX while (RightX < A_ScreenWidth) { PixelGetColor, C, %RightX+1%, %FoundY%, RGB if (Abs(C - TargetColor) > Variation) { break } RightX++ } ; 向上扩展到边缘 TopY := FoundY while (TopY > 1) { RowHasMatch := false Loop, % RightX - LeftX + 1 { PixelGetColor, C, %LeftX + A_Index -1%, %TopY-1%, RGB if (Abs(C - TargetColor) <= Variation) { RowHasMatch := true break } } if (!RowHasMatch) break TopY-- } ; 向下扩展到边缘 BottomY := FoundY while (BottomY < A_ScreenHeight) { RowHasMatch := false Loop, % RightX - LeftX + 1 { PixelGetColor, C, %LeftX + A_Index -1%, %BottomY+1%, RGB if (Abs(C - TargetColor) <= Variation) { RowHasMatch := true break } } if (!RowHasMatch) break BottomY++ } ; 检查是否已记录该血条(避免重复) AlreadyExists := false for _, Bar in Bars { if (LeftX >= Bar.Left && RightX <= Bar.Right && TopY >= Bar.Top && BottomY <= Bar.Bottom) { AlreadyExists := true break } } if (!AlreadyExists) { Bars.Push({Left: LeftX, Right: RightX, Top: TopY, Bottom: BottomY}) } ; 调整下一次搜索的区域(跳过已扫描的血条) if (RightX + 1 > SearchX2) { SearchX1 := 1 SearchY1 := BottomY + 1 } else { SearchX1 := RightX + 1 } if (SearchY1 > SearchY2) break } ; 后续筛选最近血条的逻辑与方法1完全一致 FilteredBars := [] for _, Bar in Bars { BarHeight := Bar.Bottom - Bar.Top + 1 if (BarHeight >= MinBarHeight) { Bar.CenterX := (Bar.Left + Bar.Right) // 2 Bar.CenterY := (Bar.Top + Bar.Bottom) // 2 Bar.DistanceSq := (Bar.CenterX - ScreenCenterX)**2 + (Bar.CenterY - ScreenCenterY)**2 FilteredBars.Push(Bar) } } if (FilteredBars.Length() > 0) { ClosestBar := FilteredBars[1] for _, Bar in FilteredBars { if (Bar.DistanceSq < ClosestBar.DistanceSq) { ClosestBar := Bar } } MouseMove, %ClosestBar.Left%, %ClosestBar.CenterY%, 0 ToolTip, 已定位到最近血条最左侧 SetTimer, ToolTip, -1000 } else { ToolTip, 未识别到符合条件的血条 SetTimer, ToolTip, -1000 }
关键细节说明
- 颜色匹配:使用
Abs(Color - TargetColor) <= Variation处理颜色偏差,避免因屏幕色差或抗锯齿导致的识别失败。 - 距离计算:采用距离平方而非实际距离,省去开根号运算,提升筛选效率。
- 区域合并:通过检查上下行的连续像素,将同一血条的分散像素段合并为完整区域,避免重复记录。
- 参数调整:根据实际血条的大小、颜色,调整
MinBarWidth、MinBarHeight和Variation参数,优化识别精度。
内容的提问来源于stack exchange,提问作者divinelemon
相关产品推荐
相关产品推荐

