Example #1
0
        public Fix RayCastCallback(FixVec2 pointA, FixVec2 pointB, Fix maxFraction, int proxyID, out TFRaycastHit2D hit, TFLayerMask mask)
        {
            TFRigidbody rigid = bodies[dynamicTree.nodes[proxyID].bodyIndex];


            if (!(mask == (mask | (1 << rigid.layer))))
            {
                // This object is not in the mask, ignore it.
                hit = new TFRaycastHit2D();
                return(maxFraction);
            }

            rigid.coll.Raycast(out hit, pointA, pointB, maxFraction);

            if (!hit)
            {
                // We did not hit the body, ignore it and use our max ray length.
                return(maxFraction);
            }

            Fix     fraction = hit.fraction;
            FixVec2 point    = (Fix.one - fraction) * pointA + fraction * pointB;

            hit.point    = point;
            hit.distance = (point - pointA).GetMagnitude();
            return(RayCastCallback(rigid, hit.point, hit.normal, hit.fraction));
        }
Example #2
0
        public override bool Raycast(out TFRaycastHit2D hit, FixVec2 pointA, FixVec2 pointB, Fix maxFraction)
        {
            hit = default;
            FixVec2 center = (FixVec2)tdTransform.Position;
            FixVec2 s      = pointA - center;
            Fix     b      = FixVec2.Dot(s, s) - radius * radius;

            // Solve quadratic equation.
            FixVec2 r     = pointB - pointA;
            Fix     c     = FixVec2.Dot(s, r);
            Fix     rr    = FixVec2.Dot(r, r);
            Fix     sigma = c * c - rr * b;

            // Check for negative discriminant and short segment.
            if (sigma < Fix.zero || rr < Fix.Epsilon)
            {
                return(false);
            }

            // Find the point of intersection on the line with the circle.
            Fix a = -(c + FixMath.Sqrt(sigma));

            // Is the intersection point on the segment?
            if (Fix.zero <= a && a <= maxFraction * rr)
            {
                a           /= rr;
                hit.fraction = a;
                hit.normal   = s + a * r;
                hit.normal.Normalize();
                return(true);
            }
            return(false);
        }
    void moveVertically(ref FixVec2 deltaMovement)
    {
        var isGoingUp        = deltaMovement.y > 0;
        var rayDistance      = FixMath.Abs(deltaMovement.y) + _skinWidth;
        var rayDirection     = isGoingUp ? FixVec2.up : -FixVec2.up;
        var initialRayOrigin = isGoingUp ? _raycastOrigins.topLeft : _raycastOrigins.bottomLeft;

        // apply our horizontal deltaMovement here so that we do our raycast from the actual position we would be in if we had moved
        initialRayOrigin.x += deltaMovement.x;

        // if we are moving up, we should ignore the layers in oneWayPlatformMask
        var mask = platformMask;

        if ((isGoingUp && !collisionState.wasGroundedLastFrame) || ignoreOneWayPlatformsThisFrame)
        {
            mask &= ~oneWayPlatformMask;
        }

        for (var i = 0; i < totalVerticalRays; i++)
        {
            FixVec2 ray = new FixVec2(initialRayOrigin.x + i * _horizontalDistanceBetweenRays, initialRayOrigin.y);

            DrawRay((Vector3)ray, (Vector3)(rayDirection * rayDistance), Color.red);
            _raycastHit = TFPhysics.Raycast(ray, rayDirection, rayDistance, mask);
            if (_raycastHit)
            {
                // set our new deltaMovement and recalculate the rayDistance taking it into account
                deltaMovement.y = _raycastHit.point.y - ray.y;
                rayDistance     = FixMath.Abs(deltaMovement.y);

                // remember to remove the skinWidth from our deltaMovement
                if (isGoingUp)
                {
                    deltaMovement.y     -= _skinWidth;
                    collisionState.above = true;
                }
                else
                {
                    deltaMovement.y     += _skinWidth;
                    collisionState.below = true;
                }

                _raycastHitsThisFrame.Add(_raycastHit);

                // this is a hack to deal with the top of slopes. if we walk up a slope and reach the apex we can get in a situation
                // where our ray gets a hit that is less then skinWidth causing us to be ungrounded the next frame due to residual velocity.
                if (!isGoingUp && deltaMovement.y > Fix.zero)
                {
                    _isGoingUpSlope = true;
                }

                // we add a small fudge factor for the float operations here. if our rayDistance is smaller
                // than the width + fudge bail out because we have a direct impact
                if (rayDistance < _skinWidth + kSkinWidthFloatFudgeFactor)
                {
                    break;
                }
            }
        }
    }
    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            var v3 = Input.mousePosition;
            v3.z = 0;
            v3   = Camera.main.ScreenToWorldPoint(v3);

            FixVec2 pointB = new FixVec2((Fix)v3.x, (Fix)v3.y);

            TFRaycastHit2D h = TFPhysics.Raycast((FixVec2)tfTransform.Position, (FixVec2)(pointB - tfTransform.Position).Normalized(), 5, platformMask);
        }
    }
    /// <summary>
    /// checks the center point under the BoxCollider2D for a slope. If it finds one then the deltaMovement is adjusted so that
    /// the player stays grounded and the slopeSpeedModifier is taken into account to speed up movement.
    /// </summary>
    /// <param name="deltaMovement">Delta movement.</param>
    private void handleVerticalSlope(ref FixVec2 deltaMovement)
    {
        // slope check from the center of our collider
        Fix     centerOfCollider = (_raycastOrigins.bottomLeft.x + _raycastOrigins.bottomRight.x) * (Fix.one / 2);
        FixVec2 rayDirection     = -FixVec2.up;

        // the ray distance is based on our slopeLimit
        Fix slopeCheckRayDistance = _slopeLimitTangent * (_raycastOrigins.bottomRight.x - centerOfCollider);

        FixVec2 slopeRay = new FixVec2(centerOfCollider, _raycastOrigins.bottomLeft.y);

        _raycastHit = TFPhysics.Raycast(slopeRay, rayDirection, slopeCheckRayDistance, platformMask);
        if (_raycastHit)
        {
            // bail out if we have no slope
            var angle = FixVec2.Angle(_raycastHit.normal, FixVec2.up);
            if (angle == 0)
            {
                return;
            }

            // we are moving down the slope if our normal and movement direction are in the same x direction
            var isMovingDownSlope = FixMath.Sign(_raycastHit.normal.x) == FixMath.Sign(deltaMovement.x);
            if (isMovingDownSlope)
            {
                // going down we want to speed up in most cases so the slopeSpeedMultiplier curve should be > 1 for negative angles
                Fix slopeModifier = (Fix)slopeSpeedMultiplier.Evaluate((float)-angle);
                // we add the extra downward movement here to ensure we "stick" to the surface below
                deltaMovement.y += _raycastHit.point.y - slopeRay.y - skinWidth;
                deltaMovement    = new FixVec2(deltaMovement.x, deltaMovement.y)
                                   + new FixVec2(deltaMovement.x * slopeModifier * (1 - (angle / 90)), (deltaMovement.x * slopeModifier * (angle / 90)));
                collisionState.movingDownSlope = true;
                collisionState.slopeAngle      = angle;
            }
        }
    }
