Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision Next revisionBoth sides next revision | ||
quick:design [2018/05/18 21:40] – pedro | quick:design [2018/11/15 10:39] – [In Behavior Bricks efficiency matters] pedro | ||
---|---|---|---|
Line 30: | Line 30: | ||
The scene should be set up now. | The scene should be set up now. | ||
- | ![Initial scene](images/ | + | {{ :images:design:InitialScene.png |
## Wander: the first enemy behavior | ## Wander: the first enemy behavior | ||
Line 38: | Line 38: | ||
The first step is to create a new behavior tree by writing its name in the text field at the upper part of the `Collection` tab and cliking the `Create new behavior` button. Although it is not important for this tutorial, you can create a hierarchy using `/` in the name (for example `Basic/ | The first step is to create a new behavior tree by writing its name in the text field at the upper part of the `Collection` tab and cliking the `Create new behavior` button. Although it is not important for this tutorial, you can create a hierarchy using `/` in the name (for example `Basic/ | ||
- | ![Create new behavior](images/ | + | {{ :images:design:CreateNewBehavior.png |
Once the new behavior asset has been saved it will appear in the `Behaviors` list and will be opened as a new tab in the behavior graphical editor, showing an initially empty canvas. In order to add a new node to the behavior you just need to right-click on any empty point of the canvas and choose one from the drop-down menu. In order to simplify the search, a text field is available for filtering the elements by name in the menu. Browse the hierarchy to have an idea about the built-in actions, conditions, behaviors and composite nodes. | Once the new behavior asset has been saved it will appear in the `Behaviors` list and will be opened as a new tab in the behavior graphical editor, showing an initially empty canvas. In order to add a new node to the behavior you just need to right-click on any empty point of the canvas and choose one from the drop-down menu. In order to simplify the search, a text field is available for filtering the elements by name in the menu. Browse the hierarchy to have an idea about the built-in actions, conditions, behaviors and composite nodes. | ||
- | ![Behavior Bricks Editor](images/ | + | {{ :images:design:BBEditor.png |
Our first step will be to make the enemy go to a fixed position, in (-20, 0.5, 20), moving parallel to the upper floor edge. | Our first step will be to make the enemy go to a fixed position, in (-20, 0.5, 20), moving parallel to the upper floor edge. | ||
Line 49: | Line 49: | ||
- Click on the new node to select it (a yellow border appears when selected) and click on the `Node` tab in the Behavior Bricks inspector. This tab shows the propoerties of the selected node in the canvas. In this case you will find the _input parameters_, | - Click on the new node to select it (a yellow border appears when selected) and click on the `Node` tab in the Behavior Bricks inspector. This tab shows the propoerties of the selected node in the canvas. In this case you will find the _input parameters_, | ||
- | ![Steps for creating the first behavior](images/ | + | {{ :images:design:WanderFirstSteps.png |
- All changes are automatically saved in your project so, once done, you can close the Behavior Bricks editor. | - All changes are automatically saved in your project so, once done, you can close the Behavior Bricks editor. | ||
Line 56: | Line 56: | ||
At runtime, behaviors are executed by the _behavior executor_, a component that must be added to any game object that would be controlled by Behavior Bricks. Select the `Enemy` and add the component Behavior Bricks - Behavior Executor. | At runtime, behaviors are executed by the _behavior executor_, a component that must be added to any game object that would be controlled by Behavior Bricks. Select the `Enemy` and add the component Behavior Bricks - Behavior Executor. | ||
- | ![Steps for adding the executor component](images/ | + | {{ :images:design:AddingTheExecutorComponent.png |
The new component shown in the Inspector provides a place to drop the behavior to execute. From the asset folders, look for the ' | The new component shown in the Inspector provides a place to drop the behavior to execute. From the asset folders, look for the ' | ||
Line 71: | Line 71: | ||
- Now in the `Blackboard` tab of the BB Inspector we can find the new _behavior input parameter_ with that name. You can change the value for the parameter there. | - Now in the `Blackboard` tab of the BB Inspector we can find the new _behavior input parameter_ with that name. You can change the value for the parameter there. | ||
- | ![Steps for creating an entry in the blackboard](images/ | + | {{ :images:design:FirstBlackboardParameter.png |
As seen, the value for the new blackboard field can be globally changed in the behavior properties. But, more interesting, | As seen, the value for the new blackboard field can be globally changed in the behavior properties. But, more interesting, | ||
Line 80: | Line 80: | ||
- Change the value to (-20, 0.5, 20) in order to mimic the initial configuration. | - Change the value to (-20, 0.5, 20) in order to mimic the initial configuration. | ||
- | ![`wanderTarget` parameter in the behavior executor component](images/ | + | {{ :images:design:blackboardParameterInTheComponent.png |
Line 95: | Line 95: | ||
- Select the previous `wanderTarget` as the blackboard parameter where the action will write the selected position. | - Select the previous `wanderTarget` as the blackboard parameter where the action will write the selected position. | ||
- | ![`GetRandomInArea` properties](images/ | + | {{ :images:design:GetRandomInAreaProperties.png |
- | | + | The key point here is that the actions `GetRandomInArea` and `MoveToPosition` are now connected through the `wanderTarget` blackboard parameter. |
- Close the editor and select, if needed, the `Enemy` game object. | - Close the editor and select, if needed, the `Enemy` game object. | ||
- Note the behavior executor component. Now it has _two_ parameters, the old `wanderTarget` and the new one `wanderArea`. | - Note the behavior executor component. Now it has _two_ parameters, the old `wanderTarget` and the new one `wanderArea`. | ||
- | ![Two parameters in the behavior](images/ | + | {{ :images:design:WanderParametersInComponent.png |
- `wanderTarget` is actually an _internal_ value that should not be manipulated from the outside. It is something like _temporal information_ that the behavior needs, but that is unimportant when using the behavior in a game object. | - `wanderTarget` is actually an _internal_ value that should not be manipulated from the outside. It is something like _temporal information_ that the behavior needs, but that is unimportant when using the behavior in a game object. | ||
Line 109: | Line 109: | ||
- Change the type of the `wanderTarget` parameter to " | - Change the type of the `wanderTarget` parameter to " | ||
- | ![Changing the type of a blackboard parameter](images/ | + | {{ :images:design:ChangingBlackboardParamType.png |
- Note that now the `wanderTarget` parameter has disappeared from the behavior executor component in the `Enemy`. | - Note that now the `wanderTarget` parameter has disappeared from the behavior executor component in the `Enemy`. | ||
Line 115: | Line 115: | ||
In this moment we have two actions in our behavior but what of them will be executed earlier? Obviously, we want the `GetRandomInArea` to be executed before `MoveToPosition`, | In this moment we have two actions in our behavior but what of them will be executed earlier? Obviously, we want the `GetRandomInArea` to be executed before `MoveToPosition`, | ||
- | ![Two actions, one of them the tree root](images/ | + | {{ :images:design:TwoActionsWithNoParent.png |
The action marked with an _R_ (`MoveToPosition`) is the root of the behavior tree and will be executed in the first place. In fact, `GetRandomInArea` will _not be_ executed at all. In order to create a behavior with _two_ actions, we need to _compose them_. | The action marked with an _R_ (`MoveToPosition`) is the root of the behavior tree and will be executed in the first place. In fact, `GetRandomInArea` will _not be_ executed at all. In order to create a behavior with _two_ actions, we need to _compose them_. | ||
Line 121: | Line 121: | ||
- Right click on the canvas an add a composite `Sequence Node`. The concrete position is unimportant: | - Right click on the canvas an add a composite `Sequence Node`. The concrete position is unimportant: | ||
- | ![Adding a `Sequence`](images/ | + | {{ :images:design:AddingASequence.png |
- Click on the bottom _handle_ in the `Sequence` and then click on top handle of the `GetRandomInArea` node. This will create a _connection_ relating both _nodes_. A connection can be easily removed by right clicking on it and selecting `Remove connection` in the drop-down menu. | - Click on the bottom _handle_ in the `Sequence` and then click on top handle of the `GetRandomInArea` node. This will create a _connection_ relating both _nodes_. A connection can be easily removed by right clicking on it and selecting `Remove connection` in the drop-down menu. | ||
- Create a new connection from the `Sequence` into `MoveToPosition`. Observe that the `R` marking the root moves to the `Sequence` node. Our behavior _is now a valid tree_. The executor will start with the sequence, that will, in turn, execute the `GetRandomInArea` action and then the `MoveToPosition` one, following a left-to-right order. The order is marked by numbers in the top right corner of the node, and can be altered just by dragging the nodes. For example, by dragging the `MoveToPosition` node to the left of the `GetRandomInArea` node we can alter the order of execution of the sequence children. | - Create a new connection from the `Sequence` into `MoveToPosition`. Observe that the `R` marking the root moves to the `Sequence` node. Our behavior _is now a valid tree_. The executor will start with the sequence, that will, in turn, execute the `GetRandomInArea` action and then the `MoveToPosition` one, following a left-to-right order. The order is marked by numbers in the top right corner of the node, and can be altered just by dragging the nodes. For example, by dragging the `MoveToPosition` node to the left of the `GetRandomInArea` node we can alter the order of execution of the sequence children. | ||
- | ![Steps for creating the tree](images/ | + | {{ :images:design:CreatingASequence.png |
- The sequence executes its children _once_. We want the enemy to continually wander around, so when it reaches the random position, another one should be selected and the enemy should go there. | - The sequence executes its children _once_. We want the enemy to continually wander around, so when it reaches the random position, another one should be selected and the enemy should go there. | ||
Line 132: | Line 132: | ||
- Create a connection from the `Repeat` node to the `Sequence`. Observe how the `R` mark readjusts itself. | - Create a connection from the `Repeat` node to the `Sequence`. Observe how the `R` mark readjusts itself. | ||
- | ![Complete wander behavior](images/ | + | {{ :images:design:WanderBehavior.png |
- Close the editor. Remember that behaviors are automatically saved. | - Close the editor. Remember that behaviors are automatically saved. | ||
Line 190: | Line 190: | ||
- Add a `Repeat` node so the sequence loops again and again and the user can move around to different positions. | - Add a `Repeat` node so the sequence loops again and again and the user can move around to different positions. | ||
- | ![`ClickAndGo` behavior](images/ | + | {{ :images:design:ClickAndGo.png |
- Close the editor and add a Behavior executor component in the `Player` game object. Attach to it the `ClickAndGo` behavior. | - Close the editor and add a Behavior executor component in the `Player` game object. Attach to it the `ClickAndGo` behavior. | ||
Line 237: | Line 237: | ||
- Create a connection from the `Priority` to the `MoveToGameObject`. Note the yellow node that appears on top of the `MoveToGameObject` action. Every child of a priority must be a guarded node composed of a condition plus an action (or behavior). Therefore when a `Priority` node is connected to a `MoveToGameObject` node, a default `AlwaysTrue` condition is automatically added to guard the `MoveToGameObject` action node. | - Create a connection from the `Priority` to the `MoveToGameObject`. Note the yellow node that appears on top of the `MoveToGameObject` action. Every child of a priority must be a guarded node composed of a condition plus an action (or behavior). Therefore when a `Priority` node is connected to a `MoveToGameObject` node, a default `AlwaysTrue` condition is automatically added to guard the `MoveToGameObject` action node. | ||
- | ![Default priority condition](images/ | + | {{ :images:design:DefaultPriorityCondition.png |
- Select the `AlwaysTrue` node, right-click on it and select `Replace with ...` in the drop-down menu, and finally select `IsTargetClose` in the list of available conditions. | - Select the `AlwaysTrue` node, right-click on it and select `Replace with ...` in the drop-down menu, and finally select `IsTargetClose` in the list of available conditions. | ||
- | ![Setting a new condition in a priority child](images/ | + | {{ :images:design:ChangingPriorityCondition.png |
- `IsTargetClose` has two parameters: | - `IsTargetClose` has two parameters: | ||
Line 249: | Line 249: | ||
- Add a new `MoveToPosition` node and make it the second child of the priority, becoming the fall-back behavior. This time instead of first creating the action and then connecting it to the priority, click on the bottom connection area of the priority, then click on an empty point in the canvas, and finally select `MoveToPosition` in the drop-down menu. Set the `MoveToPosition` `target` parameter to the constant value (-20, 0.5, 0). | - Add a new `MoveToPosition` node and make it the second child of the priority, becoming the fall-back behavior. This time instead of first creating the action and then connecting it to the priority, click on the bottom connection area of the priority, then click on an empty point in the canvas, and finally select `MoveToPosition` in the drop-down menu. Set the `MoveToPosition` `target` parameter to the constant value (-20, 0.5, 0). | ||
- | ![Priority selector with two children](images/ | + | {{ :images:design:PursueOrGoHome.png |
- Close the editor and set the new behavior to the enemy, substituting the previous one, `Wander`. Note that the old one will not be removed from your behavior Collection. | - Close the editor and set the new behavior to the enemy, substituting the previous one, `Wander`. Note that the old one will not be removed from your behavior Collection. | ||
Line 271: | Line 271: | ||
- Make the `Wander` node the fall-back (`AlwaysTrue`) child of the `Priority`. | - Make the `Wander` node the fall-back (`AlwaysTrue`) child of the `Priority`. | ||
- | ![Adding a behavior as any other action](images/ | + | {{ :images:design:ReusingABehavior.png |
- Remember to assign the Floor GameObject to the wanderArea parameter in the Unity Inspector. | - Remember to assign the Floor GameObject to the wanderArea parameter in the Unity Inspector. | ||
Line 320: | Line 320: | ||
- In the `Player` field drag the `Player` object from the scene | - In the `Player` field drag the `Player` object from the scene | ||
- | ![Spawn component parameters](images/ | + | {{ :images:design:SpawnParameters.png |
- | ![Spawn scene](images/ | + | {{ :images:design:SpawnScene.png |
Now play the scene and everything should work as before. | Now play the scene and everything should work as before. | ||
Line 345: | Line 345: | ||
public GameObject prefab; | public GameObject prefab; | ||
public GameObject wanderArea; | public GameObject wanderArea; | ||
+ | public GameObject player; | ||
+ | |||
public int Spawns = 750; | public int Spawns = 750; | ||
Line 352: | Line 354: | ||
void Start() | void Start() | ||
{ | { | ||
- | entities = | + | entities = new List< |
- | | + | entities.Add(player); |
- | as GameObject[]); | + | |
- | entities.RemoveAll(e => e.GetComponent< | + | |
InvokeRepeating(" | InvokeRepeating(" | ||
} | } | ||
Line 418: | Line 418: | ||
- in the `Spawns` field you can specify the numher of enemies to spawn | - in the `Spawns` field you can specify the numher of enemies to spawn | ||
- | ![MassSpawn component parameters](images/ | + | {{ :images:design:MassSpawnerParameters.png }} |
Once the enemies have been spawn you can check the scene stats, by pressing the `Stats` button in the `Game` pannel, and see how many frames per second (FPS) are you getting, 50 in the example shown in the figure. | Once the enemies have been spawn you can check the scene stats, by pressing the `Stats` button in the `Game` pannel, and see how many frames per second (FPS) are you getting, 50 in the example shown in the figure. | ||
- | ![Performance Tasks per tick = 500](images/ | + | {{ :images:design:PerformanceTest1.png }} |
One way to increase the performance without decreasing the number of spawns or making modifications to the behavior tree is to modify the field `Max Task Per Tick` of the `Behavior Executor` component of the `Enemy prefab`, by default its value is 500. Set it to `1`, play the scene again and see how it affects performance. In this case our frame rate increases up to 65 FPS without apparently affecting the observed behavior of the enemies. | One way to increase the performance without decreasing the number of spawns or making modifications to the behavior tree is to modify the field `Max Task Per Tick` of the `Behavior Executor` component of the `Enemy prefab`, by default its value is 500. Set it to `1`, play the scene again and see how it affects performance. In this case our frame rate increases up to 65 FPS without apparently affecting the observed behavior of the enemies. | ||
- | ![Performance Tasks per tick = 1](images/ | + | {{ :images:design:PerformanceTest2.png }} |
## What's next? | ## What's next? |