// Update is called once per frame
        void Update () {

            // Calculate the tile the target is standing on
            Int2 p = new Int2 ( Mathf.RoundToInt ((target.position.x - tileSize*0.5f) / tileSize), Mathf.RoundToInt ((target.position.z - tileSize*0.5f) / tileSize) );

            // Clamp range
            range = range < 1 ? 1 : range;

            // Remove tiles which are out of range
            bool changed = true;
            while ( changed ) {
                changed = false;
                foreach (KeyValuePair<Int2,ProceduralTile> pair in tiles ) {
                    if ( Mathf.Abs (pair.Key.x-p.x) > range || Mathf.Abs (pair.Key.y-p.y) > range ) {
                        pair.Value.Destroy ();
                        tiles.Remove ( pair.Key );
                        changed = true;
                        break;
                    }
                }
            }

            // Enqueue tiles which have come in range
            // and start calculating them
            for ( int x = p.x-range; x <= p.x+range; x++ ) {
                for ( int z = p.y-range; z <= p.y+range; z++ ) {
                    if ( !tiles.ContainsKey ( new Int2(x,z) ) ) {
                        ProceduralTile tile = new ProceduralTile ( this, x, z );
                        var generator = tile.Generate ();
                        // Tick it one step forward
                        generator.MoveNext ();
                        // Calculate the rest later
                        tileGenerationQueue.Enqueue (generator);
                        tiles.Add ( new Int2(x,z), tile );
                    }
                }
            }

            // The ones directly adjacent to the current one
            // should always be completely calculated
            // make sure they are
            for ( int x = p.x-1; x <= p.x+1; x++ ) {
                for ( int z = p.y-1; z <= p.y+1; z++ ) {
                    tiles[new Int2(x,z)].ForceFinish();
                }
            }

        }
Exemple #2
0
		/** Calculates the bounding box in XZ space of all nodes between \a from (inclusive) and \a to (exclusive) */
		static IntRect NodeBounds (MeshNode[] nodes, int from, int to) {
			if (to - from <= 0) throw new ArgumentException();

			var first = nodes[from].GetVertex(0);
			var min = new Int2(first.x,first.z);
			Int2 max = min;

			for (int j = from; j < to; j++) {
				var node = nodes[j];
				var nverts = node.GetVertexCount();
				for (int i = 0; i < nverts; i++) {
					var p = node.GetVertex(i);
					min.x = Math.Min (min.x, p.x);
					min.y = Math.Min (min.y, p.z);

					max.x = Math.Max (max.x, p.x);
					max.y = Math.Max (max.y, p.z);
				}
			}

			return new IntRect (min.x, min.y, max.x, max.y);
		}
        /** Returns a new rect which is offset by the specified amount.
		 */
        public IntRect Offset ( Int2 offset ) {
            return new IntRect ( xmin+offset.x, ymin + offset.y, xmax + offset.x, ymax + offset.y );
        }
Exemple #4
0
		/** Returns if the line segment \a a2 - \a b2 intersects the line segment \a a - \a b.
		 * If only the endpoints coincide, the result is undefined (may be true or false).
		 */
		public static bool Intersects (Int2 a, Int2 b, Int2 a2, Int2 b2) {
			return Left (a,b,a2) != Left (a,b,b2) && Left (a2,b2,a) != Left (a2,b2,b);
		}
Exemple #5
0
		/** Returns true if the points a in a clockwise order or if they are colinear */
		public static bool IsClockwiseMargin (Int2 a, Int2 b, Int2 c) {
			return Left(a, b, c);
		}
Exemple #6
0
		/** Returns if \a p lies on the left side of the line \a a - \a b. Also returns true if the points are colinear */
		public static bool Left (Int2 a, Int2 b, Int2 c) {
			return (long)(b.x - a.x) * (long)(c.y - a.y) - (long)(c.x - a.x) * (long)(b.y - a.y) <= 0;
		}
Exemple #7
0
		/** Returns if the triangle \a ABC contains the point \a p */
		public static bool ContainsPoint (Int2 a, Int2 b, Int2 c, Int2 p) {
			return Polygon.IsClockwiseMargin (a,b, p) && Polygon.IsClockwiseMargin (b,c, p) && Polygon.IsClockwiseMargin (c,a, p);
		}
