private static bool Path(CreatureMovement mover, Voxel start, GoalRegion goal, ChunkManager chunks, int maxExpansions, ref List <Creature.MoveAction> toReturn, bool reverse) { VoxelChunk startChunk = chunks.ChunkData.ChunkMap[start.ChunkID]; VoxelChunk endChunk = chunks.ChunkData.ChunkMap[goal.GetVoxel().ChunkID]; if (startChunk.IsCompletelySurrounded(start) || endChunk.IsCompletelySurrounded(goal.GetVoxel())) { toReturn = null; return(false); } HashSet <Voxel> closedSet = new HashSet <Voxel>(); HashSet <Voxel> openSet = new HashSet <Voxel> { start }; Dictionary <Voxel, Creature.MoveAction> cameFrom = new Dictionary <Voxel, Creature.MoveAction>(); Dictionary <Voxel, float> gScore = new Dictionary <Voxel, float>(); PriorityQueue <Voxel> fScore = new PriorityQueue <Voxel>(); gScore[start] = 0.0f; fScore.Enqueue(start, gScore[start] + Heuristic(start, goal.GetVoxel())); int numExpansions = 0; List <Voxel> manhattanNeighbors = new List <Voxel>(6); for (int i = 0; i < 6; i++) { manhattanNeighbors.Add(new Voxel()); } while (openSet.Count > 0 && numExpansions < maxExpansions) { Voxel current = GetVoxelWithMinimumFScore(fScore, openSet); if (current == null) { current = start; numExpansions++; } numExpansions++; if (goal.IsInGoalRegion(current)) { Creature.MoveAction first = new Creature.MoveAction() { Voxel = current, MoveType = Creature.MoveType.Walk }; toReturn = ReconstructPath(cameFrom, first); return(true); } openSet.Remove(current); closedSet.Add(current); VoxelChunk currentChunk = chunks.ChunkData.ChunkMap[current.ChunkID]; List <Creature.MoveAction> neighbors = null; neighbors = mover.GetMoveActions(current); currentChunk.GetNeighborsManhattan(current, manhattanNeighbors); if (manhattanNeighbors.Contains(goal.GetVoxel())) { Creature.MoveAction first = new Creature.MoveAction() { Voxel = current, MoveType = Creature.MoveType.Walk }; Creature.MoveAction last = new Creature.MoveAction() { Voxel = goal.GetVoxel(), MoveType = Creature.MoveType.Walk }; List <Creature.MoveAction> subPath = ReconstructPath(cameFrom, first); subPath.Add(last); toReturn = subPath; return(true); } foreach (Creature.MoveAction n in neighbors) { if (closedSet.Contains(n.Voxel)) { continue; } float tenativeGScore = gScore[current] + GetDistance(current, n.Voxel, n.MoveType, chunks); if (openSet.Contains(n.Voxel) && !(tenativeGScore < gScore[n.Voxel])) { continue; } openSet.Add(n.Voxel); Creature.MoveAction cameAction = n; cameAction.Voxel = current; cameFrom[n.Voxel] = cameAction; gScore[n.Voxel] = tenativeGScore; fScore.Enqueue(n.Voxel, gScore[n.Voxel] + Heuristic(n.Voxel, goal.GetVoxel())); } if (numExpansions >= maxExpansions) { return(false); } } toReturn = null; return(false); }
/// <summary> /// Creates a path from the start voxel to some goal region, returning a list of movements that can /// take a mover from the start voxel to the goal region. /// </summary> /// <param name="mover">The agent that we want to find a path for.</param> /// <param name="start">The voxel that the agent starts in.</param> /// <param name="goal">Goal conditions that the agent is trying to satisfy.</param> /// <param name="chunks">The voxels that the agent is moving through</param> /// <param name="maxExpansions">Maximum number of voxels to consider before giving up.</param> /// <param name="toReturn">The path of movements that the mover should take to reach the goal.</param> /// <param name="weight"> /// A heuristic weight to apply to the planner. If 1.0, the path is garunteed to be optimal. Higher values /// usually result in faster plans that are suboptimal. /// </param> /// <returns>True if a path could be found, or false otherwise.</returns> private static bool Path(CreatureMovement mover, Voxel start, GoalRegion goal, ChunkManager chunks, int maxExpansions, ref List <Creature.MoveAction> toReturn, float weight) { // Sometimes a goal may not even be achievable a.priori. If this is true, we know there can't be a path // which satisifies that goal. if (!goal.IsPossible()) { toReturn = null; return(false); } // Voxels that have already been explored. var closedSet = new HashSet <Voxel>(); // Voxels which should be explored. var openSet = new HashSet <Voxel> { start }; // Dictionary of voxels to the optimal action that got the mover to that voxel. var cameFrom = new Dictionary <Voxel, Creature.MoveAction>(); // Optimal score of a voxel based on the path it took to get there. var gScore = new Dictionary <Voxel, float>(); // Expansion priority of voxels. var fScore = new PriorityQueue <Voxel>(); // Starting conditions of the search. gScore[start] = 0.0f; fScore.Enqueue(start, gScore[start] + weight * goal.Heuristic(start)); // Keep count of the number of expansions we've taken to get to the goal. int numExpansions = 0; // Check the voxels adjacent to the current voxel as a quick test of adjacency to the goal. var manhattanNeighbors = new List <Voxel>(6); for (int i = 0; i < 6; i++) { manhattanNeighbors.Add(new Voxel()); } // Loop until we've either checked every possible voxel, or we've exceeded the maximum number of // expansions. while (openSet.Count > 0 && numExpansions < maxExpansions) { // Check the next voxel to explore. Voxel current = GetVoxelWithMinimumFScore(fScore); if (current == null) { // If there wasn't a voxel to explore, just try to expand from // the start again. current = start; numExpansions++; } numExpansions++; // If we've reached the goal already, reconstruct the path from the start to the // goal. if (goal.IsInGoalRegion(current)) { // Assume that the last action in the path involves walking to the goal. var first = new Creature.MoveAction { Voxel = current, MoveType = Creature.MoveType.Walk }; toReturn = ReconstructPath(cameFrom, first); return(true); } // We've already considered the voxel, so add it to the closed set. openSet.Remove(current); closedSet.Add(current); VoxelChunk currentChunk = chunks.ChunkData.ChunkMap[current.ChunkID]; List <Creature.MoveAction> neighbors = null; // Get the voxels that can be moved to from the current voxel. neighbors = mover.GetMoveActions(current); currentChunk.GetNeighborsManhattan(current, manhattanNeighbors); // A quick test to see if we're already adjacent to the goal. If we are, assume // that we can just walk to it. if (manhattanNeighbors.Contains(goal.GetVoxel())) { var first = new Creature.MoveAction { Voxel = current, MoveType = Creature.MoveType.Walk }; var last = new Creature.MoveAction { Voxel = goal.GetVoxel(), MoveType = Creature.MoveType.Walk }; List <Creature.MoveAction> subPath = ReconstructPath(cameFrom, first); subPath.Add(last); toReturn = subPath; return(true); } // Otherwise, consider all of the neighbors of the current voxel that can be moved to, // and determine how to add them to the list of expansions. foreach (Creature.MoveAction n in neighbors) { // If we've already explored that voxel, don't explore it again. if (closedSet.Contains(n.Voxel)) { continue; } // Otherwise, consider the case of moving to that neighbor. float tenativeGScore = gScore[current] + GetDistance(current, n.Voxel, n.MoveType, mover); // IF the neighbor can already be reached more efficiently, ignore it. if (openSet.Contains(n.Voxel) && !(tenativeGScore < gScore[n.Voxel])) { continue; } // Otherwise, add it to the list of voxels for consideration. openSet.Add(n.Voxel); // Add an edge to the voxel from the current voxel. Creature.MoveAction cameAction = n; cameAction.Voxel = current; cameFrom[n.Voxel] = cameAction; // Update the expansion scores for the next voxel. gScore[n.Voxel] = tenativeGScore; fScore.Enqueue(n.Voxel, gScore[n.Voxel] + weight * goal.Heuristic(n.Voxel)); } // If we've expanded too many voxels, just give up. if (numExpansions >= maxExpansions) { return(false); } } // Somehow we've reached this code without having found a path. Return false. toReturn = null; return(false); }
public void GenerateCaves(VoxelChunk chunk) { Vector3 origin = chunk.Origin; int chunkSizeX = chunk.SizeX; int chunkSizeY = chunk.SizeY; int chunkSizeZ = chunk.SizeZ; BiomeData biome = BiomeLibrary.Biomes[Overworld.Biome.Cave]; List <Voxel> neighbors = new List <Voxel>(); Voxel vUnder = chunk.MakeVoxel(0, 0, 0); for (int x = 0; x < chunkSizeX; x++) { for (int z = 0; z < chunkSizeZ; z++) { int h = chunk.GetFilledVoxelGridHeightAt(x, chunk.SizeY - 1, z); for (int i = 0; i < CaveLevels.Count; i++) { int y = CaveLevels[i]; if (y <= 0 || y >= h - 1) { continue; } Vector3 vec = new Vector3(x, y, z) + chunk.Origin; double caveNoise = CaveNoise.GetValue((x + origin.X) * CaveNoiseScale * CaveFrequencies[i], (y + origin.Y) * CaveNoiseScale * 3.0f, (z + origin.Z) * CaveNoiseScale * CaveFrequencies[i]); double heightnoise = NoiseGenerator.Noise((x + origin.X) * NoiseScale * CaveFrequencies[i], (y + origin.Y) * NoiseScale * 3.0f, (z + origin.Z) * NoiseScale * CaveFrequencies[i]); int caveHeight = Math.Min(Math.Max((int)(heightnoise * 5), 1), 3); if (caveNoise > CaveSize) { bool waterFound = false; for (int dy = 0; dy < caveHeight; dy++) { int index = chunk.Data.IndexAt(x, y - dy, z); chunk.GetNeighborsManhattan(x, y - dy, z, neighbors); if (neighbors.Any(v => v.WaterLevel > 0)) { waterFound = true; } if (waterFound) { break; } chunk.Data.Types[index] = 0; } if (!waterFound && caveNoise > CaveSize * 1.8f && y - caveHeight > 0) { int indexunder = chunk.Data.IndexAt(x, y - caveHeight, z); chunk.Data.Types[indexunder] = (byte)VoxelLibrary.GetVoxelType(biome.GrassVoxel).ID; chunk.Data.Health[indexunder] = (byte)VoxelLibrary.GetVoxelType(biome.GrassVoxel).StartingHealth; chunk.Data.IsExplored[indexunder] = false; foreach (VegetationData veg in biome.Vegetation) { if (!MathFunctions.RandEvent(veg.SpawnProbability)) { continue; } if (NoiseGenerator.Noise(vec.X / veg.ClumpSize, veg.NoiseOffset, vec.Y / veg.ClumpSize) < veg.ClumpThreshold) { continue; } vUnder.GridPosition = new Vector3(x, y - 1, z); if (!vUnder.IsEmpty && vUnder.TypeName == biome.GrassVoxel) { vUnder.Type = VoxelLibrary.GetVoxelType(biome.SoilVoxel); float offset = veg.VerticalOffset; if (vUnder.RampType != RampType.None) { offset -= 0.25f; } float treeSize = MathFunctions.Rand() * veg.SizeVariance + veg.MeanSize; GameComponent entity = EntityFactory.CreateEntity <GameComponent>(veg.Name, chunk.Origin + new Vector3(x, y, z) + new Vector3(0, treeSize * offset, 0), Blackboard.Create("Scale", treeSize)); entity.GetRootComponent().SetActiveRecursive(false); entity.GetRootComponent().SetVisibleRecursive(false); if (GameSettings.Default.FogofWar) { ExploredListener listener = new ExploredListener( PlayState.ComponentManager, entity, PlayState.ChunkManager, vUnder); } } } } foreach (FaunaData animal in biome.Fauna) { if (y <= 0 || !(PlayState.Random.NextDouble() < animal.SpawnProbability)) { continue; } var entity = EntityFactory.CreateEntity <GameComponent>(animal.Name, chunk.Origin + new Vector3(x, y, z) + Vector3.Up * 1.0f); entity.GetRootComponent().SetActiveRecursive(false); entity.GetRootComponent().SetVisibleRecursive(false); if (GameSettings.Default.FogofWar) { ExploredListener listener = new ExploredListener(PlayState.ComponentManager, entity, PlayState.ChunkManager, chunk.MakeVoxel(x, y, z)); } break; } } } } } }