protected static SteeringVelocity DoReachOrientation(Entity <Game> owner, Fix targetOrientation) { var tolerance = owner.Get <AlignTolerance>(); var steering = SteeringVelocity.Zero; var orientation = owner.Get <Orientation>().Value; var rotation = ArithmeticUtils.WrapAngleAroundZero(targetOrientation - orientation); var rotationSize = FixMath.Abs(rotation); if (rotationSize <= tolerance.Angle) { return(steering); } var maxAngularVelocity = owner.Get <MaxAngularVelocity>().Value; var angular = maxAngularVelocity * rotation / rotationSize; var decelerationAngle = tolerance.DecelerationAngle; if (rotationSize <= decelerationAngle) { angular *= rotationSize / decelerationAngle; } steering.Angular = angular; return(steering); }
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; } } } }
public Mat22 Abs() { Mat22 m = new Mat22(); m.m00 = FixMath.Abs(m00); m.m01 = FixMath.Abs(m01); m.m10 = FixMath.Abs(m10); m.m11 = FixMath.Abs(m11); return(m); }
/// Wrap a vector to a bounding box. public static FixVec2 WrapVect(AABB aabb, FixVec2 v) { // wrap a vector to a bbox var ix = FixMath.Abs(aabb.R - aabb.L); var modx = (v.X - aabb.L) % ix; var x = (modx > 0) ? modx : modx + ix; var iy = FixMath.Abs(aabb.T - aabb.B); var mody = (v.Y - aabb.B) % iy; var y = (mody > 0) ? mody : mody + iy; return(new FixVec2(x + aabb.L, y + aabb.B)); }
/// <summary> /// this should be called anytime you have to modify the BoxCollider2D at runtime. It will recalculate the distance between the rays used for collision detection. /// It is also used in the skinWidth setter in case it is changed at runtime. /// </summary> public void recalculateDistanceBetweenRays() { FixVec2 size = boxCollider.Size; // figure out the distance between our rays in both directions // horizontal var colliderUseableHeight = size.y * FixMath.Abs(tfTransform.LocalScale.y) - (2 * _skinWidth); _verticalDistanceBetweenRays = colliderUseableHeight / (totalHorizontalRays - Fix.one); // vertical var colliderUseableWidth = size.x * FixMath.Abs(tfTransform.LocalScale.x) - (2 * _skinWidth); _horizontalDistanceBetweenRays = colliderUseableWidth / (totalVerticalRays - Fix.one); }
/// <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 FixVec3 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. } }
public bool CheckInterrupt() { GameInfo gi = cController.cacheGM.gameInfo; if (cController.CheckForAttack()) { return(true); } else if (FixMath.Abs(cInput.LeftStick().axis.X) >= gi.walkSensitivity) { cController.ChangeState("Walk"); return(true); } return(false); }
public void OnUpdate() { if (!CheckInterrupt()) { float perc = (float)((FixMath.Abs(cInput.LeftStick().axis.X) - cController.cacheGM.gameInfo.walkSensitivity) / (Fix.One - cController.cacheGM.gameInfo.walkSensitivity)); Vector2 wantedSpeed = new Vector2(); wantedSpeed.x = (cController.vars.facingRight ? 1 : -1) * Mathf.Lerp((float)cController.cInfo.attributes.minWalkSpeed, (float)cController.cInfo.attributes.walkSpeed, perc); Vector2 currentSpeed = (Vector2)cController.kineticEnergies.horizontal; currentSpeed = Vector2.Lerp(currentSpeed, wantedSpeed, (float)cController.cInfo.attributes.walkAcceleration); cController.kineticEnergies.horizontal = (FixVec2)currentSpeed; cController.HandleForces(); } }
void moveVertically(ref FixVec3 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++) { var ray = new FixVec2(initialRayOrigin.x + i * _horizontalDistanceBetweenRays, initialRayOrigin.y); } }
internal void Raycast(ITreeRaycastCallback callback, FixVec2 pointA, FixVec2 pointB) { FixVec2 r = pointB - pointA; if (r.GetMagnitudeSquared() <= Fix.zero) { return; } 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); 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()) { Fix value = callback.RayCastCallback(pointA, pointB, maxFraction, nodeId); if (value == Fix.zero) { // The client has terminated the ray cast. return; } if (value > Fix.zero) { // 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); } } }
public static Fix Proximity(AABB a, AABB b) { return(FixMath.Abs(a.L + a.R - b.L - b.R) + FixMath.Abs(a.B + a.T - b.B - b.T)); }
public void ApplyImpulse() { // Early out and positional correct if both objects have infinite mass if (A.invMass + B.invMass == 0) { InfiniteMassCorrection(); return; } for (int i = 0; i < contactCount; ++i) { // Calculate radii from COM to contact FixVec2 ra = contacts[i] - A.Position; FixVec2 rb = contacts[i] - B.Position; //Relative velocity FixVec2 rv = B.info.velocity + FixVec2.Cross(B.info.angularVelocity, rb) - A.info.velocity - FixVec2.Cross(A.info.angularVelocity, ra); //Relative velocity along the normal Fix contactVel = rv.Dot(normal); if (contactVel > 0) { return; } Fix raCrossN = FixVec2.Cross(ra, normal); Fix rbCrossN = FixVec2.Cross(rb, normal); Fix invMassSum = A.invMass + B.invMass + (raCrossN * raCrossN) * A.invInertia + (rbCrossN * rbCrossN) * B.invInertia; // Calculate impulse scalar Fix j = -(Fix.one + e) * contactVel; j /= invMassSum; j /= contactCount; // Apply impulse FixVec2 impulse = normal * j; A.ApplyImpulse(-impulse, ra); B.ApplyImpulse(impulse, rb); // Friction Impulse rv = B.info.velocity + FixVec2.Cross(B.info.angularVelocity, rb) - A.info.velocity - FixVec2.Cross(A.info.angularVelocity, ra); FixVec2 t = rv - (normal * FixVec2.Dot(rv, normal)); t = t.Normalized(); // j tangent magnitude Fix jt = -FixVec2.Dot(rv, t); jt /= invMassSum; jt /= contactCount; //Don't apply tiny friction impulses if (FixMath.Abs(jt) <= Fix.zero) { return; } // Coulumb's law FixVec2 tangentImpulse; if (FixMath.Abs(jt) < j * sf) { tangentImpulse = t * jt; } else { tangentImpulse = t * -j * df; } // Apply friction impulse A.ApplyImpulse(-tangentImpulse, ra); B.ApplyImpulse(tangentImpulse, rb); } }
// Check if polygon A is going to collide with polygon B for the given velocity public static PolygonCollisionResult CheckCollision(FixPolygon polygonA, FixPolygon polygonB, FixVec2 velocity) { var result = new PolygonCollisionResult { Intersect = true, WillIntersect = true }; var edgeCountA = polygonA.Edges.Length; var edgeCountB = polygonB.Edges.Length; var minIntervalDistance = Fix.MaxValue; var translationAxis = new FixVec2(); // Loop through all the edges of both polygons for (var edgeIndex = 0; edgeIndex < edgeCountA + edgeCountB; edgeIndex++) { var edge = edgeIndex < edgeCountA ? polygonA.Edges[edgeIndex] : polygonB.Edges[edgeIndex - edgeCountA]; // ===== 1. Find if the polygons are currently intersecting ===== // Find the axis perpendicular to the current edge var axis = new FixVec2(-edge.Y, edge.X).Normalize(); // Find the projection of the polygon on the current axis Fix minA; Fix maxA; Fix minB; Fix maxB; ProjectPolygon(axis, polygonA, out minA, out maxA); ProjectPolygon(axis, polygonB, out minB, out maxB); // Check if the polygon projections are currently intersecting if (IntervalDistance(minA, maxA, minB, maxB) > 0) { result.Intersect = false; } // ===== 2. Now find if the polygons *will* intersect ===== // Project the velocity on the current axis var velocityProjection = axis.Dot(velocity); // Get the projection of polygon A during the movement if (velocityProjection < 0) { minA += velocityProjection; } else { maxA += velocityProjection; } // Do the same test as above for the new projection var intervalDistance = IntervalDistance(minA, maxA, minB, maxB); if (intervalDistance > 0) { result.WillIntersect = false; } // If the polygons are not intersecting and won't intersect, exit the loop if (!result.Intersect && !result.WillIntersect) { break; } // Check if the current interval distance is the minimum one. If so store // the interval distance and the current distance. // This will be used to calculate the minimum translation FixVec2 intervalDistance = FixMath.Abs(intervalDistance); if (intervalDistance < minIntervalDistance) { minIntervalDistance = intervalDistance; translationAxis = axis; var d = polygonA.Center - polygonB.Center; if (d.Dot(translationAxis) < 0) { translationAxis = -translationAxis; } } } // The minimum translation FixVec2 can be used to push the polygons apart. // First moves the polygons by their velocity // then move polygonA by MinimumTranslationVector. if (result.WillIntersect) { result.MinimumTranslationVector = translationAxis * minIntervalDistance; } return(result); }
public override void OnEnable() { base.OnEnable(); PlayerControlComponents.OnAdd() .Where(entity => !entity.HasComponent <NetworkPlayerComponent>()) .Subscribe(entity => { var playerControlComponent = entity.GetComponent <PlayerControlComponent>(); var characterController = entity.GetComponent <CharacterController>(); var viewComponent = entity.GetComponent <ViewComponent>(); Observable.EveryUpdate().Subscribe(_ => { if (characterController.isGrounded) { var angle = Vector3.Angle(viewComponent.Transforms[0].forward, mainCamera.transform.forward); if (angle < InvertRange.Min || angle > InvertRange.Max) { playerControlComponent.motion = new FixVector3(Input.GetAxis(InputParameters.Horizontal), 0, Input.GetAxis(InputParameters.Vertical)); } else { playerControlComponent.motion = new FixVector3(Input.GetAxis(InputParameters.Vertical), 0, Input.GetAxis(InputParameters.Horizontal)); } playerControlComponent.motion = (FixVector3)viewComponent.Transforms[0].TransformDirection((Vector3)playerControlComponent.motion); playerControlComponent.motion *= playerControlComponent.Run; if (Input.GetButton(InputParameters.Jump)) { playerControlComponent.motion.y = playerControlComponent.Jump; } } playerControlComponent.motion.y -= playerControlComponent.Gravity * Time.deltaTime; characterController.Move((Vector3)playerControlComponent.motion * Time.deltaTime); }).AddTo(this.Disposer).AddTo(playerControlComponent.Disposer); }).AddTo(this.Disposer); NetwrokTimeline.OnReverse((entity, result) => { var playerControlComponent = entity.GetComponent <PlayerControlComponent>(); var viewComponent = entity.GetComponent <ViewComponent>(); var playerControlResult = (PlayerControlResult)result[0]; viewComponent.Transforms[0].rotation = playerControlResult.Rotation; playerControlComponent.Follow.rotation = playerControlResult.Follow; viewComponent.Transforms[0].position = playerControlResult.Position; }).AddTo(this.Disposer); NetwrokTimeline.OnForward((entity, userInputData, deltaTime) => { var playerControlComponent = entity.GetComponent <PlayerControlComponent>(); var characterController = entity.GetComponent <CharacterController>(); var viewComponent = entity.GetComponent <ViewComponent>(); var animator = entity.GetComponent <Animator>(); var axisInput = userInputData[0].GetInput <AxisInput>(); var keyInput = userInputData[1].GetInput <KeyInput>(); var mouseInput = userInputData[2].GetInput <MouseInput>(); var playerControlResult = new PlayerControlResult(); var smoothTime = playerControlComponent.smoothTime; var yAngle = Fix64.Zero; playerControlResult.Rotation = viewComponent.Transforms[0].rotation; playerControlResult.Follow = playerControlComponent.Follow.rotation; playerControlResult.Position = viewComponent.Transforms[0].position; if (mouseInput != null && keyInput != null && axisInput != null) { var rotLeftRight = mouseInput.Delta.x * playerControlComponent.MouseSensivity.x * deltaTime; var rotUpDown = mouseInput.Delta.y * playerControlComponent.MouseSensivity.y * deltaTime; if (mouseInput.MouseButtons.Contains(1)) { playerControlComponent.aimTime += deltaTime; if (playerControlComponent.Aim.Value == AimMode.Free && playerControlComponent.aimTime > ShoulderAimTime) { playerControlComponent.Aim.Value = AimMode.Shoulder; } } else { if (playerControlComponent.Aim.Value == AimMode.Free) { if (playerControlComponent.aimTime > 0) { playerControlComponent.Aim.Value = AimMode.AimDownSight; } } else if (playerControlComponent.aimTime > 0) { playerControlComponent.Aim.Value = AimMode.Free; } playerControlComponent.aimTime = 0; } if (playerControlComponent.Aim.Value == AimMode.Free && keyInput.KeyCodes.Contains((int)KeyCode.LeftAlt)) { playerControlComponent.Follow.rotation = (Quaternion)((FixQuaternion)playerControlComponent.Follow.rotation * FixQuaternion.Euler(-rotUpDown, rotLeftRight, 0)); animator.SetFloat(Head_Vertical_f, 0); animator.SetFloat(Body_Vertical_f, 0); smoothTime = SmoothTime; } else { if (smoothTime > 0) { playerControlComponent.Follow.localRotation = (Quaternion)FixQuaternion.Lerp(FixQuaternion.identity, (FixQuaternion)playerControlComponent.Follow.localRotation, FixMath.Clamp01(smoothTime / SmoothTime)); animator.SetFloat(Head_Vertical_f, 0); animator.SetFloat(Body_Vertical_f, 0); smoothTime -= deltaTime; } else { yAngle = ClampAngle(playerControlComponent.Follow.localEulerAngles.x, playerControlComponent.YAngleLimit.Min, playerControlComponent.YAngleLimit.Max); yAngle = ClampAngle(yAngle - rotUpDown, playerControlComponent.YAngleLimit.Min, playerControlComponent.YAngleLimit.Max); var vertical = 2 * (0.5f - FixMath.Abs(yAngle - playerControlComponent.YAngleLimit.Min) / FixMath.Abs(playerControlComponent.YAngleLimit.Max - playerControlComponent.YAngleLimit.Min)); animator.SetFloat(Head_Vertical_f, 0.5f * (float)vertical); animator.SetFloat(Body_Vertical_f, (float)vertical); viewComponent.Transforms[0].rotation = (Quaternion)((FixQuaternion)viewComponent.Transforms[0].rotation * FixQuaternion.Euler(0, rotLeftRight, 0)); playerControlComponent.Follow.localEulerAngles = (Vector3) new FixVector3(yAngle, 0, 0); } } if (characterController.isGrounded) { playerControlComponent.motion = new FixVector3((float)axisInput.Horizontal, 0, (float)axisInput.Vertical); playerControlComponent.motion = (FixVector3)viewComponent.Transforms[0].TransformDirection((Vector3)playerControlComponent.motion); Fix64 t = 0.75f; Fix64 speed = playerControlComponent.Walk; Fix64 h = FixMath.Abs(axisInput.Horizontal); Fix64 sum = h + FixMath.Abs(axisInput.Vertical); Fix64 horizontal = sum == 0 ? 0 : axisInput.Horizontal * (h / sum); if (playerControlComponent.Aim.Value == AimMode.Shoulder) { t = 0.5f; } else if (playerControlComponent.Aim.Value == AimMode.AimDownSight) { t = 0.25f; } else if (keyInput != null && keyInput.KeyCodes.Contains((int)KeyCode.LeftShift)) { t = 1f; speed = playerControlComponent.Run; } playerControlComponent.Crouched = keyInput.KeyCodes.Contains((int)KeyCode.LeftControl); animator.SetBool(Crouch_b, playerControlComponent.Crouched); if (playerControlComponent.Crouched) { speed = 0; } playerControlComponent.motion *= t * speed; animator.SetFloat(Speed_f, (axisInput.Horizontal != Fix64.Zero || axisInput.Vertical != Fix64.Zero) ? (float)FixMath.Clamp(t, 0.3f, 1f) : 0f); animator.SetFloat(Head_Horizontal_f, 0.25f * (float)horizontal); animator.SetFloat(Body_Horizontal_f, 0.35f * (float)horizontal); } if (keyInput.KeyCodes.Contains((int)KeyCode.Space) && !playerControlComponent.Crouched) { playerControlComponent.motion.y = playerControlComponent.Jump; } } playerControlComponent.motion.y -= playerControlComponent.Gravity * deltaTime; playerControlComponent.smoothTime = smoothTime; characterController.Move((Vector3)(playerControlComponent.motion * deltaTime)); animator.SetBool(Jump_b, playerControlComponent.motion.y > 0); animator.SetBool(Grounded, characterController.isGrounded && playerControlComponent.motion.y <= 0); return(new IUserInputResult[] { playerControlResult }); }).AddTo(this.Disposer); }
/// <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; } } } }
/// <summary> /// handles adjusting deltaMovement if we are going up a slope. /// </summary> /// <returns><c>true</c>, if horizontal slope was handled, <c>false</c> otherwise.</returns> /// <param name="deltaMovement">Delta movement.</param> /// <param name="angle">Angle.</param> bool handleHorizontalSlope(ref FixVec2 deltaMovement, Fix angle) { // disregard 90 degree angles (walls) if (FixMath.Round(angle) == 90) { return(false); } if (angle < slopeLimit) { // we only need to adjust the deltaMovement if we are not jumping // TODO: this uses a magic number which isn't ideal! The alternative is to have the user pass in if there is a jump this frame if (deltaMovement.y < jumpingThreshold) { // apply the slopeModifier to slow our movement up the slope Fix slopeModifier = (Fix)slopeSpeedMultiplier.Evaluate((float)angle); deltaMovement.x *= slopeModifier; // we dont set collisions on the sides for this since a slope is not technically a side collision. // smooth y movement when we climb. we make the y movement equivalent to the actual y location that corresponds // to our new x location using our good friend Pythagoras deltaMovement.y = FixMath.Abs(FixMath.Tan(angle * FixMath.Deg2Rad) * deltaMovement.x); var isGoingRight = deltaMovement.x > 0; // safety check. we fire a ray in the direction of movement just in case the diagonal we calculated above ends up // going through a wall. if the ray hits, we back off the horizontal movement to stay in bounds. var ray = isGoingRight ? _raycastOrigins.bottomRight : _raycastOrigins.bottomLeft; TFRaycastHit2D raycastHit; if (collisionState.wasGroundedLastFrame) { raycastHit = TFPhysics.Raycast((FixVec2)ray, (FixVec2)deltaMovement.Normalized(), deltaMovement.GetMagnitude(), platformMask); } else { raycastHit = TFPhysics.Raycast((FixVec2)ray, (FixVec2)deltaMovement.Normalized(), deltaMovement.GetMagnitude(), platformMask & ~oneWayPlatformMask); } if (raycastHit) { // we crossed an edge when using Pythagoras calculation, so we set the actual delta movement to the ray hit location deltaMovement = raycastHit.point - ray; if (isGoingRight) { deltaMovement.x -= _skinWidth; } else { deltaMovement.x += _skinWidth; } } _isGoingUpSlope = true; collisionState.below = true; collisionState.slopeAngle = -angle; } } else // too steep. get out of here { deltaMovement.x = Fix.zero; } return(true); }
public static DynValue abs(ScriptExecutionContext executionContext, CallbackArguments args) { return(exec1(args, "abs", (x) => FixMath.Abs(x))); }
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); }
private bool Approximately(Fix a, Fix b) { return(FixMath.Abs(b - a) < (Fix)0.001); }