瀏覽代碼

Added documentation on optimization.

Added missing containers files.
Juan Linietsky 6 年之前
父節點
當前提交
dbf5a855d5

+ 1 - 0
index.rst

@@ -101,6 +101,7 @@ The main documentation for the site is organized into the following sections:
    tutorials/platform/index
    tutorials/threads/index
    tutorials/content/index
+   tutorials/optimization/index
    tutorials/misc/index
    tutorials/debug/index
    tutorials/legal/index

二進制
tutorials/gui/container_example.gif


二進制
tutorials/gui/containers_tab_click.gif


+ 167 - 0
tutorials/gui/gui_containers.rst

@@ -0,0 +1,167 @@
+.. _doc_gui_containers:
+
+Containers
+===========
+
+For basic multiple resolution handling for GUIs, :ref:`anchors <size_and_anchors>` are an 
+efficient way to handle different aspect ratios.
+
+For more complex user interfaces, however, they can become difficult to use. 
+
+This is often the case of games, such as RPGs, online chats, tycoons or simulations. Another common case where more advanced layout features may be required is in-game tools (or simply just tools). 
+
+All these situations require a more capable OS-like user interface, with advanced layout and formatting.
+
+Container Layout
+-----------------
+
+Containers provide a huge amount layout power (as an example, the Godot editor user interface is entirely done using them):
+
+   .. image:: img/godot_containers.png
+
+When a :ref:`Container <class_Container>`-derived node is used, all children :ref:`Control <class_Control>` nodes give up their
+positioning ability to it. This means the *Container* will control their positioning and any attempt to manually alter these
+nodes will be either ignored or invalidated the next time their parent is resized.
+
+Likewise, when a *Container* derived node is resized, all it's children will be re-positioned according to it, with a behavior based on the type of container used:
+
+   .. image:: img/container_example.gif
+
+Example of *HBoxContainer* resizing children buttons.
+
+The real strength of containers is that they can be nested (as nodes), allowing the creation of very complex layouts that resize effortlessly.
+
+Size Flags
+----------
+
+When adding a node to a container, the way the container treats each child depends mainly on their *size flags*. These flags
+can be found by inspecting any control that is a child of a *Container*.
+
+   .. image:: img/container_size_flags.png
+
+Size flags are independent for vertical and horizontal sizing and not all containers make use of them (but most do):
+
+* **Fill**: Ensures the control *fills* the designated area within the container. No matter if a control *expands* or not (see below), it will only *fill* the designated area when this is toggled on (it is by default).
+* **Expand**: Attempts to use as most space as possible in the parent container (in this each axis). Controls that don't expand will be pushed away by those that do. Between those expanding, the amount of space they take from each other is determined by the *Ratio* (see below).
+* **Shrink Center** When expanding (and if not filling), try to remain at the center of the expanded area (by default it remains at the left or top).
+* **Shrink Center** When expanding (and if not filling), try to remain at the center of the expanded area (by default it remains at the left).
+* **Ratio** Simple ratio of how much expanded controls take up the available space in relation to each other. A control with "2", will take up twice as much available space as one with "1".
+
+Experimenting with these flags and different containers is recommended to get a better grasp on how they work.
+
+Container Types
+---------------
+
+Godot provides several container types out of the box, they serve multiple different purposes:
+
+Box Containers
+^^^^^^^^^^^^^^
+
+Arrange child controls vertically or horizontally (via :ref:`HBoxContainer <class_HBoxContainer>` and :ref:`VBoxContainer <class_VBoxContainer>`). In the opposite of the designated direction (as in, vertical for an horizontal container), it just expands the children.
+
+   .. image:: img/containers_box.png
+
+These containers make use of the *Ratio* property for children with the *Expand* flag set.
+
+Grid Container
+^^^^^^^^^^^^^^
+
+Arranges child controls in a grid layout (via :ref:`GridContainer <class_GridContainer>`, amount of columns must be specified). Uses both the vertical and horizontal expand flags.
+
+   .. image:: img/containers_grid.png
+
+Margin Container
+^^^^^^^^^^^^^^
+
+Child controls are expanded towards the bounds of this control (via :ref:`MarginContainer <class_MarginContainer>`). Padding will be
+added on the margins depending on the theme configuration.
+
+   .. image:: img/containers_margin.png
+
+Again, keep in mind that the margins are a *Theme* value, so they need to be edited at the constants overrides section if desired for a single control:
+   .. image:: img/containers_margin_constants.png
+
+Tab Container
+^^^^^^^^^^^^^^
+
+Allows to place several child controls stacked on top of each other (via :ref:`TabContainer <class_TabContainer>`), with only the *current* one visible. 
+
+   .. image:: img/containers_tab.png
+
+Changing the *current* one is done via tabs located at the top of the container, via clicking:
+
+   .. image:: img/containers_tab_click.gif
+
+The titles are generated from the node names by default (although they can be overriden via *TabContainer* API).
+
+Settings such as tab placement and *StyleBox* can be modified in the *TabContainer* theme overrides.
+
+Split Container
+^^^^^^^^^^^^^^
+
+Accepts only one or two children controls, then places them side to side with a divisor (via :ref:`HSplitContainer <class_HSplitContainer>` and :ref:`VSplitContainer <class_VSplitContainer>`). Respects both horizontal and vertical flags, as well as *Ratio*.
+
+   .. image:: img/containers_split.png
+
+The divisor can be dragged around to change the size relation between both children:
+
+   .. image:: img/containers_split_drag.gif
+
+
+PanelContainer
+^^^^^^^^^^^^^^
+
+Simple container that draws a *StyleBox*, then expands children to cover its whole area (via :ref:`PanelContainer <class_PanelContainer>`, respecting the *StyleBox* margins). It respects both the horizontal and vertical size flags.
+
+   .. image:: img/containers_panel.png
+
+This container is useful as top-level, or just to add custom backgrounds to sections of a layout.
+
+ScrollContainer
+^^^^^^^^^^^^^^
+
+Accepts a single child node. If this node is bigger than the container, scrollbars will be added to allow paning the node around (via :ref:`ScrollContainer <class_ScrollContainer>`). Both vertical and horizontal size flags are respected, and the behavior can be turned on or off per axis in the properties.
+
+   .. image:: img/containers_scroll.png
+
+Mouse wheel and touch drag (when touch is available) are also valid ways to pan the child control around.
+
+   .. image:: img/containers_center_pan.gif
+
+As in the example above, one of the most common ways to use this container is together with a *VBoxContainer* as child.
+
+
+ViewportContainer
+^^^^^^^^^^^^^^
+
+This is a special control that will only accept a single *Viewport* node as child, and it will display it as if it was an image (via :ref:`ViewportContainer <class_ViewportContainer>`).
+
+Creating custom Containers
+--------------------------
+
+It is possible to easily create a custom container using script. Here is an example of a simple container that fits children
+to its rect size:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    extends Container
+
+    func _notification(what):
+        if (what==NOTIFICATION_SORT_CHILDREN):
+            # Must re-sort the children
+            for c in get_children():
+                # Fit to own size
+                fit_child_in_rect( c, Rect2( Vector2(), rect_size ) )
+	
+    func set_some_setting():
+        # Some setting changed, ask for childre re-sort
+        queue_sort()
+
+
+	
+	
+
+
+
+

