dcl-npc-toolkit 1.0.10-20230523134956.commit-0a9ee1c → 1.0.10-20230523142340.commit-27288fc

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.
package/README.md CHANGED
@@ -1,927 +1,1492 @@
1
1
 
2
2
 
3
3
 
4
+
5
+
6
+
7
+
4
8
  # NPC-library
5
9
 
6
10
 
7
11
 
12
+
13
+
14
+
15
+
8
16
  A collection of tools for creating Non-Player-Characters (NPCs). These are capable of having conversations with the player, and play different animations.
9
17
 
10
18
 
11
19
 
20
+
21
+
22
+
23
+
12
24
  Capabilities of the NPCs in this library:
13
25
 
14
26
 
15
27
 
28
+
29
+
30
+
31
+
16
32
  - Start a conversation when clicked or when walking near
17
33
 
34
+
35
+
36
+
37
+
18
38
  - Trigger any action when clicked or when walking near
19
39
 
40
+
41
+
42
+
43
+
20
44
  - Trigger any action when the player walks away
21
45
 
46
+
47
+
48
+
49
+
22
50
  - Turn around slowly to always face the player
23
51
 
52
+
53
+
54
+
55
+
24
56
  - Play an animation in the NPC 3d model, optionally returning to loop the idle animation afterwards
25
57
 
26
58
 
27
59
 
60
+
61
+
62
+
63
+
28
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.
29
65
 
30
66
 
31
67
 
68
+
69
+
70
+
71
+
32
72
  To use NPCs in your scene:
33
73
 
34
74
 
35
75
 
76
+
77
+
78
+
79
+
36
80
  1. Install the library as an npm bundle. Run this command in your scene's project folder:
37
81
 
38
82
 
39
83
 
40
- ```
84
+
41
85
 
42
- npm i dcl-npc-toolkit -B
86
+
43
87
 
88
+ ```ts
89
+ npm i dcl-npc-toolkit -B
44
90
  ```
45
91
 
92
+
93
+
94
+
95
+
46
96
  2. Install the dependent sdk utils library as an npm bundle. Run this command in your scene's project folder:
47
97
 
48
98
 
49
99
 
50
- ```
100
+
51
101
 
52
- npm i @dcl-sdk/utils -B
102
+
53
103
 
54
104
  ```
105
+ npm i @dcl-sdk/utils -B
106
+ ```
107
+
108
+
109
+
55
110
 
56
111
 
57
112
  3. Run `dcl start` or `dcl build` so the dependencies are correctly installed.
58
113
 
59
114
 
60
115
 
116
+
117
+
118
+
119
+
61
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:
62
121
 
63
122
 
64
123
 
65
- ```ts
124
+
66
125
 
67
- import * as npc from 'dcl-npc-toolkit'
126
+
68
127
 
128
+ ```ts
129
+ import * as npc from 'dcl-npc-toolkit'
69
130
  ```
70
131
 
71
132
 
72
133
 
134
+
135
+
136
+
137
+
73
138
  5. In your TypeScript file, call the `create` function passing it a `TransformType` and a `NPCData` object. The `NPCData` object requires a minimum of a `NPCType` and a function to trigger when the NPC is activated:
74
139
 
75
140
 
76
141
 
77
- ```ts
142
+
143
+
144
+
78
145
 
146
+ ```ts
79
147
  export let myNPC = npc.create({position: Vector3.create(8,0,8),rotation:Quaternion.Zero(), scale: Vector3.create(1,1,1)},
80
148
 
81
149
  //NPC Data Object
82
-
83
150
  {
84
-
85
151
  type: npc.NPCType.CUSTOM,
86
-
87
152
  model: 'models/npc.glb',
88
-
89
153
  onActivate:()=>{console.log('npc activated');}
90
-
91
154
  }
92
-
93
155
  )
94
-
95
156
  ```
96
157
 
97
158
 
98
159
 
99
- 5. Write a dialog script for your character, preferably on a separate file, making it of type `Dialog[]`.
160
+
100
161
 
101
162
 
102
163
 
103
- ```ts
164
+ 5. Write a dialog script for your character, preferably on a separate file, making it of type `Dialog[]`.
104
165
 
105
- import { Dialog } from 'dcl-npc-toolkit'
166
+
167
+
168
+
106
169
 
107
170
 
108
171
 
172
+ ```ts
173
+ import { Dialog } from 'dcl-npc-toolkit'
109
174
  export let ILoveCats: Dialog[] = [
175
+ {
176
+ text: `I really lo-ove cats`,
177
+ isEndOfDialog: true
178
+ }
179
+ ]
180
+ ```
110
181
 
111
- {
182
+
112
183
 
113
- text: `I really lo-ove cats`,
184
+
114
185
 
115
- isEndOfDialog: true
186
+
116
187
 
117
- }
188
+ ## NPC Default Behavior
118
189
 
119
- ]
190
+
120
191
 
121
- ```
192
+
122
193
 
123
194
 
124
195
 
125
- ## NPC Default Behavior
196
+ NPCs at the very least must have:
126
197
 
127
198
 
128
199
 
129
- NPCs at the very least must have:
200
+
130
201
 
131
202
 
132
203
 
133
204
  - `position`: (_TransformType_) Must include position, rotation and scale.
134
205
 
206
+
207
+
208
+
209
+
135
210
  - `NPCData`: (_Data Object_) with a minimum of two variables
136
211
 
212
+
213
+
214
+
215
+
137
216
  - `type`: (_NPCType_) you have the choice to use a custom GLB object or an `AvatarShape` for your npc
138
217
 
218
+
219
+
220
+
221
+
139
222
  - `NPCType.CUSTOM`
140
223
 
224
+
225
+
226
+
227
+
141
228
  - `NPCType.AVATAR`
142
229
 
230
+
231
+
232
+
233
+
143
234
  - `onActivate()`: (_()=> void_) A function to call when the NPC is activated.
144
235
 
145
236
 
146
237
 
238
+
239
+
240
+
241
+
147
242
  *if you decide to use a `NPCType.CUSTOM` GLB model for your avatar, you must pass in a model object inside the `NPCData`*
148
243
 
244
+
245
+
246
+
247
+
149
248
  - `model`: (_string_) The path to a 3D model
150
249
 
151
250
 
152
251
 
153
- ```ts
252
+
253
+
254
+
154
255
 
256
+ ```ts
155
257
  export let myNPC = npc.create({position: Vector3.create(8,0,8),rotation:Quaternion.Zero(), scale: Vector3.create(1,1,1)},
156
258
 
157
259
  //NPC Data Object
158
-
159
260
  {
160
-
161
261
  type: npc.NPCType.CUSTOM,
162
-
163
262
  model: 'models/npc.glb',
164
-
165
263
  onActivate:()=>{console.log('npc activated');}
166
-
167
264
  }
168
-
169
265
  )
