使用的插件
当前Vectrosity的版本有问题,尽量用5以上的版本
整体,参数有线的材质,线的大小,已经一些UI组件
每个需要连线的点都需要挂载当前脚本方便标识
using UnityEngine; public class WireConnectionPoint : MonoBehaviour { public string pointName; // 连接点的名称 private void OnDrawGizmos() { // 在场景中可视化连接点 Gizmos.color = Color.red; Gizmos.DrawSphere(transform.position, 0.1f); } } |
实现整体逻辑代码
using System.Collections; using System.Collections.Generic; using UnityEngine; using Vectrosity; using UnityEngine.UI; public class DynamicWire : MonoBehaviour { public Material wireMaterial; // 线缆的材质 public float wireSize = 0.1f; // 线缆的半径 private MegaWire wire; // MegaWire 线缆对象 private List<WireConnectionPoint> connectionPoints; // 存储连接点的列表 public List<MegaWireSpan> activeWires; // 存储当前激活的线缆 private GameObject currentWire; // 当前正在绘制的线缆 private bool isDrawing = false; // 绘制状态 private WireConnectionPoint startPoint; // 线缆的起始连接点 private VectorLine currentLine; // 当前正在拖拽的线条 private int indexSpanNum = -1; //记录当前线段 public Button clearLinesBtn; void Start() { connectionPoints = new List<WireConnectionPoint>(); // 初始化连接点列表 activeWires = new List<MegaWireSpan>(); // 初始化激活的线缆列表 clearLinesBtn.onClick.AddListener(()=> { if (wire!=null&&wire.spans.Count>0) { wire.RemoveWire(); activeWires.Clear(); indexSpanNum = -1; } }); } int index = 0; void Update() { if (Input.GetMouseButtonDown(0)) { // 获取当前鼠标点击的连接点 WireConnectionPoint clickedPoint = GetClickedConnectionPoint(); if (clickedPoint != null) { startPoint = clickedPoint; // 设置起始点 //currentWire = new GameObject("Wire"); // 创建新的线缆对象 //AddColliderToWire(currentWire); // 添加 Collider 以便选中 //currentWire.transform.SetParent(clickedPoint.transform); // 创建新的 VectorLine currentLine = new VectorLine("WireLine", new List<Vector3>(), wireSize, LineType.Continuous, Joins.Weld); currentLine.material = wireMaterial; // 添加起始点和一个初始的终点 currentLine.points3.Add(startPoint.transform.position); // 起始点 currentLine.points3.Add(startPoint.transform.position); // 添加一个默认的终点(与起点相同) isDrawing = true; // 开始绘制状态 } } if (isDrawing && Input.GetMouseButton(0)) { // 持续更新线缆位置 UpdateWirePosition(); } if (Input.GetMouseButtonUp(0)) { if (currentLine!=null&& currentLine.points3.Count>0) { currentLine.points3.Clear(); // 清除线条点 VectorLine.Destroy(ref currentLine); }
// 完成线缆绘制 WireConnectionPoint endPoint = GetClickedConnectionPoint(); if (endPoint != null && endPoint != startPoint) { // 如果连接到另一个点,创建线缆 CreateWire(endPoint); } else { // 否则,删除未连接的线缆 //Destroy(currentWire); } isDrawing = false; // 结束绘制状态 } if (Input.GetMouseButtonDown(1)) { // 右键点击:选择并删除线条 MegaWireSpan selectedWire = GetClickedWire(); if (selectedWire != null) { DeleteWire(selectedWire); } if (indexSpanNum>=0) { //从最后一位删除 DeleteWire(activeWires[indexSpanNum]); }
} } WireConnectionPoint GetClickedConnectionPoint() { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out RaycastHit hit)) { // 检测到的连接点 return hit.collider.GetComponent<WireConnectionPoint>(); } return null; } void UpdateWirePosition() { Vector3 mousePos = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 10f)); currentLine.points3[1] = mousePos; // 动态更新终点位置 currentLine.Draw3D(); // 绘制线条 //if (currentWire != null) //{ // // 更新线缆的终点位置 // Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); // if (Physics.Raycast(ray, out RaycastHit hit)) // { // currentWire.transform.position = hit.point; // 设置线缆的位置 // } //} } void CreateWire(WireConnectionPoint endPoint) { indexSpanNum++; List<GameObject> points = new List<GameObject> { startPoint.gameObject, endPoint.gameObject }; if (wire == null) { wire = MegaWire.Create(wire, points, wireMaterial, "Wire", null, wireSize, 1.0f); //设置物理效果 wire.Mass = 0.1f; wire.stretch = 0.1f; wire.massRand = 0.1f; //wire.hidespans = false; wire.doCollisions = true; activeWires.Add(wire.spans[0]); // 将创建的线缆添加到激活列表 } else { // 重写下插件内部方法,微调下 for (int i = 0; i < points.Count - 1; i++) { GameObject pole = new GameObject();
pole.name = name + " Span Mesh " + i; pole.transform.parent = wire.gameObject.transform; MegaWireSpan span = pole.AddComponent<MegaWireSpan>(); span.start = points[i].transform; span.end = points[i + 1].transform; MeshFilter mf = pole.GetComponent<MeshFilter>(); mf.sharedMesh = new Mesh(); MeshRenderer mr = pole.GetComponent<MeshRenderer>(); Material[] mats = new Material[1]; mats[0] = wire.material; mr.sharedMaterials = mats; span.mesh = mf.sharedMesh; span.mesh.name = name + " Wire Mesh " + i; span.Init(wire, indexSpanNum); Debug.Log("indexSpanNum=>" + indexSpanNum);
wire.spans.Add(span); activeWires.Add(span); // 将创建的线缆添加到激活列表 wire.poles.Add(points[i + 1].transform);
} } wire.RebuildWire(); //AddColliderToWire(wire.gameObject); // 添加 Collider 以便选中 } MegaWireSpan GetClickedWire() { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out RaycastHit hit)) { // 检查点击的对象是否在 activeWires 列表中 //if (activeWires.Find(x=>x.gameObject==hit.collider.gameObject)) //{ // return hit.collider.gameObject; //} Debug.Log("hit=>"+ hit.collider.gameObject.name); foreach (var item in activeWires) { if (item.gameObject== hit.collider.gameObject) { return item; } } } return null; } public void DeleteWire(MegaWireSpan wireToDelete) { if (activeWires.Contains(wireToDelete)) { Debug.Log("wireToDelete==>" + activeWires.IndexOf(wireToDelete)); indexSpanNum--; wire.RemoveWire(wireToDelete); // 删除线缆 activeWires.Remove(wireToDelete); } } } |
需要注意的是插件的代码是开源的,上述代码有部分功能需要在插件代码里去自定义实现
插件 MegaWire.cs 在Create函数中找到span.Init的方法
static public MegaWire Create(MegaWire wire, List<GameObject> objs, Material mat, string name, MegaWire copyfrom, float wiresize, float str) { ... //暂时只需要一条 后期多条会报错需要修改 span.Init(wire,0); ... } |
在修改完MegaWire里初始化线段的方法同时也需要给线段的脚本MegaWireSpan.cs增加一个标识符,方便日后操作指定的某一条线段(目前功能代码暂时不需要加也能正常运行)
public class MegaWireSpan : MonoBehaviour { ... public int spanId; // 唯一标识符
... public void Init(MegaWire wire,int id) { spanId = id; ... } } |
同时MegaWire.cs 代码里还需要自定义删除的方法,加在代码最后面就可以了,前面代码调用过单击右键就可以按顺序删除已添加的线段
public class MegaWire : MonoBehaviour { ...
// 自定义指定删除方法 //清空所有 public void RemoveWire() { // 清除 wire 内部的 span 及相关数据 foreach (var span in spans) { if (span != null) { Destroy(span.gameObject); } } spans.Clear(); // 清除 poles 和 connections poles.Clear(); connections.Clear(); // 销毁 MegaWire 的 GameObject Destroy(this.gameObject); } //清除单个线段 public void RemoveWire(MegaWireSpan span) { spans.Remove(span); Destroy(span.gameObject); } } |
最后效果展示
前面写的DynamicWire.cs有个地方可以初始化线段物理效果,根据需求可以自己设置值的大小
public class DynamicWire : MonoBehaviour { ... void CreateWire(WireConnectionPoint endPoint) { //设置物理效果 wire.Mass = 0.1f; wire.stretch = 0.1f; wire.massRand = 0.1f; } } |
现在属于线段紧绷的物理状态,因为我们把值设置的很小
如果使用插件默认的值,只需要把我们刚刚设置的值注释掉就可以了
public class DynamicWire : MonoBehaviour { ... void CreateWire(WireConnectionPoint endPoint) { //设置物理效果 // wire.Mass = 0.1f; //wire.stretch = 0.1f; //wire.massRand = 0.1f; } } |
很明显,受到物理效果,质量和重力等一系列参数增大后,线段就会变软下垂的物理效果。
这就是Vectrosity和MegaWire两个插件结合使用的3D画线功能
(注:当前使用的unity版本是2019.4.5的,其他版本暂未测试)