Example #6
0
        internal TFRaycastHit2D Raycast(ITreeRaycastCallback callback, FixVec2 pointA, FixVec2 pointB, TFLayerMask mask)
        {
            TFRaycastHit2D hit = new TFRaycastHit2D();
            FixVec2        r   = pointB - pointA;

            if (r.GetMagnitudeSquared() <= Fix.zero)
            {
                return(hit);
            }
            r.Normalize();

            // v is perpendicular to the segment.
            FixVec2 v     = FixVec2.Cross(Fix.one, r);
            FixVec2 abs_v = FixVec2.Abs(v);

            // Separating axis for segment (Gino, p80).
            // |dot(v, p1 - c)| > dot(|v|, h)
            Fix maxFraction = Fix.one;

            // Build a bounding box for the segment.
            AABB    segmentAABB = new AABB();
            FixVec2 t           = pointA + maxFraction * (pointB - pointA);

            segmentAABB.min = FixVec2.Min(pointA, t);
            segmentAABB.max = FixVec2.Max(pointA, t);

            Stack <int> stack = new Stack <int>();

            stack.Push(rootIndex);

            List <TFRaycastOutput> hitNodes = new List <TFRaycastOutput>(2);

            while (stack.Count > 0)
            {
                var nodeId = stack.Pop();
                if (nodeId == nullNode)
                {
                    continue;
                }

                var node = nodes[nodeId];

                if (!node.aabb.Overlaps(segmentAABB))
                {
                    continue;
                }

                // Separating axis for segment (Gino, p80).
                // |dot(v, p1 - c)| > dot(|v|, h)
                var c          = node.aabb.GetCenter();
                var h          = node.aabb.GetExtents();
                var separation = FixMath.Abs(FixVec2.Dot(v, pointA - c)) - FixVec2.Dot(abs_v, h);
                if (separation > Fix.zero)
                {
                    continue;
                }

                if (node.IsLeaf())
                {
                    // If value is >= 0, then we hit the node.
                    TFRaycastHit2D rHit;
                    Fix            value = callback.RayCastCallback(pointA, pointB, maxFraction, nodeId, out rHit, mask);

                    if (value == Fix.zero)
                    {
                        // The client has terminated the ray cast.
                        if (rHit)
                        {
                            // We actually hit the node, add it to the list.
                            hitNodes.Add(new TFRaycastOutput(nodeId, rHit));
                        }
                        break;
                    }

                    if (value == maxFraction)
                    {
                        if (rHit)
                        {
                            // We actually hit the node, add it to the list.
                            hitNodes.Add(new TFRaycastOutput(nodeId, rHit));
                        }
                    }
                    else if (value > Fix.zero)
                    {
                        if (rHit)
                        {
                            // We actually hit the node, add it to the list.
                            hitNodes.Add(new TFRaycastOutput(nodeId, rHit));
                        }
                        // Update segment bounding box.
                        maxFraction = value;
                        FixVec2 g = pointA + maxFraction * (pointB - pointA);
                        segmentAABB.min = FixVec2.Min(pointA, g);
                        segmentAABB.max = FixVec2.Max(pointA, g);
                    }
                }
                else
                {
                    stack.Push(node.leftChildIndex);
                    stack.Push(node.rightChildIndex);
                }
            }

            // Decide which node was the closest to the starting point.
            Fix closestNode = maxFraction;

            for (int i = 0; i < hitNodes.Count; i++)
            {
                if (hitNodes[i].hit.fraction < closestNode)
                {
                    closestNode = hitNodes[i].hit.fraction;
                    hit         = hitNodes[i].hit;
                }
            }
            return(hit);
        }
