protected void PathTo(Vector3 endPosition)
        {
            // Snap start/end positions to road lattice nodes
            RoadLatticeNode startNode =
                BaseMapLoader.MapsService.RoadLattice.SnapToNode(transform.position);
            RoadLatticeNode endNode = BaseMapLoader.MapsService.RoadLattice.SnapToNode(endPosition);

            // Find path between start/end nodes
            PathToDestination = RoadLattice.UncheckedFindPath(startNode, endNode, PathSearchLimit);

            // Reset our pathfinding instructions - waypoint at index 0 is our start position
            WaypointIndex = 0;

            if (PathToDestination != null && PathToDestination.Count > 0)
            {
                // Init navigation details - our next target is the waypoint after the start position
                CurrentTargetPosition = new Vector3(
                    PathToDestination[WaypointIndex].Location.x,
                    transform.position.y,
                    PathToDestination[WaypointIndex].Location.y);
                IsMoving = true;
            }
            else
            {
                IsMoving = false;
            }

            DisplayRoute(PathToDestination);
        }
Exemple #2
0
    /// <summary>
    /// Receives two game objects, snaps them to the current road lattice, and displays the shortest
    /// path between them.
    /// </summary>
    /// <remarks>
    /// Can be attached as a Unity event handler to the
    /// <see cref="RoadLatticePathFindingObjectMover.UpdatedPositions"/> UnityEvent.
    /// </remarks>
    /// <param name="start">The path start object.</param>
    /// <param name="end">The path end object.</param>
    public void CreatePathBetweenObjects(GameObject start, GameObject end)
    {
        if (PathDisplay != null)
        {
            Destroy(PathDisplay);
        }

        RoadLatticeNode startNode = MapsService.RoadLattice.SnapToNode(start.transform.position);
        RoadLatticeNode endNode   = MapsService.RoadLattice.SnapToNode(end.transform.position);

        start.transform.position = new Vector3(startNode.Location.x, 0, startNode.Location.y);
        end.transform.position   = new Vector3(endNode.Location.x, 0, endNode.Location.y);
        List <RoadLatticeNode> path =
            RoadLattice.UncheckedFindPath(startNode, endNode, PathSearchLimit);

        if (path == null)
        {
            Debug.LogFormat("No path found!");
        }
        else
        {
            PathDisplay = MakeRouteDisplay(path);
            PathDisplay.transform.Translate(Vector3.up * PathDisplayY);
        }
    }
    /// <summary>
    /// Creates a maker (a ground plane penetrating sphere of unit radius) at the location of the
    /// supplied <see cref="RoadLatticeNode"/>
    /// </summary>
    /// <param name="node">The node to indicate</param>
    /// <param name="parent">The parent GameObject under which to create the indicator object</param>
    static void MakeRoadLatticeNodeIndicator(RoadLatticeNode node, GameObject parent)
    {
        GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);

        sphere.transform.position = new Vector3(node.Location.x, 0, node.Location.y);
        sphere.name             = "RoadLatticeNode";
        sphere.transform.parent = parent.transform;
    }
        /// <summary>
        /// Request a ticket for a particular path.
        /// </summary>
        /// <param name="from">The start location.</param>
        /// <param name="to">The target location.</param>
        /// <param name="vehicle">The requesting vehicle.</param>
        /// <returns>A ticket for the path, or null if one could not be given.</returns>
        public PathTicket Request(ulong from, ulong to, Vehicle vehicle)
        {
            string key = GetPathKey(from, to);
            Path   path;

            // Check if this is the first time this path has been requested.
            if (!Paths.TryGetValue(key, out path))
            {
                // Generate the path.
                RoadLatticeNode fromNode = RoadLattice.FindNodeAt(from);
                RoadLatticeNode toNode   = RoadLattice.FindNodeAt(to);
                RoadLatticeEdge edge     = fromNode.EdgeTo(toNode);

                // Check if the edge for this path is flagged as entering an intersection.
                if ((edge.EdgeFlags & RoadLatticeEdge.Flags.Intersection) != 0)
                {
                    // Generate a path that opens and closes, like a traffic light. Randomize the values of
                    // this behaviour.
                    int closeDurationMs  = Random.Range(3000, 8000);
                    int closeFrequencyMs = Random.Range(10000, 30000);
                    path = new IntersectionPath(fromNode.Location, toNode.Location, closeFrequencyMs,
                                                closeDurationMs);
                }
                else
                {
                    // Not an intersection edge. Generate a regular path.
                    path = new Path(fromNode.Location, toNode.Location);
                }

                // Store the path.
                Paths.Add(key, path);
            }

            if (path.IsClosed())
            {
                // Path is closed, don't return a ticket. The requesting vehicle should try again later.
                return(null);
            }

            // Check if the vehicle can fit on the path given its available length, or if the path is
            // currently empty.
            if (path.Length - path.OccupiedLength >= vehicle.Clearance || path.Tickets.Count == 0)
            {
                // Add this vehicle's clearance to the path to update its available length.
                path.OccupiedLength += vehicle.Clearance;

                // Generate and return a ticket for the path.
                LinkedListNode <PathTicket> node = new LinkedListNode <PathTicket>(null);
                PathTicket ticket = new PathTicket(path, vehicle.Clearance, node);
                node.Value = ticket;
                path.Tickets.AddLast(node);

                return(ticket);
            }

            return(null);
        }
