dcl-npc-toolkit 1.0.10-20230523140244.commit-fbf44ee → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +544 -813
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -3,74 +3,94 @@
3
3
 
4
4
 
5
5
 
6
+
7
+
6
8
  # NPC-library
7
9
 
8
10
 
9
11
 
10
12
 
11
13
 
14
+
15
+
12
16
  A collection of tools for creating Non-Player-Characters (NPCs). These are capable of having conversations with the player, and play different animations.
13
17
 
14
18
 
15
19
 
16
20
 
17
21
 
22
+
23
+
18
24
  Capabilities of the NPCs in this library:
19
25
 
20
26
 
21
27
 
22
28
 
23
29
 
30
+
31
+
24
32
  - Start a conversation when clicked or when walking near
25
33
 
26
34
 
27
35
 
36
+
37
+
28
38
  - Trigger any action when clicked or when walking near
29
39
 
30
40
 
31
41
 
42
+
43
+
32
44
  - Trigger any action when the player walks away
33
45
 
34
46
 
35
47
 
48
+
49
+
36
50
  - Turn around slowly to always face the player
37
51
 
38
52
 
39
53
 
54
+
55
+
40
56
  - Play an animation in the NPC 3d model, optionally returning to loop the idle animation afterwards
41
57
 
42
58
 
43
59
 
44
60
 
45
61
 
62
+
63
+
46
64
  The dialog messages can also require that the player chooses options, and any action can be triggered when the player picks an option or advances past a message.
47
65
 
48
66
 
49
67
 
50
68
 
51
69
 
70
+
71
+
52
72
  To use NPCs in your scene:
53
73
 
54
74
 
55
75
 
56
76
 
57
77
 
78
+
79
+
58
80
  1. Install the library as an npm bundle. Run this command in your scene's project folder:
59
81
 
60
82
 
61
83
 
62
84
 
63
85
 
64
- ```
65
-
66
86
 
67
87
 
88
+ ```ts
68
89
  npm i dcl-npc-toolkit -B
90
+ ```
69
91
 
70
92
 
71
93
 
72
- ```
73
-
74
94
 
75
95
 
76
96
  2. Install the dependent sdk utils library as an npm bundle. Run this command in your scene's project folder:
@@ -79,16 +99,14 @@ npm i dcl-npc-toolkit -B
79
99
 
80
100
 
81
101
 
82
- ```
83
-
84
102
 
85
103
 
104
+ ```
86
105
  npm i @dcl-sdk/utils -B
106
+ ```
87
107
 
88
108
 
89
109
 
90
- ```
91
-
92
110
 
93
111
 
94
112
  3. Run `dcl start` or `dcl build` so the dependencies are correctly installed.
@@ -97,22 +115,22 @@ npm i @dcl-sdk/utils -B
97
115
 
98
116
 
99
117
 
118
+
119
+
100
120
  4. Import the library into the scene's script. Add this line at the start of your `game.ts` file, or any other TypeScript files that require it:
101
121
 
102
122
 
103
123
 
104
124
 
105
125
 
106
- ```ts
107
-
108
126
 
109
127
 
128
+ ```ts
110
129
  import * as npc from 'dcl-npc-toolkit'
130
+ ```
111
131
 
112
132
 
113
133
 
114
- ```
115
-
116
134
 
117
135
 
118
136
 
@@ -123,44 +141,22 @@ import * as npc from 'dcl-npc-toolkit'
123
141
 
124
142
 
125
143
 
126
- ```ts
127
-
128
144
 
129
145
 
146
+ ```ts
130
147
  export let myNPC = npc.create({position: Vector3.create(8,0,8),rotation:Quaternion.Zero(), scale: Vector3.create(1,1,1)},
131
148
 
132
-
133
-
134
149
  //NPC Data Object
135
-
136
-
137
-
138
150
  {
139
-
140
-
141
-
142
151
  type: npc.NPCType.CUSTOM,
143
-
144
-
145
-
146
152
  model: 'models/npc.glb',
147
-
148
-
149
-
150
153
  onActivate:()=>{console.log('npc activated');}
151
-
152
-
153
-
154
154
  }
155
-
156
-
157
-
158
155
  )
156
+ ```
159
157
 
160
158
 
161
159
 
162
- ```
163
-
164
160
 
165
161
 
166
162
 
@@ -171,80 +167,74 @@ onActivate:()=>{console.log('npc activated');}
171
167
 
172
168
 
173
169
 
174
- ```ts
175
-
176
170
 
177
171
 
172
+ ```ts
178
173
  import { Dialog } from 'dcl-npc-toolkit'
174
+ export let ILoveCats: Dialog[] = [
175
+ {
176
+ text: `I really lo-ove cats`,
177
+ isEndOfDialog: true
178
+ }
179
+ ]
180
+ ```
179
181
 
180
182
 
181
183
 
182
184
 
183
185
 
184
- export let ILoveCats: Dialog[] = [
185
-
186
186
 
187
187
 
188
- {
188
+ ## NPC Default Behavior
189
189
 
190
190
 
191
191
 
192
- text: `I really lo-ove cats`,
193
-
194
192
 
195
193
 
196
- isEndOfDialog: true
197
-
198
194
 
199
195
 
200
- }
196
+ NPCs at the very least must have:
201
197
 
202
198
 
203
199
 
204
- ]
200
+
205
201
 
206
202
 
207
203
 
208
- ```
204
+ - `position`: (_TransformType_) Must include position, rotation and scale.
209
205
 
210
206
 
211
207
 
212
208
 
213
209
 
214
- ## NPC Default Behavior
210
+ - `NPCData`: (_Data Object_) with a minimum of two variables
215
211
 
216
212
 
217
213
 
218
214
 
219
215
 
220
- NPCs at the very least must have:
216
+ - `type`: (_NPCType_) you have the choice to use a custom GLB object or an `AvatarShape` for your npc
221
217
 
222
218
 
223
219
 
224
220
 
225
221
 
226
- - `position`: (_TransformType_) Must include position, rotation and scale.
222
+ - `NPCType.CUSTOM`
227
223
 
228
224
 
229
225
 
230
- - `NPCData`: (_Data Object_) with a minimum of two variables
231
-
232
226
 
233
227
 
234
- - `type`: (_NPCType_) you have the choice to use a custom GLB object or an `AvatarShape` for your npc
228
+ - `NPCType.AVATAR`
235
229
 
236
230
 
237
231
 
238
- - `NPCType.CUSTOM`
239
-
240
232
 
241
233
 
242
- - `NPCType.AVATAR`
234
+ - `onActivate()`: (_()=> void_) A function to call when the NPC is activated.
243
235
 
244
236
 
245
237
 
246
- - `onActivate()`: (_()=> void_) A function to call when the NPC is activated.
247
-
248
238
 
249
239
 
250
240
 
@@ -253,50 +243,30 @@ NPCs at the very least must have:
253
243
 
254
244
 
255
245
 
246
+
247
+
256
248
  - `model`: (_string_) The path to a 3D model
257
249
 
258
250
 
259
251
 
260
252
 
261
253
 
262
- ```ts
263
-
264
254
 
265
255
 
256
+ ```ts
266
257
  export let myNPC = npc.create({position: Vector3.create(8,0,8),rotation:Quaternion.Zero(), scale: Vector3.create(1,1,1)},
267
258
 
268
-
269
-
270
259
  //NPC Data Object
271
-
272
-
273
-
274
260
  {
275
-
276
-
277
-
278
261
  type: npc.NPCType.CUSTOM,
279
-
280
-
281
-
282
262
  model: 'models/npc.glb',
283
-
284
-
285
-
286
263
  onActivate:()=>{console.log('npc activated');}
287
-
288
-
289
-
290
264
  }
291
-
292
-
293
-
294
265
  )
266
+ ```
295
267
 
296
268
 
297
269
 
298
- ```
299
-
300
270
 
301
271
 
302
272
 
@@ -307,363 +277,412 @@ With this default configuration, the NPC behaves in the following way:
307
277
 
308
278
 
309
279
 
280
+
281
+
310
282
  - The `onActivate()` function is called when pressing E on the NPC, and when the player walks near at a distance of 6 meters.
311
283
 
312
284
 
313
285
 
286
+
287
+
314
288
  - Once activated, there's a cooldown period of 5 seconds, that prevents the NPC to be activated again.
315
289
 
316
290
 
317
291
 
292
+
293
+
318
294
  - After walking away from the NPC, if its dialog window was open it will be closed, and if the NPC was rotating to follow the player it will stop.
319
295
 
320
296
 
321
297
 
298
+
299
+
322
300
  - If the NPC already has an open dialog window, clicking on the NPC won't do anything, to prevent accidentally clicking on it while flipping through the conversation.
323
301
 
324
302
 
325
303
 
304
+
305
+
326
306
  - If the NPC has an animation named 'Idle', it will play it in a loop. If other non-looping animations are played, it will return to looping the 'Idle' animation after the indicated duration.
327
307
 
328
308
 
329
309
 
330
310
 
331
311
 
312
+
313
+
332
314
  Many of these behaviors can be overridden or tweaked with the exposed properties.
333
315
 
334
316
 
