public void initializeWithArray(Vector2 lowLeftBound, Vector2 upRightBound, Node.SquareType[][] terrainArr) { lowerLeftBound = lowLeftBound; upperRightBound = upRightBound; numXNodes = terrainArr[0].Length; numYNodes = terrainArr.Length; // Determine correct node density float xDist = lowerLeftBound.x - upperRightBound.x; nodeDensity = numXNodes / xDist; // Magic number allows for proper drawing of nodes. radii = 0.75f / nodeDensity; numValidNodes = 0; // Initialize the node array. Note the inverted y, normal x setup. nodeArr = new Node[numYNodes][]; for (int y = 0; y < numYNodes; y++) { nodeArr[y] = new Node[numXNodes]; for (int x = 0; x < numXNodes; x++) { IntVec2 arrPos = new IntVec2(x, y); Vector2 newPosV2 = ArrPosToWorldSpace(arrPos); nodeArr[y][x] = new Node(transform.gameObject, nodeImg, newPosV2, arrPos, terrainArr[y][x], radii * 2, numValidNodes++); //add up-left edge (inverse y) if (allowedPaths > Paths.quadDir) { if (x - 1 >= 0 && y - 1 >= 0 && isOkayToFloodDiag(arrPos, IntVec2.down, IntVec2.left)) nodeArr[y][x].addBidirEdge(nodeArr[y - 1][x - 1], sqrt2); //add up-right edge (inverse y) if (x + 1 < numXNodes && y - 1 >= 0 && isOkayToFloodDiag(arrPos, IntVec2.down, IntVec2.right)) nodeArr[y][x].addBidirEdge(nodeArr[y - 1][x + 1], sqrt2); } //add up edge if (y - 1 >= 0 && isOkayToFloodUDLR(arrPos, IntVec2.down, (Vector2)IntVec2.left)) nodeArr[y][x].addBidirEdge(nodeArr[y - 1][x], 1); //add left edge if (x - 1 >= 0 && isOkayToFloodUDLR(arrPos, IntVec2.left, (Vector2)IntVec2.up)) nodeArr[y][x].addBidirEdge(nodeArr[y][x - 1], 1); } } isInitialized = true; }
// Same, but with diagonals. private bool isOkayToFloodDiag(IntVec2 startPos, IntVec2 dir1, IntVec2 dir2) { return true; }
/// <summary> /// Converts an array position to world space. /// </summary> /// <param name="pos">The position in array space.</param> /// <returns>The position in world space.</returns> public Vector2 ArrPosToWorldSpace(IntVec2 pos) { float x = pos.x / nodeDensity; float y = pos.y / nodeDensity; x += lowerLeftBound.x; y += lowerLeftBound.y; return new Vector2(x, y); }
/// <summary> /// null helper function for floodfill. /// Returns true. /// </summary> /// <returns>true!</returns> private bool isOkayToFloodUDLR(IntVec2 startPos, IntVec2 direction, Vector2 orthAngle) { return true; }
// Same, but with diagonals. private bool isOkayToFloodDiag(IntVec2 startPos, IntVec2 dir1, IntVec2 dir2) { IntVec2 newPos = startPos + dir1 + dir2; IntVec2 orth = dir1 - dir2; Vector2 orthAngle = ((Vector2) orth) / sqrt2; Vector2 newPosV2 = ArrPosToWorldSpace(newPos); Vector2 oldPosV2 = ArrPosToWorldSpace(startPos); //Linecase uses width in consideration of spreading to new nodes, this time diagonally return (!Physics2D.OverlapCircle(newPosV2, radii, LAYER_FILTER_MASK) && !Physics2D.Linecast(oldPosV2 + orthAngle * radii, newPosV2 + orthAngle * radii, LAYER_FILTER_MASK) && !Physics2D.Linecast(oldPosV2 - orthAngle * radii, newPosV2 - orthAngle * radii, LAYER_FILTER_MASK)); }
/// <summary> /// Helper function for floodfill. Does extra checks / physics checks to see if we can add a node in the specified location. /// </summary> /// <param name="startPos">The position to start from.</param> /// <param name="direction">The direction to go to.</param> /// <param name="orthAngle">The orthogonal to the direction.</param> /// <returns></returns> private bool isOkayToFloodUDLR(IntVec2 startPos, IntVec2 direction, Vector2 orthAngle) { IntVec2 newPos = startPos + direction; Vector2 newPosV2 = ArrPosToWorldSpace(newPos); Vector2 oldPosV2 = ArrPosToWorldSpace(startPos); //Linecase uses width in consideration of spreading to new nodes. return (!Physics2D.OverlapCircle(newPosV2, radii, LAYER_FILTER_MASK) && !Physics2D.Linecast(oldPosV2 + orthAngle * radii, newPosV2 + orthAngle * radii, LAYER_FILTER_MASK) && !Physics2D.Linecast(oldPosV2 - orthAngle * radii, newPosV2 - orthAngle * radii, LAYER_FILTER_MASK)); }
/// <summary> /// Fills all nodes in area and connects edges as necessesary. /// Does not have stack overflow problems, but may create a graph /// that isn't connected all the way through. /// </summary> private void fillAll() { for (int y = 0; y < numYNodes; y++) { for (int x = 0; x < numXNodes; x++) { IntVec2 arrPos = new IntVec2(x, y); Vector2 newPosV2 = ArrPosToWorldSpace(arrPos); #if DEBUG_PATHFINDER_UPDATELOOP if (!Physics2D.OverlapCircle(newPosV2, radii, LAYER_FILTER_MASK)) #endif { nodeArr[y][x] = new Node(transform.gameObject, nodeImg, newPosV2, arrPos, Node.randWalkState(), radii * 2, numValidNodes++); //add up-left edge (inverse y) if (allowedPaths > Paths.quadDir) { if (x - 1 >= 0 && y - 1 >= 0 && nodeArr[y - 1][x - 1] != null && isOkayToFloodDiag(arrPos, IntVec2.down, IntVec2.left)) nodeArr[y][x].addBidirEdge(nodeArr[y - 1][x - 1], sqrt2); //add up-right edge (inverse y) if (x + 1 < numXNodes && y - 1 >= 0 && nodeArr[y - 1][x + 1] != null && isOkayToFloodDiag(arrPos, IntVec2.down, IntVec2.right)) nodeArr[y][x].addBidirEdge(nodeArr[y - 1][x + 1], sqrt2); } //add up edge if (y - 1 >= 0 && nodeArr[y - 1][x] != null && isOkayToFloodUDLR(arrPos, IntVec2.down, (Vector2)IntVec2.left)) nodeArr[y][x].addBidirEdge(nodeArr[y - 1][x], 1); //add left edge if (x - 1 >= 0 && nodeArr[y][x - 1] != null && isOkayToFloodUDLR(arrPos, IntVec2.left, (Vector2)IntVec2.up)) nodeArr[y][x].addBidirEdge(nodeArr[y][x - 1], 1); } } } }
/// <summary> /// Constructs a Node. /// </summary> /// <param name="floorImg">The image which the node will use to display itself if set to do so.</param> /// <param name="position">Where the node is positioned in world space.</param> /// <param name="gridPos">The node position in the graph space.</param> /// <param name="scale">The node's visible scale.</param> /// <param name="num">The node's internal number.</param> /// <param name="vis">Whether the node is visible or not. Default is false.</param> public Node(GameObject parent, Sprite floorImg, Vector2 position, IntVec2 gridPos, SquareType typeOfTerrain, float scale = 0.75f, int num = 0, bool vis = true) { pos = position; this.gridPos = gridPos; GameObject drawer = new GameObject("Node " + pos); drawer.isStatic = true; drawer.transform.parent = parent.transform; //set sprite back in z, so it draws beneath everything. drawer.transform.position = new Vector3(position.x, position.y, 1); spriteDraw = drawer.AddComponent<SpriteRenderer>(); spriteDraw.sprite = floorImg; drawer.tag = gameTag; // Draw a little bigger than scale. spriteDraw.transform.localScale = new Vector2(scale * 1.35f, scale * 1.35f); // Draw with 50% alpha-white. //spriteDraw.color = defColor; //terrainType = randWalkState(); terrainType = typeOfTerrain; edgePenalty = costOfWalkState(terrainType); resetColor(); //spriteDraw.color = Random.ColorHSV(0.2f, 1.0f, 0.1f, 0.7f, 0.2f, 1.0f); CameFrom = null; number = num; #if DEBUG_NODE_EDGES lineDraw = drawer.AddComponent<LineRenderer>(); lineDraw.SetWidth(0.1f, 0.1f); lineDraw.SetVertexCount(maxEdges * 2 + 1); for (int i = 0; i < 17; i++) lineDraw.SetPosition(i, pos); #endif #if DEBUG_NODE_TEXT GameObject guiText = new GameObject("Debug text on Node " + pos); Vector2 guiPos = new Vector2(Camera.main.WorldToScreenPoint(pos).x / Camera.main.pixelWidth, Camera.main.WorldToScreenPoint(pos).y / Camera.main.pixelHeight); guiText.transform.position = guiPos; visNodeNum = guiText.AddComponent<GUIText>(); visNodeNum.text = num.ToString(); visNodeNum.anchor = TextAnchor.MiddleCenter; #endif // Do not move this, everything must be defined before calling Visible. Visible = vis; }