Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use a component-based system instead of inheritance [NOT ECS] #2796

Closed
Tracked by #15
ka23ppa3 opened this issue May 29, 2021 · 27 comments
Closed
Tracked by #15

Use a component-based system instead of inheritance [NOT ECS] #2796

ka23ppa3 opened this issue May 29, 2021 · 27 comments

Comments

@ka23ppa3
Copy link

ka23ppa3 commented May 29, 2021

Describe the project you are working on

Any game

Describe the problem or limitation you are having in your project

Cannot make two separated modifiers(scripts/components) for the same one node like in Unity. With time of developing project to keep this deal easy and effective I have to create new scripts and move some common functionality from others scripts to new one, so it will be reusable module for all others cases. Also in this case I dont need to think hard about inheriting which is gonna be only harder and breake-deal with time(very soon basically for every game harder than "very simple"). I think engine-maintainers can have same problems.

Describe the feature / enhancement and how it helps to overcome the problem or

Component-based system is known to be better than inheriting for every game more complex than just "very simple". It makes code more reusable, project easier to maintain and extend potential of the project unlimited.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or

I think this link is good enough to research question and compare both methods of developing engine and game: https://www.raywenderlich.com/2806-introduction-to-component-based-architecture-in-games

If this enhancement will not be used often, can it be worked around with a few lines

No

Is there a reason why this should be core and not an add-on in the asset library?

Yes. This cannot be implemented in good manner without engine-side support/orientation I think.
As others suggested you can use nodes/scenes-hierarchy to implement Entity-Component pattern, where "entity" is a parent node and his "components" are located inside "component"-child node. You can also implement "AddComponent() / RemoveComponent()" functionality in Singleton.

  • ParentNode(Entity)
    |- Child Node(Components Holder)
    ||- Node With Your Component1
    ||- Node With Your Component2
    |- Any Other Node you need1
    |- Any Other Node you need2

Lets discuss!

@Zireael07
Copy link

Has been suggested many times. There's a Godot-ECS fork by Andrea Catania if you really want that - alternately, use children nodes as your components (if you use just a Node, it has no superfluous data like position unlike Spatial/Node2D)

@Calinou
Copy link
Member

Calinou commented May 29, 2021

See also Why Godot isn't an ECS-based game engine?.

@Calinou Calinou changed the title Component-based system instead of inheritance Use a component-based system instead of inheritance May 29, 2021
@ka23ppa3
Copy link
Author

I forgot to say that I does not mean Entity-Component-System but rather Entity-Component way, where you still can make game playing with nodes with single-components and reuse them, but also you can place two or more components on one node and reuse them, like in Unity. Unity are not ECS engine but rather Entity(gameobject)-component engine which is looks really greate from my experience. The point is not divide data and logic like in ECS which is makes really more harder to develop simple games, but rather divide inheritance in components. In this case, again, you will lost nothing but will get easier way to extend functionality.

I see your point about using root node as container and childs as components, it can be good, Im trying it already... but also this is not really clear to go in this way for newbies I think... I was confused a bit when I created "Button" node on scene and noticed it "packed" as one node and not like Button >> Text

@ka23ppa3 ka23ppa3 changed the title Use a component-based system instead of inheritance Use a component-based system instead of inheritance [NOT ECS] May 29, 2021
@ka23ppa3
Copy link
Author

ka23ppa3 commented May 29, 2021

Hierarchy can be other problem, cause it going to be too big to just keep it fully unfolded all time, especially when you are playing with hierarchy in components-like way. When you think about small examples do not forget about big-ones. User will be not able to just open and spot hierarchy in bigger than very-small games... Also we need fast way to add component with keeping strict naming of it node-holder in this way. We can rename component and need to update node-holder name too. Maybe addon can make deal

@mrjustaguy
Copy link

I found Godot far more intuitive with the Node-Scene approach compared to Unity, so it's really about personal preference.
Properties are basically Components (not totally) and a Unity like approach would be more in line with using the plain Node as an Entity, and adding Properties/Children to it, depending on the way you want to go.

Hierarchy is Not a problem with Godot, it's a problem for users that don't collapse it & segment it into different stand alone scenes. Those stand alone scenes are akin to Prefabs

@Zireael07
Copy link

Entity-component has also been suggested many times (mostly using Unity as an example). I am almost 100% certain there is an addon for this already, but again: use children nodes as components.

@ka23ppa3
Copy link
Author