170
-
171
266
  ```
172
267
 
173
268
 
174
269
 
175
- With this default configuration, the NPC behaves in the following way:
270
+
176
271
 
177
272
 
178
273
 
179
- - The `onActivate()` function is called when pressing E on the NPC, and when the player walks near at a distance of 6 meters.
274
+ With this default configuration, the NPC behaves in the following way:
180
275
 
181
- - Once activated, there's a cooldown period of 5 seconds, that prevents the NPC to be activated again.
276
+
182
277
 
183
- - 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.
278
+
184
279
 
185
- - 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.
280
+
186
281
 
187
- - 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.
282
+ - The `onActivate()` function is called when pressing E on the NPC, and when the player walks near at a distance of 6 meters.
188
283
 
189
284
 
190
285
 
191
- Many of these behaviors can be overridden or tweaked with the exposed properties.
192
-
193
286
 
194
287
 
195
- ## NPC Additional Properties
288
+ - Once activated, there's a cooldown period of 5 seconds, that prevents the NPC to be activated again.
196
289
 
197
290
 
198
291
 
199
- To configure other properties of an NPC, add a fourth argument as an `NPCData` object. This object can have the following optional properties:
292
+
293
+
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.
200
295
 
201
296
 
202
297
 
203
- - `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.
298
+
204
299
 
205
- - `faceUser`: _(boolean)_ Set if the NPC rotates to face the user while active.
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.
206
301
 
207
- - `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.
302
+
208
303
 
209
- - `coolDownDuration`: _(number)_ Change the cooldown period for activating the NPC again. The number is in seconds.
304
+
210
305
 
211
- - `hoverText`: _(string)_ Set the UI hover feedback when pointing the cursor at the NPC. _TALK_ by default.
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.
212
307
 
213
- - `onlyClickTrigger`: _(boolean)_ If true, the NPC can't be activated by walking near. Just by clicking on it or calling its `activate()` function.
308
+
214
309
 
215
- - `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.
310
+
216
311
 
217
- - `onlyExternalTrigger`: _(boolean)_ If true, the NPC can't be activated by clicking, pressing E, or walking near. Just by calling its `activate()` function.
312
+
218
313
 
219
- - `reactDistance`: _(number)_ Radius in meters for the player to activate the NPC or trigger the `onWalkAway()` function when leaving the radius.
314
+ Many of these behaviors can be overridden or tweaked with the exposed properties.
220
315
 
221
- - `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.
316
+
222
317
 
223
- - `onWalkAway`: (_()=> void_) Function to call every time the player walks out of the `reactDistance` radius.
318
+ ## SDK7 UI
224
319
 
225
- - `walkingAnim`: _(string)_ Name of the walking animation on the model. This animation is looped when calling the `followPath()` function.
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:
226
321
 
227
- - `walkingSpeed`: _(number)_ Speed of the NPC when walking. By default _2_.
322
+ - create a variable to hold *all* of your 2D UI objects
228
323
 
229
- - `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.
230
- - `bubbleHeight`: _(number)_ The height at which to display the speech bubble above the head of the NPC.
231
- - `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.
324
+ - import the NPC UI from the library and add the React object to your scene UI tree
232
325
 
233
- - `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.
326
+ - create a function to be called once to render all of your 2D UI objects
234
327
 
235
328
 
236
329
 
237
330
  ```ts
331
+ import ReactEcs, { Label, ReactEcsRenderer, UiEntity } from '@dcl/sdk/react-ecs'
332
+ import { NpcUtilsUi } from 'dcl-npc-toolkit'
238
333
 
239
- export let myNPC = npc.create({position: Vector3.create(8,0,8),rotation:Quaternion.Zero(), scale: Vector3.create(1,1,1)},
334
+ const SceneOwnedUi = () =>
240
335
 
241
- //NPC Data Object
336
+ <UiEntity>
337
+ <NpcUtilsUi />
242
338
 
243
- {
339
+ { /* rest of user defined UI */ }
340
+ </UiEntity>
244
341
 
245
- type: npc.NPCType.CUSTOM,
342
+
246
343
 
247
- model: 'models/npc.glb',
344
+ export function setupUi() {
345
+ ReactEcsRenderer.setUiRenderer(SceneOwnedUi)
346
+ }
248
347
 
249
- onActivate: ()=>{console.log('npc activated');},
348
+ ```
250
349
 
251
- onWalkAway: ()=>{console.log('test on walk away function')},
350
+
252
351
 
253
- faceUser: true,
352
+
254
353
 
255
- reactDistance: 3,
354
+ ## NPC Additional Properties
256
355
 
257
- idleAnim: 'idle1',
356
+
258
357
 
259
- walkingAnim: 'walk1',
358
+
260
359
 
261
- hoverText: 'Activate',
360
+
262
361
 
263
- continueOnWalkAway: true,
362
+ To configure other properties of an NPC, add a fourth argument as an `NPCData` object. This object can have the following optional properties:
264
363
 
265
- onlyClickTrigger: false,
364
+
266
365
 
267
- onlyExternalTrigger: false
366
+
268
367
 
269
- }
368
+
270
369
 
271
- )
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.
272
371
 
273
- ```
372
+
274
373
 
275
374
 
276
375
 
277
- ## Get NPC Data
376
+ - `faceUser`: _(boolean)_ Set if the NPC rotates to face the user while active.
278
377
 
279
378
 
280
379
 
281
- ```ts
380
+
282
381
 
283
- npc.getData(myNPC)
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.
284
383
 
285
- ```
384
+
286
385
 
287
386
 
288
387
 
289
- There are several properties you can check on an NPC to know what its current state is:
388
+ - `coolDownDuration`: _(number)_ Change the cooldown period for activating the NPC again. The number is in seconds.
290
389
 
291
390
 
292
391
 
293
- - `.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.
294
-
295
- - `.introduced`: Boolean, false by default. Set to true if the NPC has spoken to the player at least once in this session.
392
+
296
393
 
297
- - `.visible`: Returns a Boolean, false by default. True if the dialog window for this NPC is currently open.
394
+ - `hoverText`: _(string)_ Set the UI hover feedback when pointing the cursor at the NPC. _TALK_ by default.
298
395
 
