public override void Collide(CollisionEvent coll, PlayerPhysics physics) { coll.Ball.Hit.Collide3DWall(coll.HitNormal, Elasticity, ElasticityFalloff, Friction, Scatter); // distance from plane to ball surface var bnd = _normal.Dot(coll.Ball.State.Pos) - coll.Ball.Data.Radius - _d; if (bnd < 0) { // if ball has penetrated, push it out of the plane var v = _normal.Clone().MultiplyScalar(bnd); coll.Ball.State.Pos.Add(v); } }
public override void Collide(CollisionEvent coll, PlayerPhysics physics) { var ball = coll.Ball; var hitNormal = coll.HitNormal; var dot = -hitNormal.Dot(ball.Hit.Vel); ball.Hit.Collide3DWall(hitNormal, Elasticity, ElasticityFalloff, Friction, Scatter); // manage item-specific logic if (Obj != null && FireEvents && dot >= Threshold) { Obj.OnCollision?.Invoke(this, ball, dot); } }
public override void Collide(CollisionEvent coll, PlayerPhysics physics) { var ball = coll.Ball; var hitNormal = coll.HitNormal; var dot = coll.HitNormal.Dot(coll.Ball.Hit.Vel); // normal velocity to slingshot var threshold = dot <= -_surfaceData.SlingshotThreshold; // normal greater than threshold? if (!_surfaceData.IsDisabled && threshold) { // enabled and if velocity greater than threshold level var len = (V2.X - V1.X) * hitNormal.Y - (V2.Y - V1.Y) * hitNormal.X; // length of segment, Unit TAN points from V1 to V2 var vHitPoint = new Vertex2D( ball.State.Pos.X - hitNormal.X * ball.Data.Radius, // project ball radius along norm ball.State.Pos.Y - hitNormal.Y * ball.Data.Radius ); // vHitPoint will now be the point where the ball hits the line // Calculate this distance from the center of the slingshot to get force var btd = (vHitPoint.X - V1.X) * hitNormal.Y - (vHitPoint.Y - V1.Y) * hitNormal.X; // distance to vhit from V1 var force = MathF.Abs(len) > 1.0e-6 ? (btd + btd) / len - 1.0f : -1.0f; // -1..+1 force = 0.5f * (1.0f - force * force); // !! maximum value 0.5 ...I think this should have been 1.0...Oh well // will match the previous physics force *= Force; //-80; // boost velocity, drive into slingshot (counter normal), allow CollideWall to handle the remainder var normForce = hitNormal.Clone().MultiplyScalar(force); ball.Hit.Vel.Sub(normForce); } ball.Hit.Collide3DWall(hitNormal, Elasticity, ElasticityFalloff, Friction, Scatter); if (Obj != null && FireEvents && !_surfaceData.IsDisabled && Threshold != 0) { // is this the same place as last event? if same then ignore it var eventPos = ball.Hit.EventPos.Clone(); var distLs = eventPos.Sub(ball.State.Pos).LengthSq(); ball.Hit.EventPos.Set(ball.State.Pos); //remember last collide position if (distLs > 0.25) { // must be a new place if only by a little Obj.FireGroupEvent(EventId.SurfaceEventsSlingshot); _slingshotAnim.TimeReset = physics.TimeMsec + 100; } } }
public void DoHitTest(Ball ball, CollisionEvent coll, PlayerPhysics physics) { if (ball == null) { return; } if (Obj?.AbortHitTest != null && Obj.AbortHitTest()) { return; } var newColl = new CollisionEvent(ball); var newTime = HitTest(ball, coll.HitTime, !physics.RecordContacts ? coll : newColl, physics); var validHit = newTime >= 0 && newTime <= coll.HitTime; if (!physics.RecordContacts) { // simply find first event if (validHit) { coll.Ball = ball; coll.Obj = this; coll.HitTime = newTime; } } else { // find first collision, but also remember all contacts if (newColl.IsContact || validHit) { newColl.Ball = ball; newColl.Obj = this; if (newColl.IsContact) { physics.Contacts.Add(newColl); } else { // if (validhit) coll.Set(newColl); coll.HitTime = newTime; } } } }
public void HitTestBall(Ball ball, CollisionEvent coll, PlayerPhysics physics) { foreach (var vho in _hitObjects) { if (ball.Hit != vho && // ball can not hit itself vho.HitBBox.IntersectRect(ball.Hit.HitBBox) && vho.HitBBox.IntersectSphere(ball.State.Pos, ball.Hit.HitRadiusSqr)) { vho.DoHitTest(ball, coll, physics); } } if (!_isLeaf) { var isLeft = ball.Hit.HitBBox.Left <= _center.X; var isRight = ball.Hit.HitBBox.Right >= _center.X; if (ball.Hit.HitBBox.Top <= _center.Y) { // Top if (isLeft) { _children[0].HitTestBall(ball, coll, physics); } if (isRight) { _children[1].HitTestBall(ball, coll, physics); } } if (ball.Hit.HitBBox.Bottom >= _center.Y) { // Bottom if (isLeft) { _children[2].HitTestBall(ball, coll, physics); } if (isRight) { _children[3].HitTestBall(ball, coll, physics); } } } }
public override void Collide(CollisionEvent coll, PlayerPhysics physics) { var ball = coll.Ball; var hitNormal = coll.HitNormal; /* istanbul ignore This else seems dead code to me. The actual trigger logic is handled in TriggerHitCircle and TriggerHitLine. */ if (ObjType != CollisionType.Trigger) { var dot = -hitNormal.Dot(ball.Hit.Vel); ball.Hit.Collide3DWall(_normal, Elasticity, ElasticityFalloff, Friction, Scatter); // manage item-specific logic if (Obj != null && FireEvents && dot >= Threshold) { Obj.OnCollision?.Invoke(this, ball, dot); } } else // trigger (probably unused code) { if (!ball.Hit.IsRealBall()) { return; } var i = ball.Hit.VpVolObjs.IndexOf(Obj); // if -1 then not in objects volume set (i.e not already hit) if (!coll.HitFlag == i < 0) // Hit == NotAlreadyHit { var addPos = ball.Hit.Vel.Clone().MultiplyScalar(PhysicsConstants.StaticTime); ball.State.Pos.Add(addPos); // move ball slightly forward if (i < 0) { ball.Hit.VpVolObjs.Add(Obj); Obj.FireGroupEvent(Event.HitEventsHit); } else { ball.Hit.VpVolObjs.RemoveAt(i); Obj.FireGroupEvent(Event.HitEventsUnhit); } } } }
public abstract void Collide(CollisionEvent coll, PlayerPhysics physics);
public abstract float HitTest(Ball ball, float dTime, CollisionEvent coll, PlayerPhysics physics);
public float HitTestBasic(Ball ball, float dTime, CollisionEvent coll, bool direction, bool lateral, bool rigid) { if (!IsEnabled || ball.State.IsFrozen) { return(-1.0f); } // ball velocity var ballVx = ball.Hit.Vel.X; var ballVy = ball.Hit.Vel.Y; // ball velocity normal to segment, positive if receding, zero=parallel var bnv = ballVx * Normal.X + ballVy * Normal.Y; var isUnHit = bnv > PhysicsConstants.LowNormVel; // direction true and clearly receding from normal face if (direction && bnv > PhysicsConstants.LowNormVel) { return(-1.0f); } // ball position var ballX = ball.State.Pos.X; var ballY = ball.State.Pos.Y; // ball normal contact distance distance normal to segment. lateral contact subtract the ball radius var rollingRadius = lateral ? ball.Data.Radius : PhysicsConstants.ToleranceRadius; // lateral or rolling point var bcpd = (ballX - V1.X) * Normal.X + (ballY - V1.Y) * Normal.Y; // ball center to plane distance var bnd = bcpd - rollingRadius; // for a spinner add the ball radius otherwise the ball goes half through the spinner until it moves if (ObjType == ItemType.Spinner || ObjType == ItemType.Gate) { bnd = bcpd + rollingRadius; } var inside = bnd <= 0; // in ball inside object volume float hitTime; if (rigid) { if (bnd < -ball.Data.Radius || lateral && bcpd < 0) { // (ball normal distance) excessive penetration of object skin ... no collision HACK return(-1.0f); } if (lateral && bnd <= PhysicsConstants.PhysTouch) { if (inside || MathF.Abs(bnv) > PhysicsConstants.ContactVel || // fast velocity, return zero time bnd <= -PhysicsConstants.PhysTouch) { // zero time for rigid fast bodies hitTime = 0; // slow moving but embedded } else { hitTime = bnd * (float)(1.0 / (2.0 * PhysicsConstants.PhysTouch)) + 0.5f; // don't compete for fast zero time events } } else if (MathF.Abs(bnv) > PhysicsConstants.LowNormVel) { // not velocity low ???? hitTime = bnd / -bnv; // rate ok for safe divide } else { return(-1.0f); // wait for touching } } else { //non-rigid ... target hits if (bnv * bnd >= 0) { // outside-receding || inside-approaching if (ObjType != ItemType.Trigger || // not a trigger !ball.Hit.IsRealBall() || // is a trigger, so test: MathF.Abs(bnd) >= ball.Data.Radius * 0.5 || // not too close ... nor too far away inside == ball.Hit.VpVolObjs.Contains(Obj)) { // ...Ball outside and hit set or ball inside and no hit set return(-1.0f); } hitTime = 0; isUnHit = !inside; // ball on outside is UnHit, otherwise it"s a Hit } else { hitTime = bnd / -bnv; } } if (float.IsNaN(hitTime) || float.IsInfinity(hitTime) || hitTime < 0 || hitTime > dTime) { return(-1.0f); // time is outside this frame ... no collision } var btv = ballVx * Normal.Y - ballVy * Normal.X; // ball velocity tangent to segment with respect to direction from V1 to V2 var btd = (ballX - V1.X) * Normal.Y - (ballY - V1.Y) * Normal.X // ball tangent distance + btv * hitTime; // ball tangent distance (projection) (initial position + velocity * hitime) if (btd < -PhysicsConstants.ToleranceEndPoints || btd > Length + PhysicsConstants.ToleranceEndPoints) { // is the contact off the line segment??? return(-1.0f); } if (!rigid) { // non rigid body collision? return direction coll.HitFlag = isUnHit; // UnHit signal is receding from outside target } var ballRadius = ball.Data.Radius; var hitZ = ball.State.Pos.Z + ball.Hit.Vel.Z * hitTime; // check too high or low relative to ball rolling point at hittime if (hitZ + ballRadius * 0.5 < HitBBox.ZLow || // check limits of object"s height and depth hitZ - ballRadius * 0.5 > HitBBox.ZHigh) { return(-1.0f); } // hit normal is same as line segment normal coll.HitNormal.Set(Normal.X, Normal.Y, 0.0f); coll.HitDistance = bnd; // actual contact distance ... //coll.M_hitRigid = rigid; // collision type // check for contact if (MathF.Abs(bnv) <= PhysicsConstants.ContactVel && MathF.Abs(bnd) <= PhysicsConstants.PhysTouch) { coll.IsContact = true; coll.HitOrgNormalVelocity = bnv; } return(hitTime); }
public override float HitTest(Ball ball, float dTime, CollisionEvent coll, PlayerPhysics physics) { return(HitTestBasic(ball, dTime, coll, true, true, true)); // normal face, lateral, rigid }
public override float HitTest(Ball ball, float dTime, CollisionEvent coll, PlayerPhysics physics) { if (!IsEnabled) { return(-1.0f); } var bp2d = new Vertex2D(ball.State.Pos.X, ball.State.Pos.Y); var dist = bp2d.Clone().Sub(Xy); // relative ball position var dv = new Vertex2D(ball.Hit.Vel.X, ball.Hit.Vel.Y); var bcddsq = dist.LengthSq(); // ball center to line distance squared var bcdd = MathF.Sqrt(bcddsq); // distance ball to line if (bcdd <= 1.0e-6) { // no hit on exact center return(-1.0f); } var b = dist.Dot(dv); var bnv = b / bcdd; // ball normal velocity if (bnv > PhysicsConstants.ContactVel) { // clearly receding from radius return(-1.0f); } var bnd = bcdd - ball.Data.Radius; // ball distance to line var a = dv.LengthSq(); float hitTime; var isContact = false; if (bnd < PhysicsConstants.PhysTouch) { // already in collision distance? if (MathF.Abs(bnv) <= PhysicsConstants.ContactVel) { isContact = true; hitTime = 0f; } else { // estimate based on distance and speed along distance hitTime = -bnd / bnv; } } else { if (a < 1.0e-8) { // no hit - ball not moving relative to object return(-1.0f); } var sol = Functions.SolveQuadraticEq(a, 2.0f * b, bcddsq - ball.Data.Radius * ball.Data.Radius); if (sol == null) { return(-1.0f); } var time1 = sol.Item1; var time2 = sol.Item2; // find smallest non-negative solution hitTime = time1 * time2 < 0 ? MathF.Max(time1, time2) : MathF.Min(time1, time2); } if (float.IsNaN(hitTime) || float.IsInfinity(hitTime) || hitTime < 0 || hitTime > dTime) { // contact out of physics frame return(-1.0f); } var hitZ = ball.State.Pos.Z + hitTime * ball.Hit.Vel.Z; // ball z position at hit time if (hitZ < HitBBox.ZLow || hitZ > HitBBox.ZHigh) { // check z coordinate return(-1.0f); } var hitX = ball.State.Pos.X + hitTime * ball.Hit.Vel.X; // ball x position at hit time var hitY = ball.State.Pos.Y + hitTime * ball.Hit.Vel.Y; // ball y position at hit time var norm = new Vertex2D(hitX - Xy.X, hitY - Xy.Y).Normalize(); coll.HitNormal.Set(norm.X, norm.Y, 0.0f); coll.IsContact = isContact; if (isContact) { coll.HitOrgNormalVelocity = bnv; } coll.HitDistance = bnd; // actual contact distance //coll.M_hitRigid = true; return(hitTime); }
public override float HitTest(Ball ball, float dTime, CollisionEvent coll, PlayerPhysics physics) { if (!IsEnabled) { return(-1.0f); } // relative ball position var dist = ball.State.Pos.Clone().Sub(P); var bcddsq = dist.LengthSq(); // ball center to line distance squared var bcdd = MathF.Sqrt(bcddsq); // distance ball to line if (bcdd <= 1.0e-6) { // no hit on exact center return(-1.0f); } var b = dist.Dot(ball.Hit.Vel); var bnv = b / bcdd; // ball normal velocity if (bnv > PhysicsConstants.ContactVel) { // clearly receding from radius return(-1.0f); } var bnd = bcdd - ball.Data.Radius; // ball distance to line var a = ball.Hit.Vel.LengthSq(); float hitTime; var isContact = false; if (bnd < PhysicsConstants.PhysTouch) { // already in collision distance? if (MathF.Abs(bnv) <= PhysicsConstants.ContactVel) { isContact = true; hitTime = 0; } else { // estimate based on distance and speed along distance hitTime = MathF.Max(0.0f, -bnd / bnv); } } else { if (a < 1.0e-8) { // no hit - ball not moving relative to object return(-1.0f); } var sol = Functions.SolveQuadraticEq(a, 2.0f * b, bcddsq - ball.Data.Radius * ball.Data.Radius); if (sol == null) { return(-1.0f); } var time1 = sol.Item1; var time2 = sol.Item2; // find smallest non-negative solution hitTime = time1 * time2 < 0 ? MathF.Max(time1, time2) : MathF.Min(time1, time2); } if (float.IsNaN(hitTime) || float.IsInfinity(hitTime) || hitTime < 0 || hitTime > dTime) { // contact out of physics frame return(-1.0f); } var hitVel = ball.Hit.Vel.Clone().MultiplyScalar(hitTime); var hitNormal = ball.State.Pos.Clone() .Add(hitVel) .Sub(P) .Normalize(); coll.HitNormal.Set(hitNormal); coll.IsContact = isContact; if (isContact) { coll.HitOrgNormalVelocity = bnv; } coll.HitDistance = bnd; // actual contact distance //coll.M_hitRigid = true; return(hitTime); }
public override void Collide(CollisionEvent coll, PlayerPhysics physics) { // not needed in unity ECS throw new System.NotImplementedException(); }
public override float HitTest(Ball ball, float dTime, CollisionEvent coll, PlayerPhysics physics) { // not needed in unity ECS throw new System.NotImplementedException(); }
public override float HitTest(Ball ball, float dTime, CollisionEvent coll, PlayerPhysics physics) { if (!IsEnabled) { return(-1.0f); } // speed in Normal-vector direction var bnv = _normal.Dot(ball.Hit.Vel); // return if clearly ball is receding from object if (ObjType != CollisionType.Trigger && bnv > PhysicsConstants.LowNormVel) { return(-1.0f); } // Point on the ball that will hit the polygon, if it hits at all var normRadius = _normal.Clone().MultiplyScalar(ball.Data.Radius); var hitPos = ball.State.Pos.Clone().Sub(normRadius); // nearest point on ball ... projected radius along norm var planeToBall = hitPos.Clone().Sub(_rgv[0]); var bnd = _normal.Dot(planeToBall); // distance from plane to ball var bUnHit = bnv > PhysicsConstants.LowNormVel; var inside = bnd <= 0; // in ball inside object volume var rigid = ObjType != CollisionType.Trigger; float hitTime; if (rigid) { // rigid polygon if (bnd < -ball.Data.Radius) { // (ball normal distance) excessive penetration of object skin ... no collision HACK //!! *2 necessary? return(-1.0f); } if (bnd <= PhysicsConstants.PhysTouch) { if (inside || MathF.Abs(bnv) > PhysicsConstants.ContactVel // fast velocity, return zero time //zero time for rigid fast bodies || bnd <= -PhysicsConstants.PhysTouch) { // slow moving but embedded hitTime = 0; } else { hitTime = bnd * (float)(1.0 / (2.0 * PhysicsConstants.PhysTouch)) + 0.5f; // don't compete for fast zero time events } } else if (MathF.Abs(bnv) > PhysicsConstants.LowNormVel) { // not velocity low? hitTime = bnd / -bnv; // rate ok for safe divide } else { return(-1.0f); // wait for touching } } else { // non-rigid polygon if (bnv * bnd >= 0) { // outside-receding || inside-approaching if (!ball.Hit.IsRealBall() || // temporary ball MathF.Abs(bnd) >= ball.Data.Radius * 0.5 || // not too close ... nor too far away inside == ball.Hit.VpVolObjs.Contains(Obj)) { // ...Ball outside and hit set or ball inside and no hit set return(-1.0f); } hitTime = 0; bUnHit = !inside; // ball on outside is UnHit, otherwise it"s a Hit } else { hitTime = bnd / -bnv; } } if (float.IsNaN(hitTime) || float.IsInfinity(hitTime) || hitTime < 0 || hitTime > dTime) { // time is outside this frame ... no collision return(-1.0f); } var adv = ball.Hit.Vel.Clone().MultiplyScalar(hitTime); hitPos.Add(adv); // advance hit point to contact // Do a point in poly test, using the xy plane, to see if the hit point is inside the polygon // this need to be changed to a point in polygon on 3D plane var x2 = _rgv[0].X; var y2 = _rgv[0].Y; var hx2 = hitPos.X >= x2; var hy2 = hitPos.Y <= y2; var crossCount = 0; // count of lines which the hit point is to the left of for (var i = 0; i < _rgv.Length; i++) { var x1 = x2; var y1 = y2; var hx1 = hx2; var hy1 = hy2; var j = i < _rgv.Length - 1 ? i + 1 : 0; x2 = _rgv[j].X; y2 = _rgv[j].Y; hx2 = hitPos.X >= x2; hy2 = hitPos.Y <= y2; if (y1 == y2 || hy1 && hy2 || !hy1 && !hy2 || hx1 && hx2) { // Hit point is on the right of the line continue; } if (!hx1 && !hx2) { crossCount ^= 1; continue; } if (x2 == x1) { if (!hx2) { crossCount ^= 1; } continue; } // Now the hard part - the hit point is in the line bounding box if (x2 - (y2 - hitPos.Y) * (x1 - x2) / (y1 - y2) > hitPos.X) { crossCount ^= 1; } } if ((crossCount & 1) != 0) { coll.HitNormal.Set(_normal); if (!rigid) { // non rigid body collision? return direction coll.HitFlag = bUnHit; // UnHit signal is receding from outside target } coll.HitDistance = bnd; // 3dhit actual contact distance ... //coll.M_hitRigid = rigid; // collision type return(hitTime); } return(-1.0f); }
/// <summary> /// Apply contact forces for the given time interval. Ball, Spinner and /// Gate do nothing here, Flipper has a specialized handling /// </summary> /// <param name="coll"></param> /// <param name="dTime"></param> /// <param name="physics"></param> public virtual void Contact(CollisionEvent coll, float dTime, PlayerPhysics physics) { coll.Ball.Hit.HandleStaticContact(coll, Friction, dTime, physics); }
public void HitTestBall(Ball ball, CollisionEvent coll, PlayerPhysics physics, HitKd hitOct) { var orgItems = Items & 0x3FFFFFFF; var axis = Items >> 30; for (var i = Start; i < Start + orgItems; i++) { var pho = hitOct.GetItemAt(i); if (ball.Hit != pho && pho.HitBBox.IntersectSphere(ball.State.Pos, ball.Hit.HitRadiusSqr)) { pho.DoHitTest(ball, coll, physics); } } if (_children != null) { switch (axis) { // not a leaf case 0: { var vCenter = (RectBounds.Left + RectBounds.Right) * 0.5f; if (ball.Hit.HitBBox.Left <= vCenter) { _children[0].HitTestBall(ball, coll, physics, hitOct); } if (ball.Hit.HitBBox.Right >= vCenter) { _children[1].HitTestBall(ball, coll, physics, hitOct); } break; } case 1: { var vCenter = (RectBounds.Top + RectBounds.Bottom) * 0.5f; if (ball.Hit.HitBBox.Top <= vCenter) { _children[0].HitTestBall(ball, coll, physics, hitOct); } if (ball.Hit.HitBBox.Bottom >= vCenter) { _children[1].HitTestBall(ball, coll, physics, hitOct); } break; } default: { var vCenter = (RectBounds.ZLow + RectBounds.ZHigh) * 0.5f; if (ball.Hit.HitBBox.ZLow <= vCenter) { _children[0].HitTestBall(ball, coll, physics, hitOct); } if (ball.Hit.HitBBox.ZHigh >= vCenter) { _children[1].HitTestBall(ball, coll, physics, hitOct); } break; } } } }
public override void Collide(CollisionEvent coll, PlayerPhysics physics) { coll.Ball.Hit.Collide3DWall(coll.HitNormal, Elasticity, ElasticityFalloff, Friction, Scatter); }
public void HitTestBall(Ball ball, CollisionEvent collision, PlayerPhysics physics) { _rootNode.HitTestBall(ball, collision, physics, this); }
protected float HitTestBasicRadius(Ball ball, float dTime, CollisionEvent coll, bool direction, bool lateral, bool rigid) { if (!IsEnabled || ball.State.IsFrozen) { return(-1.0f); } var c = new Vertex3D(Center.X, Center.Y, 0.0f); var dist = ball.State.Pos.Clone().Sub(c); // relative ball position var dv = ball.Hit.Vel.Clone(); var capsule3D = !lateral && ball.State.Pos.Z > HitBBox.ZHigh; var isKicker = ObjType == CollisionType.Kicker; var isKickerOrTrigger = ObjType == CollisionType.Trigger || ObjType == CollisionType.Kicker; float targetRadius; if (capsule3D) { targetRadius = Radius * (float)(13.0 / 5.0); c.Z = HitBBox.ZHigh - Radius * (float)(12.0 / 5.0); dist.Z = ball.State.Pos.Z - c.Z; // ball rolling point - capsule center height } else { targetRadius = Radius; if (lateral) { targetRadius += ball.Data.Radius; } dist.Z = 0.0f; dv.Z = 0.0f; } var bcddsq = dist.LengthSq(); // ball center to circle center distance ... squared var bcdd = MathF.Sqrt(bcddsq); // distance center to center if (bcdd <= 1.0e-6) { // no hit on exact center return(-1.0f); } var b = dist.Dot(dv); var bnv = b / bcdd; // ball normal velocity if (direction && bnv > PhysicsConstants.LowNormVel) { // clearly receding from radius return(-1.0f); } var bnd = bcdd - targetRadius; // ball normal distance to var a = dv.LengthSq(); var hitTime = 0f; var isUnhit = false; var isContact = false; // Kicker is special.. handle ball stalled on kicker, commonly hit while receding, knocking back into kicker pocket if (isKicker && bnd <= 0 && bnd >= -Radius && a < PhysicsConstants.ContactVel * PhysicsConstants.ContactVel && ball.Hit.IsRealBall()) { if (ball.Hit.VpVolObjs.Contains(Obj)) { ball.Hit.VpVolObjs.Remove(Obj); // cause capture } } // contact positive possible in future ... objects Negative in contact now if (rigid && bnd < PhysicsConstants.PhysTouch) { if (bnd < -ball.Data.Radius) { return(-1.0f); } if (MathF.Abs(bnv) <= PhysicsConstants.ContactVel) { isContact = true; } else { // estimate based on distance and speed along distance // the ball can be that fast that in the next hit cycle the ball will be inside the hit shape of a bumper or other element. // if that happens bnd is negative and greater than the negative bnv value that results in a negative hittime // below the "if (infNan(hittime) || hittime <0.F...)" will then be true and the hit function will return -1.0f = no hit hitTime = MathF.Max(0.0f, (float)(-bnd / bnv)); } } else if (isKickerOrTrigger && ball.Hit.IsRealBall() && bnd < 0 == ball.Hit.VpVolObjs.IndexOf(Obj) < 0) { // triggers & kickers // here if ... ball inside and no hit set .... or ... ball outside and hit set if (MathF.Abs(bnd - Radius) < 0.05) { // if ball appears in center of trigger, then assumed it was gen"ed there ball.Hit.VpVolObjs.Add(Obj); // special case for trigger overlaying a kicker } else { // this will add the ball to the trigger space without a Hit isUnhit = bnd > 0; // ball on outside is UnHit, otherwise it"s a Hit } } else { if (!rigid && bnd * bnv > 0 || a < 1.0e-8) { // (outside and receding) or (inside and approaching) // no hit ... ball not moving relative to object return(-1.0f); } var sol = Functions.SolveQuadraticEq(a, 2.0f * b, bcddsq - targetRadius * targetRadius); if (sol == null) { return(-1.0f); } var(time1, time2) = sol; isUnhit = time1 * time2 < 0; hitTime = isUnhit ? MathF.Max(time1, time2) : MathF.Min(time1, time2); // ball is inside the circle } if (float.IsNaN(hitTime) || float.IsInfinity(hitTime) || hitTime < 0 || hitTime > dTime) { // contact out of physics frame return(-1.0f); } var hitZ = ball.State.Pos.Z + ball.Hit.Vel.Z * hitTime; // rolling point if (hitZ + ball.Data.Radius * 0.5 < HitBBox.ZLow || !capsule3D && hitZ - ball.Data.Radius * 0.5 > HitBBox.ZHigh || capsule3D && hitZ < HitBBox.ZHigh) { return(-1.0f); } var hitX = ball.State.Pos.X + ball.Hit.Vel.X * hitTime; var hitY = ball.State.Pos.Y + ball.Hit.Vel.Y * hitTime; var sqrLen = (hitX - c.X) * (hitX - c.X) + (hitY - c.Y) * (hitY - c.Y); coll.HitNormal.SetZero(); // over center? if (sqrLen > 1.0e-8) { // no var invLen = 1.0f / MathF.Sqrt(sqrLen); coll.HitNormal.X = (hitX - c.X) * invLen; coll.HitNormal.Y = (hitY - c.Y) * invLen; } else { // yes, over center coll.HitNormal.X = 0.0f; // make up a value, any direction is ok coll.HitNormal.Y = 1.0f; coll.HitNormal.Z = 0.0f; } if (!rigid) { // non rigid body collision? return direction coll.HitFlag = isUnhit; // UnHit signal is receding from target } coll.IsContact = isContact; if (isContact) { coll.HitOrgNormalVelocity = bnv; } coll.HitDistance = bnd; // actual contact distance ... //coll.M_hitRigid = rigid; // collision type return(hitTime); }