当前位置: 首页 > news >正文

Unity RTS游戏框选系统实战:从UI搭建到视觉反馈完整实现

1. 项目概述:从“点选”到“框选”的RTS核心交互进化

在上一篇文章里,我们聊透了RTS游戏中单位点选、编队和基础移动的逻辑。那就像是给一支军队任命了指挥官,下达了“前进”的指令。但任何一个合格的指挥官都知道,在瞬息万变的战场上,只靠鼠标一个个去点选士兵是低效且致命的。真正的战场指挥,需要的是“划一片区域,这片区域里的所有单位,听我号令”。这就是我们今天要深入骨髓去实现的**框选(Box Selection)**功能。

框选不仅仅是UI界面上一个半透明的矩形框那么简单。它是连接玩家意图与游戏世界内数百个独立单位实体的桥梁,是RTS游戏操作感的基石。一个响应迅速、逻辑清晰、视觉效果流畅的框选系统,能极大提升游戏的策略性和操作爽快感。想象一下《星际争霸》中框选一队机枪兵散开阵型,或是《帝国时代》里框选农民集体采集资源,其背后都是一套精心设计的框选逻辑在支撑。

本篇文章,我们将完全聚焦于“生成选择框”这一核心环节。我们将从零开始,构建一个在3D游戏世界中,根据鼠标拖拽轨迹,动态生成并渲染一个贴合视角、边界清晰的选择框。这不仅是UI的绘制,更涉及屏幕空间到世界空间的坐标转换、几何体的动态构建与渲染,以及为后续的单位选择逻辑打下坚实的数据基础。无论你是Unity的初学者,还是想深化对RTS核心机制理解的开发者,跟随这篇实战解析,你都能获得一套可直接复用到自己项目中的、工业级的框选解决方案。

2. 框选系统的核心设计思路与架构

在动手写代码之前,我们必须想清楚框选系统到底要做什么,以及如何优雅地实现它。一个健壮的框选系统,可以拆解为三个核心阶段:输入捕获、几何生成、逻辑判定。本篇我们解决前两个,为第三阶段铺平道路。

2.1 核心流程拆解:从鼠标按下到屏幕矩形

框选的本质,是玩家用鼠标在屏幕上定义了一个二维的矩形区域。我们的系统需要将这个二维区域,转化为游戏世界中的一个“选择体”,并找出其中的单位。流程如下:

  1. 按下鼠标:记录鼠标按下的初始屏幕坐标点(startPos)。
  2. 拖动鼠标:实时获取当前鼠标的屏幕坐标点(currentPos)。
  3. 生成矩形:根据startPoscurrentPos,计算出一个屏幕空间的矩形。
  4. 投影到世界:将这个屏幕矩形投影到游戏世界的近裁切面和远裁切面,形成一个3D空间中的“视锥体切片”。更简化且常用的方法是,我们只关心这个矩形在游戏世界水平面(如y=0的地面)或单位所在高度上的投影区域。
  5. 检测单位(下篇重点):使用物理系统(如Physics.OverlapBoxPhysics.OverlapArea)检测投影区域内的所有单位。

我们的首要任务,就是清晰、可视化地将第1-3步的“屏幕矩形”呈现给玩家,这就是“生成选择框”。

2.2 方案选型:如何绘制这个动态框?

在Unity中,动态绘制一个2D UI矩形框,主要有以下几种主流方案,各有优劣:

方案一:使用Unity UI (uGUI) 的Image组件这是最直观的方法。创建一个全屏的Canvas,下面放一个Image,将其Image Type设置为Sliced(使用九宫格精灵以避免拉伸模糊),然后通过代码控制其rectTransformoffsetMinoffsetMax,或者anchoredPositionsizeDelta来改变位置和大小。

  • 优点:简单,易上手,与UI系统集成度高,易于处理屏幕自适应。
  • 缺点Canvas的渲染可能带来额外的Overdraw;框选框被限制在UI层,如果游戏有复杂的UI层级可能需要额外处理;性能在极端情况下(如需要大量UI元素时)可能不如直接渲染。

方案二:使用GL类或Graphics.DrawMesh进行即时模式渲染这是更底层、更灵活的图形API。GL类可以用于在OnPostRender或特定摄像机的渲染事件中绘制简单的几何图形(如线段)。Graphics.DrawMesh则允许你动态绘制一个网格。

  • 优点:性能极高,完全可控,不依赖UI系统,绘制出的框可以存在于世界空间或屏幕空间。
  • 缺点:代码相对复杂,需要自己处理网格生成、材质和渲染队列,对初学者不友好。

