Hi guys, I try to extend godot class functionality but Inheritance fails in C#

//Extend AnimationPlayer Class
public class MyPlayer:AnimationPlayer
{
	public bool First=false;
	public string OriginalName="";
	public void MyOwnMethod()
	{
	.
	.
	.}
}

//Where I use extended class
public class SceneScript : Control
{
	public MyPlayer Player;
	public override void _Ready()
	{
		//Try 1:
		Player=(MyPlayer)GetNode("something");//Try 1: Fails with message "Specified cast is not valid",so I removed it
		//Try 2:
		Player=GetNode("something") as MyPlayer;//Building doe snot show any error here
		Player.Connect("animation_finished",this,nameof(Task));//also here fails with same message "Object reference not set to an instance of an object"

	}
}

I know the meaning of errors but I don't know a solution, Please any one have solution for this?

//========================================================
//============My current solution is===========================
//========================================================
public class MyPlayer
{
	public AnimationPlayer Player;
	public bool First=false;
	public string OriginalName="";
	public void MyOwnMethod()
	{
	.
	.
	.}
}
public class SceneScript : Control
{
	public MyPlayer ExPlayer=new MyPlayer();
	public override void _Ready()
	{
		ExPlayer.Player=(AnimationPlayer)GetNode("something");
		ExPlayer.Player.Connect("animation_finished",this,nameof(Task));
	}
}

it woks perfectly but is not what I want.

I am writing this from memory, but I think I have done casting to custom inherited classes using syntax like this and its worked:

Example thing = GetNodeOrNull<Example>("Path_To_Example");

So in your case, something like this may work:

ExPlayer.Player = GetNodeOrNull<AnimationPlayer>("something")

Thank you for answering , first solution does not work, while second solution is not bad, I use second solution since long time, it solves most problems perfectly but not all problems

What problems does the second solution have? Is it just with the Connect function? If so, it may be something on that line, probably Task in nameof(Task) is null if I had to guess.