Exemple #5
0
        /// <summary>
        /// Once the map is loaded, setup main character and AI agents.
        /// Start activating the search.
        /// </summary>
        private void OnMapLoaded(MapLoadedArgs args)
        {
            if (CharactersContainer != null && BaseMapLoader != null)
            {
                GameObject avatarGO = GetRandomCharacter(AvatarPrefab);
                RoadLatticeCharacterController cc = avatarGO.GetComponent <RoadLatticeCharacterController>();

                if (cc != null)
                {
                    cc.BaseMapLoader = BaseMapLoader;
                    cc.ShowPath      = true;
                }

                // Start following our Avatar on the map
                if (SmoothFollowCamera != null)
                {
                    SmoothFollowCamera.target = avatarGO.transform;
                    SmoothFollowCamera.transform.LookAt(SmoothFollowCamera.target);
                }

                // Add random npc characters on map (red)
                for (int i = 0; i < NumberOfOpponents; i++)
                {
                    GameObject npcGO            = GetRandomCharacter(NPCPrefab);
                    RoadLatticeAIController aic = npcGO.GetComponent <RoadLatticeAIController>();

                    if (aic != null)
                    {
                        aic.BaseMapLoader = BaseMapLoader;
                        aic.Target        = avatarGO;
                        aic.enabled       = true;
                        NPCs.Add(aic);

                        if (cc != null)
                        {
                            // Move distant NPCs closer to the avatar so that they don't take too long to reach
                            // their target.
                            Vector3 diff            = aic.transform.position - cc.transform.position;
                            Vector3 desiredPosition = Vector3.MoveTowards(cc.transform.position,
                                                                          aic.transform.position, Mathf.Min(diff.magnitude, MaxAIStartDistanceFromAvatar));

                            RoadLatticeNode node =
                                BaseMapLoader.MapsService.RoadLattice.SnapToNode(desiredPosition);
                            aic.transform.position = new Vector3(node.Location.x, 0, node.Location.y);
                        }
                    }
                }
            }

            ActiveAllNPCs(IsAISearchActive);
            ShowAIPaths(IsDebugPathOn);
        }
