question about more appropriate code structure for optimization

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Inces

Recently I thought of different code structure for my project and I started implementing it, but now i have doubts about it eventual drawbacks.

I wanted to avoid amassing enormous amounts of script and scene files and instead I place all functions for all situations and objects in one script for whole class. For example there is a node SpellMenager and it has like 50 functions named after spells, that are being called using signals

on_signalled_cast(spellname,owner,place):
           call(spellname)

the same goes for all projectiles, status effects and so on : they all have one script, that will pick behavior based on projectiles name.

func on_timer_tick():
      call(name,power)
func poison(power):
          deal power * damage

This code structure means a lot of objects in the scene with large shared script, that only use a small piece of their total script at a time. Thanks to it my project requires less space and there is more order in files, while code still has great elasticity

Is it really a good choice optimization-wise ? What do I loose because of this ?

:bust_in_silhouette: Reply From: gnumaru

Your exact question was “Is it really a good choice optimization-wise? What do I loose because of this?”. But please hear me out until I get there.


A large quantity of files is, by itself alone, not a problem. Loosing track of what exists in your project due to lack of organization or information gathering techniques and doing duplicated rework because of it is the real problem. And even loosing track of things is not a game related problem, it is only a developer workflow problem. Ideally one should not solve a workflow problem by penalizing or constraining the game (but an indie solo dev life teaches the opposite is often required =P)

Using command line tools one can easily gather quick information about the project. With this:

find . -iname "*.gd" | wc -l

You can see how many scripts you project has. And with this:

find . -iname "*.gd" | sort

you can list them all. If they have good, descriptive and meaningful names, you can also remember a lot just by looking at the list. The same can be made with any kind of file by replacing “.gd" with any other file extension like ".tres”, “.tscn", ".gdshader” and so on.

You could also list all functions in a file with this:

grep -ni '^\(static\|func\)' ./Utils.gd | sort

Or all functions in the whole project with this:

grep -rni '^\(static\|func\)' ./ | sort

The same applies for counting functions instead of listing:

grep -ni '^\(static\|func\)' ./Utils.gd | wc -l
grep -rni '^\(static\|func\)' ./ | wc -l

Assuming that your functions have good, descriptive and meaningful names and parameters, just looking at the signatures is enough to remember a lot even if it’s been years since you last used them. If you also create a comment documenting the function behavior at the end of the same line as the function signature, those greps alone will get you not only the signature, but also that comment, which is all the info you need assuming your description is complete and concise.


But back to your questions. There’s the optimization one and the ‘what I’m loosing’ one.

About optimization, there are a lot of different scenarios: on some you need to save ram, on other you need to save network, and on other you would save processing and so on. You cannot expect to save everything, you should prioritize.

Assuming you prioritize saving cpu cycles, having 100 instances of 100 different scripts or the same script doesn’t make a difference, what matter is what is being processed each frame on _process or _physics_process, or on ‘coroutines’ that process every frame or several times a second. So I think your new approach doesn’t penalize cpu by itself.

About what you’re losing, I think enforcing a give pattern or structure to a project because it seems a better general solution may not always be the best option, even more on game related projects where the kind of problems to solve are always very broad.

The thing is that “general solutions solves general problems”, but in reality general problems doesn’t exist, what exist are several specific problems that may or may not have varying degrees of similitude. In one specific scenario it could be best to use a script built into the node with very specific behavior, in other scenario you could use a script saved as it’s own file that can be used by other objects. In other scenario you would connect signals on the editor and on other you would do it via script. In other scenario you could use heavily OOP principles and class inheritance and in others you would make an Utils singleton with a bunch of small static functions and make things without creating barely any class.

What I’m saying is that I think it is best to cater the solution to the specific problem. Maybe your specific problem was a workflow one, so the specific solution would be related to altering your workflow, not the game. And even if you new code structure and patterns prove very good to you, you should not refrain from escaping from the pattern at times when it proves better to the specific problem you may be facing at that time.

I hope i haven’t solved your problem, but rather broadened your view on the problem =)

Ps: Even on windows you can use the usual linux command line tools through git bash, msys2 or with windows subsystem for linux (windows 10/11 only)

gnumaru | 2021-10-27 09:26

Thank You so much, this answer gave all than I asked and all I would ask in the future :slight_smile:
It is true, organization was always my problem and I intended to counteract it with locking myself to one chosen pattern per project, to remember my own sollutions, to reduce chaos. In my former project I had multitude of scenes and scripts, but I named them inconsistently and it became pain :). I am sure those methods of listing will save me a lot of frustration

Inces | 2021-10-27 09:46

I’m glad I could provide any help =).

Just by using bash and unix cli tools one can extract a lot of information from many places. Besides the commands I mentioned (find, grep, sort, wc) take a look at all these other ones: cat, tac, head, tail, uniq, nl, tr, cut, sed, awk, xargs, less. Beyond that, shell scripting in general is really helpfull, but for more complex needs it would be better to use other scripting languages like python, javascript (with nodejs) and others.

Also, getting familiar with glob patterns (that “*.gd” thing) and regex (that ‘^(static|func)’ thing) is always pretty helpful in a lot of situations. You can take a look at these for more info on regex:

https://regexr.com/

https://regexone.com/

My last tip would be to be lazy. Only create and implement things the moment you need them, and only if you really need them. Implementing things in advance because they may be usefull someday is the root of all evil =P.

gnumaru | 2021-10-27 12:35

Hehe that is interesting, I strongly believed in implementing in advance. Because of this my workflow is kind of slow, always trying to design code beforehand in a way to not barrier eventual future needs . This is what happened with my former project : after all was done I had no way to implement tutorial, because of how stiff this code was. I felt like i built a house without a cellar :).

Inces | 2021-10-27 14:44