/// <summary> /// 圆和边的平面动态相交检测(注意是边的平面 并不是线段) /// </summary> /// <param name="tedge"></param> /// <param name="crd"></param> /// <returns>是否在该段时间内相交</returns> public static bool CheckCircle_LineContact(tableEdge tedge, CircleRunData crd, ref FP t_percent) { //Sc TSVector2 Sc = PointToLineDir(tedge.start, tedge.end, crd.cur_pos); //Se TSVector2 Se = PointToLineDir(tedge.start, tedge.end, crd.next_pos); TSVector2 Scnormal = Sc.normalized; TSVector2 Senormal = Se.normalized; //TSVector2 Scnormal = Sc.normalized; //TSVector2 Senormal = Se.normalized; //只有两种结果 同向和 反向 FP result = TSVector2.Dot(Scnormal, Senormal); //1同向,0垂直,-1反向 FP Scnorm = TSMath.Sqrt(TSMath.Abs(TSVector2.Dot(Sc, Sc))); //Sc模 FP Senorm = TSMath.Sqrt(TSMath.Abs(TSVector2.Dot(Se, Se))); //Se模 //FP radius_square = crd.radius * crd.radius; if (result > 0 && Scnorm > crd.radius && Senorm > crd.radius)//Sc,Se同向,俩圆圆半径大于到直线距离,不相交 { return(false); } else//相交 求t { FP S = 0; if (result > 0) { S = Scnorm - Senorm; } else { S = Scnorm + Senorm; } //TSVector2 sce = Sc - Se; //FP S = TSMath.Sqrt( TSVector2.Dot(sce, sce)); t_percent = (Scnorm - crd.radius) / S;//圆心到达撞击点的距离/圆心经过的总距离 来求出时间占比 if (t_percent > 1) { return(false); Debug.Log("路程百分比大于1,注意!"); } //if (t_percent < 0) //{ // if (Detection.CheckCircle_tableEdgeEndContact(crd, tedge, ref t_percent)) // { // Debug.Log("修正"); // } //} return(t_percent >= 0?true:false); } }
/// <summary> /// 计算球和球的互相作用方向 /// </summary> /// <returns></returns> //public static TSVector2[] CheckCircle_CircleCollision(CircleRunData runCircle, CircleRunData staticCircle) //{ // TSVector2 V = runCircle.next_pos - runCircle.cur_pos; // TSVector2 U = staticCircle.next_pos - staticCircle.cur_pos; // TSVector2 AB = staticCircle.cur_pos - runCircle.cur_pos; // TSVector2 BA = runCircle.cur_pos - staticCircle.cur_pos; // TSVector2 Vx = TSVector2.Dot(V, AB.normalized)*AB.normalized; // TSVector2 Vy = V - Vx; // TSVector2 Ux = TSVector2.Dot(U, BA.normalized) * BA.normalized; // TSVector2 Uy = U - Ux; // V = Ux + Vy;//反弹后的方向 // U = Vx + Uy;//反弹后的方向 // return new TSVector2[2] { V,U }; //} //public static TSVector2[] CheckCircle_CircleCollision(TSVector2 V, TSVector2 U) //{ // TSVector2 Vx = new TSVector2(V.x, 0); // TSVector2 Vy = new TSVector2(0, V.y); // TSVector2 Ux = new TSVector2(U.x, 0); // TSVector2 Uy = new TSVector2(0, U.y); // V = Ux + Vy;//反弹后的方向 // U = Vx + Uy;//反弹后的方向 // return new TSVector2[2] { V, U }; //} /// <summary> /// 圆和圆的动态相交检测(根据相对运动,抽象为一方是运动,另一方是静止) /// </summary> /// <param name="cd"></param> /// <param name="crd"></param> /// <returns></returns> public static bool CheckCircle_CircleContact(CircleRunData runCircle, CircleRunData staticCircle, FP deltaTime, ref FP _percent) { TSVector2 VA = runCircle.next_pos - runCircle.cur_pos; TSVector2 VB = staticCircle.next_pos - staticCircle.cur_pos; //两个运动方向描述为一方运动另一方静止 so TSVector2 VAB = VA - VB; //runCircle相对于staticCircle的运动方向pc TSVector2 Idir = staticCircle.cur_pos - runCircle.cur_pos; //射线起点到静态圆的方向 //FP Idir_length_square = TSVector2.Dot(Idir, Idir); FP Idir_length_square = Idir.LengthSquared(); FP static_radius_square = (staticCircle.radius * 2) * (staticCircle.radius * 2); //Func<TSVector2,FP,FP> calHitInfo = (e_dir,a_projvalue) => ////可以在返回true的时候再计算,后优化 //{ // //TSVector2 e_dir = staticCircle.cur_pos - runCircle.cur_pos; // //FP a_projvalue = TSMath.Abs(TSVector2.Dot(e_dir, VAB.normalized)); // a_projvalue = TSMath.Abs(a_projvalue); // FP b_squar = TSVector2.Dot(e_dir, e_dir) - a_projvalue * a_projvalue; // FP f = TSMath.Sqrt(static_radius_square - b_squar); // FP t = a_projvalue - f;//碰撞到静态圆所走的路程,总路程是runCircle.cur_pos+VAB*delataTime; // return t /*/ (VAB * deltaTime).magnitude*/;//求出占比 //}; if (Idir_length_square < static_radius_square) //射线起点在圆心内部,相交 { //_percent = calHitInfo(); //_percent = 1;//一开始就相交的 //Debug.Log("射线起点在圆心内部"); return(false); } else//射线起点在圆心外部的情况 { FP a_projvalue = TSVector2.Dot(Idir, VAB.normalized); if (a_projvalue < 0)//球体位于射线原点的后面 不相交 { return(false); } else { FP m_square = Idir_length_square - a_projvalue * a_projvalue; //球心到投影点距离的平方 if (m_square - static_radius_square > 0) //预测不相交 { return(false); } else//有可能有交点,因为有可能距离不够 { //var t = calHitInfo(Idir, a_projvalue); FP b_squar = m_square; FP f = TSMath.Sqrt(static_radius_square - b_squar); //理论上来说 f是开跟后的结果,应该有俩个值? FP t1 = a_projvalue - f; //碰撞到静态圆所走的路程,总路程是runCircle.cur_pos+VAB*delataTime; FP t2 = a_projvalue + f; FP per = 0; bool isFlag = false; if (t1 > 0 && t1 - VAB.magnitude < 0) { isFlag = true; if (VAB.magnitude < 0) { Debug.Log("除数不能为0"); } per = t1 / VAB.magnitude; } if (t2 > 0 && t2 - VAB.magnitude < 0) { isFlag = true; if (VAB.magnitude < 0) { Debug.Log("除数不能为0"); } var per2 = t2 / VAB.magnitude; if (per2 < per) { per = per2; } } _percent = per; if (isFlag && _percent < FP.EN4) { return(false); } return(isFlag); } } } }
/// <summary> /// 检测圆是否和线段的端点相交,参考了俩个动态圆的动态相交测试,都是转化为射线和圆的相交测试 /// </summary> /// <returns></returns> public static bool CheckCircle_tableEdgeEndContact(CircleRunData runCircle, tableEdge segement, ref FP _percent, ref TSVector2 _nearestPos) { TSVector2 cirPos = runCircle.cur_pos; TSVector2 nearestPos = TSVector2.zero; //先确定圆的起始位置离哪个端点最近 if (TSVector2.DistanceSquared(runCircle.cur_pos, segement.start) < TSVector2.DistanceSquared(runCircle.cur_pos, segement.end)) { _nearestPos = nearestPos = segement.start; } else { _nearestPos = nearestPos = segement.end; } //TSVector2 VA = runCircle.next_pos - runCircle.cur_pos; //TSVector2 VB = staticCircle.next_pos - staticCircle.cur_pos; //两个运动方向描述为一方运动另一方静止 so //TSVector2 VAB = VA - VB;//runCircle相对于staticCircle的运动方向pc TSVector2 VAB = runCircle.next_pos - runCircle.cur_pos; //动态圆射线运动方向 TSVector2 Idir = nearestPos - cirPos; //射线起点到静态圆的方向 //FP Idir_length_square = TSVector2.Dot(Idir, Idir); FP Idir_length_square = Idir.LengthSquared(); FP static_radius_square = runCircle.radius * runCircle.radius; if (Idir_length_square < static_radius_square)//射线起点在圆心内部,相交 { //_percent = calHitInfo(); //_percent = 1;//一开始就相交的 //Debug.Log("射线起点在圆心内部"); return(false); } else//射线起点在圆心外部的情况 { FP a_projvalue = TSVector2.Dot(Idir, VAB.normalized); if (a_projvalue < 0)//球体位于射线原点的后面 不相交 { return(false); } else { FP m_square = Idir_length_square - a_projvalue * a_projvalue; //球心到投影点距离的平方 if (m_square - static_radius_square > 0) //预测不相交 { return(false); } else//有可能有交点,因为有可能距离不够 { //var t = calHitInfo(Idir, a_projvalue); FP b_squar = m_square; FP f = TSMath.Sqrt(static_radius_square - b_squar); //理论上来说 f是开跟后的结果,应该有俩个值? FP t1 = a_projvalue - f; //碰撞到静态圆所走的路程,总路程是runCircle.cur_pos+VAB*delataTime; FP t2 = a_projvalue + f; FP per = 0; bool isFlag = false; if (t1 > 0 && t1 - VAB.magnitude < FP.EN8) { isFlag = true; if (VAB.magnitude < 0) { Debug.Log("除数不能为0"); } per = t1 / VAB.magnitude; } if (t2 > 0 && t2 - VAB.magnitude < 0) { isFlag = true; if (VAB.magnitude < 0) { Debug.Log("除数不能为0"); } var per2 = t2 / VAB.magnitude; if (per2 < per) { per = per2; } } _percent = per; if (_percent > 1) { Debug.Log("路程百分比大于1,注意!"); } if (isFlag && _percent < FP.EN4) { return(false); } return(isFlag); } } } }
void UpdatePhysicStep(FP _deltaTime) { step++; //AddTestData(step,balls); for (int k = 0; k < balls.Count; k++) { var ball = balls[k]; ball.deltaTime = _deltaTime; ball.lockcheck = false; } testnumber = 0; FP leftSyncTime = _deltaTime; while (true) { testnumber++; if (testnumber > 100) { if (testnumber > 200) { Debug.Log("防止死循环"); break; } } //List<BaseHit> baseHits = new List<BaseHit>(); baseHits.Clear(); FP _percent = 0; for (int i = 0; i < balls.Count - 1; i++) { var ball = balls[i]; //CircleRunData run_crd = new CircleRunData(ball.GetPos(), ball.PredictPos(leftSyncTime), ball.GetRadius()); CircleRunData run_crd = run_crdPool.New(); run_crd.Init(ball.GetPos(), ball.PredictPos(leftSyncTime), ball.GetRadius()); for (int j = i + 1; j < balls.Count; j++) { var otherball = balls[j]; //CircleRunData static_crd = new CircleRunData(otherball.GetPos(), otherball.PredictPos(leftSyncTime), otherball.GetRadius()); CircleRunData static_crd = run_crdPool.New(); static_crd.Init(otherball.GetPos(), otherball.PredictPos(leftSyncTime), otherball.GetRadius()); if (Detection.CheckCircle_CircleContact(run_crd, static_crd, ball.deltaTime, ref _percent)) { var obj = fastHitBallPool.New(); obj.Init(ball, otherball, _percent); baseHits.Add(obj); //baseHits.Add(new fastHitBall(ball, otherball, _percent)); } } } for (int ii = 0; ii < balls.Count; ii++) { var ball = balls[ii]; TSVector2 predictEndPos = ball.GetPos() + ball.GetMoveDir() * 100; //CircleRunData run_crd = new CircleRunData(ball.GetPos(), ball.PredictPos(leftSyncTime), ball.GetRadius()); CircleRunData run_crd = run_crdPool.New(); run_crd.Init(ball.GetPos(), ball.PredictPos(leftSyncTime), ball.GetRadius()); //在当前速度下,预测圆最先和哪条边碰撞 for (int jj = 0; jj < tableEdges.Count; jj++) { TSVector2 predictCirclePos = TSVector2.zero; if (Detection.CheckCloseSegement(tableEdges[jj], ball.moveDir)) //if (Detection.CheckCloseEdge(tableEdges[jj].start, tableEdges[jj].end, ball.GetPos(), predictEndPos)) { //预测是否和边所在平面碰撞 if (Detection.CheckCircle_LineContact(tableEdges[jj], run_crd, ref _percent) /*|| Detection.CheckCircle_tableEdgeEndContact(run_crd, tableEdges[jj], ref _percent)*/) { predictCirclePos = ball.PredictPos(leftSyncTime * _percent); if (Detection.CheckCircle_SegementContact(predictCirclePos, tableEdges[jj], ball.radius))//然后检测离真实线段最近的点是否符合要求 { var obj = fastEdgePool.New(); obj.Init(ball, tableEdges[jj], _percent, tableEdges[jj].normal); baseHits.Add(obj); } else { _percent = 0; } } else { //TSVector2 nearestPos = TSVector2.zero; //if(Detection.CheckCircle_tableEdgeEndContact(run_crd, tableEdges[jj], ref _percent,ref nearestPos))//检测是否和线段的端点产生了碰撞 } } if (Detection._CheckCircle_tableEdgeEndContact(run_crd, tableEdges[jj].start, ref _percent))//检测是否和线段的左端点产生了碰撞 { predictCirclePos = ball.PredictPos(leftSyncTime * _percent); if (Detection.CheckCircle_SegementContact(predictCirclePos, tableEdges[jj], ball.radius))//然后检测离真实线段最近的点是否符合要求 { var obj = fastEdgePool.New(); obj.Init(ball, tableEdges[jj], _percent, (predictCirclePos - tableEdges[jj].start).normalized); baseHits.Add(obj); } else { _percent = 0; } } else if (Detection._CheckCircle_tableEdgeEndContact(run_crd, tableEdges[jj].end, ref _percent))//检测是否和线段的右端点产生了碰撞 { predictCirclePos = ball.PredictPos(leftSyncTime * _percent); if (Detection.CheckCircle_SegementContact(predictCirclePos, tableEdges[jj], ball.radius))//然后检测离真实线段最近的点是否符合要求 { var obj = fastEdgePool.New(); obj.Init(ball, tableEdges[jj], _percent, (predictCirclePos - tableEdges[jj].end).normalized); baseHits.Add(obj); } else { _percent = 0; } } else { _percent = 0; } } } if (baseHits.Count > 0) { var closedHit = baseHits.OrderBy((m) => m.t_percent).First(); closedHit.TagProcess(); var syncTime = closedHit.t_percent * leftSyncTime; leftSyncTime -= syncTime; if (leftSyncTime <= 0) { leftSyncTime = 0; } if (closedHit.hitType == HitType.Ball) { var _baseHit = closedHit as fastHitBall; //_baseHit.runballObj.CalBallPos(_baseHit.t_percent * _baseHit.runballObj.deltaTime); //_baseHit.staticballObj.CalBallPos(_baseHit.t_percent * _baseHit.staticballObj.deltaTime); updateDirAndTimeByBall(_baseHit.t_percent, _baseHit.runballObj, _baseHit.staticballObj, syncTime); } else if (closedHit.hitType == HitType.Edge) { var _baseHit = closedHit as fastEdge; //_baseHit.ball.CalBallPos(_baseHit.t_percent * _baseHit.ball.deltaTime); //updateDirAndTimeByEdge(_baseHit.t_percent, _baseHit.tbe, _baseHit.ball, syncTime); _updateDirAndTimeByEdge(_baseHit.t_percent, _baseHit.hitNormal, _baseHit.ball, syncTime); } for (int m = 0; m < balls.Count; m++) { var nothitBall = balls[m]; if (nothitBall.lockcheck == false) { nothitBall.UpdateBallPos(syncTime); } } } else { for (int n = 0; n < balls.Count; n++) { var nothitBall = balls[n]; //if (nothitBall.deltaTime > 0) nothitBall.UpdateBallPos(leftSyncTime); } break; } run_crdPool.ResetAll(); fastEdgePool.ResetAll(); fastHitBallPool.ResetAll(); UnLockBalls(); } //ClearTestData(); }