0
\$\begingroup\$

For my Grand Strategy game, I am looking to use maps generated by Godot's FastNoiseLite implementation of Perlin noise. While generating the texture for the map was straightforward, I've already spent weeks to figure out a way to determine the overall terrain type of Voronoi cells drawn on the map. For this, I implemented a "Star Check" to spread points on the borders of each cell and from those border points to the respective site points, like so:

func star_check(cell_center: Vector2, n_steps: int=10) -> Array:
    var terrain_types: Array
    var polygon: Array = _polygon
    var n_points = polygon.size()
    var checkpoints: Array
    var lines: Array

    # read in lines as arrays of [start, end point, distance]
    for i in range(n_points):
        var last_index: int = n_points - 1
        var line: Array
        match i:
            last_index:
                line = [polygon[i],polygon[0],polygon[i].distance_to(polygon[0])]
            _:
                line = [polygon[i],polygon[i + 1],polygon[i].distance_to(polygon[i + 1])]

        lines.append(line)

    var border_checkpoints: Array
    # compute ceckpoints along edges of points
    for line in lines:
        var line_points: Array = []
        var step_size: int = floor(line[2] / n_steps)
        var from: Vector2 = line[0]
        var to: Vector2 = line[1]

        for k in range(n_steps):
            var point: Vector2 = from + k * step_size * from.direction_to(to)
            line_points.append(point)

        border_checkpoints.append_array(line_points)

    var center_lines: Array = border_checkpoints.map(func(p):

        return [p, cell_center, p.distance_to(cell_center)]
        )

    # conpute center checkpoints from border points
    var center_checkpoints: Array
    for line in center_lines:
        var line_points: Array = []
        var step_size: int = floor(line[2] / n_steps)
        var from: Vector2 = Vector2(line[0])
        var to: Vector2 = Vector2(line[1])

        for k in range(n_steps):
            var point: Vector2 = from + k * step_size * from.direction_to(to)
            line_points.append(point)

        center_checkpoints.append_array(line_points)

    border_checkpoints.append_array(center_checkpoints)

    checkpoints = border_checkpoints.filter(func(p): return Geometry2D.is_point_in_polygon(p, _polygon)) #only use points in voronoi site
    check_points = checkpoints #internal class_variable

    terrain_types = checkpoints.map(func(p): return _evaluate_point_type(p))
    check_point_values = terrain_types #internal class_variables

    return terrain_types

Determining the terrain type of a point is done in _evaluate_point_type using enums to represent their position in the (non-normalized and centered) NoiseTexture's color ramp:

enum Terrains {DEEP_OCEAN, SHALLOW_OCEAN, COAST, GRASSLAND, MOUNTAIN_BOTTOM, MOUNTAIN_MEDIUM, MOUNTAIN_TOP}

func _evaluate_point_type(point: Vector2) -> int:
    var point_value = ((_map_noise.get_noise_2dv(point) + 1) * 0.5) #translate to [0,1] for offset matching
    var type: int = -1
    var offsets = _terrain_gradient.offsets
    var last_index: int = len(offsets) - 1
    for i in range(len(offsets)): #find corresponding offset
        match i:
            0:
                if point_value < offsets[Terrains.SHALLOW_OCEAN]: #everything below first non-zero offset
                    type = Terrains.DEEP_OCEAN
            last_index:
                if point_value > offsets[Terrains.MOUNTAIN_TOP]: # everything above highest offset
                    type = Terrains.MOUNTAIN_TOP
            _:
                if point_value >= offsets[i] and point_value < offsets[i + 1]:
                    type = i

    return type

The noise gradient is loaded from a saved Gradient resource that corresponds to the color ramp used by the noise texture:

[gd_resource type="Gradient" format=3 uid="uid://wrob2ye40vuu"]

[resource]
interpolation_mode = 1
offsets = PackedFloat32Array(0, 0.481236, 0.505519, 0.516556, 0.626932, 0.660044, 0.679912)
colors = PackedColorArray(0, 0, 1, 1, 0.299985, 0.470862, 1, 1, 0.657183, 0.508635, 0.167871, 1, 0.280679, 0.399257, 0.108318, 1, 0.333719, 0.196772, 0.0853799, 1, 0.396025, 0.358924, 0.223805, 1, 1, 1, 1, 1)

Now, instead of a representation of the point type, I get weird results, such as ocean points on grassland areas of the texture, or coastline points on ocean portions of the noise texture. I'd be grateful for any pointers to what I might be doing wrong here.

EDIT: Here is the Noise Resource used in texture generation:

[gd_resource type="FastNoiseLite" format=3 uid="uid://davh6t1gndhdm"]

[resource]
noise_type = 3
frequency = 0.002
fractal_lacunarity = 2.575
domain_warp_type = 1
domain_warp_amplitude = 29.055

Example of point on "mountain" portion of gradient being evaluated as an ocean point (circle is larger than point for visibility)

Example of point on "mountain" portion of gradient being evaluated as an ocean point (circle is larger than point for visibility)

\$\endgroup\$
2
  • \$\begingroup\$ It would help if you could edit to include an example of the input noise texture & maybe the output as well. I've found that proc gen of this type is very dependent on properly tuned input. Folks working through your code won't necessarily get the same results if they're not applying it to that same data as you're using. Similarly, sample output would help spot potential errors in the logic. \$\endgroup\$
    – Pikalek
    Apr 1 at 15:02
  • \$\begingroup\$ Thanks for the advice. I'll add the contents of the godot resource file containing the parameters to set it up in Godot \$\endgroup\$ Apr 1 at 15:21

0

You must log in to answer this question.

Browse other questions tagged .