I agree with you all for now. From my current point its more-like clean to do same in suggested approach. But I still want to add new info here while I researching how to properly work with Godot.
Afterwards: I does not want to make Godot as Unity, I just want to make sure best in Unity is going to be in Godot, so with time it will be even better than Unity.

@Xrayez
Copy link
Contributor

Xrayez commented May 29, 2021

I think this proposal raises a valid point, but the solution could be different.

See proposals #2296 and godotengine/godot#23101 which basically suggest implementing the traits system as a GDScript feature (more or less).

See my comment in godotengine/godot#23101 (comment) which describes my current approach at solving this problem using existing system. Indeed, using the node system is quite intuitive in and of itself. However, I do believe that this process of reusing components in scripts could be greatly simplified, it's something that you need to do if you work on any non-trivial project:

  • Have to create in-editor script-generators to make the same functionality to be available across different base types.
  • Define various component scripts that way.
  • Have to make sure to properly wire components to parent/host nodes. Really tiresome process because you have to use onready var almost everywhere to expose component's functionality up to controller. Or use fancy delegator classes like suggested in Add ability to set additional scripts to nodes/objects #2296, with lack of autocompletion etc.

Note that I have no previous experience in Unity, but I'm afraid that I'm missing Unity features in Godot, looking at this proposal. 😃

@DriNeo
Copy link

DriNeo commented May 29, 2021

As everything in Godot ends in a resource, it would be interesting to help an advanced user to work without nodes, except, eventually, a special node dedicated to this purpose.
Just giving an access to low level could replace all these suggestions that try to force the scene system into another paradigm.

@GeorgeS2019
Copy link

@DriNeo You have raised an interesting suggestion. Please help us to understand perhaps with more elaborations or use cases, with reference to known implementation e.g. What in Unity, and what Andrea Catania ECS for godot has done, how your suggestions have benefits that have not been addressed.

Ultimately many of us WANTS the BEST for godot. We are here to learn different views FOR THE PURPOSE to make Godot one of the best Open Source Game engine.

@SpectralDragon
Copy link

SpectralDragon commented Jun 1, 2021

@GeorgeS2019 in my opinion, the main goal is using a reusable piece of logic between nodes. This looks like multiple extends nodes, but the components can be fetched from nodes to make some great thing.

For example (pseudo code mode ON):

// This class can be stored in the node in components collection and be fetched later.
// Component has some lifecycle as a node and has access to the host node.
// Component can be deleted or inserted at any moment.
class Component: (Node or Object) {
	// some great code here...
}

class InventoryComponent: Component 
{
	public void AppendItem(PickableComponent item) 
	{
		// some logic
	}
}

// Component also can be a tag
class PickableComponent: Component 
{
	[Export]
	public bool needsRemoveParentFromScene = false;
}

class Player: KinematicBody {

	private InventoryComponent _inventory;

	public override void _Ready() 
	{
		// Get stored component from Player node.
		_inventory = GetComponent<InventoryComponent>();
	}

	public override void _PhysicsProcess(float delta) 
	{
		var itemNode = // some raycasting here

		var pickableComponent = itemNode.GetComponent<PickableComponent>();
		
		if (pickableComponent != null) 
		{
			_inventory.AppendItem(pickableComponent)
		}
	}
}

We also can manage our components using some new methods like:

void AddComponent(Component component);

void RemoveComponent<T>() where T: Component;

void GetComponent<T>() where T: Component;

GDScript

# inventory.gd

component InventoryComponent

func append_item(item):
	# some logic

# pickable.gd

# Just a tag that informs the game that this node can be picked.
component PickableComponent

export var needs_remove_parent_from_scene = false

# player.gd

extends KinematicBody

var inventory: InventoryComponent

func _ready():
	inventory = get_component("InventoryComponent")

func _physics_process(delta):
	var item_node = # some raycastign
	var pickable_component = item_node.get_component("PickableComponent")
	if pickable_component:
		inventory.append_item(pickable_component)

P.S. I'm a newbie in Godot and I don't know some fundamental things, but for me, it looks like a good practice to manage your game logic into a little piece of code without creating support nodes in scenes. And I'm not sure as the current OOP system works with nodes tree when we attach or delete our helper node from the game scene. Components look better, IMHO.

@ka23ppa3
Copy link
Author

ka23ppa3 commented Jun 1, 2021

@SpectralDragon If I understand you correctly you need a simple way to add/remove components from the "entity"-container in godot-way... As others suggested to use scenetree I think you need to create some manager-singleton where you can implement those methods to add or delete component, so it will look automatically for the requested node(or node type) and remove or add it in proper hierarchy way. I cannot suggest you to create some base type named "Component" as you will need to extend godot-nodes in your scripts too, so better to make a deal with singleton.

