Esempio n. 1
0
            /** Gradient and value of the cost function of this VO.
             * The VO has a cost function which is 0 outside the VO
             * and increases inside it as the point moves further into
             * the VO.
             *
             * This is the negative gradient of that function as well as its
             * value (the weight). The negative gradient points in the direction
             * where the function decreases the fastest.
             *
             * The value of the function is the distance to the closest edge
             * of the VO and the gradient is normalized.
             */
            public Vector2 Gradient(Vector2 p, out float weight)
            {
                if (colliding)
                {
                    // Calculate double signed area of the triangle consisting of the points
                    // {line1, line1+dir1, p}
                    float l1 = SignedDistanceFromLine(line1, dir1, p);

                    // Serves as a check for which side of the line the point p is
                    if (l1 >= 0)
                    {
                        weight = l1;
                        return(new Vector2(-dir1.y, dir1.x));
                    }
                    else
                    {
                        weight = 0;
                        return(new Vector2(0, 0));
                    }
                }

                float det3 = SignedDistanceFromLine(cutoffLine, cutoffDir, p);

                if (det3 <= 0)
                {
                    weight = 0;
                    return(Vector2.zero);
                }
                else
                {
                    // Signed distances to the two edges along the sides of the VO
                    float det1 = SignedDistanceFromLine(line1, dir1, p);
                    float det2 = SignedDistanceFromLine(line2, dir2, p);
                    if (det1 >= 0 && det2 >= 0)
                    {
                        // We are inside both of the half planes
                        // (all three if we count the cutoff line)
                        // and thus inside the forbidden region in velocity space

                        // Actually the negative gradient because we want the
                        // direction where it slopes the most downwards, not upwards
                        Vector2 gradient;

                        // Check if we are in the semicircle region near the cap of the VO
                        if (Vector2.Dot(p - line1, dir1) > 0 && Vector2.Dot(p - line2, dir2) < 0)
                        {
                            if (segment)
                            {
                                // This part will only be reached for line obstacles (i.e not other agents)
                                if (det3 < radius)
                                {
                                    PF.Vector3 closestPointOnLine = VectorMath.ClosestPointOnSegment(segmentStart.ToPFV2(), segmentEnd.ToPFV2(), p.ToPFV2());
                                    var        dirFromCenter      = p.ToPFV2() - closestPointOnLine.ToV2();
                                    float      distToCenter;
                                    gradient = VectorMath.Normalize(dirFromCenter, out distToCenter);
                                    // The weight is the distance to the edge
                                    weight = radius - distToCenter;
                                    return(gradient);
                                }
                            }
                            else
                            {
                                var   dirFromCenter = p - circleCenter;
                                float distToCenter;
                                gradient = VectorMath.Normalize(dirFromCenter, out distToCenter);
                                // The weight is the distance to the edge
                                weight = radius - distToCenter;
                                return(gradient);
                            }
                        }

                        if (segment && det3 < det1 && det3 < det2)
                        {
                            weight   = det3;
                            gradient = new Vector2(-cutoffDir.y, cutoffDir.x);
                            return(gradient);
                        }

                        // Just move towards the closest edge
                        // The weight is the distance to the edge
                        if (det1 < det2)
                        {
                            weight   = det1;
                            gradient = new Vector2(-dir1.y, dir1.x);
                        }
                        else
                        {
                            weight   = det2;
                            gradient = new Vector2(-dir2.y, dir2.x);
                        }

                        return(gradient);
                    }

                    weight = 0;
                    return(Vector2.zero);
                }
            }
Esempio n. 2
0
 /** Simulates rotating the agent towards the specified direction and returns the new rotation.
  * \param direction Direction in the movement plane to rotate towards.
  * \param maxDegrees Maximum number of degrees to rotate this frame.
  *
  * Note that this only calculates a new rotation, it does not change the actual rotation of the agent.
  *
  * \see #rotationIn2D
  * \see #movementPlane
  */
 protected Quaternion SimulateRotationTowards(Vector2 direction, float maxDegrees)
 {
     if (direction != Vector2.zero)
     {
         Quaternion targetRotation = Quaternion.LookRotation(movementPlane.ToWorld(direction.ToPFV2(), 0).ToUnityV3(), movementPlane.ToWorld(Vector2.zero.ToPFV2(), 1).ToUnityV3());
         // This causes the character to only rotate around the Z axis
         if (rotationIn2D)
         {
             targetRotation *= Quaternion.Euler(90, 0, 0);
         }
         return(Quaternion.RotateTowards(simulatedRotation, targetRotation, maxDegrees));
     }
     return(simulatedRotation);
 }
Esempio n. 3
0
            /** 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.
             */
            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.ToPFV2(), segmentEnd.ToPFV2(), Vector2.zero.ToPFV2());

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

                    vo.line1  = closestOnSegment.normalized.ToUnityV3() * (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.ToUnityV2() * radius + offset;
                    vo.line2 = segmentEnd + normal2.ToUnityV2() * 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);
            }
Esempio n. 4
0
 /** Calculates how far to move during a single frame */
 protected Vector2 CalculateDeltaToMoveThisFrame(Vector2 position, float distanceToEndOfPath, float deltaTime)
 {
     if (rvoController != null && rvoController.enabled)
     {
         // Use RVOController to get a processed delta position
         // such that collisions will be avoided if possible
         return(movementPlane.ToPlane(rvoController.CalculateMovementDelta(movementPlane.ToWorld(position.ToPFV2(), 0).ToUnityV3(), deltaTime).ToPFV3()).ToUnityV2());
     }
     // Direction and distance to move during this frame
     return(Vector2.ClampMagnitude(velocity2D * deltaTime, distanceToEndOfPath));
 }