Sorry for confusing About Inheritance and adding new properties and features to godot classes(nodes) All Inheritance ways work in C# under these conditions: - Extended class can be created and added to scene programmatically and all new properties and methods work fine but you can not link Extended class to existing node in scene in any way (ExtendedClass =(ExtendedClass)GetNode("Something"), this will fail). This: public class ExtClass:Godot.Class { public AnyC#Type Field;//property to extend original Godot class . . public ReturnValue methodName(arg..)//new method { . . } } will work programmatically , you can create extended class and add it to scene with all new and original functionalities and features

  • if you plan To Connect Extended class to existing node in editor, you can choose: First:- public class ExtClass: { public Godot.Class Name;//you can use this as reference to node in Editor public AnyC#Type Field;//property to extend original Godot class . . public ReturnType MethodName(arg..)//new method { . . } } Or Second:-
    • Create scene has one node/class ONLY(node you want to extend it),it will be the root.
    • Attach script to node, this script will contains new method and properties in extended class(you can show properties in editor).
    • Instancing scene in editor or programmatically

this is what I got after deep digging, if you have different solution or better than this, please share us

@TwistedTwigleg said: What problems does the second solution have? Is it just with the Connect function? If so, it may be something on that line, probably Task in nameof(Task) is null if I had to guess.

You don't know my purpose, I gave you example not project, my project extends more than 10 types of godot nodes, I need to access object directly , my current solution has to search for object instance using godot object name, in complicated project you can not follow objects relations, you have to access object directly. if you have 3 sprites(as ExSprite extended from sprite class), A is fire and B is knife while C is Player if A or B enters to C area, my current solution something like this(code from my head): public void OnEnterArea(object obj) { if (obj is Sprite) { if (obj.Name=="Fire") C.Health+=Fire.Health; if (obj.Name=="Knife") C.Health+=Knife.Health; } } While the right solution has to be: public void OnEnterArea(object obj) { if (obj is ExSprite) C.Health+=((ExSprite)obj).Health; } I need this solution, I search for this solution, if you see complicated project , you will understand me. I don't need to think about fire or knife or snow or any thing, for me they are just extended objects from sprite class. Remember this is example from head not real project

Extended class can be created and added to scene programmatically and all new properties and methods work fine but you can not link Extended class to existing node in scene in any way

Because fundamentally C# class is a proper class, unlike gdscript where you strap extra functions and properties on via a script bound to the godot built in node. In this case, the extra functionality and properties are added to the node in a javascript prototype like way via a dictionary of the new properties and functions associated with the built in node

Possible approaches:

You can look into c# delegates and ponder if this feature can help. https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/using-delegates and also extension methods: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods

or you can write Proxy classes that add fields and methods to your nodes. The C# node you create might then store the proxy for repeated use.

class Proxy:Object
{
     Extended extending;
     public Proxy(Extended obj) { extending = obj }
}

class Extended:BaseClass
{
   Proxy proxy;
}

Extended e = // something
var p = new Proxy(e);
e.proxy.some_field = something
e.proxy.some_method();

or

p.some_field = something;
p.some_method();
p.extending.node_property_or_method

---

While the right solution has to be: public void OnEnterArea(object obj) { if (obj is ExSprite) C.Health+=((ExSprite)obj).Health; }

does the following work?

ExSprite ex = obj as ExSprite
if ex: C.Health += ex.Health

or

ExSprite ex = GetNodeOrNull("thingy") as ExSprite
if ex: C.Health += ex.Health

consider writing methods to do this task in this manner:

public int as_thing_health(Godot.Object gObj)
{
    Thing  ex = obj as Thing
	return ex ? ex.Health : 0
}

so use would be:

C.Health += as_thing_health(obj)


In either case, I would highly recommend making use of the 'using' keyword

using Godot;

because namespaces can trip you up big time.

otherwise:

Break down your use case in a separate scratch class to work out your issues, as this may save your significant time by eliminating some other factor causing your problem

@almunsaah said:

@TwistedTwigleg said: What problems does the second solution have? Is it just with the Connect function? If so, it may be something on that line, probably Task in nameof(Task) is null if I had to guess.

You don't know my purpose, I gave you example not project, my project extends more than 10 types of godot nodes, I need to access object directly , my current solution has to search for object instance using godot object name, in complicated project you can not follow objects relations, you have to access object directly.

Noted, though there is no way for me to know your purpose without you stating it, which is why I cannot provide any sort of help to the exact purpose.

if you have 3 sprites(as ExSprite extended from sprite class), A is fire and B is knife while C is Player if A or B enters to C area, my current solution something like this(code from my head): public void OnEnterArea(object obj) { if (obj is Sprite) { if (obj.Name=="Fire") C.Health+=Fire.Health; if (obj.Name=="Knife") C.Health+=Knife.Health; } } While the right solution has to be: public void OnEnterArea(object obj) { if (obj is ExSprite) C.Health+=((ExSprite)obj).Health; } I need this solution, I search for this solution, if you see complicated project , you will understand me. I don't need to think about fire or knife or snow or any thing, for me they are just extended objects from sprite class. Remember this is example from head not real project

What it sounds like you are trying to do is access a variable that is shared across multiple classes. Each of these classes extends Sprite as their base, but you want to use them in a polymorphic way, where once you know the general type of the object you can access certain things without directly knowing how they are implemented or what they contain.

If my understanding is correct on what you are looking to achieve, then the ideal solution is to have a base class and then have your other nodes extend that. Here's an example:

using System;
using Godot;

// The base class
public class ExSprite : Sprite {
	public float Health = 1.0f;
	
	public override void _Ready() {
	}
	
	// example function we can override with custom functionality
	public virtual void Print_Type() {
	}
}

// The fire class
public class FireSprite : ExSprite {
	public override void _Ready() {
		// Set health to whatever health the fire would have
		Health = 2.0f;
	}
	public override void Print_Type() {
		GD.print("Type is fire!");
	}
}

// The knife class
public class KnifeSprite : ExSprite {
	public override void _Ready() {
		Health = 12.0f;
	}
	public override void Print_Type() {
		GD.print("Type is knife!");
	}
}

// ***********************
// Then in use, you could do something like this
public void OnEnterArea(object obj) {
	if (obj is ExSprite) {
		C.Health += ((ExSprite)obj),Health;
		((ExSprite)obj),Print_Type;
	}
}

That should work as you are wanting, where you can use the properties and functions across all the classes that extend ExSprite, regardless of what type it is. I have used this structure before many times to control a series of nodes without needing to directly cast them or know how they work, I can instead just program it to work with the base class.

Hopefully this helps! :smile:

2 years later