//Check if the car outside of map (MATT)
        public static bool TargetPositionWithinTrack(Vector3 carRearWheelPos, float heading, CarData carData)
        {
            bool withinTrack = true;

            //Make the car bigger than it is to be on the safe side
            float marginOfSafety = 0.5f;

            float carLength = carData.GetLength() + marginOfSafety;
            float carWidth  = carData.GetWidth() + marginOfSafety;


            //Find the center pos of the car (carPos is at the rearWheels)
            float distCenterToRearWheels = carData.GetDistanceToRearWheels();

            float xCenter = carRearWheelPos.x + distCenterToRearWheels * Mathf.Sin(heading);
            float zCenter = carRearWheelPos.z + distCenterToRearWheels * Mathf.Cos(heading);

            Vector3 carPos = new Vector3(xCenter, carRearWheelPos.y, zCenter);

            //Find all corners of the car
            Rectangle carCornerPos = SkeletonCar.GetCornerPositions(carPos, heading, carWidth, carLength);

            //Detect if any of the corners is outside of the map
            if (
                !PathfindingController.IsPositionWithinGrid(carCornerPos.FL) ||
                !PathfindingController.IsPositionWithinGrid(carCornerPos.FR) ||
                !PathfindingController.IsPositionWithinGrid(carCornerPos.BL) ||
                !PathfindingController.IsPositionWithinGrid(carCornerPos.BR))
            {
                //At least one of the corners is outside of the map
                withinTrack = false;
            }

            return(withinTrack);
        }
        //Update the circles to see if we can identify the coordinates from geometry
        void UpdateCircles(Vector3 carPos, float carHeading)
        {
            circlePositions = SkeletonCar.GetCirclePositions(carPos, carHeading, circlePositions);

            for (int i = 0; i < circleOBj.Length; i++)
            {
                AddCoordinates(circleOBj[i], circlePositions[i].x, circlePositions[i].z);
            }
        }
        //Update the corners to see if we can identify the coordinates from geometry
        void UpdateCorners(Vector3 carPos, float carHeading)
        {
            float carWidth  = 0.95f * 2f;
            float carLength = 2.44f * 2f;

            Rectangle cornerPos = SkeletonCar.GetCornerPositions(carPos, carHeading, carWidth, carLength);

            //Add the coordinates to the spheres
            AddCoordinates(cornerFR, cornerPos.FR.x, cornerPos.FR.z);
            AddCoordinates(cornerFL, cornerPos.FL.x, cornerPos.FL.z);
            AddCoordinates(cornerBR, cornerPos.BR.x, cornerPos.BR.z);
            AddCoordinates(cornerBL, cornerPos.BL.x, cornerPos.BL.z);
        }
        void TestDrive()
        {
            //Distance between the wheels (= wheelbase)
            float L = 2.959f;
            //Driving distance each update
            float d = 10f;

            //Steering angle in radians
            float alpha = 20f * Mathf.Deg2Rad;
            //Heading direction in radians
            float theta = transform.eulerAngles.y * Mathf.Deg2Rad;

            //Manual control
            d      = 0.1f * Input.GetAxis("Vertical");
            alpha *= Input.GetAxis("Horizontal");

            //Turning angle
            float beta = (d / L) * Mathf.Tan(alpha);


            //Get the new position
            Vector3 newPos = SkeletonCar.CalculateNewPosition(theta, beta, d, transform.position);

            //Get the new heading
            float newHeading = SkeletonCar.CalculateNewHeading(theta, beta);


            //Add the new position to the car
            transform.position = newPos;

            //Add the new heading to the car
            Vector3 currentRot = transform.rotation.eulerAngles;

            Vector3 newRotation = new Vector3(currentRot.x, newHeading * Mathf.Rad2Deg, currentRot.z);

            transform.rotation = Quaternion.Euler(newRotation);


            UpdateCorners(newPos, newHeading);

            UpdateCircles(newPos, newHeading);
        }
        //Expand one node
        private void ExpandNode(Node currentNode)
        {
            //To be able to expand we need the simulated car's heading and position
            float heading = currentNode.heading;

            //Save which cells are expanded so we can close them after we have expanded all directions
            expandedCellsForward.Clear();
            expandedCellsReverse.Clear();

            //Expand both forward and reverse
            for (int j = 0; j < driveDistances.Length; j++)
            {
                float driveDistance = driveDistances[j];

                //Expand by looping through all steering angles
                for (int i = 0; i < steeringAngles.Count; i++)
                {
                    //Steering angle
                    float alpha = steeringAngles[i];

                    //Turning angle
                    float beta = (driveDistance / carData.GetWheelBase()) * Mathf.Tan(alpha);

                    //Simulate the skeleton car
                    Vector3 newCarPos = SkeletonCar.CalculateNewPosition(heading, beta, driveDistance, currentNode.carPos);

                    float newHeading = SkeletonCar.CalculateNewHeading(heading, beta);

                    //Get the cell pos of the new position
                    IntVector2 cellPos = PathfindingController.ConvertCoordinateToCellPos(newCarPos);

                    //Detect if the car is colliding with obstacle or is outside of map
                    if (!ObstaclesDetection.TargetPositionWithinTrack(newCarPos, newHeading, carData))
                    //if (ObstaclesDetection.HasCarInvalidPosition(newCarPos, newHeading, carData)) (MATT)
                    {
                        continue;
                    }
                    //Is this node closed? Important this is after obstacle/outside of map detection or may be out of range
                    else if (
                        (driveDistance < 0f && closedCellsReverse[cellPos.x, cellPos.z]) ||
                        (driveDistance > 0f && closedCellsForward[cellPos.x, cellPos.z]))
                    {
                        continue;
                    }
                    //We can create a new node because this is a valid position
                    else
                    {
                        //
                        //Calculate costs
                        //

                        //The cost it took to get to this node
                        float cost = Mathf.Abs(driveDistance);

                        //Add cost for turning if we are not having the same steering angle as previously
                        if (alpha != currentNode.steeringAngle)
                        {
                            cost += turningCost;
                        }

                        //Add a cost if we are close to an obstacle, its better to drive around them than close to them
                        //We can use the flow map to check this

                        /*  (MATT)
                         * if (ObstaclesController.distanceToClosestObstacle[cellPos.x, cellPos.z] < 6)
                         * {
                         *  cost += obstacleCost;
                         * }
                         */

                        //Add cost for reversing
                        if (driveDistance < 0f)
                        {
                            cost += reverseCost;
                        }

                        //Add a cost if we are switching from reverse -> forward or the opposite
                        bool isReversing = driveDistance < 0f ? true : false;

                        if ((isReversing && !currentNode.isReversing) || (!isReversing && currentNode.isReversing))
                        {
                            cost += switchingDirectionOfMovementCost;
                        }

                        //The cost to reach this node
                        float g2 = currentNode.g + cost;

                        //Is this cost lower than it was?
                        if (
                            (driveDistance > 0f && g2 < lowestCostForward[cellPos.x, cellPos.z]) ||
                            (driveDistance < 0f && g2 < lowestCostReverse[cellPos.x, cellPos.z]))
                        {
                            //We have found a better path
                            if (driveDistance > 0f)
                            {
                                //lowestCostForward[cellPos.x, cellPos.z] = g2;
                                expandedCellsForward.Add(new ExpandedCellsStorage(cellPos, g2));
                            }
                            if (driveDistance < 0f)
                            {
                                //lowestCostReverse[cellPos.x, cellPos.z] = g2;
                                expandedCellsReverse.Add(new ExpandedCellsStorage(cellPos, g2));
                            }

                            //
                            //Create a new node
                            //
                            Node nextNode = new Node();

                            nextNode.g            = g2;
                            nextNode.h            = HeuristicsController.heuristics[cellPos.x, cellPos.z];
                            nextNode.cellPos      = cellPos;
                            nextNode.previousNode = currentNode;

                            //Add the car data to the node
                            nextNode.carPos        = newCarPos;
                            nextNode.heading       = newHeading;
                            nextNode.steeringAngle = steeringAngles[i];
                            //Are we reversing?
                            nextNode.isReversing = driveDistance < 0f ? true : false;

                            //Add the node to the list with open nodes
                            openNodes.Add(nextNode);
                        }
                    }
                }
            }


            //Close all cells we expanded to from this node so we cant reach them again from another node
            if (expandedCellsForward.Count > 0)
            {
                for (int k = 0; k < expandedCellsForward.Count; k++)
                {
                    //closedArrayForward[expandedCellsForward[k].x, expandedCellsForward[k].z] = true;

                    IntVector2 cellPos = expandedCellsForward[k].cellPos;

                    if (expandedCellsForward[k].cost < lowestCostForward[cellPos.x, cellPos.z])
                    {
                        lowestCostForward[cellPos.x, cellPos.z] = expandedCellsForward[k].cost;
                    }
                }
            }
            if (expandedCellsReverse.Count > 0)
            {
                for (int k = 0; k < expandedCellsReverse.Count; k++)
                {
                    //closedArrayReverse[expandedCellsReverse[k].x, expandedCellsReverse[k].z] = true;

                    IntVector2 cellPos = expandedCellsReverse[k].cellPos;

                    if (expandedCellsReverse[k].cost < lowestCostReverse[cellPos.x, cellPos.z])
                    {
                        lowestCostReverse[cellPos.x, cellPos.z] = expandedCellsReverse[k].cost;
                    }
                }
            }
        }
        //Check if the car is colliding with an obstacle or is outside of map
        public static bool HasCarInvalidPosition(Vector3 carRearWheelPos, float heading, CarData carData)
        {
            bool hasInvalidPosition = false;

            //Make the car bigger than it is to be on the safe side
            float marginOfSafety = 0.5f;

            float carLength = carData.GetLength() + marginOfSafety;
            float carWidth  = carData.GetWidth() + marginOfSafety;


            //Find the center pos of the car (carPos is at the rearWheels)
            float distCenterToRearWheels = carData.GetDistanceToRearWheels();

            float xCenter = carRearWheelPos.x + distCenterToRearWheels * Mathf.Sin(heading);
            float zCenter = carRearWheelPos.z + distCenterToRearWheels * Mathf.Cos(heading);

            Vector3 carPos = new Vector3(xCenter, carRearWheelPos.y, zCenter);


            //
            // Step 1. Check if the car's corners is inside of the map
            //

            //Find all corners of the car
            Rectangle carCornerPos = SkeletonCar.GetCornerPositions(carPos, heading, carWidth, carLength);

            //Detect if any of the corners is outside of the map
            if (
                !PathfindingController.IsPositionWithinGrid(carCornerPos.FL) ||
                !PathfindingController.IsPositionWithinGrid(carCornerPos.FR) ||
                !PathfindingController.IsPositionWithinGrid(carCornerPos.BL) ||
                !PathfindingController.IsPositionWithinGrid(carCornerPos.BR))
            {
                //At least one of the corners is outside of the map
                hasInvalidPosition = true;

                return(hasInvalidPosition);
            }



            //
            // Step 2. Check if the car's center position is far away from an obstacle
            //
            //We dont need to check if the car is colliding with an obstacle if the distance to an obstacle is great
            IntVector2 cellPos = PathfindingController.ConvertCoordinateToCellPos(carPos);

            //The car is not colliding with anything if the steps to an obstacle is greater than the length of the car
            if (ObstaclesController.distanceToClosestObstacle[cellPos.x, cellPos.z] > carData.GetLength())
            {
                //This is a valid position
                hasInvalidPosition = false;

                return(hasInvalidPosition);
            }



            //
            // Step 3. Check if the car is hitting an obstacle
            //

            //Find all corners of the car
            Rectangle carCornerPosFat = SkeletonCar.GetCornerPositions(carPos, heading, carWidth, carLength);

            //Method 1 - Use the car's corners and then rectangle-rectangle-intersection with the obstacles
            hasInvalidPosition = ObstacleDetectionCorners(carPos, carCornerPosFat);

            //Method 2 - Approximate the car with circles
            //hasInvalidPosition = ObstacleDetectionCircles(carCenterPos, heading, carData, carCornerPosFat);


            return(hasInvalidPosition);
        }
        //Approximate the car's area with circles to detect if there's an obstacle within the circle
        private static bool ObstacleDetectionCircles(Vector3 carPos, float heading, CarData carData, Rectangle carCornerPos)
        {
            bool hasInvalidPosition = false;

            Vector3[] circlePositions = new Vector3[3];

            //Get the position of the 3 circles that approximates the size of the car
            circlePositions = SkeletonCar.GetCirclePositions(carPos, heading, circlePositions);

            //
            //Find all obstacles close to the car
            //
            //The car's length is 5 m and each obstacle is 1 m, so add a little to be on the safe side
            //float searchRadius = carData.GetLength() * 0.5f + 1.5f;

            //List<ObstacleData> obstaclesThatAreClose = FindCloseObstaclesWithinRadius(carPos, searchRadius * searchRadius);

            List <ObstacleData> obstaclesThatAreClose = FindCloseObstaclesCell(carPos, carCornerPos);

            //If there are no obstacle close, then return
            if (obstaclesThatAreClose.Count == 0)
            {
                return(hasInvalidPosition);
            }


            //
            //If there are obstacles around the car, then we have to see if some of them intersect
            //
            //The radius of one circle that approximates the area of the car
            //The width of the car is 2 m but the circle has to be larger to provide a margin of safety
            float circleRadius = 1.40f;

            //But we also need to add the radius of the box obstacle which has a width of 1 m
            float criticalRadius = circleRadius + 0.5f;

            //And square it to speed up
            float criticalRadiusSqr = criticalRadius * criticalRadius;

            //Loop through all circles and detect if there's an obstacle within the circle
            for (int i = 0; i < circlePositions.Length; i++)
            {
                Vector3 currentCirclePos = circlePositions[i];

                //Is there an obstacle within the radius of this circle
                for (int j = 0; j < obstaclesThatAreClose.Count; j++)
                {
                    float distSqr = (currentCirclePos - obstaclesThatAreClose[j].centerPos).sqrMagnitude;

                    if (distSqr < criticalRadiusSqr)
                    {
                        hasInvalidPosition = true;

                        return(hasInvalidPosition);
                    }
                }
            }


            return(hasInvalidPosition);
        }
