How do you organize your projects?

Hi everyone! I recently ran into a problem.

We participated in Ludum Dare and decided to expand our project a bit, polish it, and bring it to a more solid release state outside the Ludum Dare scope.

That’s where project management became an issue. During the jam, we obviously just threw everything together in one place. Now we want to work in a more structured and convenient way. I’ve already tried a bunch of project management tools, but I still haven’t found anything that really fits our needs.

Ideally, we’d like to stay within the open-source ecosystem and build our entire pipeline on free software.

As I understand game development, we need:

  • A place to store assets separately from the code
  • A way to write and manage documentation
  • A version control system for the code
  • A way to manage the project itself (like a Kanban board)

While testing, I tried Plane, AppFlowy, Obsidian, Affine, and several other similar tools. In the end, none of them felt suitable for long-term use. That’s why I came here.

How do you organize your projects? I can’t seem to design a pipeline that works comfortably for everyone. I’d like artists to interact with the code as little as possible, have a convenient way to write documentation, and let programmers stay within their own workflow.

Right now, it feels almost impossible to achieve, but maybe you’ve found some working solutions.

Please share how you organize your projects.

Sorry if this sounds a bit messy — my English isn’t very good, so I translated this using an AI.

I recommend you checkout GitLab and GitHub. They offer all of those things for free if your software is open source. However, if you’re making a game you’re going to sell, you may have to pay. Although for small teams, you can still find things for free.

First question is - why?

All your assets need to be in your game. Just make a separate assets folder and let your artists and sound people add stuff there. It can all be versioned in your version control system.

You can create a docs folder and put all your documentation in it. Even if you use an external tool to create it, you can still check it into your project. For example, check out GDUnit4’s Docs.

GitHub or GitLab, but based on your desire for project management, I suggest GitLab.

Both GitLab and GitHub have the ability to do project management and Kanban boards. They also both integrate well into Jira, which is kinda FOSS. It used to be FOSS, and now it’s free for small groups.


Personally, I like GitHub for most things, but for the past 6 months or so I’ve been using GitLab for project management for a Godot game, and I am liking it a lot.

3 Likes

(Google translator)
I’m no expert, but I think this is an interesting post, so I’d appreciate any corrections.

I structure my projects similarly to how they appear in my scene hierarchy.

I prefer to keep the assets for each scene in the same directories as the rest of the scene’s resources. The reason is that it’s easier to always search in the same place, and it’s also easier to edit the resources and save them in the same location.

If I don’t do it this way, I waste a lot of time searching through directories or importing resources into other final directories. It’s madness for me.

Last week, in fact, I asked how to prevent Godot from importing certain resources just to keep them with the code and prevent certain Blender files from being imported continuously.

Having the resources outside my project means I end up not knowing where I put them.

For me, it’s important not to create superfluous directories.

Version control. In my case, I work on my projects alone, using Git with Gitea installed on another computer I have at home. I use GitHub Desktop as my client. Obviously, if you work with other people, it makes sense to use GitHub or GitLab. For now, I don’t want Microsoft to have access to my code to share it with others through their AI.

I also use Obsidian locally to document my work.

Everything is within the same Git repository.

I think it is important to be able to work with the original resources. If, for example, I’m working with Aseprite files, I prefer to have Aseprite Wizard installed so I can work directly with them and not lose my work when exporting to PNGs.

2 Likes

Thanks for help! I decided to self host gitlab. About your questions:

We’re working in a team of 7 people, 3 of them are artist, and they really don’t like working with git. I tried to do something with it, or to make it easier for them in someway, but after all it all ended up by sending assets with telegram. After 3 month of work it’s just a trashcan full of used and unused assets, concepts. One of our gds finally made a table with all this assets, but I think it would be easier if we would use something like google disk from the beginning. Idk how it works in gamedev, but in our team artist are having problems with git

I did it like this when I worked on game for LD, but when project grows bigger it’s getting hard to work with this kind of docs. Like, we’ve got 7 pages of docs + balance tables right now. We just can’t store all of this in markdown inside of the project. So, we decided to separate this part to.