方案三:使用一个简单的3D Quad(平面)与动态材质创建一个非常简单的平面网格,附着在一个GameObject上。通过脚本动态更新这个网格的四个顶点坐标,使其构成一个矩形,并应用一个带有透明和边框效果的Shader材质。

  • 优点:概念清晰(直接操作世界空间顶点),性能较好,框选框可以轻松融入3D世界(如带有透视效果)。
  • 缺点:需要处理屏幕坐标到世界坐标的转换,以确定矩形的四个角在世界中的位置,逻辑上比UI方案稍绕。

我们的选择与理由对于一款典型的RTS游戏,框选框是一个需要频繁更新、且要求响应速度极快的视觉反馈。UI方案在开发效率、与现有UI系统的兼容性以及屏幕坐标处理的简便性上具有明显优势。RTS游戏的界面通常复杂,框选框作为界面交互的一部分,使用UI系统管理在架构上更清晰。只要对Canvas进行合理规划(例如使用一个专用的、渲染模式为Screen Space - OverlayCanvas,并控制其渲染顺序),性能完全不是问题。

因此,本案例我们将采用方案一(uGUI)作为实现基础。我们会深入细节,不仅实现功能,还会优化体验,比如解决框选反向拖动时的矩形显示问题,以及实现一个美观的半透明填充边框效果。

3. 构建动态选择框:从UI搭建到代码驱动

理论清晰了,现在开始实战。我们一步步构建出那个跟随鼠标拖拽而变化的半透明选择框。

3.1 UI层级搭建与组件配置

首先,在Unity编辑器中完成UI部分的搭建。

  1. 创建一个新的Canvas。将Render Mode设置为Screen Space - Overlay,这样它就会始终绘制在最上层。为了性能,可以勾选Additional Shader Channels下的TexCoord1,Normal,Tangent(虽然本例不一定需要,但好习惯)。
  2. Canvas下创建一个空的GameObject,命名为SelectionBox。它将作为我们框选框的根节点。
  3. SelectionBox下创建一个Image组件,命名为BoxVisual。这就是我们框选框的视觉表现。
  4. 配置BoxVisualImage组件:
    • Source Image: 这里需要一个“九宫格(Sliced)”精灵来确保边框在任何缩放下都不模糊。你可以使用Unity内置的白色精灵(WhiteSprite),或者自己制作一个简单的边框贴图。关键步骤:在Project面板选中你的精灵,在Inspector中设置Sprite ModeMultiple,然后点击Sprite Editor。在编辑器中,将绿色的边框线向内拉,定义出九宫格的内部区域(例如每边设置10像素)。这样,当Image拉伸时,只有中间部分被拉伸,边框保持原样。
    • Image Type: 设置为Sliced。这是实现不模糊边框的核心。
    • Color: 设置为一个半透明的颜色,比如RGBA(0, 120, 255, 0.2)。A值(Alpha)控制透明度。
  5. 调整BoxVisualRectTransform
    • AnchorPivot都设置为(0, 0),即左下角。这会让我们的坐标计算变得非常直观——矩形的起点就是左下角,尺寸就是宽和高。
    • 初始时,将其WidthHeight都设为0,PosXPosY设为0,让它处于不可见状态。

注意:使用Sliced模式时,如果矩形尺寸小于你定义的九宫格边框总和,它可能无法正确显示。在代码中,我们可以通过判断尺寸来避免这个问题,或者使用Tiled模式作为备选。

3.2 核心脚本:SelectionBoxController

接下来,我们创建核心脚本SelectionBoxController.cs,并将其挂载到SelectionBox游戏对象上。