In C# it also was possible to make a deal with extension-methods, where you can extend or maybe "pseudo-extend" functionality of any class, which feels are really comfortable.

@SpectralDragon
Copy link

@ka23ppa3 No, we can't use a singleton, because component can be attach only to node, and only host node can fetch own components.

I mean that in Unity, we have inspector with all connected components to GameObject (e.g. Node), and these components can be change by the user.
image

In Godot I don't see any solution to add this functionality, because Godot doesn't support extensions like in Swift.

@ka23ppa3
Copy link
Author

ka23ppa3 commented Jun 1, 2021

@SpectralDragon Im Unity-developer so I surely know what you mean. Yes, you cant attach more than one script(component) to one node, but you can just create another one child node to hold your other component. I updated my post so you can see a hierarchy-like example from it.

and only host node can fetch own components.

This is wrong from what I know. You can get component from any node you want using get_node(path)

@GeorgeS2019

This comment has been minimized.

@willnationsdev
Copy link
Contributor

willnationsdev commented Jun 2, 2021

@Zireael07

I am almost 100% certain there is an addon for this already, but again: use children nodes as components.

@DriNeo

As everything in Godot ends in a resource, it would be interesting to help an advanced user to work without nodes, except, eventually, a special node dedicated to this purpose.

I believe Zireael07 might be thinking of Godot Next which includes a CallbackDelegator node that can maintain a Set (instead of an Array) of custom Resource instances that each must have separate Scripts and which can be accessed by Script, specifically emulating the Unity workflow. Many of the standard callbacks that the CallbackDelegator receives are directly relayed to each of the resources in its data. There is a provided Behavior resource that implements each of the supported methods which you need only override and they will be called accordingly. The CallbackDelegator and Behavior rely on the DelegationInspectorPlugin to function properly, so the Godot Next plugin must be activated. Not sure how out-of-date the code is since it was more a proof of concept than anything else, but it is certainly possible to do (though I seriously doubt effective for memory/performance purposes). There's a video on my YouTube channel demoing it that I shared (made by xDGameStudios) ages ago.

The above-mentioned node provides a sample of behavior similar to DriNeo's explanation. You could feasibly have just one Node with tons of different Resource "behaviors" attached to it, and all of them would receive the same system notifications as the actual node.

Still, I hardly think it calls for any changes to the Godot source code. "components" need not even be actual child nodes necessarily. I do think that having a trait system in GDScript would be a great addition, but that'll probably be something revisited for 4.1 or 4.2. I recall vnen liked the idea back when it was initially discussed, but there were many other things to do before we could ever consider working on it (hence the GDScript 2.0 rewrite).

@GeorgeS2019
Copy link

GeorgeS2019 commented Jun 2, 2021

@willnationsdev It seems U have a vision how best to make a certain aspect of Godot great, based on what you have written!

You promoted Godot-Next

To be honest, I could not immediately get the advantages of Godot-Next, except it seems there is serious thinking behind the proposal and implementation.

Please either help us by spending more time making the complex and deep concepts of Godot-Next simpler to understand OR reach out for HELP to make Godot-Next consisting of features that are indispensable for making difficult Game concepts EASY to implement through Godot-NEXT.

PLEASE bridge the gaps between your vision/implementation and the level we can comprehend.

Juan always states the key motivation behind Godot is the simplicity of adoption. Juan would spend huge amount of time to make great Game engine features which may involve multiple advanced steps in other top tier game engine BUT accessible in Godot within a few clicks.

Please help us to understand Godot-Next in "Juan" spirit.

@theraot
Copy link

theraot commented Jun 4, 2021

