public void OnPickStart(Topology.Face pickedFace, int button) { if (_moving || pickedFace.isExternal) { return; } if (button == 0) { if (_selectedFace && _selectedFace != pickedFace) { Deselect(_selectedFace); _selectedFace = Topology.Face.none; } if (_faceUnits[pickedFace] != null) { Select(pickedFace); _selectedFace = pickedFace; } } else if (button == 1) { if (_selectedFace && _selectedFace != pickedFace && pickedFace.isInternal) { _path = _pathFinder.FindPath(_selectedFace, pickedFace, CostHeuristic, Cost, _path); if (_path.Count > 0) { StartCoroutine(MoveUnitAlongPath(_path)); } } } }
private void BlockPathFaces(IFaceEdgePath path) { foreach (var face in path.AsFacePath()) { _faceBlockedStates[face] = true; _dynamicMesh.RebuildFace(face, _triangulationBlocked); } }
public void OnPickChange(Topology.Face oldFace, Topology.Face newFace) { if (_startFace) { ClearPreviousPickState(); if (newFace == _startFace || newFace.isExternal || _faceBlockedStates[_startFace] == true || _faceBlockedStates[newFace] == true) { _path = null; } else { _path = _pathFinder.FindPath(_startFace, newFace, CostHeuristic, Cost, _path); } DrawCurrentPickState(newFace); _dynamicMesh.RebuildMesh(DynamicMesh.VertexAttributes.Normal | DynamicMesh.VertexAttributes.Color); } }
public void OnPickEnd(Topology.Face face, int button) { if (button == 0 && _startFace) { ClearPreviousPickState(); if (face == _startFace) { ClearBlockedFaces(_startFace); } else if (_path != null && _path.Count > 0) { BlockPathFaces(_path); } _path = null; _startFace = Topology.Face.none; _dynamicMesh.RebuildMesh(DynamicMesh.VertexAttributes.Normal | DynamicMesh.VertexAttributes.Color); } }
/// <summary> /// Finds the shortest or otherwise least costly path from the specified source face to /// the specified target face, using the A* algorithm and the supplied heuristic and cost /// delegates to measure costs between faces and over face edges. /// </summary> /// <param name="source">The source face from which the path should start.</param> /// <param name="target">The target face that the path should attempt to reach.</param> /// <param name="costHeuristic">Delegate for estimating the cost of the path from the specified source face to the specified target face.</param> /// <param name="cost">Delegate for determining the actual cost of the path along the specified face edge, from its near vertex to its far face.</param> /// <param name="path">An optional existing path created by an earlier call to one of the <seealso cref="O:MakeIt.Tile.PathFinder.FindPath"/> functions, which will be overwritten with the new path data.</param> /// <returns>A face edge path instance describing the path found from source to target, or an incomplete object if no path was found.</returns> /// <remarks><para>The optional <paramref name="path"/> parameter is useful for reducing allocation activity /// and pressure on the garbage collector. Reusing an existing path object will not require an additional /// allocation to store the path as long as the new path fits inside the capacity already available in the /// existing path.</para></remarks> public IFaceEdgePath FindPath(Topology.Face source, Topology.Face target, FaceCostHeuristicDelegate costHeuristic, FaceCostDelegate cost, IFaceEdgePath path = null) { if (!source) { throw new ArgumentException("The source face must be a valid face.", "source"); } if (!target) { throw new ArgumentException("The target face must be a valid face.", "target"); } if (source.topology != target.topology) { throw new ArgumentException("The target face must belong to the same topology as the source face.", "target"); } var concretePath = path as FaceEdgePath; if (concretePath == null) { if (path != null) { throw new ArgumentException("The provided pre-allocated path was not an instance of the necessary underlying type recognized by this path finder.", "path"); } concretePath = new FaceEdgePath(); } var topology = source.topology; if (source == target) { return(concretePath.Rebuild(source, target)); } if (_queue == null) { _queue = new DelegateOrderedPriorityQueue <Node>(Node.AreOrdered, Mathf.CeilToInt(Mathf.Sqrt(source.topology.faces.Count))); _openSet = new Dictionary <int, Node>(); _closedSet = new Dictionary <int, Node>(); } else { _queue.Clear(); _openSet.Clear(); _closedSet.Clear(); } _queue.Push(new Node(0f, 0f, costHeuristic(source, target, 0), source.index, -1, -1, 0)); _openSet.Add(source.index, _queue.front); while (_queue.Count > 0) { var node = _queue.front; _queue.Pop(); if (node._elementIndex == target.index) { return(concretePath.Rebuild(source, target, node, _closedSet)); } _closedSet.Add(node._elementIndex, node); var face = new Topology.Face(topology, node._elementIndex); foreach (var edge in face.edges) { if (_closedSet.ContainsKey(edge.face.index)) { continue; } var g = node._g + cost(edge, node._length); if (!float.IsPositiveInfinity(g)) { Node neighborNode; if (!_openSet.TryGetValue(edge.face.index, out neighborNode)) { var h = costHeuristic(edge.face, target, node._length); neighborNode = new Node(g + h, g, h, edge.face.index, edge.index, node._elementIndex, node._length + 1); _queue.Push(neighborNode); _openSet.Add(edge.face.index, neighborNode); } else if (g < neighborNode._g) { var h = costHeuristic(edge.face, target, node._length); neighborNode = new Node(g + h, g, h, edge.face.index, edge.index, node._elementIndex, node._length + 1); _openSet[edge.face.index] = neighborNode; _queue.Reprioritize(neighborNode); } } } } return(concretePath.Rebuild(source, target)); }
/// <summary> /// Finds the shortest or path from the specified source face to the specified target face, /// using the A* algorithm and the supplied face positions to measure spherical arc distance /// between faces and over face edges. /// </summary> /// <param name="source">The source face from which the path should start.</param> /// <param name="target">The target face that the path should attempt to reach.</param> /// <param name="surface">The surface describing the overall shape of the spherical manifold.</param> /// <param name="facePositions">The three dimensional positions of each face in the world.</param> /// <param name="path">An optional existing path created by an earlier call to one of the <seealso cref="O:MakeIt.Tile.PathFinder.FindPath"/> functions, which will be overwritten with the new path data.</param> /// <returns>A face edge path instance describing the path found from source to target, or an incomplete object if no path was found.</returns> /// <remarks><para>The optional <paramref name="path"/> parameter is useful for reducing allocation activity /// and pressure on the garbage collector. Reusing an existing path object will not require an additional /// allocation to store the path as long as the new path fits inside the capacity already available in the /// existing path.</para></remarks> public IFaceEdgePath FindSphericalEuclideanPath(Topology.Face source, Topology.Face target, SphericalSurface surface, IFaceAttribute <Vector3> facePositions, IFaceEdgePath path = null) { return(FindPath(source, target, (Topology.Face s, Topology.Face t, int pathLength) => { var sourcePosition = facePositions[s]; var targetPosition = facePositions[t]; return Geometry.SphericalArcLength(sourcePosition, targetPosition, surface.radius); }, (Topology.FaceEdge edge, int pathLength) => { if (edge.isOuterBoundary) { return float.PositiveInfinity; } var sourcePosition = facePositions[edge.nearFace]; var targetPosition = facePositions[edge.farFace]; return Geometry.SphericalArcLength(sourcePosition, targetPosition, surface.radius); }, path)); }
/// <summary> /// Finds the shortest or path from the specified source face to the specified target face, /// using the A* algorithm and the supplied face positions to measure standard Euclidean /// distance between faces and over face edges. /// </summary> /// <param name="source">The source face from which the path should start.</param> /// <param name="target">The target face that the path should attempt to reach.</param> /// <param name="facePositions">The three dimensional positions of each face in the world.</param> /// <param name="path">An optional existing path created by an earlier call to one of the <seealso cref="O:MakeIt.Tile.PathFinder.FindPath"/> functions, which will be overwritten with the new path data.</param> /// <returns>A face edge path instance describing the path found from source to target, or an incomplete object if no path was found.</returns> /// <remarks><para>The optional <paramref name="path"/> parameter is useful for reducing allocation activity /// and pressure on the garbage collector. Reusing an existing path object will not require an additional /// allocation to store the path as long as the new path fits inside the capacity already available in the /// existing path.</para></remarks> public IFaceEdgePath FindEuclideanPath(Topology.Face source, Topology.Face target, IFaceAttribute <Vector3> facePositions, IFaceEdgePath path = null) { return(FindPath(source, target, (Topology.Face s, Topology.Face t, int pathLength) => { return (facePositions[s] - facePositions[t]).magnitude; }, (Topology.FaceEdge edge, int pathLength) => { if (edge.isOuterBoundary) { return float.PositiveInfinity; } return (facePositions[edge.nearFace] - facePositions[edge.farFace]).magnitude; }, path)); }
private IEnumerator MoveUnitAlongPath(IFaceEdgePath path) { _moving = true; var edge = path[0]; var prevFace = edge.nearFace; var nextFace = edge.farFace; var unit = _faceUnits[prevFace]; if (_selectedFace != prevFace) { if (_selectedFace) { _dynamicMesh.RebuildFace(_selectedFace, _faceTriangulation); } _selectedFace = prevFace; _dynamicMesh.RebuildFace(_selectedFace, _selectedFaceTriangulation); _dynamicMesh.RebuildMesh(DynamicMesh.VertexAttributes.UV2); } var accumulatedTime = 0f; var pathIndex = 0; var effectiveMovementDuration = movementDuration * Cost(edge, pathIndex); while (pathIndex < path.Count) { yield return(null); accumulatedTime += Time.deltaTime; var stepProgress = accumulatedTime / effectiveMovementDuration; bool rebuildMesh = false; do { if (stepProgress >= 0.5f && _selectedFace == prevFace) { UpdateUnitFace(unit, prevFace, nextFace); rebuildMesh = true; } if (accumulatedTime >= effectiveMovementDuration) { accumulatedTime -= effectiveMovementDuration; ++pathIndex; if (pathIndex == path.Count) { var endPosition = _facePositions[_selectedFace]; unit.position = endPosition + _surface.GetNormal(endPosition) * 0.15f; _moving = false; yield break; } else { edge = path[pathIndex]; prevFace = edge.nearFace; nextFace = edge.farFace; effectiveMovementDuration = movementDuration * Cost(edge, pathIndex); stepProgress = accumulatedTime / effectiveMovementDuration; } } } while ((stepProgress >= 0.5f && _selectedFace == prevFace) || (accumulatedTime >= effectiveMovementDuration)); if (rebuildMesh) { _dynamicMesh.RebuildMesh(DynamicMesh.VertexAttributes.UV2); } var position = Vector3.Slerp(_facePositions[prevFace], _facePositions[nextFace], (-2f * stepProgress + 3f) * stepProgress * stepProgress); unit.position = position + _surface.GetNormal(position) * 0.15f; } }