Beispiel #1
0
        // Token: 0x06002966 RID: 10598 RVA: 0x001C0DF0 File Offset: 0x001BEFF0
        public static Vector2 CalculateAccelerationToReachPoint(Vector2 deltaPosition, Vector2 targetVelocity, Vector2 currentVelocity, float forwardsAcceleration, float rotationSpeed, float maxSpeed, Vector2 forwardsVector)
        {
            if (forwardsAcceleration <= 0f)
            {
                return(Vector2.zero);
            }
            float magnitude = currentVelocity.magnitude;
            float a         = magnitude * rotationSpeed * 0.0174532924f;

            a               = Mathf.Max(a, forwardsAcceleration);
            deltaPosition   = VectorMath.ComplexMultiplyConjugate(deltaPosition, forwardsVector);
            targetVelocity  = VectorMath.ComplexMultiplyConjugate(targetVelocity, forwardsVector);
            currentVelocity = VectorMath.ComplexMultiplyConjugate(currentVelocity, forwardsVector);
            float num  = 1f / (forwardsAcceleration * forwardsAcceleration);
            float num2 = 1f / (forwardsAcceleration * forwardsAcceleration);

            if (targetVelocity == Vector2.zero)
            {
                float num3 = 0.01f;
                float num4 = 10f;
                while (num4 - num3 > 0.01f)
                {
                    float   num5    = (num4 + num3) * 0.5f;
                    Vector2 vector  = (6f * deltaPosition - 4f * num5 * currentVelocity) / (num5 * num5);
                    Vector2 a2      = 6f * (num5 * currentVelocity - 2f * deltaPosition) / (num5 * num5 * num5);
                    Vector2 vector2 = vector + a2 * num5;
                    if (vector.x * vector.x * num + vector.y * vector.y * num2 > 1f || vector2.x * vector2.x * num + vector2.y * vector2.y * num2 > 1f)
                    {
                        num3 = num5;
                    }
                    else
                    {
                        num4 = num5;
                    }
                }
                Vector2 vector3 = (6f * deltaPosition - 4f * num4 * currentVelocity) / (num4 * num4);
                vector3.y *= 2f;
                float num6 = vector3.x * vector3.x * num + vector3.y * vector3.y * num2;
                if (num6 > 1f)
                {
                    vector3 /= Mathf.Sqrt(num6);
                }
                return(VectorMath.ComplexMultiply(vector3, forwardsVector));
            }
            float   num7;
            Vector2 a3         = VectorMath.Normalize(targetVelocity, out num7);
            float   magnitude2 = deltaPosition.magnitude;
            Vector2 vector4    = ((deltaPosition - a3 * Math.Min(0.5f * magnitude2 * num7 / (magnitude + num7), maxSpeed * 1.5f)).normalized * maxSpeed - currentVelocity) * 10f;
            float   num8       = vector4.x * vector4.x * num + vector4.y * vector4.y * num2;

            if (num8 > 1f)
            {
                vector4 /= Mathf.Sqrt(num8);
            }
            return(VectorMath.ComplexMultiply(vector4, forwardsVector));
        }
            // Token: 0x06002DF4 RID: 11764 RVA: 0x001D5714 File Offset: 0x001D3914
            public static Agent.VO SegmentObstacle(Vector2 segmentStart, Vector2 segmentEnd, Vector2 offset, float radius, float inverseDt, float inverseDeltaTime)
            {
                Agent.VO vo = default(Agent.VO);
                vo.weightFactor = 1f;
                vo.weightBonus  = Mathf.Max(radius, 1f) * 40f;
                Vector3 vector = VectorMath.ClosestPointOnSegment(segmentStart, segmentEnd, Vector2.zero);

                if (vector.magnitude <= radius)
                {
                    vo.colliding    = true;
                    vo.line1        = vector.normalized * (vector.magnitude - radius) * 0.3f * inverseDeltaTime;
                    vo.dir1         = new Vector2(vo.line1.y, -vo.line1.x).normalized;
                    vo.line1       += offset;
                    vo.cutoffDir    = Vector2.zero;
                    vo.cutoffLine   = Vector2.zero;
                    vo.dir2         = Vector2.zero;
                    vo.line2        = Vector2.zero;
                    vo.radius       = 0f;
                    vo.segmentStart = Vector2.zero;
                    vo.segmentEnd   = Vector2.zero;
                    vo.segment      = false;
                }
                else
                {
                    vo.colliding  = false;
                    segmentStart *= inverseDt;
                    segmentEnd   *= inverseDt;
                    radius       *= inverseDt;
                    Vector2 normalized = (segmentEnd - segmentStart).normalized;
                    vo.cutoffDir   = normalized;
                    vo.cutoffLine  = segmentStart + new Vector2(-normalized.y, normalized.x) * radius;
                    vo.cutoffLine += offset;
                    float   sqrMagnitude  = segmentStart.sqrMagnitude;
                    Vector2 vector2       = -VectorMath.ComplexMultiply(segmentStart, new Vector2(radius, Mathf.Sqrt(Mathf.Max(0f, sqrMagnitude - radius * radius)))) / sqrMagnitude;
                    float   sqrMagnitude2 = segmentEnd.sqrMagnitude;
                    Vector2 vector3       = -VectorMath.ComplexMultiply(segmentEnd, new Vector2(radius, -Mathf.Sqrt(Mathf.Max(0f, sqrMagnitude2 - radius * radius)))) / sqrMagnitude2;
                    vo.line1        = segmentStart + vector2 * radius + offset;
                    vo.line2        = segmentEnd + vector3 * radius + offset;
                    vo.dir1         = new Vector2(vector2.y, -vector2.x);
                    vo.dir2         = new Vector2(vector3.y, -vector3.x);
                    vo.segmentStart = segmentStart;
                    vo.segmentEnd   = segmentEnd;
                    vo.radius       = radius;
                    vo.segment      = true;
                }
                return(vo);
            }
            /// <summary>
            /// Creates a VO for avoiding another agent.
            /// Note that the segment is directed, the agent will want to be on the left side of the segment.
            /// </summary>
            public static VO SegmentObstacle(Vector2 segmentStart, Vector2 segmentEnd, Vector2 offset, float radius, float inverseDt, float inverseDeltaTime)
            {
                var vo = new VO();

                // Adjusted so that a parameter weightFactor of 1 will be the default ("natural") weight factor
                vo.weightFactor = 1;
                // Just higher than anything else
                vo.weightBonus = Mathf.Max(radius, 1) * 40;

                var closestOnSegment = VectorMath.ClosestPointOnSegment(segmentStart, segmentEnd, Vector2.zero);

                // Collision?
                if (closestOnSegment.magnitude <= radius)
                {
                    vo.colliding = true;

                    vo.line1  = closestOnSegment.normalized * (closestOnSegment.magnitude - radius) * 0.3f * inverseDeltaTime;
                    vo.dir1   = new Vector2(vo.line1.y, -vo.line1.x).normalized;
                    vo.line1 += offset;

                    vo.cutoffDir  = Vector2.zero;
                    vo.cutoffLine = Vector2.zero;
                    vo.dir2       = Vector2.zero;
                    vo.line2      = Vector2.zero;
                    vo.radius     = 0;

                    vo.segmentStart = Vector2.zero;
                    vo.segmentEnd   = Vector2.zero;
                    vo.segment      = false;
                }
                else
                {
                    vo.colliding = false;

                    segmentStart *= inverseDt;
                    segmentEnd   *= inverseDt;
                    radius       *= inverseDt;

                    var cutoffTangent = (segmentEnd - segmentStart).normalized;
                    vo.cutoffDir   = cutoffTangent;
                    vo.cutoffLine  = segmentStart + new Vector2(-cutoffTangent.y, cutoffTangent.x) * radius;
                    vo.cutoffLine += offset;

                    // See documentation for details
                    // The call to Max is just to prevent floating point errors causing NaNs to appear
                    var startSqrMagnitude = segmentStart.sqrMagnitude;
                    var normal1           = -VectorMath.ComplexMultiply(segmentStart, new Vector2(radius, Mathf.Sqrt(Mathf.Max(0, startSqrMagnitude - radius * radius)))) / startSqrMagnitude;
                    var endSqrMagnitude   = segmentEnd.sqrMagnitude;
                    var normal2           = -VectorMath.ComplexMultiply(segmentEnd, new Vector2(radius, -Mathf.Sqrt(Mathf.Max(0, endSqrMagnitude - radius * radius)))) / endSqrMagnitude;

                    vo.line1 = segmentStart + normal1 * radius + offset;
                    vo.line2 = segmentEnd + normal2 * radius + offset;

                    // Note that the normals are already normalized
                    vo.dir1 = new Vector2(normal1.y, -normal1.x);
                    vo.dir2 = new Vector2(normal2.y, -normal2.x);

                    vo.segmentStart = segmentStart;
                    vo.segmentEnd   = segmentEnd;
                    vo.radius       = radius;
                    vo.segment      = true;
                }

                return(vo);
            }