299
- - `.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.
396
+
300
397
 
301
398
 
302
399
 
303
- > 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.
400
+ - `onlyClickTrigger`: _(boolean)_ If true, the NPC can't be activated by walking near. Just by clicking on it or calling its `activate()` function.
304
401
 
305
402
 
306
403
 
307
- ## NPC Callable Actions
308
-
309
404
 
310
405
 
311
- An NPC object has several callable functions that come with the class:
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.
312
407
 
313
408
 
314
409
 
315
- ### Talk
316
-
317
410
 
318
411
 
319
- To start a conversation with the NPC using the dialog UI, call the `talk()` function. The function takes the following **required** parameter:
412
+ - `onlyExternalTrigger`: _(boolean)_ If true, the NPC can't be activated by clicking, pressing E, or walking near. Just by calling its `activate()` function.
320
413
 
321
414
 
322
415
 
323
- - `script`: _(Dialog[])_ This array contains the information to manage the conversation, including events that may be triggered, options to choose, etc.
324
-
325
416
 
326
417
 
327
- It can also take the following optional parameters:
418
+ - `reactDistance`: _(number)_ Radius in meters for the player to activate the NPC or trigger the `onWalkAway()` function when leaving the radius.
328
419
 
329
420
 
330
421
 
331
- - `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.
422
+
332
423
 
333
- - `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.
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.
334
425
 
335
426
 
336
427
 
337
- ```ts
428
+
338
429
 
339
- npc.talk(myNPC,myScript, 0)
430
+ - `onWalkAway`: (_()=> void_) Function to call every time the player walks out of the `reactDistance` radius.
340
431
 
341
- ```
432
+
342
433
 
343
434
 
344
435
 
345
- Learn how to build a script object for NPCs in a section below.
436
+ - `walkingAnim`: _(string)_ Name of the walking animation on the model. This animation is looped when calling the `followPath()` function.
346
437
 
347
438
 
348
439
 
349
- ### Play Animations
350
-
351
440
 
352
441
 
353
- By default, the NPC will loop an animation named 'Idle', or with a name passed in the `idleAnim` parameter.
442
+ - `walkingSpeed`: _(number)_ Speed of the NPC when walking. By default _2_.
354
443
 
355
444
 
356
445
 
357
- Make the NPC play another animation by calling the `playAnimation()` function. The function takes the following **required** parameter:
446
+
447
+
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.
358
449
 
359
450
 
360
451
 
361
- - `animationName`: _(string)_ The name of the animation to play.
452
+ - `bubbleHeight`: _(number)_ The height at which to display the speech bubble above the head of the NPC.
362
453
 
363
454
 
364
455
 
365
- It can also take the following optional parameters:
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.
366
457
 
367
458
 
368
459
 
369
- - `noLoop`: _(boolean)_ If true, plays the animation just once. Otherwise, the animation is looped.
460
+
370
461
 
371
- - `duration`: _(number)_ Specifies the duration in seconds of the animation. When finished, it returns to playing the idle animation.
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.
372
463
 
373
464
 
374
465
 
375
- > Note: If `noLoop` is true but no `duration` is set, the model will stay still after playing the animation instead of returning to the idle animation.
466
+
376
467
 
377
468
 
378
469
 
379
470
  ```ts
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
+ ```
380
489
 
381
- npc.playAnimation(myNPC, `Head_Yes`, true, 2.63)
490
+
382
491
 
383
- ```
492
+
384
493
 
385
494
 
386
495
 
387
- ### Change idle animation
496
+ ## Get NPC Data
388
497
 
389
498
 
390
499
 
391
- 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.
500
+
392
501
 
393
502
 
394
503
 
395
- 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()`.
504
+ ```ts
505
+ npc.getData(myNPC)
506
+ ```
396
507
 
397
508
 
398
509
 
399
- The `changeIdleAnim()` function takes two arguments:
510
+
400
511
 
401
512
 
402
513
 
403
- - `animation`: The name of the new animation to set as the idle animation
514
+ There are several properties you can check on an NPC to know what its current state is:
404
515
 
405
- - `play`: Optionally pass this value as _true_ if you want this new animation to start playing right away.
516
+
406
517
 
407
518
 
408
519
 
409
- ```ts
520
+
410
521
 
411
- npc.changeIdleAnim(myNPC,`AngryIdle`, true)
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.
412
523
 
413
- ```
524
+
414
525
 
415
526
 
416
527
 
417
- ### Activate
528
+ - `.introduced`: Boolean, false by default. Set to true if the NPC has spoken to the player at least once in this session.
418
529
 
419
530
 
420
531
 
421
- The `activate()` function can be used to trigger the `onActivate()` function, as an alternative to pressing E or walking near.
422
-
423
532
 
424
533
 
425
- ```ts
534
+ - `.visible`: Returns a Boolean, false by default. True if the dialog window for this NPC is currently open.
426
535
 
427
- npc.activate(myNPC)
536
+
428
537
 
429
- ```
538
+
539
+
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.
430
541
 
431
542
 
432
543
 
433
- The `activate()` function is callable even when in cool down period, and it doesn't start a new cool down period.
544
+
434
545
 
435
546
 
436
547
 
437
- ### Stop Walking
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.
438
549
 
439
550
 
440
551
 
441
- If the NPC is currently walking, call `stopWalking()` to stop it moving and return to playing its idle animation.
552
+
442
553
 
443
554
 
444
555
 
445
- ```ts
556
+ ## NPC Callable Actions
446
557
 
447
- npc.stopWalking(myNPC)
558
+
448
559
 
449
- ```
560
+
450
561
 
451
562
 
452
563
 
453
- `stopWalking()` can be called with no parameters, or it can also be called with:
564
+ An NPC object has several callable functions that come with the class:
454
565
 
455
566
 
456
567
 
457
- - `duration`: Seconds to wait before starting to walk again. If not provided, the NPC will stop walking indefinitely.
568
+
458
569
 
459
570
 
460
571
 
461
- > Note: If the NPC is has its dialog window open when the timer for the `duration` ends, the NPC will not return to walking.
572
+ ### Talk
462
573
 
463
574
 
464
575
 
465
- To make the NPC play a different animation from idle when paused, call `playAnimation()` after `stopWalking()`.
576
+
466
577
 
467
578
 
468
579
 
469
- ### Follow Path
580
+ To start a conversation with the NPC using the dialog UI, call the `talk()` function. The function takes the following **required** parameter:
470
581
 
471
582
 
472
583
 
473
- 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.
584
+
474
585
 
475
586
 
476
587
 
477
- `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.
588
+ - `script`: _(Dialog[])_ This array contains the information to manage the conversation, including events that may be triggered, options to choose, etc.
478
589
 
479
590
 
480
591
 
481
- ```ts
592
+
482
593
 
483
- npc.followPath(myNPC)
594
+
484
595
 
485
- ```
596
+ It can also take the following optional parameters:
486
597
 
487
598
 
488
599
 
489
- > Note: If the NPC is initialized with a `path` value, it will start out walking that path in a loop, no need to run `followPath()`.
600
+
490
601
 
491
602
 
492
603
 
493
- `followPath()` has a single optional parameter of type `FollowPathData`. This object may have the following optional fields:
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.
494
605
 
495
606
 
496
607
 