335
- ## SDK7 UI
336
- With sdk7, there are new ways to implement similar features from sdk6, one of them being the way 2D UI objects get created. To add the NPC dialogs to your sdk7 2D UI:
317
+
318
+ ## SDK7 UI
319
+
320
+ With sdk7, there are new ways to implement similar features from sdk6, one of them being the way 2D UI objects get created. To add the NPC dialogs to your sdk7 2D UI:
321
+
322
+ - create a variable to hold *all* of your 2D UI objects
323
+
324
+ - import the NPC UI from the library and add the React object to your scene UI tree
325
+
326
+ - create a function to be called once to render all of your 2D UI objects
327
+
337
328
 
338
- - create a variable to hold *all* of your 2D UI objects
339
- - import the NPC UI from the library and add the React object to your scene UI tree
340
- - create a function to be called once to render all of your 2D UI objects
341
329
 
342
330
  ```ts
343
331
  import ReactEcs, { Label, ReactEcsRenderer, UiEntity } from '@dcl/sdk/react-ecs'
344
332
  import { NpcUtilsUi } from 'dcl-npc-toolkit'
345
333
 
346
334
  const SceneOwnedUi = () =>
335
+
347
336
  <UiEntity>
348
- <NpcUtilsUi />
349
- { /* rest of user defined UI */ }
337
+ <NpcUtilsUi />
338
+
339
+ { /* rest of user defined UI */ }
350
340
  </UiEntity>
351
341
 
342
+
343
+
352
344
  export function setupUi() {
353
345
  ReactEcsRenderer.setUiRenderer(SceneOwnedUi)
354
346
  }
347
+
355
348
  ```
356
349
 
357
350
 
358
351
 
352
+
353
+
359
354
  ## NPC Additional Properties
360
355
 
361
356
 
362
357
 
363
358
 
364
359
 
360
+
361
+
365
362
  To configure other properties of an NPC, add a fourth argument as an `NPCData` object. This object can have the following optional properties:
366
363
 
367
364
 
368
365
 
369
366
 
370
367
 
368
+
369
+
371
370
  - `idleAnim`: _(string)_ Name of the idle animation in the model. This animation is always looped. After playing a non-looping animation it returns to looping this one.
372
371
 
373
372
 
374
373
 
374
+
375
+
375
376
  - `faceUser`: _(boolean)_ Set if the NPC rotates to face the user while active.
376
377
 
377
378
 
378
379
 
380
+
381
+
379
382
  - `dialogSound`: _(string)_ Path to sound file to play once for every entry shown on the UI. If the dialog entry being shown has an `audio` field, the NPC will play the file referenced by the `audio` field instead.
380
383
 
381
384
 
382
385
 
386
+
387
+
383
388
  - `coolDownDuration`: _(number)_ Change the cooldown period for activating the NPC again. The number is in seconds.
384
389
 
385
390
 
386
391
 
392
+
393
+
387
394
  - `hoverText`: _(string)_ Set the UI hover feedback when pointing the cursor at the NPC. _TALK_ by default.
388
395
 
389
396
 
390
397
 
398
+
399
+
391
400
  - `onlyClickTrigger`: _(boolean)_ If true, the NPC can't be activated by walking near. Just by clicking on it or calling its `activate()` function.
392
401
 
393
402
 
394
403
 
404
+
405
+
395
406
  - `onlyETrigger`: _(boolean)_ If true, the NPC can't be activated by walking near. Just by pressing the E key on it or calling its `activate()` function.
396
407
 
397
408
 
398
409
 
410
+
411
+
399
412
  - `onlyExternalTrigger`: _(boolean)_ If true, the NPC can't be activated by clicking, pressing E, or walking near. Just by calling its `activate()` function.
400
413
 
401
414
 
402
415
 
416
+
417
+
403
418
  - `reactDistance`: _(number)_ Radius in meters for the player to activate the NPC or trigger the `onWalkAway()` function when leaving the radius.
404
419
 
405
420
 
406
421
 
422
+
423
+
407
424
  - `continueOnWalkAway`: _(boolean)_ If true,when the player walks out of the `reactDistance` radius, the dialog window stays open and the NPC keeps turning to face the player (if applicable). It doesn't affect the triggering of the `onWalkAway()` function.
408
425
 
409
426
 
410
427
 
428
+
429
+
411
430
  - `onWalkAway`: (_()=> void_) Function to call every time the player walks out of the `reactDistance` radius.
412
431
 
413
432
 
414
433
 
434
+
435
+
415
436
  - `walkingAnim`: _(string)_ Name of the walking animation on the model. This animation is looped when calling the `followPath()` function.
416
437
 
417
438
 
418
439
 
440
+
441
+
419
442
  - `walkingSpeed`: _(number)_ Speed of the NPC when walking. By default _2_.
420
443
 
421
444
 
422
445
 
446
+
447
+
423
448
  - `path`: _(Vector3)_ Default path to walk. If a value is provided for this field on NPC initialization, the NPC will walk over this path in loop from the start.
424
449
 
450
+
451
+
425
452
  - `bubbleHeight`: _(number)_ The height at which to display the speech bubble above the head of the NPC.
426
453
 
454
+
455
+
427
456
  - `textBubble`: _(boolean)_ If true, NPC starts with a speech bubble object ready to be accessed from the start. Otherwise, they text bubble is only built on the first call to `talkBubble()` on the NPC.
428
457
 
429
458
 
430
459
 
460
+
461
+
431
462
  - `noUI`: _(boolean)_ If true, no UI object is built for UI dialogs for this NPC. This may help optimize the scene if this feature is not used.
432
463
 
433
464
 
434
465
 
435
466
 
436
467
 
437
- ```ts
438
-
439
468
 
440
469
 
470
+ ```ts
441
471
  export let myNPC = npc.create({position: Vector3.create(8,0,8),rotation:Quaternion.Zero(), scale: Vector3.create(1,1,1)},
472
+ //NPC Data Object
473
+ {
474
+ type: npc.NPCType.CUSTOM,
475
+ model: 'models/npc.glb',
476
+ onActivate: ()=>{console.log('npc activated');},
477
+ onWalkAway: ()=>{console.log('test on walk away function')},
478
+ faceUser: true,
479
+ reactDistance: 3,
480
+ idleAnim: 'idle1',
481
+ walkingAnim: 'walk1',
482
+ hoverText: 'Activate',
483
+ continueOnWalkAway: true,
484
+ onlyClickTrigger: false,
485
+ onlyExternalTrigger: false
486
+ }
487
+ )
488
+ ```
442
489
 
443
490
 
444
491
 
445
- //NPC Data Object
492
+
446
493
 
447
494
 
448
495
 
449
- {
496
+ ## Get NPC Data
450
497
 
451
498
 
452
499
 
453
- type: npc.NPCType.CUSTOM,
500
+
454
501
 
455
502
 
456
503
 
457
- model: 'models/npc.glb',
504
+ ```ts
505
+ npc.getData(myNPC)
506
+ ```
458
507
 
459
508
 
460
509
 
461
- onActivate: ()=>{console.log('npc activated');},
510
+
462
511
 
463
512
 
464
513
 
465
- onWalkAway: ()=>{console.log('test on walk away function')},
514
+ There are several properties you can check on an NPC to know what its current state is:
466
515
 
467
516
 
468
517
 
469
- faceUser: true,
518
+
470
519
 
471
520
 
472
521
 
473
- reactDistance: 3,
522
+ - `.state`: An enum value of type `NPCState`. Supported values are `NPCState.STANDING` (default), `NPCState.TALKING`, and `NPCState.FOLLOWPATH`. `TALKING` is applied when the dialog window is opened, and set back to `STANDING` when the window is closed. `FOLLOWPATH` is applied when the NPC starts walking, and set back to `STANDING` when the NPC finishes its path or is stopped.
474
523
 
475
524
 
476
525
 
477
- idleAnim: 'idle1',
478
-
479
526
 
480
527
 
481
- walkingAnim: 'walk1',
528
+ - `.introduced`: Boolean, false by default. Set to true if the NPC has spoken to the player at least once in this session.
482
529
 
483
530
 
484
531
 
485
- hoverText: 'Activate',
486
-
487
532
 
488
533
 
489
- continueOnWalkAway: true,
534
+ - `.visible`: Returns a Boolean, false by default. True if the dialog window for this NPC is currently open.
490
535
 
491
536
 
492
537
 
493
- onlyClickTrigger: false,
494
-
495
538
 
496
539
 
497
- onlyExternalTrigger: false
540
+ - `.inCooldown`: Boolean, false by default. True if the NPC was recently activated and it's now in cooldown. The NPC won't respond to being activated till `inCooldown` is false.
498
541
 
499
542
 
500
543
 
501
- }
544
+
502
545
 
503
546
 
504
547
 
505
- )
548
+ > TIP: If you want to force an activation of the NPC in spite of the `inCooldown` value, you can force this value to true before activating.
506
549
 
507
550
 
508
551
 
509
- ```
510
-
511
552
 
512
553
 
513
554
 
514
555
 
515
- ## Get NPC Data
556
+ ## NPC Callable Actions
516
557
 
517
558
 
518
559
 
519
560
 
520
561
 
521
- ```ts
522
-
523
562
 
524
563
 
525
- npc.getData(myNPC)
564
+ An NPC object has several callable functions that come with the class:
526
565
 
527
566
 
528
567
 
529
- ```
530
-
531
568
 
532
569
 
533
570
 
534
571
 
535
- There are several properties you can check on an NPC to know what its current state is:
572
+ ### Talk
536
573
 
537
574
 
538
575
 
539
576
 
540
577
 