using UnityEngine; using UnityEngine.UI; public class SelectionBoxController : MonoBehaviour { [Header("UI References")] [SerializeField] private RectTransform boxVisualRectTransform; // 拖拽赋值BoxVisual [SerializeField] private Image boxVisualImage; // 拖拽赋值BoxVisual上的Image组件 [Header("Visual Settings")] [SerializeField] private Color boxColor = new Color(0f, 0.47f, 1f, 0.2f); // 默认半透明蓝色 // 框选状态 private bool isSelecting = false; private Vector2 startScreenPosition; private Vector2 currentScreenPosition; // 屏幕尺寸缓存,用于坐标转换 private Vector2 screenSize; void Start() { InitializeBox(); screenSize = new Vector2(Screen.width, Screen.height); } void Update() { HandleSelectionInput(); UpdateBoxVisual(); } /// <summary> /// 初始化框选框,使其隐藏 /// </summary> private void InitializeBox() { if (boxVisualImage != null) { boxVisualImage.color = boxColor; boxVisualImage.enabled = false; // 初始隐藏 } if (boxVisualRectTransform != null) { boxVisualRectTransform.sizeDelta = Vector2.zero; } } /// <summary> /// 处理鼠标输入,确定框选开始、进行和结束 /// </summary> private void HandleSelectionInput() { // 鼠标左键按下:开始框选 if (Input.GetMouseButtonDown(0)) { StartSelection(); } // 鼠标左键抬起:结束框选(选择逻辑在下一篇) else if (Input.GetMouseButtonUp(0)) { EndSelection(); } // 鼠标左键按住且处于框选状态:更新当前鼠标位置 if (isSelecting) { currentScreenPosition = Input.mousePosition; // 限制当前坐标在屏幕范围内(可选,但更健壮) currentScreenPosition.x = Mathf.Clamp(currentScreenPosition.x, 0, screenSize.x); currentScreenPosition.y = Mathf.Clamp(currentScreenPosition.y, 0, screenSize.y); } } /// <summary> /// 开始框选,记录起始点 /// </summary> private void StartSelection() { // 这里可以添加判断,例如当点击在UI上时不开始框选 // if (EventSystem.current.IsPointerOverGameObject()) return; isSelecting = true; startScreenPosition = Input.mousePosition; currentScreenPosition = startScreenPosition; // 初始时当前位置就是起始点 if (boxVisualImage != null) { boxVisualImage.enabled = true; // 显示框选框 } } /// <summary> /// 结束框选,隐藏框选框 /// </summary> private void EndSelection() { if (!isSelecting) return; isSelecting = false; if (boxVisualImage != null) { boxVisualImage.enabled = false; // 隐藏框选框 } if (boxVisualRectTransform != null) { boxVisualRectTransform.sizeDelta = Vector2.zero; // 重置大小 } // 触发单位选择事件(下一篇实现) // ProcessSelection(); } /// <summary> /// 根据起始点和当前点,更新框选框的视觉表现(位置和大小) /// </summary> private void UpdateBoxVisual() { if (!isSelecting || boxVisualRectTransform == null) return; // 计算矩形左下角(原点)和尺寸 // RectTransform的锚点在左下角(0,0),所以原点就是矩形左下角的屏幕坐标。 // 我们需要确保无论鼠标从哪个方向拖动,计算出的矩形原点都是左下角,尺寸都是正数。 float left = Mathf.Min(startScreenPosition.x, currentScreenPosition.x); float right = Mathf.Max(startScreenPosition.x, currentScreenPosition.x); float bottom = Mathf.Min(startScreenPosition.y, currentScreenPosition.y); float top = Mathf.Max(startScreenPosition.y, currentScreenPosition.y); // 矩形的原点(左下角)和宽高 Vector2 boxOrigin = new Vector2(left, bottom); Vector2 boxSize = new Vector2(right - left, top - bottom); // 设置RectTransform的位置和大小 // 注意:屏幕坐标原点在左下角,RectTransform在Overlay Canvas下的原点也在左下角,所以可以直接赋值。 boxVisualRectTransform.anchoredPosition = boxOrigin; boxVisualRectTransform.sizeDelta = boxSize; // 可选:如果矩形太小,可以隐藏或显示一个最小视觉反馈 if (boxSize.magnitude < 10f) // 例如小于10像素认为点击 { boxVisualImage.enabled = false; } else { boxVisualImage.enabled = true; } } // 提供一个公共属性,供其他脚本(如下一篇的选择逻辑)获取当前的屏幕空间矩形 public Rect? CurrentScreenRect { get { if (!isSelecting) return null; float left = Mathf.Min(startScreenPosition.x, currentScreenPosition.x); float right = Mathf.Max(startScreenPosition.x, currentScreenPosition.x); float bottom = Mathf.Min(startScreenPosition.y, currentScreenPosition.y); float top = Mathf.Max(startScreenPosition.y, currentScreenPosition.y); return new Rect(left, bottom, right - left, top - bottom); } } }

3.3 代码逻辑深度解析

这段代码是框选视觉反馈的核心,有几个关键点需要深入理解:

  1. 坐标系统统一:我们全程使用屏幕坐标(Input.mousePosition)。在Screen Space - Overlay模式的Canvas下,RectTransformanchoredPositionsizeDelta也直接对应屏幕像素坐标,这使得转换非常简单直接。anchoredPosition是UI元素**枢轴点(Pivot)相对于其锚点(Anchor)**的偏移量。因为我们把锚点和枢轴都设为了(0,0)即左下角,所以anchoredPosition的(x, y)就是矩形左下角距离屏幕左下角的像素距离。

  2. 矩形标准化计算UpdateBoxVisual方法中的Mathf.MinMathf.Max是精髓所在。它确保了无论玩家是从左上往右下拖,还是从右下往左上拖,亦或是任意方向拖,我们计算出的boxOrigin始终是矩形的左下角,boxSize的宽高始终是正数。这是正确显示矩形框的前提。

  3. 状态管理isSelecting布尔变量清晰地划分了框选的“开始-进行-结束”三个状态。在EndSelection中,我们不仅隐藏了UI,还将大小重置为零。这是一个好习惯,可以避免在极快点击时,上一帧的矩形残影被短暂看到。

  4. 公共接口:我们暴露了一个CurrentScreenRect属性,返回一个可空的Rect。这为下一篇要实现的单位选择逻辑提供了完美的数据接口。选择逻辑只需要查询这个属性,如果非空,就表示当前存在一个有效的屏幕选择矩形,可以将其用于物理检测。

4. 高级优化与视觉增强

基础功能已经完成,但一个精致的RTS游戏,框选框的体验也需要打磨。下面介绍几个提升体验的进阶技巧。

4.1 解决UI遮挡:区分框选与点击UI

在游戏中,玩家可能会点击按钮、滑动滚轮。我们不希望在这些UI元素上操作时触发框选。Unity的EventSystem提供了检测鼠标是否在UI上的方法。

  1. 确保场景中有EventSystem(通常创建Canvas时会自动生成)。
  2. StartSelection方法开头添加检查:
    private void StartSelection() { // 检查鼠标是否在UI元素上,如果是则忽略框选 if (EventSystem.current != null && EventSystem.current.IsPointerOverGameObject()) { return; } // ... 原有逻辑 }

    注意IsPointerOverGameObject()默认对触摸和鼠标左键有效。如果你使用其他鼠标按键框选,可能需要传递指针ID。对于鼠标,通常使用-1作为默认指针ID,或者使用EventSystem.current.currentInputModule的相关方法。

4.2 创建更美观的框选框材质

默认的UI Image颜色填充可能显得有些平淡。我们可以创建一个简单的自定义Shader或使用材质,来实现“只有边框,内部半透明填充”的效果,这更符合传统RTS的审美。

方法A:使用Sprite并配合Image的Image TypeSliced这是最简单的方法,如前所述,制作一个边框清晰的九宫格精灵图。例如,一个纯白色边框、中间透明的PNG图片。在Sprite Editor中设置好九宫格边界后,应用到Image上,并设置颜色。调整边框粗细可以通过修改九宫格的边界大小来实现。

方法B:使用简单的Shader Graph(URP/HDRP)或编写Shader如果你想实现动态颜色、虚线边框等更复杂的效果,Shader是更好的选择。

  1. 创建一个新的Unlit Shader Graph(以URP为例)。
  2. 主要逻辑是:根据UV坐标,判断片段是否靠近边缘(例如,UV的x或y小于0.05或大于0.95),如果是,则输出边框颜色,否则输出一个半透明的填充颜色。
  3. 将这个Shader保存,并创建一个对应的Material。
  4. BoxVisualImage组件的Material属性指定为这个新Material。

这里提供一个简易的Shader代码示例(适用于Built-in Render Pipeline):

Shader \"UI/SelectionBox\" { Properties { _FillColor (\"Fill Color\", Color) = (0, 0.47, 1, 0.2) _BorderColor (\"Border Color\", Color) = (0, 0.8, 1, 0.8) _BorderWidth (\"Border Width\", Range(0, 0.5)) = 0.02 } SubShader { Tags { \"Queue\"=\"Transparent\" \"RenderType\"=\"Transparent\" \"IgnoreProjector\"=\"True\" } Blend SrcAlpha OneMinusSrcAlpha ZWrite Off Cull Off Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include \"UnityCG.cginc\" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; fixed4 _FillColor; fixed4 _BorderColor; float _BorderWidth; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.uv; return o; } fixed4 frag (v2f i) : SV_Target { // 判断当前片段是否在边框区域内 float2 border = smoothstep(0, _BorderWidth, i.uv); border *= smoothstep(1, 1 - _BorderWidth, i.uv); // 如果uv的x或y靠近0或1,则border因子会变小 float borderFactor = (1 - border.x * border.y); // 混合填充色和边框色 fixed4 col = lerp(_FillColor, _BorderColor, borderFactor); // 另一种更清晰的边框计算: // if (i.uv.x < _BorderWidth || i.uv.x > 1 - _BorderWidth || i.uv.y < _BorderWidth || i.uv.y > 1 - _BorderWidth) // return _BorderColor; // else // return _FillColor; return col; } ENDCG } } }

将这个Shader赋给一个材质,再将该材质赋给BoxVisualImage组件,你就能得到一个可自定义填充色、边框色和边框宽度的漂亮选择框。

4.3 性能考量与对象池思想

虽然单个UI Image的性能消耗微乎其微,但在大型项目中养成好习惯很重要。我们的SelectionBoxController在每次拖拽时都会频繁调用UpdateBoxVisual,并修改RectTransform的属性。这是UI系统的标准操作,性能足够好。

但我们可以引入“对象池”的思想来应对未来可能更复杂的需求,比如多选框选(魔兽争霸3中英雄物品栏的框选样式)或框选框的动画效果(如框选完成时的闪烁)。我们可以:

  • 在初始化时创建好框选框的视觉对象,而不是动态实例化/销毁。
  • 通过Enable/Disable来控制显示,而不是反复创建新的UI元素。
  • 我们的当前实现已经符合这一思想:在Start中初始化,在StartSelectionEndSelection中启用/禁用。

5. 常见问题排查与调试技巧

即使按照步骤操作,你也可能会遇到一些意想不到的问题。这里记录了几个常见的坑和解决方法。

5.1 框选框不显示或显示异常

  • 检查Canvas渲染模式:确保CanvasRender ModeScreen Space - Overlay。如果是World Space,你需要将屏幕坐标转换为Canvas下的本地坐标,复杂得多。
  • 检查RectTransform锚点:确认BoxVisual的锚点(Anchors)和枢轴(Pivot)都设置为(0,0)。如果锚点拉伸,anchoredPositionsizeDelta的行为会完全不同。
  • 检查Image组件和材质:确保BoxVisual上的Image组件被启用,且Source ImageMaterial有效。如果是自定义Shader,检查Shader是否编译成功,属性是否正确赋值。
  • 调试坐标:在UpdateBoxVisual方法中添加Debug.Log,打印出boxOriginboxSize的值。确保它们在你拖拽时是合理的正数(例如,原点在屏幕内,尺寸大于0)。如果出现负数,检查Mathf.Min/Max计算逻辑。

5.2 框选框与鼠标位置不同步或抖动

  • 输入帧率与渲染帧率Input.mousePositionUpdate中获取,而UI更新可能在Update之后。Unity的UI系统通常能很好地处理,但如果感觉不同步,可以尝试在LateUpdate中调用UpdateBoxVisual
  • 坐标转换错误:牢记我们使用的是屏幕像素坐标。如果你不小心将鼠标坐标除以了屏幕宽高进行了归一化(得到0-1的范围),那么赋值给anchoredPosition时就会错得离谱。
  • Canvas Scaler的影响:如果你的Canvas使用了Canvas Scaler进行分辨率自适应(这是推荐做法),anchoredPositionsizeDelta仍然是基于参考分辨率的像素值,而不是屏幕实际像素。这通常不是问题,因为鼠标坐标Input.mousePosition是屏幕实际像素,而RectTransform的屏幕坐标转换由Unity自动处理。但为了绝对精确,你可以使用RectTransformUtility.ScreenPointToLocalPointInRectangle将屏幕坐标转换到目标RectTransform的本地坐标空间。对于简单的Overlay Canvas,我们当前的方法在大多数情况下工作良好。

5.3 框选时误触发其他操作(如摄像机移动)

这是RTS游戏常见的输入冲突。通常的解决方案是输入优先级分层

  1. StartSelection中严格判定:如前所述,通过EventSystem.current.IsPointerOverGameObject()排除UI点击。
  2. 使用修饰键:例如,约定只有按住CtrlShift键时的拖拽才是框选,否则是摄像机拖拽。这需要在StartSelection中检查Input.GetKey(KeyCode.LeftControl)
  3. 区域判定:例如,只在屏幕中间的游戏视图区域(非UI边缘)允许框选。
  4. 状态机:实现一个更全局的输入状态机。例如,定义一个GameInputMode枚举,包含None,CameraPan,BoxSelecting,UnitCommand等状态。任何输入处理前先检查当前模式。框选逻辑只在BoxSelecting模式下生效,而进入该模式可能需要特定的鼠标动作(如左键拖拽且未点击UI)。

5.4 在Editor中测试的注意事项

在Unity Editor中运行游戏时,如果你的Game视图窗口不是全屏,鼠标坐标的原点((0,0))是Game视图的左下角,而不是整个显示器的左下角。这是正常行为。Input.mousePosition返回的是相对于当前活动窗口(Game视图)的像素坐标。当你发布成独立应用后,坐标就是相对于整个游戏窗口的。

至此,一个响应灵敏、视觉清晰、代码健壮的RTS游戏动态选择框已经完整实现。它静静地躺在屏幕之上,等待着玩家的拖拽指令。在下一篇中,我们将为这个视觉框注入灵魂:将屏幕上的这个矩形区域,转化为游戏世界里对单位实体的精确选择。我们将深入探讨如何将屏幕矩形转换为世界空间的检测区域,如何使用物理系统高效地进行单位筛选,并处理多单位、分层选择等复杂情况。

http://www.jsqmd.com/news/1346358/

相关文章:

  • 2026年成都工业热电偶与PT100温度变送器厂家怎么选?高评价厂商对比分析 - 优质品牌商家
  • Docker 实战系列:接入生产场景,快速拉起服务
  • 无货源怎么一键下单?抖掌柜助力抖店商家简化货源采购流程 - 抖掌柜一键下单
  • 如何在VirtualBox的openKylin虚拟机下加载U盘(v0.1.0)
  • LVMH创新奖揭秘:AI重塑奢侈品供应链与品牌可见性
  • GRETNA图论网络分析工具箱:让复杂的脑网络分析变得如此简单!
  • 海龟汤游戏 App 开发实录:用 HarmonyOS ArkTS 构建推理互动平台
  • 电感器原理
  • 新版的百度智能云控制台-应用接入-创建应用的步骤 + 401或403的错,需要在预置推理服务中开通模型调用
  • 基于LabVIEW与NI myRIO的工业视觉边缘检测系统实战指南
  • 构建Godot GDScript自动化工作流:独立工具链gdtoolkit详解
  • Unity WebGL全屏模式中文输入难题:从原理到实战的完整解决方案
  • Spark SQL语法执行顺序
  • 三明|2026 彩钢瓦翻新、彩钢瓦防水、彩钢瓦补漏、彩钢瓦除锈、彩钢瓦喷漆、金属屋面翻新、钢结构屋面防水、厂房屋面除锈喷漆、屋顶彩钢瓦修缮合作方甄选避坑指南 - 本地便民网
  • 无货源一件发货步骤详解,抖掌柜助力抖店商家高效处理订单 - 抖掌柜一键下单
  • Qt软件开关等按钮点击时,加入权限验证的一种方案
  • 终极免费NTFS解决方案:Nigate让Mac完美读写Windows硬盘
  • LangChain核心概念,Chain、Agent、Tool、Memory、Retriever一文搞懂
  • 莆田|2026 彩钢瓦翻新、彩钢瓦防水、彩钢瓦补漏、彩钢瓦除锈、彩钢瓦喷漆、金属屋面翻新、钢结构屋面防水、厂房屋面除锈喷漆、屋顶彩钢瓦修缮合作方甄选避坑指南 - 本地便民网
  • 2026年重庆PVC管源头厂家梳理:渝腾电力产品及服务信息汇总 - 董不懂啊
  • 传统服装厂数字化转型:从代工到自主品牌的实战路径
  • 使用YOLO8训练一个自己的模型
  • ROS2+Gazebo+ArduSub构建高保真水下机器人仿真平台
  • 国有房企人才档案纸质化难更新?华恒智信成功案例
  • 2026年靠谱的建筑消防竣工检测、成都消防评估、医院消防安全评估公司怎么选?本地机构综合参考指南 - 优质品牌商家
  • 综合实践:企业网站上云部署实践
  • 【ROS2+深度相机】Orbbec相机时图像传输延迟显著 (DDS数据分发服务配置)
  • 无货源一键铺货怎么做?抖掌柜助力抖店商家高效完成货源上架运营 - 抖掌柜一键下单
  • 原神成就数据导出终极指南:快速免费备份你的游戏记录
  • Unity集成本地大模型:Ollama+DeepSeek实现智能NPC对话系统