Skip to content

Commit 7e7f443

Browse files
committed
Update docs
1 parent 366d3ab commit 7e7f443

File tree

3 files changed

+114
-41
lines changed

3 files changed

+114
-41
lines changed

Doc/QuickStart-Unity.md

Lines changed: 75 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,35 @@
11

2-
*Reading time: 12 minutes - Last Updated: 2020.11.13*
2+
*Reading time: 12 minutes - Last Updated: 2020.11.27*
33

4-
# Active Logic for Unity - quick start Guide
4+
# Active Logic for Unity - Quick start guide
55

6-
Be welcome! Here is a hands-on introduction to Active Logic. Let's get started.
6+
Be welcome! Here is a hands-on introduction to Active Logic. This guide is suitable for the following distributions:
7+
- [Active Logic](PENDING_URL)
8+
- [Active-LT](https://github.com/active-lt)
9+
10+
Features only available in Active Logic are marked with the [PRO] label; if you are upgrading from Active-LT, [read here](Upgrading.md).
11+
12+
This guide assumes some familiarity with Behavior Trees. For a gentle introduction follow the [Frogger tutorial](https://github.com/active-logic/active-lt-demos/tree/main/Frogger).
13+
For a comprehensive introduction to behavior trees, we recommend [this book](https://btirai.github.io).
14+
15+
*Reading offline? You may be looking at an out of date version of the guide; for up to date, comprehensive documentation, visit*
16+
https://github.com/active-logic/activelogic-cs
17+
18+
## Namespaces and scopes
19+
20+
- Avail Active Logic `using Active.Core`
21+
- For keywords use either `static Active.Raw` or `static Active.Status`
22+
23+
```cs
24+
using Active.Core;
25+
using static Active.Raw; // using static Active.Status;
26+
27+
// ...
28+
```
729

830
## Status expressions
931

10-
Throughout this guide we'll consider the example of a basic 'soldier' AI or agent. A soldier may attack, defend or retreat; in AL this is modeled using a *status expression*:
32+
Throughout this guide we consider the example of a basic 'soldier' AI or agent. A soldier may attack, defend or retreat; in AL this is modeled using a *status expression*:
1133

1234
```cs
1335
status s = Attack() || Defend() || Retreat();
@@ -23,7 +45,7 @@ otherwise, retreat.
2345

2446
A `status` may be `complete`, `failing` or `running` (for 'continue'). a value of *running* causes execution to *yield* until the next iteration.
2547

26-
Status expressions are invoked *frequently* - usually, within update loops. The next example shows using status expressions within a vanilla `MonoBehaviour` (a base class for behaviors in the Unity game engine):
48+
Status expressions are invoked *frequently*; below, status expresssions are used via `MonoBehaviour`:
2749

2850
```cs
2951
using UnityEngine;
@@ -49,9 +71,7 @@ public class Soldier : MonoBehaviour{
4971

5072
The above iterates frame by frame until `state` changes to *failing* or *complete*.
5173

52-
The Active Logic calculus is not restricted to Unity or `MonoBehaviour`, so this may be considered a sketch for your own integration.
53-
54-
Indeed AL does provide powerful base classes advantageously replacing `MonoBehaviour`:
74+
Instead of `MonoBehaviour`, we recommend using `UGig` or `UTask`:
5575

5676
```cs
5777
public class Soldier : UGig{ // or `Gig`
@@ -63,12 +83,10 @@ public class Soldier : UGig{ // or `Gig`
6383
}
6484
```
6585

66-
**NOTE**: `UGig` and `UTask` partake the integration available from the [Unity Asset Store](https://assetstore.unity.com/packages/tools/ai/active-logic-151850). If you are using another engine, `Gig` and `Task` provide a template/starting point for your own integration.
67-
6886
When assigning or returning a status, use `done()`, `fail()` or `cont()`:
6987

7088
```cs
71-
status Attack() => hasWeapon ? fail() : Play("Strike");
89+
status Attack() => hasWeapon ? fail : Play("Strike");
7290
```
7391

7492
AL does not restrict status expressions to sequences and selectors
@@ -108,13 +126,13 @@ Initially, mixing `&&` and `||` may be confusing. If so, write simple status exp
108126

109127
```cs
110128
status DoSomething(){
111-
if( CANNOT_DO ) return fail(); // guard A
112-
if( ALREADY_DONE ) return done(); // guard B
129+
if( CANNOT_DO ) return fail; // guard A
130+
if( ALREADY_DONE ) return done; // guard B
113131
return REALLY_DO_SOMETHING;
114132
}
115133
```
116134

117-
While perhaps surprising, *over-exercising* (ticking a done task) and *over-checking* (ticking a failing task) are integral to BT, ensuring *responsiveness*.
135+
*Over-exercising* (ticking a done task) and *over-checking* (ticking a failing task) are integral to BT, ensuring *responsiveness*.
118136

119137
Let's say a soldier dropped their sword. In BT, how would the following sequence handle this?
120138

@@ -130,8 +148,8 @@ Status expressions implement *stateless* control. In our toy model/example this
130148

131149
```cs
132150
status Attack(){
133-
if(health < 25) return fail();
134-
if(!threat) return fail();
151+
if(health < 25) return fail;
152+
if(!threat) return fail;
135153
return MoveTo(threat) && Strike(threat);
136154
}
137155
```
@@ -146,9 +164,23 @@ More generally, selectors and sequences are known as *composites*.
146164

147165
In all, status expressions and status functions combine to form behavior trees.
148166

149-
## Decorators
167+
## Status keywords
168+
169+
Status keywords include `done`, `cont` and `fail`. Throughout documentation and examples you will see either of the following:
170+
171+
- The raw, unadornated form such as `done`
172+
- Logging calls, such as `done()` or `done(log && "For no reason")`
173+
174+
Raw constants are slightly faster, and require using *Active.Raw*. Logging calls are available via *Active.Status*, and capture useful debugging information, along with custom messages.
175+
176+
Within the same project, you may use either *Active.Raw* or *Active.Status*. Ordinarily, logging calls are recommended, unless one of the following is true:
150177

151-
In the above example, we've hinted at a `Strike()` task. Effective control requires a variety of small 'utilities' used to modulate behavior. A staple of video game design, the cooldown is one such thing:
178+
- You are running AL in a physics loop and/or really need to maximise performance
179+
- You are using a test oriented workflow (then perhaps you do not need the logging/visual history features)
180+
181+
## Decorators [PRO]
182+
183+
In the above example we hinted at a `Strike()` task. Effective control requires a variety of small 'utilities' used to modulate behavior. A staple of video game design, the cooldown is one such thing:
152184

153185
```cs
154186
status Attack(){
@@ -157,15 +189,15 @@ status Attack(){
157189
}
158190
```
159191

160-
This literally is AL magic. Normally you would declare a variable to store a time stamp for the cooldown. AL manages this data (aka control state) on your behalf.
192+
AL magic lets you invoke stateful decorators without explicitly allocating storage (With a cooldown for instance, a date stamp is stored, so we know when the cooldown has expired).
161193

162-
`Gig`, `UGig` (and `MonoBehaviour`) do not support the above syntax. This only works within a class inheriting from `Task` or `UTask` ('stateful context').
194+
With the above syntax, decorators are supported via `Task` and `UTask` (aka "stateful contexts"); if you are not using decorators, prefer `Gig` and `UGig`.
163195

164196
AL offers several [built-in decorators](Reference/Decorators-Builtin.md) and you may also [craft your own](Reference/Decorators-Custom.md).
165197

166-
## Ordered Composites
198+
## Ordered Composites [PRO]
167199

168-
Stateless control encourages you to leverage world/agent state as the primary drive for agent behavior. This works well, and often reduces bugs.
200+
Stateless control encourages you to leverage world/agent state as the primary drive for behavior. This works well, and often reduces bugs.
169201

170202
However some design problems do not fit this approach; consider this:
171203

@@ -201,14 +233,15 @@ status s = Sel()
201233
- /* ... */ ;
202234
```
203235

204-
**NOTE**: *decorators and ordered composites add control state to your logic. However useful, control state is by nature* error prone*. Abusing decorators will reduce the responsiveness and resilience of your agents, whereas responsive, environment-aware agents draw current information from world state.*
236+
**NOTE**: *decorators and ordered composites add control state to your logic. Overusing stateful control reduces the responsiveness and resilience of your agents, whereas responsive, environment-aware agents draw current information from world state.*
205237

206238
## Tasks and frame agents
207239

208240
We have already encountered `UTask`. In Active Logic, `UTask` is a handy base class derived from `MonoBehaviour`. Let's define a task for the `Soldier` role:
209241

210242
```cs
211243
using Active.Core;
244+
using static Active.Raw;
212245

213246
public class Soldier : UTask{
214247

@@ -219,30 +252,29 @@ public class Soldier : UTask{
219252

220253
status Attack() => threat && health > 25
221254
? Engage(threat) && Cooldown(1.0f)?[ Strike(threat) ]
222-
: fail();
255+
: fail;
223256

224-
status Defend() => undef();
257+
// status Defend() => ...
225258
226-
status Retreat() => undef();
259+
// status Retreat() => ...
227260
228261
}
229262
```
230263

231-
Because `Soldier` inherits from `UTask`, it is also a Unity component which may be added to the Unity inspector. In the same way that `Update()` is present in many subclasses of `MonoBehaviour`, `Step()` is our entry point for BT-style update loops.
264+
Because `Soldier` inherits from `UTask`, it is also a Unity component which may be added to the Unity inspector. In the same way that `Update()` is present in many subclasses of `MonoBehaviour`, `Step()` is our entry point for BT-styled update loops.
232265

233-
A task, however, **does not run on its own**.
266+
A task **does not run on its own**.
234267

235268
To make the above runnable, add an `Agent`. Then, in the agent inspector, add `Soldier` as root (if you forget, this happens automatically).
236269

237-
**NOTE**: *`undef()` denotes an unimplemented status function. While debugging, undef randomizes its return value, which is useful for testing incomplete models; undef() is only allowed in debug mode.*
238-
239270
**Uses of task objects**
240271

241272
in AL, task objects are used in several ways:
242273

243-
When writing a low level module (such as locomotion, or simple actions), prefer `Task` or `Gig` to `UTask`, `UGig` (even when using Unity). Then you do not override the `Step()` function. Instead you provide a collection of status functions such as `status Walk(...)` and `status Jump(...)`; another task will then invoke these functions directly.
274+
When writing a low level module (such as locomotion, or a collection of game actions), prefer `Task` or `Gig` to `UTask`, `UGig` (even when using Unity).
275+
Then you do not override the `Step()` function. Instead you provide a collection of status functions such as `status Walk(...)` and `status Jump(...)`; another task will then invoke these functions directly.
244276

245-
When designing higher level behaviors, such as *roles* (say `Farmer` or `Soldier`), override `status Step()` and perhaps expose the task in the inspector via `UTask` (for parameterization). In such cases there is no need to *invoke* the step function:
277+
When designing high level behaviors, such as *roles* (say `Farmer` or `Soldier`), override `status Step()` and perhaps expose the task in the inspector via `UTask` (for parameterization). In such cases there is no need to explicitly invoke the step function:
246278

247279
```cs
248280
class Citizen : UGig{
@@ -257,7 +289,7 @@ class Citizen : UGig{
257289
}
258290
```
259291

260-
In the above example, `UGig` is used since neither decorators, nor any ordered composite(s) are needed. `UTask` would be fine but does allocate (a little) memory, even when no stateful constructs are used.
292+
In the above example, `UGig` is used since neither decorators, nor any ordered composite(s) are needed.
261293

262294
The above example also suggests how you compose versatile agents by assembling ever larger BTs, combining OOP's delegation pattern with BT's modular control paradigm.
263295

@@ -271,34 +303,36 @@ For useful output, we annotate the soldier script:
271303

272304
```cs
273305
using Active.Core;
306+
using static Active.Status; // Active.Status for logging calls
274307
275308
public class Soldier : UTask{
276309

277310
float health = 100;
278311

312+
// Wrap status expressions with 'Eval'
279313
override protected status Step() => Eval(
280314
Attack() || Defend() || Retreat()
281315
);
282316

283317

318+
// (log && $"string") for custom messages
284319
status Attack() => Eval(
285320
!threat ? fail(log && "No enemy around") :
286321
health < 25 ? fail(log && "Health too low") :
287322
Engage(threat) && Cooldown(1.0f)?[ Strike(threat) ]
288323
);
289324

290-
status Defend() => undef();
291-
292-
status Retreat() => undef();
293-
325+
// (STATUS_EXP)[log && $"string"], also for custom messages
294326
status Engage(Transform threat){
295327
var dist = Vector3.Distance(transform.position, threat.position);
296328
return Eval(
297329
dist < 1f ? done(log && "In range") : Approach()
298330
)[log && $"At {dist:0.#}meters from target"];
299331
}
300332

301-
status Approach() => undef;
333+
// status Defend() => ...
334+
// status Retreat() => ...
335+
// status Approach() => ...
302336
303337
}
304338
```
@@ -313,8 +347,9 @@ This example illustrates everything you need to know to get started with logging
313347
- `done()`, `cont()` and `fail()` may receive a custom log message as argument.
314348
- You may attach a custom message to any status, like so: `status[log && $"Custom"]`
315349
- String interpolation is preferred.
316-
- The `log && message` idiom ensures logging does not decrease performance in production builds; as such, you need not remove log messages before shipping.
350+
- The `log && message` idiom ensures logging does not decrease performance in production builds; as such, you may not need to remove log messages before shipping, and the logging annotations also help documenting your logic.
317351

318352
## Going further
319353

320-
The quick start guide is intended as a short introduction to AL; to learn more, check the [API reference](Reference/Overview.md).
354+
The quick start guide is intended as a short introduction to AL; to learn more, check the API reference at
355+
https://github.com/active-logic/activelogic-cs/blob/master/Doc/Reference/Overview.md

Doc/Reference/Unity/Logging.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ The logging feature solves three problems commonly associated with logging:
1010
- **Performance** - the API provides safe idioms ensuring minimal overheads, so that you needn't remove traces before shipping.
1111
- **Best practice** - the logging API helps you design self-explanatory control programs with minimal semantic overheads.
1212

13-
## Logging within Gigs and tasks
13+
## Logging within gigs and tasks
1414

1515
### Logging statuses
1616

@@ -76,6 +76,18 @@ public status Defend(){
7676
}
7777
```
7878

79+
***Why 'Eval' is necessary***
80+
81+
As your code executes, the AL library accumulates logging information pertaining to status expressions. As an example consider the following function:
82+
83+
public ABC() => A && B && C;
84+
85+
Upon evaluating `C`, A && `B` have already evaluated, and the associate logging information is preserved. However, without `Eval`, AL cannot determine that we are exiting ABC.
86+
87+
Because of that, although you might get output without using `Eval`, the structure of such output may be incorrect.
88+
89+
If you do not consistently use `Eval` the log-tree view will display partial/incorrectly structured output.
90+
7991
## Logging using `Via`
8092

8193
*[DOC REVIEW NOTE] in recent versions of AL, it appears that Eval is now a static keyword.*

Doc/Upgrading.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
## Upgrading from Active-LT to Active-Logic
2+
3+
First, license the Active Logic package [from the Unity Asset Store](http://u3d.as/1AZ8).
4+
5+
Your upgrade path depends on whether you are using the UPM package (Github) or the UAS Active-LT asset (from the Unity Asset Store).
6+
7+
Before upgrading, backup or commit your project.
8+
9+
### 1. Remove Active-LT
10+
11+
**If using Active-LT via UPM/Github** -
12+
Open your project and display the Unity Package Manager. Find and select Active-LT, then choose "Remove" in the bottom right corner.
13+
14+
**If using Active-lT via the Unity Asset Store** - Open your project and display the project window. Delete the *ActiveLogic* directory.
15+
16+
After removing the package, you will probably see errors in the console; this is normal and these errors may be safely ignored.
17+
18+
### 2. Install Active Logic
19+
20+
From the Unity Asset Store window, download and import the Active Logic Package. In some versions of AL, you may see warnings in the console window; these warnings may be safely ignored.
21+
22+
### 3. Retest your project
23+
24+
Press "Play" to retest your project.
25+
26+
In some cases, your agents may not run. If this happens, you only need to reassign the "root" task in the matching Agent, Ticker or PhysicsAgent.

0 commit comments

Comments
 (0)