public float distance; //距离起点的长度 public PBCircleNode(PBCircle _circle, Vector3 _position, Vector3 _tangent, float _angle) { circle = _circle; position = _position; tangent = _tangent; angle = _angle; }
public PBCircleNode(PBCircleNode node) { circle = node.circle; position = node.position; tangent = node.tangent; angle = node.angle; }
private void CreateTrack() { if (splineTrack == null) { EditorUtility.DisplayDialog(null, "请把该窗口关闭,重新选择履带!", "OK"); return; } if (circleObjects.Count == 0) { EditorUtility.DisplayDialog(null, "请选择圆!!!", "OK"); return; } if (curveType == PBCircleCurveType.OneCircle && circleObjects.Count > 1) { EditorUtility.DisplayDialog(null, "单圆履带只能选择一个圆!!!", "OK"); return; } if (curveType == PBCircleCurveType.Line && circleObjects.Count < 2) { EditorUtility.DisplayDialog(null, "排排站履带要选择至少两个圆!!!", "OK"); return; } if (curveType == PBCircleCurveType.Enclosed && circleObjects.Count < 3) { EditorUtility.DisplayDialog(null, "包围圈履带要选择至少三个圆!!!", "OK"); return; } Vector3 normal = circleObjects[0].transform.forward; for (int i = 1; i < circleObjects.Count; i++) { if (circleObjects[i].transform.forward != normal) { EditorUtility.DisplayDialog(null, "所有的圆必须要在同一个平面,forward方向保持一致!!!", "OK"); return; } } PBCircle[] circles = new PBCircle[circleObjects.Count]; for (int i = 0; i < circles.Length; i++) { float radius = 1f; if (circleObjects[i].name.Contains("40")) { radius = Gear40Radius; } else if (circleObjects[i].name.Contains("80")) { radius = Gear80Radius; } circles[i] = new PBCircle(circleObjects[i].transform, radius); } splineTrack.SetCircles(circles, curveType); EditorUtility.SetDirty(splineTrack); }
public void GetBottomMidSample(out Vector3 position, out Vector3 tangent, out Vector3 emit, out float distance) { //单圆 if (curveType == PBCircleCurveType.OneCircle) { PBCircleNode node = nodes.First.Value; Vector3 r = -node.circle.up.normalized; position = node.circle.center + r * node.circle.radius; tangent = -node.tangent; emit = r; distance = 0.5f * nodes.Last.Value.distance; return; } PBCircleNode node1; PBCircleNode node2; if (curveType == PBCircleCurveType.Line) { int half = (nodes.Count - 1) / 2; int index = half + half / 2; node1 = nodes.ElementAt(index - 1); node2 = nodes.ElementAt(index); } else { int half = (nodes.Count - 1) / 2; node1 = nodes.ElementAt(half - 1); node2 = nodes.ElementAt(half); } if (node1.ConnectLine(node2)) { position = Vector3.Lerp(node1.position, node2.position, 0.5f); tangent = node1.tangent; emit = (node1.position - node1.circle.center).normalized; distance = 0.5f * (node1.distance + node2.distance); } else { PBCircle circle = node1.circle; float angle = node1.angle + 0.5f * node1.deltaAngle(node2); Vector3 r = Quaternion.AngleAxis(angle, circle.forward) * circle.up; r.Normalize(); position = circle.center + r * circle.radius; tangent = Quaternion.AngleAxis(angle, circle.forward) * (-circle.right); emit = r; distance = 0.5f * (node1.distance + node2.distance); } }
private static PBCircleNode[] CalculateTangentLines(PBCircle c1, PBCircle c2) { Vector3 o1o2 = c2.center - c1.center; float o1o2_distance = o1o2.magnitude; o1o2.Normalize(); float angle;// 90-圆心连线与公切线的夹角 if (Mathf.Approximately(c1.radius, c2.radius)) { angle = 90; } else { float r1 = Mathf.Max(c1.radius, c2.radius); float r2 = Mathf.Min(c1.radius, c2.radius); float x = o1o2_distance * r1 / (r1 - r2); angle = Mathf.Acos(r1 / x) * Mathf.Rad2Deg; if (c1.radius < c2.radius) { angle = 180 - angle; } } Vector3[] Rs = new[] { Quaternion.AngleAxis(angle, c1.forward) * o1o2, Quaternion.AngleAxis(angle, -c1.forward) * o1o2, }; PBCircleNode[] nodes = new PBCircleNode[4]; for (int i = 0; i < Rs.Length; i++) { PBCircleNode n1, n2; //第 i 条公切线的c1、c2上的点 Vector3 r = Rs[i]; Vector3 p = c1.center + r * c1.radius; Vector3 tangent = Quaternion.AngleAxis(90, c1.forward) * r; n1 = new PBCircleNode(c1, p, tangent, GetAngle(c1, r)); p = c2.center + r * c2.radius; n2 = new PBCircleNode(c2, p, tangent, GetAngle(c2, r)); nodes[i * 2] = n1; nodes[i * 2 + 1] = n2; } return(nodes); }
/// <summary> /// 通过距离起点的距离,获取线上的点的坐标、斜率、圆心到坐标的单位向量(垂直于斜率) /// </summary> public void GetSampleAtDistance(float distance, out Vector3 position, out Vector3 tangent, out Vector3 emit) { LinkedListNode <PBCircleNode> listNode = nodes.First; PBCircleNode startNode = null; PBCircleNode endNode = null; while (listNode != null) { if (distance < listNode.Value.distance) { startNode = listNode.Previous.Value; endNode = listNode.Value; break; } listNode = listNode.Next; } if (startNode == null) { throw new ArgumentException(string.Format("Distance must be less than curve length. Curve length: {0}, distance: {1}", nodes.Last.Value.distance, distance)); } float t = (distance - startNode.distance) / (endNode.distance - startNode.distance); if (startNode.ConnectLine(endNode)) { //直线 position = Vector3.Lerp(startNode.position, endNode.position, t); tangent = startNode.tangent; emit = (startNode.position - startNode.circle.center).normalized; } else { //圆弧 PBCircle circle = startNode.circle; float angle = startNode.angle + t * startNode.deltaAngle(endNode); Vector3 r = Quaternion.AngleAxis(angle, circle.forward) * circle.up; r.Normalize(); position = circle.center + r * circle.radius; tangent = Quaternion.AngleAxis(angle, circle.forward) * (-circle.right); emit = r; } }
private static float GetAngle(PBCircle circle, Vector3 dir) { float dot = Mathf.Clamp(Vector3.Dot(circle.up, dir), -1, 1); float angle = Mathf.Acos(dot) * Mathf.Rad2Deg; Vector3 cross = Vector3.Cross(circle.up, dir); if (Vector3.Dot(cross, circle.forward) < 0) { angle = -angle; } if (angle < 0) { angle += 360; } return(angle); }
protected override void LoadString(string data) { this.ShowWindow = false; string realData = data.Substring("track:".Length); string[] values = realData.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); Spacing = float.Parse(values[0]); CurveType = (PBCircleCurveType)int.Parse(values[1]); Deviation = values.Length >= 4 ? float.Parse(values[3]) : 0; values = values[2].Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); PBAnimNode[] blocks = GameObject.FindObjectsOfType <PBAnimNode>(); PBCircle[] circles = new PBCircle[values.Length / 2]; for (int i = 0; i < circles.Length; i++) { PBAnimNode block = blocks.FirstOrDefault(b => string.Equals(b.pbID, values[i * 2])); if (block == null) { Debug.LogError(">>>>>履带:找不到齿轮,pbID: " + values[i * 2]); } if (block.transform.localScale != Vector3.one) { Debug.LogError(">>>>>履带:齿轮gameobject的localscale不为1"); } float radius = float.Parse(values[i * 2 + 1]); #if BLOCK_EDITOR if (!Mathf.Approximately(block.transform.lossyScale.x, 1)) { Debug.LogError(">>>>>搭建积木块不允许有缩放,请从根节点开始检查,并修正!"); } #else //app中在创建搭建前,可能会缩放根节点 radius *= block.transform.lossyScale.x; #endif PBCircle circle = new PBCircle(block.transform, radius); circles[i] = circle; } SetCircles(circles, CurveType); }
private static LinkedList <PBCircleNode> BuildForOne(PBCircle[] circles) { LinkedList <PBCircleNode> nodes = new LinkedList <PBCircleNode>(); PBCircle circle = circles[0]; PBCircleNode node1 = new PBCircleNode( circle, circle.center + circle.up * circle.radius, -circle.right, 0); nodes.AddFirst(node1); PBCircleNode node2 = new PBCircleNode(node1); node2.angle = 360; nodes.AddLast(node2); return(nodes); }
private static LinkedList <PBCircleNode> BuildForEnclosed(PBCircle[] circles) { LinkedList <PBCircleNode> nodes = new LinkedList <PBCircleNode>(); for (int i = 0; i < circles.Length; i++) { PBCircle c1 = circles[i]; PBCircle c2 = i == circles.Length - 1 ? circles[0] : circles[i + 1]; PBCircleNode[] subNodes = CalculateTangentLines(c1, c2); //只保留方向与圆心连线方向一致的 //所以顺时针选择圆,就包围外圈,逆时针选择圆,就包围内圈 nodes.AddLast(subNodes[2]); nodes.AddLast(subNodes[3]); } //最后一个要跟第一个相连,所以再拷贝第一个点到末尾 nodes.AddLast(new PBCircleNode(nodes.First.Value)); return(nodes); }
private void OnSceneGUI() { if (splineTrack.Curve == null || !splineTrack.Curve.IsBuilt) { return; } Color preColor = Handles.color; LinkedList <PBCircleNode> nodes = splineTrack.Curve.GetNodes(); LinkedListNode <PBCircleNode> listNode = nodes.First.Next; PBCircleNode startNode; PBCircleNode endNode; while (listNode != null) { startNode = listNode.Previous.Value; endNode = listNode.Value; Handles.color = Color.green; if (startNode.ConnectLine(endNode)) { Handles.DrawLine(startNode.position, endNode.position); } else { PBCircle circle = startNode.circle; Handles.DrawWireArc(circle.center, circle.forward, (startNode.position - circle.center).normalized, startNode.deltaAngle(endNode), circle.radius); } Handles.color = Color.blue; Handles.SphereCap(0, startNode.position, Quaternion.identity, 0.05f); listNode = listNode.Next; } Handles.color = preColor; }