I have made, yet another, Godot component based solution in a Q&A format at gamedev dot stackexchange: How to make a component solution in Godot (similar to Unity's). I suppose I could make a reusable asset based on that.

Based on my implementation, I believe that metadata is almost there. However my solution mixes metadata and node groups. I would rather be able to implement it over a single feature instead of keeping in sync two things that can change independently, which would be possible with a get_nodes_with_meta counterpart to get_nodes_in_group.

There are other things that can help (I think I saw proposals for these a while back already):

  • Making metadata editable in the editor. There is an asset that does that: https://godotengine.org/asset-library/asset/637
  • Have GDScript override get_class and is_class or otherwise a better ClassDB (we have a workaround in reading project settings).
  • Supporting custom resource export variables (so Godot does not offer every resource type under the sun, and without the need of the tool script workaround).

However, honestly, my perception is that if I was able to implement something, it will not be added to Godot because it can be an asset.

@willnationsdev
Copy link
Contributor

willnationsdev commented Jun 4, 2021

@GeorgeS2019

I wouldn't say I have a vision per se. It was something that someone wanted to do, consulted me about, and eventually submitted to Godot Next. But the idea in concept is a very clean "Godot" way of handling the problem.

For any given node that wants/needs to have more than one script, the only things necessary for such an interface are...

  1. defining a property that stores an Array of custom resources on the node.
  2. allowing only one resource of a given Script type to be present within the array.
  3. inserting/removing/getting a given resource by using the Script as a key.
  4. customizing the EditorInspector's rendering logic for the resource Array property from step 1 so that it provides an updated interface (out-of-the-box Array rendering lets you arbitrarily add new elements with numeric keys and no regard to Script uniqueness, etc.).
  5. making it so that each resource in the Array property will have callbacks executed in sync with the node's respective callbacks (like _ready(), _process, _input, etc.).

Godot Next did this by defining a CallbackDelegator with the behaviors property. The property is of type ResourceSet which is a custom class around an Array that provides the desired interface of Script uniqueness and prevents assignment of resources that do not have a specifically configured base type (in this case a "Behavior" base type). An EditorInspectorPlugin called DelegationInspectorPlugin empowers inspected objects to handle their own rendering logic for how to render themselves. This then allows the ResourceSet to instruct the Inspector how it should be rendered to abide by rule 4 above. For rule 5, CallbackDelegator iterates through the behaviors and calls their defined callback methods, if present.

As for the rest of Godot Next, it isn't really related to this concept. Godot Next itself is a mishmash of singular or small collections of nodes that are helpful or interesting and which people have added to the project for one reason or another, all to avoid writing and submitting an entirely new addon for a single node, etc.

@theraot

Looks like your metadata/groups approach essentially tries to do the same thing as what Godot Next does, except that Godot Next has a "GameObject"-like type that allows things to take a statically-typed approach. But your approach, in contrast, uses the base interface of Godot and therefore allows any arbitrary node to become a component-container.

I think the best solution long-term would be adding a trait system to GDScript (was proposed before in the godotengine/godot repo a long time ago) whereby you could just define a trait that attaches the relevant behavior array property and callback delegation operations to any arbitrary Node class. This way, you'd keep the type safety, remove the hackage associated with metadata and groups (which, as you mention, can be modified by users externally), but still retain the cleanliness of not needing a "GameObject" type like the CallbackDelegator node in Godot Next. And then this trait and a custom EditorInspectorPlugin dedicated just for this scenario could be set up as its own standalone plugin that people can install and use in their projects.

However, honestly, my perception is that if I was able to implement something, it will not be added to Godot because it can be an asset.

Yes, the Godot core will never add multiple scripts per node. It drastically increases complexity, is wholly unnecessary at the Object level where the script property is even defined, and generally has no justification in the core where the API is laid out in the first place. The most people should expect is having Resource "components" be accessible based on a composite key combining a Node reference and a Script reference, which both @theraot's implementation and Godot Next's provide, all doable with plugins.

The closest suggestion was a MultiScript language concept that was merged and then reverted ages ago where you'd have a stack of Scripts associated with one Script. But it became too awkward/confusing to deal with the logic for multi-level calls in the API and some other issues too iirc, so it was taken out. This was during the 2.1 to 3.0 release timeline.

With that said, I don't see why the linked StackExchange user was so against Godex. It seems like it does almost exactly what they want. You put a node in the world. You attach components to it from the Editor. Everything is nicely integrated with visual tools. They just didn't like the inconvenience of a custom build, but Godex provides builds of the latest stable release with the Godex module, so it wouldn't be that big of a deal I don't think (unless Godex doesn't also provide export template builds?). Either way, the problems they have with Godex seem like relatively minor issues that could be resolved on their own by conversing with the Godex maintainer and simplfiying the acquisition of the necessary binaries. Then you'd have an actual ECS framework to use in combination with Godot's API that is far more performant and effective than anything we could cook up with changes to the Object API.

@GeorgeS2019
Copy link

@willnationsdev give me some time so one day I can condense your discussions into key Godot advantages unmatched/unseen with other game engines :-)

@theraot
Copy link

theraot commented Jun 4, 2021

@willnationsdev

I don't see why the linked StackExchange user was so against Godex.

Because it is a straw man, plain and simple. I wrote both the question and answer. I have seen multiple instances of "Godot should have component system like Unity" which I find hard to justify when there are options already, of which I mentioned in the question those in my awareness at the time of writing. I opted to put those options in the question because the nature of the StackExchange network is that the person asking is expected to have done some work, at least searching online.

By the way, my personal reason to not use Godex is because I don't need it.

The way I see it, these are generic and flexible solution that solves multiple problems, which if addressed individually would allow for optimization. And you know what the "Best practices for engine contributors" article say about this kind of solutions.

Do I really need a solution that support both Node and not Node components and also objects? And also support multiple objects sharing a component? chances are that no, I will not need to cover all combinations of that in any particular game. If I only needed it for nodes, using children would probably suffice, perhaps plus groups. Or if it is not for Nodes, then metadata alone would do.

Hopefully that question helps people who search for a similar solution to find such options, or at least provide some insight on how to implement their own. On that note, I could add the Godot Next solution to the question, and invent some reason not to use it, I just know too little of it at the time.

And yes, I would like traits. Other type system improvements too. Does that have chances of happening? Because, again, we manage. And we could always use C# or something else.

@willnationsdev
Copy link
Contributor

willnationsdev commented Jun 5, 2021

@theraot

I don't see why the linked StackExchange user was so against Godex.

Because it is a straw man, plain and simple.

To some extent, I can see the Godex solution as a strawman except that the original question...

How to make a component solution in Godot (similar to Unity's).
I want to be able to assign multiple scripts to a single node, and have each script become a component.

...doesn't specifically state that it wants to only include vanilla installs of Godot, so it feels like moving the goalposts. Godex does provide a legitimate component solution for Godot that is similar to Unity's DOTS or if just using Entity nodes, could become like Unity's general GameObject/MonoBehaviour API. It lets users write component scripts and attach the scripts to Entity nodes from the Editor.

Regardless, I understand that, for those who don't want to deal with a custom build, and for those who want the ability to arbitrarily add multiple scripts to any Node, then your aim is to provide a solution/implementation to which to point those users (so Godex wouldn't fit that desire).

The way I see it, these are generic and flexible solution that solves multiple problems, which if addressed individually would allow for optimization

I'm not quite picking up on what the implied "these" and "solution[s]" are, nor the nature of the specific "problems" you mention. Do the godot-component-system addon, the GDQuest entity-component framework, and Godex all deliver some partial or full mix of feature implementations where each feature solves some specific smaller problem which, all together, optimize Godot in some way?

In order to determine which portions of the desired features could be added to core versus need to be kept as addons, we'd need a clearer idea of what each piece entails and why it would be relevant/useful. Or perhaps there is some reason why doing such things in an addon is unnecessarily complicated and some core change(s) could make it easier for such addons/plugins to be developed.

Do I really need a solution that support both Node and not Node components and also objects?

(I'm gonna go into details here, but it's not necessarily for your sake. Other less experienced users also seem to be reading this convo. Please ignore it if it seems like I'm treating you as if you're inexperienced/ignorant).

Below are the pieces to which I think you might be referring.

As far as I understand things, Unity basically just has some reproducible means of keying to a particular object instance and a particular component type to identify an instance of a component. And the entire process is fully serializable/preservable. It then has a UI that respects the uniqueness of the component type per "entity" object instance. In practice, this then manifests as GameObject entities (with a consistent instance hash across game instances?) with type-unique MonoBehaviour component slots.

Here are some requirements for doing similar things in Godot.

Entity-Component Collections

If you allow any Object to be an "entity", then you'll run into preservation problems during serialization because the Object's instance ID is their memory address and won't necessarily remain the same across Godot instances (so closing Godot or transferring the data to a different Godot instance across a network could result in data corruption). The only ways to avoid this that I know of would be to...

  1. use a Node with a NodePath so that the identifier is the placement of the Node in the SceneTree which will be consistent across Godot instances. Then, you are free to organize the components relative to the Node instance however you want.
    • this is what your metadata approach does since only set metadata on Nodes.
  2. use a Resource that has nested "component" Resources since the nested components will always be saved and loaded along with the parent Resource with full preservation.
  3. use a custom scheme of defining and preserving entity/component data (which is what Godex does with its various backend C++ storages and entity ID proxies).

Any of the above approaches can be achieved in core or with an addon, and in isolation, there is no reason to do so in core, so you are right, it likely wouldn't be merged with core. But the core API also does not appear to create any obstacles in implementing any of these solutions, so there's nothing stopping users.

Entity-Component Scripting API

Once you have a collection of component objects that are capable of being mapped to a specific entity object, you then need to ensure the full API can be supported.

  1. Can add a component by type.
    • adding a component of the same type would fail.
    • creates a default empty instance of the provided component type for the entity.
  2. Can add a component by component instance.
    • same thing as "by type", but uses supplied instance rather than default empty instance.
  3. Can remove a component by type.
  4. Can get a specific type of component from a single entity.
  5. Can get all components from a single entity.
  6. Can get all currently allocated components of a single type.

As far as I'm aware, script code is fully capable of implementing all of the logic specified above for any of the entity-component strategies detailed in the previous section. Either the entity class itself would have need some kind of API for accessing its own components (so a special class of Node/Resource/"entity") or you'd wrap all the data behind a Facade singleton (such as an autoload) and allow any Node/Resource/"entity" with the correct interface to interact with said singleton. Or some mix of the two.

The metadata/group approach you used splits the API between the SceneTree (groups) and the component access (Object API metadata), and so presents as a mixed implementation.

The core has no need for any of the functionality, and since it is all doable as part of an addon, again, there's no reason any of it would be merged. However, I don't think there are any core changes necessary to make it possible to implement this interface.

Entity-Component Editor Integration

The last part is that, with the data backend and the scripting API figured out, you need an editor GUI to provide a good user interface for the data backend. This can be done with an EditorInspectorPlugin (registered with an EditorPlugin script, also known as a "plugin" which is a special type of "addon"). The EditorInspectorPlugin has logic for adding custom control nodes to the EditorInspector, so you would just implement your own logic for the display and buttons of the "component collection" associated with the entity.

Your metadata/group concept takes advantage of the metadata editor plugin to get an out-of-the-box GUI and then does extra logic on the backend scripting side to ensure that the provided values match the intended interface.

Result

So, just as you demonstrated in your explanation, all of the necessary aspects of implementing a Unity-like API for users can be done in Godot via a plugin. None of it requires core changes or is beneficial enough to outweigh the associated maintenance cost of bloating the codebase. Therefore, implementing a core solution, or even a standalone module, likely wouldn't result in the code getting merged into the main repository.

What can be done?

With all that said, even if you did create a plugin to do everything, there is a persistent problem the engine causes for you that you would have to fight against, and that could warrant some engine changes to alleviate the friction. Namely, the fact that you want to achieve this functionality for any Node, not just a specific GameObject-like Node.

The only way to do that would be to give users the ability to attach a common set of functionality to multiple nodes in an easy way that merges the functionality into their class without subverting any of the core engine features or adding significant complexity to the engine. To abide by the latter conditions, it can't be something that affects the core Object/Script/Variant API. So instead, it has to be a language-specific feature where all "class merging" functionality is constrained to a specific language.

This is where the original justification of the GDScript Traits system came about. You could define a trait with a name, properties, methods, signals, and constants, and if any other class referenced the trait at the top of its file, it would effectively make the GDScript parser copy/paste the parse tree of the trait script into the currently parsed script. The only remaining logic necessary would then be identifying and reporting symbol collisions before proceeding with typical parsing. Then we'd figure out the syntax of how traits would work, implement it, work out any quirks with corner cases, and be done.

And yes, I would like traits. Other type system improvements too. Does that have chances of happening?

Yes, if there is a common need that users have that the engine doesn't have a reasonable solution for, then there is a very good chance that the engine will be accommodating of incorporating changes necessary to either resolve that need - if it can be done without inhibiting core maintenance - or otherwise resolving obstacles for addons to handle the need. Adding GDScript traits makes it easier for addons to deal with a variety of user concerns related to entity-component requests and other code duplication concerns without impacting how Godot internally works, so I find it very likely that it could be adopted. A proper proposal just needs to be written and someone has to allocate the time/effort to work on the feature (don't know if vnen already plans to or not).