I like Jira to but I can’t use it in my country. Mostly that’s why I decided to try something open source, because this projects can be self-hosted. Also, we are just college students with very different skill levels, so I am trying to figure out how to organize work in a gamedev team properly. Maybe my approach is not ideal, but I am trying to find a workflow that is simple enough for our team to actually use.

After all, we’ll try self hosted gitlab (the only way in my situation), I think I’ll try to search for some kind of web documentation engine to use too and file storage for asset management.

Thanks for you’re comment! Never seen this project that you linked before, I’ll try it.

Upd: Don’t like github too, after their started learning AI on opensource projects.

1 Like

Oh, I’m using another way. I’m storing everything separately and using quick search for using code and assets in scenes, never tried to do it this way. The other things are the same is yours, I really love working with obsidian, but it works very bad for team knowledge base. We tried to use this project, but Obsidian is still personal knowledge base and we had many conflicts. Migrated to google docs temporally. Also as I understand Aseprite Wizzard is still using png, it just compiles pngs in the background. Correct me if I’m wrong.

Thanks for you comment!

Just today I watched this video by Theo, where he compares GitLab with Forgejo. I would second his conclusion, that I would prefer Forgejo over GitLab for selfhosting.(and maybe Codeberg over GitLab, too, the hosting alternatives for OSS projects).

EDIT:
Forgejo at ~22:25

2 Likes

I’d never touch github with any of my personal code that I care about. Gitlab, only as an exception for small things that someone else might need access to. I just set up repos and any needed management tools on my website.

2 Likes

I think I read a news article about the Netherlands migrating their code to Forgejo.
I currently use Gitea, but I’ll look into Forgejo.

2 Likes

thou i’m new godot dev i was programming in various languages over the years, and in every one of them, no matter if i just changed minecraft’s crafting with zen-script or programmer controllers with C++, i have a system that never failed me:

  1. Make something work and have no bugs and no edge-cases
  2. If you ever need to change something and after 5 minutes of work you see you’re iritated, stop and ask why you’re iritated - then if it’s cause the code is messy then →
  3. Look at what you need to change and think how often you’ll need to change this
    A → if you just need to use it once, or must know it for later, comment it heavily, explain it in simple language
    B → if you need to change this code cuz it’s very important but you develop it, do simple refactor:
  • Change variable names to readable
  • Put things into smaller functions
  • Comment if something is hard to understand by just looking at it

C → if you know you might want to change it later, but now you don’t know - just comment that like “## This code might be changed, refactor if it causes problems”

  1. Do the refactor based off situation

Also i have few rules that are easy to use, but remove 90% of “i need to refactor this” or organization issues:

  • Use single casing that have no more than 3 rules, the more rules you add, the harder it is to use & learn by other co-devs

  • Always name variables in redable way, not just magic words that mean nothing or worse, mean something different that reader might misunderstand

  • Use single language over your scripts (usually english, even if you’re not native speaker and you’re not very good at it)

  • If something is longer than few lines → put this into a function

  • Avoid nesting by inverting ifs, by that i mean use

```
if !condition:
return

#Logic

```

Also don’t worry about organization that much, your goal is to make a game, not to make perfect code, as long as you’ll keep it simple & clear to understand without much hassle, you should have no problems with coding

2 Likes

At first I thought you were being funny.

You always make bugs. the first day I was at a company, I bet a developer $1 that I could find a bug in his code. I framed that $1.

3 Likes

by that i mean fix most serious bugs and edge-cases when your code is terrible, because many of them will be harder to fix if you had organization, or they’ll break this organization

if you know what to fix - then creating clean code around it is easier

If it works, don’t touch it

That should have been at least a $100 bet.

1 Like

How do I measure the cleanliness of my code?

1 Like

can you read it and understand it without spending 10 or more seconds? can others understand it? then it’s clear, that’s all

What’s the amount I’m supposed to be able to read (and understand) in 10 seconds?

1 Like
/**************************************************************************/
/*  native_menu.cpp                                                       */
/**************************************************************************/
/*                         This file is part of:                          */
/*                             GODOT ENGINE                               */
/*                        https://godotengine.org                         */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
/*                                                                        */
/* Permission is hereby granted, free of charge, to any person obtaining  */
/* a copy of this software and associated documentation files (the        */
/* "Software"), to deal in the Software without restriction, including    */
/* without limitation the rights to use, copy, modify, merge, publish,    */
/* distribute, sublicense, and/or sell copies of the Software, and to     */
/* permit persons to whom the Software is furnished to do so, subject to  */
/* the following conditions:                                              */
/*                                                                        */
/* The above copyright notice and this permission notice shall be         */
/* included in all copies or substantial portions of the Software.        */
/*                                                                        */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
/**************************************************************************/

#include "native_menu.h"

#include "core/object/class_db.h"
#include "scene/resources/texture.h"

NativeMenu *NativeMenu::singleton = nullptr;

void NativeMenu::_bind_methods() {
	ClassDB::bind_method(D_METHOD("has_feature", "feature"), &NativeMenu::has_feature);

	ClassDB::bind_method(D_METHOD("has_system_menu", "menu_id"), &NativeMenu::has_system_menu);
	ClassDB::bind_method(D_METHOD("get_system_menu", "menu_id"), &NativeMenu::get_system_menu);
	ClassDB::bind_method(D_METHOD("get_system_menu_name", "menu_id"), &NativeMenu::get_system_menu_name);

	ClassDB::bind_method(D_METHOD("get_system_menu_text", "menu_id"), &NativeMenu::get_system_menu_text);
	ClassDB::bind_method(D_METHOD("set_system_menu_text", "menu_id", "name"), &NativeMenu::set_system_menu_text);

	ClassDB::bind_method(D_METHOD("create_menu"), &NativeMenu::create_menu);
	ClassDB::bind_method(D_METHOD("has_menu", "rid"), &NativeMenu::has_menu);
	ClassDB::bind_method(D_METHOD("free_menu", "rid"), &NativeMenu::free_menu);

	ClassDB::bind_method(D_METHOD("get_size", "rid"), &NativeMenu::get_size);
	ClassDB::bind_method(D_METHOD("popup", "rid", "position"), &NativeMenu::popup);

	ClassDB::bind_method(D_METHOD("set_interface_direction", "rid", "is_rtl"), &NativeMenu::set_interface_direction);
	ClassDB::bind_method(D_METHOD("set_popup_open_callback", "rid", "callback"), &NativeMenu::set_popup_open_callback);
	ClassDB::bind_method(D_METHOD("get_popup_open_callback", "rid"), &NativeMenu::get_popup_open_callback);
	ClassDB::bind_method(D_METHOD("set_popup_close_callback", "rid", "callback"), &NativeMenu::set_popup_close_callback);
	ClassDB::bind_method(D_METHOD("get_popup_close_callback", "rid"), &NativeMenu::get_popup_close_callback);
	ClassDB::bind_method(D_METHOD("set_minimum_width", "rid", "width"), &NativeMenu::set_minimum_width);
	ClassDB::bind_method(D_METHOD("get_minimum_width", "rid"), &NativeMenu::get_minimum_width);

	ClassDB::bind_method(D_METHOD("is_opened", "rid"), &NativeMenu::is_opened);

	ClassDB::bind_method(D_METHOD("add_submenu_item", "rid", "label", "submenu_rid", "tag", "index"), &NativeMenu::add_submenu_item, DEFVAL(Variant()), DEFVAL(-1));
	ClassDB::bind_method(D_METHOD("add_item", "rid", "label", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
	ClassDB::bind_method(D_METHOD("add_check_item", "rid", "label", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_check_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
	ClassDB::bind_method(D_METHOD("add_icon_item", "rid", "icon", "label", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_icon_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
	ClassDB::bind_method(D_METHOD("add_icon_check_item", "rid", "icon", "label", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_icon_check_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
	ClassDB::bind_method(D_METHOD("add_radio_check_item", "rid", "label", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_radio_check_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
	ClassDB::bind_method(D_METHOD("add_icon_radio_check_item", "rid", "icon", "label", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_icon_radio_check_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
	ClassDB::bind_method(D_METHOD("add_multistate_item", "rid", "label", "max_states", "default_state", "callback", "key_callback", "tag", "accelerator", "index"), &NativeMenu::add_multistate_item, DEFVAL(Callable()), DEFVAL(Callable()), DEFVAL(Variant()), DEFVAL(Key::NONE), DEFVAL(-1));
	ClassDB::bind_method(D_METHOD("add_separator", "rid", "index"), &NativeMenu::add_separator, DEFVAL(-1));

	ClassDB::bind_method(D_METHOD("find_item_index_with_text", "rid", "text"), &NativeMenu::find_item_index_with_text);
	ClassDB::bind_method(D_METHOD("find_item_index_with_tag", "rid", "tag"), &NativeMenu::find_item_index_with_tag);
	ClassDB::bind_method(D_METHOD("find_item_index_with_submenu", "rid", "submenu_rid"), &NativeMenu::find_item_index_with_submenu);

	ClassDB::bind_method(D_METHOD("is_item_checked", "rid", "idx"), &NativeMenu::is_item_checked);
	ClassDB::bind_method(D_METHOD("is_item_checkable", "rid", "idx"), &NativeMenu::is_item_checkable);
	ClassDB::bind_method(D_METHOD("is_item_radio_checkable", "rid", "idx"), &NativeMenu::is_item_radio_checkable);
	ClassDB::bind_method(D_METHOD("get_item_callback", "rid", "idx"), &NativeMenu::get_item_callback);
	ClassDB::bind_method(D_METHOD("get_item_key_callback", "rid", "idx"), &NativeMenu::get_item_key_callback);
	ClassDB::bind_method(D_METHOD("get_item_tag", "rid", "idx"), &NativeMenu::get_item_tag);
	ClassDB::bind_method(D_METHOD("get_item_text", "rid", "idx"), &NativeMenu::get_item_text);
	ClassDB::bind_method(D_METHOD("get_item_submenu", "rid", "idx"), &NativeMenu::get_item_submenu);
	ClassDB::bind_method(D_METHOD("get_item_accelerator", "rid", "idx"), &NativeMenu::get_item_accelerator);
	ClassDB::bind_method(D_METHOD("is_item_disabled", "rid", "idx"), &NativeMenu::is_item_disabled);
	ClassDB::bind_method(D_METHOD("is_item_hidden", "rid", "idx"), &NativeMenu::is_item_hidden);
	ClassDB::bind_method(D_METHOD("get_item_tooltip", "rid", "idx"), &NativeMenu::get_item_tooltip);
	ClassDB::bind_method(D_METHOD("get_item_state", "rid", "idx"), &NativeMenu::get_item_state);
	ClassDB::bind_method(D_METHOD("get_item_max_states", "rid", "idx"), &NativeMenu::get_item_max_states);
	ClassDB::bind_method(D_METHOD("get_item_icon", "rid", "idx"), &NativeMenu::get_item_icon);
	ClassDB::bind_method(D_METHOD("get_item_indentation_level", "rid", "idx"), &NativeMenu::get_item_indentation_level);

	ClassDB::bind_method(D_METHOD("set_item_checked", "rid", "idx", "checked"), &NativeMenu::set_item_checked);
	ClassDB::bind_method(D_METHOD("set_item_checkable", "rid", "idx", "checkable"), &NativeMenu::set_item_checkable);
	ClassDB::bind_method(D_METHOD("set_item_radio_checkable", "rid", "idx", "checkable"), &NativeMenu::set_item_radio_checkable);
	ClassDB::bind_method(D_METHOD("set_item_callback", "rid", "idx", "callback"), &NativeMenu::set_item_callback);
	ClassDB::bind_method(D_METHOD("set_item_hover_callbacks", "rid", "idx", "callback"), &NativeMenu::set_item_hover_callbacks);
	ClassDB::bind_method(D_METHOD("set_item_key_callback", "rid", "idx", "key_callback"), &NativeMenu::set_item_key_callback);
	ClassDB::bind_method(D_METHOD("set_item_tag", "rid", "idx", "tag"), &NativeMenu::set_item_tag);
	ClassDB::bind_method(D_METHOD("set_item_text", "rid", "idx", "text"), &NativeMenu::set_item_text);
	ClassDB::bind_method(D_METHOD("set_item_submenu", "rid", "idx", "submenu_rid"), &NativeMenu::set_item_submenu);
	ClassDB::bind_method(D_METHOD("set_item_accelerator", "rid", "idx", "keycode"), &NativeMenu::set_item_accelerator);
	ClassDB::bind_method(D_METHOD("set_item_disabled", "rid", "idx", "disabled"), &NativeMenu::set_item_disabled);
	ClassDB::bind_method(D_METHOD("set_item_hidden", "rid", "idx", "hidden"), &NativeMenu::set_item_hidden);
	ClassDB::bind_method(D_METHOD("set_item_tooltip", "rid", "idx", "tooltip"), &NativeMenu::set_item_tooltip);
	ClassDB::bind_method(D_METHOD("set_item_state", "rid", "idx", "state"), &NativeMenu::set_item_state);
	ClassDB::bind_method(D_METHOD("set_item_max_states", "rid", "idx", "max_states"), &NativeMenu::set_item_max_states);
	ClassDB::bind_method(D_METHOD("set_item_icon", "rid", "idx", "icon"), &NativeMenu::set_item_icon);
	ClassDB::bind_method(D_METHOD("set_item_indentation_level", "rid", "idx", "level"), &NativeMenu::set_item_indentation_level);
	ClassDB::bind_method(D_METHOD("set_item_index", "rid", "idx", "target_idx"), &NativeMenu::set_item_index);

	ClassDB::bind_method(D_METHOD("get_item_count", "rid"), &NativeMenu::get_item_count);
	ClassDB::bind_method(D_METHOD("is_system_menu", "rid"), &NativeMenu::is_system_menu);

	ClassDB::bind_method(D_METHOD("remove_item", "rid", "idx"), &NativeMenu::remove_item);
	ClassDB::bind_method(D_METHOD("clear", "rid"), &NativeMenu::clear);

	BIND_ENUM_CONSTANT(FEATURE_GLOBAL_MENU);
	BIND_ENUM_CONSTANT(FEATURE_POPUP_MENU);
	BIND_ENUM_CONSTANT(FEATURE_OPEN_CLOSE_CALLBACK);
	BIND_ENUM_CONSTANT(FEATURE_HOVER_CALLBACK);
	BIND_ENUM_CONSTANT(FEATURE_KEY_CALLBACK);

	BIND_ENUM_CONSTANT(INVALID_MENU_ID);
	BIND_ENUM_CONSTANT(MAIN_MENU_ID);
	BIND_ENUM_CONSTANT(APPLICATION_MENU_ID);
	BIND_ENUM_CONSTANT(WINDOW_MENU_ID);
	BIND_ENUM_CONSTANT(HELP_MENU_ID);
	BIND_ENUM_CONSTANT(DOCK_MENU_ID);
}

bool NativeMenu::has_feature(Feature p_feature) const {
	return false;
}

bool NativeMenu::has_system_menu(SystemMenus p_menu_id) const {
	return false;
}

RID NativeMenu::get_system_menu(SystemMenus p_menu_id) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return RID();
}

String NativeMenu::get_system_menu_name(SystemMenus p_menu_id) const {
	switch (p_menu_id) {
		case MAIN_MENU_ID:
			return "Main menu";
		case APPLICATION_MENU_ID:
			return "Application menu";
		case WINDOW_MENU_ID:
			return "Window menu";
		case HELP_MENU_ID:
			return "Help menu";
		case DOCK_MENU_ID:
			return "Dock menu";
		default:
			return "Invalid";
	}
}

String NativeMenu::get_system_menu_text(SystemMenus p_menu_id) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return String();
}

void NativeMenu::set_system_menu_text(SystemMenus p_menu_id, const String &p_name) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

RID NativeMenu::create_menu() {
	WARN_PRINT("Global menus are not supported on this platform.");
	return RID();
}

bool NativeMenu::has_menu(const RID &p_rid) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return false;
}

void NativeMenu::free_menu(const RID &p_rid) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

Size2 NativeMenu::get_size(const RID &p_rid) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return Size2();
}

void NativeMenu::popup(const RID &p_rid, const Vector2i &p_position) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

void NativeMenu::set_interface_direction(const RID &p_rid, bool p_is_rtl) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

void NativeMenu::set_popup_open_callback(const RID &p_rid, const Callable &p_callback) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

Callable NativeMenu::get_popup_open_callback(const RID &p_rid) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return Callable();
}

void NativeMenu::set_popup_close_callback(const RID &p_rid, const Callable &p_callback) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

Callable NativeMenu::get_popup_close_callback(const RID &p_rid) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return Callable();
}

bool NativeMenu::is_opened(const RID &p_rid) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return false;
}

void NativeMenu::set_minimum_width(const RID &p_rid, float p_width) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

float NativeMenu::get_minimum_width(const RID &p_rid) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return 0.f;
}

int NativeMenu::add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag, int p_index) {
	WARN_PRINT("Global menus are not supported on this platform.");
	return -1;
}

int NativeMenu::add_item(const RID &p_rid, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) {
	WARN_PRINT("Global menus are not supported on this platform.");
	return -1;
}

int NativeMenu::add_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) {
	WARN_PRINT("Global menus are not supported on this platform.");
	return -1;
}

int NativeMenu::add_icon_item(const RID &p_rid, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) {
	WARN_PRINT("Global menus are not supported on this platform.");
	return -1;
}

int NativeMenu::add_icon_check_item(const RID &p_rid, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) {
	WARN_PRINT("Global menus are not supported on this platform.");
	return -1;
}

int NativeMenu::add_radio_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) {
	WARN_PRINT("Global menus are not supported on this platform.");
	return -1;
}

int NativeMenu::add_icon_radio_check_item(const RID &p_rid, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) {
	WARN_PRINT("Global menus are not supported on this platform.");
	return -1;
}

int NativeMenu::add_multistate_item(const RID &p_rid, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) {
	WARN_PRINT("Global menus are not supported on this platform.");
	return -1;
}

int NativeMenu::add_separator(const RID &p_rid, int p_index) {
	WARN_PRINT("Global menus are not supported on this platform.");
	return -1;
}

int NativeMenu::find_item_index_with_text(const RID &p_rid, const String &p_text) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return -1;
}

