Beispiel #1
0
    private float FindBlendParameters(Vector3 pos, out ClimateParameters src, out ClimateParameters dst)
    {
        if (climateLookup == null)
        {
            climateLookup = new ClimateParameters[4] {
                Arid, Temperate, Tundra, Arctic
            };
        }
        if (TerrainMeta.BiomeMap == null)
        {
            src = Temperate;
            dst = Temperate;
            return(0.5f);
        }
        int biomeMaxType  = TerrainMeta.BiomeMap.GetBiomeMaxType(pos);
        int biomeMaxType2 = TerrainMeta.BiomeMap.GetBiomeMaxType(pos, ~biomeMaxType);

        src = climateLookup[TerrainBiome.TypeToIndex(biomeMaxType)];
        dst = climateLookup[TerrainBiome.TypeToIndex(biomeMaxType2)];
        return(TerrainMeta.BiomeMap.GetBiome(pos, biomeMaxType2));
    }
Beispiel #2
0
    public void Step(
        ClimateParameters parameters
        )
    {
        HexAdjacencyGraph adjacencyGraph =
            _hexMap.AdjacencyGraph;

        // Initialize the next climate step.
        ClimateData[] nextClimates = new ClimateData[ClimateArray.Length];

        // Populate the next climate step.
        for (int i = 0; i < ClimateArray.Length; i++)
        {
            nextClimates[i] = new ClimateData();
        }

        // For each hex cell...
        for (int i = 0; i < _hexMap.SizeSquared; i++)
        {
            Hex            source        = _hexMap.GetHex(i);
            List <HexEdge> adjacentEdges =
                adjacencyGraph.GetOutEdgesList(source);

            // Get the next climate step for that cell.
            StepClimate(
                ClimateArray,
                ref nextClimates,
                source,
                adjacentEdges,
                parameters
                );

            source.clouds      = nextClimates[source.Index].clouds;
            source.moisture    = nextClimates[source.Index].moisture;
            source.temperature = nextClimates[source.Index].clouds;
        }

        ClimateArray = nextClimates;
    }
