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