Exemple #8
0
		public static int Dot (Int2 a, Int2 b) {
			return a.x*b.x + a.y*b.y;
		}
        /** Async method for moving the graph */
        IEnumerator UpdateGraphCoroutine () {

            // Find the direction
            // that we want to move the graph in.
            // Calcuculate this in graph space (where a distance of one is the size of one node)
            Vector3 dir = PointToGraphSpace(target.position) - PointToGraphSpace(graph.center);

            // Snap to a whole number of nodes
            dir.x = Mathf.Round(dir.x);
            dir.z = Mathf.Round(dir.z);
            dir.y = 0;

            // Nothing do to
            if ( dir == Vector3.zero ) yield break;

            // Number of nodes to offset in each direction
            Int2 offset = new Int2(-Mathf.RoundToInt(dir.x), -Mathf.RoundToInt(dir.z));

            // Move the center (this is in world units, so we need to convert it back from graph space)
            graph.center += graph.matrix.MultiplyVector (dir);
            graph.GenerateMatrix ();

            // Create a temporary buffer
            // required for the calculations
            if ( tmp == null || tmp.Length != graph.nodes.Length ) {
                tmp = new GridNode[graph.nodes.Length];
            }

            // Cache some variables for easier access
            int width = graph.width;
            int depth = graph.depth;
            GridNode[] nodes = graph.nodes;

            // Check if we have moved
            // less than a whole graph
            // width in any direction
            if ( Mathf.Abs(offset.x) <= width && Mathf.Abs(offset.y) <= depth ) {
		
                // Offset each node by the #offset variable
                // nodes which would end up outside the graph
                // will wrap around to the other side of it
                for ( int z=0; z < depth; z++ ) {
                    int pz = z*width;
                    int tz = ((z+offset.y + depth)%depth)*width;
                    for ( int x=0; x < width; x++ ) {
                        tmp[tz + ((x+offset.x + width) % width)] = nodes[pz + x];
                    }
                }
			
                yield return null;

                // Copy the nodes back to the graph
                // and set the correct indices
                for ( int z=0; z < depth; z++ ) {
                    int pz = z*width;
                    for ( int x=0; x < width; x++ ) {
                        GridNode node = tmp[pz + x];
                        node.NodeInGridIndex = pz + x;
                        nodes[pz + x] = node;
                    }
                }


                IntRect r = new IntRect ( 0, 0, offset.x, offset.y );
                int minz = r.ymax;
                int maxz = depth;

                // If offset.x < 0, adjust the rect
                if ( r.xmin > r.xmax ) {
                    int tmp2 = r.xmax;
                    r.xmax = width + r.xmin;
                    r.xmin = width + tmp2;
                }

                // If offset.y < 0, adjust the rect
                if ( r.ymin > r.ymax ) {
                    int tmp2 = r.ymax;
                    r.ymax = depth + r.ymin;
                    r.ymin = depth + tmp2;
	
                    minz = 0;
                    maxz = r.ymin;
                }

                // Make sure erosion is taken into account
                // Otherwise we would end up with ugly artifacts
                r = r.Expand ( graph.erodeIterations + 1 );

                // Makes sure the rect stays inside the grid
                r = IntRect.Intersection ( r, new IntRect ( 0, 0, width, depth ) );
	
                yield return null;

                // Update all nodes along one edge of the graph
                // With the same width as the rect
                for ( int z = r.ymin; z < r.ymax; z++ ) {
                    for ( int x = 0; x < width; x++ ) {
                        graph.UpdateNodePositionCollision ( nodes[z*width + x], x, z, false );
                    }
                }
	
                yield return null;
		
                // Update all nodes along the other edge of the graph
                // With the same width as the rect
                for ( int z = minz; z < maxz; z++ ) {
                    for ( int x = r.xmin; x < r.xmax; x++ ) {
                        graph.UpdateNodePositionCollision ( nodes[z*width + x], x, z, false );
                    }
                }
	
                yield return null;

                // Calculate all connections for the nodes
                // that might have changed
                for ( int z = r.ymin; z < r.ymax; z++ ) {
                    for ( int x = 0; x < width; x++ ) {
                        graph.CalculateConnections (nodes, x, z, nodes[z*width+x]);
                    }
                }
	
                yield return null;
	
                // Calculate all connections for the nodes
                // that might have changed
                for ( int z = minz; z < maxz; z++ ) {
                    for ( int x = r.xmin; x < r.xmax; x++ ) {
                        graph.CalculateConnections (nodes, x, z, nodes[z*width+x]);
                    }
                }
	
                yield return null;

                // Calculate all connections for the nodes along the boundary
                // of the graph, these always need to be updated
                /** \todo Optimize to not traverse all nodes in the graph, only those at the edges */
                for ( int z = 0; z < depth; z++ ) {
                    for ( int x = 0; x < width; x++ ) {
                        if ( x == 0 || z == 0 || x >= width-1 || z >= depth-1 ) graph.CalculateConnections (nodes, x, z, nodes[z*width+x]);
                    }
                }
			
            } else {

                // Just update all nodes
                for ( int z = 0; z < depth; z++ ) {
                    for ( int x = 0; x < width; x++ ) {
                        graph.UpdateNodePositionCollision ( nodes[z*width + x], x, z, false );
                    }
                }

                // Recalculate the connections of all nodes
                for ( int z = 0; z < depth; z++ ) {
                    for ( int x = 0; x < width; x++ ) {
                        graph.CalculateConnections (nodes, x, z, nodes[z*width+x]);
                    }
                }
            }
		
            if ( floodFill ) {
                yield return null;
                // Make sure the areas for the graph
                // have been recalculated
                // not doing this can cause pathfinding to fail
                AstarPath.active.QueueWorkItemFloodFill ();
            }
        }
		/** Returns if there is an obstacle between \a _a and \a _b on the graph.
		 * \param [in] _a Point to linecast from
		 * \param [in] _b Point to linecast to
		 * \param [out] hit Contains info on what was hit, see GraphHitInfo
		 * \param [in] hint \deprecated
		 * \param trace If a list is passed, then it will be filled with all nodes the linecast traverses
		 *
		 * This is not the same as Physics.Linecast, this function traverses the graph and looks for collisions.
		 *
		 * It uses a method similar to Bresenham's line algorithm but it has been
		 * extended to allow the start and end points to lie on non-integer coordinates
		 * (which makes the math a bit trickier).
		 *
		 * \see https://en.wikipedia.org/wiki/Bresenham's_line_algorithm
		 *
		 * \version In 3.6.8 this method was rewritten to improve accuracy and performance.
		 * Previously it used a sampling approach which could cut corners of obstacles slightly
		 * and was pretty inefficient.
		 *
		 * \astarpro
		 */
		public bool Linecast (Vector3 _a, Vector3 _b, GraphNode hint, out GraphHitInfo hit, List<GraphNode> trace) {
			hit = new GraphHitInfo ();

			hit.origin = _a;

			Vector3 aInGraphSpace = inverseMatrix.MultiplyPoint3x4(_a);
			Vector3 bInGraphSpace = inverseMatrix.MultiplyPoint3x4(_b);

			// Clip the line so that the start and end points are on the graph
			if (!ClipLineSegmentToBounds (aInGraphSpace, bInGraphSpace, out aInGraphSpace, out bInGraphSpace)) {
				// Line does not intersect the graph
				// So there are no obstacles we can hit
				return false;
			}

			// Find the closest nodes to the start and end on the part of the segment which is on the graph
			var n1 = GetNearest (matrix.MultiplyPoint3x4(aInGraphSpace),NNConstraint.None).node as GridNodeBase;
			var n2 = GetNearest (matrix.MultiplyPoint3x4(bInGraphSpace),NNConstraint.None).node as GridNodeBase;

			if (!n1.Walkable) {
				hit.node = n1;
				// Hit point is the point where the segment intersects with the graph boundary
				// or just _a if it starts inside the graph
				hit.point = matrix.MultiplyPoint3x4(aInGraphSpace);
				hit.tangentOrigin = hit.point;
				return true;
			}

			// Throw away components we don't care about (y)
			var a = new Vector2(aInGraphSpace.x,aInGraphSpace.z);
			var b = new Vector2(bInGraphSpace.x,bInGraphSpace.z);

			// Subtract 0.5 because nodes have an offset of 0.5 (first node is at (0.5,0.5) not at (0,0))
			// And it's just more convenient to remove that term here
			a -= Vector2.one*0.5f;
			b -= Vector2.one*0.5f;

			// Couldn't find a valid node
			// This shouldn't really happen unless there are NO nodes in the graph
			if (n1 == null || n2 == null) {
				hit.node = null;
				hit.point = _a;
				return true;
			}

			var dir = b-a;

			// Primary direction that we will move in
			// (e.g up and right or down and left)
			var sign = new Int2((int)Mathf.Sign(dir.x), (int)Mathf.Sign(dir.y));

			// How much further we move away from (or towards) the line when walking along #sign
			// This isn't an actual distance. It is a signed distance so it can be negative (other side of the line)
			// Also it includes an additional factor, but the same factor is used everywhere
			// and we only check for if the signed distance is greater or equal to zero so it is ok
			var primaryDirectionError = CrossMagnitude(dir, new Vector2(sign.x,sign.y))*0.5f;

			/*         Z
			 *         |
			 *         |
			 *
			 *         2
			 *         |
			 * --  3 - X - 1  ----- X
			 *         |
			 *         0
			 *
			 *         |
			 *         |
			 */

			// This is the direction which moves further to the right of the segment (when looking from the start)
			int directionToReduceError;
			// This is the direction which moves further to the left of the segment (when looking from the start)
			int directionToIncreaseError;

			if (dir.y >= 0) {
				if (dir.x >= 0) {
					// First quadrant
					directionToReduceError = 1;
					directionToIncreaseError = 2;
				} else {
					// Second quadrant
					directionToReduceError = 2;
					directionToIncreaseError = 3;
				}
			} else {
				if (dir.x < 0) {
					// Third quadrant
					directionToReduceError = 3;
					directionToIncreaseError = 0;
				} else {
					// Fourth quadrant
					directionToReduceError = 0;
					directionToIncreaseError = 1;
				}
			}


			// Current node. Start at n1
			var current = n1;

			while (current.NodeInGridIndex != n2.NodeInGridIndex) {

				// We visited #current so add it to the trace
				if (trace != null) {
					trace.Add (current);
				}

				// Position of the node in 2D graph/node space
				// Here the first node in the graph is at (0,0)
				var p = new Vector2(current.NodeInGridIndex % width, current.NodeInGridIndex / width);

				// Calculate the error
				// This is proportional to the distance between the line and the node
				var error = CrossMagnitude(dir, p-a);

				// How does the error change we take one step in the primary direction
				var nerror = error + primaryDirectionError;

				// Check if we need to reduce or increase the error (we want to keep it near zero)
				// and pick the appropriate direction to move in
				int ndir = nerror < 0 ? directionToIncreaseError : directionToReduceError;

				// Check we can move in that direction
				var other = GetNeighbourAlongDirection(current, ndir);
				if (other != null) {
					current = other;
				} else {
					// Hit obstacle
					// We know from what direction we moved in
					// so we can calculate the line which we hit

					// Either X offset is 0 or Z offset is zero since we only move in one of the 4 axis aligned directions
					// The line we hit will be right between two nodes (so a distance of 0.5 from the current node in graph space)
					Vector2 lineOrigin = p + new Vector2 (neighbourXOffsets[ndir], neighbourZOffsets[ndir]) * 0.5f;
					Vector2 lineDirection;

					if (neighbourXOffsets[ndir] == 0) {
						// We hit a line parallel to the X axis
						lineDirection = new Vector2(1,0);
					} else {
						// We hit a line parallel to the Z axis
						lineDirection = new Vector2(0,1);
					}

					// Find the intersection
					var intersection = Polygon.IntersectionPoint (lineOrigin, lineOrigin+lineDirection, a, b);

					var currentNodePositionInGraphSpace = inverseMatrix.MultiplyPoint3x4((Vector3)current.position);

					// The intersection is in graph space (with an offset of 0.5) so we need to transform it to world space
					var intersection3D = new Vector3(intersection.x + 0.5f, currentNodePositionInGraphSpace.y, intersection.y + 0.5f);
					var lineOrigin3D = new Vector3(lineOrigin.x + 0.5f, currentNodePositionInGraphSpace.y, lineOrigin.y + 0.5f);

					hit.point = matrix.MultiplyPoint3x4(intersection3D);
					hit.tangentOrigin = matrix.MultiplyPoint3x4(lineOrigin3D);
					hit.tangent = matrix.MultiplyVector(new Vector3(lineDirection.x,0,lineDirection.y));
					hit.node = current;

					return true;
				}
			}

			// Enqueue the last node to the trace
			if (trace != null) {
				trace.Add (current);
			}

			// No obstacles detected
			if (current == n2) {
				return false;
			}

			// Reached node right above or right below n2 but we cannot reach it
			hit.point = (Vector3)current.position;
			hit.tangentOrigin = hit.point;
			return true;
		}
