Organization of scene/code, is there a better way?

Godot Version

4.2.1

Question

Is there a better way to structure my project, instead of how I’ve got currently, with all the code taking place in GameMap? Would it be better I reorganize the code such that GameMap is controlling instances of a scene with each chunk individually?

class_name GameMap
extends Node3D

var num_chunks: int = 6
var chunks: Array[MeshInstance3D]
var static_bodies: Array[StaticBody3D]
var collision_shapes: Array[CollisionShape3D]

@export var uv_translate = Vector2(0.0, 0.0)
@export var uv_scale = Vector2(1.0, 1.0)
@export var chunk_size = Vector2i(180, 540)

var md: MeshDataTool = MeshDataTool.new()
var st: SurfaceTool = SurfaceTool.new()
var plane: PlaneMesh = PlaneMesh.new()

@export var plain_noise: FastNoiseLite = FastNoiseLite.new()
@export var mountain_noise: FastNoiseLite = FastNoiseLite.new()
@export var plain_scale: float = 30
@export var mountain_scale: float = 10

@export var camera_rig: Node3D = Node3D.new()
@onready var camera = camera_rig.get_child(0).get_child(0)

const RAY_LENGTH = 1000

var unit_scene = preload("res://Scenes/unit.tscn")

func _ready() -> void:
	plain_noise.seed = randi() % 9223372036854775807
	mountain_noise.seed = randi() % 9223372036854775807
	for i in range(0, num_chunks):
		chunks.append(MeshInstance3D.new())
		static_bodies.append(StaticBody3D.new())
		collision_shapes.append(CollisionShape3D.new())
		build_mesh(i)
		add_child(chunks[i])
		static_bodies[i].add_child(collision_shapes[i])
		chunks[i].add_child(static_bodies[i])
		chunks[i].translate(Vector3(chunk_size.x * i - (chunk_size.x / 2), 0, 0))

func _process(delta) -> void:
	#for i in range(0, num_chunks):
		#build_mesh(i)
	map_to_camera()

#func _physics_process(delta) -> void:
	#var space_state = get_world_3d().direct_space_state
	#var mousepos = get_viewport().get_mouse_position()
#
	#var origin = camera.project_ray_origin(mousepos)
	#var end = origin + camera.project_ray_normal(mousepos) * RAY_LENGTH
	#var query = PhysicsRayQueryParameters3D.create(origin, end)
#
	#var result = space_state.intersect_ray(query)
	#print(result)

func build_mesh(i: int) -> void:
	chunks[i].mesh = ArrayMesh.new()
	# create plane array mesh with specified subdivs
	plane.size = chunk_size
	plane.subdivide_depth = chunk_size.y
	plane.subdivide_width = chunk_size.x
	chunks[i].mesh.clear_surfaces()
	chunks[i].mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, plane.get_mesh_arrays())
	# displace vertices
	md.create_from_surface(chunks[i].mesh, 0)
	md.set_material(preload("res://Shaders/terrain_shader_material.tres"))
	for v in md.get_vertex_count():
		var cyl_coord = cylinder_uv_to_xyz(md.get_vertex_uv(v) * uv_scale * Vector2(1.0 / num_chunks, 1.0) + Vector2(i * (1.0 / num_chunks), 0.0))
		var plains = plain_noise.get_noise_3dv(cyl_coord) * (plain_scale)
		var mountains = mountain_noise.get_noise_3dv(cyl_coord) * (mountain_scale)
		var elevation = plains + (mountains - plains) * (mountains * 0.7)
		if elevation < 0.0:
			elevation *= 1.5
		md.set_vertex(v, md.get_vertex(v) + Vector3(0.0, elevation, 0.0))
	# rebuild normals
	chunks[i].mesh.clear_surfaces()
	md.commit_to_surface(chunks[i].mesh)
	st.create_from(chunks[i].mesh, 0)
	st.generate_normals()
	# commit to mesh
	chunks[i].mesh = st.commit()
	collision_shapes[i].shape = chunks[i].mesh.create_trimesh_shape()

func cylinder_uv_to_xyz(uv: Vector2) -> Vector3:
	return Vector3(1.0, uv.y * PI * 2, 0.0).rotated(Vector3.UP, uv.x * PI * 2)

func map_to_camera() -> void:
	var map_width = chunk_size.x * num_chunks
	for m in chunks:
		var widths_from_camera = (m.global_position.x - camera_rig.global_position.x) / map_width
		if (abs(widths_from_camera) <= 0.5):
			continue

		if (widths_from_camera > 0):
			widths_from_camera += 0.5
		else:
			widths_from_camera -= 0.5

		var widths_to_fix: int = widths_from_camera
		m.global_position.x -= widths_to_fix * map_width

I ask this because I need to access the mouse click input event signal of the static bodies in order to place a unit at that location on the mesh. How best should I attack this problem? The commented code in _physics_process() is working, returning the correct location of the mouse pointer.

I aint reading allat but why are you doing randint mod 922337203685477580??
That’s literally the max value for signed ints why would you do that

Because I want an int between 0 and the max int. It’s just the seed for the noise functions.

Ok well I’m not sure if I understood your question correctly but if you’re trying to go modular you could add a child node and attach it your process and physics process code to it which would work fine

I believe it is best practice to split up code instead of having one giant script doing it all
Personally I would probably have one node doing all the init stuff that I remove once it has finished and another node looping every frame / tick if that makes sense

1 Like

I’m not sure I understand how I would have a node for initialization, to be removed later. Would it reference the nodes already present in the editor? The way I have it currently, the nodes are all created in code, as it is easiest for me to think in such terms. And for the StaticBody3D, should it be parented to the MeshInstance3D, or should it be the other way around? I’m a noob when it comes to Godot, and programming in general. The whole reason I’ve got it all created in code is because it’s easier to think about. AI isn’t much help for me as it constantly messes up on what version of godot I’m using, which leads me to believe that most all the code it produces is buggy and likely ignores/creates features in the engine that are/aren’t present. I have asked gemini the best way to modularize the code, and it creates the chunks as MeshInstance3Ds with mesh generation (initialization?) code inside of it, with a top layer that extends “Control,” and manages the map_to_camera() function.

1 Like

Forget about the initialization node being freed that was a bad idea lol

For the StaticBody3D and the MeshInstance3D, the ordering of these nodes is not important I believe, as they are not going to move by design. Just choose what order makes more sense to you, I would personally make the StaticBody3D the parent as it makes more sense to me to move the physical object and have its representation follow, but once again, they’re not moving so it does not make any difference.

I don’t think I understand what your code does exactly but if the nodes you are building are similar to each other (except for position) you could make a scene containing one of them (kind of a node template) and instantiate it where you need, eventually modifying or adding the features that are not common to all these nodes, if that makes sense

1 Like

That’s pretty much what I ended up doing, except I left the MeshInstance3D as the parent, as the mesh generation is procedural and the shape for the StaticBody3D is derived from it. GameMap initializes the variables for each chunk, each chunk is a scene that builds itself, and GameMap handles wrapping the map around the camera, whereas previously GameMap handled everything. The game map is designed to be like a 2D map projection of a 3D globe, but with height.

1 Like