Beispiel #3
0
    public void RefreshTerrainTypes(
        ClimateParameters parameters,
        RiverDigraph riverDigraph
        )
    {
        int temperatureJitterChannel = Random.Range(0, 4);

        int rockDesertElevation =
            parameters.elevationMax -
            (parameters.elevationMax - parameters.waterLevel) / 2;

        Holdridge holdridge = new Holdridge();

        foreach (Hex hex in _hexMap.Hexes)
        {
            float temperature = ClimateArray[hex.Index].temperature;
            float moisture    = ClimateArray[hex.Index].moisture;
            if (!hex.IsUnderwater)
            {
                hex.HoldrigeZone = holdridge.GetHoldridgeZone(
                    temperature,
                    moisture
                    );

                continue;

                int temperatureBand = 0;

                for (
                    ;
                    temperatureBand < temperatureBands.Length;
                    temperatureBand++
                    )
                {
                    if (temperature < temperatureBands[temperatureBand])
                    {
                        break;
                    }
                }

                int moistureBand = 0;

                for (; moistureBand < moistureBands.Length; moistureBand++)
                {
                    if (moisture < moistureBands[moistureBand])
                    {
                        break;
                    }
                }

                Biome hexBiome = biomes[temperatureBand * 4 + moistureBand];

                if (hexBiome.terrain == Terrains.Desert)
                {
                    if (hex.elevation >= rockDesertElevation)
                    {
                        hexBiome.terrain = Terrains.Stone;
                    }
                }
                else if (hex.elevation == parameters.elevationMax)
                {
                    hexBiome.terrain = Terrains.Snow;
                }

                if (hexBiome.terrain == Terrains.Snow)
                {
                    hexBiome.plant = 0;
                }

                if (hexBiome.plant < 3 && riverDigraph.HasRiver(hex))
                {
                    hexBiome.plant += 1;
                }

                //hex.Biome = hexBiome;
                //hex.ClimateData = ClimateArray[hex.Index];
            }
            else
            {
                LZone lZone;

                if (hex.elevation == parameters.waterLevel - 1)
                {
                    int        cliffs = 0;
                    int        slopes = 0;
                    List <Hex> neighbors;

                    if (_hexMap.TryGetNeighbors(hex, out neighbors))
                    {
                        foreach (Hex neighbor in neighbors)
                        {
                            int delta =
                                neighbor.elevation - hex.WaterLevel;

                            if (delta == 0)
                            {
                                slopes += 1;
                            }
                            else if (delta > 0)
                            {
                                cliffs += 1;
                            }
                        }
                    }

                    // More than half neighbors at same level. Inlet or
                    // lake, therefore terrain is grass.
                    if (cliffs + slopes > 3)
                    {
                        lZone = LZone.Steppe;
                    }

                    // More than half cliffs, terrain is stone.
                    else if (cliffs > 0)
                    {
                        lZone = LZone.DesertScb;
                    }

                    // More than half slopes, terrain is beach.
                    else if (slopes > 0)
                    {
                        lZone = LZone.DesertH;
                    }

                    // Shallow non-coast, terrain is grass.
                    else
                    {
                        lZone = LZone.Steppe;
                    }
                }
                else if (hex.elevation >= parameters.waterLevel)
                {
                    lZone = LZone.Steppe;
                }
                else if (hex.elevation < 0)
                {
                    lZone = LZone.DesertH;
                }
                else
                {
                    lZone = LZone.WTundra;
                }

                // Coldest temperature band produces mud instead of grass.
                if (
                    lZone == LZone.Steppe &&
                    temperature < temperatureBands[0]
                    )
                {
                    lZone = LZone.WTundra;
                }

                //hex.Biome = new Biome(terrain, 0);
            }
        }
    }
Beispiel #4
0
    private void StepClimate(
        ClimateData[] currentClimates,
        ref ClimateData[] nextClimates,
        Hex source,
        List <HexEdge> adjacentEdges,
        ClimateParameters parameters
        )
    {
        ClimateData currentClimate = currentClimates[source.Index];

        // If the tile is water, add clouds equivalent to the evaporation
        // factor.
        if (source.IsUnderwater)
        {
            currentClimate.moisture = 1f;
            currentClimate.clouds  += parameters.evaporationFactor;
        }
        // If the tile is not water, scale evaporation with the moisture
        // of the tile and create clouds from that.
        else
        {
            float evaporation =
                currentClimate.moisture * parameters.evaporationFactor;

            currentClimate.moisture -= evaporation;
            currentClimate.clouds   += evaporation;
        }

        // Precipitation is scaled with the number of clouds.
        float precipitation =
            currentClimate.clouds * parameters.precipitationFactor;

        currentClimate.clouds   -= precipitation;
        currentClimate.moisture += precipitation;

        // As the elevation of the hex approaches the maximum elevation,
        // the maximum number of clouds decreases.
        float cloudMaximum =
            1f - source.ViewElevation / (parameters.elevationMax + 1f);

        // If the number of clouds exceeds the cloud maximum, convert
        // excess clouds to moisture.
        if (currentClimate.clouds > cloudMaximum)
        {
            currentClimate.moisture +=
                currentClimate.clouds - cloudMaximum;
            currentClimate.clouds = cloudMaximum;
        }

        // Get the main cloud dispersal direction.
        HexDirections mainCloudDispersalDirection =
            parameters.windDirection.Opposite();

        // Get the cloud dispersal magnitude.
        float cloudDispersalMagnitude =
            currentClimate.clouds * (1f / (5f + parameters.windStrength));

        // Calculate the amount of runoff.
        float runoff =
            currentClimate.moisture * parameters.runoffFactor * (1f / 6f);

        // Calculate the amount of seepage.
        float seepage =
            currentClimate.moisture * parameters.seepageFactor * (1f / 6f);

        // Disperse clouds, runoff, and seepage to adjacent climates.
        foreach (HexEdge edge in adjacentEdges)
        {
            ClimateData neighborClimate = nextClimates[edge.Target.Index];

            if (edge.Direction == mainCloudDispersalDirection)
            {
                neighborClimate.clouds +=
                    cloudDispersalMagnitude * parameters.windStrength;
            }
            else
            {
                neighborClimate.clouds += cloudDispersalMagnitude;
            }

            int elevationDelta =
                edge.Target.ViewElevation - edge.Source.ViewElevation;

            if (elevationDelta < 0)
            {
                currentClimate.moisture  -= runoff;
                neighborClimate.moisture += runoff;
            }
            else if (elevationDelta == 0)
            {
                currentClimate.moisture  -= seepage;
                neighborClimate.moisture += seepage;
            }

            neighborClimate.moisture =
                Mathf.Clamp(
                    neighborClimate.moisture,
                    0f,
                    1f
                    );

            nextClimates[edge.Target.Index] = neighborClimate;
        }

        ClimateData nextHexClimate = nextClimates[source.Index];

        nextHexClimate.moisture += currentClimate.moisture;

        // Ensure that no hex can have more moisture than a hex that is
        // underwater.
        nextHexClimate.moisture =
            Mathf.Clamp(
                nextHexClimate.moisture,
                0f,
                1f
                );

        nextHexClimate.temperature =
            GenerateTemperature(
                source,
                parameters.hemisphere,
                parameters.waterLevel,
                parameters.elevationMax,
                _temperatureJitterChannel,
                parameters.temperatureJitter,
                parameters.lowTemperature,
                parameters.highTemperature,
                parameters.hexOuterRadius
                );

        //Store the data for the next climate.
        nextClimates[source.Index] = nextHexClimate;
    }