497
- - path: Array of `Vector3` positions to walk over.
608
+
498
609
 
499
- - 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.
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.
500
611
 
501
- - 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_.
612
+
502
613
 
503
- - 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_.
614
+
504
615
 
505
- - 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.
616
+
506
617
 
507
- - startingPoint: Index position for what point to start from on the path. _0_ by default.
618
+ ```ts
619
+ npc.talk(myNPC,myScript, 0)
620
+ ```
508
621
 
509
- - onFinishCallback: Function to call when the NPC finished walking over all the points on the path. This is only called when `loop` is _false_.
622
+
510
623
 
511
- - onReachedPointCallback: Function to call once every time the NPC reaches a point in the path.
624
+
512
625
 
513
626
 
514
627
 
515
- ```ts
628
+ Learn how to build a script object for NPCs in a section below.
516
629
 
517
- export let myNPC = npc.create({position: Vector3.create(8,0,8),rotation:Quaternion.Zero(), scale: Vector3.create(1,1,1)},
630
+
518
631
 
519
- //NPC Data Object
632
+
520
633
 
521
- {
634
+
522
635
 
523
- type: npc.NPCType.CUSTOM,
636
+ ### Play Animations
524
637
 
525
- model: 'models/npc.glb',
638
+
526
639
 
527
- onActivate: ()=>{console.log('npc activated');},
640
+
528
641
 
529
- onWalkAway: ()=>{console.log('test on walk away function')},
642
+
530
643
 
531
- faceUser: true,
644
+ By default, the NPC will loop an animation named 'Idle', or with a name passed in the `idleAnim` parameter.
532
645
 
533
- reactDistance: 3,
646
+
534
647
 
535
- idleAnim: 'idle1',
648
+
536
649
 
537
- walkingAnim: 'walk1',
650
+
538
651
 
539
- hoverText: "Activate"
652
+ Make the NPC play another animation by calling the `playAnimation()` function. The function takes the following **required** parameter:
540
653
 
541
- }
654
+
542
655
 
543
- )
656
+
544
657
 
545
658
 
546
659
 
547
- npc.followPath(myNPC,
660
+ - `animationName`: _(string)_ The name of the animation to play.
548
661
 
549
- {
662
+
550
663
 
551
- path:path,
664
+
552
665
 
553
- loop:true,
666
+
554
667
 
555
- pathType: npc.NPCPathType.RIGID_PATH,
668
+ It can also take the following optional parameters:
556
669
 
557
- onFinishCallback:()=>{console.log('path is done')},
670
+
558
671
 
559
- onReachedPointCallback:()=>{console.log('ending oint')},
672
+
560
673
 
561
- totalDuration: 20
674
+
562
675
 
563
- }
676
+ - `noLoop`: _(boolean)_ If true, plays the animation just once. Otherwise, the animation is looped.
564
677
 
565
- )
678
+
566
679
 
567
680
 
568
681
 
569
- ```
682
+ - `duration`: _(number)_ Specifies the duration in seconds of the animation. When finished, it returns to playing the idle animation.
570
683
 
571
684
 
572
685
 
573
- #### NPC Walking Speed
686
+
574
687
 
575
688
 
576
689
 
577
- The following list of factors are used to determine speed in hierarchical order:
690
+ > Note: If `noLoop` is true but no `duration` is set, the model will stay still after playing the animation instead of returning to the idle animation.
578
691
 
579
692
 
580
693
 
581
- - `totalDuration` parameter set when calling `followPath()` is used over the total distance travelled over the path.
694
+
582
695
 
583
- - `speed` parameter set when calling `followPath()`
696
+
584
697
 
585
- - `walkingSpeed` parameter set when initializing NPC
698
+ ```ts
699
+ npc.playAnimation(myNPC, `Head_Yes`, true, 2.63)
700
+ ```
586
701
 
587
- - Default value _2_.
702
+
588
703
 
589
704
 
590
705
 
591
- #### Joining the path
706
+
707
+
708
+ ### Change idle animation
592
709
 
593
710
 
594
711
 
595
- If the NPC's current position when calling `followPath()` doesn't match the first position in the `path` array (or the one that matches the `startingPoint` value), the current position is added to the `path` array. The NPC will start by walking from its current position to the first point provided in the path.
712
+
596
713
 
597
714
 
598
715
 
599
- The `path` can be a single point, and the NPC will then walk a from its current position to that point.
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.
600
717
 
601
718
 
602
719
 
603
- > 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`.
720
+
604
721
 
605
722
 
606
723
 
607
- 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.
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()`.
608
725
 
609
726
 
610
727
 
611
- ```ts
728
+
612
729
 
613
- export let myNPC = npc.create({position: Vector3.create(10,0,10),rotation:Quaternion.Zero(), scale: Vector3.create(1,1,1)},
730
+
614
731
 
615
- //NPC Data Object
732
+ The `changeIdleAnim()` function takes two arguments:
616
733
 
617
- {
734
+
618
735
 
619
- type: npc.NPCType.CUSTOM,
736
+
620
737
 
621
- model: 'models/npc.glb',
738
+
622
739
 
623
- onActivate: ()=>{console.log('npc activated');},
740
+ - `animation`: The name of the new animation to set as the idle animation
624
741
 
625
- }
742
+
626
743
 
627
- )
744
+
628
745
 
629
- npc.followPath(myNPC,
746
+ - `play`: Optionally pass this value as _true_ if you want this new animation to start playing right away.
630
747
 
631
- {
748
+
632
749
 
633
- path: [new Vector3(2, 0, 2), new Vector3(4, 0, 4), new Vector3(6, 0, 6)]
750
+
634
751
 
635
- })
752
+
636
753
 
754
+ ```ts
755
+ npc.changeIdleAnim(myNPC,`AngryIdle`, true)
637
756
  ```
638
757
 
639
758
 
640
759
 
641
- #### Example Interrupting the NPC
760
+
642
761
 
643
762
 
644
763
 
645
- 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.
764
+ ### Activate
646
765
 
647
766
 
648
767
 
649
- ```ts
768
+
650
769
 
651
- export let myNPC = npc.create({position: Vector3.create(10,0,10),rotation:Quaternion.Zero(), scale: Vector3.create(1,1,1)},
770
+
652
771
 
653
- //NPC Data Object
772
+ The `activate()` function can be used to trigger the `onActivate()` function, as an alternative to pressing E or walking near.
654
773
 
655
- {
774
+
656
775
 
657
- type: npc.NPCType.CUSTOM,
776
+
658
777
 
659
- model: 'models/npc.glb',
778
+
779
+
780
+ ```ts
781
+ npc.activate(myNPC)
782
+ ```
783
+
784
+
660
785
 
661
- onActivate: ()=>{
786
+
662
787
 
663
- npc.stopWalking(myNPC);
788
+
664
789
 
665
- npc.talk(myNPC, lostCat, 0)
790
+ The `activate()` function is callable even when in cool down period, and it doesn't start a new cool down period.
666
791
 
667
- console.log('npc activated');
792
+
668
793
 
669
- },
794
+
670
795
 
671
- walkingAnim: 'walk1',
796
+
797
+
798
+ ### Stop Walking
799
+
800
+
801
+
802
+
803
+
804
+
805
+
806
+ If the NPC is currently walking, call `stopWalking()` to stop it moving and return to playing its idle animation.
807
+
808
+
809
+
810
+
811
+
812
+
813
+
814
+ ```ts
815
+ npc.stopWalking(myNPC)
816
+ ```
817
+
818
+
819
+
820
+
821
+
822
+
823
+
824
+ `stopWalking()` can be called with no parameters, or it can also be called with:
825
+
826
+
827
+
828
+
829
+
830
+
831
+
832
+ - `duration`: Seconds to wait before starting to walk again. If not provided, the NPC will stop walking indefinitely.
833
+
834
+
835
+
836
+
837
+
838
+
839
+
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.
841
+
842
+
843
+
844
+
845
+
846
+
847
+
848
+ To make the NPC play a different animation from idle when paused, call `playAnimation()` after `stopWalking()`.
849
+
850
+
851
+
852
+
853
+
854
+
855
+
856
+ ### Follow Path
857
+
858
+
859
+
860
+
861
+
862
+
863
+
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.
865
+
866
+
867
+
868
+
869
+
870
+
871
+
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.
873
+
874
+
875
+
876
+
877
+
878
+
879
+
880
+ ```ts
881
+ npc.followPath(myNPC)
882
+ ```
883
+
884
+
885
+
886
+
887
+
888
+
889
+
890
+ > Note: If the NPC is initialized with a `path` value, it will start out walking that path in a loop, no need to run `followPath()`.
891
+
892
+
893
+
894
+
895
+
896
+
897
+
898
+ `followPath()` has a single optional parameter of type `FollowPathData`. This object may have the following optional fields:
899
+
900
+
901
+
902
+
903
+
904
+
905
+
906
+ - path: Array of `Vector3` positions to walk over.
907
+
908
+
909
+
910
+
911
+
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.
913
+
914
+
915
+
916
+
672
917
 
673
- faceUser:true
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_.
674
919
 
920
+
921
+
922
+
923
+
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_.
925
+
926
+
927
+
928
+
929
+
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.
931
+
932
+
933
+
934
+
935
+
936
+ - startingPoint: Index position for what point to start from on the path. _0_ by default.
937
+
938
+
939
+
940
+
941
+
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_.
943
+
944
+
945
+
946
+
947
+
948
+ - onReachedPointCallback: Function to call once every time the NPC reaches a point in the path.
949
+
950
+
951
+
952
+
953
+
954
+
955
+
956
+ ```ts
957
+ export let myNPC = npc.create({position: Vector3.create(8,0,8),rotation:Quaternion.Zero(), scale: Vector3.create(1,1,1)},
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"
675
970
  }
971
+ )
676
972
 
973
+ npc.followPath(myNPC,
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
+ }
677
982
  )
983
+ ```
678
984
 
679
985
 
680
986
 
681
- npc.followPath(myNPC,
987
+
682
988
 
683
- {
989
+
684
990
 
685
- path: [new Vector3(4, 0, 30), new Vector3(6, 0, 29), new Vector3(15, 0, 25)],
991
+ #### NPC Walking Speed
686
992
 
687
- loop: true,
993
+
688
994
 
689
- onReachedPointCallback: () => {
995
+
690
996
 
691
- npc.stopWalking(myNPC, 3)
997
+
692
998
 
693
- npc.playAnimation(myNPC, `Cocky`, true, 2.93)
999
+ The following list of factors are used to determine speed in hierarchical order:
694
1000
 
695
- }
1001
+
696
1002
 
697
- })
1003
+
698
1004
 
699
1005
 
700
1006
 
701
- export let lostCat: Dialog[] = [
1007
+ - `totalDuration` parameter set when calling `followPath()` is used over the total distance travelled over the path.
702
1008
 
703
- {
1009
+
1010
+
1011
+
704
1012
 
705
- text: `I lost my cat, I'm going crazy here`
1013
+ - `speed` parameter set when calling `followPath()`
706
1014
 
707
- },
1015
+
708
1016
 
709
- {
1017
+
1018
+
1019
+ - `walkingSpeed` parameter set when initializing NPC
1020
+
1021
+
1022
+
1023
+
710
1024
 
711
- text: `Have you seen it anywhere?`
1025
+ - Default value _2_.
1026
+
1027
+
1028
+
1029
+
1030
+
1031
+
1032
+
1033
+ #### Joining the path
1034
+
1035
+
1036
+
1037
+
1038
+
1039
+
712
1040
 
713
- },
1041
+ If the NPC's current position when calling `followPath()` doesn't match the first position in the `path` array (or the one that matches the `startingPoint` value), the current position is added to the `path` array. The NPC will start by walking from its current position to the first point provided in the path.
1042
+
1043
+
1044
+
1045
+
1046
+
1047
+
1048
+
1049
+ The `path` can be a single point, and the NPC will then walk a from its current position to that point.
1050
+
1051
+
1052
+
1053
+
1054
+
1055
+
714
1056
 
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
+
1059
+
1060
+
1061
+
1062
+
1063
+
1064
+
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.
1066
+
1067
+
1068
+
1069
+
1070
+
1071
+
1072
+
1073
+ ```ts
1074
+ export let myNPC = npc.create({position: Vector3.create(10,0,10),rotation:Quaternion.Zero(), scale: Vector3.create(1,1,1)},
1075
+
1076
+ //NPC Data Object
1077
+ {
1078
+ type: npc.NPCType.CUSTOM,
1079
+ model: 'models/npc.glb',
1080
+ onActivate: ()=>{console.log('npc activated');},
1081
+ }
1082
+ )
1083
+ npc.followPath(myNPC,
715
1084
  {
1085
+ path: [new Vector3(2, 0, 2), new Vector3(4, 0, 4), new Vector3(6, 0, 6)]
1086
+ })
1087
+ ```
716
1088
 
717
- text: `Ok, I'm gonna go back to looking for it`,
1089
+
718
1090
 
719
- triggeredByNext: () => {
1091
+
720
1092
 
721
- npc.followPath(myNPC)
1093
+
722
1094
 
723
- },
1095
+ #### Example Interrupting the NPC
724
1096
 
725
- isEndOfDialog: true
1097
+
1098
+
1099
+
1100
+
1101
+
1102
+
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.
1104
+
1105
+
1106
+
1107
+
1108
+
1109
+
1110
+
1111
+ ```ts
1112
+ export let myNPC = npc.create({position: Vector3.create(10,0,10),rotation:Quaternion.Zero(), scale: Vector3.create(1,1,1)},
726
1113
 
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
727
1125
  }
1126
+ )
728
1127
 
729
- ]
1128
+ npc.followPath(myNPC,
1129
+ {
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
+ }
1136
+ }
1137
+ )
730
1138
 
1139
+ export let lostCat: Dialog[] = [
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
1154
+ }
1155
+ ]
731
1156
  ```
732
1157
 
733
1158
 
734
1159
 
1160
+
1161
+
1162
+
1163
+
735
1164
  ### End interaction
736
1165
 
737
1166
 
738
1167
 
1168
+
1169
+
1170
+
1171
+
739
1172
  The `endInteraction()` function can be used to abruptly end interactions with the NPC.
740
1173
 
741
1174
 
742
1175
 
1176
+
1177
+
1178
+
1179
+
743
1180
  If applicable, it closes the dialog UI, hides speech bubbles, and makes the NPC stop rotating to face the player.
744
1181
 
745
1182
 
746
1183
 
747
- ```ts
1184
+
748
1185
 
749
- npc.endInteraction(myNPC)
1186
+
750
1187
 
1188
+ ```ts
1189
+ npc.endInteraction(myNPC)
751
1190
  ```
752
1191
 
753
1192
 
754
1193
 
1194
+
1195
+
1196
+
1197
+
755
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.
756
1199
 
757
1200
 
758
1201
 
1202
+
1203
+
1204
+
1205
+
759
1206
  ## NPC Dialog Window
760
1207
 
761
1208
 
762
1209
 
1210
+
1211
+
1212
+
1213
+
763
1214
  You can display an interactive dialog window to simulate a conversation with a non-player character (NPC).
764
1215
 
765
1216
 
766
1217
 
767
- 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.
1218
+
768
1219
 
769
1220
 
770
1221
 
771
- <img src="screenshots/NPC1.png" width="500">
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.
772
1223
 
773
1224
 
774
1225
 
775
- ### The NPC script
1226
+
776
1227
 
777
1228
 
778
1229
 
779
- Each entry on the script must include at least a `text` field, but can include several more fields to further customize it.
1230
+ <img src="screenshots/NPC1.png" width="500">
780
1231
 
781
1232
 
782
1233
 
783
- Below is a minimal dialog.
1234
+
784
1235
 
785
1236
 
786
1237
 
787
- ```ts
1238
+ ### The NPC script
788
1239
 
789
- export let NPCTalk: Dialog[] = [
1240
+
790
1241
 
791
- {
1242
+
792
1243
 
793
- text: 'Hi there'
1244
+
794
1245
 
795
- },
1246
+ Each entry on the script must include at least a `text` field, but can include several more fields to further customize it.
796
1247
 
797
- {
1248
+
798
1249
 
799
- text: 'It sure is nice talking to you'
1250
+
800
1251
 
801
- },
1252
+
802
1253
 
803
- {
1254
+ Below is a minimal dialog.
804
1255
 
805
- text: 'I must go, my planet needs me',
1256
+
806
1257
 
807
- isEndOfDialog: true
1258
+
808
1259
 
809
- }
1260
+
810
1261
 
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
+ }
811
1276
  ]
812
1277
 
1278
+
1279
+
1280
+
1281
+
813
1282
  ```
814
1283
 
815
1284
 
816
1285
 
1286
+
1287
+
1288
+
1289
+
817
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`.
818
1291
 
819
1292
 
820
1293
 
1294
+
1295
+
1296
+
1297
+
821
1298
  The script must adhere to the following schema:
822
1299
 
823
1300
 
824
1301
 
825
- ```ts
1302
+
826
1303
 
827
- class Dialog {
1304
+
828
1305
 
1306
+ ```ts
1307
+ class Dialog {
829
1308
  text: string
830
-
831
1309
  fontSize?: number
832
-
833
1310
  typeSpeed?: number
834
-
835
1311
  isEndOfDialog?: boolean
836
-
837
1312
  isQuestion?:boolean
838
-
839
1313
  buttons?: ButtonData[]
840
-
841
1314
  audio?: string
842
-
843
1315
  triggeredByNext?: () => void
844
-
845
1316
  }
846
-
847
1317
  ```
848
1318
 
849
1319
 
850
1320
 
1321
+
1322
+
1323
+
1324
+
851
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.
852
1326
 
853
1327
 
1328
+
1329
+
1330
+
854
1331
 
855
1332
 
856
1333
  You can set the following fields to change the appearance of a dialog:
857
1334
 
858
1335
 
859
1336
 
860
- - `text`: The dialog text
1337
+
1338
+
1339
+
1340
+
1341
+ - `text`: The dialog text
1342
+
1343
+
1344
+
1345
+
1346
+
1347
+ - `fontSize`: Size of the text
1348
+
1349
+
1350
+
1351
+
1352
+
1353
+
1354
+
1355
+ Other fields:
1356
+
1357
+
1358
+
1359
+
1360
+
1361
+ - `buttons *`: An array of buttons to use in a question entry, covered in the next section.
1362
+
1363
+
1364
+
1365
+
1366
+
1367
+ - `audio`: String with the path to an audio file to play once when this dialog is shown on the UI.
1368
+
1369
+
1370
+
1371
+
1372
+
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.
1374
+
1375
+
1376
+
1377
+
1378
+
1379
+
1380
+
1381
+ #### Questions and conversation trees
1382
+
1383
+
1384
+
1385
+
1386
+
1387
+
1388
+
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.
1390
+
1391
+
1392
+
1393
+
1394
+
1395
+
1396
+
1397
+ <img src="screenshots/NPC2.png" width="500">
1398
+
1399
+
1400
+
1401
+
1402
+
1403
+
1404
+
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.
1406
+
1407
+
1408
+
1409
+
1410
+
1411
+
1412
+
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.
1414
+
1415
+
1416
+
1417
+
1418
+
1419
+
1420
+
1421
+ The `buttons` property of an entry contains an array of `ButtonData` objects, each one of these defines one button.
1422
+
1423
+
1424
+
1425
+
1426
+
1427
+
861
1428
 
862
- - `fontSize`: Size of the text
1429
+ When on a question entry, you must provide at least the following for each button:
863
1430
 
864
1431
 
865
1432
 
866
- Other fields:
1433
+
867
1434
 
868
- - `buttons *`: An array of buttons to use in a question entry, covered in the next section.
1435
+
869
1436
 
870
- - `audio`: String with the path to an audio file to play once when this dialog is shown on the UI.
1437
+ - `label`: _(string)_ The label to show on the button.
871
1438
 
872
- - `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.
1439
+
873
1440
 
874
1441
 
875
1442
 
876
- #### Questions and conversation trees
1443
+ - `goToDialog`: _(number | string)_ The index or name of the next dialog entry to display when activated.
877
1444
 
878
1445
 
879
1446
 
880
- 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.
1447
+
881
1448
 
882
1449
 
883
1450
 
884
- <img src="screenshots/NPC2.png" width="500">
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.
885
1452
 
886
1453
 
887
1454
 
888
- > 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.
1455
+
889
1456
 
890
1457
 
891
1458
 
892
- 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.
1459
+ You can also set the following:
893
1460
 
894
1461
 
895
1462
 
896
- The `buttons` property of an entry contains an array of `ButtonData` objects, each one of these defines one button.
1463
+
897
1464
 
898
1465
 
899
1466
 
900
- When on a question entry, you must provide at least the following for each button:
1467
+ - `triggeredActions`: _( () => void )_ An additional function to run whenever the button is activated
901
1468
 
902
1469
 
903
1470
 
904
- - `label`: _(string)_ The label to show on the button.
1471
+
905
1472
 
906
- - `goToDialog`: _(number | string)_ The index or name of the next dialog entry to display when activated.
1473
+ - `fontSize`: _(number)_ Font size of the text
907
1474
 
908
1475
 
909
1476
 
910
- > 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.
911
-
912
1477
 
913
1478
 
914
- You can also set the following:
1479
+ - `offsetX`: _(number)_ Offset of the label on the X axis, relative to its normal position.
915
1480
 
916
1481
 
917
1482
 
918
- - `triggeredActions`: _( () => void )_ An additional function to run whenever the button is activated
1483
+
919
1484
 
920
- - `fontSize`: _(number)_ Font size of the text
1485
+ - `offsetY`: _(number)_ Offset of the label on the Y axis, relative to its normal position.
921
1486
 
922
- - `offsetX`: _(number)_ Offset of the label on the X axis, relative to its normal position.
1487
+
923
1488
 
924
- - `offsetY`: _(number)_ Offset of the label on the Y axis, relative to its normal position.
1489
+
925
1490
 
926
1491
 
927
1492
 
@@ -929,307 +1494,406 @@ All buttons can be clicked to activate them. Additionally, the first button in t
929
1494
 
930
1495
 
931
1496
 
932
- <img src="screenshots/NPC3.png" width="500">
1497
+
933
1498
 
934
1499
 
935
1500
 
936
- ```ts
1501
+ <img src="screenshots/NPC3.png" width="500">
937
1502
 
938
- export let GemsMission: Dialog[] = [
1503
+
939
1504
 
940
- {
1505
+
941
1506
 
942
- text: `Hello stranger`
1507
+
943
1508
 
944
- },
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
+ ```
945
1539
 
946
- {
1540
+
947
1541
 
948
- text: `Can you help me finding my missing gems?`,
1542
+
949
1543
 
950
- isQuestion: true,
1544
+
951
1545
 
952
- buttons: [
1546
+ #### Triggering functions from the dialog
953
1547
 
954
- { label: `Yes!`, goToDialog: 2 },
1548
+
955
1549
 
956
- { label: `I'm busy`, goToDialog: 4 }
1550
+
957
1551
 
958
- ]
1552
+
959
1553
 
960
- },
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.
961
1555
 
962
- {
1556
+
963
1557
 
964
- text: `Ok, awesome, thanks!`
1558
+
965
1559
 
966
- },
1560
+
967
1561
 
968
- {
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.
969
1563
 
970
- text: `I need you to find 10 gems scattered around this scene, go find them!`,
1564
+
971
1565
 
972
- isEndOfDialog: true
1566
+
973
1567
 
974
- },
1568
+
975
1569
 
976
- {
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.
977
1571
 
978
- text: `Ok, come back soon`,
1572
+
979
1573
 
980
- isEndOfDialog: true
1574
+
981
1575
 
982
- }
1576
+
983
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
+ }
984
1624
  ]
