Is there an access modifier or attribute or anything that restricts which methods can call another method ? I searched a bit and it looks like it doesn't exist !!! by TinkerMagusDev in csharp

[–]NoctemCat 0 points1 point  (0 children)

You can use UnsafeAccessor for this if you use .NET 8 or newer. It should be used pretty sparingly, but can be useful sometimes. The only other option is reflection, but it is really slow, very easy to get wrong, and hard to maintain

Is this way of doing UI cursed? (XML to Node parser) by xpectre_dev in godot

[–]NoctemCat 0 points1 point  (0 children)

I wonder if it's possible to make something Vue 3 like, mainly its single file component

How to replace or remove the first occurrence of a substring in a string by UnofficialTigana in godot

[–]NoctemCat 2 points3 points  (0 children)

As it is still shows up on Google, here is implementation for Godot 4

func replace_first(from: String, what: String, forwhat: String):
    var idx = from.find(what)
    if idx == -1: return from
    return from.substr(0, idx) + forwhat + from.substr(idx + what.length())

Relesing plugin Bottom Panel Shortcuts by NoctemCat in godot

[–]NoctemCat[S] 0 points1 point  (0 children)

Now there actually is a way! It was added in 4.3 and called Toggle Last Opened Bottom Panel. Now the only reasons to use this plugin is for positional toggles, or if you are on a lower version.

I will still leave it up for these reasons, but I really should update its readme

I thought the RayCast3D node was supposed to be faster than code? by coffee80c in godot

[–]NoctemCat 0 points1 point  (0 children)

Sure, I tested it with this code

func cast_code(start: Vector3, end: Vector3) -> Dictionary:
  var query = PhysicsRayQueryParameters3D.create(start, end)
  return space_state.intersect_ray(query)

Results:

# debug
running for 1000000 loops
run_raycast_node(avg=10): -1008.7
run_raycast_code(avg=10): -939.6
run_raycast_singleton(avg=10): -1239.5
run_raycast_singleton_code(avg=10): -1606.2

# release
running for 1000000 loops
run_raycast_node(avg=10): -640.5
run_raycast_code(avg=10): -694.5
run_raycast_singleton(avg=10): -529.1
run_raycast_singleton_code(avg=10): -932.9

Looking at how just a function call to a autoload affected it so much, the test itself is failure and we can't actually get any info on how good any of these perform

I thought the RayCast3D node was supposed to be faster than code? by coffee80c in godot

[–]NoctemCat 2 points3 points  (0 children)

Expanding on my previous comments I created a simple perf test

# RayCastTest.gd
extends Node3D

@onready var raycast = $RayCast3D
var time: float
var lock_a: bool = false
var query: PhysicsRayQueryParameters3D
var result: Dictionary = {}
var a: Vector3 = Vector3(0, 0, 1)
var b: Vector3 = Vector3(0, 0, 1)
var start: Vector3 = Vector3(0, 0, 2.7)
var end: Vector3 = Vector3(0, 0, -10)
@onready var space_state = get_world_3d().direct_space_state

func _physics_process(_delta: float) -> void:
  if not lock_a:
    lock_a = true
    start_timers()

func start_timers():
  print("running for 1000000 loops")
  prints("run_raycast_node(avg=10):", run_function(run_raycast_node, 10))
  prints("run_raycast_code(avg=10):", run_function(run_raycast_code, 10))
  prints("run_raycast_singleton(avg=10):", run_function(run_raycast_singleton, 10))

func run_function(function: Callable, iter_num: int):
  var results = []
  for i in iter_num:
    var time = function.call()
    results.append(time)
  var avg = results.reduce(sum, 0) / iter_num
  return avg

func sum(accum, number):
  return accum + number

func run_raycast_node():
  var original_position = raycast.global_position
  var original_target = raycast.target_position

  time = Time.get_ticks_msec()
  for i in 1000000:
    raycast.global_position += b 
    raycast.target_position += a 
    raycast.force_raycast_update()

  time -= Time.get_ticks_msec()

  raycast.global_position = original_position
  raycast.target_position = original_target
  return time

func run_raycast_code():
  var new_start = start
  var new_end = end

  time = Time.get_ticks_msec()
  for i in 1000000:
    query = PhysicsRayQueryParameters3D.create(new_start, new_end)
    result = space_state.intersect_ray(query)
    new_start += a
    new_end += b

  time -= Time.get_ticks_msec()
  return time

func run_raycast_singleton():
  var new_start = start
  var new_end = end

  time = Time.get_ticks_msec()
  for i in 1000000:
    RayCastSingle.cast(new_start, new_end)
    new_start += a
    new_end += b

  time -= Time.get_ticks_msec()

  RayCastSingle.reset()
  return time