Example #7
0
 public virtual bool Raycast(out TFRaycastHit2D hit, FixVec2 pointA, FixVec2 pointB, Fix maxFraction)
 {
     hit = default;
     return(false);
 }
Example #8
0
        public TFRaycastHit2D Raycast(FixVec2 origin, FixVec2 direction, Fix distance, TFLayerMask mask)
        {
            TFRaycastHit2D hit = dynamicTree.Raycast(this, origin, origin + direction * distance, mask);

            return(hit);
        }
    /// <summary>
    /// we have to use a bit of trickery in this one. The rays must be cast from a small distance inside of our
    /// collider (skinWidth) to avoid zero distance rays which will get the wrong normal. Because of this small offset
    /// we have to increase the ray distance skinWidth then remember to remove skinWidth from deltaMovement before
    /// actually moving the player
    /// </summary>
    void moveHorizontally(ref FixVec2 deltaMovement)
    {
        var isGoingRight     = deltaMovement.x > 0;
        var rayDistance      = FixMath.Abs(deltaMovement.x) + _skinWidth;
        var rayDirection     = isGoingRight ? FixVec2.right : -FixVec2.right;
        var initialRayOrigin = isGoingRight ? _raycastOrigins.bottomRight : _raycastOrigins.bottomLeft;

        for (var i = 0; i < totalHorizontalRays; i++)
        {
            var ray = new FixVec2(initialRayOrigin.x, initialRayOrigin.y + i * _verticalDistanceBetweenRays);

            // if we are grounded we will include oneWayPlatforms only on the first ray (the bottom one). this will allow us to
            // walk up sloped oneWayPlatforms.
            if (i == 0 && collisionState.wasGroundedLastFrame)
            {
                _raycastHit = TFPhysics.Raycast(ray, rayDirection, rayDistance, platformMask);
            }
            else
            {
                _raycastHit = TFPhysics.Raycast(ray, rayDirection, rayDistance, platformMask & ~oneWayPlatformMask);
            }

            if (_raycastHit)
            {
                // the bottom ray can hit a slope but no other ray can so we have special handling for these cases
                if (i == 0 && handleHorizontalSlope(ref deltaMovement, FixVec2.Angle(_raycastHit.normal, FixVec2.up)))
                {
                    _raycastHitsThisFrame.Add(_raycastHit);
                    // if we weren't grounded last frame, that means we're landing on a slope horizontally.
                    // this ensures that we stay flush to that slope
                    if (!collisionState.wasGroundedLastFrame)
                    {
                        Fix flushDistance = FixMath.Sign(deltaMovement.x) * (_raycastHit.distance - skinWidth);
                        tfTransform.Position += new FixVec2(flushDistance, 0);
                    }
                    break;
                }

                // set our new deltaMovement and recalculate the rayDistance taking it into account
                deltaMovement.x = _raycastHit.point.x - ray.x;
                rayDistance     = FixMath.Abs(deltaMovement.x);

                // remember to remove the skinWidth from our deltaMovement
                if (isGoingRight)
                {
                    deltaMovement.x     -= _skinWidth;
                    collisionState.right = true;
                }
                else
                {
                    deltaMovement.x    += _skinWidth;
                    collisionState.left = true;
                }

                _raycastHitsThisFrame.Add(_raycastHit);

                // we add a small fudge factor for the float operations here. if our rayDistance is smaller
                // than the width + fudge bail out because we have a direct impact
                if (rayDistance < _skinWidth + kSkinWidthFloatFudgeFactor)
                {
                    break;
                }
            }
        }
    }
