Unity虚拟触摸板开发:基于EventSystem的多点触控与手势识别实现
1. 项目概述为什么要在Unity里再造一个“触摸板”最近在做一个需要复杂手势操控的移动端项目UI同事丢过来一个需求希望有一个区域手指在上面滑动时能像笔记本电脑的触摸板一样不仅控制一个光标比如一个准星或者高亮框在屏幕上移动还要能响应轻点、长按、双指缩放和旋转。Unity自带的UI系统比如Button、ScrollRect处理的是直接的点击和拖拽对于这种需要将一片区域的“相对位移”映射为另一个物体“绝对或相对运动”的间接操控就显得力不从心了。这其实就是我们常说的“虚拟摇杆”或“触摸板”交互的核心。网上搜“Unity 触摸板”教程不少但很多要么是简单贴个图用Input.GetTouch算算deltaPosition就结束了功能简陋要么代码耦合严重难以复用和扩展。真正要把这个功能做扎实做得像系统原生一样流畅、可配置、易集成必须深入到Unity的输入事件系统底层从PointerEventData这个核心类入手构建一条清晰、健壮的交互处理链路。这不仅仅是实现功能更是理解Unity UI事件机制的一次绝佳实践。无论你是想做一个游戏内的虚拟摇杆、一个图片查看器的操控面板还是一个创意应用的核心交互模块这套从事件生成、传递到最终响应的完整链条都是你必须掌握的硬核技能。2. 核心交互链设计与架构解析实现一个触摸板远不是在一个Image上挂个脚本监听Update里的Touch那么简单。一个健壮、可扩展的触摸板其交互链应该像精密的机械表一样各司其职协同工作。我们的核心设计思路是利用Unity的EventSystem构建标准输入事件流在Graphic组件如Image层面接收并解析PointerEventData将原始触摸数据转化为干净的操控指令如位移向量、缩放比例最后通过委托、事件或接口等方式将这些指令分发给需要响应的逻辑模块。2.1 交互链的四个核心层级整个交互链可以自上而下分为四个层级输入采集层由Unity的EventSystem和Input Module如StandaloneInputModule或TouchInputModule负责。它们从操作系统获取原始的触摸、鼠标输入并封装成PointerEventData对象。这一层我们通常不需要改动但要理解它提供了什么数据。事件接收与解析层这是触摸板功能的核心载体通常是一个继承了Graphic类如Image的UI元素。我们需要为其挂载实现了IPointerDownHandler、IPointerUpHandler、IDragHandler等接口的脚本。在这一层我们接收PointerEventData从中提取关键信息如指针ID、当前位置、上一帧位置并根据业务逻辑判断当前是单指拖拽、双指缩放还是旋转计算出相应的增量数据Delta。指令抽象层将解析层计算出的原始增量数据封装成易于理解的指令对象或结构化数据。例如将双指触摸的中心点位移和间距变化解耦为“平移Pan”、“缩放Zoom”、“旋转Rotate”三个独立的指令每个指令包含类型和强度参数。这一层的目的是让触摸板的输出与具体的业务逻辑无关。业务响应层接收指令抽象层发出的指令执行具体的游戏逻辑。例如控制一个摄像机移动、缩放地图、旋转3D模型。这一层与触摸板本身完全解耦只关心“收到了一个‘放大’指令强度是1.2倍”这样的事件。提示为什么强调使用EventSystem而不是直接Input.touches因为EventSystem天然处理了UI层级、射线遮挡、多点触控ID管理等问题。直接使用Input你需要自己处理触摸点是否落在UI上、如何穿透UI等繁琐问题极易出错。2.2 PointerEventData你的数据金矿PointerEventData是EventSystem传递给我们的信息包里面全是宝藏pointerId指针的唯一标识。鼠标为负值触摸屏触摸点为非负值通常从0开始。这是处理多点触控的关键我们必须用一个字典Dictionaryint, TouchPoint来跟踪每个活跃指针的状态。position当前指针在屏幕空间中的像素坐标。delta从上一帧到当前帧指针位置的像素变化量。注意这个delta在快速滑动时可能不够平滑有时我们需要自己用position和上一帧记录的位置来计算更精确的增量。pressPosition指针按下的初始位置。dragging当前指针是否处于拖拽状态。enterEventCamera/pressEventCamera与事件关联的摄像机用于坐标转换。理解并善用这些字段是精准解析用户意图的基础。3. 从零构建触摸板核心组件理论讲完我们动手实现。我将这个核心组件命名为TouchpadController它继承自Image以获得UI的绘制和射线接收能力并实现了一系列事件接口。3.1 组件初始化与状态管理首先定义内部状态和必要的数据结构。using UnityEngine; using UnityEngine.EventSystems; using System.Collections.Generic; [RequireComponent(typeof(RectTransform))] public class TouchpadController : Image, IPointerDownHandler, IPointerUpHandler, IDragHandler, IInitializePotentialDragHandler { // 公开的事件供业务层订阅 public System.ActionVector2 OnTouchpadDrag; // 传递标准化位移向量 public System.Actionfloat OnTouchpadZoom; // 传递缩放因子 public System.Actionfloat OnTouchpadRotate; // 传递旋转角度 // 配置参数 [SerializeField] private bool _normalizeDragOutput true; // 是否将拖拽输出标准化-1到1 [SerializeField] private float _dragSensitivity 1.0f; [SerializeField] private float _zoomSensitivity 0.01f; [SerializeField] private float _rotateSensitivity 0.1f; // 内部状态跟踪 private class TouchPoint { public int pointerId; public Vector2 startPosition; public Vector2 previousPosition; public bool isActive; } private Dictionaryint, TouchPoint _activeTouches new Dictionaryint, TouchPoint(); private Vector2 _previousTouchCenter; // 上一帧双指中心点 private float _previousTouchDistance; // 上一帧双指间距 }IInitializePotentialDragHandler接口的实现很重要它告诉EventSystem这个对象可以被拖拽即使它同时处理点击事件。public void OnInitializePotentialDrag(PointerEventData eventData) { eventData.useDragThreshold false; // 关键禁用默认的拖拽阈值让触摸板一滑动就有反应。 }3.2 单指拖拽的实现细节单指拖拽是最基础的功能目标是输出一个平滑、稳定的位移向量。public void OnPointerDown(PointerEventData eventData) { int pid eventData.pointerId; if (!_activeTouches.ContainsKey(pid)) { var newTouch new TouchPoint { pointerId pid, startPosition eventData.position, previousPosition eventData.position, isActive true }; _activeTouches.Add(pid, newTouch); } // 当触摸点数量变化时重置双指操作的历史数据 if (_activeTouches.Count ! 2) { _previousTouchDistance 0f; } } public void OnDrag(PointerEventData eventData) { int pid eventData.pointerId; if (!_activeTouches.TryGetValue(pid, out TouchPoint touch)) return; // 1. 更新当前触摸点位置并计算增量 Vector2 currentDelta eventData.position - touch.previousPosition; touch.previousPosition eventData.position; // 2. 根据当前触摸点数量决定操作模式 if (_activeTouches.Count 1) { ProcessSingleTouchDrag(currentDelta, eventData); } else if (_activeTouches.Count 2) { ProcessMultiTouchGesture(); } } private void ProcessSingleTouchDrag(Vector2 rawDelta, PointerEventData eventData) { // 将屏幕像素增量转换为标准化向量 Vector2 outputDelta rawDelta * _dragSensitivity; if (_normalizeDragOutput) { // 一种常见的标准化方法基于触摸板RectTransform的尺寸 RectTransform rt this.rectTransform; if (rt ! null) { Vector2 localSize rt.rect.size; // 假设触摸板尺寸为200x200delta为(10,0)则输出为(0.05, 0) outputDelta.x / localSize.x; outputDelta.y / localSize.y; } // 钳制输出范围防止过大值 outputDelta Vector2.ClampMagnitude(outputDelta, 1.0f); } // 触发事件 OnTouchpadDrag?.Invoke(outputDelta); }关键点OnDrag中我们使用touch.previousPosition和eventData.position自己计算增量而不是完全依赖eventData.delta。实测发现在某些帧率波动或事件处理顺序下eventData.delta可能不准确或为零自己记录上一帧位置更为可靠。3.3 双指缩放与旋转的精准计算双指手势是触摸板的精髓计算需要一些几何知识。private void ProcessMultiTouchGesture() { // 1. 获取两个活跃的触摸点 if (_activeTouches.Count ! 2) return; // 假设我们从字典中取出两个TouchPointtouchA和touchB // 这里需要写一个方法从字典中取出两个点代码略... TouchPoint touchA GetTouchByIndex(0); TouchPoint touchB GetTouchByIndex(1); Vector2 currentPosA touchA.previousPosition; // 已在OnDrag中更新 Vector2 currentPosB touchB.previousPosition; // 2. 计算当前帧的中心点和距离 Vector2 currentCenter (currentPosA currentPosB) / 2f; float currentDistance Vector2.Distance(currentPosA, currentPosB); // 3. 如果是双指操作的开始上一帧距离为0则只记录不计算增量 if (_previousTouchDistance Mathf.Epsilon) { _previousTouchCenter currentCenter; _previousTouchDistance currentDistance; return; } // 4. 计算缩放因子 float zoomDelta currentDistance - _previousTouchDistance; float zoomFactor 1.0f (zoomDelta * _zoomSensitivity); OnTouchpadZoom?.Invoke(zoomFactor); // 5. 计算旋转角度基于中心点的向量夹角变化 Vector2 prevVector touchB.previousPosition - touchA.previousPosition; // 注意这里需要获取“上一帧”的原始位置来计算上一帧的向量。 // 我们需要在TouchPoint中额外记录一个lastFramePosition用于旋转计算或者用其他方式。 // 简化处理使用当前帧和上一帧的中心点连线向量的旋转不够精确但较稳定 Vector2 prevCenterVector _previousTouchCenter - currentCenter; // 这个向量方向是反的 Vector2 currentCenterVector currentCenter - _previousTouchCenter; if (prevCenterVector.sqrMagnitude 0.01f currentCenterVector.sqrMagnitude 0.01f) { float angle Vector2.SignedAngle(prevCenterVector, currentCenterVector); OnTouchpadRotate?.Invoke(angle * _rotateSensitivity); } // 6. 计算双指拖拽平移 Vector2 panDelta currentCenter - _previousTouchCenter; ProcessSingleTouchDrag(panDelta, null); // 复用单指拖拽逻辑处理平移 // 7. 更新历史数据 _previousTouchCenter currentCenter; _previousTouchDistance currentDistance; }计算旋转的注意事项上面代码中计算旋转的方法基于中心点位移向量是一种简化且稳定的方案但并非物理上精确的“两指连线旋转”。精确计算需要用到Mathf.Atan2计算两帧间连线的角度差。但精确计算对触摸点的微小抖动非常敏感容易导致旋转抖动。生产环境中通常会在精确计算的基础上加入一个死区Dead Zone和低通滤波Low-pass Filter例如// 更精确但需防抖的旋转计算 Vector2 prevVector touchB.lastFramePosition - touchA.lastFramePosition; Vector2 currVector currentPosB - currentPosA; float angleDelta Vector2.SignedAngle(prevVector, currVector); // 加入死区过滤微小抖动 if (Mathf.Abs(angleDelta) 0.3f) // 死区阈值 { // 可选使用一阶低通滤波平滑角度值 _smoothedRotationDelta Mathf.Lerp(_smoothedRotationDelta, angleDelta, 0.2f); OnTouchpadRotate?.Invoke(_smoothedRotationDelta * _rotateSensitivity); }3.4 指针抬起与清理public void OnPointerUp(PointerEventData eventData) { int pid eventData.pointerId; if (_activeTouches.ContainsKey(pid)) { _activeTouches.Remove(pid); } // 当触摸点数量减少到1个或0个时重置双指历史数据防止残留数据影响下一次手势 if (_activeTouches.Count 2) { _previousTouchDistance 0f; // 同时可以发送一个拖拽结束或缩放结束的事件让业务逻辑知道手势已终止 OnTouchpadDrag?.Invoke(Vector2.zero); } }4. 性能优化与高级特性实现一个基础的触摸板已经完成但要投入生产环境我们还需要考虑更多。4.1 输入平滑与防抖处理原生触摸数据存在不可避免的噪声。我们可以在指令抽象层加入滤波。// 在TouchpadController类中添加 private Vector2 _smoothedDragDelta Vector2.zero; public float dragSmoothingFactor 0.2f; // 平滑系数0为不平滑1为完全使用新值 private void ProcessSingleTouchDrag(Vector2 rawDelta, PointerEventData eventData) { // 应用一阶指数平滑滤波 (Low-pass Filter) _smoothedDragDelta Vector2.Lerp(_smoothedDragDelta, rawDelta, dragSmoothingFactor); Vector2 outputDelta _smoothedDragDelta * _dragSensitivity; // ... 后续标准化和触发事件逻辑 }对于缩放和旋转也可以采用类似的Mathf.Lerp进行平滑。4.2 自适应DPI与分辨率不同设备屏幕DPI不同固定的像素灵敏度会导致操作体验不一致。一个更好的方案是基于屏幕的物理尺寸或逻辑DPI来调整灵敏度。private float GetDPIScaledSensitivity(float baseSensitivity) { // 方法1基于Screen.dpi可能为0 float dpi Screen.dpi; if (dpi 1) dpi 96; // 默认回退值 return baseSensitivity * (dpi / 96f); // 以96DPI为标准 // 方法2基于屏幕对角线逻辑英寸更稳定 // float screenWidthInch Screen.width / Screen.dpi; // float screenHeightInch Screen.height / Screen.dpi; // float diagonalInch Mathf.Sqrt(screenWidthInch*screenWidthInch screenHeightInch*screenHeightInch); // return baseSensitivity * (diagonalInch / 5.5f); // 以5.5英寸手机为标准 }在Awake或Start中计算一次并缓存这个缩放因子。4.3 与Unity新输入系统Input System的兼容项目如果使用了新的Input SystemPackage我们希望触摸板也能兼容。我们可以创建一个适配层。using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.InputSystem.EnhancedTouch; public class TouchpadControllerInputSystemAdapter : MonoBehaviour { public TouchpadController touchpad; private void OnEnable() { EnhancedTouchSupport.Enable(); UnityEngine.InputSystem.EnhancedTouch.Touch.onFingerDown OnFingerDown; UnityEngine.InputSystem.EnhancedTouch.Touch.onFingerMove OnFingerMove; UnityEngine.InputSystem.EnhancedTouch.Touch.onFingerUp OnFingerUp; } private void OnDisable() { /* 取消注册 */ } private void OnFingerMove(Finger finger) { // 将Input System的Finger数据模拟成PointerEventData调用touchpad的内部方法 // 这里需要将屏幕坐标转换为touchpad的局部坐标并处理多点触控ID映射 // 本质上是在复用TouchpadController的核心逻辑只是数据源变了。 } }更优雅的做法是重构TouchpadController使其核心逻辑不依赖于PointerEventData而是依赖于一个抽象的IInputProvider接口然后分别实现EventSystemInputProvider和NewInputSystemProvider。5. 实战集成与常见问题排查5.1 在UGUI中集成触摸板创建UI在Canvas下创建一个Image将其锚点Anchor和轴心Pivot设置为(0.5, 0.5)方便居中并调整到合适大小。添加组件移除Image默认的Source Image使其透明或赋予一个半透明的触摸板样式贴图。然后挂载TouchpadController脚本。配置参数在Inspector中调整_dragSensitivity等参数。可以先设为1在真机上测试后微调。订阅事件在需要控制的脚本如摄像机控制器、地图控制器中获取TouchpadController引用并订阅其事件。public class CameraRigController : MonoBehaviour { public TouchpadController touchpad; public float moveSpeed 5f; private void OnEnable() { if(touchpad ! null) touchpad.OnTouchpadDrag HandleDrag; } private void OnDisable() { if(touchpad ! null) touchpad.OnTouchpadDrag - HandleDrag; } private void HandleDrag(Vector2 delta) { // 假设控制一个RTS游戏的摄像机 Vector3 move new Vector3(delta.x, 0, delta.y) * moveSpeed * Time.deltaTime; transform.Translate(move, Space.World); } }5.2 常见问题与解决方案速查表问题现象可能原因排查步骤与解决方案触摸板完全无反应1. Canvas Render Mode 或 Camera 设置问题。2.EventSystem缺失或被禁用。3.Image组件的Raycast Target未勾选。1. 检查场景中是否有EventSystem对象。2. 确认Canvas的Render Mode与Event Camera设置正确确保射线能投射到触摸板。3. 确保TouchpadController组件所在的Image上Raycast Target为勾选状态。拖拽有延迟或卡顿1. 在OnDrag中进行了耗时操作。2. 没有禁用拖拽阈值 (useDragThreshold false)。3. 帧率过低。1. 确保OnDrag逻辑轻量复杂计算或事件触发考虑分帧或使用协程。2. 确认已实现IInitializePotentialDragHandler并设置了eventData.useDragThreshold false。3. 使用 Profiler 查看性能瓶颈。双指手势识别混乱如缩放时突然跳变1. 触摸点ID跟踪错误_activeTouches字典管理有误。2. 历史数据 (_previousTouchDistance) 在触摸点数量变化时未正确重置。3. 旋转计算未考虑死区微小抖动被放大。1. 仔细检查OnPointerDown、OnPointerUp中对_activeTouches的增删逻辑确保pointerId匹配。2. 在OnPointerUp和OnPointerDown中当触摸点数量变为非2时重置_previousTouchDistance 0。3. 为旋转计算添加死区过滤和平滑滤波。输出值不稳定、抖动1. 直接使用eventData.delta其本身可能不稳定。2. 缺乏输入平滑处理。3. 坐标转换或标准化计算有误。1. 改用自己记录的previousPosition计算增量。2. 引入平滑滤波算法如指数平滑。3. 检查标准化计算是否基于正确的RectTransform尺寸避免除零错误。触摸板区域外滑动仍能控制EventSystem的射线检测被其他全屏UI或透明图形阻挡。1. 检查是否有其他Graphic如全屏背景图的Raycast Target被勾选且层级更高。2. 确保触摸板的RectTransform层级顺序正确或在脚本中通过eventData.pointerEnter判断当前指针是否在本物体上。5.3 一个进阶技巧触摸板边界回弹与惯性模拟为了让手感更接近高端触摸板可以模拟“溢出回弹”和“惯性滑动”。这需要在指令层之后业务层之前加入一个后处理模块。public class TouchpadPostProcessor : MonoBehaviour { public TouchpadController sourceTouchpad; public System.ActionVector2 OnProcessedDrag; // 处理后的拖拽事件 private Vector2 _currentVelocity; public float elasticity 0.5f; // 回弹系数 public float inertiaDecay 0.95f; // 惯性衰减 void Update() { // 1. 接收原始输入 // 2. 模拟边界如果虚拟光标到达边界施加一个反向的“回弹”力。 // 3. 模拟惯性当手指抬起原始输入为0时_currentVelocity 不会立刻归零而是每帧衰减。 // 4. 将回弹力和惯性力叠加输出最终的位移向量。 // 5. 触发 OnProcessedDrag。 } }这个后处理器独立于核心触摸板可以灵活地挂载或卸载让你能轻松调整手感而不污染核心逻辑。实现一个工业级的Unity触摸板就像组装一台精密仪器。从PointerEventData这座数据金矿里挖掘信息在EventSystem的流水线上加工处理最终封装成干净利落的操控指令。这套架构的价值不仅在于功能本身更在于它提供了一种处理复杂UI交互的范式分层解耦、事件驱动、数据驱动。当你下次需要实现自定义滑块、旋钮或任何非标准交互控件时这套从事件接收到指令分发的链条将会是你最得力的工具箱。

相关新闻

Java日期加减实战:从Calendar到LocalDateTime的演进与最佳实践

Java日期加减实战:从Calendar到LocalDateTime的演进与最佳实践

1. 项目概述:为什么Java日期处理是每个开发者必须迈过的坎如果你刚开始接触Java,或者已经写了几个月代码,我敢打赌,你一定在某个时刻被日期时间处理搞得焦头烂额。这几乎是每个Java开发者成长路上的“必修课”。我刚开始工作时&am…

2026/10/11 13:52:50 阅读更多 →
从零制作《绝区零》蕾米埃尔MMD动画:全流程实践与问题排查指南

从零制作《绝区零》蕾米埃尔MMD动画:全流程实践与问题排查指南

在游戏、动画和虚拟偶像领域,3D角色动画的制作与分享一直是创作者社区的核心活动。其中,MMD(MikuMikuDance)作为一个拥有庞大用户基础的免费3D动画制作软件,因其相对较低的入门门槛和丰富的社区资源,成为了…

2026/10/11 13:52:34 阅读更多 →
STM32 PWM 100%占空比异常分析与解决方案

STM32 PWM 100%占空比异常分析与解决方案

1. 问题现象与背景:当PWM“满上”时发生了什么?在嵌入式开发,尤其是电机控制、LED调光、开关电源这些对精度要求极高的领域里,STM32的定时器PWM输出功能是我们的得力干将。我敢说,但凡用过STM32做这类项目的朋友&#…

2026/10/11 13:52:19 阅读更多 →

最新新闻

RTMP协议为何老而不死?核心机制、推流实践与故障排查全解析

RTMP协议为何老而不死?核心机制、推流实践与故障排查全解析

做流媒体这行,只要碰过直播推流,RTMP这三个字母就一定绕不开。它靠着一条TCP长连接,把音视频数据拆成一个个小块,从推流端一路送到服务器,再由服务器分发出去,端到端延迟通常能压在两秒以内。这是个二十多年…

2026/10/14 4:02:42 阅读更多 →
HeidiSQL 9.2.0.4947安装配置指南:从静默部署到连接排障

HeidiSQL 9.2.0.4947安装配置指南:从静默部署到连接排障

简介:HeidiSQL 9.2.0.4947 的官方安装程序,压缩包共 2 个文件(exe 安装文件及 htm 说明文档),大小仅 8.18MB。作为一款开源且跨平台的数据库管理工具,HeidiSQL 提供简洁直观的图形化界面,支持 M…

2026/10/14 4:02:42 阅读更多 →
CHB-MIT癫痫脑电数据集实战:从EDF解析到患者独立划分

CHB-MIT癫痫脑电数据集实战:从EDF解析到患者独立划分

简介:CHB-MIT数据集是由MIT与波士顿儿童医院联合发布的癫痫脑电研究资源,面向生物医学信号处理、机器学习与模式识别方向的研究者及学生,用于癫痫发作检测、分类与长期监测等课题。本压缩包共145个文件,以141个.m脚本和4个.py脚本…

2026/10/14 4:02:42 阅读更多 →
手游买量ROI测算实战:从25页研报拆解到可跑模型

手游买量ROI测算实战:从25页研报拆解到可跑模型

简介:这份中金公司2019年6月发布的传媒行业研究报告,聚焦手游流量运营(买量)模式,适合游戏行业研究者、投资分析人员及手游发行从业者阅读,用于理解效果广告分发逻辑与行业趋势。资源为1个PDF文件&#xff…

2026/10/14 4:02:42 阅读更多 →
WebSocket协议详解:从握手、帧结构到线上问题排查

WebSocket协议详解:从握手、帧结构到线上问题排查

1. 先聊清楚:WebSocket协议到底解决了什么问题一直在做网络通信相关的开发,WebSocket协议对我来说已经不算是陌生概念了。但每次面试新人或者帮同事排查线上连接异常时,我发现很多人对它的理解只停留在“浏览器和服务器能互相推消息”这个层面…

2026/10/14 4:02:42 阅读更多 →
Codex国内API配置指南:环境变量、模型映射与网关接入实战

Codex国内API配置指南:环境变量、模型映射与网关接入实战

简介:面向国内 Windows 开发者使用 OpenAI Codex 编码工具的需求,这份代码包提供了一条从环境准备到 API 联通验证的完整实践路径。资源先给出 Node.js 与 npm 的检查方法,并用一键安装脚本降低新手搭建门槛;随后梳理 Codex CLI 的…

2026/10/14 4:01:42 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →