Browse Source

Merge pull request #1368 from paulloz/csharp-custom-drawing-in-2d

Translate 'Custom drawing in 2D' to C#
Max Hilbrunner 7 years ago
parent
commit
546c9233d4
1 changed files with 212 additions and 46 deletions
  1. 212 46
      tutorials/2d/custom_drawing_in_2d.rst

+ 212 - 46
tutorials/2d/custom_drawing_in_2d.rst

@@ -39,14 +39,22 @@ Add a script to any :ref:`CanvasItem <class_CanvasItem>`
 derived node, like :ref:`Control <class_Control>` or
 :ref:`Node2D <class_Node2D>`. Then override the _draw() function.
 
-::
+.. tabs::
+ .. code-tab:: gdscript GDScript
 
     extends Node2D
 
     func _draw():
-        #your draw commands here
+        # Your draw commands here
         pass
 
+ .. code-tab:: csharp
+
+    public override void _Draw()
+    {
+        // Your draw commands here
+    }
+
 Draw commands are described in the :ref:`CanvasItem <class_CanvasItem>`
 class reference. There are plenty of them.
 
@@ -63,7 +71,8 @@ in that same node and a new _draw() call will happen.
 Here is a little more complex example. A texture variable that will be
 redrawn if modified:
 
-::
+.. tabs::
+ .. code-tab:: gdscript GDScript
 
     extends Node2D
 
@@ -78,20 +87,61 @@ redrawn if modified:
     func _draw():
         draw_texture(texture, Vector2())
 
+ .. code-tab:: csharp
+
+    public class CustomNode2D : Node2D
+    {
+        private Texture _texture;
+        public Texture Texture
+        {
+            get
+            {
+                return _texture;
+            }
+
+            set
+            {
+                _texture = value;
+                Update();
+            }
+        }
+
+        public override void _Draw()
+        {
+            DrawTexture(_texture, new Vector2());
+        }
+    }
+
 In some cases, it may be desired to draw every frame. For this, just
 call update() from the _process() callback, like this:
 
-::
+.. tabs::
+ .. code-tab:: gdscript GDScript
 
     extends Node2D
 
     func _draw():
-        #your draw commands here
+        # Your draw commands here
         pass
 
     func _process(delta):
         update()
 
+ .. code-tab:: csharp
+
+    public class CustomNode2D : Node2D
+    {
+        public override _Draw()
+        {
+            // Your draw commands here
+        }
+
+        public override _Process(delta)
+        {
+            Update();
+        }
+    }
+
 
 An example: drawing circular arcs
 ----------------------------------
@@ -106,20 +156,38 @@ An arc is defined by its support circle parameters. That is: the center position
 
 Basically, drawing a shape on screen requires it to be decomposed into a certain number of points, linked from one to the following one. As you can imagine, the more points your shape is made of, the smoother it will appear, but the heavier it will be, in terms of processing cost. In general, if your shape is huge (or in 3D, close to the camera), it will require more points to be drawn without it being angular-looking. On the contrary, if your shape is small (or in 3D, far from the camera), you may reduce its number of points to save processing costs. This is called *Level of Detail (LoD)*. In our example, we will simply use a fixed number of points, no matter the radius.
 
-::
+.. tabs::
+ .. code-tab:: gdscript GDScript
 
     func draw_circle_arc(center, radius, angle_from, angle_to, color):
         var nb_points = 32
         var points_arc = PoolVector2Array()
     
         for i in range(nb_points+1):
-            var angle_point = angle_from + i * (angle_to-angle_from) / nb_points - 90
-            var point = center + Vector2(cos(deg2rad(angle_point)), sin(deg2rad(angle_point))) * radius
-            points_arc.push_back(point)
+            var angle_point = deg2rad(angle_from + i * (angle_to-angle_from) / nb_points - 90)
+            points_arc.push_back(center + Vector2(cos(angle_point), sin(angle_point)) * radius)
     
         for index_point in range(nb_points):
             draw_line(points_arc[index_point], points_arc[index_point + 1], color)
 
