cross_language_scripting.rst 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  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 str1 : String = "foo"
  13. var str2 : String setget ,get_str2
  14. func get_str2() -> String:
  15. return "foofoo"
  16. func print_node_name(node : Node) -> void:
  17. print(node.get_name())
  18. func print_array(arr : Array) -> void:
  19. for element in arr:
  20. print(element)
  21. func print_n_times(msg : String, n : int) -> void:
  22. for i in range(n):
  23. print(msg)
  24. .. code-tab:: csharp
  25. public class MyCSharpNode : Node
  26. {
  27. public String str1 = "bar";
  28. public String str2 { get { return "barbar"; } }
  29. public void PrintNodeName(Node node)
  30. {
  31. GD.Print(node.GetName());
  32. }
  33. public void PrintArray(String[] arr)
  34. {
  35. foreach (String element in arr)
  36. {
  37. GD.Print(element);
  38. }
  39. }
  40. public void PrintNTimes(String msg, int n)
  41. {
  42. for (int i = 0; i < n; ++i)
  43. {
  44. GD.Print(msg);
  45. }
  46. }
  47. }
  48. Instantiating nodes
  49. -------------------
  50. If you're not using nodes from the scene tree, you'll probably want to
  51. instantiate nodes directly from the code.
  52. Instantiating C# nodes from GDScript
  53. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  54. Using C# from GDScript doesn't need much work. Once loaded
  55. (see :ref:`doc_gdscript_classes_as_resources`), the script can be instantiated
  56. with :ref:`new() <class_CSharpScript_method_new>`.
  57. ::
  58. var my_csharp_script = load("res://path_to_cs_file.cs")
  59. var my_csharp_node = my_csharp_script.new()
  60. print(my_csharp_node.str2) # barbar
  61. .. warning::
  62. When creating ``.cs`` scripts, you should always keep in mind that the class
  63. Godot will use is the one named like the ``.cs`` file itself. If that class
  64. does not exist in the file, you'll see the following error:
  65. ``Invalid call. Nonexistent function `new` in base``.
  66. For example, MyCoolNode.cs should contain a class named MyCoolNode.
  67. The C# class needs to derive a Godot class, for example ``Godot.Object``.
  68. Otherwise, the same error will occur.
  69. You also need to check your ``.cs`` file is referenced in the project's
  70. ``.csproj`` file. Otherwise, the same error will occur.
  71. Instantiating GDScript nodes from C#
  72. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  73. From the C# side, everything work the same way. Once loaded, the GDScript can
  74. be instantiated with :ref:`GDScript.New() <class_GDScript_method_new>`.
  75. .. code-block:: csharp
  76. GDScript MyGDScript = (GDScript) GD.Load("res://path_to_gd_file.gd");
  77. Object myGDScriptNode = (Godot.Object) MyGDScript.New(); // This is a Godot.Object
  78. Here we are using an :ref:`class_Object`, but you can use type conversion like
  79. explained in :ref:`doc_c_sharp_features_type_conversion_and_casting`.
  80. Accessing fields
  81. ----------------
  82. Accessing C# fields from GDScript
  83. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  84. Accessing C# fields from GDScript is straightforward, you shouldn't have
  85. anything to worry about.
  86. ::
  87. print(my_csharp_node.str1) # bar
  88. my_csharp_node.str1 = "BAR"
  89. print(my_csharp_node.str1) # BAR
  90. print(my_csharp_node.str2) # barbar
  91. # my_csharp_node.str2 = "BARBAR" # This line will hang and crash
  92. Note that it doesn't matter if the field is defined as a property or an
  93. attribute. However, trying to set a value on a property that does not define
  94. a setter will result in a crash.
  95. Accessing GDScript fields from C#
  96. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  97. As C# is statically typed, accessing GDScript from C# is a bit more
  98. convoluted, you will have to use :ref:`Object.Get() <class_Object_method_get>`
  99. and :ref:`Object.Set() <class_Object_method_set>`. The first argument is the name of the field you want to access.
  100. .. code-block:: csharp
  101. GD.Print(myGDScriptNode.Get("str1")); // foo
  102. myGDScriptNode.Set("str1", "FOO");
  103. GD.Print(myGDScriptNode.Get("str1")); // FOO
  104. GD.Print(myGDScriptNode.Get("str2")); // foofoo
  105. // myGDScriptNode.Set("str2", "FOOFOO"); // This line won't do anything
  106. Keep in mind that when setting a field value you should only use types the
  107. GDScript side knows about.
  108. Essentially, you want to work with built-in types as described in :ref:`doc_gdscript` or classes extending :ref:`class_Object`.
  109. Calling methods
  110. ---------------
  111. Calling C# methods from GDScript
  112. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  113. Again, calling C# methods from GDScript should be straightforward. The
  114. marshalling process will do its best to cast the arguments to match
  115. function signatures.
  116. If that's impossible, you'll see the following error: ``Invalid call. Nonexistent function `FunctionName```.
  117. ::
  118. my_csharp_node.PrintNodeName(self) # myGDScriptNode
  119. # my_csharp_node.PrintNodeName() # This line will fail.
  120. my_csharp_node.PrintNTimes("Hello there!", 2) # Hello there! Hello there!
  121. my_csharp_node.PrintArray(["a", "b", "c"]) # a, b, c
  122. my_csharp_node.PrintArray([1, 2, 3]) # 1, 2, 3
  123. Calling GDScript methods from C#
  124. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  125. To call GDScript methods from C# you'll need to use
  126. :ref:`Object.Call() <class_Object_method_call>`. The first argument is the
  127. name of the method you want to call. The following arguments will be passed
  128. to said method.
  129. .. code-block:: csharp
  130. myGDScriptNode.Call("print_node_name", this); // my_csharp_node
  131. // myGDScriptNode.Call("print_node_name"); // This line will fail silently and won't error out.
  132. myGDScriptNode.Call("print_n_times", "Hello there!", 2); // Hello there! Hello there!
  133. // When dealing with functions taking a single array as arguments, we need to be careful.
  134. // If we don't cast it into an object, the engine will treat each element of the array as a separate argument and the call will fail.
  135. String[] arr = new String[] { "a", "b", "c" };
  136. // myGDScriptNode.Call("print_array", arr); // This line will fail silently and won't error out.
  137. myGDScriptNode.Call("print_array", (object)arr); // a, b, c
  138. myGDScriptNode.Call("print_array", (object)new int[] { 1, 2, 3 }); // 1, 2, 3
  139. // Note how the type of each array entry does not matter as long as it can be handled by the marshaller
  140. .. warning::
  141. As you can see, if the first argument of the called method is an array,
  142. you'll need to cast it as ``object``.
  143. Otherwise, each element of your array will be treated as a single argument
  144. and the function signature won't match.
  145. Inheritance
  146. -----------
  147. A GDScript file may not inherit from a C# script. Likewise, a C# script may not
  148. inherit from a GDScript file. Due to how complex this would be to implement,
  149. this limitation is unlikely to be lifted in the future. See
  150. `this GitHub issue <https://github.com/godotengine/godot/issues/38352>`__
  151. for more information.