Beispiel #5
0
    /// <summary>
    /// Generate a HexMap using the standard RootGen algorithm.
    /// </summary>
    /// <param name="config">
    /// The configuration data for the map to be generated.
    /// </param>
    /// <returns>
    ///A randomly generated HexMap object.
    /// </returns>
    public HexMap GenerateMap(
        RootGenConfig config,
        bool editMode = true
        )
    {
        string diagnostics = "Generate Map Performance Diagnostics\n\n";

        HexMap result = HexMap.CreateHexMapGameObject();
        int    seed;

        if (config.useFixedSeed)
        {
            seed = config.seed;
        }
        else
        {
            config.seed  = Random.Range(0, int.MaxValue);
            config.seed ^= (int)System.DateTime.Now.Ticks;
            config.seed ^= (int)Time.time;
            config.seed &= int.MaxValue;
            seed         = config.seed;
        }

        // Snapshot the initial random state before consuming the random
        // sequence.
        Random.State snapshot = RandomState.Snapshot(seed);

        result.Initialize(
            new Rect(0, 0, config.width, config.height),
            seed,
            config.hexSize,
            config.wrapping,
            editMode
            );

        foreach (Hex hex in result.Hexes)
        {
            hex.WaterLevel = config.waterLevel;
        }

        Stopwatch stopwatch = new Stopwatch();

        stopwatch.Start();

        HexMapTectonics hexMapTectonics = new HexMapTectonics(
            result,
            config.regionBorder,
            config.mapBorderX,
            config.mapBorderZ,
            config.numRegions
            );

        int landBudget = Mathf.RoundToInt(
            result.SizeSquared *
            config.landPercentage *
            0.01f
            );

        TectonicParameters tectonicParameters =
            new TectonicParameters(
                config.hexSize,
                config.highRiseProbability,
                config.jitterProbability,
                config.sinkProbability,
                config.elevationMax,
                config.elevationMin,
                config.waterLevel,
                landBudget,
                config.maximumRegionDensity,
                config.minimumRegionDensity
                );

        string logString = "Tectonic Statistics\n";

        for (int i = 0; i < MAX_TECTONIC_STEPS; i++)
        {
            tectonicParameters.LandBudget = hexMapTectonics.Step(
                tectonicParameters
                );

            //result.Draw(config.hexSize);

            logString +=
                "Step " + i + ", Land Hexes: " +
                result.LandHexes.Count + " / Land Budget: " +
                tectonicParameters.LandBudget + " Total: " +
                (result.LandHexes.Count + tectonicParameters.LandBudget) + "\n";

            if (tectonicParameters.LandBudget == 0)
            {
                break;
            }
        }

        RootLog.Log(logString, Severity.Information, "Diagnostics");

        // If land budget is greater than 0, all land hexes specified to
        // be allocated were not allocated successfully. Log a warning,
        // decrement the remaining land budget from the result, and return
        // the result as the number of land hexes allocated.
        if (tectonicParameters.LandBudget > 0)
        {
            RootLog.Log(
                "Failed to use up " + tectonicParameters.LandBudget + " land budget.",
                Severity.Warning,
                "MapGenerator"
                );
        }

        stopwatch.Stop();
        diagnostics += "Generate Tectonics: " + stopwatch.Elapsed + "\n";

        stopwatch.Start();

        HexMapErosion erosion       = new HexMapErosion(result);
        int           erodibleHexes = erosion.ErodibleHexes.Count;

        // Calculate the target number of uneroded hexes.
        int targetUnerodedHexes =
            (int)(
                erodibleHexes *
                (100 - config.erosionPercentage) *
                0.01f
                );

        while (erosion.ErodibleHexes.Count > targetUnerodedHexes)
        {
            erosion.Step(
                config.hexSize
                );
        }

        stopwatch.Stop();
        diagnostics += "Generate Erosion: " + stopwatch.Elapsed + "\n";


        stopwatch.Start();

        HexMapClimate hexMapClimate = new HexMapClimate(
            result,
            config.startingMoisture
            );


        ClimateParameters climateParameters = new ClimateParameters(
            config.hemisphere,
            config.windDirection,
            config.evaporationFactor,
            config.highTemperature,
            config.lowTemperature,
            config.precipitationFactor,
            config.runoffFactor,
            config.seepageFactor,
            config.temperatureJitter,
            config.windStrength,
            config.hexSize,
            config.elevationMax,
            config.waterLevel
            );

        for (int i = 0; i < config.initialClimateSteps; i++)
        {
            hexMapClimate.Step(climateParameters);
        }

        List <ClimateData> climates = hexMapClimate.List;

        stopwatch.Stop();
        diagnostics += "Generate Climate: " + stopwatch.Elapsed + "\n";

        stopwatch.Start();

        HexMapRivers hexMapRivers = new HexMapRivers(
            result,
            climates,
            config.waterLevel,
            config.elevationMax
            );

        for (int i = 0; i < config.numInitialRivers; i++)
        {
            hexMapRivers.StartRiver();
        }

        for (int i = 0; i < config.numInitialRiverSteps; i++)
        {
            hexMapRivers.Step(
                config.waterLevel,
                config.elevationMax,
                config.extraLakeProbability,
                config.hexSize,
                climates
                );
        }

        result.RiverDigraph = hexMapRivers.RiverDigraph;

        stopwatch.Stop();
        diagnostics += "Generate Rivers: " + stopwatch.Elapsed + "\n";

        stopwatch.Start();

        hexMapClimate.RefreshTerrainTypes(
            climateParameters,
            result.RiverDigraph
            );

        stopwatch.Stop();
        diagnostics += "Assign Terrain Types: " + stopwatch.Elapsed + "\n";

        RootLog.Log(
            diagnostics,
            Severity.Information,
            "Diagonstics"
            );

        // Restore the snapshot of the random state taken before consuming
        // the random sequence.
        Random.state = snapshot;
        return(result);
    }