If you have other type system improvements to recommend, I would recommend opening separate proposals for each of them.

Because, again, we manage. And we could always use C# or something else.

Since the issue has to do with the core Godot API, and since C# is a scripting language (can't affect core) that doesn't have a trait system, I don't understand how a different language would make any difference.

@theraot
Copy link

theraot commented Jun 5, 2021

I'm not quite picking up on what the implied "these" and "solution[s]" are, nor the nature of the specific "problems" you mention.

If I think of specific games, I don't think they need a solution that does everything, or at least I haven't thought of a game that would. Instead, I believe, a specific solution for the particular game would be simpler to implement, and probably could be optimized further for that game if necessary.

For example, in a strategy game it makes sense that units, buildings, and so would be scenes. Similarly, in a simulation game, smart objects would be scenes. Their components can be nodes, and they can take advantage of node groups.

For another example, items in an RPG inventory can be resources. Similarly party members or even specific moves in such RPG can be resources. They can have an array or a dictionary of sub-resources for components.


I don't understand how a different language would make any difference.

C# has interfaces. I haven't tried default implementations with Godot, if that works, it is an option. And other languages that already work with Godot have other useful features. For traits, Rust have them - Although I found it a pain to setup. For a particular game, that can be the difference.

However, I agree, that does not really address making these kind of addons easier to implement. Addons would use GDScript, as it makes it easier to bring into a project, and do not force the game developer into using some other language. Thus, I agree giving traits to GDScript is a better solution than suggesting other languages.

