示例#1
0
        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);
            }
        }
示例#2
0
        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);
            }
        }
示例#3
0
        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;
                    }
                }
            }
        }
示例#5
0
        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);
示例#9
0
        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);
        }
示例#10
0
 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);
        }
示例#13
0
 public override void Collide(CollisionEvent coll, PlayerPhysics physics)
 {
     // not needed in unity ECS
     throw new System.NotImplementedException();
 }
示例#14
0
 public override float HitTest(Ball ball, float dTime, CollisionEvent coll, PlayerPhysics physics)
 {
     // not needed in unity ECS
     throw new System.NotImplementedException();
 }
示例#15
0
        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);
        }
示例#16
0
 /// <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;
                }
                }
            }
        }
示例#18
0
 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);
 }
示例#20
0
        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);
        }