/// <summary> /// 如果没有相切的错误,就返回true,否则返回false /// </summary> /// <returns></returns> public bool CheckSeperate() { var dir = (FarPoint.Position - NearPoint.Position).normalized; var hit = Physics2D.Raycast(NearPoint.Position, dir, (FarPoint.Position - NearPoint.Position).magnitude, LayerManager.LayerMask.SilkJoint); if (hit) { SilkDebug.DrawCross(hit.point, 0.3f, Color.red); var curLine = new Vector4(NearPoint.Position.x, NearPoint.Position.y, FarPoint.Position.x, FarPoint.Position.y); SilkDebug.DrawLine(LastLine, Color.green); SilkDebug.DrawLine(curLine, Color.black); LineCollisionHit lineHit; var bl = LineCollision.TryGetLineCollisionPoint(LastLine, curLine, out lineHit, LayerManager.LayerMask.SilkJoint, PhysicsConfig.SurfaceLayerThickness); var tmpDrawer = lineHit.Collider.GetComponent <TmpDrawCircleCollider2D>(); if (tmpDrawer != null) { tmpDrawer.DrawArc(lineHit.Point); Debug.LogFormat("line to circle distance:{0} @{1}", Vector2.Distance(lineHit.Point, tmpDrawer.transform.position.ToVector2()) - tmpDrawer.GetComponent <CircleCollider2D>().radius, Time.frameCount); } SilkDebug.DrawCross(lineHit.Point, 0.4f, Color.black); if (bl) { var jointItem = lineHit.Collider.gameObject.layer == LayerManager.Layer.Ground ? null : lineHit.Collider.GetComponent <Rigidbody2D>(); MySilk.SeperateSegment(IndexOnSilk, lineHit.Point, jointItem); } else { Debug.LogWarningFormat("Tangency Error[{0}]", IndexOnSilk); return(false); } } return(true); }
///// <summary> ///// 计算碰撞点 ///// </summary> ///// <param name="vertex"></param> ///// <param name="sidePoint1"></param> ///// <param name="sidePoint2"></param> ///// <param name="collisionPoint"></param> ///// <returns>有没有碰撞</returns> //public static bool TryGetLineCollisionPoint(Vector2 vertex, Vector2 sidePoint1, Vector2 sidePoint2, out Vector2 collisionPoint) //{ // throw new Exception("TODO"); //} /// <summary> /// 应该只在start时无碰撞end时有碰撞的情况返回true,给出有效hit。对运动物体会出错 /// </summary> /// <param name="startLine"></param> /// <param name="endLine"></param> /// <param name="hitInfo"></param> /// <param name="layerMask"></param> /// <param name="epsilon"></param> /// <returns></returns> public static bool TryGetLineCollisionPoint(Vector4 startLine, Vector4 endLine, out LineCollisionHit hitInfo, LayerMask layerMask, float epsilon) { hitInfo = new LineCollisionHit(); RaycastHit2D curHit; if (!CheckLineCollision(endLine, out curHit, layerMask)) { Debug.LogWarning("Penetration may happen!"); return(false); //endLine没撞到就别调用这个方法 } var startLineRay = new Ray2D(startLine.P1(), startLine.Direction()); RaycastHit2D startLineHit;//活动物体就不考虑碰撞了,现在只考虑地形 if (curHit.collider.Raycast(startLineRay, out startLineHit, startLine.Length())) { SilkDebug.DrawCross(startLineHit.point, 0.1f, new Color(0.6f, 0.6f, 0)); Debug.LogWarningFormat("Penetration may happen at {0},{1}! startLine={2} endLine={3}, distanceToCenter={4}", startLineHit.point.x, startLineHit.point.y, startLine, endLine, CalcPointToLineVertical(startLine, curHit.collider.transform.position).magnitude); return(false); //startLine都碰撞到这个东西了上一帧就该处理 } var latestHit = curHit; var vertical = CalcPointToLineVertical(startLine, latestHit.point); var iterationTimes = 0; while (vertical.magnitude >= epsilon) { iterationTimes++; if (iterationTimes > 50) { Debug.LogFormat("iteration too many times > 50!"); break; } var midLine = Vector4.Lerp(startLine, endLine, 0.5f); if (CheckLineCollision(midLine, out curHit, layerMask)) { latestHit = curHit; endLine = midLine; } else { startLine = midLine; } vertical = CalcPointToLineVertical(startLine, latestHit.point); } var nearHitInfo = latestHit; #region 计算FarHit var vectorF2N = new Vector2(endLine.x, endLine.y) - new Vector2(endLine.z, endLine.w); var fnRay = new Ray2D(new Vector2(endLine.z, endLine.w), vectorF2N); //Far->Near的射线 var hits = Physics2D.RaycastAll(fnRay.origin, fnRay.direction, vectorF2N.magnitude, layerMask); if (hits.Length == 0) //不可能没有的 { SilkDebug.DrawCross(new Vector2(endLine.x, endLine.y), 0.1f, new Color(0.5f, 0.5f, 0.1f)); SilkDebug.DrawCross(new Vector2(endLine.z, endLine.w), 0.1f, new Color(0.4f, 0.4f, 0.3f)); Debug.LogError("Error Cannot find farHit @" + Time.frameCount); Debug.LogWarningFormat("hits.Length == 0; startLine={0} endLine={1}, distanceToCenter={2}", startLine, endLine, CalcPointToLineVertical(startLine, curHit.collider.transform.position).magnitude); return(false); } if (hits.Length > 1)//很少见,穿透了多次地形 { Debug.LogWarning("hits.Length > 1 ! Very rare!"); var rightHits = hits.Where(x => x.collider == nearHitInfo.collider).ToList(); //取出碰撞到指定碰撞器的 rightHits.Sort((x, y) => (int)Mathf.Sign(x.distance - y.distance)); //按到FarPoint的远近排序 } var farHitInfo = hits[hits.Length - 1]; //取出最远的 #endregion SilkDebug.DrawCross(nearHitInfo.point, 0.2f, Color.yellow); SilkDebug.DrawCross(farHitInfo.point, 0.2f, Color.yellow); #region 计算尖点和角平分线 Vector2 c1 = nearHitInfo.point; Vector2 c2 = farHitInfo.point; var n1 = nearHitInfo.normal; var n2 = farHitInfo.normal; var n1v = new Vector2(-n1.y, n1.x); //n1的垂线,右手螺旋,即c1所在边 var n2v = new Vector2(-n2.y, n2.x); //n2的垂线,右手螺旋,即c2所在边 Vector2 vertex; //顶点,尖点 if (Vector2.Dot(n2v, n1) == 0) { vertex = (c1 + c2) * 0.5f;//如果n1v,n2v平行,则无交点,取c1,c2中点。不太可能发生 } else { vertex = c2 + Vector2.Dot(c1 - c2, n1) / Vector2.Dot(n2v, n1) * n2v; //找到尖点 } var bisector = (n2v - n1v).normalized; //角平分线,不保证方向 var bv = new Vector2(-bisector.y, bisector.x); //角平分线的垂线 //找startLine与角平分线的交点 var p1 = new Vector2(startLine.x, startLine.y); //p1,p2是startLine线段的两个端点 var p2 = new Vector2(startLine.z, startLine.w); //p1,p2是startLine线段的两个端点 var p1p2 = p2 - p1; var v = p1 + Vector2.Dot(vertex - p1, bv) / Vector2.Dot(p1p2, bv) * p1p2; //那个我们需要的碰撞点,浮于碰撞器表面一个缓冲值以内 //如果碰撞点离碰撞器过近(小于SurfaceLayerMinThickness),则强行拉回此值之外 var vertex2v = v - vertex; if (vertex2v.magnitude < PhysicsConfig.SurfaceLayerMinThickness) { //找endLine与角平分线的交点 var p1endLine = new Vector2(endLine.x, endLine.y); var p2endLine = new Vector2(endLine.z, endLine.w); var p1p2endLine = p2endLine - p1endLine; var vendLine = p1endLine + Vector2.Dot(vertex - p1endLine, bv) / Vector2.Dot(p1p2endLine, bv) * p1p2endLine; Debug.LogFormat("OutPush vertex={0} v={1} distance={2}", vertex, v, vertex2v.magnitude); v = v + (v - vendLine).normalized * PhysicsConfig.SurfaceLayerMinThickness; } SilkDebug.DrawCross(vertex, 0.3f, Color.magenta); Debug.DrawRay(vertex, bisector); SilkDebug.DrawLine(startLine, Color.blue); SilkDebug.DrawLine(endLine, Color.blue); #endregion hitInfo = new LineCollisionHit(nearHitInfo.collider, (v - p1).magnitude, (v - vertex).normalized, v); return(true); }