985
-
986
1625
  ```
987
1626
 
988
1627
 
989
1628
 
990
- #### Triggering functions from the dialog
991
-
992
1629
 
993
1630
 
994
- 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.
1631
+ ## No-NPC Dialogs
995
1632
 
996
1633
 
997
1634
 
998
- - `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.
999
-
1000
1635
 
1001
1636
 
1002
- - `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.
1003
-
1004
1637
 
1005
1638
 
1006
- ```ts
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.
1007
1640
 
1008
- export let GemsMission: Dialog[] = [
1641
+
1009
1642
 
1010
- {
1643
+
1011
1644
 
1012
- text: `Hello stranger`,
1645
+
1013
1646
 
1014
- triggeredByNext: () => {
1647
+ ### The Dialog window
1015
1648
 
1016
- // NPC plays animation to show a gem
1649
+
1017
1650
 
1018
- }
1651
+
1019
1652
 
1020
- },
1653
+
1021
1654
 
1022
- {
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.
1023
1656
 
1024
- text: `Can you help me finding my missing gems?`,
1657
+
1025
1658
 
1026
- isQuestion: true,
1659
+
1027
1660
 
1028
- buttons: [
1661
+ ```ts
1662
+ let dialogWindow = npc.createDialogWindow()
1663
+ ```
1029
1664
 
1030
- {
1665
+
1031
1666
 
1032
- label: `Yes!`,
1667
+
1033
1668
 
1034
- goToDialog: 2,
1669
+ <img src="screenshots/NPC1.png" width="500">
1035
1670
 
1036
- triggeredActions: () => {
1671
+
1037
1672
 
1038
- // NPC plays an animation to celebrate
1673
+
1039
1674
 
1040
- }
1675
+ When instantiating a new blank dialog, you can pass the following optional parameters:
1041
1676
 
1042
- },
1677
+
1043
1678
 
1044
- {
1679
+
1045
1680
 
1046
- label: `I'm busy`,
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`.
1047
1682
 
1048
- goToDialog: 4
1683
+
1049
1684
 
1050
- triggeredActions: () => {
1685
+
1051
1686
 
1052
- // NPC waves goodbye
1687
+ - `useDarkTheme`: Switch the style of the window to the dark theme.
1053
1688
 
1054
- }
1689
+
1055
1690
 
1056
- },
1691
+
1057
1692
 
1058
- ]
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.
1059
1694
 
1060
- },
1695
+
1061
1696
 
1062
- {
1697
+
1063
1698
 
1064
- text: `Ok, awesome, thanks!`,
1699
+
1065
1700
 
1066
- },
1701
+ Once you have created a dialog window, you can open a dialog window with the `openDialogWindow()` function.
1067
1702
 
1068
- {
1703
+
1069
1704
 
1070
- text: `I need you to find 10 gems scattered around this scene, go find them!`,
1705
+
1071
1706
 
1072
- isEndOfDialog: true
1707
+
1073
1708
 
1074
- triggeredByNext: () => {
1709
+ ```ts
1710
+ npc.openDialogWindow(dialogWindow, NPCTalk, 0)
1711
+ ```
1075
1712
 
1076
- // Gems are rendered all around the scene
1713
+
1077
1714
 
1078
- }
1715
+
1079
1716
 
1080
- },
1717
+
1081
1718
 
1082
- {
1719
+ When calling this function, you must specify:
1083
1720
 
1084
- text: `Ok, come back soon`,
1721
+
1085
1722
 
1086
- isEndOfDialog: true
1723
+
1087
1724
 
1088
- }
1725
+
1089
1726
 
1090
- ]
1727
+ - `NPCScript`: A JSON object composed of an array of `Dialog` objects, that includes all the dialog tree.
1091
1728
 
1092
- ```
1729
+
1093
1730
 
1094
1731
 
1095
- ## No-NPC Dialogs
1096
1732
 
1097
1733
 
1098
1734
 
1099
- 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.
1735
+ A second optional parameter is also available:
1100
1736
 
1101
1737
 
1102
1738
 
1103
- ### The Dialog window
1104
-
1105
1739
 
1106
1740
 
1107
- 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.
1741
+
1108
1742
 
1109
- ```ts
1743
+ - `textId`: The index or `name` property of the entry to show first from the script. The first entry is 0.
1110
1744
 
1111
- let dialogWindow = npc.createDialogWindow()
1745
+
1112
1746
 
1113
- ```
1747
+
1114
1748
 
1115
-
1116
- <img src="screenshots/NPC1.png" width="500">
1749
+
1117
1750
 
1118
-
1119
- When instantiating a new blank dialog, you can pass the following optional parameters:
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.
1120
1752
 
1753
+
1121
1754
 
1122
- - `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`.
1755
+
1123
1756
 
1124
- - `useDarkTheme`: Switch the style of the window to the dark theme.
1757
+
1125
1758
 
1126
- - `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.
1759
+ Close a dialog window at any time by calling the `closeDialogWindow()` function.
1127
1760
 
1128
1761
 
1129
1762
 
1130
- Once you have created a dialog window, you can open a dialog window with the `openDialogWindow()` function.
1763
+
1131
1764
 
1132
1765
 
1133
1766
 
1134
1767
  ```ts
1135
-
1136
- npc.openDialogWindow(dialogWindow, NPCTalk, 0)
1137
-
1768
+ npc.closeDialogWindow(dialogWindow)
1138
1769
  ```
1139
1770
 
1140
1771
 
1141
1772
 
1142
- When calling this function, you must specify:
1773
+
1143
1774
 
1144
1775
 
1145
1776
 
1146
- - `NPCScript`: A JSON object composed of an array of `Dialog` objects, that includes all the dialog tree.
1777
+ For details on how to construct the dialog tree, see the sections above. The required `NPCScript` by the `DialogWindow` has exactly the same characteristics as the one used on the `NPC` object when calling the `talk()` function.
1147
1778
 
1148
1779
 
1149
1780
 
1150
- A second optional parameter is also available:
1781
+
1151
1782
 
1152
1783
 
1153
1784
 
1154
- - `textId`: The index or `name` property of the entry to show first from the script. The first entry is 0.
1785
+ ---
1155
1786
 
1156
1787
 
1157
1788
 
1158
- > 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.
1159
-
1160
1789
 
1161
1790
 
1162
- Close a dialog window at any time by calling the `closeDialogWindow()` function.
1791
+ ## Contribute
1163
1792
 
1164
1793
 
1165
1794
 
1166
- ```ts
1795
+
1167
1796
 
1168
- npc.closeDialogWindow(dialogWindow)
1797
+
1169
1798
 
1170
- ```
1799
+ In order to test changes made to this repository in active scenes, do the following:
1171
1800
 
1172
1801
 
1173
1802
 
1174
- For details on how to construct the dialog tree, see the sections above. The required `NPCScript` by the `DialogWindow` has exactly the same characteristics as the one used on the `NPC` object when calling the `talk()` function.
1803
+
1175
1804
 
1176
1805
 
1177
1806
 
1178
- ---
1807
+ 1. Run `npm run build` for the internal files of the library to be generated
1179
1808
 
1180
- ## Contribute
1809
+
1181
1810
 
1182
1811
 
1183
1812
 
1184
- In order to test changes made to this repository in active scenes, do the following:
1813
+ 2. Run `npm run link` on this repository
1185
1814
 
1186
1815
 
1187
1816
 
1188
- 1. Run `npm run build` for the internal files of the library to be generated
1189
-
1190
- 2. Run `npm run link` on this repository
1817
+
1191
1818
 
1192
1819
  3. On a new Decentraland scene, import this library as you normally would and include the tests you need
1193
1820
 
1821
+
1822
+
1823
+
1824
+
1194
1825
  4. On the scene directory, run `npm link dcl-npc-toolkit`
1195
1826
 
1196
1827
 
1197
1828
 
1829
+
1830
+
1831
+
1832
+
1198
1833
  > Note: When done testing, run `npm unlink` on both folders, so that the scene stops using the local version of the library.
1199
1834
 
1200
1835
 
1836
+
1837
+
1838
+
1201
1839
 
1202
1840
 
1203
1841
  ## CI/CD
1204
1842
 
1205
1843
 
1206
1844
 
1845
+
1846
+
1847
+
1848
+
1207
1849
  This repository uses `semantic-release` to automatically release new versions of the package to NPM.
1208
1850
 
1209
1851
 
1210
1852
 
1853
+
1854
+
1855
+
1856
+
1211
1857
  Use the following convention for commit names:
1212
1858
 
1213
1859
 
1214
1860
 
1861
+
1862
+
1863
+
1864
+
1215
1865
  `feat: something`: Minor release, every time you add a feature or enhancement that doesn’t break the api.
1216
1866
 
1217
1867
 
1218
1868
 
1869
+
1870
+
1871
+
1872
+
1219
1873
  `fix: something`: Bug fixing / patch
1220
1874
 
1221
1875
 
1222
1876
 
1877
+
1878
+
1879
+
1880
+
1223
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`.
1224
1882
 
1225
1883
 
1226
1884
 
1885
+
1886
+
1887
+
1888
+
1227
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:
1228
1890
 
1229
1891
 
1230
1892
 
1231
- ```
1893
+
1232
1894
 
1233
- commit -m "feat: changed the signature of a method" -m "BREAKING CHANGE: this commit breaks the API, changing foo(arg1) to foo(arg1, arg2)"
1895
+
1234
1896
 
1897
+ ```
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)"
1235
1899
  ```