Exemple #11
0
		public static Int3 ToInt3XZ (Int2 o) {
			return new Int3 (o.x,0,o.y);
		}
Exemple #12
0
		public static Int2 Max (Int2 a, Int2 b) {
			return new Int2 (System.Math.Max (a.x,b.x), System.Math.Max (a.y,b.y));
		}
Exemple #13
0
		/** Returns a new Int2 rotated 90*r degrees around the origin. */
		public static Int2 Rotate ( Int2 v, int r ) {
			r = r % 4;
			return new Int2 ( v.x*Rotations[r*4+0] + v.y*Rotations[r*4+1], v.x*Rotations[r*4+2] + v.y*Rotations[r*4+3] );
		}
Exemple #14
0
		public static long DotLong (Int2 a, Int2 b) {
			return (long)a.x*(long)b.x + (long)a.y*(long)b.y;
		}
Exemple #15
0
			public BBTreeBox (MeshNode node) {
				this.node = node;
				var first = node.GetVertex(0);
				var min = new Int2(first.x,first.z);
				Int2 max = min;

				for (int i=1;i<node.GetVertexCount();i++) {
					var p = node.GetVertex(i);
					min.x = Math.Min (min.x,p.x);
					min.y = Math.Min (min.y,p.z);

					max.x = Math.Max (max.x,p.x);
					max.y = Math.Max (max.y,p.z);
				}

				rect = new IntRect (min.x,min.y,max.x,max.y);
				left = right = -1;
			}