Exemple #6
0
        /// <summary>
        /// Returns the next target node after traversing the edge between two nodes.
        /// </summary>
        /// <param name="endNode">The previous end node.</param>
        /// <param name="startNode">
        /// The previous start node. This node won't be returned unless it's the only neighbor of
        /// <see cref="endNode"/>.
        /// </param>
        /// <returns>The next target node to path to.</returns>
        private RoadLatticeNode FindNextTarget(RoadLatticeNode endNode, RoadLatticeNode startNode)
        {
            List <RoadLatticeNode> neighbors = new List <RoadLatticeNode>(endNode.Neighbors);

            if (neighbors.Count > 1)
            {
                // Remove the start node so we don't return it.
                neighbors.Remove(startNode);
            }

            // Return a random neighbor node.
            return(neighbors[Random.Range(0, neighbors.Count)]);
        }
Exemple #7
0
        /// <summary>
        /// Initializes the vehicle.
        /// </summary>
        /// <param name="trafficSystem">The traffic system.</param>
        /// <param name="startNode">The node the vehicle should start at.</param>
        public void Initialize(TrafficSystem trafficSystem, RoadLatticeNode startNode)
        {
            TrafficSystem = trafficSystem;

            // Find a target node from the start node.
            RoadLatticeNode targetNode = FindNextTarget(startNode, null);

            CurrentNodeLocationUID = startNode.LocationUID;
            TargetNodeLocationUID  = targetNode.LocationUID;

            // Set the vehicle's position and rotation.
            Vector2 heading = (targetNode.Location - startNode.Location).normalized;

            transform.position = new Vector3(startNode.Location.x, 0, startNode.Location.y) +
                                 new Vector3(-heading.y, 0, heading.x) * DistanceFromRoadCenter;
            transform.forward = new Vector3(heading.x, 0, heading.y);
        }
Exemple #8
0
        /// <summary>
        /// Spawns as many vehicles as specified by <see cref="VehiclesToSpawn"/>.
        /// </summary>
        private void OnRoadLatticeModified(DidModifyRoadLatticeArgs args)
        {
            // Get the nodes in the road lattice.
            List <RoadLatticeNode> nodes = new List <RoadLatticeNode>(args.RoadLattice.Nodes);

            for (; VehiclesToSpawn > 0; VehiclesToSpawn--)
            {
                // Get a random node to spawn a vehicle on.
                RoadLatticeNode spawnNode = nodes[Random.Range(0, nodes.Count)];

                // Instantiate a random vehicle.
                Vehicle vehiclePrefab = Vehicles[Random.Range(0, Vehicles.Length)];
                Vehicle vehicle       = Instantiate(vehiclePrefab);

                // Initialize it at the spawn node.
                vehicle.Initialize(TrafficSystem, spawnNode);
            }
        }
Exemple #9
0
        /// <summary>
        /// Returns the next target node after traversing the edge between two nodes.
        /// </summary>
        /// <param name="endNode">The previous end node.</param>
        /// <param name="startNode">
        /// The previous start node. This node won't be returned unless it's the only neighbor of
        /// <see cref="endNode"/>.
        /// </param>
        /// <returns>The next target node to path to.</returns>
        private RoadLatticeNode FindNextTarget(RoadLatticeNode endNode, RoadLatticeNode startNode)
        {
            // Find all traversable neighbor nodes.
            List <RoadLatticeNode> neighbors = new List <RoadLatticeNode>();

            foreach (RoadLatticeEdge edge in endNode.Edges)
            {
                if (IsTraversableRoad(edge.Segment))
                {
                    neighbors.Add(edge.Target);
                }
            }

            if (neighbors.Count > 1)
            {
                // Remove the start node so we don't return it.
                neighbors.Remove(startNode);
            }

            // Return a random neighbor node.
            return(neighbors[Random.Range(0, neighbors.Count)]);
        }
        /// <summary>
        /// Creates characters and places them randomly on the map.
        /// </summary>
        private GameObject GetRandomCharacter(GameObject prefab)
        {
            GameObject character = Instantiate(prefab, CharactersContainer, false);

            List <RoadLatticeNode> nodes = BaseMapLoader.MapsService.RoadLattice.GetNodes();

            if (nodes.Count == 0)
            {
                Debug.LogError("Could not find any nodes to spawn a character at.");
            }
            else
            {
                RoadLatticeNode firstRandomNode = nodes.ElementAt(Random.Range(0, nodes.Count));
                Debug.LogFormat("Character spawned at {0}", firstRandomNode.Location);

                Vector3 newPosition =
                    new Vector3(firstRandomNode.Location.x, 0f, firstRandomNode.Location.y);
                character.transform.position = newPosition;
            }

            return(character);
        }
        /// <summary>
        /// Spawns as many vehicles as specified by <see cref="VehiclesToSpawn"/>.
        /// </summary>
        private void OnRoadLatticeModified(DidModifyRoadLatticeArgs args)
        {
            // Get all traversable nodes in the road lattice.
            List <RoadLatticeNode> nodes = new List <RoadLatticeNode>();

            foreach (RoadLatticeNode node in args.RoadLattice.Nodes)
            {
                foreach (RoadLatticeEdge edge in node.Edges)
                {
                    if (!Vehicle.IsTraversableRoad(edge.Segment))
                    {
                        continue;
                    }

                    nodes.Add(node);
                    break;
                }
            }

            // Don't spawn any vehicles if we have no traversable nodes to spawn them  on.
            if (nodes.Count == 0)
            {
                return;
            }

            for (; VehiclesToSpawn > 0; VehiclesToSpawn--)
            {
                // Get a random node to spawn a vehicle on.
                RoadLatticeNode spawnNode = nodes[Random.Range(0, nodes.Count)];

                // Instantiate a random vehicle.
                Vehicle vehiclePrefab = Vehicles[Random.Range(0, Vehicles.Length)];
                Vehicle vehicle       = Instantiate(vehiclePrefab);

                // Initialize it at the spawn node.
                vehicle.Initialize(TrafficSystem, spawnNode);
            }
        }
