Godot Version
v4.2.1.stable.mono.official [b09f793f5]
Context
I’m currently attempting to create a gizmo plugin for one of my custom classes. The good news is that I can draw gizmos and position them using the node’s transform. That’s great.
However, since I’m building this gizmo for one of my custom classes, I would like to retrieve its specialized information such that I can configure the gizmo from that data.
One of the problems I’m running into is being unable to cast the Node3D
to my custom class.
// This produces a null reference exception (even though this works elsewhere)
MyClass customClass = gizmo.GetNode3D() as MyClass;
Because of this, I instead tried to use the Get()
method to retrieve the data that I need.
This works for all supported Variant
types (int
, string
, Vector3
etc.) so I’m now able to retrieve most of the data. The problem with this approach is that I am unable to retrieve variables whose type is not a Variant
(in my case, I have created a class called Octree
with no base class).
My attempt at fixing this was based on the documentation that states (see C# Variant)
So I tried to make my Octree
class derive from GodotObject
. My hope was that I could then do
Octree oct = region.Get("octree").As<Octree>();
…since Octree
was now “Variant-compatible”. This turned out to not be the case. I even tried to tweak the casting (based on this post), but this didn’t work either.
Octree oct = region.Get("octree").AsGodotObject() as Octree;
Question
- Is it not possible to cast the
Node3D
associated with a 3D gizmo to a custom class?- If not, is there another way to retrieve the non-Variant data that I need from the instance?
Any help with this is greatly appreciated!
Code
OctreeGizmo.cs
Look at _HasGizmo()
and _Redraw()
and how casting the Node3D, in both contexts, is invalid.
using Godot;
using System;
using System.Collections.Generic;
[Tool]
public partial class OctreeGizmo : EditorNode3DGizmoPlugin
{
private Mesh mesh;
public OctreeGizmo()
{
CreateMaterial("main", new Color(1f, 1f, 0f, 0.2f));
mesh = new BoxMesh() { Size = Vector3.One * 2f };
}
public override string _GetGizmoName()
{
return "Octree Region 3D";
}
public override bool _HasGizmo(Node3D node3D)
{
bool hasGizmo = node3D is OctreeRegion3D;
GD.Print($"Comparing {node3D.Name} node to type OctreeRegion3D | result: {hasGizmo}");
GD.Print($"Node is of type {node3D.GetType()}");
// The second print-statement prints the following:
// Node is of type Node3D
return hasGizmo;
}
public override void _Redraw(EditorNode3DGizmo gizmo)
{
GD.Print($"Drawing gizmo for node: {gizmo.GetNode3D().Name}");
gizmo.Clear();
// GD.Print($"Script type on gizmo's node3D: {gizmo.GetNode3D().GetScript().As<OctreeRegion3D>()}");
var region = gizmo.GetNode3D();
Octree oct = region.Get("octree").AsGodotObject() as Octree;
GD.Print($"Octree data test | Octree: {oct.IsValid()}");
Vector3[] lines = new Vector3[]
{
Vector3.Up,
Vector3.Up * 3f + Vector3.Right
};
gizmo.AddLines(lines, GetMaterial("main", gizmo), false);
gizmo.AddMesh(mesh, GetMaterial("main", gizmo), region.Transform);
}
}
OctreeRegion3DPlugin.cs
#if TOOLS
using Godot;
using System;
[Tool]
public partial class OctreeRegion3DPlugin : EditorPlugin
{
OctreeGizmo gizmo = new OctreeGizmo();
public override void _EnterTree()
{
// Add the gizmo for the script
AddNode3DGizmoPlugin(gizmo);
// // Add the script as a custom type (functions similarly, maybe even identically, to using the [GlobalClass] attribute)
// var script = GD.Load<Script>("res://Scripts/Navigation/OctreeRegion3D.cs");
// AddCustomType("OctreeRegion3D", "Node3D", script, null);
}
public override void _ExitTree()
{
// Clean-up of the plugin goes here.
RemoveNode3DGizmoPlugin(gizmo);
}
}
#endif
OctreeRegion3D.cs
using Godot;
using System;
[GlobalClass]
public partial class OctreeRegion3D : Node3D
{
[Export] private Vector3 regionOffset;
[Export] private float rootSize = 100f;
[Export] private int maxDepth = 8;
private Octree octree;
public Octree Octree { get => octree; }
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
octree = new Octree(this, regionOffset, rootSize, maxDepth);
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
}
}
Octree.cs
using Godot;
using System;
public partial class Octree : GodotObject
{
// ========================================================================
// There is some stuff in this class, but it's not relevant to the problem
// ========================================================================
}