I like that Godot is easy to extend, and I hope it continues to improve on that path. I'd be in favor of any proposal that adds thing to the core that eases extension in useful ways, they have my virtually meaningless thumbs up. For example it makes sense that Camera3D only has the common projections, but where is the low level API for a custom one? There is a proposal.

@ka23ppa3
Copy link
Author

ka23ppa3 commented Jul 31, 2021

I'm not quite picking up on what the implied "these" and "solution[s]" are, nor the nature of the specific "problems" you mention.

If I think of specific games, I don't think they need a solution that does everything, or at least I haven't thought of a game that would. Instead, I believe, a specific solution for the particular game would be simpler to implement, and probably could be optimized further for that game if necessary.

For example, in a strategy game it makes sense that units, buildings, and so would be scenes. Similarly, in a simulation game, smart objects would be scenes. Their components can be nodes, and they can take advantage of node groups.

For another example, items in an RPG inventory can be resources. Similarly party members or even specific moves in such RPG can be resources. They can have an array or a dictionary of sub-resources for components.

I don't understand how a different language would make any difference.

C# has interfaces. I haven't tried default implementations with Godot, if that works, it is an option. And other languages that already work with Godot have other useful features. For traits, Rust have them - Although I found it a pain to setup. For a particular game, that can be the difference.