int NativeMenu::find_item_index_with_tag(const RID &p_rid, const Variant &p_tag) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return -1;
}

int NativeMenu::find_item_index_with_submenu(const RID &p_rid, const RID &p_submenu_rid) const {
	if (!has_menu(p_rid) || !has_menu(p_submenu_rid)) {
		return -1;
	}
	int count = get_item_count(p_rid);
	for (int i = 0; i < count; i++) {
		if (p_submenu_rid == get_item_submenu(p_rid, i)) {
			return i;
		}
	}
	return -1;
}

bool NativeMenu::is_item_checked(const RID &p_rid, int p_idx) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return false;
}

bool NativeMenu::is_item_checkable(const RID &p_rid, int p_idx) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return false;
}

bool NativeMenu::is_item_radio_checkable(const RID &p_rid, int p_idx) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return false;
}

Callable NativeMenu::get_item_callback(const RID &p_rid, int p_idx) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return Callable();
}

Callable NativeMenu::get_item_key_callback(const RID &p_rid, int p_idx) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return Callable();
}

Variant NativeMenu::get_item_tag(const RID &p_rid, int p_idx) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return Variant();
}

String NativeMenu::get_item_text(const RID &p_rid, int p_idx) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return String();
}

RID NativeMenu::get_item_submenu(const RID &p_rid, int p_idx) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return RID();
}