Exemple #12
0
        /// <summary>
        /// Queries <see cref="TrafficSystem"/> and moves the vehicle.
        /// </summary>
        void Update()
        {
            // Get the current and target node objects from the road lattice.
            RoadLatticeNode currentNode = TrafficSystem.RoadLattice.FindNodeAt(CurrentNodeLocationUID);
            RoadLatticeNode targetNode  = TrafficSystem.RoadLattice.FindNodeAt(TargetNodeLocationUID);

            // Check that our current and target nodes exist in the road lattice, and that an edge
            // exists between them.
            if (targetNode == null || currentNode == null || !currentNode.HasEdgeTo(targetNode))
            {
                // Release our (invalid) ticket.
                if (Ticket != null)
                {
                    Ticket.Release();
                    Ticket = null;
                }

                if (currentNode != null && currentNode.NeighborCount > 0)
                {
                    // Current node exists and has neighbors; try to find a new target.
                    TargetNodeLocationUID = FindNextTarget(currentNode, null).LocationUID;
                }
                else
                {
                    // Current node does not exist or has no neighbors. Destroy the vehicle.
                    Destroy(gameObject);
                }
                return;
            }

            // Check if we have a ticket to the target node.
            if (Ticket == null)
            {
                // Don't have a ticket. Request one.
                Ticket = TrafficSystem.Request(CurrentNodeLocationUID, TargetNodeLocationUID, this);
                if (Ticket == null)
                {
                    // Didn't get a ticket. Either the path is at capacity or is closed. Try again later.
                    return;
                }
            }

            // Get the target position of the vehicle. This is the position offset from the centreline
            // by `DistanceFromRoadCenter`.
            Vector3 perpendicular  = new Vector3(-Ticket.Heading.z, 0, Ticket.Heading.x);
            Vector3 targetPosition = Ticket.CurrentPosition + perpendicular * DistanceFromRoadCenter;

            // Face the target position.
            Vector3 heading = (targetPosition - transform.position).normalized;

            if (heading.sqrMagnitude > 0)
            {
                transform.forward = heading;
            }

            // Move the vehicle.
            transform.position = Vector3.MoveTowards(transform.position, targetPosition,
                                                     Speed * Time.deltaTime);

            // Advance the ticket position if we've reached `targetPosition`.
            if ((transform.position - targetPosition).sqrMagnitude < 0.1f)
            {
                Ticket.Move(Speed * Time.deltaTime);
            }

            // Check if we've reached the end of the path.
            if (Ticket.DistanceToTarget < MaxDistanceUntilNextTarget)
            {
                // Find the next target node.
                RoadLatticeNode nextTargetNode = FindNextTarget(targetNode, currentNode);

                // Try acquiring a ticket to the new node.
                TrafficSystem.PathTicket nextTicket =
                    TrafficSystem.Request(TargetNodeLocationUID, nextTargetNode.LocationUID, this);
                if (nextTicket == null)
                {
                    // Couldn't acquire a ticket, try again next frame. Don't release the current ticket
                    // until we can acquire the next one.
                    return;
                }

                // Acquired a ticket. Release the old one and set the new current and target nodes.
                Ticket.Release();
                Ticket = nextTicket;
                CurrentNodeLocationUID = TargetNodeLocationUID;
                TargetNodeLocationUID  = nextTargetNode.LocationUID;
            }
        }
    /// <summary>
    /// Instantiates a GameObject showing connections between <see cref="RoadLatticeNode"/>s using
    /// materials to distinguish disjoint partitions of the road graph.
    /// </summary>
    /// <param name="lattice">The lattice from which to generate a GameObject</param>
    /// <param name="indicateNodes">Whether to add markers at each node</param>
    /// <param name="materials">The materials to apply to the disjoint sub-lattices</param>
    /// <returns>The GameObject representation of the supplied lattice</returns>
    private static GameObject MakePartitionedRoadLatticeDebugGameObject(
        RoadLattice lattice, bool indicateNodes, Material[] materials)
    {
        // A list for collecting road segment end point locations.
        List <Vector2> vertices = new List <Vector2>();
        // A set of nodes with connections to other nodes that have all been converted into geometry.
        HashSet <RoadLatticeNode> processed = new HashSet <RoadLatticeNode>();

        int        materialIndex = 0;
        GameObject latticeParent = new GameObject();

        latticeParent.name = "Road Lattice Parent";
        List <RoadLatticeNode> nodes = lattice.GetNodes();

        foreach (RoadLatticeNode node in nodes)
        {
            // Skip this node if it has already had all its connections turned into geometry, otherwise
            // use it as the starting node for a new disjoint sub-lattice representation.
            if (processed.Contains(node))
            {
                continue;
            }
            vertices.Clear();
            // A set of nodes reachable from the set of nodes that have been processed so far. Note: this
            // list may contain nodes that have already been processed if such ndoes are reachable from
            // more than one neighbouring node.
            Stack <RoadLatticeNode> reachableNodes = new Stack <RoadLatticeNode>();
            reachableNodes.Push(node);
            // Keep processing nodes til we have visited all reachable nodes.
            while (reachableNodes.Count > 0)
            {
                RoadLatticeNode source = reachableNodes.Pop();
                // Skip nodes we have previously processed by reaching them through an alternative path.
                if (processed.Contains(source))
                {
                    continue;
                }
                if (indicateNodes)
                {
                    MakeRoadLatticeNodeIndicator(source, latticeParent);
                }
                // Mark this node as processed, add segments for all its connections, and add all its
                // neighbours to the reachable set.
                processed.Add(source);
                foreach (RoadLatticeNode neighbor in source.Neighbors)
                {
                    if (!processed.Contains(neighbor))
                    {
                        vertices.Add(source.Location);
                        vertices.Add(neighbor.Location);
                        reachableNodes.Push(neighbor);
                    }
                }
            }
            GameObject go = new GameObject();
            go.name = "Road Lattice";
            MakeSublatticeMeshes(go, vertices, materials[materialIndex]);
            materialIndex       = (materialIndex + 1) % materials.Length;
            go.transform.parent = latticeParent.transform;
        }

        return(latticeParent);
    }