However, I agree, that does not really address making these kind of addons easier to implement. Addons would use GDScript, as it makes it easier to bring into a project, and do not force the game developer into using some other language. Thus, I agree giving traits to GDScript is a better solution than suggesting other languages.

I like that Godot is easy to extend, and I hope it continues to improve on that path. I'd be in favor of any proposal that adds thing to the core that eases extension in useful ways, they have my virtually meaningless thumbs up. For example it makes sense that Camera3D only has the common projections, but where is the low level API for a custom one? There is a proposal.

Wow, you just mentioned "interfaces in C#" as a some sort of solution? Please, no! Interfaces are absolutely unnecessary in terms of component-based system and I can add from my experience that interfaces should be avoided especially in this manner, because interfaces, in comparing to components, have no strict behavior, its just a signatures which can contains anything. You cant modify interface to change behavior as you can do it with components - this is totally useless. Also you easily getting the code which is doing anything and nothing at one time with interfaces. All my experience with interfaces show that it always was very limited temporary "solution" which was removed with time and replaced with components, where its needed - I can say that it was my only bad practices and choices, when I tried to put interfaces somewhere in games.

Interfaces seems to be used in object-based-programming as a "tags" for classes or "tags + signatures, to "mark & extend" them", but wait a minute: today you can do it with components. You can even create empty components or components with delegates(at least in C#) if you even REALLY need such cases. Component-based system is still like an old object-oriented paradigm but just with additional abilities, same as a components is a more mature version of old interfaces - they can do same job + more.

Did I missed something?
Thanks.

@theraot
Copy link

theraot commented Jul 31, 2021

@ka23ppa3

Interfaces are absolutely unnecessary in terms of component-based system

Correct.

By the way, I want traits, in GDSciript. If I look at the languages with official support in Godot, C# default interface method implementation is as close as it gets.

And yes, these are different things. For example, we could not add interfaces or traits at runtime. I'm not sure if you want to be able to add components at runtime, but presumably you could.

because interfaces, in comparing to components, have no strict behavior

You are correct again.

I understand you want guarantees.

While we are at it, let me tell you to stay away from GDScript. It does not even have private fields! Without private fields what prevents me from writing the fields of another object? Well, me. I stop my self. If GDScript had private fields, I would stop myself my marking the field private (and stopping myself from removing it). But since GDScript does not have private fields - we only have the naming convention that fields with a "_" prefix are to be considered private - I have no option but to stop myself.

Similarly, I would stop myself from replacing the default interface method implementation.

I might be giving the impression that I don't like static checking. I like static checking.

You cant modify interface to change behavior as you can do it with components - this is totally useless

You can modify the default method implementation in a single place. And assuming you didn't replace it with a different implementation in your classes, that would change the implementation for them too. And you wouldn't replace it if your plan was to use them this way.

All my experience with interfaces show that it always was very limited temporary "solution" which was removed with time and replaced with components, where its needed - I can say that it was my only bad practices and choices, when I tried to put interfaces somewhere in games.

I'll stop defending interfaces. They are not the point. I was arguing that other languages have tools that might be solutions for a particular game. And I felt compelled to mention C# interface default method implementation, because Godot has C# support. Alright, not the best idea, let us move on.

While Godot lacks a component system, every time we need one for a game, we have to come up with something to get it done. Sometimes that something will be nodes. Sometimes it will be metadata. Sometimes it will be object aggregation. Sometimes will be a singleton with dictionaries. Or it could be with other language features, let us say Rust traits this time. Or some combination of these. These are solutions for getting a game done, not solutions for Godot.

but wait a minute: today you can do it with components

Like going to another engine, or using some third party solution? What are we talking about? That sounds like we could close the proposal.


I do believe it is an hurdle for this proposal that we can get by without components, and that there are features that have similarity: metadata, children nodes, node groups… It would make sense to extend some of these features so they can play the role of components.

There are many way to implement a component system for a game. Let us say our main concern is behavior. Probably start with nodes, which have behavior. They also can have properties and manipulate each other. So we add them as children. We can go for the Unity feature list… If we make sure the name matches the type of components (and we can stop ourselves from doing it wrong), then we can add a component by type, remove it by type, check if a node has a component by type, get all the components a node has... We need a way to get all the components of the same type. We could have these components nodes register themselves to groups, so we can get them from there. This solution might not be good for all games. But it is just one one way to do it.

Since we can work around this limitation, I find it hard to argue that Godot needs a component solution.

@willnationsdev
Copy link
Contributor

willnationsdev commented Jul 31, 2021

With Godot 4 introducing the GDNative Extensions API, I could see a separately implemented extension that adds Unity-like constructs to appease users who feel like they can't organize things the way they want to without a component-based architecture. And then it would all feel "built-in" so long as they have the extension present in their project since the classes all show up as C++ types in the engine rather than as scripted types.

Also...

Component-based system is still like an old object-oriented paradigm but just with additional abilities, same as a components is a more mature version of old interfaces - they can do same job + more.

Did I missed something?

Yes, there is a very strong distinction between interfaces and components in this regard. Interfaces are an integrated part of a language's type system and so the data can be checked statically at compile-time by the C/C++ code that evaluates the language. In comparison, component APIs that perform similar checks are things that evaluate serialized data structures with additional tooling. You have to actually compile the language and run it while the Editor is still operational in order to have it tell you that something is wrong. And generally speaking, these are things that are processed in the scripted runtime, not in the high-performance engine code.

The same distinction is also present when referring to the presence of a method implementation on a conceptual class. An interface demands that a particular implementation exists and a trait enforces the presence of a particular implementation by merging it in. In contrast, a component can't demand that a given conceptual "class" has an implementation except by having pre-compiled editor tooling evaluate the serialized data structures in the scripted runtime and perform a manual check that a delegate has been assigned a value statically (if that's even possible).

By and large, when it comes to enforcing that a particular class contract is met, or that it is met in a particular way, components are inferior to interfaces/traits in regards to performance (uses engine/compiler code) and reliability (evaluates serialized data structures, so relies on data structures being formatted correctly. Also relies on editor tooling existing for the check that is needed).

When it comes to enforcing that a particular structural contract is met, or that it is met in a particular way, components are superior to interfaces/traits, specifically because they rely on scripting and editor tooling. They may perform checks more slowly, be less reliable, and require more work to setup, but the fact that they are customizable by users allows their static checks to be expanded upon.

Both methods are useful for different purposes. But as theraot said, a component API does not have any need to be integrated into the core engine. Addons can and should be used to create and support component structures and their associated editor tooling. There's simply no reason to build it into the core engine when absolutely none of the core has any need for that functionality.

@Calinou
Copy link
Member

Calinou commented Sep 1, 2021

Closing due to lack of support (see reactions on the first post).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants