/// <summary>
    /// Determines entities hit by the effect and returns the resulting list of entities.
    /// </summary>
    /// <returns>The list of entities hit by the action.</returns>
    public LinkedList <Entity> DetermineEntitiesHit()
    {
        //Calculate the range by finding the diagonal of the rectangle
        float range = TwoDimLinearHelper.PythagoreanLength(WidthFromCenter, HeightFromCenter);

        //Grab the entities within proximity to this effect
        LinkedList <Entity> nearbyEntities = EntityCollisionHelper.AllEntitiesInCircularArea(position, range);

        //Check each nearby entity to see if it lies within the effect's cone
        foreach (Entity entity in nearbyEntities)
        {
            if (!WithinRectangle(entity.transform.position, entity.PhysicalRadius))
            {
                nearbyEntities.Remove(entity);
            }
        }

        return(nearbyEntities);
    }
Example #2
0
    /// <summary>
    /// Returns whether or not the entity at the provided position with the provided radius is within the cone specified by the effect's
    /// angle, range, and direction.
    /// </summary>
    /// <param name="entityPosition">The position of the entity.</param>
    /// <param name="entityRadius">The physical radius of the entity, or the radius used for collision detection.</param>
    /// <returns>True if the entity is inside the cone, false if the entity is not inside the cone.</returns>
    private bool DirectionallyWithinEffect(Vector3 entityPosition, float entityRadius)
    {
        int     targetAngle      = Mathf.Clamp(AngleInDegrees, 0, 180);
        Vector2 positionXY       = position;
        Vector2 entityPositionXY = entityPosition;

        //If the effect is a full circle, angles do not need to be calculated since any angle is valid
        if (targetAngle == 0)
        {
            return(false);
        }

        //Find the angle the left, right edges of the cone are from the direction vector of the cone
        float halfAngle = (float)targetAngle / 2;

        //Convert the given position information into a displacement, returning true if the entity and the effect have the same position
        Vector2 entityDirection = entityPositionXY - positionXY;

        if (entityDirection.Equals(Vector2.zero))
        {
            return(true);
        }

        //CHECK 1: ENTITY CENTER TEST

        //Return true if the center of the entity (entityDirection) lies in between the edges
        float entityAngle = Mathf.Abs(TwoDimLinearHelper.AngleInDegreesBetween(direction, entityDirection.normalized));

        if (entityAngle <= halfAngle)
        {
            return(true);
        }

        //CHECK 2: ENTITY EDGE TEST

        //Create the left, right edge vectors for the cone. We know these vectors are already normalized
        Vector2 leftEdge  = TwoDimLinearHelper.RotateVectorByDegrees(direction, halfAngle);
        Vector2 rightEdge = TwoDimLinearHelper.RotateVectorByDegrees(direction, -halfAngle);

        //Get the perpendicular vectors (pointing inward) of the edges. We know these vectors are already normalized
        Vector2 perpendicularLeftEdge  = new Vector2(leftEdge.y, -leftEdge.x);
        Vector2 perpendicularRightEdge = new Vector2(-rightEdge.y, rightEdge.x);

        //Find the points on the edge of the entity's radius that are closest to the cone's edges
        Vector2 entityLeftEdgeTangent  = entityPositionXY + (perpendicularLeftEdge * entityRadius);
        Vector2 entityRightEdgeTangent = entityPositionXY + (perpendicularRightEdge * entityRadius);

        //Use the tangent points to generate displacement vectors
        Vector2 entityLeftTangentDirection  = entityLeftEdgeTangent - positionXY;
        Vector2 entityRightTangentDirection = entityRightEdgeTangent - positionXY;

        //Find the angles between the tangent and the tangent point directions
        float entityLeftTangentRelationAngle  = TwoDimLinearHelper.AngleInDegreesBetween(leftEdge, entityLeftTangentDirection);
        float entityRightTangentRelationAngle = TwoDimLinearHelper.AngleInDegreesBetween(rightEdge, entityRightTangentDirection);

        //Test the angles of the tangent point directions and return false on failure
        bool entityLeftTangentWithinLeftEdge   = entityLeftTangentRelationAngle <= 0;
        bool entityRightTangentWithinRightEdge = entityRightTangentRelationAngle >= 0;

        if (!(entityLeftTangentWithinLeftEdge && entityRightTangentWithinRightEdge))
        {
            return(false);
        }

        //Return false if the entity is actually behind the cone and is physically unable to be hit by the cone
        bool leftOutsidePerpendicular  = Math.Abs(entityLeftTangentRelationAngle) > 90;
        bool rightOutsidePerpendicular = Math.Abs(entityRightTangentRelationAngle) > 90;

        if (leftOutsidePerpendicular && rightOutsidePerpendicular)
        {
            if (entityDirection.magnitude > entityRadius)
            {
                return(false);
            }
        }

        //END TESTS

        //At this point, all tests have passed. Return true
        return(true);
    }
    /// <summary>
    /// Determines whether or not the entity with the specified position and radius is within the rectangle of the effect.
    /// </summary>
    /// <param name="entityPosition">The position of the entity.</param>
    /// <param name="entityRadius">The physical radius of the entity, or the radius used for collision detection.</param>
    /// <returns>True if the entity is within the effect's rectangle, false if the entity is not within the effect's rectangle.</returns>
    private bool WithinRectangle(Vector2 entityPosition, float entityRadius)
    {
        //Calculate the entity's displacement vector
        Vector2 entityDisplacement = entityPosition - position;

        //CHECK 1: EFFECT CENTER TEST

        //Find the proximity of the entity's edge to the center of the effect
        float entityEdgeDistance = entityDisplacement.magnitude - entityRadius;

        //Assuming a positive radius, return true if the entity covers the center of the effect
        if (entityEdgeDistance <= 0)
        {
            return(true);
        }

        //CHECK 2: SMALLEST RECTANGLE TEST

        //Return true if the distance to the entity's edge is smaller than the width and height
        float minDimension = Mathf.Min(Width, Height);

        if (entityEdgeDistance <= minDimension)
        {
            return(true);
        }

        //CHECK 3: TANGENT POINT TEST

        //Verify the direction is not the zero vector
        if (direction.magnitude == 0)
        {
            return(false);
        }

        //Effectively rotate the entity according to the rectangle's rotation, so that we can perform
        //simple collision logic with a rectangle along the x/y axes
        float   rectangleRotationInDegrees = TwoDimLinearHelper.AngleInDegreesBetween(Vector2.up, direction);
        Vector2 rotatedEntityDisplacement  = TwoDimLinearHelper.RotateVectorByDegrees(entityDisplacement, rectangleRotationInDegrees);

        //Create the four tangent points on the edge of the entity
        Vector2[] tangentPoints = new Vector2[] {
            new Vector2(rotatedEntityDisplacement.x + entityRadius, rotatedEntityDisplacement.y),
            new Vector2(rotatedEntityDisplacement.x - entityRadius, rotatedEntityDisplacement.y),
            new Vector2(rotatedEntityDisplacement.x, rotatedEntityDisplacement.y + entityRadius),
            new Vector2(rotatedEntityDisplacement.x, rotatedEntityDisplacement.y - entityRadius)
        };

        //Keep the half-values of the rectangle parameters on reference t avoid recalculations
        float halfWidth  = WidthFromCenter;
        float halfHeight = HeightFromCenter;

        //For each tangent point, return true if the point lies inside the rectangle
        bool toRightOfLeftEdge;
        bool toLeftOfRightEdge;
        bool aboveLowerEdge;
        bool belowUpperEdge;

        foreach (Vector2 tangentPoint in tangentPoints)
        {
            toRightOfLeftEdge = -halfWidth <= tangentPoint.x;
            toLeftOfRightEdge = tangentPoint.x <= halfWidth;
            aboveLowerEdge    = -halfHeight <= tangentPoint.y;
            belowUpperEdge    = tangentPoint.y <= halfHeight;

            if (toRightOfLeftEdge && toLeftOfRightEdge && aboveLowerEdge && belowUpperEdge)
            {
                return(true);
            }
        }

        //CHECK 4: RECTANGLE CORNER TEST

        //Find the corners of the rectangle
        Vector2[] rectangleCorners = new Vector2[] {
            new Vector2(-halfWidth, -halfHeight),
            new Vector2(halfWidth, -halfHeight),
            new Vector2(-halfWidth, halfHeight),
            new Vector2(halfWidth, halfHeight)
        };

        //Since no tangent points are inside the rectangle, return true if any corner is within the entity's radius
        Vector2 rotatedEntityDisplacementFromCorner;

        foreach (Vector2 rectangleCorner in rectangleCorners)
        {
            rotatedEntityDisplacementFromCorner = rotatedEntityDisplacement - rectangleCorner;
            if (rotatedEntityDisplacementFromCorner.magnitude <= entityRadius)
            {
                return(true);
            }
        }

        //END TESTS

        //At this point, all tests have failed and the entity is not overlapping with the rectangle. Return false
        return(false);
    }