Key NativeMenu::get_item_accelerator(const RID &p_rid, int p_idx) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return Key::NONE;
}

bool NativeMenu::is_item_disabled(const RID &p_rid, int p_idx) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return false;
}

bool NativeMenu::is_item_hidden(const RID &p_rid, int p_idx) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return false;
}

String NativeMenu::get_item_tooltip(const RID &p_rid, int p_idx) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return String();
}

int NativeMenu::get_item_state(const RID &p_rid, int p_idx) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return -1;
}

int NativeMenu::get_item_max_states(const RID &p_rid, int p_idx) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return -1;
}

Ref<Texture2D> NativeMenu::get_item_icon(const RID &p_rid, int p_idx) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return Ref<Texture2D>();
}

int NativeMenu::get_item_indentation_level(const RID &p_rid, int p_idx) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return 0;
}

void NativeMenu::set_item_checked(const RID &p_rid, int p_idx, bool p_checked) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

void NativeMenu::set_item_checkable(const RID &p_rid, int p_idx, bool p_checkable) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

void NativeMenu::set_item_radio_checkable(const RID &p_rid, int p_idx, bool p_checkable) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

void NativeMenu::set_item_callback(const RID &p_rid, int p_idx, const Callable &p_callback) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

void NativeMenu::set_item_key_callback(const RID &p_rid, int p_idx, const Callable &p_key_callback) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

