0 votes

I'm new to godot and enjoying learning about it.

I am trying to understand more about procedural generated noise. I have followed a Youtube video by Codat showing how to get procedural terrain generated in chunks. It does ok for what i need it to do right now. The issue I am facing is when it comes to placing something like trees on the surface of the generated noise. As there is no set height map to use and when i try to print vertex.y above a number to console it lags badly.

The idea of my project is a star fox type of game. the player is stationary and the terrain moves toward the player on the z axis. the terrain continuously generates and unloads as it leaves behind the camera view. I am not sure about proceeding with this as the programming concepts are out of my league at the moment. but before starting over from scratch i thought i would ask anyway. Thanks for your time.

Here is the code to create chunks and world generation.

# Original Source: Codat Youtube 
extends Spatial
class_name Chunk

var mesh_instance
var noise
var x
var z
var chunk_size
var should_remove = true

func _init(noise, x, z, chunk_size):
    self.noise = noise
    self.x = x
    self.z = z
    self.chunk_size = chunk_size

func _ready():

func generate_chunk():
    var plane_mesh = PlaneMesh.new()
    plane_mesh.size = Vector2(chunk_size, chunk_size)
    plane_mesh.subdivide_depth = chunk_size * 0.5
    plane_mesh.subdivide_width = chunk_size * 0.5
    plane_mesh.material = preload("res://asset/material/terrain.material")

    var surface_tool = SurfaceTool.new()
    var data_tool = MeshDataTool.new()
    surface_tool.create_from(plane_mesh, 0)
    var array_plane = surface_tool.commit()
    var error = data_tool.create_from_surface(array_plane, 0)

    for i in range(data_tool.get_vertex_count()):
        var vertex = data_tool.get_vertex(i)
        # Chunk Height
        vertex.y = noise.get_noise_3d(vertex.x + x, vertex.y, vertex.z +z) * 10 #80

        data_tool.set_vertex(i, vertex)

    for s in range(array_plane.get_surface_count()):

    surface_tool.create_from(array_plane, 0)

    mesh_instance = MeshInstance.new()
    mesh_instance.mesh = surface_tool.commit()
    mesh_instance.cast_shadow = GeometryInstance.SHADOW_CASTING_SETTING_OFF

func generate_water():
    var plane_mesh = PlaneMesh.new()
    plane_mesh.size = Vector2(chunk_size, chunk_size)

    # To Do give it a material
    plane_mesh.material = preload("res://asset/material/water.material")
    var mesh_instance = MeshInstance.new()
    mesh_instance.mesh = plane_mesh


extends Spatial

const chunk_size = 32 #64 default
const chunk_amount = 16 #16 default

var noise
var chunks = {}
var unready_chunks = {}
var thread

func _ready():
    noise = OpenSimplexNoise.new()
    noise.seed = randi()
    noise.octaves = 6
    noise.period = 80

    thread = Thread.new()

func add_chunk(x, z):
    var key = str(x) + "," + str(z)
    if chunks.has(key) or unready_chunks.has(key):

    if not thread.is_active():
        thread.start(self, "load_chunk", [thread, x, z])
        unready_chunks[key] = 1

func load_chunk(arr):
    var thread = arr[0]
    var x = arr[1]
    var z = arr[2]

    var chunk = Chunk.new(noise, x * chunk_size, z * chunk_size, chunk_size)
    # Chunk Y axis Location
    chunk.translation = Vector3(x * chunk_size, 0, z * chunk_size)

    call_deferred("load_done", chunk, thread)

func load_done(chunk, thread):
    var key = str(chunk.x / chunk_size) + "," + str(chunk.z / chunk_size)
    chunks[key] = chunk

func get_chunk(x, z):
    var key = str(x) + "," + str(z)
    if chunks.has(key):
        return chunks.get(key)

    return null

func _process(delta):

func update_chunks():

    var player_translation = $Player.translation
    var p_x = int(player_translation.x) / chunk_size
    var p_z = int(player_translation.z) / chunk_size
# Chunk Location To Player
    for x in range(p_x - chunk_amount * 1, p_x + chunk_amount * 1):
        for z in range(p_z - chunk_amount, p_z + 1): #(p_z - chunk_amount * 0.5, p_z + chunk_amount * 0.5):
            add_chunk(x, z)
            var chunk = get_chunk(x, z)
            if chunk != null:
                chunk.should_remove = false

func clean_up_chunks():
    for key in chunks:
        var chunk = chunks[key]
        if chunk.should_remove:

func reset_chunks():
    for key in chunks:
        chunks[key].should_remove = true


in Engine by (15 points)

1 Answer

+1 vote
Best answer

You can use a RayCast.

  1. You will need to create a Collision Object (probably a Static Body) to your terrain mesh, and after that, a Collision Shape.

  2. Once you have that, the RayCast will be able to detect your terrain.

  3. Be sure your raycast is enabled.

  4. Be sure to place your terrain in a PhysicsLayer your raycast is checking.

  5. Inside a loop, randomly position your raycast, check for your terrain, and using the raycast function GetCollisionPoint, you will have a Vector3 with the point in space the raycast intersected your terrain.

  6. With this point in hand, place a tree or whatever object you want.

  7. Be sure to use raycast function ForceRayCastUpdate after each loop iteration.

  8. Be sure to ramdomize inside the loop, so that you get a random value in each iteration.
by (128 points)
selected by
Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read Frequently asked questions and How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to [email protected] with your username.