+ .. code-tab:: csharp
+
+    public void DrawCircleArc(Vector2 center, float radius, float angleFrom, float angleTo, Color color)
+    {
+        int nbPoints = 32;
+        var pointsArc = new Vector2[nbPoints];
+
+        for (int i = 0; i < nbPoints; ++i)
+        {
+            float anglePoint = Mathf.Deg2Rad(angleFrom + i * (angleTo - angleFrom) / nbPoints - 90f);
+            pointsArc[i] = center + new Vector2(Mathf.Cos(anglePoint), Mathf.Sin(anglePoint)) * radius;
+        }
+
+        for (int i = 0; i < nbPoints - 1; ++i)
+            DrawLine(pointsArc[i], pointsArc[i + 1], color);
+    }
+
+
 Remember the number of points our shape has to be decomposed into? We fixed this number in the nb_points variable to a value of 32. Then, we initialize an empty PoolVector2Array, which is simply an array of Vector2.
 
 The next step consists of computing the actual positions of these 32 points that compose an arc. This is done in the first for-loop: we iterate over the number of points for which we want to compute the positions, plus one to include the last point. We first determine the angle of each point, between the starting and ending angles. 
@@ -134,7 +202,9 @@ Draw the arc on screen
 ^^^^^^^^^^^^^^^^^^^^^^
 We now have a function that draws stuff on the screen: it is time to call in the _draw() function.
 
-::
+.. tabs::
+
+ .. code-tab:: gdscript GDScript
 
     func _draw():
         var center = Vector2(200, 200)
@@ -144,6 +214,18 @@ We now have a function that draws stuff on the screen: it is time to call in the
         var color = Color(1.0, 0.0, 0.0)
         draw_circle_arc(center, radius, angle_from, angle_to, color)
 
+ .. code-tab:: csharp
+
+    public override void _Draw()
+    {
+        var center = new Vector2(200, 200);
+        float radius = 80;
+        float angleFrom = 75;
+        float angleTo = 195;
+        var color = new Color(1, 0, 0);
+        DrawCircleArc(center, radius, angleFrom, angleTo, color);
+    }
+
 Result:
 
 .. image:: img/result_drawarc.png
@@ -154,7 +236,8 @@ Arc polygon function
 ^^^^^^^^^^^^^^^^^^^^
 We can take this a step further and not only write a function that draws the plain portion of the disc defined by the arc, but also its shape. The method is exactly the same as previously, except that we draw a polygon instead of lines:
 
-::
+.. tabs::
+ .. code-tab:: gdscript GDScript
 
     func draw_circle_arc_poly(center, radius, angle_from, angle_to, color):
         var nb_points = 32
@@ -163,10 +246,28 @@ We can take this a step further and not only write a function that draws the pla
         var colors = PoolColorArray([color])
     
         for i in range(nb_points+1):