541
- - `.state`: An enum value of type `NPCState`. Supported values are `NPCState.STANDING` (default), `NPCState.TALKING`, and `NPCState.FOLLOWPATH`. `TALKING` is applied when the dialog window is opened, and set back to `STANDING` when the window is closed. `FOLLOWPATH` is applied when the NPC starts walking, and set back to `STANDING` when the NPC finishes its path or is stopped.
542
-
543
578
 
544
579
 
545
- - `.introduced`: Boolean, false by default. Set to true if the NPC has spoken to the player at least once in this session.
580
+ To start a conversation with the NPC using the dialog UI, call the `talk()` function. The function takes the following **required** parameter:
546
581
 
547
582
 
548
583
 
549
- - `.visible`: Returns a Boolean, false by default. True if the dialog window for this NPC is currently open.
584
+
550
585
 
551
586
 
552
587
 
553
- - `.inCooldown`: Boolean, false by default. True if the NPC was recently activated and it's now in cooldown. The NPC won't respond to being activated till `inCooldown` is false.
588
+ - `script`: _(Dialog[])_ This array contains the information to manage the conversation, including events that may be triggered, options to choose, etc.
554
589
 
555
590
 
556
591
 
557
592
 
558
593
 
559
- > TIP: If you want to force an activation of the NPC in spite of the `inCooldown` value, you can force this value to true before activating.
560
-
561
594
 
562
595
 
563
-
596
+ It can also take the following optional parameters:
564
597
 
565
- ## NPC Callable Actions
598
+
566
599
 
567
600
 
568
601
 
569
602
 
570
603
 
571
- An NPC object has several callable functions that come with the class:
604
+ - `startIndex`: _(number | string)_ The _Dialog_ object from the `script` array to open first. By default this is _0_, the first element of the array. Pass a number to open the entry on a given array position, or pass a string to open the entry with a `name` property matching that string.
572
605
 
573
606
 
574
607
 
575
608
 
576
609
 
577
- ### Talk
610
+ - `duration`: _(number)_ Number of seconds to wait before closing the dialog window. If no value is set, the window is kept open till the player reaches the end of the conversation or something else closes it.
578
611
 
579
612
 
580
613
 
581
614
 
582
615
 
583
- To start a conversation with the NPC using the dialog UI, call the `talk()` function. The function takes the following **required** parameter:
584
-
585
616
 
586
617
 
587
-
618
+ ```ts
619
+ npc.talk(myNPC,myScript, 0)
620
+ ```
588
621
 
589
- - `script`: _(Dialog[])_ This array contains the information to manage the conversation, including events that may be triggered, options to choose, etc.
622
+
590
623
 
591
624
 
592
625
 
593
626
 
594
627
 
595
- It can also take the following optional parameters:
628
+ Learn how to build a script object for NPCs in a section below.
596
629
 
597
630
 
598
631
 
599
632
 
600
633
 
601
- - `startIndex`: _(number | string)_ The _Dialog_ object from the `script` array to open first. By default this is _0_, the first element of the array. Pass a number to open the entry on a given array position, or pass a string to open the entry with a `name` property matching that string.
602
-
603
634
 
604
635
 
605
- - `duration`: _(number)_ Number of seconds to wait before closing the dialog window. If no value is set, the window is kept open till the player reaches the end of the conversation or something else closes it.
636
+ ### Play Animations
606
637
 
607
638
 
608
639
 
609
640
 
610
641
 
611
- ```ts
612
-
613
642
 
614
643
 
615
- npc.talk(myNPC,myScript, 0)
644
+ By default, the NPC will loop an animation named 'Idle', or with a name passed in the `idleAnim` parameter.
616
645
 
617
646
 
618
647
 
619
- ```
620
-
621
648
 
622
649
 
623
650
 
624
651
 
625
- Learn how to build a script object for NPCs in a section below.
626
-
627
-
652
+ Make the NPC play another animation by calling the `playAnimation()` function. The function takes the following **required** parameter:
628
653
 
629
654
 
630
655
 
631
- ### Play Animations
632
-
633
656
 
634
657
 
635
658
 
636
659
 
637
- By default, the NPC will loop an animation named 'Idle', or with a name passed in the `idleAnim` parameter.
660
+ - `animationName`: _(string)_ The name of the animation to play.
638
661
 
639
662
 
640
663
 
641
664
 
642
665
 
643
- Make the NPC play another animation by calling the `playAnimation()` function. The function takes the following **required** parameter:
644
-
645
666
 
646
667
 
647
-
668
+ It can also take the following optional parameters:
648
669
 
649
- - `animationName`: _(string)_ The name of the animation to play.
670
+
650
671
 
651
672
 
652
673
 
653
674
 
654
675
 
655
- It can also take the following optional parameters:
676
+ - `noLoop`: _(boolean)_ If true, plays the animation just once. Otherwise, the animation is looped.
656
677
 
657
678
 
658
679
 
659
680
 
660
681
 
661
- - `noLoop`: _(boolean)_ If true, plays the animation just once. Otherwise, the animation is looped.
682
+ - `duration`: _(number)_ Specifies the duration in seconds of the animation. When finished, it returns to playing the idle animation.
662
683
 
663
684
 
664
685
 
665
- - `duration`: _(number)_ Specifies the duration in seconds of the animation. When finished, it returns to playing the idle animation.
666
-
667
686
 
668
687
 
669
688
 
@@ -674,16 +693,14 @@ It can also take the following optional parameters:
674
693
 
675
694
 
676
695
 
677
- ```ts
678
-
679
696
 
680
697
 
698
+ ```ts
681
699
  npc.playAnimation(myNPC, `Head_Yes`, true, 2.63)
700
+ ```
682
701
 
683
702
 
684
703
 
685
- ```
686
-
687
704
 
688
705
 
689
706
 
@@ -694,44 +711,52 @@ npc.playAnimation(myNPC, `Head_Yes`, true, 2.63)
694
711
 
695
712
 
696
713
 
714
+
715
+
697
716
  The NPC's idle animation is looped by default whenever the NPC is not playing any other animations. In some cases you may want to have different idle animations depending on the circumstances, like while in a conversation, or if the NPC changes its general attitude after some event.
698
717
 
699
718
 
700
719
 
701
720
 
702
721
 
722
+
723
+
703
724
  You set the NPC's idle animation when creating the NPC, using the `idleAnim` field. To change this animation at some later time, use `changeIdleAnim()`.
704
725
 
705
726
 
706
727
 
707
728
 
708
729
 
730
+
731
+
709
732
  The `changeIdleAnim()` function takes two arguments:
710
733
 
711
734
 
712
735
 
713
736
 
714
737
 
738
+
739
+
715
740
  - `animation`: The name of the new animation to set as the idle animation
716
741
 
717
742
 
718
743
 
744
+
745
+
719
746
  - `play`: Optionally pass this value as _true_ if you want this new animation to start playing right away.
720
747
 
721
748
 
722
749
 
723
750
 
724
751
 
725
- ```ts
726
-
727
752
 
728
753
 
754
+ ```ts
729
755
  npc.changeIdleAnim(myNPC,`AngryIdle`, true)
756
+ ```
730
757
 
731
758
 
732
759
 
733
- ```
734
-
735
760
 
736
761
 
737
762
 
@@ -742,22 +767,22 @@ npc.changeIdleAnim(myNPC,`AngryIdle`, true)
742
767
 
743
768
 
744
769
 
770
+
771
+
745
772
  The `activate()` function can be used to trigger the `onActivate()` function, as an alternative to pressing E or walking near.
746
773
 
747
774
 
748
775
 
749
776
 
750
777
 
751
- ```ts
752
-
753
778
 
754
779
 
780
+ ```ts
755
781
  npc.activate(myNPC)
782
+ ```
756
783
 
757
784
 
758
785
 
759
- ```
760
-
761
786
 
762
787
 
763
788
 
@@ -768,28 +793,30 @@ The `activate()` function is callable even when in cool down period, and it does
768
793
 
769
794
 
770
795
 
796
+
797
+
771
798
  ### Stop Walking
772
799
 
773
800
 
774
801
 
775
802
 
776
803
 
804
+
805
+
777
806
  If the NPC is currently walking, call `stopWalking()` to stop it moving and return to playing its idle animation.
778
807
 
779
808
 
780
809
 
781
810
 
782
811
 
783
- ```ts
784
-
785
812
 
786
813
 
814
+ ```ts
787
815
  npc.stopWalking(myNPC)
816
+ ```
788
817
 
789
818
 
790
819
 
791
- ```
792
-
793
820
 
794
821
 
795
822
 
@@ -800,52 +827,62 @@ npc.stopWalking(myNPC)
800
827
 
801
828
 
802
829
 
830
+
831
+
803
832
  - `duration`: Seconds to wait before starting to walk again. If not provided, the NPC will stop walking indefinitely.
804
833
 
805
834
 
806
835
 
807
836
 
808
837
 
838
+
839
+
809
840
  > Note: If the NPC is has its dialog window open when the timer for the `duration` ends, the NPC will not return to walking.
810
841
 
811
842
 
812
843
 
813
844
 
814
845
 
846
+
847
+
815
848
  To make the NPC play a different animation from idle when paused, call `playAnimation()` after `stopWalking()`.
816
849
 
817
850
 
818
851
 
819
852
 
820
853
 
854
+
855
+
821
856
  ### Follow Path
822
857
 
823
858
 
824
859
 
825
860
 
826
861
 
862
+
863
+
827
864
  Make an NPC walk following a path of `Vector3` points by calling `followPath()`. While walking, the NPC will play the `walkingAnim` if one was set when defining the NPC. The path can be taken once or on a loop.
828
865
 
829
866
 
830
867
 
831
868
 
832
869
 
870
+
871
+
833
872
  `followPath()` can be called with no parameters if a `path` was already provided in the NPC's initialization or in a previous calling of `followPath()`. If the NPC was previously in the middle of walking a path and was interrupted, calling `followPath()` again with no arguments will return the NPC to that path.
834
873
 
835
874
 
836
875
 
837
876
 
838
877
 
839
- ```ts
840
-
841
878
 
842
879
 
880
+ ```ts
843
881
  npc.followPath(myNPC)
882
+ ```
844
883
 
845
884
 
846
885
 
847
- ```
848
-
849
886
 
850
887
 
851
888
 
@@ -856,188 +893,147 @@ npc.followPath(myNPC)
856
893
 
857
894
 
858
895
 
859
- `followPath()` has a single optional parameter of type `FollowPathData`. This object may have the following optional fields:
860
-
861
-
862
-
863
896
 
864
897
 
865
- - path: Array of `Vector3` positions to walk over.
898
+ `followPath()` has a single optional parameter of type `FollowPathData`. This object may have the following optional fields:
866
899
 
867
900
 
868
901
 
869
- - speed: Speed to move at while walking this path. If no `speed` or `totalDuration` is provided, it uses the NPC's `walkingSpeed`, which is _2_ by default.
870
-
871
902
 
872
903
 
873
- - totalDuration: The duration in _seconds_ that the whole path should take. The NPC will move at the constant speed required to finish in that time. This value overrides that of the _speed_.
874
-
875
904
 
876
905
 
877
- - loop: _boolean_ If true, the NPC walks in circles over the provided set of points in the path. _false_ by default, unless the NPC is initiated with a `path`, in which case it starts as _true_.
906
+ - path: Array of `Vector3` positions to walk over.
878
907
 
879
908
 
880
909
 
881
- - curve: _boolean_ If true, the path is traced a single smooth curve that passes over each of the indicated points. The curve is made out of straight-line segments, the path is stored with 4 times as many points as originally defined. _false_ by default.
882
-
883
910
 
884
911
 
885
- - startingPoint: Index position for what point to start from on the path. _0_ by default.
912
+ - speed: Speed to move at while walking this path. If no `speed` or `totalDuration` is provided, it uses the NPC's `walkingSpeed`, which is _2_ by default.
886
913
 
887
914
 
888
915
 
889
- - onFinishCallback: Function to call when the NPC finished walking over all the points on the path. This is only called when `loop` is _false_.
890
-
891
916
 
892
917
 
893
- - onReachedPointCallback: Function to call once every time the NPC reaches a point in the path.
894
-
895
-
918
+ - totalDuration: The duration in _seconds_ that the whole path should take. The NPC will move at the constant speed required to finish in that time. This value overrides that of the _speed_.
896
919
 
897
920
 
898
921
 
899
- ```ts
900
-
901
922
 
902
923
 
903
- export let myNPC = npc.create({position: Vector3.create(8,0,8),rotation:Quaternion.Zero(), scale: Vector3.create(1,1,1)},
924
+ - loop: _boolean_ If true, the NPC walks in circles over the provided set of points in the path. _false_ by default, unless the NPC is initiated with a `path`, in which case it starts as _true_.
904
925
 
905
926
 
906
927
 
907
- //NPC Data Object
908
-
909
928
 
910
929
 
911
- {
930
+ - curve: _boolean_ If true, the path is traced a single smooth curve that passes over each of the indicated points. The curve is made out of straight-line segments, the path is stored with 4 times as many points as originally defined. _false_ by default.
912
931
 
913
932
 
914
933
 
915
- type: npc.NPCType.CUSTOM,
916
-
917
934
 
918
935
 
919
- model: 'models/npc.glb',
936
+ - startingPoint: Index position for what point to start from on the path. _0_ by default.
920
937
 
921
938
 
922
939
 
923
- onActivate: ()=>{console.log('npc activated');},
924
-
925
940
 
926
941
 
927
- onWalkAway: ()=>{console.log('test on walk away function')},
942
+ - onFinishCallback: Function to call when the NPC finished walking over all the points on the path. This is only called when `loop` is _false_.
928
943
 
929
944
 
930
945
 
931
- faceUser: true,
932
-
933
946
 
934
947
 
935
- reactDistance: 3,
948
+ - onReachedPointCallback: Function to call once every time the NPC reaches a point in the path.
936
949
 
937
950
 
938
951
 
939
- idleAnim: 'idle1',
940
-
941
952
 
942
953
 
943
- walkingAnim: 'walk1',
944
-
945
954
 
946
955
 
947
- hoverText: "Activate"
948
-
949
-
956
+ ```ts
957
+ export let myNPC = npc.create({position: Vector3.create(8,0,8),rotation:Quaternion.Zero(), scale: Vector3.create(1,1,1)},
950
958
 
959
+ //NPC Data Object
960
+ {
961
+ type: npc.NPCType.CUSTOM,
962
+ model: 'models/npc.glb',
963
+ onActivate: ()=>{console.log('npc activated');},
964
+ onWalkAway: ()=>{console.log('test on walk away function')},
965
+ faceUser: true,
966
+ reactDistance: 3,
967
+ idleAnim: 'idle1',
968
+ walkingAnim: 'walk1',
969
+ hoverText: "Activate"
951
970
  }
952
-
953
-
954
-
955
971
  )
956
972
 
957
-
958
-
959
-
960
-
961
973
  npc.followPath(myNPC,
962
-
963
-
964
-
965
974
  {
975
+ path:path,
976
+ loop:true,
977
+ pathType: npc.NPCPathType.RIGID_PATH,
978
+ onFinishCallback:()=>{console.log('path is done')},
979
+ onReachedPointCallback:()=>{console.log('ending oint')},
980
+ totalDuration: 20
981
+ }
982
+ )
983
+ ```
966
984
 
967
985
 
968
986
 
969
- path:path,
970
-
971
-
972
-
973
- loop:true,
974
-
975
987
 
976
988
 
977
- pathType: npc.NPCPathType.RIGID_PATH,
978
-
979
989
 
980
990
 
981
- onFinishCallback:()=>{console.log('path is done')},
991
+ #### NPC Walking Speed
982
992
 
983
993
 
984
994
 
985
- onReachedPointCallback:()=>{console.log('ending oint')},
986
-
987
995
 
988
996
 
989
- totalDuration: 20
990
-
991
997
 
992
998
 
993
- }
999
+ The following list of factors are used to determine speed in hierarchical order:
994
1000
 
995
1001
 
996
1002
 
997
- )
998
-
999
1003
 
1000
1004
 
1001
1005
 
1002
1006
 
1003
- ```
1007
+ - `totalDuration` parameter set when calling `followPath()` is used over the total distance travelled over the path.
1004
1008
 
1005
1009
 
1006
1010
 
1007
1011
 
1008
1012
 
1009
- #### NPC Walking Speed
1013
+ - `speed` parameter set when calling `followPath()`
1010
1014
 
1011
1015
 
1012
1016
 
1013
1017
 
1014
1018
 
1015
- The following list of factors are used to determine speed in hierarchical order:
1019
+ - `walkingSpeed` parameter set when initializing NPC
1016
1020
 
1017
1021
 
1018
1022
 
1019
1023
 
1020
1024
 
1021
- - `totalDuration` parameter set when calling `followPath()` is used over the total distance travelled over the path.
1025
+ - Default value _2_.
1022
1026
 
1023
1027
 
1024
1028
 
1025
- - `speed` parameter set when calling `followPath()`
1026
-
1027
1029
 
1028
1030
 
1029
- - `walkingSpeed` parameter set when initializing NPC
1030
-
1031
1031
 
1032
1032
 
1033
- - Default value _2_.
1034
-
1035
-
1033
+ #### Joining the path
1036
1034
 
1037
1035
 
1038
1036
 
1039
- #### Joining the path
1040
-
1041
1037
 
1042
1038
 
1043
1039
 
@@ -1048,898 +1044,588 @@ If the NPC's current position when calling `followPath()` doesn't match the firs
1048
1044
 
1049
1045
 
1050
1046
 
1047
+
1048
+
1051
1049
  The `path` can be a single point, and the NPC will then walk a from its current position to that point.
1052
1050
 
1053
1051
 
1054
1052
 
1055
1053
 
1056
1054
 
1055
+
1056
+
1057
1057
  > Note: If the speed of the NPC is determined by a `totalDuration` value, the segment that the NPC walks to join into the path is counted as part of the full path. If this segment is long, it will increase the NPC walking speed so that the full path lasts as what's indicated by the `totalDuration`.
1058
1058
 
1059
1059
 
1060
1060
 
1061
1061
 
1062
1062
 
1063
+
1064
+
1063
1065
  In this example the NPC is far away from the start of the path. It will first walk from _10, 0, 10_ to _2, 0, 2_ and then continue the path.
1064
1066
 
1065
1067
 
1066
1068
 
1067
1069
 
1068
1070
 
1069
- ```ts
1070
-
1071
1071
 
1072
1072
 
1073
+ ```ts
1073
1074
  export let myNPC = npc.create({position: Vector3.create(10,0,10),rotation:Quaternion.Zero(), scale: Vector3.create(1,1,1)},
1074
1075
 
1075
-
1076
-
1077
1076
  //NPC Data Object
1078
-
1079
-
1080
-
1081
1077
  {
1078
+ type: npc.NPCType.CUSTOM,
1079
+ model: 'models/npc.glb',
1080
+ onActivate: ()=>{console.log('npc activated');},
1081
+ }
1082
+ )
1083
+ npc.followPath(myNPC,
1084
+ {
1085
+ path: [new Vector3(2, 0, 2), new Vector3(4, 0, 4), new Vector3(6, 0, 6)]
1086
+ })
1087
+ ```
1082
1088
 
1083
1089
 
1084
1090
 
1085
- type: npc.NPCType.CUSTOM,
1086
-
1087
1091
 
1088
1092
 
1089
- model: 'models/npc.glb',
1090
-
1091
1093
 
1092
1094
 
1093
- onActivate: ()=>{console.log('npc activated');},
1095
+ #### Example Interrupting the NPC
1094
1096
 
1095
1097
 
1096
1098
 
1097
- }
1098
-
1099
1099
 
1100
1100
 
1101
- )
1102
-
1103
1101
 
1104
1102
 
1105
- npc.followPath(myNPC,
1103
+ In the following example, an NPC starts roaming walking over a path, pausing on every point to call out for its lost kitten. If the player activates the NPC (by pressing E on it or walking near it) the NPC stops, and turns to face the player and talk. When the conversation is over, the NPC returns to walking its path from where it left off.
1106
1104
 
1107
1105
 
1108
1106
 
1109
- {
1110
-
1111
1107
 
1112
1108
 
1113
- path: [new Vector3(2, 0, 2), new Vector3(4, 0, 4), new Vector3(6, 0, 6)]
1114
-
1115
1109
 
1116
1110
 
1117
- })
1111
+ ```ts
1112
+ export let myNPC = npc.create({position: Vector3.create(10,0,10),rotation:Quaternion.Zero(), scale: Vector3.create(1,1,1)},
1118
1113
 
1119
-
1120
-
1121
- ```
1122
-
1123
-
1124
-
1125
-
1126
-
1127
- #### Example Interrupting the NPC
1128
-
1129
-
1130
-
1131
-
1132
-
1133
- In the following example, an NPC starts roaming walking over a path, pausing on every point to call out for its lost kitten. If the player activates the NPC (by pressing E on it or walking near it) the NPC stops, and turns to face the player and talk. When the conversation is over, the NPC returns to walking its path from where it left off.
1134
-
1135
-
1136
-
1137
-
1138
-
1139
- ```ts
1140
-
1141
-
1142
-
1143
- export let myNPC = npc.create({position: Vector3.create(10,0,10),rotation:Quaternion.Zero(), scale: Vector3.create(1,1,1)},
1144
-
1145
-
1146
-
1147
- //NPC Data Object
1148
-
1149
-
1150
-
1151
- {
1152
-
1153
-
1154
-
1155
- type: npc.NPCType.CUSTOM,
1156
-
1157
-
1158
-
1159
- model: 'models/npc.glb',
1160
-
1161
-
1162
-
1163
- onActivate: ()=>{
1164
-
1165
-
1166
-
1167
- npc.stopWalking(myNPC);
1168
-
1169
-
1170
-
1171
- npc.talk(myNPC, lostCat, 0)
1172
-
1173
-
1174
-
1175
- console.log('npc activated');
1176
-
1177
-
1178
-
1179
- },
1180
-
1181
-
1182
-
1183
- walkingAnim: 'walk1',
1184
-
1185
-
1186
-
1187
- faceUser:true
1188
-
1189
-
1190
-
1191
- }
1192
-
1193
-
1194
-
1195
- )
1196
-
1197
-
1198
-
1199
-
1114
+ //NPC Data Object
1115
+ {
1116
+ type: npc.NPCType.CUSTOM,
1117
+ model: 'models/npc.glb',
1118
+ onActivate: ()=>{
1119
+ npc.stopWalking(myNPC);
1120
+ npc.talk(myNPC, lostCat, 0)
1121
+ console.log('npc activated');
1122
+ },
1123
+ walkingAnim: 'walk1',
1124
+ faceUser:true
1125
+ }
1126
+ )
1200
1127
 
1201
1128
  npc.followPath(myNPC,
1202
-
1203
-
1204
-
1205
1129
  {
1206
-
1207
-
1208
-
1209
- path: [new Vector3(4, 0, 30), new Vector3(6, 0, 29), new Vector3(15, 0, 25)],
1210
-
1211
-
1212
-
1213
- loop: true,
1214
-
1215
-
1216
-
1217
- onReachedPointCallback: () => {
1218
-
1219
-
1220
-
1221
- npc.stopWalking(myNPC, 3)
1222
-
1223
-
1224
-
1225
- npc.playAnimation(myNPC, `Cocky`, true, 2.93)
1226
-
1227
-
1228
-
1130
+ path: [new Vector3(4, 0, 30), new Vector3(6, 0, 29), new Vector3(15, 0, 25)],
1131
+ loop: true,
1132
+ onReachedPointCallback: () => {
1133
+ npc.stopWalking(myNPC, 3)
1134
+ npc.playAnimation(myNPC, `Cocky`, true, 2.93)
1135
+ }
1229
1136
  }
1230
-
1231
-
1232
-
1233
- })
1234
-
1235
-
1236
-
1237
-
1137
+ )
1238
1138
 
1239
1139
  export let lostCat: Dialog[] = [
1240
-
1241
-
1242
-
1243
- {
1244
-
1245
-
1246
-
1247
- text: `I lost my cat, I'm going crazy here`
1248
-
1249
-
1250
-
1251
- },
1252
-
1253
-
1254
-
1255
- {
1256
-
1257
-
1258
-
1259
- text: `Have you seen it anywhere?`
1260
-
1261
-
1262
-
1263
- },
1264
-
1265
-
1266
-
1267
- {
1268
-
1269
-
1270
-
1271
- text: `Ok, I'm gonna go back to looking for it`,
1272
-
1273
-
1274
-
1275
- triggeredByNext: () => {
1276
-
1277
-
1278
-
1279
- npc.followPath(myNPC)
1280
-
1281
-
1282
-
1283
- },
1284
-
1285
-
1286
-
1287
- isEndOfDialog: true
1288
-
1289
-
1290
-
1291
- }
1292
-
1293
-
1294
-
1295
- ]
1296
-
1297
-
1298
-
1299
- ```
1300
-
1301
-
1302
-
1303
-
1304
-
1305
- ### End interaction
1306
-
1307
-
1308
-
1309
-
1310
-
1311
- The `endInteraction()` function can be used to abruptly end interactions with the NPC.
1312
-
1313
-
1314
-
1315
-
1316
-
1317
- If applicable, it closes the dialog UI, hides speech bubbles, and makes the NPC stop rotating to face the player.
1318
-
1319
-
1320
-
1321
-
1322
-
1323
- ```ts
1324
-
1325
-
1326
-
1327
- npc.endInteraction(myNPC)
1328
-
1329
-
1330
-
1331
- ```
1332
-
1333
-
1334
-
1335
-
1336
-
1337
- As an alternative, you can call the `handleWalkAway()` function, which has the same effects (as long as `continueOnWalkAway` isn't set to true), but also triggers the `onWalkAway()` function.
1338
-
1339
-
1340
-
1341
-
1342
-
1343
- ## NPC Dialog Window
1344
-
1345
-
1346
-
1347
-
1348
-
1349
- You can display an interactive dialog window to simulate a conversation with a non-player character (NPC).
1350
-
1351
-
1352
-
1353
-
1354
-
1355
- The conversation is based on a script in JSON format. The script can include questions that can take you forward or backward, or end the conversation.
1356
-
1357
-
1358
-
1359
-
1360
-
1361
- <img src="screenshots/NPC1.png" width="500">
1362
-
1363
-
1364
-
1365
-
1366
-
1367
- ### The NPC script
1368
-
1369
-
1370
-
1371
-
1372
-
1373
- Each entry on the script must include at least a `text` field, but can include several more fields to further customize it.
1374
-
1375
-
1376
-
1377
-
1378
-
1379
- Below is a minimal dialog.
1380
-
1381
-
1382
-
1383
-
1384
-
1385
- ```ts
1386
-
1387
-
1388
-
1389
- export let NPCTalk: Dialog[] = [
1390
-
1391
-
1392
-
1393
- {
1394
-
1395
-
1396
-
1397
- text: 'Hi there'
1398
-
1399
-
1400
-
1401
- },
1402
-
1403
-
1404
-
1405
- {
1406
-
1407
-
1408
-
1409
- text: 'It sure is nice talking to you'
1410
-
1411
-
1412
-
1413
- },
1414
-
1415
-
1416
-
1417
- {
1418
-
1419
-
1420
-
1421
- text: 'I must go, my planet needs me',
1422
-
1423
-
1424
-
1425
- isEndOfDialog: true
1426
-
1427
-
1428
-
1140
+ {
1141
+ text: `I lost my cat, I'm going crazy here`
1142
+ },
1143
+
1144
+ {
1145
+ text: `Have you seen it anywhere?`
1146
+ },
1147
+
1148
+ {
1149
+ text: `Ok, I'm gonna go back to looking for it`,
1150
+ triggeredByNext: () => {
1151
+ npc.followPath(myNPC)
1152
+ },
1153
+ isEndOfDialog: true
1429
1154
  }
1430
-
1431
-
1432
-
1433
1155
  ]
1434
-
1435
-
1436
-
1437
1156
  ```
1438
1157
 
1439
1158
 
1440
1159
 
1441
1160
 
1442
1161
 
1443
- The player advances through each entry by clicking the mouse button. Once the last is reached, clicking again closes the window, as it's marked as `isEndOfDialog`.
1444
-
1445
-
1446
-
1447
-
1448
-
1449
- The script must adhere to the following schema:
1450
-
1451
-
1452
-
1453
-
1454
-
1455
- ```ts
1456
-
1457
-
1458
-
1459
- class Dialog {
1460
-
1461
-
1462
-
1463
- text: string
1464
-
1465
-
1466
-
1467
- fontSize?: number
1468
-
1469
-
1470
-
1471
- typeSpeed?: number
1472
-
1473
-
1474
-
1475
- isEndOfDialog?: boolean
1476
-
1477
-
1478
-
1479
- isQuestion?:boolean
1480
-
1481
-
1482
-
1483
- buttons?: ButtonData[]
1484
-
1485
-
1486
-
1487
- audio?: string
1488
-
1489
-
1490
-
1491
- triggeredByNext?: () => void
1492
-
1493
-
1494
-
1495
- }
1496
-
1497
-
1498
-
1499
- ```
1500
-
1501
-
1502
-
1503
-
1504
-
1505
- > Note: A `Dialog` object can be used as an input both for the `talk()` function (that is displayed in the UI), and the `talkBubble()` function (that is displayed in a floating bubble over the NPC). Properties marked with `*` are only applicable to UI dialogs.
1506
-
1507
-
1508
-
1509
-
1510
-
1511
- You can set the following fields to change the appearance of a dialog:
1512
-
1513
-
1514
-
1515
-
1516
-
1517
- - `text`: The dialog text
1518
-
1519
-
1520
-
1521
- - `fontSize`: Size of the text
1522
-
1523
-
1524
-
1525
1162
 
1526
1163
 
1527
- Other fields:
1164
+ ### End interaction
1528
1165
 
1529
1166
 
1530
1167
 
1531
- - `buttons *`: An array of buttons to use in a question entry, covered in the next section.
1168
+
1532
1169
 
1533
1170
 
1534
1171
 
1535
- - `audio`: String with the path to an audio file to play once when this dialog is shown on the UI.
1172
+ The `endInteraction()` function can be used to abruptly end interactions with the NPC.
1536
1173
 
1537
1174
 
1538
1175
 
1539
- - `typeSpeed`: The text appears one character at a time, simulating typing. Players can click to skip the animation. Tune the speed of this typing (30 by default) to go slower or faster. Set to _-1_ to skip the animation.
1540
-
1541
1176
 
1542
1177
 
1543
1178
 
1544
1179
 
1545
- #### Questions and conversation trees
1180
+ If applicable, it closes the dialog UI, hides speech bubbles, and makes the NPC stop rotating to face the player.
1546
1181
 
1547
1182
 
1548
1183
 
1549
1184
 
1550
1185
 
1551
- The script can include questions that prompt the player to pick between two or up to four options. These questions can branch the conversation out and trigger other actions in the scene.
1552
-
1553
1186
 
1554
1187
 
1555
-
1188
+ ```ts
1189
+ npc.endInteraction(myNPC)
1190
+ ```
1556
1191
 
1557
- <img src="screenshots/NPC2.png" width="500">
1192
+
1558
1193
 
1559
1194
 
1560
1195
 
1561
1196
 
1562
1197
 
1563
- > Note: Questions are only used by UI dialogs. If used in a speech bubble, questions will be displayed as regular entries with no buttons or options.
1198
+ As an alternative, you can call the `handleWalkAway()` function, which has the same effects (as long as `continueOnWalkAway` isn't set to true), but also triggers the `onWalkAway()` function.
1564
1199
 
1565
1200
 
1566
1201
 
1567
1202
 
1568
1203
 
1569
- To make an entry a question, set the `isQuestion` field to _true_. This displays a set of buttons rather than the click icon. It also disables the click to advance to the next entry.
1570
-
1571
1204
 
1572
1205
 
1573
-
1206
+ ## NPC Dialog Window
1574
1207
 
1575
- The `buttons` property of an entry contains an array of `ButtonData` objects, each one of these defines one button.
1208
+
1576
1209
 
1577
1210
 
1578
1211
 
1579
1212
 
1580
1213
 
1581
- When on a question entry, you must provide at least the following for each button:
1214
+ You can display an interactive dialog window to simulate a conversation with a non-player character (NPC).
1582
1215
 
1583
1216
 
1584
1217
 
1585
1218
 
1586
1219
 
1587
- - `label`: _(string)_ The label to show on the button.
1588
-
1589
1220
 
1590
1221
 
1591
- - `goToDialog`: _(number | string)_ The index or name of the next dialog entry to display when activated.
1592
-
1593
-
1222
+ The conversation is based on a script in JSON format. The script can include questions that can take you forward or backward, or end the conversation.
1594
1223
 
1595
1224
 
1596
1225
 
1597
- > TIP: It's always better to refer to an entry by name, since the array index might shift if you add more entries and it can get hard to keep track of these references.
1598
-
1599
1226
 
1600
1227
 
1601
1228
 
1602
1229
 
1603
- You can also set the following:
1230
+ <img src="screenshots/NPC1.png" width="500">
1604
1231
 
1605
1232
 
1606
1233
 
1607
1234
 
1608
1235
 
1609
- - `triggeredActions`: _( () => void )_ An additional function to run whenever the button is activated
1610
-
1611
1236
 
1612
1237
 
1613
- - `fontSize`: _(number)_ Font size of the text
1238
+ ### The NPC script
1614
1239
 
1615
1240
 
1616
1241
 
1617
- - `offsetX`: _(number)_ Offset of the label on the X axis, relative to its normal position.
1242
+
1618
1243
 
1619
1244
 
1620
1245
 
1621
- - `offsetY`: _(number)_ Offset of the label on the Y axis, relative to its normal position.
1246
+ Each entry on the script must include at least a `text` field, but can include several more fields to further customize it.
1622
1247
 
1623
1248
 
1624
1249
 
1625
1250
 
1626
1251
 
1627
- All buttons can be clicked to activate them. Additionally, the first button in the array can be activated by pressing the _E_ key. The second button in the array can be activated by pressing the _F_ key,
1628
-
1629
1252
 
1630
1253
 
1631
-
1254
+ Below is a minimal dialog.
1632
1255
 
1633
- <img src="screenshots/NPC3.png" width="500">
1256
+
1634
1257
 
1635
1258
 
1636
1259
 
1637
1260
 
1638
1261
 
1639
1262
  ```ts
1263
+ export let NPCTalk: Dialog[] = [
1264
+ {
1265
+ text: 'Hi there'
1266
+ },
1267
+
1268
+ {
1269
+ text: 'It sure is nice talking to you'
1270
+ },
1271
+
1272
+ {
1273
+ text: 'I must go, my planet needs me',
1274
+ isEndOfDialog: true
1275
+ }
1276
+ ]
1640
1277
 
1641
1278
 
1642
1279
 
1643
- export let GemsMission: Dialog[] = [
1644
-
1645
1280
 
1646
1281
 
1647
- {
1282
+ ```
1648
1283
 
1649
1284
 
1650
1285
 
1651
- text: `Hello stranger`
1652
-
1653
1286
 
1654
1287
 
1655
- },
1656
-
1657
1288
 
1658
1289
 
1659
- {
1290
+ The player advances through each entry by clicking the mouse button. Once the last is reached, clicking again closes the window, as it's marked as `isEndOfDialog`.
1660
1291
 
1661
1292
 
1662
1293
 
1663
- text: `Can you help me finding my missing gems?`,
1664
-
1665
1294
 
1666
1295
 
1667
- isQuestion: true,
1668
-
1669
1296
 
1670
1297
 
1671
- buttons: [
1298
+ The script must adhere to the following schema:
1672
1299
 
1673
1300
 
1674
1301
 
1675
- { label: `Yes!`, goToDialog: 2 },
1676
-
1677
1302
 
1678
1303
 
1679
- { label: `I'm busy`, goToDialog: 4 }
1680
-
1681
1304
 
1682
1305
 
1683
- ]
1306
+ ```ts
1307
+ class Dialog {
1308
+ text: string
1309
+ fontSize?: number
1310
+ typeSpeed?: number
1311
+ isEndOfDialog?: boolean
1312
+ isQuestion?:boolean
1313
+ buttons?: ButtonData[]
1314
+ audio?: string
1315
+ triggeredByNext?: () => void
1316
+ }
1317
+ ```
1684
1318
 
1685
1319
 
1686
1320
 
1687
- },
1688
-
1689
1321
 
1690
1322
 
1691
- {
1692
-
1693
1323
 
1694
1324
 
1695
- text: `Ok, awesome, thanks!`
1325
+ > Note: A `Dialog` object can be used as an input both for the `talk()` function (that is displayed in the UI), and the `talkBubble()` function (that is displayed in a floating bubble over the NPC). Properties marked with `*` are only applicable to UI dialogs.
1696
1326
 
1697
1327
 
1698
1328
 
1699
- },
1700
-
1701
1329
 
1702
1330
 
1703
- {
1704
-
1705
1331
 
1706
1332
 
1707
- text: `I need you to find 10 gems scattered around this scene, go find them!`,
1333
+ You can set the following fields to change the appearance of a dialog:
1708
1334
 
1709
1335
 
1710
1336
 
1711
- isEndOfDialog: true
1337
+
1712
1338
 
1713
1339
 
1714
1340
 
1715
- },
1341
+ - `text`: The dialog text
1716
1342
 
1717
1343
 
1718
1344
 
1719
- {
1720
-
1721
1345
 
1722
1346
 
1723
- text: `Ok, come back soon`,
1347
+ - `fontSize`: Size of the text
1724
1348
 
1725
1349
 
1726
1350
 
1727
- isEndOfDialog: true
1351
+
1728
1352
 
1729
1353
 
1730
1354
 
1731
- }
1355
+ Other fields:
1732
1356
 
1733
1357
 
1734
1358
 
1735
- ]
1736
-
1737
1359
 