Exemple #16
0
		/** Factor of the nearest point on the segment.
		 * Returned value is in the range [0,1] if the point lies on the segment otherwise it just lies on the line.
		 * The closest point can be got by (end-start)*factor + start;
		 */
		public static float NearestPointFactor (Int2 lineStart, Int2 lineEnd, Int2 point)
		{
			Int2 lineDirection = lineEnd-lineStart;
			double magn = lineDirection.sqrMagnitudeLong;

			double closestPoint = Int2.DotLong(point-lineStart,lineDirection); //Vector3.Dot(lineDirection,lineDirection);
			if (magn != 0) closestPoint /= magn;

			return (float)closestPoint;
			//return closestPoint / magn;
		}
		/** Create connections between all nodes.
		 * \warning This implementation is not thread safe. It uses cached variables to improve performance
		 */
		void CreateNodeConnections (TriangleMeshNode[] nodes) {

			List<MeshNode> connections = ListPool<MeshNode>.Claim (); //new List<MeshNode>();
			List<uint> connectionCosts = ListPool<uint>.Claim (); //new List<uint>();

			Dictionary<Int2,int> nodeRefs = cachedInt2_int_dict;
			nodeRefs.Clear();

			// Build node neighbours
			for (int i=0;i<nodes.Length;i++) {

				TriangleMeshNode node = nodes[i];

				int av = node.GetVertexCount ();

				for (int a=0;a<av;a++) {

					// Recast can in some very special cases generate degenerate triangles which are simply lines
					// In that case, duplicate keys might be added and thus an exception will be thrown
					// It is safe to ignore the second edge though... I think (only found one case where this happens)
					var key = new Int2 (node.GetVertexIndex(a), node.GetVertexIndex ((a+1) % av));
					if (!nodeRefs.ContainsKey(key)) {
						nodeRefs.Add (key, i);
					}
				}
			}


			for (int i=0;i<nodes.Length;i++) {

				TriangleMeshNode node = nodes[i];

				connections.Clear ();
				connectionCosts.Clear ();

				int av = node.GetVertexCount ();

				for (int a=0;a<av;a++) {
					int first = node.GetVertexIndex(a);
					int second = node.GetVertexIndex((a+1) % av);
					int connNode;

					if (nodeRefs.TryGetValue (new Int2 (second, first), out connNode)) {
						TriangleMeshNode other = nodes[connNode];

						int bv = other.GetVertexCount ();

						for (int b=0;b<bv;b++) {
							/** \todo This will fail on edges which are only partially shared */
							if (other.GetVertexIndex (b) == second && other.GetVertexIndex ((b+1) % bv) == first) {
								uint cost = (uint)(node.position - other.position).costMagnitude;
								connections.Add (other);
								connectionCosts.Add (cost);
								break;
							}
						}
					}
				}

				node.connections = connections.ToArray ();
				node.connectionCosts = connectionCosts.ToArray ();
			}

			ListPool<MeshNode>.Release (connections);
			ListPool<uint>.Release (connectionCosts);
		}