void NativeMenu::set_item_hover_callbacks(const RID &p_rid, int p_idx, const Callable &p_callback) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

void NativeMenu::set_item_tag(const RID &p_rid, int p_idx, const Variant &p_tag) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

void NativeMenu::set_item_text(const RID &p_rid, int p_idx, const String &p_text) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

void NativeMenu::set_item_submenu(const RID &p_rid, int p_idx, const RID &p_submenu_rid) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

void NativeMenu::set_item_accelerator(const RID &p_rid, int p_idx, Key p_keycode) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

void NativeMenu::set_item_disabled(const RID &p_rid, int p_idx, bool p_disabled) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

void NativeMenu::set_item_hidden(const RID &p_rid, int p_idx, bool p_hidden) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

void NativeMenu::set_item_tooltip(const RID &p_rid, int p_idx, const String &p_tooltip) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

void NativeMenu::set_item_state(const RID &p_rid, int p_idx, int p_state) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

void NativeMenu::set_item_max_states(const RID &p_rid, int p_idx, int p_max_states) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

void NativeMenu::set_item_icon(const RID &p_rid, int p_idx, const Ref<Texture2D> &p_icon) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

void NativeMenu::set_item_indentation_level(const RID &p_rid, int p_idx, int p_level) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

int NativeMenu::set_item_index(const RID &p_rid, int p_idx, int p_target_idx) {
	WARN_PRINT("Global menus are not supported on this platform.");
	return -1;
}

int NativeMenu::get_item_count(const RID &p_rid) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return 0;
}

bool NativeMenu::is_system_menu(const RID &p_rid) const {
	WARN_PRINT("Global menus are not supported on this platform.");
	return false;
}

void NativeMenu::remove_item(const RID &p_rid, int p_idx) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

void NativeMenu::clear(const RID &p_rid) {
	WARN_PRINT("Global menus are not supported on this platform.");
}

Ehhh, Godot got bad code! can’t read it in 10 seconds.
That’s not even the code! i can’t find a file under 32000 chars(forum limit)

just kidding btw

1 Like

about the fragment that does what you need, it’s pretty much individual preference, but for me it’s about a 10-20 lines

1 Like

If it’s individual preference then it follows that determining if code is clean is individual judgement. Which makes it completely useless as an objective metric that can be shared between team members.

2 Likes

really, objective metrics aren’t that good for coding, not that strict, as i’ve said, if you can read cleanly 10-20 lines, average developer with about year of experience should be able to do this too, that’s bare minimum

I say it’s personal because idk your team, idk if your team have someone who is slower at reading code, or maybe you are all lighting speed

the best advice i could give is use average or minimum, also listen if any member of your team is complaining, and then ask why are they complayning, then you might find best method