예제 #8
0
        //Expand one node
        private void ExpandNode(Node currentNode)
        {
            //To be able to expand we need the simulated car's heading and position
            float heading = currentNode.heading;

            //Expand both forward and reverse
            for (int j = 0; j < driveDistances.Length; j++)
            {
                float driveDistance = driveDistances[j];

                //Expand by looping through all steering angles
                for (int i = 0; i < steeringAngles.Count; i++)
                {
                    //Steering angle
                    float alpha = steeringAngles[i];

                    //Turning angle
                    float beta = (driveDistance / carData.GetWheelBase()) * Mathf.Tan(alpha);

                    //Simulate the skeleton car
                    Vector3 newCarPos = SkeletonCar.CalculateNewPosition(heading, beta, driveDistance, currentNode.carPos);

                    float newHeading = SkeletonCar.CalculateNewHeading(heading, beta);

                    //Get the cell pos of the new position
                    IntVector2 cellPos = PathfindingController.ConvertCoordinateToCellPos(newCarPos);


                    //
                    //Check if the car is colliding with obstacle or is outside of map
                    //
                    if (!ObstaclesDetection.TargetPositionWithinTrack(newCarPos, newHeading, carData))
                    //if (ObstaclesDetection.HasCarInvalidPosition(newCarPos, newHeading, carData)) (MATT)
                    {
                        continue;
                    }


                    //
                    //Check if this node is closed
                    //
                    //Important this is after obstacle/outside of map detection or may be out of range
                    int roundedAngle = RoundAngle(newHeading * Mathf.Rad2Deg);

                    if (closedCells[cellPos.x, cellPos.z].ContainsKey(roundedAngle))
                    {
                        continue;
                    }


                    //
                    //Check if this node is cheaper than any other node by calculating costs
                    //
                    //First create a new node with all data we need to calculate costs
                    Node nextNode = new Node();

                    nextNode.cellPos       = cellPos;
                    nextNode.carPos        = newCarPos;
                    nextNode.heading       = newHeading;
                    nextNode.steeringAngle = steeringAngles[i];
                    nextNode.isReversing   = driveDistance < 0f ? true : false;
                    nextNode.h             = HeuristicsController.heuristics[cellPos.x, cellPos.z];
                    nextNode.previousNode  = currentNode;

                    //Now we can calculate the cost to reach this node
                    nextNode.g = CalculateCosts(nextNode);


                    //Is this cost lower than it was or have we not expanded to this this cell with this angle?
                    if (
                        ((lowestCostCells[cellPos.x, cellPos.z].ContainsKey(roundedAngle) && nextNode.g < lowestCostCells[cellPos.x, cellPos.z][roundedAngle])) ||
                        !lowestCostCells[cellPos.x, cellPos.z].ContainsKey(roundedAngle))

                    {
                        //We havent expanded to this node before with this angle
                        if (!lowestCostCells[cellPos.x, cellPos.z].ContainsKey(roundedAngle))
                        {
                            lowestCostCells[cellPos.x, cellPos.z].Add(roundedAngle, nextNode.g);
                        }
                        //The costs is lower than a previous expansion
                        else
                        {
                            lowestCostCells[cellPos.x, cellPos.z][roundedAngle] = nextNode.g;

                            //Now we should remove the old node from the heap
                            //Actually not needed because it's costly to remove nodes from the heap
                            //So its better to just skip the node when finding nodes with lowest cost
                        }

                        //Add the node to the list with open nodes
                        openNodes.Add(nextNode);
                    }
                }
            }
        }