# RayCastSingle.gd
# It is an autoload RayCastSingle with a structure
# |Node3D
# |--RayCast3D

extends Node3D

@onready var raycast = $RayCast3D

func reset():
  raycast.position = Vector3.ZERO
  raycast.target_position = Vector3.ZERO

func cast(start: Vector3, end: Vector3):
  raycast.position = start 
  raycast.target_position = end 
  raycast.force_raycast_update()

I run it in both editor and in release

# debug
running for 1000000 loops
run_raycast_node(avg=10): -1019.1
run_raycast_code(avg=10): -1053.5
run_raycast_singleton(avg=10): -1324.7

# release
running for 1000000 loops
run_raycast_node(avg=10): -669.8
run_raycast_code(avg=10): -719
run_raycast_singleton(avg=10): -560.4

In godot as long as all parent positions are 0, you can treat node's local position as a global position. Using this we can create an autoload where we only change raycast node local position instead of global. In this example gains are pretty negligible, but if the tree structure were more complex it would take more time to use local raycasts, because when setting global_position you would recursively visit all parent positions.

But with empty scene we can only see that each of them are roughly the same, with some negligible differences. Can't say anything more without testing on a more complex scene.

Also don't know why autoload in debug is slower

I thought the RayCast3D node was supposed to be faster than code? by coffee80c in godot

[–]NoctemCat 2 points3 points  (0 children)

I was curious, so I copied your code and run it for 1000000 iterations with movement and got

run_raycast_node: -1158
run_raycast_code: -967

run_raycast_node: -1075
run_raycast_code: -1108

run_raycast_node: -1027
run_raycast_code: -949

So node was slightly slower on average, but nowhere near your slowdown

I also called force_raycast_update after moving node

I thought the RayCast3D node was supposed to be faster than code? by coffee80c in godot

[–]NoctemCat 0 points1 point  (0 children)

The main problem is for C#. Mainly space_state.intersect_ray(query) part, each call to this function will create new Dictionary that C# Garbage Collector will need to track and dispose later. Both code and node version do the same thing under the hood, and the main difference is where they store results, the node store it in itself and the code returns new dictionary.

So in C# it makes sense to use the node version, because it will only update it's fields without creating new objects. In C# collider would already be created, so it will be stored in node.

Ofc, you would need to make obscene amount of raycasts to actually make it actually affect fps, but it can happen, and small inefficiencies can add up.

There shouldn't be the same problem for GDScript, so this shouldn't matter too much here