Beispiel #4
0
        /** Calculate an acceleration to move deltaPosition units and get there with approximately a velocity of targetVelocity */
        public static Vector2 CalculateAccelerationToReachPoint(Vector2 deltaPosition, Vector2 targetVelocity, Vector2 currentVelocity, float forwardsAcceleration, float rotationSpeed, float maxSpeed, Vector2 forwardsVector)
        {
            // Guard against div by zero
            if (forwardsAcceleration <= 0)
            {
                return(Vector2.zero);
            }

            float currentSpeed = currentVelocity.magnitude;

            // Convert rotation speed to an acceleration
            // See https://en.wikipedia.org/wiki/Centripetal_force
            var sidewaysAcceleration = currentSpeed * rotationSpeed * Mathf.Deg2Rad;

            // To avoid weird behaviour when the rotation speed is very low we allow the agent to accelerate sideways without rotating much
            // if the rotation speed is very small. Also guards against division by zero.
            sidewaysAcceleration = Mathf.Max(sidewaysAcceleration, forwardsAcceleration);
            sidewaysAcceleration = forwardsAcceleration;

            // Transform coordinates to local space where +X is the forwards direction
            // This is essentially equivalent to Transform.InverseTransformDirection.
            deltaPosition   = VectorMath.ComplexMultiplyConjugate(deltaPosition.ToPFV2(), forwardsVector.ToPFV2()).ToUnityV2();
            targetVelocity  = VectorMath.ComplexMultiplyConjugate(targetVelocity.ToPFV2(), forwardsVector.ToPFV2()).ToUnityV2();
            currentVelocity = VectorMath.ComplexMultiplyConjugate(currentVelocity.ToPFV2(), forwardsVector.ToPFV2()).ToUnityV2();
            float ellipseSqrFactorX = 1 / (forwardsAcceleration * forwardsAcceleration);
            float ellipseSqrFactorY = 1 / (sidewaysAcceleration * sidewaysAcceleration);

            // If the target velocity is zero we can use a more fancy approach
            // and calculate a nicer path.
            // In particular, this is the case at the end of the path.
            if (targetVelocity == Vector2.zero)
            {
                // Run a binary search over the time to get to the target point.
                float mn = 0.01f;
                float mx = 10;
                while (mx - mn > 0.01f)
                {
                    var time = (mx + mn) * 0.5f;

                    // Given that we want to move deltaPosition units from out current position, that our current velocity is given
                    // and that when we reach the target we want our velocity to be zero. Also assume that our acceleration will
                    // vary linearly during the slowdown. Then we can calculate what our acceleration should be during this frame.

                    //{ t = time
                    //{ deltaPosition = vt + at^2/2 + qt^3/6
                    //{ 0 = v + at + qt^2/2
                    //{ solve for a
                    // a = acceleration vector
                    // q = derivative of the acceleration vector
                    var a = (6 * deltaPosition - 4 * time * currentVelocity) / (time * time);
                    var q = 6 * (time * currentVelocity - 2 * deltaPosition) / (time * time * time);

                    // Make sure the acceleration is not greater than our maximum allowed acceleration.
                    // If it is we increase the time we want to use to get to the target
                    // and if it is not, we decrease the time to get there faster.
                    // Since the acceleration is described by acceleration = a + q*t
                    // we only need to check at t=0 and t=time.
                    // Note that the acceleration limit is described by an ellipse, not a circle.
                    var nextA = a + q * time;
                    if (a.x * a.x * ellipseSqrFactorX + a.y * a.y * ellipseSqrFactorY > 1.0f || nextA.x * nextA.x * ellipseSqrFactorX + nextA.y * nextA.y * ellipseSqrFactorY > 1.0f)
                    {
                        mn = time;
                    }
                    else
                    {
                        mx = time;
                    }
                }

                var finalAcceleration = (6 * deltaPosition - 4 * mx * currentVelocity) / (mx * mx);

                // Boosting
                {
                    // The trajectory calculated above has a tendency to use very wide arcs
                    // and that does unfortunately not look particularly good in some cases.
                    // Here we amplify the component of the acceleration that is perpendicular
                    // to our current velocity. This will make the agent turn towards the
                    // target quicker.
                    // How much amplification to use. Value is unitless.
                    const float Boost = 1;
                    finalAcceleration.y *= 1 + Boost;

                    // Clamp the velocity to the maximum acceleration.
                    // Note that the maximum acceleration constraint is shaped like an ellipse, not like a circle.
                    float ellipseMagnitude = finalAcceleration.x * finalAcceleration.x * ellipseSqrFactorX + finalAcceleration.y * finalAcceleration.y * ellipseSqrFactorY;
                    if (ellipseMagnitude > 1.0f)
                    {
                        finalAcceleration /= Mathf.Sqrt(ellipseMagnitude);
                    }
                }

                return(VectorMath.ComplexMultiply(finalAcceleration.ToPFV2(), forwardsVector.ToPFV2()).ToUnityV2());
            }
            else
            {
                // Here we try to move towards the next waypoint which has been modified slightly using our
                // desired velocity at that point so that the agent will more smoothly round the corner.

                // How much to strive for making sure we reach the target point with the target velocity. Unitless.
                const float TargetVelocityWeight = 0.5f;

                // Limit to how much to care about the target velocity. Value is in seconds.
                // This prevents the character from moving away from the path too much when the target point is far away
                const float TargetVelocityWeightLimit = 1.5f;
                float       targetSpeed;
                var         normalizedTargetVelocity = VectorMath.Normalize(targetVelocity.ToPFV2(), out targetSpeed);

                var distance    = deltaPosition.magnitude;
                var targetPoint = deltaPosition.ToPFV2() - normalizedTargetVelocity * System.Math.Min(TargetVelocityWeight * distance * targetSpeed / (currentSpeed + targetSpeed), maxSpeed * TargetVelocityWeightLimit);

                // How quickly the agent will try to reach the velocity that we want it to have.
                // We need this to prevent oscillations and jitter which is what happens if
                // we let the constant go towards zero. Value is in seconds.
                const float TimeToReachDesiredVelocity = 0.1f;
                // TODO: Clamp to ellipse using more accurate acceleration (use rotation speed as well)
                var finalAcceleration = (targetPoint.normalized * maxSpeed - currentVelocity.ToPFV2()) * (1f / TimeToReachDesiredVelocity);

                // Clamp the velocity to the maximum acceleration.
                // Note that the maximum acceleration constraint is shaped like an ellipse, not like a circle.
                float ellipseMagnitude = finalAcceleration.x * finalAcceleration.x * ellipseSqrFactorX + finalAcceleration.y * finalAcceleration.y * ellipseSqrFactorY;
                if (ellipseMagnitude > 1.0f)
                {
                    finalAcceleration /= Mathf.Sqrt(ellipseMagnitude);
                }

                return(VectorMath.ComplexMultiply(finalAcceleration, forwardsVector.ToPFV2()).ToUnityV2());
            }
        }