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)); }
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; } } }
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); }
public virtual bool Raycast(out TFRaycastHit2D hit, FixVec2 pointA, FixVec2 pointB, Fix maxFraction) { hit = default; return(false); }
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; } } } }
public static TFRaycastHit2D Raycast(FixVec2 origin, FixVec2 direction, Fix distance, TFLayerMask mask) { TFRaycastHit2D hit = physicsScene.Raycast(origin, direction, distance, mask); return(hit); }
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); }
public bool Raycast(FixVec2 pointA, FixVec2 pointB, out TFRaycastHit2D hit) { hit = default; dynamicTree.Raycast(this, pointA, pointB); return(false); }