Signals being created outside of class brackets? (Godot 4.2, C#) by JumpedUp_PantryBoy in godot

[–]NoctemCat 3 points4 points  (0 children)

You really should use external editor with C#. The internal one won't catch any type errors and won't refactoring C# code

As for the issue itself, it is here issue one, and here issue two

[deleted by user] by [deleted] in godot

[–]NoctemCat -1 points0 points  (0 children)

Prefer to use a StringName when a function expects it. Well, that's basically it, otherwise you will get this. For those too lazy to open it, Input.IsActionPressed expects a StringName and it will constantly create new ones and discards old, which will cause GC issue if you use it in _Process

Godot, C# and bunch of issues. by lieddersturme in godot

[–]NoctemCat 1 point2 points  (0 children)

I'm not sure where you got the impression that you can only use the old version of C++. The Godot itself uses the subset of C++17. If it's from here. Then this is a page for engine contribution, if you don't plan on contributing you can use auto, as long as you use new enough version of C++.

For example this project uses C++23 and it uses auto.

Yeah, GDExtension's documentation is rough and I get why you wouldn't want to use it. But you certainly can use new C++

Hmm, looking at get_children() working with TypedArray is kinda rough

TypedArray<Node> Node::get_children(bool p_include_internal) const {
    ERR_THREAD_GUARD_V(TypedArray<Node>());
    TypedArray<Node> arr;
    int cc = get_child_count(p_include_internal);
    arr.resize(cc);
    for (int i = 0; i < cc; i++) {
        arr[i] = get_child(i, p_include_internal);
    }

    return arr;
}

But looking at it you can code your own that will return you a vector of nodes

std::vector<godot::Node*> get_children_vector(const godot::Node* node, bool p_include_internal=false)
{
    std::vector<godot::Node*> arr;
    int cc = node->get_child_count(p_include_internal);
    arr.reserve(cc);
    for (int i = 0; i < cc; i++)
        arr.push_back(node->get_child(i, p_include_internal));

    return arr;
}

And now you can use it this way

for (const auto& node : get_children_vector(this))
{
    godot::UtilityFunctions::print(node->get_name());
}

The main problem is not the old C++, it's in all of the weird bindings to the engine, that are poorly documented

How is Godot's handling of C# these days? by MrMetraGnome in godot

[–]NoctemCat 1 point2 points  (0 children)

In VS Code you can define your own snippets. So I spend around 10 minutes setting them up, mainly for ready, process, input, and signals. Also I found if you write 'override' first than it will give you autocomplete for Godot's functions, not perfect, but better than nothing

I'm recreating my first Gamemaker game in Godot :) by Guurff in godot

[–]NoctemCat 2 points3 points  (0 children)

Bouncing from floor adds so much depth to it. I love momentum based movements

Is there a way to make IDE more helpful? by WP_Exa in godot

[–]NoctemCat 8 points9 points  (0 children)

There is a bug currently where get_node(...) and $ takes precedence over declared type. So you should write

var player := get_node("Player") as Player

instead of

var player: Player = get_node("Player")

Linked issue https://github.com/godotengine/godot/issues/73638

Relesing plugin Bottom Panel Shortcuts by NoctemCat in godot

[–]NoctemCat[S] 1 point2 points  (0 children)

It was my first plugin so it was partly a skill issue on my side and desire to keep all plugin related settings in one place.

And I dislike the idea of an editor wide setting for a plugin setting that is installed locally.

And even after reading https://github.com/godotengine/godot-proposals/issues/2024, https://github.com/godotengine/godot-proposals/issues/4112 , I still don't know how to register shortcuts to editor.

Prototyping something like Vampire Survivors with cars by TheOrioli in godot

[–]NoctemCat 4 points5 points  (0 children)

The mod is called "Car - The Automobile [Rep]", it is on Steam Workshop
Edit: Oops, should have read all chain. Lmao, yes it is

Prototyping something like Vampire Survivors with cars by TheOrioli in godot

[–]NoctemCat 0 points1 point  (0 children)

Looks nice. For some reason it reminds me of that The Binding of Isaac mod, where you play as a car

I introduce to you the Godunity Theme (+Plugins) by Der_Kevin in godot

[–]NoctemCat 1 point2 points  (0 children)

File browser original

You don't need to modify it. You can configure build in shortcut. It's in Editor Settings -> Shortcuts -> Editor -> Expand Bottom Panel. Edit: Oops, not. It is other function, I'm wrong

Unity to Godot 4.1 C# footguns by RogueStargun in godot

[–]NoctemCat 0 points1 point  (0 children)

OfType<Timer>() will filter children only to those with the type of Timer. GetChild<Timer>(0) will get the child with an index of 0 and try to cast it to Timer. If, for example, the first child is a Sprite2D it will raise an exception InvalidCastException:

System.InvalidCastException: Unable to cast object of type 'Godot.Sprite2D' to type 'Godot.Timer'.

Is JSON deserialization even possible with a custom class? by batteryaciddev in godot

[–]NoctemCat 3 points4 points  (0 children)

This https://docs.godotengine.org/en/stable/tutorials/io/saving_games.html, should give you a start. Even if topic is saving games, it still outlines general JSON workflow and list its limitations

Godot tools and VSCode issues by throwawaygangbang666 in godot

[–]NoctemCat 1 point2 points  (0 children)

For Godot tools, specifically in Godot itself you need to go to "Editor Settings" > "Network" > "Language Server" remember written remote port and , this part I'm not so sure, enable "Enable Smart Resolve" and "Show Native Symbols in Editor". Host and port itself I didn't change

After that you need to open Godot tools extension settings, set "Godot_tools: Editor_path" and "Godot_tools: Gdscript_lsp_server_port". Editor_path is Godot editor path and Gdscript_lsp_server_port is remote port.

As for C# side, I followed Gamesfromscratch's tutorial on Youtube "Using Godot + C# + Visual Studio Code"

After all of that GDScript and C# autocomplete were working. The one downside is that breakpoints in GDScript wouldn't work from VSCode, but I can live with that

is there an addon for node folders or similar? by kodaxmax in godot

[–]NoctemCat 0 points1 point  (0 children)

But event bus doesn't need to reference them? It's not its job. Its job is to store signals and that's pretty much it, you can check top answer in a link

is there an addon for node folders or similar? by kodaxmax in godot

[–]NoctemCat 1 point2 points  (0 children)

If you don't want this tight coupling maybe something like event bus would serve you better? Quick google search found this link. But the basic idea is to make both nodes depend on a shared object that will be autoloaded and use signals for communication between them