1738
1360
 
1739
- ```
1361
+ - `buttons *`: An array of buttons to use in a question entry, covered in the next section.
1740
1362
 
1741
1363
 
1742
1364
 
1743
1365
 
1744
1366
 
1745
- #### Triggering functions from the dialog
1367
+ - `audio`: String with the path to an audio file to play once when this dialog is shown on the UI.
1746
1368
 
1747
1369
 
1748
1370
 
1749
1371
 
1750
1372
 
1751
- You can run functions that may affect any other part of your scene. These functions get triggered when the player interacts with the dialog window, or when the NPC displays speech bubbles.
1373
+ - `typeSpeed`: The text appears one character at a time, simulating typing. Players can click to skip the animation. Tune the speed of this typing (30 by default) to go slower or faster. Set to _-1_ to skip the animation.
1752
1374
 
1753
1375
 
1754
1376
 
1755
1377
 
1756
1378
 
1757
- - `triggeredByNext`: Is executed when the player advances to the next dialog on a non-question dialog. The function also gets called if the dialog is the end of the conversation. It also gets called when a speech bubble advances to the next entry.
1758
-
1759
1379
 
1760
1380
 
1761
-
1381
+ #### Questions and conversation trees
1762
1382
 
1763
- - `triggeredActions`: This property is associated to a button and is executed on a question dialog if the player activates the corresponding button. You can have up to 4 different buttons per entry, each with its own actions.
1383
+
1764
1384
 
1765
1385
 
1766
1386
 
1767
1387
 
1768
1388
 
1769
- ```ts
1389
+ The script can include questions that prompt the player to pick between two or up to four options. These questions can branch the conversation out and trigger other actions in the scene.
1770
1390
 
1771
1391
 
1772
1392
 
