Browse Source

Update & fix C# blocks under tutorials/scripting/

- `_Process` and `_PhysicsProcess` take their delta argument as a `double`
- Use string interpolation instead of concatenating strings
- Use `string` instead of `String` (`using System` was removed from everywhere in a previous PR)
- Use `System.Array.Empty<T>()` to init empty arrays
- Use `SignalName.*` instead of `nameof(*)` for signals
- Do not use `event` as an argument name (that's a keyword)
- Match node paths to screenshots when retrieving nodes from the tree
- Add a C# example for scene unique nodes
- Update the "Cross language scripting" page
- Add a note about using `is` against `null`
Paul Joannon 2 years ago
parent
commit
ac22d6e537

+ 1 - 1
tutorials/scripting/c_sharp/c_sharp_basics.rst

@@ -218,7 +218,7 @@ Here's a blank C# script with some comments to demonstrate how it works.
             GD.Print("Hello from C# to Godot :)");
         }
 
-        public override void _Process(float delta)
+        public override void _Process(double delta)
         {
             // Called every frame. Delta is time since the last frame.
             // Update game logic here.

+ 1 - 1
tutorials/scripting/c_sharp/c_sharp_differences.rst

@@ -466,7 +466,7 @@ GDScript                C#
 ``PackedByteArray``      ``byte[]``
 ``PackedFloat32Array``   ``float[]``
 ``PackedFloat64Array``   ``double[]``
-``PackedStringArray``    ``String[]``
+``PackedStringArray``    ``string[]``
 ``PackedColorArray``     ``Color[]``
 ``PackedVector2Array``   ``Vector2[]``
 ``PackedVector3Array``   ``Vector3[]``

+ 2 - 2
tutorials/scripting/c_sharp/c_sharp_exports.rst

@@ -307,9 +307,9 @@ Exported arrays should be initialized empty.
 .. code-block:: csharp
 
     [Export]
-    private Vector3[] Vector3s = new Vector3[0];
+    private Vector3[] Vector3s = System.Array.Empty<Vector3>();
     [Export]
-    private String[] String = new String[0];
+    private string[] Strings = System.Array.Empty<string>();
 
 
 You can omit the default value, but then it would be null if not assigned.

+ 10 - 5
tutorials/scripting/c_sharp/c_sharp_features.rst

@@ -67,7 +67,7 @@ Generic methods are also provided to make this type conversion transparent.
 
 To check if the node can be cast to Sprite2D, you can use the ``is`` operator.
 The ``is`` operator returns false if the node cannot be cast to Sprite2D,
-otherwise it returns true.
+otherwise it returns true. Note that using the ``is`` operator against ``null`` is always going to be falsy.
 
 .. code-block:: csharp
 
@@ -76,6 +76,11 @@ otherwise it returns true.
         // Yup, it's a Sprite2D!
     }
 
+    if (null is Sprite2D)
+    {
+        // This block can never happen.
+    }
+
 For more advanced type checking, you can look into `Pattern Matching <https://docs.microsoft.com/en-us/dotnet/csharp/pattern-matching>`_.
 
 .. _doc_c_sharp_signals:
@@ -107,7 +112,7 @@ If you want to connect a signal in the editor, you need to (re)build the project
 
     public void MyCallbackWithArguments(string foo, int bar)
     {
-        GD.Print("My callback with: ", foo, " and ", bar, "!");
+        GD.Print($"My callback with: {foo} and {bar}!");
     }
 
     public void SomeFunction()
@@ -122,11 +127,11 @@ Emitting signals is done with the ``EmitSignal`` method.
 
     public void SomeFunction()
     {
-        EmitSignal(nameof(MySignal));
-        EmitSignal(nameof(MySignalWithArguments), "hello there", 28);
+        EmitSignal(SignalName.MySignal);
+        EmitSignal(SignalName.MySignalWithArguments, "hello there", 28);
     }
 
-Notice that you can always reference a signal name with the ``nameof`` keyword (applied on the delegate itself).
+Notice that you can always reference a signal name with its generated ``SignalName``.
 
 It is possible to bind values when establishing a connection by passing a Godot array.
 

+ 1 - 1
tutorials/scripting/creating_script_templates.rst

@@ -166,7 +166,7 @@ use these as the base for creating other templates:
        }
 
        // Called every frame. 'delta' is the elapsed time since the previous frame.
-       public override void _Process(float delta)
+       public override void _Process(double delta)
        {
        }
    }

+ 19 - 37
tutorials/scripting/cross_language_scripting.rst

@@ -16,11 +16,7 @@ The following two scripts will be used as references throughout this page.
 
     extends Node
 
-    var str1 : String = "foo"
-    var str2 : String setget ,get_str2
-
-    func get_str2() -> String:
-        return "foofoo"
+    var my_field : String = "foo"
 
     func print_node_name(node : Node) -> void:
         print(node.get_name())
