Просмотр исходного кода

Updated factory and collection factory manuals. Fixes to resource management manual.

Mikael Säker 8 лет назад
Родитель
Сommit
ed9634553a

+ 4 - 0
docs/en/en.json

@@ -144,6 +144,10 @@
                         "path": "/manuals/message-passing",
                         "name": "Message passing"
                     },
+                    {
+                        "path": "/manuals/resource",
+                        "name": "Resource management"
+                    },
                     {
                         "path": "/manuals/application-lifecycle",
                         "name": "Application lifecycle"

+ 37 - 5
docs/en/manuals/addressing.md

@@ -130,13 +130,12 @@ Shorthands
    msg.post("#", "reset")
   ```
 
-## Game object full identifiers
+## Game object paths
 
 To correctly understand the naming mechanism, let's look at what happens when you build and run the project:
 
 1. The editor reads the bootstrap collection ("main.collection") and all its content (game objects and other collections).
-2. The compiler creates instances for all game objects, flattening the collection hierarchy.
-3. Identifiers are built as "paths" down the collection hierarchy, assigning each game object a unique identifier.
+2. For each static game object, the compiler creates an identifier. These are built as "paths" starting at the bootstrap root, down the collection hierarchy to the object. A '/' character is added at each level.
 
 For our example above, the game will run with the following 4 game objects:
 
@@ -146,7 +145,7 @@ For our example above, the game will run with the following 4 game objects:
 - /team_2/buddy
 
 ::: sidenote
-Identities are stored as hashed values. The runtime also stores the hashed value of each collection identity and uses a rolling hash algorithm to resolve the full address.
+Identities are stored as hashed values. The runtime also stores the hash state for each collection identity which is used to continue hashing relative string to an absolute id.
 :::
 
 In runtime, the collection grouping do not exist. There is no way to find out what collection a specific game object belonged to before compilation. Nor is it possible to manipulate all the objects in a collection at once. If you need to do such operations, you can easily do the tracking yourself in code. Each object's identifier is static, it is guaranteed to stay fixed throughout the object's lifetime. This means that you can safely store the identity of an object and use it later.
@@ -169,6 +168,39 @@ The absolute address of the manager script is `"/manager#controller"` and this a
 
 ![absolute addressing](images/addressing/absolute.png)
 
+## Hashed identifiers
+
+The engine stores all identifiers as hashed values. All functions that take as argument a component or a game object accepts a string, hash or an URL object. We have seen how to use strings for addressing above.
+
+When you get the identifier of a game object, the engine will always return an absolute path identifier that is hashed:
+
+```lua
+local my_id = go.get_id()
+print(my_id) --> hash: [/path/to/the/object]
+
+local spawned_id = factory.create("#some_factory")
+print(spawned_id) --> hash: [/instance42]
+```
+
+You can use such an identifier in place of a string id, or construct one yourself. Note though that a hashed id corresponds to the path to the object, i.e. an absolute address:
+
+::: sidenote
+The reason relative addresses must be given as strings is because the engine will compute a new hash id based on the hash state of the current naming context (collection) with the given string added to the hash.
+:::
+
+```lua
+local spawned_id = factory.create("#some_factory")
+local pos = vmath.vector3(100, 100, 0)
+go.set_position(pos, spawned_id)
+
+local other_id = hash("/path/to/the/object")
+go.set_position(pos, other_id)
+
+-- This will not work! Relative addresses must be given as strings.
+local relative_id = hash("my_object")
+go.set_position(pos, relative_id)
+```
+
 ## URLs
 
 To complete the picture, let's look at the full format of Defold addresses: the URL.
@@ -181,7 +213,7 @@ socket
 : Identifies the game world of the target. This is important when working with [Collection Proxies](/manuals/collection-proxy) and is then used to identify the _dynamically loaded collection_.
 
 path
-: This part of the URL usually contains the full id of the target game object.
+: This part of the URL contains the full id of the target game object.
 
 fragment
 : The identity of the target component within the specified game object.

+ 109 - 34
docs/en/manuals/collection-factory.md

@@ -5,71 +5,146 @@ brief: This manual explains how to use collection factory components to spawn hi
 
 # Collection factories
 
-The collection factory component is used to spawn ready made blueprint hierarchies of game objects (collections) into a running game.
+The collection factory component is used to spawn groups and hierarchies of game objects stored in collection files into a running game.
 
-Collections provide a powerful mechanism to create reusable templates, or "prefabs" in Defold. For an overview on Collections, see the [Building blocks documentation](/manuals/building-blocks#_collections). Collections can be placed in the editor and be cloned into the game at compile-time, or they can be dynamically inserted into your game in two ways:
+Collections provide a powerful mechanism to create reusable templates, or "prefabs" in Defold. For an overview on Collections, see the [Building blocks documentation](/manuals/building-blocks#_collections). Collections can be placed in the editor, or they can be dynamically inserted into your game.
 
-1. Loaded through collection proxies. This method essentially loads a new isolated world (including how physics interact) into a running game. The loaded collection is accessed through a new socket. All assets contained in the collection are loaded through the proxy when you message the proxy to start loading. This makes them very useful to, for instance, change levels in a game. For more information, see the [Collection proxy documentation](/manuals/collection-proxy).
+With a collection factory component you can spawn the contents of a collection file into a game world. This is analogous to performing factory spawning of all game objects inside the collection and then building the parent-child hierarchy between the objects. A typical use case is to spawn enemies consisting of multiple game objects (enemy + weapon, for instance).
 
-2. Spawned into the current main collection with a collection factory component. This is analogous to performing factory spawning of all game objects inside the collection and then building the parent-child hierarchy between the objects. A typical use case is to spawn enemies consisting of multiple game objects (enemy + weapon, for instance).
+::: sidenote
+The *collection proxy* component is used to create a new game world, including a separate physics world, based on a collection. The new world is accessed through a new socket. All assets contained in the collection are loaded through the proxy when you message the proxy to start loading. This makes them very useful to, for instance, change levels in a game. New game worlds come with quite a lot of overhead though so do not use them for dynamic loading of small stuff. For more information, see the [Collection proxy documentation](/manuals/collection-proxy).
+:::
 
 ## Spawning a collection
 
-For simple cases, you can spawn a collection just as you would spawn a game object. Suppose we are creating a planet sprite and want to spawn complex astronaut figures on the planet surface. We can simply add a *Collection factory* to the "planet" gameobject and set *astronaut.collection* (supposing it exists) as the component's *prototype*:
+Suppose we want a character game object and a separate shield game object childed to the character. We build the game object hierarchy in a collection file and save it as "bean.collection".
 
-![Collection factory](images/collection_factory/collection_factory_factory.png)
+![Collection to spawn](images/collection_factory/collection.png)
 
-Spawning an astronaut is now a matter of sending a message to the factory:
+We then add a *Collection factory* to a gameobject that will take care of the spawning and set "bean.collection" as the component's *Prototype*:
+
+![Collection factory](images/collection_factory/factory.png)
+
+Spawning a bean and shield is now just a matter of calling the `collectionfactory.create()` function:
 
 ```lua
-local astro = collectionfactory.create("#factory", nil, nil, {}, nil)
+local bean_ids = collectionfactory.create("#bean_factory")
 ```
 
-The astronaut that is spawned is a tree of game objects, and we need to be able to address all those objects after spawning:
+The function takes 5 parameters:
+
+`url`
+: The id of the collection factory component that should spawn the new set of game objects.
+
+`[position]`
+: (optional) The world position of the spawned game objects. This should be a `vector3`. If you do not specify a position, the objects are spawned at the position of the collection factory component.
+
+`[rotation]`
+: (optional) The world rotation of the new game objects. This should be a `quat`.
 
-![Collection to spawn](images/collection_factory/collection_factory_collection.png)
+`[properties]`
+: (optional) A Lua table with `id`-`table` pairs used to initiate the spawned game objects. See below for how to construct this table.
 
-The regular factory component returns the id of the spawned object. The collection factory returns a table that maps a hash of the collection-local id of each object to the runtime id of each object. A prefix `/collection[N]/`, where `[N]` is a counter, is added to the id to uniquely identify each instance:
+`[scale]`
+: (optional) The scale of the spawned game objects. The scale can be expressed as a `number` (greater than 0) which specifies uniform scaling along all axes. You can also provide a `vector3` where each component specifies scaling along the corresponding axis.
+
+`collectionfactory.create()` returns the identities of the spawned game objects as a table. The table keys map the hash of the collection-local id of each object to the runtime id of each object:
+
+::: sidenote
+The parent-child relationship between "bean" and "shield" is *not* reflected in the returned table. This relation only exist in the runtime scene-graph, i.e. how objects are transformed together. Re-parenting an object never changes its id.
+:::
 
 ```lua
-pprint(astro)
+local bean_ids = collectionfactory.create("#bean_factory")
+go.set_scale(0.5, bean_ids[hash("/bean")])
+pprint(bean_ids)
 -- DEBUG:SCRIPT:
 -- {
---   hash: [/probe2] = hash: [/collection0/probe2],
---   hash: [/probe1] = hash: [/collection0/probe1],
---   hash: [/astronaut] = hash: [/collection0/astronaut],
+--   hash: [/shield] = hash: [/collection0/shield], -- <1>
+--   hash: [/bean] = hash: [/collection0/bean],
 -- }
 ```
-
-Observe that the parent-child relationships between "astronaut" and "probe" are not reflected in the id/path of the objects but only in the runtime scene-graph, i.e. how objects are transformed together. Re-parenting an object never changes its id.
+1. A prefix `/collection[N]/`, where `[N]` is a counter, is added to the id to uniquely identify each instance:
 
 ## Properties
 
-When spawning a collection, we can pass property parameters to each game object by constructing a table with pairs of collection-local object ids and tables of script properties to set:
+When spawning a collection, you can pass property parameters to each game object by constructing a table where the keys are object ids and the values are tables with the script properties to set.
 
 ```lua
--- planet.script
---
 local props = {}
-props[hash("/astronaut")] = { size = 10.0 }
-props[hash("/probe1")] = { color = hash("red") }
-props[hash("/probe2")] = { color = hash("green") }
-local astro = collectionfactory.create("#factory", nil, nil, props, nil)
-...
+props[hash("/bean")] = { shield = false }
+local ids = collectionfactory.create("#bean_factory", nil, nil, props)
 ```
 
-Each spawned instance of "astronaut" gets its `size` property set to the passed value and each "probe" its `color` property:
+Supposing the "bean" game object in "bean.collection" defines the "shield" property. [The Script property manual](/manuals/script-properties) contains information on script properties.
 
 ```lua
--- probe.script
---
-go.property("color", hash("blue"))
+-- bean/controller.script
+go.property("shield", true)
 
 function init(self)
-  ...
+    if not self.shield then
+        go.delete("shield")
+    end     
+end
 ```
 
-By spawning a set of astronauts and carefully placing them and passing the right properties, we can now populate the planet:
-
-![Populated planet](images/collection_factory/collection_factory_game.png)
-
+## Dynamic loading of factory resources
+
+By checking the *Load Dynamically* checkbox in the collection factory properties, the engine postpones the loading of the resources associated with the factory.
+
+![Load dynamically](images/collection_factory/load_dynamically.png)
+
+With the box unchecked the engine loads the prototype resources when the collection factory component is loaded so they are immediately ready for spawning.
+
+With the box checked, you have two options for usage:
+
+Synchronous loading
+: Call [`collectionfactory.create()`](/ref/collectionfactory/#collecionfactory.create) when you want to spawn objects. This  will load the resources synchronously, which may cause a hitch, then spawn new instances.
+
+  ```lua
+  function init(self)
+      -- No factory resources are loaded when the collection factory’s
+      -- parent collection is loaded. Calling create without
+      -- having called load will create the resources synchronously.
+      self.go_ids = collecionfactory.create("#collectionfactory")
+  end
+  
+  function final(self)  
+      -- Delete game objects. Will decref resources.
+      -- In this case resources are deleted since the collection
+      -- factory component holds no reference.
+      go.delete_all(self.go_ids)
+
+      -- Calling unload will do nothing since factory holds
+      -- no references
+      collectionfactory.unload("#factory")
+  end
+  ```
+
+Asynchronous loading
+: Call [`collectionfactory.load()`](/ref/collectionfactory/#collectionfactory.load) to explicitly load the resources asynchronously. When the resources are ready for spawning, a callback is received.
+
+  ```lua
+  function load_complete(self, url, result)
+      -- Loading is complete, resources are ready to spawn
+      self.go_ids = collectionfactory.create(url)
+  end
+  
+  function init(self)
+      -- No factory resources are loaded when the collection factory’s
+      -- parent collection is loaded. Calling load will load the resources.
+      collectionfactory.load("#factory", load_complete)
+  end
+  
+  function final(self)
+      -- Delete game object. Will decref resources.
+      -- In this case resources aren’t deleted since the collection factory
+      -- component still holds a reference.
+      go.delete_all(self.go_ids)
+  
+      -- Calling unload will decref resources held by the factory component,
+      -- resulting in resources being destroyed.
+      collectionfactory.unload("#factory")
+  end
+  ```

+ 65 - 5
docs/en/manuals/factory.md

@@ -5,14 +5,14 @@ brief: This manual explains how to use factory components to dynamically spawn g
 
 # Factory components
 
-Factory components are used to dynamically spawn game objects from a pool of objects into the running game.
+Factory components are used to dynamically spawn game objects from a pool of objects into a running game.
 
 When you add a factory component to a game object you specify in the *Prototype* property what game object file the factory should use as a blueprint for all new game objects it creates.
 
-![Factory component](images/factory/factory_component.png)
-
 ![Factory component](images/factory/factory_collection.png)
 
+![Factory component](images/factory/factory_component.png)
+
 To trigger the creation of a game object, call `factory.create()`:
 
 ```lua
@@ -49,13 +49,14 @@ For example:
 local p = go.get_position()
 p.y = vmath.lerp(math.random(), min_y, max_y)
 local component = "#star_factory"
--- Spawn with no rotation but double scale. Set the score of the star to 10.
+-- Spawn with no rotation but double scale.
+-- Set the score of the star to 10.
 factory.create(component, p, nil, { score = 10 }, 2.0) -- <1>
 ```
 1. Sets the "score" property of the star game object.
 
-.star.script
 ```lua
+-- star.script
 go.property("score", 1) -- <1>
 
 local speed = -240
@@ -143,6 +144,65 @@ function final(self)
 end
 ```
 
+## Dynamic loading of factory resources
+
+By checking the *Load Dynamically* checkbox in the factory properties, the engine postpones the loading of the resources associated with the factory.
+
+![Load dynamically](images/factory/load_dynamically.png)
+
+With the box unchecked the engine loads the prototype resources when the factory component is loaded so they are immediately ready for spawning.
+
+With the box checked, you have two options for usage:
+
+Synchronous loading
+: Call [`factory.create()`](/ref/factory/#factory.create) when you want to spawn objects. This  will load the resources synchronously, which may cause a hitch, then spawn new instances.
+
+  ```lua
+  function init(self)
+      -- No factory resources are loaded when the factory’s parent
+      -- collection is loaded. Calling create without having called
+      -- load will create the resources synchronously.
+      self.go_id = factory.create("#factory")
+  end
+  
+  function final(self)  
+      -- Delete game objects. Will decref resources.
+      -- In this case resources are deleted since the factory component
+      -- holds no reference.
+      go.delete(self.go_id)
+
+      -- Calling unload will do nothing since factory holds no references
+      factory.unload("#factory")
+  end
+  ```
+
+Asynchronous loading
+: Call [`factory.load()`](/ref/factory/#factory.load) to explicitly load the resources asynchronously. When the resources are ready for spawning, a callback is received.
+
+  ```lua
+  function load_complete(self, url, result)
+      -- Loading is complete, resources are ready to spawn
+      self.go_id = factory.create(url)
+  end
+  
+  function init(self)
+      -- No factory resources are loaded when the factory’s parent 
+      -- collection is loaded. Calling load will load the resources.
+      factory.load("#factory", load_complete)
+  end
+  
+  function final(self)
+      -- Delete game object. Will decref resources.
+      -- In this case resources aren’t deleted since the factory component
+      -- still holds a reference.
+      go.delete(self.go_id)
+  
+      -- Calling unload will decref resources held by the factory component,
+      -- resulting in resources being destroyed.
+      factory.unload("#factory")
+  end
+  ```
+
 ## Instance limits
 
 The project setting *max_instances* in *Collection related settings* limits the total number of game object instances that can exist in a world (the main.collection loaded at startup or any world loaded via a collection proxy). All game objects that exist in the world are counted agaist that limit and it does not matter if they are placed by hand in the editor or spawned in runtime through a script.

BIN
docs/en/manuals/images/factory/factory_collection.png


BIN
docs/en/manuals/images/factory/factory_component.png


BIN
docs/en/manuals/images/factory/factory_max_instances.png


BIN
docs/en/manuals/images/resource/load_dynamically.png


+ 18 - 11
docs/en/manuals/resource.md

@@ -1,11 +1,11 @@
 ---
 title: Defold resource management
-brief: This manual explains how Defold automatically manages resources and how you can manually manage loading of resources to adher to memory constraints.
+brief: This manual explains how Defold automatically manages resources and how you can manually manage loading of resources to adher to memory footprint and bundle size constraints.
 ---
 
 # Resource management
 
-For smaller games, memory may not ever be an issue. However, when making larger games, and especially on handheld devices, memory problems will likely become a problem. Defold provides a range of features to help handle different scenarios.
+If you make a very small game, the limitations of the target platform (memory footprint, bundle size, computing power and battery consumption) may not ever pose any proplems. However, when making larger games, and especially on handheld devices, memory consumption will likely be one of the biggest constraints. An experienced team will carefully make resource budgets against platform constraints. Defold provides a range of features to help manage memory and bundle size. This manual gives an overview to these features.
 
 ## The static resource tree
 
@@ -16,6 +16,8 @@ When you build a game in Defold, you statically declare the resource tree. Every
 - Collection proxy component references (collections).
 - Custom resources declared in "game.project".
 
+![Resource tree](images/resource/resource_tree.png)
+
 When the game is *bundled*, only what is in the resource tree will be included. Anything that is not referenced in the tree is left out. There is no need to manually select what to include or exclude from the bundle.
 
 When the game is *run*, the engine starts at the bootstrap root of the tree and pulls resources into memory:
@@ -24,21 +26,25 @@ When the game is *run*, the engine starts at the bootstrap root of the tree and
 - Game objects and component data.
 - Factory component prototypes (game objects and collections).
 
-However, the engine will not automatically follow and load the following types of references:
+However, the engine will not automatically load the following types of referenced resources at runtime:
 
 - Game world collections referenced through collection proxies. Game worlds are relatively large so you will need to manually trigger loading and unloading of these in code. See [the Collection proxy manual](/manuals/collection-proxy) for details.
 - Files added via the *Custom Resources* setting in "game.project". These files are manually loaded with the [sys.load_resource()](/ref/sys/#sys.load_resource) function.
 
-## Dynamically loading bundled resources
+The default way Defold bundles and loads resources can be altered to give fine grained control over how and when resources enter memory.
+
+![Resource loading](images/resource/loading.png)
 
-To postpone the loading of the resources referenced through a factory component (factory or collection factory), you can simply mark a factory with the *Load Dynamically* checkbox.
+## Dynamically loading factory resources
+
+Resources referenced by factory components are normally loaded into memory when the component is loaded. The resources are then ready for being spawned into the game as soon as the factory exists in the runtime. To change the default behavior and postpone the loading of factory resources you can simply mark a factory with the *Load Dynamically* checkbox.
 
 ![Load dynamically](images/resource/load_dynamically.png)
 
-By checking this box, the engine will still include the referenced resources in the game bundle, but it will not be automatically loaded when the game object holding the component is loaded. Instead, you have two options:
+With this box checked, the engine will still include the referenced resources in the game bundle, but it will not automatically load the factory resources. Instead, you have two options:
 
-1. Call `factory.create()` or `collectionfactory.create()` which will load the resources synchronously, then spawn new instances.
-2. Call `factory.load()` or `collectionfactory.load()` to load the resources asynchronously. When the resources are ready, a callback is received and you can spawn new instances.
+1. Call [`factory.create()`](/ref/factory/#factory.create) or [`collectionfactory.create()`](/ref/collectionfactory/#collectionfactory.create) when you want to spawn objects. This  will load the resources synchronously, then spawn new instances.
+2. Call [`factory.load()`](/ref/factory/#factory.load) or [`collectionfactory.load()`](/ref/collectionfactory/#collectionfactory.load) to load the resources asynchronously. When the resources are ready for spawning, a callback is received.
 
 Read the [Factory manual](/manuals/factory) and the [Collection factory manual](/manual/collection-factory) for details on how this works.
 
@@ -46,11 +52,12 @@ Read the [Factory manual](/manuals/factory) and the [Collection factory manual](
 
 Defold keeps reference counters for all resources. If a resource's counter reaches zero it means that nothing refers to it anymore. The resource is then automatically unloaded from memory. For example, if you delete all objects spawned by a factory and you also delete the object holding the factory component, the resources previously referred to by the factory is unloaded from memory.
 
-For factories that are marked *Load Dynamically* you can call the `factory.unload()` or `collectionfactory.unload()` function. This removes the factory component's reference to the resource. If nothing else refers to the resource (all spawned objects are deleted, for instance), thie resource is unloaded from memory.
+For factories that are marked *Load Dynamically* you can call the [`factory.unload()`](/ref/factory/#factory.unload) or [`collectionfactory.unload()`](/ref/collectionfactory/#collectionfactory.unload) function. This call removes the factory component's reference to the resource. If nothing else refers to the resource (all spawned objects are deleted, for instance), the resource will be unloaded from memory.
 
 ## Excluding resources from bundle
 
-With collection proxies, it is possible to leave out all the resources the component refers to from the bundling process.
+With collection proxies, it is possible to leave out all the resources the component refers to from the bundling process. This is useful if you need to keep the bundle size to a minimum. For instance, when running games on the web as HTML5 the browser will download the whole bundle before executing the game.
 
-![Resource loading](images/resource/loading.png)
+![Exclude](images/resource/exclude.png)
 
+By marking a collection proxy as *Exclude* the referenced resource will be left out of the game bundle. Instead, you can store excluded collections on selected cloud storage. The [Live update manual](/manuals/live-update/) explains how this feature works.