【AutoJs】从基础到实战:坐标自适应与多点手势模拟的进阶指南

📅 2026/7/15 17:20:57
【AutoJs】从基础到实战:坐标自适应与多点手势模拟的进阶指南
1. 坐标自适应多设备兼容性的基石在移动端自动化领域设备屏幕分辨率碎片化是最常见的挑战之一。我曾在实际项目中遇到过这样的场景一个在1080P设备上完美运行的脚本放到2K屏手机上却完全失效原因就是硬编码的坐标点超出了实际屏幕范围。这时候就需要setScreenMetrics(width, height)这个神器出场了。这个方法的本质是建立一套虚拟坐标系。比如你设置setScreenMetrics(1080, 2400)就相当于告诉脚本所有坐标操作都基于1080×2400的屏幕来换算。当实际设备分辨率不同时AutoJs会自动进行等比缩放。来看个具体案例// 基准分辨率设置 setScreenMetrics(1080, 2400); // 游戏开始按钮坐标(假设在1080x2400设备上位于这个位置) click(300, 2200);在1920×1080的设备上运行时实际点击的坐标会智能换算为X轴300 × (1080/1080) 300Y轴2200 × (1080/2400) ≈ 990常见踩坑点部分全面屏手机的异形切割区域如刘海屏、挖孔屏会导致边缘坐标失效横竖屏切换时需要重新设置基准分辨率动态分辨率设备如游戏内调整画质需要监听分辨率变化实测建议在脚本开头加入设备检测逻辑// 获取实际分辨率 let realWidth device.width; let realHeight device.height; // 设置基准分辨率建议使用常见比例如16:9 let baseWidth 1080; let baseHeight 2400; // 异常比例处理 if(Math.abs(realWidth/realHeight - baseWidth/baseHeight) 0.2){ toast(非常规屏幕比例请手动调整基准分辨率); baseHeight Math.round(baseWidth * realHeight / realWidth); } setScreenMetrics(baseWidth, baseHeight);2. 点击操作的三种进阶玩法2.1 精准点击控制基础的click(x,y)大家都会用但实际项目中我们经常需要更精细的控制。比如在游戏连招场景中我发现连续快速点击会导致操作丢失这时候就需要引入点击间隔控制function preciseClick(x, y, interval200){ click(x, y); sleep(interval); // 默认200ms间隔 } // 连招示例A-B-A-C preciseClick(500, 1800); // A技能 preciseClick(700, 1800); // B技能 preciseClick(500, 1800); // A技能 preciseClick(300, 1800); // C技能2.2 长按的妙用longClick()不仅仅是简单的长按操作。在ARPG游戏中我常用它来实现蓄力技能function chargeAttack(x, y, chargeTime){ let start Date.now(); longClick(x, y); let actualTime Date.now() - start; toast(蓄力时间${actualTime}ms); return actualTime; } // 蓄力超过800ms触发强化攻击 if(chargeAttack(600, 1500) 800){ click(600, 1300); // 释放强化技能 }2.3 压力感应模拟press(x, y, duration)是最容易被低估的方法。在赛车游戏中我通过它实现了油门力度控制// 根据弯道角度控制油门时长 function throttleControl(angle){ const baseX 900; const baseY 2000; const maxDuration 1000; let duration map(angle, 0, 90, 100, maxDuration); press(baseX, baseY, duration); } // 角度映射工具函数 function map(value, inMin, inMax, outMin, outMax){ return (value - inMin) * (outMax - outMin) / (inMax - inMin) outMin; }3. 滑动操作的实战技巧3.1 基础滑动的三种变体swipe()的参数看似简单但通过调整duration参数可以实现完全不同的效果持续时间(ms)效果描述适用场景50-200快速滑动页面翻页、快速切换300-800中等速度列表浏览、卡片滑动1000慢速拖动精确调整、进度控制// 游戏场景应用案例 function dodge(direction){ const centerX device.width/2; const centerY device.height/2; let endX, endY; switch(direction){ case left: endX centerX - 300; endY centerY; break; case right: endX centerX 300; endY centerY; break; // 其他方向... } // 闪避需要快速滑动 swipe(centerX, centerY, endX, endY, 150); }3.2 复杂路径模拟gesture()方法可以绘制任意轨迹这在解谜游戏中特别有用。比如最近帮朋友写的一个图案解锁脚本// 九宫格解锁图案Z字形 gesture(1000, [100, 1000], // 起点 [500, 1000], // 右移 [500, 1500], // 下移 [100, 1500], // 左移 [100, 2000] // 下移 );关键细节duration参数是每段路径的时间总时长为 (点数-1)×duration相邻点距离不宜超过500px否则会被系统识别为异常操作建议在关键点之间插入中间点使轨迹更平滑3.3 多点触控的黑科技gestures()方法可以模拟多指操作最典型的应用就是图片的缩放和旋转// 图片放大手势双指外扩 gestures( [0, 500, [300, 500], [100, 300]], // 手指1向左上移动 [0, 500, [500, 500], [700, 300]] // 手指2向右上移动 ); // 图片旋转手势双指扭转 gestures( [0, 800, [400, 400], [200, 600]], // 手指1逆时针移动 [0, 800, [600, 400], [800, 600]] // 手指2顺时针移动 );在实现这个功能时我踩过一个坑两组手势的delay参数必须相同才能实现同步操作。如果设置不同的delay时间会导致动作不同步变成分步操作而非同时操作。4. 游戏自动化实战案例4.1 角色移动控制以MOBA游戏为例要实现智能走位需要结合多种操作// 智能走位函数 function smartMove(targetX, targetY){ const heroX 540; // 角色初始X const heroY 1600; // 角色初始Y // 计算移动方向向量 let dx targetX - heroX; let dy targetY - heroY; let distance Math.sqrt(dx*dx dy*dy); // 归一化 dx / distance; dy / distance; // 动态调整移动时长距离越远移动时间越长 let duration Math.min(300 distance, 1000); // 执行滑动操作 swipe(heroX, heroY, heroX dx*300, heroY dy*300, duration); // 添加随机偏移更拟人 let randomOffset () Math.random()*20 - 10; return [dx*300 randomOffset(), dy*300 randomOffset()]; }4.2 连招组合技实现结合点击和滑动实现复杂连招function comboAttack(){ // 起手技能 click(300, 1800); sleep(300); // 位移接近敌人 swipe(500, 1800, 500, 1500, 200); sleep(100); // 核心输出 click(700, 1800); sleep(500); // 终结技手势滑动 gesture(600, [500, 1600], [500, 1400], [300, 1200] ); }性能优化技巧在循环操作前先获取屏幕尺寸避免每次操作都调用device.width复杂手势建议预先生成坐标数组减少实时计算适当添加随机延迟±50ms避免被检测为脚本4.3 自动刷图解决方案结合图像识别和坐标操作的全自动方案// 伪代码示例 function autoBattle(){ while(true){ // 1. 识别敌人位置需图像识别支持 let enemy findEnemy(); if(enemy){ // 2. 移动到敌人附近 moveTo(enemy.x - 100, enemy.y); // 3. 释放攻击技能 click(600, 1800); // 4. 随机走位躲避 if(Math.random() 0.7){ dodge([left,right][Math.floor(Math.random()*2)]); } } // 5. 拾取战利品 clickRandomItems(); // 6. 检查是否进入下一关 if(isNextLevel()){ click(900, 500); sleep(2000); } } }5. 防检测与优化策略在长时间使用自动化脚本后我总结出这些反检测经验轨迹优化人类操作轨迹带有弧度可以用贝塞尔曲线模拟function bezierSwipe(x1, y1, x2, y2, duration){ const cp1x x1 (x2-x1)*0.3 Math.random()*50-25; const cp1y y1 (y2-y1)*0.7 Math.random()*50-25; let points []; for(let t0; t1; t0.1){ let x Math.pow(1-t,2)*x1 2*(1-t)*t*cp1x t*t*x2; let y Math.pow(1-t,2)*y1 2*(1-t)*t*cp1y t*t*y2; points.push([x, y]); } gesture(duration/points.length, ...points); }操作间隔随机化function randomSleep(baseTime){ sleep(baseTime Math.random()*200 - 100); }坐标模糊处理function fuzzyClick(x, y, radius10){ let offsetX Math.random()*radius*2 - radius; let offsetY Math.random()*radius*2 - radius; click(x offsetX, y offsetY); }行为模式多样化建议准备3-5套操作模式轮流使用6. 调试技巧与性能优化6.1 可视化调试方案在开发复杂手势时我习惯先可视化验证轨迹// 绘制手势轨迹调试用 function drawGesture(points, color#FF0000){ let window floaty.rawWindow( canvas idcanvas layout_weight1/ ); window.setSize(device.width, device.height); window.setTouchable(false); window.canvas.on(draw, (canvas) { let paint new Paint(); paint.setStrokeWidth(5); paint.setColor(colors.parseColor(color)); paint.setStyle(Paint.Style.STROKE); let path new Path(); path.moveTo(points[0][0], points[0][1]); for(let i1; ipoints.length; i){ path.lineTo(points[i][0], points[i][1]); } canvas.drawPath(path, paint); }); return window; } // 使用示例 let testWindow drawGesture([[100,100],[500,500],[300,800]]); sleep(3000); testWindow.close();6.2 性能监控方案长时间运行脚本时需要监控性能function startPerformanceMonitor(){ threads.start(function(){ while(true){ let usage runtime.performance.memoryUsage(); console.log(内存使用${usage.used}/${usage.total}); if(usage.used/usage.total 0.8){ toast(内存占用过高建议重启脚本); } sleep(5000); } }); }6.3 脚本异常处理健壮的异常处理能让脚本持续运行function safeClick(x, y, retry3){ for(let i0; iretry; i){ try{ return click(x, y); }catch(e){ console.error(点击失败${e}); sleep(1000); } } throw new Error(点击[${x},${y}]失败已达最大重试次数); }7. 多设备适配进阶方案对于需要适配大量不同设备的场景可以采用配置中心方案// 设备配置表 const DEVICE_PROFILES { 1920x1080: { buttonPositions: { attack: [500, 1800], skill1: [300, 1800], // ... }, screenMetrics: [1080, 1920] }, 2244x1080: { buttonPositions: { attack: [550, 1900], skill1: [350, 1900], // ... }, screenMetrics: [1080, 2244] } // 其他设备... }; function initDeviceProfile(){ let resolution ${device.width}x${device.height}; let profile DEVICE_PROFILES[resolution]; if(!profile){ // 自动生成近似配置 profile { screenMetrics: [1080, Math.round(1080*device.height/device.width)], buttonPositions: autoDetectButtons() }; } setScreenMetrics(...profile.screenMetrics); return profile; }8. 手势录制与回放系统对于特别复杂的手势可以开发录制回放功能let recordedGestures []; function startRecording(){ recordedGestures []; events.on(touch, recordTouchEvent); } function stopRecording(){ events.removeAllListeners(touch); return recordedGestures; } function recordTouchEvent(event){ recordedGestures.push({ action: event.getAction(), x: event.getX(), y: event.getY(), time: Date.now() }); } function replayGestures(gestures){ let startTime Date.now(); let fingers {}; gestures.forEach(gesture { let delay gesture.time - startTime; if(!fingers[gesture.pointerId]){ fingers[gesture.pointerId] []; } fingers[gesture.pointerId].push([gesture.x, gesture.y]); }); let gestureParams []; Object.values(fingers).forEach(points { if(points.length 1){ gestureParams.push([ 0, (points[points.length-1][2] - points[0][2]), ...points.map(p [p[0], p[1]]) ]); } }); gestures(...gestureParams); }