@@ -39,23 +35,22 @@ The following two scripts will be used as references throughout this page.
 
     public partial class MyCSharpNode : Node
     {
-        public String str1 = "bar";
-        public String str2 { get { return "barbar"; } }
+        public string myField = "bar";
 
         public void PrintNodeName(Node node)
         {
-            GD.Print(node.GetName());
+            GD.Print(node.Name);
         }
 
-        public void PrintArray(String[] arr)
+        public void PrintArray(string[] arr)
         {
-            foreach (String element in arr)
+            foreach (string element in arr)
             {
                 GD.Print(element);
             }
         }
 
-        public void PrintNTimes(String msg, int n)
+        public void PrintNTimes(string msg, int n)
         {
             for (int i = 0; i < n; ++i)
             {
@@ -77,7 +72,7 @@ Using C# from GDScript doesn't need much work. Once loaded
 (see :ref:`doc_gdscript_classes_as_resources`), the script can be instantiated
 with :ref:`new() <class_CSharpScript_method_new>`.
 
-::
+.. code-block:: gdscript
 
     var my_csharp_script = load("res://path_to_cs_file.cs")
     var my_csharp_node = my_csharp_script.new()
@@ -91,7 +86,7 @@ with :ref:`new() <class_CSharpScript_method_new>`.
     ``Invalid call. Nonexistent function `new` in base``.
 
     For example, MyCoolNode.cs should contain a class named MyCoolNode.
-    
+
     The C# class needs to derive a Godot class, for example ``Godot.Object``.
     Otherwise, the same error will occur.
 
@@ -121,18 +116,11 @@ Accessing C# fields from GDScript
 Accessing C# fields from GDScript is straightforward, you shouldn't have
 anything to worry about.
 
-::
-
-    print(my_csharp_node.str1) # bar
-    my_csharp_node.str1 = "BAR"
-    print(my_csharp_node.str1) # BAR
+.. code-block:: gdscript
 
-    print(my_csharp_node.str2) # barbar
-    # my_csharp_node.str2 = "BARBAR" # This line will hang and crash
-
-Note that it doesn't matter if the field is defined as a property or an
-attribute. However, trying to set a value on a property that does not define
-a setter will result in a crash.
+    print(my_csharp_node.myField) # bar
+    my_csharp_node.myField = "BAR"
+    print(my_csharp_node.myField) # BAR
 
 Accessing GDScript fields from C#
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -143,12 +131,9 @@ and :ref:`Object.Set() <class_Object_method_set>`. The first argument is the nam
 
 .. code-block:: csharp
 
-    GD.Print(myGDScriptNode.Get("str1")); // foo
-    myGDScriptNode.Set("str1", "FOO");
-    GD.Print(myGDScriptNode.Get("str1")); // FOO
-
-    GD.Print(myGDScriptNode.Get("str2")); // foofoo
-    // myGDScriptNode.Set("str2", "FOOFOO"); // This line won't do anything
+    GD.Print(myGDScriptNode.Get("my_field")); // foo
+    myGDScriptNode.Set("my_field", "FOO");
+    GD.Print(myGDScriptNode.Get("my_field")); // FOO
 
 Keep in mind that when setting a field value you should only use types the
 GDScript side knows about.
@@ -165,7 +150,7 @@ marshalling process will do its best to cast the arguments to match
 function signatures.
 If that's impossible, you'll see the following error: ``Invalid call. Nonexistent function `FunctionName```.
 
-::
+.. code-block:: gdscript
 
     my_csharp_node.PrintNodeName(self) # myGDScriptNode
     # my_csharp_node.PrintNodeName() # This line will fail.
@@ -190,12 +175,9 @@ to said method.
 
     myGDScriptNode.Call("print_n_times", "Hello there!", 2); // Hello there! Hello there!
 
-    // When dealing with functions taking a single array as arguments, we need to be careful.
-    // 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.
-    String[] arr = new String[] { "a", "b", "c" };
-    // myGDScriptNode.Call("print_array", arr); // This line will fail silently and won't error out.
-    myGDScriptNode.Call("print_array", (object)arr); // a, b, c
-    myGDScriptNode.Call("print_array", (object)new int[] { 1, 2, 3 }); // 1, 2, 3
+    string[] arr = new string[] { "a", "b", "c" };
+    myGDScriptNode.Call("print_array", arr); // a, b, c
+    myGDScriptNode.Call("print_array", new int[] { 1, 2, 3 }); // 1, 2, 3
     // Note how the type of each array entry does not matter as long as it can be handled by the marshaller
 
 .. warning::

+ 4 - 4
tutorials/scripting/idle_and_physics_processing.rst

@@ -33,7 +33,7 @@ The engine calls this method every time it draws a frame:
 
  .. code-tab:: csharp
 
-    public override void _Process(float delta)
+    public override void _Process(double delta)
     {
         // Do something...
     }
@@ -65,7 +65,7 @@ The engine calls this method before every physics step:
 
  .. code-tab:: csharp
 
-    public override void _PhysicsProcess(float delta)
+    public override void _PhysicsProcess(double delta)
     {
         // Do something...
     }
@@ -94,9 +94,9 @@ single Label node, with the following script attached to it:
 
     public partial class CustomLabel : Label
     {
-        private float _time;
+        private double _time;
 
-        public override void _Process(float delta)
+        public override void _Process(double delta)
         {
             _time += delta;
             Text = _time.ToString(); // 'Text' is a built-in Label property.

+ 7 - 7
tutorials/scripting/instancing_with_signals.rst

@@ -43,9 +43,9 @@ given velocity:
     {
         Vector2 Velocity = new Vector2();
 
-        public override void _PhysicsProcess(float delta)
+        public override void _PhysicsProcess(double delta)
         {
-            Position += Velocity * delta;
+            Position += Velocity * (float)delta;
         }
     }
 
@@ -115,18 +115,18 @@ Here is the code for the player using signals to emit the bullet:
 
         private PackedScene _bullet = GD.Load<PackedScene>("res://Bullet.tscn");
 
-        public override void _Input(InputEvent event)
+        public override void _Input(InputEvent @event)
         {
-            if (input is InputEventMouseButton mouseButton)
+            if (@event is InputEventMouseButton mouseButton)
             {
-                if (mouseButton.ButtonIndex == (int)ButtonList.Left && mouseButton.Pressed)
+                if (mouseButton.ButtonIndex == MouseButton.Left && mouseButton.Pressed)
                 {
-                    EmitSignal(nameof(Shoot), _bullet, Rotation, Position);
+                    EmitSignal(SignalName.Shoot, _bullet, Rotation, Position);
                 }
             }
         }
 
-        public override _Process(float delta)
+        public override void _Process(double delta)
         {
             LookAt(GetGlobalMousePosition());
         }

+ 2 - 2
tutorials/scripting/nodes_and_scene_instances.rst

@@ -28,7 +28,7 @@ To do so, you can use the following code.
     var camera2d
 
     func _ready():
-        sprite2d = get_node("Sprite2D")
+        sprite2d = get_node("Sprite")
         camera2d = get_node("Camera2D")
 
  .. code-tab:: csharp
@@ -40,7 +40,7 @@ To do so, you can use the following code.
     {
         base._Ready();
 
-        _sprite2D = GetNode<Sprite2D>("Sprite2D");
+        _sprite2D = GetNode<Sprite2D>("Sprite");
         _camera2D = GetNode<Camera2D>("Camera2D");
     }
 

+ 4 - 4
tutorials/scripting/overridable_functions.rst

@@ -86,13 +86,13 @@ information, read the dedicated documentation:
 
  .. code-tab:: csharp
 
-    public override void _Process(float delta)
+    public override void _Process(double delta)
     {
         // Called every frame, as often as possible.
         base._Process(delta);
     }
 
-    public override void _PhysicsProcess(float delta)
+    public override void _PhysicsProcess(double delta)
     {
         // Called every physics frame.
         base._PhysicsProcess(delta);
@@ -126,14 +126,14 @@ To learn more about inputs in Godot, see the :ref:`Input section <toc-learn-feat
     // Called once for every event.
     public override void _UnhandledInput(InputEvent @event)
     {
-        base._UnhandledInput(event);
+        base._UnhandledInput(@event);
     }
 
     // Called once for every event, before _unhandled_input(), allowing you to
     // consume some events.
     public override void _Input(InputEvent @event)
     {
-        base._Input(event);
+        base._Input(@event);
     }
 
 There are some more overridable functions like

+ 4 - 3
tutorials/scripting/resources.rst

@@ -253,7 +253,7 @@ Attach a script to it named ``bot_stats.gd`` (or just create a new script, and t
                 {
                     Health = health;
                     SubResource = subResource;
-                    Strings = strings ?? Array.Empty<string>();
+                    Strings = strings ?? System.Array.Empty<string>();
                 }
             }
         }
@@ -288,7 +288,8 @@ Now, create a :ref:`CharacterBody3D <class_CharacterBody3D>`, name it ``Bot``, a
 
                 public override void _Ready()
                 {
-                    if (Stats != null && Stats is BotStats botStats) {
+                    if (Stats is BotStats botStats)
+                    {
                         GD.Print(botStats.Health); // Prints '10'.
                     }
                 }
@@ -330,7 +331,7 @@ Now, select the :ref:`CharacterBody3D <class_CharacterBody3D>` node which we nam
 
         public partial class BotStatsTable : Resource
         {
-            private Godot.Dictionary<String, BotStats> _stats = new Godot.Dictionary<String, BotStats>();
+            private Godot.Dictionary<string, BotStats> _stats = new Godot.Dictionary<string, BotStats>();
 
             public BotStatsTable()
             {

+ 4 - 0
tutorials/scripting/scene_unique_nodes.rst

@@ -36,3 +36,7 @@ name in the path for ``get_node()``. For example:
  .. code-tab:: gdscript GDScript
 
     get_node("%RedButton").text = "Hello"
+
+ .. code-tab:: csharp
+
+    GetNode<Button>("%RedButton").Text = "Hello";