+ 0 - 3
tutorials/misc/index.rst

@@ -1,6 +1,3 @@
-Miscellaneous
-=============
-
 .. toctree::
    :maxdepth: 1
    :name: toc-learn-features-misc

+ 9 - 0
tutorials/optimization/index.rst

@@ -0,0 +1,9 @@
+Optimization
+=============
+
+.. toctree::
+   :maxdepth: 1
+   :name: toc-learn-features-optimization
+
+   using_servers
+   using_multimesh

+ 54 - 0
tutorials/optimization/using_multimesh.rst

@@ -0,0 +1,54 @@
+.. _doc_using_multimesh:
+
+Optimization Using Multimeshes
+============
+
+For large amount of instances (in the thousands), that need to be constantly processed (and certain amount of control needs to be retained), :ref:`using servers directly<doc_using_servers>` is the recommended optimization.
+
+When the amount of objects reach the dozens of thousands, hundreds of thousands or even millions, none of these approaches are efficient any more. Still, depending on the requirements, there is one more optimization possible.
+
+
+Multimeshes
+-----------
+
+A :ref:`MultiMesh<class_MultiMesh>` is a single draw primitive that can draw up to millions of objects in one go. It's extremely efficient because it uses the GPU hardware to do this (In OpenGL ES 2.0, it's less efficient because there is no hardware support for it, though).
+
+The only drawback is that there is no *screen* or *frustum* culling possible for individual instances. This means, that millions of objects will be *always* or *never* drawn, depending on the visibility of the whole MultiMesh. It is possible to provide a custom visibility rect for them, but it will always be *all-or-none* visibility.
+
+If the objects are simple enough (just a couple of vertices), this is generally not much of a problem as most modern GPUs are optimized for this use case.
+
+If logic is too simple, it is also possible to execute it inside the vertex shader (using the INSTANCE_ID integer built-in constant). Information to it can be provided via textures (there are floating point :ref:`Image<class_Image>` formats which are ideal for this).
+
+Another alternative is to use GDNative and C++, which should be extremely efficient (it's possible to set the entire state for all objects using linear memory via the `VisualServer.multimesh_set_as_bulk_array()<class_VisualServer_method_multimesh_set_as_bulk_array>` function. This way, the array can be created with multiple threads, then set in one call, providing high cache efficiency.
+
+Finally, it's not required to have all multimesh instances visible. The amount of visible ones can be controlled with the *MultiMesh.visible_instance_count* property. The typical workflow is to allocate the maximum amount of instances that will be used,
+then change the amount visible depending on how many are currently needed.
+
+Multimesh Example
+-----------
+
+Here is a simple example of using a multimesh from code (using GDScript). Other languages may be more efficient for millions of objects, but for a few thousands, GDScript should be fine.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    extends MultiMeshInstance
+    
+    
+    func _ready():
+
+        # Create multimesh
+        multimesh = MultiMesh.new()
+        # Set format first
+        multimesh.transform_format = MultiMesh.TRANSFORM_3D
+        multimesh.color_format = MultiMesh.COLOR_NONE
+        multimesh.custom_data_format = MultiMesh.CUSTOM_DATA_NONE
+        # Then resize (else changing format is not allowed)
+        multimesh.instance_count = 10000
+        # Maybe not all of them should be visible at first
+        multimesh.visible_instance_count = 1000
+        # Set instance transforms
+        for i in range(multimesh.visible_instance_count):
+            multimesh.set_instance_transform(i,Transform( Basis(), Vector3(i*20,0,0) ) )
+
+

+ 159 - 0
tutorials/optimization/using_servers.rst

@@ -0,0 +1,159 @@
+.. _doc_using_servers:
+
+Optimization Using Servers
+============
+
+Engines like Godot provide increased ease of use thanks to it's high level constructs and features. Most of them are accessed and used via the :ref:`Scene System<doc_scene_tree>`. Using nodes and resources simplify project organization and asset management
+in complex games.
+
+There are, of course always drawbacks
+
+* There is an extra layer of complexity
+* Performance is lower than using simple APIs directly
+* It is not possible to use multiple threads to control them
+* More memory is needed.
+
+In far most cases, this is not really a problem (Godot is very optimized, and most operations are handled with signals, so no polling is required). Still, sometimes it may be. As an example, dealing with dozens of thousands of instances for something that needs to be processed every frame can pose a bottleneck. 
+
+This type of situation makes some programmers regret they are using a game engine and wish they could go back to a more handcrafted, low level implementation of game code. 
+
+Still, Godot is designed to work around this problem too.
+
+Servers
+-------
+
+One of the most interesting design decisions for Godot, is the fact that the whole scene system is *optional*. While it's not currently possible to compile it out, it can be completely bypassed.
+
+At the core, Godot uses the concept of Servers. They are very low level APIs to control rendering, physics, sound, etc. The scene system is built on top of them and uses them directly. The most common servers are:
+
+* :ref:`Visual Server<class_VisualServer>` handles everything related to graphics.
+* :ref:`PhysicsServer<class_PhysicsServer>` handles everything related to 3D physics.
+* :ref:`Physics2DServer<class_Physics2DServer>` handles everything related to 2D physics.
+* :ref:`AudioServer<class_AudioServer>` handles everything related to audio.
+
+Just explore their API and you will realize that the all functions provided are low level implementations of everything Godot allows to do.
+
+RIDs
+----
+
+The key to using servers is understanding RID objects (RID means Resource ID). These are opaque handles to the sever implementation. They are allocated and freed manually. Almost every function in the servers requires RIDs to access the actual resource.
+
+Most Godot nodes and resources contain internally these RIDs from the servers, and they can be obtained with different functions. In fact, anything that inherits `Resource<class_Resource>` can be directly casted to an RID (not all resources contain an RID, though, in such cases the RID will be empty). In fact, resources can be passed to server APIs as RIDs. Just make sure to keep references to the resources ouside the server, because if the resource is erased, the internal RID is erased too.
+
+For nodes, there are many functions available:
+
+* For CanvasItem, the `CanvasItem.get_canvas_item()<class_CanvasItem_method_get_canvas_item>` method will return the canvas item RID in the server. 
+* For CanvasLayer, the `CanvasLayer.get_canvas()<class_CanvasLayer_method_get_canvas>` method will return the canvas RID in the server. 
+* For Viewport, the `Viewport.get_viewport_rid()<class_Viewport_method_get_viewport_rid>` method will return the viewport RID in the server. 
+* For 3D, the `World<class_World>` resource (obtainable in the *Viewport* and *Spatial* nodes, contains function to get the *VisualServer Scenario*, and the *PhysicsServer Space*. This allows creating 3D objects directly with the server API and using them.
+* For 2D, the `World2D<class_World2D>` resource (obtainable in the *Viewport* and *CanvasItem* nodes, contains function to get the *VisualServer Canvas*, and the *Physics2DServer Space*. This allows creating 2D objects directly with the server API and using them.
+* The`VisualInstance<class_VisualInstance` class, allows getting the scenario *instance* and *instance base* via the `VisualInstance.get_instance()<class_VisualInstance_method_get_instance>` and `VisualInstance.get_base()<class_VisualInstance_method_get_base>` respectively.
+
+Just explore the nodes and resources you are familiar with and find the functions to obtain the server *RIDs*. 
+
+It is not advised to control RIDs from objects that already have a node associated. Instead, server functions should always be used for creating and controlling new ones and interacting with the existing ones.
+
+Creating a sprite
+-----------------
+
+This is a simple example of how to create a sprite from code and move it using the low level Canvas Item API.
+
+extends Node2D
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func _ready():
+	
+        # Create a canvas item, child of this node
+        var ci_rid = VisualServer.canvas_item_create()
+        # Make this node the parent
+        VisualServer.canvas_item_set_parent( ci_rid, get_canvas_item() )
+        # Draw a sprite on it
+        # Remember, keep this reference
+        var sprite = load("res://mysprite.png")
+        # Add it, centered
+        VisualServer.canvas_item_add_texture_rect(ci_rid, Rect2( sprite.get_size() / 2, sprite.get_size() ), sprite )
+        # Add the item, rotated 45 degrees and translated
+        var xform = Transform2D().rotated( deg2rad(45) ).translated( Vector2( 20, 30 ) )	
+        VisualServer.canvas_item_set_transform( ci_rid, xform )
+
+The Canvas Item API in the server allows you to add draw primitives to it. Once added, they can't be modified. The Item needs to be cleared and the primitives re-added (this is not the case for setting the transform, which can be done as many times as desired).
+
+Primitives are cleared this way:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    VisualServer.canvas_item_clear( ci_rid )
+	
+
+Instantiating a Mesh into 3D Space
+----------------------------------
+
+The 3D APIs are considerably different than the 2D ones, so the instantiation API must be used.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    extends Spatial
+    
+    func _ready():
+
+        # Create a visual instance (for 3D)
+        var instance = VisualServer.instance_create()	
+        # Set the scenario from the world, this ensures it
+        # appears with the same objects as the scene
+        var scenario = get_world().scenario
+        VisualServer.instance_set_scenario(instance,scenario)
+        # add a mesh to it
+        # remember, keep the reference
+        var mesh = load("res://mymesh.obj")
+        VisualServer.instance_set_base(instance,mesh)
+        # move the mesh around
+        var xform = Transform( Basis(), Vector3(20,100,0) )
+        VisualServer.instance_set_transform(instance,xform)
+	
+Creating a 2D Rigid Body and moving a sprite with it
+-----------------------------------------------------
+
+This creates a *RigidBody* using the *Physics2DServer* API, and moves a *Canvas Item*  when the body moves.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func  _body_moved(state : Physics2DDirectBodyState, index):
+        # Created your own canvas item, use it here
+        VisualServer.canvas_item_set_transform( canvas_item, state.transform )
+
+    func _ready():
+    
+        # Create the body
+        var body = Physics2DServer.body_create()
+        Physics2DServer.body_set_mode( body, Physics2DServer.BODY_MODE_RIGID )
+        # Add a shape
+        var shape = RectangleShape2D.new() 
+        shape.extents = Vector2(10,10)
+        # make sure to keep the shape reference!
+        Physics2DServer.body_add_shape( body, shape ) #
+        # Set space, so it collides in the same space as current scene
+        Physics2DServer.body_set_space( body, get_world_2d().space )
+        # Move initial position
+        Physics2DServer.body_set_state( body, Physics2DServer.BODY_STATE_TRANSFORM, Transform2D(0, Vector2(10,20) ) )
+        # Add the transform callback, when body moves
+        # The last parameter is optional, can be used as index if you have many bodies
+        # And a single callback.
+        Physics2DServer.body_set_force_integration_callback( body, self, "_body_moved", 0)
+	
+The 3D version should be very similar, as 2D and 3D physics servers are identical.
+
+Getting data from the servers
+-----------------------------
+
+Try to **never** request any information from *VisualServer*, *PhysicsServer* or *Physics2DServer* by calling functions unless you know what you are doing. These servers will often run asynchronously for performance and calling any function that returns a value will stall them and force them to process anything pending until the function is actually called. This will severely decrease performance if you call them every frame (and it won't be obvious why).
+
+Because of this, most APIs in such servers are designed so it's not even possible to request information back, until it's actual data that can be saved.
+
+
+
+