public void CheckAndAddEdgeToVertex(Collider other, bool editMode = false) { if (!other) // shorthand for (other == null) { return; } EdgeScript edge = other.GetComponent <EdgeScript> (); if (edge) { if (editMode) { // update the edge to link to this vertex, but only in edit mode edge.CheckAndAddVertexToEdge(GetComponent <Collider> ()); } for (int i = 0; i < Edges.Length; i++) { if (Edges [i] == edge) { // if this edge is already in the list there is no need to add it a second time break; } if (Edges [i] == null) { // add edge to first empty slot in the list Edges [i] = edge; break; } } } }
public void CheckAndRemoveEdgeFromVertex(Collider other, bool editMode = false) { if (!other) // shorthand for (other == null) { return; } EdgeScript edge = other.GetComponent <EdgeScript> (); if (edge) { if (editMode) { // update the edge that links to this vertex, but only in edit mode edge.CheckAndRemoveVertexFromEdge(GetComponent <Collider> ()); } for (int i = 0; i < Edges.Length; i++) { if (Edges [i] == edge) { // remove the edge from the list and move all following edges in the list up one spot for (int j = i + 1; j < Edges.Length; j++) { Edges [j - 1] = Edges [j]; } Edges [Edges.Length - 1] = null; break; } } } }
private void loadPlan(string path) { print("importing plan"); string line; using (StreamReader sr = new StreamReader(path)) { line = sr.ReadToEnd(); } JsonReader reader = new JsonReader(); var output = reader.Read <Dictionary <string, object>[]>(line); foreach (Dictionary <string, object> step in output) { int t = (int)step["t"]; try { foreach (Dictionary <string, object> signal in (Dictionary <string, object>[])step["signals"]) { //Find edge //modify occupants/signal //Set up cache for quickly finding edges GameObject edge = GameObject.Find(signal["start"].ToString() + "->" + signal["end"].ToString()); EdgeScript scpt = edge.GetComponent <EdgeScript>(); scpt.occupantsSchedule[t] = (int)signal["predictedOccupancy"]; scpt.flowSchedule[t] = (int)signal["inFlow"]; scpt.signalSchedule[t] = (bool)signal["signal"]; } } catch (Exception e) { } maxT = t; } }
private EdgeWrapper GetOrMakeEdgeWrapper(EdgeScript edge, Dictionary <EdgeScript, EdgeWrapper> edgeDict) { EdgeWrapper wrapper; if (!edgeDict.TryGetValue(edge, out wrapper)) { wrapper = new EdgeWrapper(edge); edgeDict [edge] = wrapper; } return(wrapper); }
// Uses the neighbours given to the node to generate edges public void ManualEdgeCreation() { foreach (GameObject node in neighbours) { NodeScript neighbourNode = node.GetComponent <NodeScript>(); //don't add edge if already exists if (node != null && node != this && this.FindEdgeTo(neighbourNode) == null) { EdgeScript edge = new EdgeScript(this, neighbourNode); edge.SetCost((node.transform.position - this.transform.position).magnitude); edges.Add(edge); } } }
// Generate edges for auto grid generation public void GenerateEdges() { Collider[] neighbourNodes = Physics.OverlapBox(transform.position, transform.localScale); foreach (Collider nodeCollider in neighbourNodes) { NodeScript node = nodeCollider.gameObject.GetComponent <NodeScript>(); if (node != null && node != this) { EdgeScript edge = new EdgeScript(this, node); edge.SetCost((node.transform.position - this.transform.position).magnitude); edges.Add(edge); } } }
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // TODO: Take this specific function, Modify it to only calculate BPM cost // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // public float GetCost(VertexWrapper fromNodeWrapper, VertexWrapper toNodeWrapper, EdgeScript edge, CostMethodType method, TravelerProfile Traveler = null, VertexScript startNode = null, VertexScript goalNode = null) { VertexScript fromNode = fromNodeWrapper.Vertex; VertexScript toNode = toNodeWrapper.Vertex; // the baseCost is our interpretation of the physical cost of travel, ignoring any personality traits float baseCost = 0f; switch (method) { case CostMethodType.ClosestToCompass: // pick the path with the smallest angle compared to the vector between you and the destination Vector3 differenceForEdge = toNode.transform.position - fromNode.transform.position; Vector3 differenceForGoal = goalNode.transform.position - fromNode.transform.position; baseCost = Mathf.Abs(Vector3.Angle(differenceForEdge, differenceForGoal)); break; case CostMethodType.DistanceHorizontal: // use only the pure horizontal distance, ignoring any vertical distance Vector3 difference = toNode.transform.position - fromNode.transform.position; difference.y = 0; baseCost = difference.magnitude; break; case CostMethodType.DistanceTrue: // use the typical 3D distance between the two points baseCost = (toNode.transform.position - fromNode.transform.position).magnitude; break; case CostMethodType.DistanceVertical: // use only the pure vertical distance, ignoring any horizontal distance // this is equivalent to saying "walking on flat ground is easy, but any change in elevation is hard" baseCost = Mathf.Abs(toNode.transform.position.y - fromNode.transform.position.y); break; case CostMethodType.NumberOfStops: // counts the number the vertices between the start and the goal nodes baseCost = 1f; break; case CostMethodType.PeakLover: // for people who enjoy walking along the tops of hills and mountains so they can enjoy the view // there is a cost for going down, but not for going up baseCost = Mathf.Max(0f, fromNode.transform.position.y - toNode.transform.position.y); break; case CostMethodType.SmoothTurns: // when you're driving very fast or driving a very large vehicle it's important that your turns are smooth // the cost is smaller if the edge is a straight-forward continuation of the last edge and larger if you have to make a sharp turn if (fromNodeWrapper.LowestCostEdgeSoFar != null) { // all nodes except the start node will have a "lowest cost edge so far", so we can use it Vector3 differenceNext = toNode.transform.position - fromNode.transform.position; Vector3 differencePrevious = fromNode.transform.position - fromNodeWrapper.LowestCostEdgeSoFar.Edge.GetOtherVertex(fromNode).transform.position; // ignore changes in elevation so that only horizontal turning is taken into account differenceNext.y = 0; differencePrevious.y = 0; // the cost should be higher if the angle is larger or if the distance traveled is shorter (i.e. it's a sharper turn) // the cost should be lower if the angle is smaller or if the distance traveled is larger (i.e. it's a long smooth turn) baseCost = Mathf.Abs(Vector3.Angle(differencePrevious, differenceNext)) / (differencePrevious.magnitude + differenceNext.magnitude); } else { // the start node has no "lowest cost edge so far" so just use 0 (no need to turn when leaving the start node) baseCost = 0f; } break; /////////////////// case CostMethodType.Steepness: // <- Use this for calculating edge BPM Cost // the cost is the angle that the edge makes with the horizon // so flat paths are good but the steeper the edge (going up or going down) the more it costs Vector3 differenceFull = toNode.transform.position - fromNode.transform.position; Vector3 differenceFlat = new Vector3(differenceFull.x, differenceFull.z, 0f); baseCost = Mathf.Abs(Vector3.Angle(differenceFlat, differenceFull)); break; case CostMethodType.UpSucksDownOkay: // <- work with both // for people who hate walking uphill but don't mind going downhill // there is a cost for going up but no cost for going down baseCost = Mathf.Max(0f, toNode.transform.position.y - fromNode.transform.position.y); break; //////////////////// default: // if nothing else, just use the physical length of the edge baseCost = (edge.transform.localScale.y * 2f); break; } // end of baseCost calculation // retrieve the traveler profile, then check if the profile is null to prevent errors if (Traveler == null) { Traveler = TravelerProfileCatalog.GetProfile(TravelerProfileCatalog.TravelerType.Neutral); } // if profile is not null use the average of the trait indexes (which will be -1 to 1) // but if profile is null then just use zero float compatibility = (Traveler != null ? (Traveler.LikesToWalk * edge.PedestrianFriendly + Traveler.LikesToBicycle * edge.BicyclistFriendly + Traveler.LikesToDrive * edge.CarFriendly + Traveler.LikesVistas * edge.Beautiful + Traveler.LikesFood * edge.FoodAvailable + Traveler.LikesHeart * edge.Heart) / 5f : 0f); // (1 - compatibility) gives a value that is 0 (good) to 2 (bad) // this translates to: // -- cost is reduced when the traveler likes the edge // -- cost is not affected when the traveler has no opinion of the edge // -- cost is increased when the traveler dislikes the edge float personalityCost = (1f - compatibility); // using personalityCost * baseCost means "the traveler's emotions can make anything easy or anything hard" return(personalityCost * baseCost); }
// Update is called once per frame public void Update() { if (group && group.goals == null) { group = null; goal = null; } if (!paused) { if (GridManagerScript.enableCollisions && avatar.GetComponent <Trigger>().stop) { return; } //sanity check if (nextNode == null || currentNode == null || currentNode.FindEdgeTo(nextNode) == null) { return; } //update a the path when end is closed if (GridManagerScript.usingAStarOnly && (AStarPath.Count == 0 || (entering && !AStarPath.Last().goal))) { Debug.Log("NewPath " + AStarPath.Last()); indexAtAstarPath = 0; AStarPath = GridManagerScript.AStar. GenerateEntryPath(currentNode, group != null? group.goals.ToArray() : null); nextNode = GetBestDiffusionNode(currentNode); } if (!GridManagerScript.isTest) { float speedLimit = GridManagerScript.citySpeed; //get the edge speed and calculate speed limit if it exist, else assume no difference //calc traffic EdgeScript edge = currentNode.FindEdgeTo(nextNode); if (currentNode != null && edge != null) { //speed in miles per hour //0.0003miles/s = 0.066p/second per second (fs - fake seconds) speedLimit = edge.GetSpeed(); //GET THIS CHECKED OUT // speedLimit = speedLimit - speedLimit * (edge.nodesTraffic / EdgeScript.maxTraffic) * EdgeScript.maxTrafficP; } if (GridManagerScript.accelerationCalculations) { //if the next node is the parking node and avatar has some speed0 slow down if (nextNode.openParking && (nextNode.NodeStatus == NodeScript.NODE_STATUS.END && atmSpeed >= (speedLimit * parkingSpeed))) { atmSpeed -= atmSpeed * (decceleration * 2); } //else accelerate// decelerate else { //calc deceleration //if above speed limit or if (atmSpeed > speedLimit) { atmSpeed -= atmSpeed * decceleration; } //calc acceleration else if (atmSpeed < speedLimit) { acceleration = acceleration * accelerationSteepness; atmSpeed += atmSpeed * acceleration; if (atmSpeed > speedLimit) { atmSpeed = speedLimit; //skip coming into calculations for dec or acc next time } } } speedLimit = atmSpeed; } //foreach frame translate //checkout 123 for explanation //1 mile irl = 240p in game //add about 20p to make up for inconsistency of time.deltatime //every update calc how much of a second has passed in game and add it on the speed float speedinpixels = (speedLimit * 260) / 3600; //mile/h float translatePerFrame = speedinpixels * GridManagerScript.secondsPerSecond * Time.deltaTime; Vector3 translateAvatar = avatar.transform.position + (nextNode.transform.position - avatar.transform.position).normalized * translatePerFrame; //vector to translate by //get the distance between avatar and goal and see if you're overshooting //if you're not overshooting just continue //if you are overshooting just arrive //Db (distance before) is hypothenos from origin to goal (basic distance) //Da (distance after) is the hypothenos from origin to the potential new destination (towards goal) float Db = Vector3.Distance(avatar.transform.position, nextNode.transform.position); float Da = Vector3.Distance(avatar.transform.position, translateAvatar); if (Da > Db) { avatar.transform.position = nextNode.transform.position; } else { avatar.transform.position = translateAvatar; } } else { avatar.transform.position = avatar.transform.position + (nextNode.transform.position - avatar.transform.position).normalized; } // Check if the avatar is within the bounds of the next node's centre if (avatar.transform.position.x >= nextNode.transform.position.x - 1.0f && avatar.transform.position.y >= nextNode.transform.position.y - 1.0f && avatar.transform.position.z >= nextNode.transform.position.z - 1.0f && avatar.transform.position.x <= nextNode.transform.position.x + 1.0f && avatar.transform.position.y <= nextNode.transform.position.y + 1.0f && avatar.transform.position.z <= nextNode.transform.position.z + 1.0f) { double timeOfDay = getTimeOfDay(); currentNode.FindEdgeTo(nextNode).removeOccupancy(timeOfDay); // If not at the destination //TODO REWRITE THIS CHECK AND THE ONES BELOW //if no specific goal any goals count //if specific goal, all goals count if ((goal == null && ((nextNode.NodeStatus != NodeScript.NODE_STATUS.END && !isCityAvatar) || (!nextNode.goal && isCityAvatar && entering) || (!nextNode.exit && isCityAvatar && !entering))) || (goal != null && goal != nextNode)) { //set the previous node to the current node previousNode = currentNode; // Set the current node as the next node currentNode = nextNode; // Calculate the next node if (entering) { nextNode = GetBestDiffusionNode(nextNode); } else { nextNode = GetBestExitDiffusionNode(nextNode); } currentNode.FindEdgeTo(nextNode).addOccupancy(timeOfDay); totalPathLength += currentNode.FindEdgeTo(nextNode).roadLen; } else { // check if destination reached is in goals //if goal is not specified arrive at any of the goals //if goal is specified only arrive at the given goal //only arrive at redir goal if ((goal == null && (group == null || group.goals == null || (group.goals.Contains(nextNode) || (redirected && group.redirectToGoals.Contains(nextNode))) || !entering)) || (goal == nextNode)) { //reset the overlapping avatars on the node behind you //if(GridManagerScript.isTest) currentNode.avatarsOverlapping = -1; // If your destination was a parking lot add a car to the lot if ((entering && nextNode.goal && nextNode.GetComponentInParent <ParkingLot>() || goal == nextNode)) { timeArrived = (float)timeOfDay; //get the current time timeLeftArrivedVTCAll.Add(new Tuple <float, float, float>(timeLeft, timeArrived, totalPathLength)); nextNode.GetComponentInParent <ParkingLot>().AddCar(this); endReached = true; } else if (!entering) { endReached = true; } } //go to the actual goal else { //set the previous node to the current node previousNode = currentNode; // Set the current node as the next node currentNode = nextNode; // Calculate the next node //passes current end that is not it's own goal nextNode = GetBestDiffusionNode(nextNode); currentNode.FindEdgeTo(nextNode).addOccupancy(timeOfDay); totalPathLength += currentNode.FindEdgeTo(nextNode).roadLen; } } } } }
public void loadGraph() { //Destroy all nodes //read file string line; using (StreamReader sr = new StreamReader(path)) { line = sr.ReadToEnd(); } JsonReader reader = new JsonReader(); var output = reader.Read <Dictionary <string, object> >(line); Dictionary <string, object>[] jnode = (Dictionary <string, object>[])output ["nodes"]; //create nodes print("CREATE NODES"); foreach (Dictionary <string, object> jNode in jnode) { GameObject node = (GameObject)Instantiate(nodeTemp); node.name = jNode["id"].ToString(); NodeScript scpt = node.GetComponent <NodeScript>(); scpt.Id = jNode["id"].ToString(); scpt.occupants = (int)jNode["arrivals"]; scpt.goalState = (bool)jNode["isGoal"]; Dictionary <string, object> pos = (Dictionary <string, object>)jNode["position"]; node.transform.position = new Vector3(float.Parse(pos["x"].ToString()), float.Parse(pos["y"].ToString()), float.Parse(pos["z"].ToString())); } print("CREATE EDGES"); //Create edges foreach (Dictionary <string, object> jNode in jnode) { try { foreach (Dictionary <string, object> jEdge in (Dictionary <string, object>[])jNode["edges"]) { GameObject start = GameObject.Find(jEdge["start"].ToString()); GameObject end = GameObject.Find(jEdge["end"].ToString()); GameObject edge = (GameObject)Instantiate(edgeTemp, start.transform.position, start.transform.rotation); edge.name = jEdge ["start"].ToString() + "->" + jEdge ["end"].ToString(); EdgeScript edgeScpt = edge.GetComponent <EdgeScript> (); edgeScpt.flowRate = (int)jEdge ["flowRate"]; edgeScpt.cost = (int)jEdge ["cost"]; edgeScpt.start = start; edgeScpt.end = end; edge.transform.parent = start.transform; } } catch (InvalidCastException e) { } } //Create elevators try { foreach (Dictionary <string, object> jElevator in (Dictionary <string, object>[])output ["elevators"]) { //instantiate new elevator GameObject elevator = (GameObject)Instantiate(elevatorTemp); elevator.name = jElevator["id"].ToString() + "elevator"; ElevatorScript scpt = elevator.GetComponent <ElevatorScript>(); Dictionary <string, object>[] jNodes = (Dictionary <string, object>[])jElevator["nodes"]; GameObject[] nodes = new GameObject[jNodes.Length]; //Create new Edge array int index = 0; foreach (Dictionary <string, object> node in jNodes) { nodes[index] = GameObject.Find(node["id"].ToString()); index++; } scpt.Id = jElevator["id"].ToString(); scpt.nodes = nodes; scpt.initialLocation = jElevator["initialLocation"].ToString(); //Find edges by name } } catch (InvalidCastException e) { } }
public EdgeWrapper(EdgeScript Edge, float Cost = float.PositiveInfinity) { this.Edge = Edge; this.Cost = Cost; }
public void StretchEdgeBetweenTwoVertices(VertexScript vertexA, VertexScript vertexB, EdgeScript edge) { Vector3 leftVertexPosition = vertexA.transform.position; Vector3 rightVertexPosition = vertexB.transform.position; edge.transform.position = Vector3.Lerp(leftVertexPosition, rightVertexPosition, 0.5f); Vector3 positionDifferences = rightVertexPosition - leftVertexPosition; edge.transform.localScale = new Vector3(EdgeThickness, positionDifferences.magnitude * 0.5f, EdgeThickness); edge.transform.rotation = Quaternion.LookRotation(positionDifferences); edge.transform.Rotate(new Vector3(90, 0, 0)); }