1773
- export let GemsMission: Dialog[] = [
1393
+
1774
1394
 
1775
1395
 
1776
1396
 
1777
- {
1397
+ <img src="screenshots/NPC2.png" width="500">
1778
1398
 
1779
1399
 
1780
1400
 
1781
- text: `Hello stranger`,
1401
+
1782
1402
 
1783
1403
 
1784
1404
 
1785
- triggeredByNext: () => {
1405
+ > Note: Questions are only used by UI dialogs. If used in a speech bubble, questions will be displayed as regular entries with no buttons or options.
1786
1406
 
1787
1407
 
1788
1408
 
1789
- // NPC plays animation to show a gem
1409
+
1790
1410
 
1791
1411
 
1792
1412
 
1793
- }
1413
+ To make an entry a question, set the `isQuestion` field to _true_. This displays a set of buttons rather than the click icon. It also disables the click to advance to the next entry.
1794
1414
 
1795
1415
 
1796
1416
 
1797
- },
1417
+
1798
1418
 
1799
1419
 
1800
1420
 
1801
- {
1421
+ The `buttons` property of an entry contains an array of `ButtonData` objects, each one of these defines one button.
1802
1422
 
1803
1423
 
1804
1424
 
1805
- text: `Can you help me finding my missing gems?`,
1425
+
1806
1426
 
1807
1427
 
1808
1428
 
1809
- isQuestion: true,
1429
+ When on a question entry, you must provide at least the following for each button:
1810
1430
 
1811
1431
 
1812
1432
 
1813
- buttons: [
1433
+
1814
1434
 
1815
1435
 
1816
1436
 
1817
- {
1437
+ - `label`: _(string)_ The label to show on the button.
1818
1438
 
1819
1439
 
1820
1440
 
1821
- label: `Yes!`,
1822
-
1823
1441
 
1824
1442
 
1825
- goToDialog: 2,
1443
+ - `goToDialog`: _(number | string)_ The index or name of the next dialog entry to display when activated.
1826
1444
 
1827
1445
 
1828
1446
 
1829
- triggeredActions: () => {
1447
+
1830
1448
 
1831
1449
 
1832
1450
 
1833
- // NPC plays an animation to celebrate
1451
+ > TIP: It's always better to refer to an entry by name, since the array index might shift if you add more entries and it can get hard to keep track of these references.
1834
1452
 
1835
1453
 
1836
1454
 
1837
- }
1455
+
1838
1456
 
1839
1457
 
1840
1458
 
1841
- },
1459
+ You can also set the following:
1842
1460
 
1843
1461
 
1844
1462
 
1845
- {
1463
+
1846
1464
 
1847
1465
 
1848
1466
 
1849
- label: `I'm busy`,
1467
+ - `triggeredActions`: _( () => void )_ An additional function to run whenever the button is activated
1850
1468
 
1851
1469
 
1852
1470
 
1853
- goToDialog: 4
1854
-
1855
1471
 
1856
1472
 
1857
- triggeredActions: () => {
1473
+ - `fontSize`: _(number)_ Font size of the text
1858
1474
 
1859
1475
 
1860
1476
 
1861
- // NPC waves goodbye
1862
-
1863
1477
 
1864
1478
 
1865
- }
1479
+ - `offsetX`: _(number)_ Offset of the label on the X axis, relative to its normal position.
1866
1480
 
1867
1481
 
1868
1482
 
1869
- },
1870
-
1871
1483
 
1872
1484
 
1873
- ]
1485
+ - `offsetY`: _(number)_ Offset of the label on the Y axis, relative to its normal position.
1874
1486
 
1875
1487
 
1876
1488
 
1877
- },
1489
+
1878
1490
 
1879
1491
 
1880
1492
 
1881
- {
1493
+ All buttons can be clicked to activate them. Additionally, the first button in the array can be activated by pressing the _E_ key. The second button in the array can be activated by pressing the _F_ key,
1882
1494
 
1883
1495
 
1884
1496
 
1885
- text: `Ok, awesome, thanks!`,
1497
+
1886
1498
 
1887
1499
 
1888
1500
 
1889
- },
1501
+ <img src="screenshots/NPC3.png" width="500">
1890
1502
 
1891
1503
 
1892
1504
 
1893
- {
1505
+
1894
1506
 
1895
1507
 
1896
1508
 
1897
- text: `I need you to find 10 gems scattered around this scene, go find them!`,
1509
+ ```ts
1510
+ export let GemsMission: Dialog[] = [
1511
+ {
1512
+ text: `Hello stranger`
1513
+ },
1514
+
1515
+ {
1516
+ text: `Can you help me finding my missing gems?`,
1517
+ isQuestion: true,
1518
+ buttons: [
1519
+ { label: `Yes!`, goToDialog: 2 },
1520
+ { label: `I'm busy`, goToDialog: 4 }
1521
+ ]
1522
+ },
1523
+
1524
+ {
1525
+ text: `Ok, awesome, thanks!`
1526
+ },
1527
+
1528
+ {
1529
+ text: `I need you to find 10 gems scattered around this scene, go find them!`,
1530
+ isEndOfDialog: true
1531
+ },
1532
+
1533
+ {
1534
+ text: `Ok, come back soon`,
1535
+ isEndOfDialog: true
1536
+ }
1537
+ ]
1538
+ ```
1898
1539
 
1899
1540
 
1900
1541
 
1901
- isEndOfDialog: true
1542
+
1902
1543
 
1903
1544
 
1904
1545
 
1905
- triggeredByNext: () => {
1546
+ #### Triggering functions from the dialog
1906
1547
 
1907
1548
 
1908
1549
 
1909
- // Gems are rendered all around the scene
1550
+
1910
1551
 
1911
1552
 
1912
1553
 
1913
- }
1554
+ You can run functions that may affect any other part of your scene. These functions get triggered when the player interacts with the dialog window, or when the NPC displays speech bubbles.
1914
1555
 
1915
1556
 
1916
1557
 
1917
- },
1558
+
1918
1559
 
1919
1560
 
1920
1561
 
1921
- {
1562
+ - `triggeredByNext`: Is executed when the player advances to the next dialog on a non-question dialog. The function also gets called if the dialog is the end of the conversation. It also gets called when a speech bubble advances to the next entry.
1922
1563
 
1923
1564
 
1924
1565
 
1925
- text: `Ok, come back soon`,
1566
+
1926
1567
 
1927
1568
 
1928
1569
 
1929
- isEndOfDialog: true
1570
+ - `triggeredActions`: This property is associated to a button and is executed on a question dialog if the player activates the corresponding button. You can have up to 4 different buttons per entry, each with its own actions.
1930
1571
 
1931
1572
 
1932
1573
 
1933
- }
1574
+
1934
1575
 
1935
1576
 
1936
1577
 
1578
+ ```ts
1579
+ export let GemsMission: Dialog[] = [
1580
+ {
1581
+ text: `Hello stranger`,
1582
+ triggeredByNext: () => {
1583
+ // NPC plays animation to show a gem
1584
+ }
1585
+ },
1586
+
1587
+ {
1588
+ text: `Can you help me finding my missing gems?`,
1589
+ isQuestion: true,
1590
+ buttons: [
1591
+ {
1592
+ label: `Yes!`,
1593
+ goToDialog: 2,
1594
+ triggeredActions: () => {
1595
+ // NPC plays an animation to celebrate
1596
+ }
1597
+ },
1598
+
1599
+ {
1600
+ label: `I'm busy`,
1601
+ goToDialog: 4
1602
+ triggeredActions: () => {
1603
+ // NPC waves goodbye
1604
+ }
1605
+ }
1606
+ ]
1607
+ },
1608
+
1609
+ {
1610
+ text: `Ok, awesome, thanks!`,
1611
+ },
1612
+
1613
+ {
1614
+ text: `I need you to find 10 gems scattered around this scene, go find them!`,
1615
+ isEndOfDialog: true
1616
+ triggeredByNext: () => {
1617
+ // Gems are rendered all around the scene
1618
+ },
1619
+
1620
+ {
1621
+ text: `Ok, come back soon`,
1622
+ isEndOfDialog: true
1623
+ }
1937
1624
  ]
1625
+ ```
1938
1626
 
1939
1627
 
1940
1628
 
1941
- ```
1942
-
1943
1629
 
1944
1630
 
1945
1631
  ## No-NPC Dialogs
@@ -1948,73 +1634,84 @@ isEndOfDialog: true
1948
1634
 
1949
1635
 
1950
1636
 
1637
+
1638
+
1951
1639
  You can open a Dialog window that isn't associated with any `NPC` object in the scene. The `openDialogWindow()` function has all the same functionality as calling the `talk()` function on an NPC, but may be more practical in scenarios where a character isn't physically there, or where the conversation isn't with a particular character.
1952
1640
 
1953
1641
 
1954
1642
 
1955
1643
 
1956
1644
 
1645
+
1646
+
1957
1647
  ### The Dialog window
1958
1648
 
1959
1649
 
1960
1650
 
1961
1651
 
1962
1652
 
1653
+
1654
+
1963
1655
  To create a new dialog window, call `createDialogWindow()` and store as a variable. This will instantiate the window but keep it hidden until you open it.
1964
1656
 
1965
1657
 
1966
1658
 
1967
- ```ts
1968
-
1969
1659
 
1970
1660
 
1661
+ ```ts
1971
1662
  let dialogWindow = npc.createDialogWindow()
1663
+ ```
1972
1664
 
1973
1665
 
1974
1666
 
1975
- ```
1976
-
1977
1667
 
1978
1668
 
1979
1669
  <img src="screenshots/NPC1.png" width="500">
1980
1670
 
1981
1671
 
1982
1672
 
1673
+
1674
+
1983
1675
  When instantiating a new blank dialog, you can pass the following optional parameters:
1984
1676
 
1985
1677
 
1678
+
1986
1679
 
1987
1680
 
1988
1681
  - `defaultPortrait`: Sets a default portrait image to use on the left of all dialogs that don't specify an image. If a dialog has no portrait and no default is provided, no image is shown on the left. This field expects a `Portrait` object, that may include the following fields: - `path`: Path to the image file - `xOffset`: Offset on X, relative to the normal position of the portrait. - `yOffset`: Offset on Y, relative to the normal position of the portrait. - `section`: Use only a section of the image file, useful when arranging multiple icons into an image atlas. This field takes an `ImageSection` object, specifying `sourceWidth` and `sourceHeight`, and optionally also `sourceLeft` and `sourceTop`.
1989
1682
 
1990
1683
 
1991
1684
 
1685
+
1686
+
1992
1687
  - `useDarkTheme`: Switch the style of the window to the dark theme.
1993
1688
 
1994
1689
 
1995
1690
 
1691
+
1692
+
1996
1693
  - `sound`: Path to a sound file that will be played once for every dialog entry shown, as long as the dialog entry doesn't have its own `audio` property.
1997
1694
 
1998
1695
 
1999
1696
 
2000
1697
 
2001
1698
 
1699
+
1700
+
2002
1701
  Once you have created a dialog window, you can open a dialog window with the `openDialogWindow()` function.
2003
1702
 
2004
1703
 
2005
1704
 
2006
1705
 
2007
1706
 
2008
- ```ts
2009
-
2010
1707
 
2011
1708
 
1709
+ ```ts
2012
1710
  npc.openDialogWindow(dialogWindow, NPCTalk, 0)
1711
+ ```
2013
1712
 
2014
1713
 
2015
1714
 
2016
- ```
2017
-
2018
1715
 
2019
1716
 
2020
1717
 
@@ -2025,46 +1722,54 @@ When calling this function, you must specify:
2025
1722
 
2026
1723
 
2027
1724
 
1725
+
1726
+
2028
1727
  - `NPCScript`: A JSON object composed of an array of `Dialog` objects, that includes all the dialog tree.
2029
1728
 
2030
1729
 
2031
1730
 
2032
1731
 
2033
1732
 
1733
+
1734
+
2034
1735
  A second optional parameter is also available:
2035
1736
 
2036
1737
 
2037
1738
 
2038
1739
 
2039
1740
 
1741
+
1742
+
2040
1743
  - `textId`: The index or `name` property of the entry to show first from the script. The first entry is 0.
2041
1744
 
2042
1745
 
2043
1746
 
2044
1747
 
2045
1748
 
1749
+
1750
+
2046
1751
  > TIP: It's always better to refer to an entry by name, since the array index might shift if you add more entries and it can get hard to keep track of these references.
2047
1752
 
2048
1753
 
2049
1754
 
2050
1755
 
2051
1756
 
1757
+
1758
+
2052
1759
  Close a dialog window at any time by calling the `closeDialogWindow()` function.
2053
1760
 
2054
1761
 
2055
1762
 
2056
1763
 
2057
1764
 
2058
- ```ts
2059
-
2060
1765
 
2061
1766
 
1767
+ ```ts
2062
1768
  npc.closeDialogWindow(dialogWindow)
1769
+ ```
2063
1770
 
2064
1771
 
2065
1772
 
2066
- ```
2067
-
2068
1773
 
2069
1774
 
2070
1775
 
@@ -2075,94 +1780,120 @@ For details on how to construct the dialog tree, see the sections above. The req
2075
1780
 
2076
1781
 
2077
1782
 
1783
+
1784
+
2078
1785
  ---
2079
1786
 
2080
1787
 
2081
1788
 
1789
+
1790
+
2082
1791
  ## Contribute
2083
1792
 
2084
1793
 
2085
1794
 
2086
1795
 
2087
1796
 
1797
+
1798
+
2088
1799
  In order to test changes made to this repository in active scenes, do the following:
2089
1800
 
2090
1801
 
2091
1802
 
2092
1803
 
2093
1804
 
1805
+
1806
+
2094
1807
  1. Run `npm run build` for the internal files of the library to be generated
2095
1808
 
2096
1809
 
2097
1810
 
1811
+
1812
+
2098
1813
  2. Run `npm run link` on this repository
2099
1814
 
2100
1815
 
2101
1816
 
1817
+
1818
+
2102
1819
  3. On a new Decentraland scene, import this library as you normally would and include the tests you need
2103
1820
 
2104
1821
 
2105
1822
 
1823
+
1824
+
2106
1825
  4. On the scene directory, run `npm link dcl-npc-toolkit`
2107
1826
 
2108
1827
 
2109
1828
 
2110
1829
 
2111
1830
 
1831
+
1832
+
2112
1833
  > Note: When done testing, run `npm unlink` on both folders, so that the scene stops using the local version of the library.
2113
1834
 
2114
1835
 
2115
1836
 
2116
1837
 
2117
1838
 
1839
+
1840
+
2118
1841
  ## CI/CD
2119
1842
 
2120
1843
 
2121
1844
 
2122
1845
 
2123
1846
 
1847
+
1848
+
2124
1849
  This repository uses `semantic-release` to automatically release new versions of the package to NPM.
2125
1850
 
2126
1851
 
2127
1852
 
2128
1853
 
2129
1854
 
1855
+
1856
+
2130
1857
  Use the following convention for commit names:
2131
1858
 
2132
1859
 
2133
1860
 
2134
1861
 
2135
1862
 
1863
+
1864
+
2136
1865
  `feat: something`: Minor release, every time you add a feature or enhancement that doesn’t break the api.
2137
1866
 
2138
1867
 
2139
1868
 
2140
1869
 
2141
1870
 
1871
+
1872
+
2142
1873
  `fix: something`: Bug fixing / patch
2143
1874
 
2144
1875
 
2145
1876
 
2146
1877
 
2147
1878
 
1879
+
1880
+
2148
1881
  `chore: something`: Anything that doesn't require a release to npm, like changing the readme. Updating a dependency is **not** a chore if it fixes a bug or a vulnerability, that's a `fix`.
2149
1882
 
2150
1883
 
2151
1884
 
2152
1885
 
2153
1886
 
1887
+
1888
+
2154
1889
  If you break the API of the library, you need to do a major release, and that's done a different way. You need to add a second comment that starts with `BREAKING CHANGE`, like:
2155
1890
 
2156
1891
 
2157
1892
 
2158
1893
 
2159
1894
 
2160
- ```
2161
-
2162
1895
 
2163
1896
 
1897
+ ```
2164
1898
  commit -m "feat: changed the signature of a method" -m "BREAKING CHANGE: this commit breaks the API, changing foo(arg1) to foo(arg1, arg2)"
2165
-
2166
-
2167
-
2168
1899
  ```