public MyGuiScreenEditorWaypoint(MyWayPoint waypoint) : base(null, new Vector2(0.5f, 0.5f), MyGuiConstants.SCREEN_BACKGROUND_COLOR, null, MyTextsWrapperEnum.WayPoint) { m_waypoints = new List<MyWayPoint>(); m_waypoints.Add(waypoint); Init(); }
public MyGuiScreenEditorWaypoint(MyWayPoint waypoint) : base(null, new Vector2(0.5f, 0.5f), MyGuiConstants.SCREEN_BACKGROUND_COLOR, null, MyTextsWrapperEnum.WayPoint) { m_waypoints = new List <MyWayPoint>(); m_waypoints.Add(waypoint); Init(); }
// Add a new vertex to the graph. Return true if the vertex was successfully added. // If you also want to add it into the world (MyEntities), use CreateWaypoint instead. public static bool AddVertex(MyWayPoint v) { MakeConnectedComponentsDirty(); using (VerticesLock.AcquireExclusiveUsing()) { System.Diagnostics.Debug.Assert(readingVertices == false); return m_vertices.Add(v); } }
/// <summary> /// Create entities from action object builders /// </summary> void AddOrCreate() { MyEditorGizmo.ClearSelection(); // Once entities has been created in this action, they remain and if needed, are only removed/added to scene if (ActionEntities != null && ActionEntities.Count > 0) { foreach (MyEntity actionEntity in ActionEntities) { AddToScene(actionEntity); } } else { // If no ActionEntites are present, create them from provided object builders foreach (ObjectBuilderCreate crate in ActionObjectBuilders) { CreateFromObjectBuilder(crate.ObjectBuilder, crate.Matrix, crate.ScreenPosition); } } // Link the new entities and clean up waypoint vertices foreach (var e in ActionEntities) { e.Link(); } MyWayPointGraph.DeleteNullVerticesFromPaths(); // When copying a single waypoint, connect it to its source if (this is MyEditorActionEntityCopy && ActionEntities.Count == 1 && ActionEntities[0] is MyWayPoint) { var source = (this as MyEditorActionEntityCopy).SourceEntities[0] as MyWayPoint; if (source != null) { MyWayPoint.Connect(source, ActionEntities[0] as MyWayPoint); } } // When copying prefabs, connect snap points connections if (this is MyEditorActionEntityCopy) { MyEditor.Static.CopySnapPointLinks((this as MyEditorActionEntityCopy).SourceEntities, (this as MyEditorActionEntityCopy).RemapContext); } MyEditor.Static.IssueCheckAllCollidingObjects(); }
// Remove a vertex and all of its edges. Return true if the vertex was successfully removed from the graph. // Note that this function doesn't remove the vertex from the world (MyEntities): use MyWayPoint.Close for that. public static bool RemoveVertex(MyWayPoint v) { MakeConnectedComponentsDirty(); v.DisconnectFromAllNeighbors(); // delete vertex from all paths foreach (var path in StoredPaths) if (path.WayPoints.Contains(v)) path.WayPoints.Remove(v); RemovePathsWithZeroVertices(); using (VerticesLock.AcquireExclusiveUsing()) { System.Diagnostics.Debug.Assert(readingVertices == false); return m_vertices.Remove(v); } }
private void UpdateBlockedEdges() { if (IsWorking()) { foreach (var edge in m_edges) { MyWayPoint.RemoveBlockedEdgesForBots(edge); MyWayPoint.RemoveBlockedEdgesForPlayer(edge); } } else { foreach (var edge in m_edges) { MyWayPoint.AddBlockedEdgesForBots(edge); if (!IsDestructible) { bool isDestructible = false; foreach (var par in m_parts) { if (par != null && par.IsDestructible) { isDestructible = true; break; } } if (!isDestructible) { MyWayPoint.AddBlockedEdgesForPlayer(edge); } } } } }
public bool ContainsEdge(MyWayPoint v, MyWayPoint w) { using (MyEntities.EntityCloseLock.AcquireSharedUsing()) { for (int i = 0; i < WayPoints.Count - 1; i++) { var subpath = WayPoints[i].GetShortestPathTo(WayPoints[i + 1]); for (int j = 0; j < subpath.Count - 1; j++) if ((subpath[j] == v && subpath[j + 1] == w) || (subpath[j] == w && subpath[j + 1] == v)) return true; } } return false; }
public void SetWaypointPath(string name) { CurrentWaypoint = null; WaypointPath = MyWayPointGraph.GetPath(name); if (WaypointPath != null && WaypointPath.WayPoints.Count > 0) { CurrentWaypoint = WaypointPath.WayPoints[0]; } }
private bool FindPathBetweenWaypoints( List<MyWayPoint> closestVisibleWaypoints, MyWayPoint goal, Dictionary<MyWayPoint, bool> visibleFromStartPosCache, HashSet<Tuple<MyWayPoint, MyWayPoint>> blockedEdges, HashSet<Tuple<MyWayPoint, MyWayPoint>> unblockedEdges ) { // find the best path candidate float shortestPathLength = float.MaxValue; int retryCount = 0; for (int j = 0; j < closestVisibleWaypoints.Count && retryCount < 30; j++) // max retry count is 30 { var path = closestVisibleWaypoints[j].GetShortestPathTo(goal, blockedEdges, true, false); if (path.Count == 0) continue; // optimize the path: try to delete waypoints from the start of the path (if the next waypoints are visible) int farthestVisibleWaypointInPath = 0; for (int i = Math.Min(path.Count - 1, 20); i > 0; i--) // path optimization: max 20 raycasts per path candidate, reuse previous results if (path[i].IsVisibleFrom(m_startPos, m_goalEntity, visibleFromStartPosCache)) { farthestVisibleWaypointInPath = i; break; } // compute the length of the path candidate float length = Vector3.Distance(m_startPos, path[farthestVisibleWaypointInPath].WorldMatrix.Translation) + Vector3.Distance(path[path.Count - 1].WorldMatrix.Translation, m_goalPos); for (int i = farthestVisibleWaypointInPath; i < path.Count - 1; i++) length += Vector3.Distance(path[i].WorldMatrix.Translation, path[i + 1].WorldMatrix.Translation); // if it's the shortest path candidate yet, make it the new GPS path if (length < shortestPathLength) { // but first check that the edges are free of any obstructions (and remember it) bool pathBlocked = false; for (int i = farthestVisibleWaypointInPath; i < path.Count - 2; i++) { // assume that edges between non-generated waypoints are always ok if (path[i].Save && path[i + 1].Save) continue; var tuple = Tuple.Create(path[i], path[i + 1]); if (unblockedEdges.Contains(tuple)) // already tested and it was ok { } else if (path[i + 1].IsVisibleFrom(path[i].Position, m_goalEntity)) { unblockedEdges.Add(tuple); unblockedEdges.Add(Tuple.Create(path[i + 1], path[i])); } else { blockedEdges.Add(tuple); blockedEdges.Add(Tuple.Create(path[i + 1], path[i])); pathBlocked = true; break; } } if (pathBlocked) { j--; // retry current waypoint with updated cache of blocked edges retryCount++; continue; } // seems legit shortestPathLength = length; Path = new List<Vector3>(); Path.Add(m_startPos); for (int i = farthestVisibleWaypointInPath; i < path.Count; i++) Path.Add(path[i].WorldMatrix.Translation); Path.Add(m_goalPos); } } if (Path.Count != 0) { Message = new StringBuilder().AppendFormat(MyTextsWrapper.Get(MyTextsWrapperEnum.GPSDistance).ToString(), shortestPathLength); return true; } else { return false; } }
public static Matrix UpdateShapePosition() { if (DetachedVoxelHand != null) { VoxelHandShape.MoveAndRotate(DetachedVoxelHand.WorldMatrix.Translation, DetachedVoxelHand.WorldMatrix); return(DetachedVoxelHand.WorldMatrix); } Matrix world = Matrix.Identity; float minDist = 2 * VoxelHandShape.LocalVolume.Radius; float dist = 2 * minDist; Vector3 from = MyCamera.Position - MyCamera.UpVector * minDist * 0.5f; if (IsProjected) { if (!MyFakes.MWBUILDER) { var line = new MyLine(from, from + MyCamera.ForwardVector * 10000, true); var hit = MyEntities.GetIntersectionWithLine_IgnoreOtherThanSpecifiedClass(ref line, new System.Type[] { typeof(MyVoxelMap) }); if (hit != null) { dist = Vector3.Distance(MyCamera.Position, hit.Value.IntersectionPointInWorldSpace); } else { dist = 5000; } m_conePosition = from + MyCamera.ForwardVector * minDist * 0.7f; Vector3 shapePosition = from + MyCamera.ForwardVector * (dist + VoxelHandShape.LocalVolume.Radius * m_distance * 2); Vector3 shapeForward = Vector3.Normalize(MyCamera.UpVector * minDist * 0.5f + shapePosition - from); Vector3 shapeUp = shapeForward - MyCamera.ForwardVector + MyCamera.UpVector; world = Matrix.CreateWorld(shapePosition, shapeForward, shapeUp); VoxelHandShape.MoveAndRotate(shapePosition, world); return(world); } else { var line = new MyLine(from, from + MyCamera.ForwardVector * 10000, true); var hit = MyEntities.GetIntersectionWithLine_IgnoreOtherThanSpecifiedClass(ref line, new System.Type[] { typeof(MyVoxelMap) }); Vector3 normal = Vector3.Up; dist = 5000; Vector3 shapePosition = from + MyCamera.ForwardVector * dist; if (hit != null) { dist = Vector3.Distance(MyCamera.Position, hit.Value.IntersectionPointInWorldSpace); normal = hit.Value.NormalInWorldSpace; shapePosition = hit.Value.IntersectionPointInWorldSpace; } m_conePosition = from + MyCamera.ForwardVector * minDist * 0.7f; Vector3 shapeUp = normal; Vector3 shapeForward = Vector3.Cross(-MyCamera.LeftVector, shapeUp); float dot = Vector3.Dot(shapeUp, shapeForward); if ((dot > 0.9f) || (dot < -0.9f)) { shapeForward = Vector3.Forward; } shapePosition.X = MyEditorGrid.GetGridStepInMeters() * (float)Math.Round(shapePosition.X / MyEditorGrid.GetGridStepInMeters()); shapePosition.Y = MyEditorGrid.GetGridStepInMeters() * (float)Math.Round(shapePosition.Y / MyEditorGrid.GetGridStepInMeters()); shapePosition.Z = MyEditorGrid.GetGridStepInMeters() * (float)Math.Round(shapePosition.Z / MyEditorGrid.GetGridStepInMeters()); world = Matrix.CreateWorld(shapePosition, shapeForward, shapeUp); VoxelHandShape.MoveAndRotate(shapePosition, world); return(world); } } else if (IsProjectedToWaypoints) { //Lets find projection on closest collision with waypoint edge from = MyCamera.Position - MyCamera.UpVector * 10.5f; m_conePosition = from + MyCamera.ForwardVector * 10.7f; Vector3 shapePosition = from + MyCamera.ForwardVector * m_distance; List <Tuple <MyWayPoint, MyWayPoint> > edges = MyWayPointGraph.GetAllEdgesInSphere(shapePosition, VoxelHandShape.LocalVolume.Radius * 2); Tuple <MyWayPoint, MyWayPoint> closestEdge = null; float minDistance = float.MaxValue; Vector3 closestPoint = shapePosition; float distFromFirstWaypoint = 0; foreach (Tuple <MyWayPoint, MyWayPoint> edge in edges) { Vector3 linePos1 = edge.Item1.Position; Vector3 linePos2 = edge.Item2.Position; Vector3 point = MyUtils.GetClosestPointOnLine(ref linePos1, ref linePos2, ref shapePosition, out distFromFirstWaypoint); float distance = Vector3.Distance(shapePosition, point); if (distance < minDistance) { minDistance = distance; closestEdge = edge; closestPoint = point; } } if (closestEdge != null) { shapePosition = closestPoint; Vector3 shapeForward = Vector3.Normalize(shapePosition - from); Vector3 shapeUp = MyCamera.UpVector; world = Matrix.CreateWorld(shapePosition, shapeForward, shapeUp); VoxelHandShape.MoveAndRotate(shapePosition, world); MyWayPoint firstWaypoint = null; MyWayPoint secondWaypoint = null; float edgeDistance = Vector3.Distance(closestEdge.Item1.Position, closestEdge.Item2.Position); if (closestEdge.Item1.Position.Y < closestEdge.Item2.Position.Y) { firstWaypoint = closestEdge.Item1; secondWaypoint = closestEdge.Item2; } else { firstWaypoint = closestEdge.Item2; secondWaypoint = closestEdge.Item1; distFromFirstWaypoint = edgeDistance - distFromFirstWaypoint; } float edgeRatio = edgeDistance > 0 ? distFromFirstWaypoint / edgeDistance : 0; Quaternion quaternion1 = Quaternion.CreateFromRotationMatrix(closestEdge.Item1.WorldMatrix); Quaternion quaternion2 = Quaternion.CreateFromRotationMatrix(closestEdge.Item2.WorldMatrix); Quaternion resultQuaternion = Quaternion.Lerp(quaternion1, quaternion2, edgeRatio); Matrix resultMatrix = Matrix.CreateFromQuaternion(resultQuaternion); resultMatrix.Translation = shapePosition; VoxelHandShape.MoveAndRotate(shapePosition, resultMatrix); return(resultMatrix); } } if (MyFakes.MWBUILDER) { from = MyCamera.Position; m_conePosition = from + MyCamera.ForwardVector * 10.7f; /* * //Vector3 planeNormal = new Vector3(MyCamera.ForwardVector.X, 0, MyCamera.ForwardVector.Z); * Vector3 planeNormal = new Vector3(0, MyCamera.ForwardVector.Y, MyCamera.ForwardVector.Z); * * * planeNormal.Normalize(); * Vector3 planePoint = planePoint = from + planeNormal * m_distance; * planeNormal = -planeNormal; * * Plane plane = new Plane(planeNormal, -Vector3.Dot(planeNormal, planePoint)); * * Ray r = new Ray(from, MyCamera.ForwardVector); * float? intr = r.Intersects(plane); */ // if (intr.HasValue) { Vector3 shapePosition = from + MyCamera.ForwardVector * m_distance; Vector3 shapeForward = Vector3.Forward; Vector3 shapeUp = Vector3.Up; shapePosition.X = MyEditorGrid.GetGridStepInMeters() * (float)Math.Round(shapePosition.X / MyEditorGrid.GetGridStepInMeters()); shapePosition.Y = MyEditorGrid.GetGridStepInMeters() * (float)Math.Round(shapePosition.Y / MyEditorGrid.GetGridStepInMeters()); shapePosition.Z = MyEditorGrid.GetGridStepInMeters() * (float)Math.Round(shapePosition.Z / MyEditorGrid.GetGridStepInMeters()); world = Matrix.CreateWorld(shapePosition, shapeForward, shapeUp); VoxelHandShape.MoveAndRotate(shapePosition, world); } } else { from = MyCamera.Position - MyCamera.UpVector * 10.5f; m_conePosition = from + MyCamera.ForwardVector * 10.7f; Vector3 shapePosition = from + MyCamera.ForwardVector * m_distance; Vector3 shapeForward = Vector3.Normalize(/*MyCamera.UpVector * minDist * 0.5f*/ shapePosition - from); Vector3 shapeUp = /*shapeForward - MyCamera.ForwardVector +*/ MyCamera.UpVector; world = Matrix.CreateWorld(shapePosition, shapeForward, shapeUp); VoxelHandShape.MoveAndRotate(shapePosition, world); } return(world); }
/// <summary> /// Create and connect waypoints to enable navigation outside prefab containers. /// Needs to be called from the main thread (because it uses MyEntities.GetSafeIterationHelperForAll). /// </summary> public static void RecreateWaypointsAroundMadelyn() { MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("RecreateWaypointsAroundMadelyn"); // counters for debugging int largeObjects = 0; int totalWaypointsOutside = 0; int bigSubdivisions = 0; int closed = 0; var envelopes = new List<MyWayPoint[, ,]>(); var envelopeEntity = new List<MyEntity>(); var envelopeBvh = new MyDynamicAABBTree(MyConstants.GAME_PRUNING_STRUCTURE_AABB_EXTENSION); var nonFree = new HashSet<MyWayPoint>(); MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("create envelope"); int madelynsBox = -1; // create envelopes { var entity = MyEntities.GetEntityByName("Madelyn"); entity.UpdateAABBHr(); BoundingBox box = entity.WorldAABBHr; if (box.Max - box.Min == Vector3.Zero) box = entity.WorldAABB; // enlarge by 1% and 15 meters on each side BoundingBox extrudedBox = new BoundingBox(box.Min - (box.Max - box.Min) * 0.01f - new Vector3(15, 15, 15), box.Max + (box.Max - box.Min) * 0.01f + new Vector3(15, 15, 15)); var waypointsOutside = new HashSet<MyWayPoint>(); MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("find crossing"); // add all edges that cross the non-extruded box from inside to outside (remember out-vertices) foreach (var waypoint in MyGamePruningStructure.GetAllEntitiesInBox(ref extrudedBox, MyGamePruningStructure.QueryFlags.Waypoints)) { var v = waypoint as MyWayPoint; if (!v.Save) continue; nonFree.Add(v); using (MyWayPoint.NeighborsLock.AcquireSharedUsing()) { foreach (var n in v.Neighbors) if (n.Save && extrudedBox.Contains(n.Position) != ContainmentType.Contains) { if (waypointsOutside.Add(n)) totalWaypointsOutside++; nonFree.Add(n); } } } MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock(); // create envelope int s = 1; if (waypointsOutside.Count > 0 || entity as MyStaticAsteroid == null) // voxel maps and prefabs are automatically subdivided more { s = 2; bigSubdivisions++; } MyWayPoint[, ,] envelope = new MyWayPoint[s + 1, s + 1, s + 1]; for (int i = 0; i <= s; i++) for (int j = 0; j <= s; j++) for (int k = 0; k <= s; k++) { if (s == 2 && i == 1 && j == 1 && k == 1) continue; envelope[i, j, k] = CreateWaypoint(new Vector3( extrudedBox.Min.X + i * (extrudedBox.Max.X - extrudedBox.Min.X) / s, extrudedBox.Min.Y + j * (extrudedBox.Max.Y - extrudedBox.Min.Y) / s, extrudedBox.Min.Z + k * (extrudedBox.Max.Z - extrudedBox.Min.Z) / s ), null); envelope[i, j, k].Save = false; // don't save generated waypoints nonFree.Add(envelope[i, j, k]); // assume Madelyn's envelope has no blockers if (i != 0) if (!(s == 2 && i - 1 == 1 && j == 1 && k == 1)) MyWayPoint.Connect(envelope[i, j, k], envelope[i - 1, j, k]); if (j != 0) if (!(s == 2 && j - 1 == 1 && i == 1 && k == 1)) MyWayPoint.Connect(envelope[i, j, k], envelope[i, j - 1, k]); if (k != 0) if (!(s == 2 && k - 1 == 1 && j == 1 && i == 1)) MyWayPoint.Connect(envelope[i, j, k], envelope[i, j, k - 1]); // if it's a part of a face that faces an out-vertex, connect it foreach (var v in waypointsOutside) if ((i == 0 && v.Position.X <= envelope[i, j, k].Position.X) || (i == s && v.Position.X >= envelope[i, j, k].Position.X) || (j == 0 && v.Position.Y <= envelope[i, j, k].Position.Y) || (j == s && v.Position.Y >= envelope[i, j, k].Position.Y) || (k == 0 && v.Position.Z <= envelope[i, j, k].Position.Z) || (k == s && v.Position.Z >= envelope[i, j, k].Position.Z) ) { MyWayPoint.Connect(v, envelope[i, j, k]); } } envelopes.Add(envelope); envelopeEntity.Add(entity); envelopeBvh.AddProxy(ref extrudedBox, envelopes.Count - 1, 0); largeObjects++; madelynsBox = envelopes.Count - 1; } MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock(); MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("disconnect from old waypoints"); var madelynWaypoints = new HashSet<MyWayPoint>(); { var entity = MyEntities.GetEntityByName("Madelyn"); foreach (var child in entity.Children) { var w = child as MyWayPoint; if (w == null) continue; madelynWaypoints.Add(w); } foreach (var v in madelynWaypoints) { var outsiders = new List<MyWayPoint>(); foreach (var n in v.Neighbors) if (!madelynWaypoints.Contains(n)) outsiders.Add(n); foreach (var n in outsiders) MyWayPoint.Disconnect(v, n); } } MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock(); MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("connect to new surrounding waypoints"); for (int eIndex = 0; eIndex < envelopes.Count; eIndex++) { var e = envelopes[eIndex]; int es = e.GetLength(0) - 1; var eCenter = 0.5f * (e[0, 0, 0].Position + e[es, es, es].Position); // connect Madelyn's inner waypoints to waypoints around her if (eIndex == madelynsBox) { MyEntity madelyn = envelopeEntity[madelynsBox]; madelyn.UpdateAABBHr(); BoundingBox extrudedAABB = madelyn.WorldAABBHr; extrudedAABB.Min -= new Vector3(500); extrudedAABB.Max += new Vector3(500); List<MyWayPoint> nearMadelynWaypoints = new List<MyWayPoint>(); foreach (var waypoint in MyGamePruningStructure.GetAllEntitiesInBox(ref extrudedAABB, MyGamePruningStructure.QueryFlags.Waypoints)) { MyWayPoint v = waypoint as MyWayPoint; if (v != null) { if (!v.Save) continue; if (madelyn.Children.Contains(v)) continue; nearMadelynWaypoints.Add(v); } } int connected = 0, raycasts = 0; foreach (MyWayPoint v in nearMadelynWaypoints) { foreach (var child in madelyn.Children) { var w = child as MyWayPoint; if (w == null) continue; if (v.Neighbors.Contains(w)) continue; raycasts++; if (MyWayPoint.ConnectIfVisible(w, v)) // make a raycast { connected++; break; } } } } } // delete generated waypoints without edges foreach (var v in GetVertexListForModification()) { if (!v.Save && v.Neighbors.Count == 0) { v.MarkForClose(); closed++; } } InvalidateStoredPathCache(); MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock(); MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock(); }
public void DoWork() { try { MyEntities.EntityCloseLock.AcquireShared(); if (m_goalEntity == null) { return; } // try the direct path { var directLine = new MyLine(m_startPos, m_goalPos, true); if (MyEntities.GetAnyIntersectionWithLine(ref directLine, m_goalEntity, null, true, true, true, false) == null) { Path.Add(m_startPos); Path.Add(m_goalPos); Message = new StringBuilder().AppendFormat(MyTextsWrapper.Get(MyTextsWrapperEnum.GPSDistance).ToString(), Vector3.Distance(m_startPos, m_goalPos)); return; } } // get the closest waypoint to the goal (ignore visibility) MyWayPoint goal = MyWayPointGraph.GetClosestNonGeneratedWaypoint(m_goalPos); if (goal == null) { return; } // remember which waypoints were visible/invisible from startPos // remember blocked/unblocked edges var visibleFromStartPosCache = new Dictionary <MyWayPoint, bool>(); //var blockedEdges = new HashSet<Tuple<MyWayPoint, MyWayPoint>>(); HashSet <Tuple <MyWayPoint, MyWayPoint> > blockedEdges = null; using (MyWayPoint.BlockedEdgesLock.AcquireSharedUsing()) { blockedEdges = new HashSet <Tuple <MyWayPoint, MyWayPoint> >(MyWayPoint.BlockedEdgesForPlayer); } var unblockedEdges = new HashSet <Tuple <MyWayPoint, MyWayPoint> >(); // get 7 closest visible waypoints to startPos and compute shortest paths from them // first try 7 closest var closestVisibleWaypoints = MyWayPointGraph.GetClosestVisibleWaypoints(m_startPos, m_goalEntity, 7, 7, visibleFromStartPosCache); if (closestVisibleWaypoints.Count == 0 || !FindPathBetweenWaypoints(closestVisibleWaypoints, goal, visibleFromStartPosCache, blockedEdges, unblockedEdges)) { // failure: try 50 closest closestVisibleWaypoints = MyWayPointGraph.GetClosestVisibleWaypoints(m_startPos, m_goalEntity, 12, 50, visibleFromStartPosCache); if (closestVisibleWaypoints.Count == 0 || !FindPathBetweenWaypoints(closestVisibleWaypoints, goal, visibleFromStartPosCache, blockedEdges, unblockedEdges)) { return; // no use } } } finally { if (m_goalEntity != null) { m_goalEntity.OnClose -= goalEntity_OnClose; } MyEntities.EntityCloseLock.ReleaseShared(); } }
private bool FindPathBetweenWaypoints( List <MyWayPoint> closestVisibleWaypoints, MyWayPoint goal, Dictionary <MyWayPoint, bool> visibleFromStartPosCache, HashSet <Tuple <MyWayPoint, MyWayPoint> > blockedEdges, HashSet <Tuple <MyWayPoint, MyWayPoint> > unblockedEdges ) { // find the best path candidate float shortestPathLength = float.MaxValue; int retryCount = 0; for (int j = 0; j < closestVisibleWaypoints.Count && retryCount < 30; j++) // max retry count is 30 { var path = closestVisibleWaypoints[j].GetShortestPathTo(goal, blockedEdges, true, false); if (path.Count == 0) { continue; } // optimize the path: try to delete waypoints from the start of the path (if the next waypoints are visible) int farthestVisibleWaypointInPath = 0; for (int i = Math.Min(path.Count - 1, 20); i > 0; i--) // path optimization: max 20 raycasts per path candidate, reuse previous results { if (path[i].IsVisibleFrom(m_startPos, m_goalEntity, visibleFromStartPosCache)) { farthestVisibleWaypointInPath = i; break; } } // compute the length of the path candidate float length = Vector3.Distance(m_startPos, path[farthestVisibleWaypointInPath].WorldMatrix.Translation) + Vector3.Distance(path[path.Count - 1].WorldMatrix.Translation, m_goalPos); for (int i = farthestVisibleWaypointInPath; i < path.Count - 1; i++) { length += Vector3.Distance(path[i].WorldMatrix.Translation, path[i + 1].WorldMatrix.Translation); } // if it's the shortest path candidate yet, make it the new GPS path if (length < shortestPathLength) { // but first check that the edges are free of any obstructions (and remember it) bool pathBlocked = false; for (int i = farthestVisibleWaypointInPath; i < path.Count - 2; i++) { // assume that edges between non-generated waypoints are always ok if (path[i].Save && path[i + 1].Save) { continue; } var tuple = Tuple.Create(path[i], path[i + 1]); if (unblockedEdges.Contains(tuple)) // already tested and it was ok { } else if (path[i + 1].IsVisibleFrom(path[i].Position, m_goalEntity)) { unblockedEdges.Add(tuple); unblockedEdges.Add(Tuple.Create(path[i + 1], path[i])); } else { blockedEdges.Add(tuple); blockedEdges.Add(Tuple.Create(path[i + 1], path[i])); pathBlocked = true; break; } } if (pathBlocked) { j--; // retry current waypoint with updated cache of blocked edges retryCount++; continue; } // seems legit shortestPathLength = length; Path = new List <Vector3>(); Path.Add(m_startPos); for (int i = farthestVisibleWaypointInPath; i < path.Count; i++) { Path.Add(path[i].WorldMatrix.Translation); } Path.Add(m_goalPos); } } if (Path.Count != 0) { Message = new StringBuilder().AppendFormat(MyTextsWrapper.Get(MyTextsWrapperEnum.GPSDistance).ToString(), shortestPathLength); return(true); } else { return(false); } }
/// <summary> /// Create and connect waypoints to enable navigation outside prefab containers. /// Needs to be called from the main thread (because it uses MyEntities.GetSafeIterationHelperForAll). /// </summary> public static void CreateWaypointsAroundLargeStaticObjects() { if (m_GPSWaypointsInited) return; MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("CreateWaypointsAroundLargeStaticObjects"); // memory benchmark //GC.Collect(2); //MyMwcLog.WriteLine("#CreateWaypointsAroundLargeStaticObjects# before: working set " + MyValueFormatter.GetFormatedLong(Environment.WorkingSet) + ", gc " + MyValueFormatter.GetFormatedLong(GC.GetTotalMemory(false))); // counters for debugging int largeObjects = 0; int totalWaypointsOutside = 0; int bigSubdivisions = 0; int freeEnvelopes = 0; int edgesWithoutRaycasts = 0; int edgesWithRaycastsAdded = 0; int edgesWithRaycastsNotAdded = 0; int closed = 0; var envelopes = new List<MyWayPoint[, ,]>(); var envelopeEntity = new List<MyEntity>(); var envelopeBvh = new MyDynamicAABBTree(MyConstants.GAME_PRUNING_STRUCTURE_AABB_EXTENSION); var nonFree = new HashSet<MyWayPoint>(); MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("create envelopes"); var interestingBoxInflation = new Vector3(MyWaypointConstants.MAXIMUM_BOX_DISTANCE_TO_INTERESTING_STUFF_TO_GENERATE_WAYPOINTS); var interestingBoxes = new List<BoundingBox>(); // find interesting stuff (only prefab containers for now) foreach (var entity in MyEntities.GetEntities()) { if (!(entity is MyPrefabContainer)) continue; entity.UpdateAABBHr(); BoundingBox box = entity.WorldAABBHr; if (box.Max - box.Min == Vector3.Zero) { box = entity.WorldAABB; if (box.Max - box.Min == Vector3.Zero) continue; // no bounding box } BoundingBox extrudedBox = new BoundingBox(box.Min - interestingBoxInflation, box.Max + interestingBoxInflation); interestingBoxes.Add(extrudedBox); } int ss = 0; int madelynsBox = -1; // create envelopes foreach (var entity in MyEntities.GetSafeIterationHelperForAll()) { if (!(entity is MyVoxelMap || entity is MyPrefabContainer || entity is MyStaticAsteroid)) continue; entity.UpdateAABBHr(); BoundingBox box = entity.WorldAABBHr; if (box.Max - box.Min == Vector3.Zero) box = entity.WorldAABB; if (entity is MyStaticAsteroid && (box.Max - box.Min).LengthSquared() < MyWaypointConstants.MINIMUM_ASTEROID_DIAGONAL_LENGTH_TO_GENERATE_WAYPOINTS * MyWaypointConstants.MINIMUM_ASTEROID_DIAGONAL_LENGTH_TO_GENERATE_WAYPOINTS) { continue; // small static asteroids: ignore } if (entity is MyStaticAsteroid) { ss++; bool inInteresting = false; foreach (var iBox in interestingBoxes) { if (iBox.Contains(box) != ContainmentType.Disjoint) { inInteresting = true; break; } } if (!inInteresting) continue; // static asteroids far from interesting stuff: ignore } // enlarge by 1% and 15 meters on each side BoundingBox extrudedBox = new BoundingBox(box.Min - (box.Max - box.Min) * 0.01f - new Vector3(15, 15, 15), box.Max + (box.Max - box.Min) * 0.01f + new Vector3(15, 15, 15)); //diagonals.Add((float)Math.Sqrt((box.Max - box.Min).LengthSquared())); var waypointsOutside = new HashSet<MyWayPoint>(); MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("find crossing"); // add all edges that cross the non-extruded box from inside to outside (remember out-vertices) foreach (var waypoint in MyGamePruningStructure.GetAllEntitiesInBox(ref extrudedBox, MyGamePruningStructure.QueryFlags.Waypoints)) { var v = waypoint as MyWayPoint; if (!v.Save) continue; nonFree.Add(v); using (MyWayPoint.NeighborsLock.AcquireSharedUsing()) { foreach (var n in v.Neighbors) if (n.Save && extrudedBox.Contains(n.Position) != ContainmentType.Contains) { if (waypointsOutside.Add(n)) totalWaypointsOutside++; nonFree.Add(n); } } } MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock(); // create envelope int s = 1; if (waypointsOutside.Count > 0 || entity as MyStaticAsteroid == null) // voxel maps and prefabs are automatically subdivided more { s = 2; bigSubdivisions++; } MyWayPoint[, ,] envelope = new MyWayPoint[s + 1, s + 1, s + 1]; for (int i = 0; i <= s; i++) for (int j = 0; j <= s; j++) for (int k = 0; k <= s; k++) { if (s == 2 && i == 1 && j == 1 && k == 1) continue; envelope[i, j, k] = CreateWaypoint(new Vector3( extrudedBox.Min.X + i * (extrudedBox.Max.X - extrudedBox.Min.X) / s, extrudedBox.Min.Y + j * (extrudedBox.Max.Y - extrudedBox.Min.Y) / s, extrudedBox.Min.Z + k * (extrudedBox.Max.Z - extrudedBox.Min.Z) / s ), null); envelope[i, j, k].Save = false; // don't save generated waypoints nonFree.Add(envelope[i, j, k]); // connect with neighbors // use ConnectIfNoAABBBlockers only for non-static asteroids // don't connect to the non-existing middle vertex if (entity is MyStaticAsteroid) { if (i != 0) if (!(s == 2 && i - 1 == 1 && j == 1 && k == 1)) { MyWayPoint.Connect(envelope[i, j, k], envelope[i - 1, j, k]); edgesWithoutRaycasts++; } if (j != 0) if (!(s == 2 && j - 1 == 1 && i == 1 && k == 1)) { MyWayPoint.Connect(envelope[i, j, k], envelope[i, j - 1, k]); edgesWithoutRaycasts++; } if (k != 0) if (!(s == 2 && k - 1 == 1 && j == 1 && i == 1)) { MyWayPoint.Connect(envelope[i, j, k], envelope[i, j, k - 1]); edgesWithoutRaycasts++; } } else { if (i != 0) if (!(s == 2 && i - 1 == 1 && j == 1 && k == 1)) { if (MyWayPoint.ConnectIfNoAABBBlockers(envelope[i, j, k], envelope[i - 1, j, k])) edgesWithRaycastsAdded++; else edgesWithRaycastsNotAdded++; } if (j != 0) if (!(s == 2 && j - 1 == 1 && i == 1 && k == 1)) { if (MyWayPoint.ConnectIfNoAABBBlockers(envelope[i, j, k], envelope[i, j - 1, k])) edgesWithRaycastsAdded++; else edgesWithRaycastsNotAdded++; } if (k != 0) if (!(s == 2 && k - 1 == 1 && j == 1 && i == 1)) { if (MyWayPoint.ConnectIfNoAABBBlockers(envelope[i, j, k], envelope[i, j, k - 1])) edgesWithRaycastsAdded++; else edgesWithRaycastsNotAdded++; } } // if it's a part of a face that faces an out-vertex, connect it foreach (var v in waypointsOutside) if ((i == 0 && v.Position.X <= envelope[i, j, k].Position.X) || (i == s && v.Position.X >= envelope[i, j, k].Position.X) || (j == 0 && v.Position.Y <= envelope[i, j, k].Position.Y) || (j == s && v.Position.Y >= envelope[i, j, k].Position.Y) || (k == 0 && v.Position.Z <= envelope[i, j, k].Position.Z) || (k == s && v.Position.Z >= envelope[i, j, k].Position.Z) ) { if (MyWayPoint.ConnectIfNoAABBBlockers(v, envelope[i, j, k])) edgesWithRaycastsAdded++; else edgesWithRaycastsNotAdded++; } } envelopes.Add(envelope); envelopeEntity.Add(entity); envelopeBvh.AddProxy(ref extrudedBox, envelopes.Count - 1, 0); largeObjects++; if (entity.Name == "Madelyn") { madelynsBox = envelopes.Count - 1; } } MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock(); var componentsDone = new HashSet<int>(); MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("connect free wps"); // free waypoint: check whether it's connected to an envelope foreach (var v in GetVertexListForModification()) if (!nonFree.Contains(v)) { int id = GetConnectedComponentId(v); if (componentsDone.Contains(id)) continue; componentsDone.Add(id); var connectedWaypoints = GetWaypointsWithConnectedComponentId(id); // is this component already connected to an envelope? var box = new BoundingBox(v.Position, v.Position); foreach (var w in connectedWaypoints) if (w.Save) { var pos = w.Position; box = box.Include(ref pos); if (nonFree.Contains(w)) goto alreadyConnected; } BoundingBox extrudedBox = new BoundingBox(box.Min - (box.Max - box.Min) * 0.01f - new Vector3(5, 5, 5), box.Max + (box.Max - box.Min) * 0.01f + new Vector3(5, 5, 5)); // no - create a new one int s = 1; MyWayPoint[, ,] envelope = new MyWayPoint[s + 1, s + 1, s + 1]; for (int i = 0; i <= s; i++) for (int j = 0; j <= s; j++) for (int k = 0; k <= s; k++) { envelope[i, j, k] = CreateWaypoint(new Vector3( extrudedBox.Min.X + i * (extrudedBox.Max.X - extrudedBox.Min.X) / s, extrudedBox.Min.Y + j * (extrudedBox.Max.Y - extrudedBox.Min.Y) / s, extrudedBox.Min.Z + k * (extrudedBox.Max.Z - extrudedBox.Min.Z) / s ), null); envelope[i, j, k].Save = false; // don't save generated waypoints nonFree.Add(envelope[i, j, k]); // connect with neighbors // should use ConnectIfVisible, but it's slow and we can resolve it while computing the GPS if (i != 0) { if (MyWayPoint.ConnectIfNoAABBBlockers(envelope[i, j, k], envelope[i - 1, j, k])) edgesWithRaycastsAdded++; else edgesWithRaycastsNotAdded++; } if (j != 0) { if (MyWayPoint.ConnectIfNoAABBBlockers(envelope[i, j, k], envelope[i, j - 1, k])) edgesWithRaycastsAdded++; else edgesWithRaycastsNotAdded++; } if (k != 0) { if (MyWayPoint.ConnectIfNoAABBBlockers(envelope[i, j, k], envelope[i, j, k - 1])) edgesWithRaycastsAdded++; else edgesWithRaycastsNotAdded++; } } // connect all waypoints to the closest corner of the new envelope foreach (var w in connectedWaypoints) { var pos = w.Position; if (MyWayPoint.ConnectIfNoAABBBlockers(w, envelope[pos.X < extrudedBox.GetCenter().X ? 0 : 1, pos.Y < extrudedBox.GetCenter().Y ? 0 : 1, pos.Z < extrudedBox.GetCenter().Z ? 0 : 1])) edgesWithRaycastsAdded++; else edgesWithRaycastsNotAdded++; } envelopes.Add(envelope); envelopeEntity.Add(null); envelopeBvh.AddProxy(ref extrudedBox, envelopes.Count - 1, 0); freeEnvelopes++; alreadyConnected: { } } MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock(); MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("connect envelopes"); // connect envelopes together for (int eIndex = 0; eIndex < envelopes.Count; eIndex++) { var e = envelopes[eIndex]; int es = e.GetLength(0) - 1; var eCenter = 0.5f * (e[0, 0, 0].Position + e[es, es, es].Position); // get K closest indices var closestEnvelopeIndices = new List<int>(); for (int i = 200; i <= 6400; i *= 2) // try 200, 400, 800, 1600, 3200, 6400 m { var halfExtent = new Vector3(i); var bbox = new BoundingBox(eCenter - halfExtent, eCenter + halfExtent); envelopeBvh.OverlapAllBoundingBox(ref bbox, closestEnvelopeIndices); if (closestEnvelopeIndices.Count >= 16) break; } // connect them together int k = 0; foreach (var qIndex in closestEnvelopeIndices) { if (++k == 16) break; // take only 16 envelopes if (qIndex == eIndex) continue; var q = envelopes[qIndex]; int qs = q.GetLength(0) - 1; var qCenter = 0.5f * (q[0, 0, 0].Position + q[qs, qs, qs].Position); // connect the closest opposite vertices int qx, qy, qz, ex, ey, ez; if (qCenter.X < eCenter.X) { qx = qs; ex = 0; } else { qx = 0; ex = es; } if (qCenter.Y < eCenter.Y) { qy = qs; ey = 0; } else { qy = 0; ey = es; } if (qCenter.Z < eCenter.Z) { qz = qs; ez = 0; } else { qz = 0; ez = es; } if (es > 1 || qs > 1) { if (MyWayPoint.ConnectIfNoAABBBlockers(e[ex, ey, ez], q[qx, qy, qz], envelopeEntity[eIndex], envelopeEntity[qIndex])) edgesWithRaycastsAdded++; else edgesWithRaycastsNotAdded++; } else { // don't make a raycast if one of the envelopes isn't important MyWayPoint.Connect(e[ex, ey, ez], q[qx, qy, qz]); edgesWithoutRaycasts++; } // connect Madelyn's waypoint to envelopes if (eIndex == madelynsBox) { MyEntity madelyn = envelopeEntity[madelynsBox]; foreach (var child in madelyn.Children) { var w = child as MyWayPoint; if (w == null) continue; MyWayPoint.ConnectIfNoAABBBlockers(w, q[qx, qy, qz]); // make a raycast } } } // connect Madelyn's waypoint to envelopes if (eIndex == madelynsBox) { MyEntity madelyn = envelopeEntity[madelynsBox]; madelyn.UpdateAABBHr(); BoundingBox extrudedAABB = madelyn.WorldAABBHr; extrudedAABB.Min -= new Vector3(500); extrudedAABB.Max += new Vector3(500); List<MyWayPoint> nearMadelynWaypoints = new List<MyWayPoint>(); foreach (var waypoint in MyGamePruningStructure.GetAllEntitiesInBox(ref extrudedAABB, MyGamePruningStructure.QueryFlags.Waypoints)) { MyWayPoint v = waypoint as MyWayPoint; if (v != null) { if (!v.Save) continue; nearMadelynWaypoints.Add(v); } } foreach (var child in madelyn.Children) { var w = child as MyWayPoint; if (w == null) continue; foreach (MyWayPoint v in nearMadelynWaypoints) { MyWayPoint.ConnectIfVisible(w, v); // make a raycast } } } } // delete generated waypoints without edges foreach (var v in GetVertexListForModification()) { if (!v.Save && v.Neighbors.Count == 0) { v.MarkForClose(); closed++; } } m_GPSWaypointsInited = true; InvalidateStoredPathCache(); // memory benchmark //GC.Collect(2); //MyMwcLog.WriteLine("#CreateWaypointsAroundLargeStaticObjects# after: working set " + MyValueFormatter.GetFormatedLong(Environment.WorkingSet) + ", gc " + MyValueFormatter.GetFormatedLong(GC.GetTotalMemory(false))); MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock(); MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock(); }
// Return the component id, or -1 if the given waypoint doesn't lie in the waypoint graph. /// <summary>This function needs to be called with EntityCloseLock already acquired, or not an issue.</summary> public static int GetConnectedComponentId(MyWayPoint wp) { RecomputeConnectedComponents(); int value; if (m_componentIds.TryGetValue(wp, out value)) return value; return -1; }
// Return the complete shortest path between vertices in a list. public List<MyWayPoint> CompletePath(HashSet<Tuple<MyWayPoint, MyWayPoint>> blockedEdges, MyWayPoint currentWayPoint, bool useGeneratedWaypoints = true, bool cycle = false, bool cachedIsOk = true) { if (!cachedIsOk || m_cachedCompletePath == null) { using (MyEntities.EntityCloseLock.AcquireSharedUsing()) { m_cachedCompletePath = new List<MyWayPoint>(); if (WayPoints == null || WayPoints.Count == 0) return m_cachedCompletePath; int currentIndex = currentWayPoint != null ? WayPoints.IndexOf(currentWayPoint) : 0; m_cachedCompletePath.Add(WayPoints[0]); //for (int i = 0; i < WayPoints.Count - 1; i++) //{ // var subpath = WayPoints[i].GetShortestPathTo(WayPoints[i + 1], blockedEdges, useGeneratedWaypoints); // if (subpath.Count == 0) // m_cachedCompletePath.Add(WayPoints[i + 1]); // no path: add straight line to next waypoint // else // m_cachedCompletePath.AddRange(subpath.GetRange(1, subpath.Count - 1)); // the first point was the last point of the previous segment //} //if (cycle) //{ // var subpath = WayPoints[WayPoints.Count - 1].GetShortestPathTo(WayPoints[0], blockedEdges, useGeneratedWaypoints); // if (subpath.Count > 2) // m_cachedCompletePath.AddRange(subpath.GetRange(1, subpath.Count - 2)); // the first point was the last point of the previous segment, the last point was point 0 //} for (int i = 0; i < WayPoints.Count - 1; i++) { var subpath = WayPoints[i].GetShortestPathTo(WayPoints[i + 1], blockedEdges, useGeneratedWaypoints); if (subpath.Count == 0) { if (blockedEdges != null && blockedEdges.Contains(Tuple.Create(WayPoints[i], WayPoints[i + 1]))) { // this part of the path is unreachable if (currentIndex > i) { m_cachedCompletePath.Clear(); m_cachedCompletePath.Add(WayPoints[i + 1]); } else { break; } } else { m_cachedCompletePath.Add(WayPoints[i + 1]); // no path: add straight line to next waypoint } } else { m_cachedCompletePath.AddRange(subpath.GetRange(1, subpath.Count - 1)); // the first point was the last point of the previous segment } } if (cycle) { var subpath = WayPoints[WayPoints.Count - 1].GetShortestPathTo(WayPoints[0], blockedEdges, useGeneratedWaypoints); if (subpath.Count > 2) m_cachedCompletePath.AddRange(subpath.GetRange(1, subpath.Count - 2)); // the first point was the last point of the previous segment, the last point was point 0 } } } return m_cachedCompletePath; }
internal override void Update(MySmallShipBot bot) { base.Update(bot); if (ShouldFallAsleep(bot)) { bot.IsSleeping = true; return; } bool pathChange = m_lastWayPointPath != bot.WaypointPath; if (pathChange) { m_lastWayPointPath = bot.WaypointPath; m_currentWayPointIndex = 0; } bool cycle = bot.PatrolMode == MyPatrolMode.CYCLE; if (bot.WaypointPath != null && !bot.SuspendPatrol && bot.WaypointPath.WayPoints.Count > 0) { UpdateVisibility(bot, bot.CurrentWaypoint.GetPosition()); if (!m_targetVisible) { findSmallship.Update(bot, bot.CurrentWaypoint); if (findSmallship.PathNotFound) { bot.IsSleeping = true; m_isInvalid = true; } } else { bool blockedEdgesIdDirty = m_lastBlockedEdgesChangeId != MyWayPoint.BlockedEdgesChangeId; m_path.Clear(); using (MyWayPoint.BlockedEdgesLock.AcquireSharedUsing()) { m_path.AddRange(bot.WaypointPath.CompletePath(MyWayPoint.BlockedEdgesForBots, bot.CurrentWaypoint, false, cycle, !blockedEdgesIdDirty)); } m_lastBlockedEdgesChangeId = MyWayPoint.BlockedEdgesChangeId; if (blockedEdgesIdDirty) { if (bot.CurrentWaypoint == null) { m_currentWayPointIndex = 0; } else { m_currentWayPointIndex = m_path.IndexOf(bot.CurrentWaypoint); } } // no path found if (m_currentWayPointIndex == -1) { return; } bot.CurrentWaypoint = m_path[m_currentWayPointIndex]; if (Vector3.DistanceSquared(bot.GetPosition(), bot.CurrentWaypoint.GetPosition()) <= WAYPOINT_NEAR_DISTANCE_SQR) { if (bot.CurrentWaypoint.EntityId != null && m_lastWayPoint != bot.CurrentWaypoint) { m_lastWayPoint = bot.CurrentWaypoint; MyScriptWrapper.BotReachedWaypoint(bot, bot.CurrentWaypoint); } //++processedWaypoints; int count = m_path.Count; switch (bot.PatrolMode) { case MyPatrolMode.CYCLE: //bot.CurrentWaypointIndex = processedWaypoints % count; m_currentWayPointIndex++; if (m_currentWayPointIndex >= count) { m_currentWayPointIndex = 0; } break; case MyPatrolMode.PING_PONG: if (count > 1) { //bot.CurrentWaypointIndex = processedWaypoints % (count * 2 - 2); //if (bot.CurrentWaypointIndex >= count) //{ // bot.CurrentWaypointIndex = (count * 2 - 2) - bot.CurrentWaypointIndex; //} if (m_forward) { if (m_currentWayPointIndex < count - 1) { m_currentWayPointIndex++; } else { m_currentWayPointIndex--; m_forward = false; } } else { if (m_currentWayPointIndex > 0) { m_currentWayPointIndex--; } else { m_currentWayPointIndex++; m_forward = true; } } } else { m_currentWayPointIndex = 0; } break; case MyPatrolMode.ONE_WAY: if (m_currentWayPointIndex < m_path.Count - 1) { ++m_currentWayPointIndex; } break; } bot.CurrentWaypoint = m_path[m_currentWayPointIndex]; } bot.Move(bot.CurrentWaypoint.GetPosition(), bot.CurrentWaypoint.GetPosition(), GetUpPlane(), false); findSmallship.Init(bot); } } }