Unity场景标注指南:运行时点击任意位置添加可持久化文字备注
1. 需求缘起与方案定位做Unity开发久了你会发现一个很常见的需求在场景的某个位置加一个备注写点提示信息。比如模型审查时在问题部位标记“此处碰撞体未对齐”或者给策划看关卡时在某块区域写“这个平台将来放传送门”再或者做数字孪生项目时在对应设备位置标注“三号泵待检修”。需求听着简单真要做得顺手、可维护还是有不少门道的。先说一下标题里的关键词——自定义位置和添加备注文字。前者意味着用户不是通过编辑器手动摆一个对象而是在运行状态下、甚至是在打包后的程序里通过点击场景中的任意位置来创建标注后者说明核心交互是文字不是画线、画框、截图之类的复杂标注。这个定语组合决定了我们做的是一套运行时交互系统而不是静态编辑工具。我个人的建议是第一版不用追求花哨先把“点击场景→获取坐标→生成可编辑文字标记→数据能存能读”这条主链路打通。后面再加拖拽调整、贴地吸附、按距离缩放这些增强功能就顺理成章了。这个功能适合谁适合做项目原型验证的独立开发者、做模型审查工具的团队以及需要非技术人员比如策划、美术、甲方在场景里直接提意见的场景。对这类使用者来说简单可靠远比功能炫酷重要。2. 核心技术选型与原理拆解2.1 三种备选方案的对比运行时的文字标注业内最常用的有下面几条路线方案AGUI或UI层的屏幕坐标标注。把3D坐标通过WorldToScreenPoint转成屏幕坐标文本生成在Canvas下不跟任何3D对象绑定。优点是实现快、性能开销小缺点也明显——一旦旋转视角标注不会跟着物体走必须每帧重新映射坐标到屏幕位置本质上是“浮”在屏幕上的标签和场景本身没有空间依附关系。方案BWorld Space Canvas。在Canvas的Render Mode里选择World Space把它放在某个3D位置文字就变成真正存在于场景中的对象。这是很多标注工具采用的折中方案——既能被相机看到又能参与遮挡关系还能在编辑模式下直接摆放。缺点是Canvas是矩形批量渲染结构如果你场景里有两三百个独立标注用一个个Canvas去承载文本会产生大量DrawCall性能上要有所控制。方案CTextMeshPro 3D文本TextMeshPro字面理解是SDF渲染的文本网格配合Billboard朝向逻辑。这是我最推荐的路线。TMPTextMeshPro的简称本身渲染质量高而且每个标注是一个独立GameObject天然支持批处理和遮挡关系配合自定义材质可以控制远近显隐灵活度最高。可能有朋友会问方案B不是最简单吗确实如果只是做编辑器内验证方案B半小时就能搞定。但注意我们的核心需求是“自定义位置”的运行时添加你的使用者可能是完全不碰Unity编辑器的人场景里动态生成的World Space Canvas会有一堆物体层级管理的问题。方案C一个Prefab搞定所有代码里只维护一个列表干净利落。2.2 关键实现机制为什么需要BillboardTMP文本默认是“躺”在场景里的平面物体。你从正面看很正常但绕到侧面看文字就像一张纸片立在那里斜视时甚至完全看不清。解决思路很朴素把标注物体每一帧都转成正对相机——这就是Billboard广告牌技术的经典用法和粒子系统里的“面向摄像机”是同一个道理。实现时有两点要注意。第一转动的参考系要选好。直接用transform.LookAt(camera.transform)会让物体的正Z轴指向相机如果文本本身的正方向不是Z轴文字会上下颠倒或者左右镜像。稳妥的做法是只处理相机的Y轴旋转避免俯仰角影响让文字永远直立朝上。第二不要每帧对每个标注做完整的四元数运算几百个对象还好上千个时可以考虑只在相机Rotate事件或OnPreCull里刷新减少逐帧开销。2.3 坐标拾取物理射线是地基“自定义位置”的实现本质上靠的是物理射线检测。用户点击屏幕我们往屏幕对应的方向发射一条射线和场景中带碰撞体的对象求交点得到的P点就是标注的世界坐标。逻辑一句话说不完但做起来就那么几行代码真正决定体验的是下面几个细节射线最大距离。场景很大时射线默认射程可能够不到目标。建议给个合理上限或者干脆设成射线无穷远Mathf.Infinity但要注意和你的碰撞体范围配合。命中层过滤。如果地面、墙体、角色、特效都挂在同一个碰撞体系里点人身上还是点地上会直接影响标注坐标的合理性。我的习惯是把“可标注对象”挂到一个专门的Layer上射线只检测这个层如果项目里所有物体都可能被标注那就默认全检测但至少留一个LayerMask参数方便将来排斥掉UI、水面这类区域。空场景补救。射线没打中任何物体时比如地图边界外可以退化为地面高度固定、XZ坐标取点击射线与世界平面的交点。这个逻辑加上之后用户就算点到天上标注也会落到地面上视觉上更自然。3. 数据层设计与持久化方案3.1 备注数据的最小结构标注物在场景里是GameObject但真正需要保存的只有那么几条数据。我建议定义这样一个类[System.Serializable] public class MarkerData { public string id; public Vector3 position; public string content; public long createTime; public Color colorTint; public float fontSize; public string layerTag; }字段解释一下id必须唯一后面增删改查全靠它position存世界坐标content是备注正文createTime用于排序和管理colorTint和fontSize属于显示层面的个性化配置layerTag可留可不留用于区分这个备注关联到哪个业务模块。我见过一些项目把数据直接塞在MonoBehaviour字段里然后依赖场景序列化保存——这种方案在编辑器里好用到了打包运行时就不靠谱了必须走JsonUtility或者LitJson之类的序列化路线。3.2 序列化与存储路径Unity自带的JsonUtility对Vector3、Quaternion这类Unity原生类型支持得很好不需要手动转换成float[]这是最省事的方案。写文件时推荐把路径放在Application.persistentDataPath下而不是StreamingAssets或DataPath。原因很简单persistentDataPath在不同平台Windows、Android、iOS都是可写目录不依赖安装包路径也不受只读权限限制。存储格式上用JSON文本就够了不要为了“性能”去搞二进制。标注数据量再大也就几百条记录JSON可读性好出问题可以直接用文本编辑器打开检查这个好处在实际排查问题的时候太重要了。public static void SaveMarkers(ListMarkerData markers) { MarkerListWrapper wrapper new MarkerListWrapper(); wrapper.items markers; string json JsonUtility.ToJson(wrapper, true); File.WriteAllText(Path.Combine(Application.persistentDataPath, markers.json), json); } public static ListMarkerData LoadMarkers() { string path Path.Combine(Application.persistentDataPath, markers.json); if (!File.Exists(path)) return new ListMarkerData(); string json File.ReadAllText(path); MarkerListWrapper wrapper JsonUtility.FromJsonMarkerListWrapper(json); return wrapper.items ?? new ListMarkerData(); }这里有个坑JsonUtility不支持直接序列化顶层数组所以得套一层MarkerListWrapper类。不套的话反序列化会返回空对象还不报错排查起来很费劲。3.3 数据字典为什么不用List直接存直接拿List存MarkerData也能跑但我在实际项目中吃过亏删除一个标注后要遍历整表、按ID查数据时也要遍历代码写多了特别别扭。后来改成用Dictionarystring, MarkerData做内存态存储增删改查全部走Key性能也好、语义也清晰。但序列化时Dictionary不支持直接JsonUtility所以做法是内存里用Dictionary存盘时转成List读盘时再转回Dictionary。这个转换逻辑封装好之后数据层就很稳定了。4. 完整实践从零实现标注系统4.1 标注对象的Prefab结构在动手写代码之前先在Unity编辑器里把标注的预制体搭出来。结构建议如下根节点MarkerRoot挂MarkerRenderer脚本后面讲坐标放在根节点上。子节点TextObj挂RectTransform会比较奇怪直接用TransformTextMeshPro组件。子节点BackgroundQuad一个Quad或SpriteRenderer用半透明材质做文字底衬增强可读性。子节点PivotIndicator一个小球或箭头模型表示这个标注锚定的3D位置点。注意父节点不要旋转所有朝向逻辑放在MarkerRenderer里处理。Radial的TextMeshPro会有rectTransform的概念如果你在World Space下用它的尺寸单位是米可以通过text.rectTransform.sizeDelta控制框体大小不过这属于进阶技巧第一版用默认设置就行。4.2 核心脚本实现MarkerRenderer这个脚本负责标注物的运行时行为朝向相机、响应点击事件、以及配合数据层刷新内容。using TMPro; using UnityEngine; public class MarkerRenderer : MonoBehaviour { public TextMeshPro textMesh; public GameObject backgroundQuad; public Color normalColor Color.white; private Camera mainCam; private MarkerData data; public void Init(MarkerData markerData, Camera cam) { data markerData; mainCam cam; transform.position markerData.position; textMesh.text markerData.content; textMesh.color markerData.colorTint; textMesh.fontSize markerData.fontSize; gameObject.SetActive(true); } private void LateUpdate() { if (mainCam null) return; // Billboard朝向只绕Y轴转动保持文字正立 Vector3 forward transform.position - mainCam.transform.position; forward.y 0f; if (forward.sqrMagnitude 0.001f) { transform.rotation Quaternion.LookRotation(forward.normalized); } } private void OnMouseDown() { // 点击标注时可以通过事件回调通知管理器进入编辑状态 if (MarkerBus.OnMarkerClicked ! null) MarkerBus.OnMarkerClicked.Invoke(data.id); } }LateUpdate比Update适合处理朝向逻辑因为它在所有相机的移动更新之后执行这样标注不会“慢半拍”。4.3 管理器MarkerManager管理器是整个系统的中枢负责添删改查、序列化、以及把UI输入转换成世界坐标。public class MarkerManager : MonoBehaviour { public Camera targetCamera; public GameObject markerPrefab; public LayerMask hitLayer; public float maxRayDistance 500f; public KeyCode addModeKey KeyCode.LeftShift; private Dictionarystring, MarkerRenderer activeMarkers new Dictionarystring, MarkerRenderer(); private bool addModeEnabled false; void Update() { if (Input.GetKeyDown(addModeKey)) addModeEnabled true; if (Input.GetKeyUp(addModeKey)) addModeEnabled false; if (addModeEnabled Input.GetMouseButtonDown(0)) { TryAddMarkerAtMouse(); } } private void TryAddMarkerAtMouse() { Ray ray targetCamera.ScreenPointToRay(Input.mousePosition); bool hitFound false; Vector3 spawnPos Vector3.zero; RaycastHit hit; if (Physics.Raycast(ray, out hit, maxRayDistance, hitLayer, QueryTriggerInteraction.Ignore)) { hitFound true; spawnPos hit.point; } else { // 射线未命中时让标注落到水平面上可选 Plane groundPlane new Plane(Vector3.up, Vector3.zero); float enter; if (groundPlane.Raycast(ray, out enter)) { hitFound true; spawnPos ray.GetPoint(enter); } } if (!hitFound) return; MarkerData md new MarkerData(); md.id System.Guid.NewGuid().ToString(); md.position spawnPos; md.content ; // 空内容等待用户输入 md.createTime System.DateTime.Now.Ticks; GameObject go Instantiate(markerPrefab, spawnPos, Quaternion.identity, transform); MarkerRenderer renderer go.GetComponentMarkerRenderer(); renderer.Init(md, targetCamera); activeMarkers.Add(md.id, renderer); // 弹出输入框可以是Unity UI的InputField也可以是调用系统弹窗 UIManager.Instance.OpenEditor(md.id); } }几个设计的取舍在这里解释一下。按住Shift进入添加模式是为了避免误点——因为鼠标在场景里要频繁进行其他操作如果每次左键都创建一个标注那用户根本没法正常浏览模型。现在这样只有在按着Shift的情况下点击才会走创建逻辑。第二加一个击中层过滤那些不需要被标注的物件比如纯装饰草、粒子碰撞体就不会干扰拾取了。4.4 UI输入面板与创建流程用户创建标注后弹出一个输入面板这是最常见的交互方式。面板用Unity UGUI做即可挂在Canvas上通过SetActive控制显示隐藏。public class UIManager : MonoBehaviour { public static UIManager Instance; public InputField inputField; public GameObject editorPanel; private string currentMarkerId; void Awake() { Instance this; } public void OpenEditor(string markerId) { currentMarkerId markerId; inputField.text ; editorPanel.SetActive(true); inputField.Select(); inputField.ActivateInputField(); } public void OnConfirm() { MarkerManager.Instance.SetMarkerContent(currentMarkerId, inputField.text); editorPanel.SetActive(false); } public void OnCancel() { MarkerManager.Instance.DeleteMarker(currentMarkerId); editorPanel.SetActive(false); } }有个很值得注意的小细节用户如果取消了输入必须把刚才那个已经生成到场景里的标注删掉。否则就会出现“场景里多了个空标记”的脏状态。我第一版没处理这个问题后来实测中用户取消输入的频率极高空标注一多数据里全是垃圾后面清理起来很麻烦。InputField输入中文没有问题但注意在移动端上需要检查InputField对应的Input Type设置如果是数字类型会弹不出中文键盘。这个问题在真机测试时才暴露开发期PC上完全看不出来。4.5 删除与编辑功能删除逻辑做成右键点击标注或者在选中状态下通过UI按钮触发。右键点击的识别方式在OnMouseOver里监听Input.GetMouseButtonDown(1)。但注意如果场景里同时有多个可点击对象右键事件可能会被别的脚本抢走稳妥做法是在OnMouseDown内判断按钮编号private void OnMouseDown() { if (Input.GetMouseButtonDown(1)) { MarkerManager.Instance.RequestDelete(data.id); } else if (Input.GetMouseButtonDown(0)) { UIManager.Instance.OpenEditorForEdit(data.id); } }编辑的逻辑和新建几乎一样只是打开输入框时要把现有的文字填进去并且确认时走Update而不是Create。不需要单独写一套数据流直接复用SetMarkerContent接口只是从“新建写入”变成“按ID查表写入”。5. 常见问题与排查技巧实录问题一创建出来的标注文字特别糊像贴图没有分辨率。TMP文字默认在多分辨率下需要调整字体贴图的采样模式。出现这个问题的原因通常是没有正确设置TMP的atlas尺寸特别是你用了动态字体时字体会按当前场景的像素比生成贴图放大字体后糊成一团。解决方式在TMP设置里把Atlas Resolution调高比如改成2048或4096会明显改善。另外场景灯光比较暗时可以手动把TMP材质面板里的Local Lighting关掉用全白色材质让文字自发光不受环境光照影响。问题二移动端上射线检测命中位置和手指点击位置对不上。移动端的Input.mousePosition使用习惯上和PC一致但问题是触摸屏上点击的瞬间手指往往还在移动导致最终的屏幕坐标和用户“想要的”位置偏差几个像素。解决思路不要监听GetMouseButtonDown改成监听Touch.phase TouchPhase.Began并且取touch.position而不是Input.mousePosition。另外如果手机上UI和3D场景叠加渲染需要确认你的射线起点坐标不包含UI的尺寸偏差——主要是CanvasScaler的缩放模式导致的问题UI坐标系和屏幕像素坐标系不一致时会偏差。最稳的验证方式是打日志输出屏幕坐标和世界坐标用编辑器模拟器对比一下。问题三标注的Billboard朝向在相机旋转后不刷新。这个问题的根源是代码放在Update里而相机的旋转可能发生在OnPreCull或者别的回调里且Update执行顺序比相机更新早。将朝向逻辑挪到LateUpdate一般能解决百分之八十的情况。另外一个少见的原因是多个相机的存在比如拼接大屏场景里有两台相机各渲染一半标注物只绑定了一个相机另一面的相机看过去没有朝向逻辑文字是歪的。解决办法是对所有需要渲染该标注的相机都做朝向判断或者干脆在脚本里注册到一个全局相机管理器中。问题四序列化后读取中文变成乱码或者直接报错。JsonUtility在Unity的某些旧版本里对中文支持不太好但其实多数情况下问题是编码。File.WriteAllText默认是UTF-8无BOM而有些平台特别是Windows下的一些第三方文本编辑器会以GBK打开导致看起来乱码。实际只要代码层用Encoding.UTF8读写就稳定不确认时可以在读文件后打印字符串长度看是否正常。如果依然是空的检查一下JsonUtility.FromJson是否因为类没有[System.Serializable]而返回空对象这是最常见的原因。问题五批量创建100个标注后帧率肉眼可见地下降。每个标注都是一个独立GameObject都有TMP组件这会导致DrawCall增加。优化思路第一给背景Quad用同一个图集同一材质让它们尽可能合并批次第二TMP的文本内容如果变化不频繁可以尽量复用同一个材质实例避免动态生成新材质第三如果真有上千个标注可以引入LOD策略——距离相机一定距离后把文本显示切换成小分辨率图片或者直接隐藏文字只保留一个点标记。问题六用户点击标注时总触发到地面点击两个操作冲突了。这是事件系统常见的坑。UGUI的EventSystem和3D物体的OnMouseDown会同时响应如果地面走了物理射线而UI也抢了点击事件就会冲突。解决方案在EventSystem的PhysicsRaycaster组件上设置一个单独的UILayer让3D拾取走另一个LayerMask两者互不干扰。简单粗暴但好用在标注的点击处理里加一个Delay判断如果按钮状态为“刚刚点击过UI”就跳过本次响应也能解决大部分问题。问题七坐标存下来后再次加载位置整体偏移了。这是个隐蔽的坑。如果你在制作时用运行时生成的地图而地图有随机生成逻辑比如每次启动地形微调那保存的坐标是这批地形的坐标下次加载地形变化了自然对不上。解决办法要么是固化场景种子要么是把坐标存成相对某个锚点的偏移量加载时再相对锚点还原。锚点坐标可以在保存时一并写进序列化文件加载时先实例化锚点物体再根据偏移量计算每个标注的位置。问题八编辑器模式正常打包到Android后点击没反应。大概率是PhysicsRaycaster或碰撞体的Layer没打包进去或者在移动端默认的touch input没有开启。最简单确认手段打包前在Player Settings里检查Active Input Handling是否设成了Input Manager新版项目默认是Input System Package两者API不同。如果项目同时用了两套输入系统可能导致Input.GetMouseButtonDown一直返回false。这种问题开发期不容易暴露都是在真机上踩雷。6. 数据可视化与进阶特性扩展核心链路打通之后后续的扩展方向其实非常明确。我建议优先加这几个特性距离自适应显示。标注的内容在近处很清晰远处小得看不清。可以在MarkerRenderer里根据相机距离动态调整字体大小距离越远字号越小这个由观察者自然感知但为了防止极小字在远距离产生“噪点”视觉配合一个显隐开关——距离超过阈值时隐藏文字只保留PivotIndicator小球。实现起来非常简单private void UpdateVisibility() { float dist Vector3.Distance(mainCam.transform.position, transform.position); bool visible dist distThreshold; textMesh.gameObject.SetActive(visible); backgroundQuad.SetActive(visible); }这个功能在实际使用时价值极高因为标注多了以后远看近看都要舒服否则满屏文字根本没法看。拖拽微调。允许用户在创建后拖拽标注到更精确的位置。实现方式可以通过鼠标拖拽改变MarkerRoot的坐标但注意要限制拖动的偏移范围防止用户把标注拉到意想不到的地方。UI上可以加一个“锁定位置”的按钮锁定后拖拽失效防止误操作。归类管理与筛选。给标注加一个Category字段UI上提供几个过滤按钮比如“只看设备类”或者“只看问题类”。这对标注多的项目非常实用否则满屏都是标签谁也看不清。数据层和UI层都不复杂加就是了。批量导出报告。标注本身就是数据资产。可以一键导出所有标注内容为CSV或者JSON交给其他人做问题跟进。这个功能做起来成本很低但往往能带来意外的好评因为对非技术人员来说在一个文件里看到“问题位置描述”比在编辑器里一个一个看高效得多。贴地高度修正。射线检测到目标点后可以把Y坐标稍微抬高一点避免标注文字直接嵌入模型表面。一般抬高0.3米到0.5米效果就不错。这个阈值需要根据模型尺寸调节建议做成公开参数方便不同项目微调。7. 工程化组件结构总结为了让读者能直接上手我把整个系统按模块列清楚。模块核心类职责数据层MarkerData / MarkerListWrapper / MarkerDataStore定义数据模型、序列化与反序列化、读写文件交互层MarkerRenderer / MarkerManager射线拾取、标注创建、删除、内容更新、朝向刷新UI层UIManager / MarkerEditorPanel输入框管理、编辑面板开关、内容确认与取消事件层MarkerBus 静态事件标注点击、创建、删除、内容变更的消息广播扩展层MarkerFilter / MarkerLOD / ExportTool分类筛选、距离显示控制、批量导出事件层是我后加进去的一开始觉得一个需求而已何必要搞一套事件系统。实际用下来发现标注被点击之后可能UI要刷新列表、可能数据层要保存、可能某个业务脚本要响应大家各自监听就行了不用在MarkerManager里堆一堆耦合代码。所以这个事件总线不是可有可无是真正能帮你少踩坑。8. 最终工程运行效果与验收清单整套系统跑起来后用户的典型操作流程是按住Shift点击场景→输入框弹出→输入文字确认→场景里出现一个固定在点击位置的文字标注左键点它可以再次编辑右键点它删除关闭程序再打开标注还在原来的位置。这就是一个完整闭环。验收时建议按这个清单逐项检查在新场景创建10个标注确认全部能正常显示并朝向相机。修改其中一个内容确认重新加载后内容还是修改后的。删除一个标注确认数据文件里对应记录也被清掉。在空区域点击没有碰撞体确认标注落到地面上而不是飞在半空中。把相机旋转到标注的正后方确认文字是正对相机的。批量创建50个标注观察帧率和DrawCall变化情况。多平台测试一次输入中文的稳定性PC端和移动端都要测。确定数据文件路径手动打开JSON文件确认内容可读。每一项过了这套系统基本就算“可以在真实项目里用”了。说实话做这种基础功能类工具真正考验你的不是功能本身而是边界情况处理得细不细。取消输入时的脏标注、远程射线的坐标失真、中文编码的无标签坑、输入系统不匹配这类问题只有踩过才会记得住。我写这篇文章的目的就是把这一圈坑替你们趟一遍少走点弯路。剩下的路径你都看到了按着做很快就能跑起来跑起来之后再根据自己的项目特点加你想要的花活。

相关新闻

光纤传感器参数全面仿真:建模、扫描与交叉耦合分析

光纤传感器参数全面仿真:建模、扫描与交叉耦合分析

做光纤传感研究最怕什么?答案很统一:参数太多,变量踩不完。数值孔径、纤芯半径、掺杂浓度、传感区长度、弯曲半径……随便哪个动一动,光谱就给你演一出“大变活人”。我做这个仿真课题的时候,最大的体会就是&#xff1…

2026/10/15 23:37:54 阅读更多 →
Freemarker 模板注入安全加固:重写 Configuration 与白名单解析器

Freemarker 模板注入安全加固:重写 Configuration 与白名单解析器

简介:这份资源面向仍在使用老旧 JeecgBoot 版本、无法平滑升级的 Java 开发者,提供一种低成本修复 Freemarker 模板注入漏洞的应急方案。其核心做法是直接重写 freemarker 的 Configuration 类,在实例化方法中默认加入 this.setNewBuiltinCla…

2026/10/15 23:37:54 阅读更多 →
H264/H265码流类型详解:Annex-B与AVCC格式原理、转换及避坑指南

H264/H265码流类型详解:Annex-B与AVCC格式原理、转换及避坑指南

1. 码流类型到底在聊什么刚接触音视频编解码这块的朋友,看到“H264/H265码流类型”这个词,第一反应往往是懵的。码流不就是视频数据吗,怎么还有类型之分?我当初也是这么想的,直到有一次在项目里做流媒体转发&#xff0…

2026/10/15 23:37:54 阅读更多 →

最新新闻

微电网调度MATLAB工程实现:双时间尺度MILP优化与不确定性建模

微电网调度MATLAB工程实现:双时间尺度MILP优化与不确定性建模

简介:本资源是一套面向电力系统方向本科生、研究生及科研人员的智能微电网调度算法MATLAB实现方案,聚焦可再生能源接入背景下微电网的经济性调度与稳定性优化问题。压缩包共20个文件,含18个核心MATLAB函数(.m)、1个备份…

2026/10/16 0:34:22 阅读更多 →
Windows SDK中国象棋:从双缓冲到Alpha-Beta的完整课设实战

Windows SDK中国象棋:从双缓冲到Alpha-Beta的完整课设实战

简介:基于Windows SDK的象棋程序完整工程源码包,面向C/C初学者及桌面游戏开发入门者,重点演示如何使用Win32 API与GDI实现棋盘绘制、棋子拖放、走棋规则判断和人机对弈。压缩包共38个文件,主要包含C源文件(.cpp)、头文件(.h)、图标…

2026/10/16 0:34:22 阅读更多 →
NC附件上传的本地保存与OSS上传架构设计

NC附件上传的本地保存与OSS上传架构设计

1. 项目概述:为什么“NC传输附件的本地文件保存和文件上传”是企业级系统绕不开的硬需求在某高校实验室参与一个跨部门协同管理平台开发时,我第一次被业务方拉着开了个紧急会——他们刚上线的NC(用友NCCloud)系统里,所…

2026/10/16 0:34:22 阅读更多 →
离散周期信号频谱分析:DFS原理、混叠与泄露的工程实战

离散周期信号频谱分析:DFS原理、混叠与泄露的工程实战

1. 从“周期”二字说起:为什么离散周期信号的频谱值得单独拎出来讲很多人学数字信号处理,学到“离散周期信号的频谱”这一节时,第一反应是——公式好多,但好像跟前面学的离散时间傅里叶变换(DTFT)差不多&am…

2026/10/16 0:34:22 阅读更多 →
Python安装与环境配置全指南:Windows/macOS/Linux从零到跑通项目

Python安装与环境配置全指南:Windows/macOS/Linux从零到跑通项目

简介:一份面向Python零基础学习者的图文安装指南,通过步骤拆解与截图对照,围绕Python解释器与PyCharm集成开发环境的完整搭建流程展开,帮助读者从官网下载、安装选项勾选、命令行验证到创建并运行第一个Python程序,解决…

2026/10/16 0:34:22 阅读更多 →
脑电信号模拟发生系统:为脑机接口测试打造可复现的基准数据

脑电信号模拟发生系统:为脑机接口测试打造可复现的基准数据

简介:面向脑机接口测试的高精度脑电信号模拟发生系统设计文档,聚焦医疗设备制造商、科研工作者与脑机接口开发团队的测试需求。方案针对传统信号发生器系统复杂、体积庞大、精度不足等问题,提出含数模转换、同相比例放大与信号衰减电路的模拟…

2026/10/16 0:33:22 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

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

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 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/15 0:14:36 阅读更多 →
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/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练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/15 9:49:14 阅读更多 →