-            var angle_point = angle_from + i * (angle_to - angle_from) / nb_points - 90
-            points_arc.push_back(center + Vector2(cos(deg2rad(angle_point)), sin(deg2rad(angle_point))) * radius)
+            var angle_point = deg2rad(angle_from + i * (angle_to - angle_from) / nb_points - 90
+            points_arc.push_back(center + Vector2(cos(angle_point), sin(angle_point)) * radius)
         draw_polygon(points_arc, colors)
         
+ .. code-tab:: csharp
+
+    public void DrawCircleArcPoly(Vector2 center, float radius, float angleFrom, float angleTo, Color color)
+    {
+        int nbPoints = 32;
+        var pointsArc = new Vector2[nbPoints + 1];
+        pointsArc[0] = center;
+        var colors = new Color[] { color };
+
+        for (int i = 0; i < nbPoints; ++i)
+        {
+            float anglePoint = Mathf.Deg2Rad(angleFrom + i * (angleTo - angleFrom) / nbPoints - 90);
+            pointsArc[i + 1] = center + new Vector2(Mathf.Cos(anglePoint), Mathf.Sin(anglePoint)) * radius;
+        }
+
+        DrawPolygon(pointsArc, colors);
+    }
+
         
 .. image:: img/result_drawarc_poly.png
 
@@ -176,15 +277,23 @@ Alright, we are now able to draw custom stuff on screen. However, it is very sta
 
 First, we have to make both angle_from and angle_to variables global at the top of our script. Also note that you can store them in other nodes and access them using get_node().
 
-::
+.. tabs::
+ .. code-tab:: gdscript GDScript
 
- extends Node2D
+    extends Node2D
 
- var rotation_ang = 50
- var angle_from = 75
- var angle_to = 195
+    var rotation_angle = 50
+    var angle_from = 75
+    var angle_to = 195
 
+ .. code-tab:: csharp
 
+    public class CustomNode2D : Node2D
+    {
+        private float _rotationAngle = 50;
+        private float _angleFrom = 75;
+        private float _angleTo = 195;
+    }
 
 We make these values change in the _process(delta) function. 
 
@@ -192,32 +301,71 @@ We also increment our angle_from and angle_to values here. However, we must not
 
 Finally, we must not forget to call the update() function, which automatically calls _draw(). This way, you can control when you want to refresh the frame.
 
-::
+.. tabs::
+ .. code-tab:: gdscript GDScript
 
- func wrap(value, min_val, max_val):
-     var f1 = value - min_val
-     var f2 = max_val - min_val
-     return fmod(f1, f2) + min_val
+    func wrap(value, min_val, max_val):
+        var f1 = value - min_val
+        var f2 = max_val - min_val
+        return fmod(f1, f2) + min_val
 
- func _process(delta):
-     angle_from += rotation_ang
-     angle_to += rotation_ang
+    func _process(delta):
+        angle_from += rotation_ang
+        angle_to += rotation_ang
      
-     # we only wrap angles if both of them are bigger than 360
-     if angle_from > 360 and angle_to > 360:
-         angle_from = wrap(angle_from, 0, 360)
-         angle_to = wrap(angle_to, 0, 360)
-     update()
+        # We only wrap angles if both of them are bigger than 360
+        if angle_from > 360 and angle_to > 360:
+            angle_from = wrap(angle_from, 0, 360)
+            angle_to = wrap(angle_to, 0, 360)
+        update()
+
+ .. code-tab:: csharp
+
+    private float Wrap(float value, float minVal, float maxVal)
+    {
+        float f1 = value - minVal;
+        float f2 = maxVal - minVal;
+        return (f1 % f2) + minVal;
+    }
+
+    public override void _Process(float delta)
+    {
+        _angleFrom += _rotationAngle;
+        _angleTo += _rotationAngle;
+
+        // We only wrap angles if both of them are bigger than 360
+        if (_angleFrom > 360 && _angleTo > 360)
+        {
+            _angleFrom = Wrap(_angleFrom, 0, 360);
+            _angleTo = Wrap(_angleTo, 0, 360);
+        }
+        Update();
+    }
+
 
 Also, don't forget to modify the _draw() function to make use of these variables:
-::
 
- func _draw():
-	var center = Vector2(200, 200)
-	var radius = 80
-	var color = Color(1.0, 0.0, 0.0)
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+     func _draw():
+        var center = Vector2(200, 200)
+        var radius = 80
+        var color = Color(1.0, 0.0, 0.0)
+
+        draw_circle_arc( center, radius, angle_from, angle_to, color )
+
+ .. code-tab:: csharp
+
+    public override void _Draw()
+    {
+        var center = new Vector2(200, 200);
+        float radius = 80;
+        var color = new Color(1, 0, 0);
+
+        DrawCircleArc(center, radius, _angleFrom, _angleTo, color);
+    }
 
-	draw_circle_arc( center, radius, angle_from, angle_to, color )
 
 Let's run!
 It works, but the arc is rotating insanely fast! What's wrong?
@@ -226,17 +374,35 @@ The reason is that your GPU is actually displaying the frames as fast as it can.
 
 In our case, we simply need to multiply our 'rotation_ang' variable by 'delta' in the _process() function. This way, our 2 angles will be increased by a much smaller value, which directly depends on the rendering speed.
 
-::
+.. tabs::
+ .. code-tab:: gdscript GDScript
 
- func _process(delta):
-     angle_from += rotation_ang * delta
-     angle_to += rotation_ang * delta
+    func _process(delta):
+        angle_from += rotation_ang * delta
+        angle_to += rotation_ang * delta
      
-     # we only wrap angles if both of them are bigger than 360
-     if angle_from > 360 and angle_to > 360:
-         angle_from = wrap(angle_from, 0, 360)
-         angle_to = wrap(angle_to, 0, 360)
-     update()
+        # we only wrap angles if both of them are bigger than 360
+        if angle_from > 360 and angle_to > 360:
+            angle_from = wrap(angle_from, 0, 360)
+            angle_to = wrap(angle_to, 0, 360)
+        update()
+
+ .. code-tab:: csharp
+
+    public override void _Process(float delta)
+    {
+        _angleFrom += _rotationAngle * delta;
+        _angleTo += _rotationAngle * delta;
+
+        // We only wrap angles if both of them are bigger than 360
+        if (_angleFrom > 360 && _angleTo > 360)
+        {
+            _angleFrom = Wrap(_angleFrom, 0, 360);
+            _angleTo = Wrap(_angleTo, 0, 360);
+        }
+        Update();
+    }
+
 
 Let's run again! This time, the rotation displays fine!