public virtual void ApplyTraction() { if (kineticEnergies.horizontal.X == Fix.Zero) { return; } bool startedDir = FixMath.Sign(kineticEnergies.horizontal.X) == 1 ? true : false; if (startedDir) { kineticEnergies.horizontal._x -= cInfo.attributes.traction; if (kineticEnergies.horizontal.X < Fix.Zero) { kineticEnergies.horizontal = FixVec2.Zero; } } else { kineticEnergies.horizontal._x += cInfo.attributes.traction; if (kineticEnergies.horizontal.X > Fix.Zero) { kineticEnergies.horizontal = FixVec2.Zero; } } }
// The smaller of the two possible angles between the two vectors is returned, therefore the result will never be greater than 180 degrees or smaller than -180 degrees. // If you imagine the from and to vectors as lines on a piece of paper, both originating from the same point, then the /axis/ vector would point up out of the paper. // The measured angle between the two vectors would be positive in a clockwise direction and negative in an anti-clockwise direction. public static Fix64 SignedAngle(FixVector3 from, FixVector3 to, FixVector3 axis) { FixVector3 fromNorm = from.Normalized, toNorm = to.Normalized; Fix64 unsignedAngle = FixMath.Acos(FixMath.Clamp(Dot(fromNorm, toNorm), -Fix64.ONE, Fix64.ONE)) * FixMath.Rad2Deg; Fix64 sign = FixMath.Sign(Dot(axis, Cross(fromNorm, toNorm))); return(unsignedAngle * sign); }
/// <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; } } }
/// <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; } } } }