Example #10
0
        public static TFRaycastHit2D Raycast(FixVec2 origin, FixVec2 direction, Fix distance, TFLayerMask mask)
        {
            TFRaycastHit2D hit = physicsScene.Raycast(origin, direction, distance, mask);

            return(hit);
        }
Example #11
0
        public override bool Raycast(out TFRaycastHit2D hit, FixVec2 pointA, FixVec2 pointB, Fix maxFraction)
        {
            hit = new TFRaycastHit2D();

            // Put the ray into the polygon's frame of reference.
            var p1 = u.Transposed() * (pointA - (FixVec2)tdTransform.Position);
            var p2 = u.Transposed() * (pointB - (FixVec2)tdTransform.Position);
            var d  = p2 - p1;

            Fix lower = Fix.zero, upper = maxFraction;


            var index = -1;

            for (var i = 0; i < vertices.Count; ++i)
            {
                // p = p1 + a * d
                // dot(normal, p - v) = 0
                // dot(normal, p1 - v) + a * dot(normal, d) = 0
                var numerator   = FixVec2.Dot(normals[i], GetVertex(i) - p1);
                var denominator = FixVec2.Dot(normals[i], d);

                if (denominator == Fix.zero)
                {
                    if (numerator < Fix.zero)
                    {
                        return(false);
                    }
                }
                else
                {
                    // Note: we want this predicate without division:
                    // lower < numerator / denominator, where denominator < 0
                    // Since denominator < 0, we have to flip the inequality:
                    // lower < numerator / denominator <==> denominator * lower > numerator.
                    if (denominator < Fix.zero && numerator < lower * denominator)
                    {
                        // Increase lower.
                        // The segment enters this half-space.
                        lower = numerator / denominator;
                        index = i;
                    }
                    else if (denominator > Fix.zero && numerator < upper * denominator)
                    {
                        // Decrease upper.
                        // The segment exits this half-space.
                        upper = numerator / denominator;
                    }
                }

                // The use of epsilon here causes the assert on lower to trip
                // in some cases. Apparently the use of epsilon was to make edge
                // shapes work, but now those are handled separately.
                //if (upper < lower - b2_epsilon)
                if (upper < lower)
                {
                    return(false);
                }
            }

            if (index >= 0)
            {
                hit.fraction = lower;
                hit.normal   = tdTransform.Rotation * normals[index];
                hit.collider = this;
                return(true);
            }
            return(false);
        }
Example #12
0
 public bool Raycast(FixVec2 pointA, FixVec2 pointB, out TFRaycastHit2D hit)
 {
     hit = default;
     dynamicTree.Raycast(this, pointA, pointB);
     return(false);
 }