Best Practices for Modular Systems in Godot?

Hey everyone, I am getting back into game dev as a way to exercise my programming muscles because all the work I do for pay is easy and kind of boring.

I want to make a simple RTS with only a handful of units, a builder, tower, and resource building. I am struggling to find a good way to set up my systems. For example let’s say I want to have a “movement” component that all mobile units have, or a “resource container” component that lets units or structures hold and transfer resources between one another.

I am actually revisiting this project after like a year so maybe I should spend some time playing around in the project so I can ask my question better. Right now my question is just “why is this bad and what can I do that is better?”. But here is some example code I had that felt very spaghetti.

I have a handful of Behavior classes which take in the agent doing the action, plus some other variables. Here is the poorly-named “seek” behavior:

class_name SeekBehavior
extends Behavior

var path: Array[Vector3]
var position: Vector3

func _init(agent: Node3D, position: Vector3) -> void:
	self.agent = agent
	self.position = position
	self.path = Pathfinding.generate_path(agent.position, position)
			
func apply(delta: float) -> void:
	
	if path.size() == 0:
		self.is_finished = true
		return
	
	var waypoint = path[0];
	waypoint.y = agent.position.y
	if agent.position.distance_to(waypoint) > 0.01:
		agent.rc.consume_power()
		agent.position += agent.position.direction_to(waypoint).normalized() * agent.speed * Resources.efficiency
	else:
		path.pop_front()
		
	agent.look_at(waypoint, Vector3.UP)

Each unit has a queue of behaviors that it will do until each one is finished. In practice it feels very hacky though because rather than some central system telling each unit what to do, each unit has to keep track of its own internal queue of behavior objects, each one has some opaque state.

Anyways, sorry this question is not formatted better, I should have a better mental model of what I actually want later in the week.

EDIT: I found this project. It is structured in a clean way and I am just going through and copying and modifying the systems I need.

2 Likes

Read your post and my answer would be so unhelpful that I’m not sure it warrant posting :confused:

My advice would be whatever you like. I don’t think there is anything wrong with having units deciding where they go, provided information about the world, and a sort of world/god class that decide for them is doable too.

At the end of the day, it’s for you to figure how to write a cool interface that make content/behavior easy to add. If you have a gut feeling that one way is bad, just do the other way.

(also specific for Godot Pathfinding, there’s their Navigation servers and agent that you can look/use/inspire yourself from, perhaps?)

2 Likes

The goal is first and foremost to have fun, of course. But I also wanna learn from people who have lots of experience so I can get a feel for how to properly structure projects. Every time I have approached Godot I always felt hamstrung by not knowing how to work with the node system. There’s also a lot of small things I didn’t know, like being able to set Area3D nodes to be Ray Pickable, which means I don’t have to write my own convoluted Raycast logic for unit selection.

I found this project and have been poking around it and learning from it:

1 Like

I don’t really have an answer for this either. Godot embraces object oriented programming, an architecture that I frequently find myself frustrated with, and I haven’t yet settled on something I like and which scales well as the game gets larger and more complicated.

I tend to prefer storing state in one static location, as (among other reasons) this makes saving and loading a relatively trivial task. In other words, I tend to prefer a single data structure that represents the game’s state. So, in the case of your RTS, I would probably try to store things like “who has what resource” in some kind of static data structure and render the the game based on that data structure. This approach… breaks down a bit when applied to Godot though, which is why I don’t feel like I have anything great to suggest.

Yeah, I’m not sure- this is soon to be a problem we run into in our game as well, where we want properties of minecraft blocks to be extensible and “stackable” with each other. Unfortunately, it’s not how we currently do things but maybe it’s something we should keep in mind for the architecture.

What about this project you found feels better structured?

It leans very heavily on signals, which makes sense to me given that Godot is structured as a big tree. I worked on a big Qt GUI program a couple years back and eventually settled on an architecture with centralized state at the top, and everything except the most trivial button callbacks going through a global (or namespaced) message bus. GUI programs are big trees, and Godot games are big trees, so it makes sense.

I also like the way that it works with the Node system, Traits and Actions are 3D nodes. Traits are assigned in the editor (Movement, Selection, ResourceContainer) and Actions are attached/detached at runtime. I still don’t like that there are state machines (they are convoluted) but for simple behavior loops I think it’s fine.

The downside is that state is distributed all over the place and for things like the resource UI at the top-left of the screen, I basically have to get all the nodes that have resources (they have to belong to the “resource_container” group, or “power_consumer”/“power_produce” group) and then recalculate the value for each frame (functional programmers punching their computer monitors).

As for what @exodrifter said:

I would probably try to store things like “who has what resource” in some kind of static data structure and render the the game based on that data structure

This is how I would want to write a game if I was making my own engine, but it seems like Godot really doesn’t like that, and it’s easier to just run signals through some Singleton bus that links everyone together. Central state is just a tally that gets informed by all the little nodes scattered throughout the scene :distorted_face:

Also I’m not sure how much grabbing nodes by group will impact performance, but I only plan on having like 100-200 units total in this game. My plan is for it to be a short 30-60 minute base builder with some simple tower defense mechanics that you can play to kill time.

I am punching my monitor. /j

Godot uses a hashmap for groups so I imagine it’s probably quite fast, but I’ve never profiled it.