cross_language_scripting.rst 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. .. _doc_cross_language_scripting:
  2. Cross-language scripting
  3. ========================
  4. Godot allows you to mix and match scripting languages to suit your needs.
  5. This means a single project can define nodes in both C# and GDScript.
  6. This page will go through the possible interactions between two nodes written
  7. in different languages.
  8. The following two scripts will be used as references throughout this page.
  9. .. tabs::
  10. .. code-tab:: gdscript GDScript
  11. extends Node
  12. var my_property: String = "my gdscript value":
  13. get:
  14. return my_property
  15. set(value):
  16. my_property = value
  17. signal my_signal
  18. signal my_signal_with_params(msg: String, n: int)
  19. func print_node_name(node: Node) -> void:
  20. print(node.get_name())
  21. func print_array(arr: Array) -> void:
  22. for element in arr:
  23. print(element)
  24. func print_n_times(msg: String, n: int) -> void:
  25. for i in range(n):
  26. print(msg)
  27. func my_signal_handler():
  28. print("The signal handler was called!")
  29. func my_signal_with_params_handler(msg: String, n: int):
  30. print_n_times(msg, n)
  31. .. code-tab:: csharp
  32. using Godot;
  33. public partial class MyCSharpNode : Node
  34. {
  35. public string MyProperty { get; set; } = "my c# value";
  36. [Signal] public delegate void MySignalEventHandler();
  37. [Signal] public delegate void MySignalWithParamsEventHandler(string msg, int n);
  38. public void PrintNodeName(Node node)
  39. {
  40. GD.Print(node.Name);
  41. }
  42. public void PrintArray(string[] arr)
  43. {
  44. foreach (string element in arr)
  45. {
  46. GD.Print(element);
  47. }
  48. }
  49. public void PrintNTimes(string msg, int n)
  50. {
  51. for (int i = 0; i < n; ++i)
  52. {
  53. GD.Print(msg);
  54. }
  55. }
  56. public void MySignalHandler()
  57. {
  58. GD.Print("The signal handler was called!");
  59. }
  60. public void MySignalWithParamsHandler(string msg, int n)
  61. {
  62. PrintNTimes(msg, n);
  63. }
  64. }
  65. Instantiating nodes
  66. -------------------
  67. If you're not using nodes from the scene tree, you'll probably want to
  68. instantiate nodes directly from the code.
  69. Instantiating C# nodes from GDScript
  70. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  71. Using C# from GDScript doesn't need much work. Once loaded
  72. (see :ref:`doc_gdscript_classes_as_resources`), the script can be instantiated
  73. with :ref:`new() <class_CSharpScript_method_new>`.
  74. .. code-block:: gdscript
  75. var MyCSharpScript = load("res://Path/To/MyCSharpNode.cs")
  76. var my_csharp_node = MyCSharpScript.new()
  77. .. warning::
  78. When creating ``.cs`` scripts, you should always keep in mind that the class
  79. Godot will use is the one named like the ``.cs`` file itself. If that class
  80. does not exist in the file, you'll see the following error:
  81. ``Invalid call. Nonexistent function `new` in base``.
  82. For example, MyCoolNode.cs should contain a class named MyCoolNode.
  83. The C# class needs to derive a Godot class, for example ``GodotObject``.
  84. Otherwise, the same error will occur.
  85. You also need to check your ``.cs`` file is referenced in the project's
  86. ``.csproj`` file. Otherwise, the same error will occur.
  87. Instantiating GDScript nodes from C#
  88. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  89. From the C# side, everything work the same way. Once loaded, the GDScript can
  90. be instantiated with :ref:`GDScript.New() <class_GDScript_method_new>`.
  91. .. code-block:: csharp
  92. var myGDScript = GD.Load<GDScript>("res://path/to/my_gd_script.gd");
  93. var myGDScriptNode = (GodotObject)myGDScript.New(); // This is a GodotObject.
  94. Here we are using an :ref:`class_Object`, but you can use type conversion like
  95. explained in :ref:`doc_c_sharp_features_type_conversion_and_casting`.
  96. Accessing fields
  97. ----------------
  98. Accessing C# fields from GDScript
  99. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  100. Accessing C# fields from GDScript is straightforward, you shouldn't have
  101. anything to worry about.
  102. .. code-block:: gdscript
  103. # Output: "my c# value".
  104. print(my_csharp_node.MyProperty)
  105. my_csharp_node.MyProperty = "MY C# VALUE"
  106. # Output: "MY C# VALUE".
  107. print(my_csharp_node.MyProperty)
  108. Accessing GDScript fields from C#
  109. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  110. As C# is statically typed, accessing GDScript from C# is a bit more
  111. convoluted. You will have to use :ref:`GodotObject.Get() <class_Object_method_get>`
  112. and :ref:`GodotObject.Set() <class_Object_method_set>`. The first argument is the name of the field you want to access.
  113. .. code-block:: csharp
  114. // Output: "my gdscript value".
  115. GD.Print(myGDScriptNode.Get("my_property"));
  116. myGDScriptNode.Set("my_property", "MY GDSCRIPT VALUE");
  117. // Output: "MY GDSCRIPT VALUE".
  118. GD.Print(myGDScriptNode.Get("my_property"));
  119. Keep in mind that when setting a field value you should only use types the
  120. GDScript side knows about.
  121. Essentially, you want to work with built-in types as described in :ref:`doc_gdscript` or classes extending :ref:`class_Object`.
  122. Calling methods
  123. ---------------
  124. Calling C# methods from GDScript
  125. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  126. Again, calling C# methods from GDScript should be straightforward. The
  127. marshalling process will do its best to cast the arguments to match
  128. function signatures.
  129. If that's impossible, you'll see the following error: ``Invalid call. Nonexistent function `FunctionName```.
  130. .. code-block:: gdscript
  131. # Output: "my_gd_script_node" (or name of node where this code is placed).
  132. my_csharp_node.PrintNodeName(self)
  133. # This line will fail.
  134. # my_csharp_node.PrintNodeName()
  135. # Outputs "Hello there!" twice, once per line.
  136. my_csharp_node.PrintNTimes("Hello there!", 2)
  137. # Output: "a", "b", "c" (one per line).
  138. my_csharp_node.PrintArray(["a", "b", "c"])
  139. # Output: "1", "2", "3" (one per line).
  140. my_csharp_node.PrintArray([1, 2, 3])
  141. Calling GDScript methods from C#
  142. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  143. To call GDScript methods from C# you'll need to use
  144. :ref:`GodotObject.Call() <class_Object_method_call>`. The first argument is the
  145. name of the method you want to call. The following arguments will be passed
  146. to said method.
  147. .. code-block:: csharp
  148. // Output: "MyCSharpNode" (or name of node where this code is placed).
  149. myGDScriptNode.Call("print_node_name", this);
  150. // This line will fail silently and won't error out.
  151. // myGDScriptNode.Call("print_node_name");
  152. // Outputs "Hello there!" twice, once per line.
  153. myGDScriptNode.Call("print_n_times", "Hello there!", 2);
  154. string[] arr = new string[] { "a", "b", "c" };
  155. // Output: "a", "b", "c" (one per line).
  156. myGDScriptNode.Call("print_array", arr);
  157. // Output: "1", "2", "3" (one per line).
  158. myGDScriptNode.Call("print_array", new int[] { 1, 2, 3 });
  159. // Note how the type of each array entry does not matter
  160. // as long as it can be handled by the marshaller.
  161. .. _connecting_to_signals_cross_language:
  162. Connecting to signals
  163. ---------------------
  164. Connecting to C# signals from GDScript
  165. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  166. Connecting to a C# signal from GDScript is the same as connecting to a signal
  167. defined in GDScript:
  168. .. code-block:: gdscript
  169. my_csharp_node.MySignal.connect(my_signal_handler)
  170. my_csharp_node.MySignalWithParams.connect(my_signal_with_params_handler)
  171. Connecting to GDScript signals from C#
  172. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  173. Connecting to a GDScript signal from C# only works with the ``Connect`` method
  174. because no C# static types exist for signals defined by GDScript:
  175. .. code-block:: csharp
  176. myGDScriptNode.Connect("my_signal", Callable.From(MySignalHandler));
  177. myGDScriptNode.Connect("my_signal_with_params", Callable.From<string, int>(MySignalWithParamsHandler));
  178. Inheritance
  179. -----------
  180. A GDScript file may not inherit from a C# script. Likewise, a C# script may not
  181. inherit from a GDScript file. Due to how complex this would be to implement,
  182. this limitation is unlikely to be lifted in the future. See
  183. `this GitHub issue <https://github.com/godotengine/godot/issues/38352>`__
  184. for more information.