@m2c2kit/cli 0.3.6 → 0.3.7
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/dist/index.d.ts +22 -0
- package/dist/index.js +446 -0
- package/package.json +14 -26
- package/dist/.env +0 -1
- package/dist/assets/cli-starter/fonts/roboto/LICENSE.txt +0 -202
- package/dist/assets/cli-starter/fonts/roboto/Roboto-Regular.ttf +0 -0
- package/dist/assets/cli-starter/images/assessmentExample.png +0 -0
- package/dist/assets/css/bootstrap-datepicker.standalone.css +0 -539
- package/dist/assets/css/bootstrap-slider.css +0 -328
- package/dist/assets/css/defaultV2.css +0 -3655
- package/dist/assets/css/m2c2kit.css +0 -72
- package/dist/assets/css/modern.css +0 -2564
- package/dist/assets/css/nouislider.css +0 -309
- package/dist/assets/css/select2.css +0 -712
- package/dist/assets/css/survey.css +0 -1006
- package/dist/cli.js +0 -430
- package/dist/isReservedWord.js +0 -98
- package/dist/templates/README.md.handlebars +0 -17
- package/dist/templates/index.html.handlebars +0 -29
- package/dist/templates/index.ts.handlebars +0 -586
- package/dist/templates/launch.json.handlebars +0 -15
- package/dist/templates/m2c2kit.json.handlebars +0 -5
- package/dist/templates/package.json.handlebars +0 -26
- package/dist/templates/rollup.config.mjs.handlebars +0 -73
- package/dist/templates/tsconfig.json.handlebars +0 -16
|
@@ -1,586 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Game,
|
|
3
|
-
Action,
|
|
4
|
-
Scene,
|
|
5
|
-
Label,
|
|
6
|
-
WebColors,
|
|
7
|
-
RandomDraws,
|
|
8
|
-
GameParameters,
|
|
9
|
-
GameOptions,
|
|
10
|
-
TrialSchema,
|
|
11
|
-
Timer,
|
|
12
|
-
Session,
|
|
13
|
-
SessionLifecycleEvent,
|
|
14
|
-
ActivityResultsEvent,
|
|
15
|
-
ActivityLifecycleEvent,
|
|
16
|
-
ActivityType,
|
|
17
|
-
EventType,
|
|
18
|
-
RgbaColor,
|
|
19
|
-
} from "@m2c2kit/core";
|
|
20
|
-
import { Button, Instructions } from "@m2c2kit/addons";
|
|
21
|
-
|
|
22
|
-
class {{className}} extends Game {
|
|
23
|
-
constructor() {
|
|
24
|
-
/**
|
|
25
|
-
* These are configurable game parameters and their defaults.
|
|
26
|
-
* Each game parameter should have a type, default (this is the default
|
|
27
|
-
* value), and a description.
|
|
28
|
-
*/
|
|
29
|
-
const defaultParameters: GameParameters = {
|
|
30
|
-
preparation_duration_ms: {
|
|
31
|
-
type: "number",
|
|
32
|
-
default: 500,
|
|
33
|
-
description: "How long the 'get ready' message is shown, milliseconds.",
|
|
34
|
-
},
|
|
35
|
-
number_of_trials: {
|
|
36
|
-
type: "integer",
|
|
37
|
-
default: 5,
|
|
38
|
-
description: "How many trials to run.",
|
|
39
|
-
},
|
|
40
|
-
show_fps: {
|
|
41
|
-
type: "boolean",
|
|
42
|
-
default: false,
|
|
43
|
-
description: "Should the FPS be shown?",
|
|
44
|
-
},
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* This describes all the data that will be generated by the assessment
|
|
49
|
-
* in a single trial. Each variable should have a type and description.
|
|
50
|
-
* If a variable might be null, the type can be an array:
|
|
51
|
-
* type: ["string", "null"]
|
|
52
|
-
* Object and array types are also allowed, but this example uses only
|
|
53
|
-
* simple types.
|
|
54
|
-
*
|
|
55
|
-
* More advanced schema parameters such as format or enum are optional.
|
|
56
|
-
*
|
|
57
|
-
* At runtime, when a trial completes, the data will be returned to the
|
|
58
|
-
* session with a callback, along with this schema transformed into
|
|
59
|
-
* JSON Schema.
|
|
60
|
-
*/
|
|
61
|
-
const trialSchema: TrialSchema = {
|
|
62
|
-
activity_uuid: {
|
|
63
|
-
type: "string",
|
|
64
|
-
format: "uuid",
|
|
65
|
-
description: "Unique identifier for all trials in this activity.",
|
|
66
|
-
},
|
|
67
|
-
trial_index: {
|
|
68
|
-
type: ["integer", "null"],
|
|
69
|
-
description: "Index of the trial within this assessment, 0-based.",
|
|
70
|
-
},
|
|
71
|
-
presented_word_text: {
|
|
72
|
-
type: "string",
|
|
73
|
-
description: "The text that was presented.",
|
|
74
|
-
},
|
|
75
|
-
presented_word_color: {
|
|
76
|
-
type: "string",
|
|
77
|
-
description: "The color of the text that was presented.",
|
|
78
|
-
},
|
|
79
|
-
selected_text: {
|
|
80
|
-
type: "string",
|
|
81
|
-
description: "The text that was selected by the user.",
|
|
82
|
-
},
|
|
83
|
-
selection_correct: {
|
|
84
|
-
type: "boolean",
|
|
85
|
-
description: "Was the text selected correctly?",
|
|
86
|
-
},
|
|
87
|
-
response_time_ms: {
|
|
88
|
-
type: "number",
|
|
89
|
-
description:
|
|
90
|
-
"How long, in milliseconds, from when the word was presented until the user made a selection.",
|
|
91
|
-
},
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
const options: GameOptions = {
|
|
95
|
-
name: "{{appName}}",
|
|
96
|
-
id: "{{appName}}",
|
|
97
|
-
version: "1.0.0",
|
|
98
|
-
shortDescription:
|
|
99
|
-
"A starter assessment created by the m2c2kit cli demonstrating the Stroop effect.",
|
|
100
|
-
longDescription: `In psychology, the Stroop effect is the delay in \
|
|
101
|
-
reaction time between congruent and incongruent stimuli. The effect has \
|
|
102
|
-
been used to create a psychological test (the Stroop test) that is widely \
|
|
103
|
-
used in clinical practice and investigation. A basic task that demonstrates \
|
|
104
|
-
this effect occurs when there is a mismatch between the name of a color \
|
|
105
|
-
(e.g., "blue", "green", or "red") and the color it is printed on (i.e., the \
|
|
106
|
-
word "red" printed in blue ink instead of red ink). When asked to name the \
|
|
107
|
-
color of the word it takes longer and is more prone to errors when the color \
|
|
108
|
-
of the ink does not match the name of the color. The effect is named after \
|
|
109
|
-
John Ridley Stroop, who first published the effect in English in 1935. The \
|
|
110
|
-
effect had previously been published in Germany in 1929 by other authors. \
|
|
111
|
-
The original paper by Stroop has been one of the most cited papers in the \
|
|
112
|
-
history of experimental psychology, leading to more than 700 Stroop-related \
|
|
113
|
-
articles in literature. Source: https://en.wikipedia.org/wiki/Stroop_effect`,
|
|
114
|
-
uri: "An external link to your assessment repository or informational website.",
|
|
115
|
-
showFps: defaultParameters.show_fps.default,
|
|
116
|
-
/**
|
|
117
|
-
* Actual pixel resolution will be scaled to fit the device, while
|
|
118
|
-
* preserving the aspect ratio. It is important, however, to specify
|
|
119
|
-
* a width and height to obtain the desired aspect ratio. In most
|
|
120
|
-
* cases, you should not change this. 1:2 is a good aspect ratio
|
|
121
|
-
* for modern phones.
|
|
122
|
-
*/
|
|
123
|
-
width: 400,
|
|
124
|
-
height: 800,
|
|
125
|
-
trialSchema: trialSchema,
|
|
126
|
-
parameters: defaultParameters,
|
|
127
|
-
/**
|
|
128
|
-
* IMPORTANT: fonts is an array of FontAsset objects. The url for each
|
|
129
|
-
* fontAsset must be a string literal. If you use anything else, the
|
|
130
|
-
* cache busting functionality will not work when building for
|
|
131
|
-
* production.
|
|
132
|
-
* The following are examples of what should NOT be used, even though
|
|
133
|
-
* they are syntactically correct:
|
|
134
|
-
*
|
|
135
|
-
* url: "fonts/" + "roboto/Roboto-Regular.ttf"
|
|
136
|
-
*
|
|
137
|
-
* const prefix = "fonts/";
|
|
138
|
-
* ...
|
|
139
|
-
* url: prefix + "roboto/Roboto-Regular.ttf"
|
|
140
|
-
*/
|
|
141
|
-
/**
|
|
142
|
-
* The Roboto-Regular.ttf font is licensed under the Apache License,
|
|
143
|
-
* and its LICENSE.TXT will be copied as part of the build.
|
|
144
|
-
*/
|
|
145
|
-
fonts: [
|
|
146
|
-
{
|
|
147
|
-
fontName: "roboto",
|
|
148
|
-
url: "fonts/roboto/Roboto-Regular.ttf",
|
|
149
|
-
},
|
|
150
|
-
],
|
|
151
|
-
/**
|
|
152
|
-
* IMPORTANT: Similar to FontAsset.url, the url for an image must be
|
|
153
|
-
* a string literal.
|
|
154
|
-
*/
|
|
155
|
-
images: [
|
|
156
|
-
{
|
|
157
|
-
imageName: "assessmentImage",
|
|
158
|
-
/**
|
|
159
|
-
* The image will be resized to the height and width specified.
|
|
160
|
-
*/
|
|
161
|
-
height: 441,
|
|
162
|
-
width: 255,
|
|
163
|
-
/**
|
|
164
|
-
* The image url must match the location of the image under the
|
|
165
|
-
* src folder.
|
|
166
|
-
*/
|
|
167
|
-
url: "images/assessmentExample.png",
|
|
168
|
-
},
|
|
169
|
-
],
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
super(options);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
override async init() {
|
|
176
|
-
await super.init();
|
|
177
|
-
/**
|
|
178
|
-
* Just for convenience, alias the variable game to "this"
|
|
179
|
-
* (even though eslint doesn't like it)
|
|
180
|
-
*/
|
|
181
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
182
|
-
const game = this;
|
|
183
|
-
|
|
184
|
-
// ==============================================================
|
|
185
|
-
/**
|
|
186
|
-
* Create the trial configuration of all trials.
|
|
187
|
-
* It is often necessary to create the full trial configuration before
|
|
188
|
-
* starting any trials. For example, in the stroop task, one could
|
|
189
|
-
* add game parameters to specify a certain percent of the correct
|
|
190
|
-
* responses are the left versus right buttons. Or, one might want an
|
|
191
|
-
* equal number of trials for each font color.
|
|
192
|
-
*/
|
|
193
|
-
|
|
194
|
-
interface StroopColor {
|
|
195
|
-
name: string;
|
|
196
|
-
rgba: RgbaColor;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* These are the colors that will be used in the game.
|
|
201
|
-
*/
|
|
202
|
-
const stroopColors: StroopColor[] = [
|
|
203
|
-
{ name: "Red", rgba: [255, 0, 0, 1] },
|
|
204
|
-
{ name: "Green", rgba: [0, 255, 0, 1] },
|
|
205
|
-
{ name: "Blue", rgba: [0, 0, 255, 1] },
|
|
206
|
-
{ name: "Orange", rgba: [255, 165, 0, 1] },
|
|
207
|
-
];
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* This completely describes the configuration of single trial.
|
|
211
|
-
*/
|
|
212
|
-
interface TrialConfiguration {
|
|
213
|
-
presented_text: string;
|
|
214
|
-
presented_color: StroopColor;
|
|
215
|
-
selection_options_text: string[];
|
|
216
|
-
correct_option_index: number;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const trialConfigurations: TrialConfiguration[] = [];
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Note: TypeScript will try to infer the type of the game parameter that
|
|
223
|
-
* you request in game.getParameter(). If the type cannot be inferred, you
|
|
224
|
-
* will get a compiler error, and you must specify the type, as in the
|
|
225
|
-
* next statement.
|
|
226
|
-
*/
|
|
227
|
-
for (let i = 0; i < game.getParameter<number>("number_of_trials"); i++) {
|
|
228
|
-
const presentedTextIndex = RandomDraws.SingleFromRange(
|
|
229
|
-
0,
|
|
230
|
-
stroopColors.length - 1
|
|
231
|
-
);
|
|
232
|
-
const presentedColorIndex = RandomDraws.SingleFromRange(
|
|
233
|
-
0,
|
|
234
|
-
stroopColors.length - 1
|
|
235
|
-
);
|
|
236
|
-
|
|
237
|
-
const selection_options_text = new Array<string>();
|
|
238
|
-
const firstIsCorrect = RandomDraws.SingleFromRange(0, 1);
|
|
239
|
-
let correctOptionIndex;
|
|
240
|
-
|
|
241
|
-
if (firstIsCorrect === 1) {
|
|
242
|
-
correctOptionIndex = 0;
|
|
243
|
-
selection_options_text.push(stroopColors[presentedColorIndex].name);
|
|
244
|
-
const remainingColors = stroopColors.filter(
|
|
245
|
-
(c) => c.name !== stroopColors[presentedColorIndex].name
|
|
246
|
-
);
|
|
247
|
-
const secondOptionIndex = RandomDraws.SingleFromRange(
|
|
248
|
-
0,
|
|
249
|
-
remainingColors.length - 1
|
|
250
|
-
);
|
|
251
|
-
selection_options_text.push(remainingColors[secondOptionIndex].name);
|
|
252
|
-
} else {
|
|
253
|
-
correctOptionIndex = 1;
|
|
254
|
-
const remainingColors = stroopColors.filter(
|
|
255
|
-
(c) => c.name !== stroopColors[presentedColorIndex].name
|
|
256
|
-
);
|
|
257
|
-
const secondOptionIndex = RandomDraws.SingleFromRange(
|
|
258
|
-
0,
|
|
259
|
-
remainingColors.length - 1
|
|
260
|
-
);
|
|
261
|
-
selection_options_text.push(remainingColors[secondOptionIndex].name);
|
|
262
|
-
selection_options_text.push(stroopColors[presentedColorIndex].name);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
trialConfigurations.push({
|
|
266
|
-
presented_text: stroopColors[presentedTextIndex].name,
|
|
267
|
-
presented_color: stroopColors[presentedColorIndex],
|
|
268
|
-
selection_options_text: selection_options_text,
|
|
269
|
-
correct_option_index: correctOptionIndex,
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// ==============================================================
|
|
274
|
-
// SCENES: instructions
|
|
275
|
-
const instructionsScenes = Instructions.Create({
|
|
276
|
-
instructionScenes: [
|
|
277
|
-
{
|
|
278
|
-
title: "{{appName}}",
|
|
279
|
-
text: `Select the color that matches the font color. This is commonly known as the Stroop task.`,
|
|
280
|
-
textFontSize: 20,
|
|
281
|
-
titleFontSize: 30,
|
|
282
|
-
},
|
|
283
|
-
{
|
|
284
|
-
title: "{{appName}}",
|
|
285
|
-
text: `For example, the word Blue is colored Orange, so the correct response is Orange.`,
|
|
286
|
-
textFontSize: 20,
|
|
287
|
-
titleFontSize: 30,
|
|
288
|
-
textVerticalBias: 0.15,
|
|
289
|
-
imageName: "assessmentImage",
|
|
290
|
-
imageAboveText: false,
|
|
291
|
-
imageMarginTop: 20,
|
|
292
|
-
/**
|
|
293
|
-
* We override the next button's default text and color
|
|
294
|
-
*/
|
|
295
|
-
nextButtonText: "START",
|
|
296
|
-
nextButtonBackgroundColor: WebColors.Green,
|
|
297
|
-
},
|
|
298
|
-
],
|
|
299
|
-
});
|
|
300
|
-
game.addScenes(instructionsScenes);
|
|
301
|
-
|
|
302
|
-
// ==============================================================
|
|
303
|
-
// SCENE: preparation. Show get ready message, then advance after
|
|
304
|
-
// preparation_duration_ms milliseconds
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* For entities that are persistent across trials, such as the
|
|
308
|
-
* scenes themsevles and labels that are always displayed, we create
|
|
309
|
-
* them here.
|
|
310
|
-
*/
|
|
311
|
-
const preparationScene = new Scene();
|
|
312
|
-
game.addScene(preparationScene);
|
|
313
|
-
|
|
314
|
-
const getReadyMessage = new Label({
|
|
315
|
-
text: "Get Ready",
|
|
316
|
-
fontSize: 24,
|
|
317
|
-
position: { x: 200, y: 400 },
|
|
318
|
-
});
|
|
319
|
-
preparationScene.addChild(getReadyMessage);
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* For entities that are displayed or actions that are run only when a
|
|
323
|
-
* scene has been presented, we do them within the scene's onAppear()
|
|
324
|
-
* or onSetup() callbacks. When a scene is presented, the order of
|
|
325
|
-
* execution is:
|
|
326
|
-
* OnSetup() -> transitions -> OnAppear()
|
|
327
|
-
* If there are no transitions, such as a scene sliding in, then
|
|
328
|
-
* it makes no difference if you put code in OnSetup() or OnAppear().
|
|
329
|
-
*/
|
|
330
|
-
preparationScene.onAppear(() => {
|
|
331
|
-
preparationScene.run(
|
|
332
|
-
Action.sequence([
|
|
333
|
-
Action.wait({
|
|
334
|
-
duration: game.getParameter("preparation_duration_ms"),
|
|
335
|
-
}),
|
|
336
|
-
// Custom actions are used execute code.
|
|
337
|
-
Action.custom({
|
|
338
|
-
callback: () => {
|
|
339
|
-
game.presentScene(presentationScene);
|
|
340
|
-
},
|
|
341
|
-
}),
|
|
342
|
-
])
|
|
343
|
-
);
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
// ==============================================================
|
|
347
|
-
// SCENE: Present the word and get user selection
|
|
348
|
-
const presentationScene = new Scene();
|
|
349
|
-
game.addScene(presentationScene);
|
|
350
|
-
|
|
351
|
-
/**
|
|
352
|
-
* The "What colors is the font?" label will always be displayed in
|
|
353
|
-
* the presentation scene, so we create it here and add it to the scene.
|
|
354
|
-
*/
|
|
355
|
-
const whatColorIsFont = new Label({
|
|
356
|
-
text: "What color is the font?",
|
|
357
|
-
fontSize: 24,
|
|
358
|
-
position: { x: 200, y: 100 },
|
|
359
|
-
});
|
|
360
|
-
presentationScene.addChild(whatColorIsFont);
|
|
361
|
-
|
|
362
|
-
presentationScene.onAppear(() => {
|
|
363
|
-
Timer.start("responseTime");
|
|
364
|
-
const trialConfiguration = trialConfigurations[game.trialIndex];
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* The presented word will vary across trials. Thus, we create the
|
|
368
|
-
* presented word label here within the scene's onAppear() callback.
|
|
369
|
-
*/
|
|
370
|
-
const presentedWord = new Label({
|
|
371
|
-
text: trialConfiguration.presented_text,
|
|
372
|
-
position: { x: 200, y: 400 },
|
|
373
|
-
fontSize: 48,
|
|
374
|
-
fontColor: trialConfiguration.presented_color.rgba,
|
|
375
|
-
});
|
|
376
|
-
presentationScene.addChild(presentedWord);
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Similarly, we create the buttons within the scene's onAppear()
|
|
380
|
-
* callback because the buttons are different across trials.
|
|
381
|
-
*/
|
|
382
|
-
const button0 = new Button({
|
|
383
|
-
text: trialConfiguration.selection_options_text[0],
|
|
384
|
-
size: { width: 150, height: 50 },
|
|
385
|
-
position: { x: 100, y: 700 },
|
|
386
|
-
isUserInteractionEnabled: true,
|
|
387
|
-
});
|
|
388
|
-
button0.onTapDown(() => {
|
|
389
|
-
handleUserSelection(0);
|
|
390
|
-
});
|
|
391
|
-
presentationScene.addChild(button0);
|
|
392
|
-
|
|
393
|
-
const button1 = new Button({
|
|
394
|
-
text: trialConfiguration.selection_options_text[1],
|
|
395
|
-
size: { width: 150, height: 50 },
|
|
396
|
-
position: { x: 300, y: 700 },
|
|
397
|
-
isUserInteractionEnabled: true,
|
|
398
|
-
});
|
|
399
|
-
button1.onTapDown(() => {
|
|
400
|
-
handleUserSelection(1);
|
|
401
|
-
});
|
|
402
|
-
presentationScene.addChild(button1);
|
|
403
|
-
|
|
404
|
-
function handleUserSelection(selectionIndex: number): void {
|
|
405
|
-
/**
|
|
406
|
-
* Set both buttons' isUserInteractionEnabled to false to prevent
|
|
407
|
-
* double taps.
|
|
408
|
-
*/
|
|
409
|
-
button0.isUserInteractionEnabled = false;
|
|
410
|
-
button1.isUserInteractionEnabled = false;
|
|
411
|
-
Timer.stop("responseTime");
|
|
412
|
-
game.addTrialData("response_time_ms", Timer.elapsed("responseTime"));
|
|
413
|
-
Timer.remove("responseTime");
|
|
414
|
-
game.addTrialData(
|
|
415
|
-
"presented_word_text",
|
|
416
|
-
trialConfiguration.presented_text
|
|
417
|
-
);
|
|
418
|
-
game.addTrialData(
|
|
419
|
-
"presented_word_color",
|
|
420
|
-
trialConfiguration.presented_color.name
|
|
421
|
-
);
|
|
422
|
-
game.addTrialData(
|
|
423
|
-
"selected_text",
|
|
424
|
-
trialConfiguration.selection_options_text[selectionIndex]
|
|
425
|
-
);
|
|
426
|
-
const correct =
|
|
427
|
-
trialConfiguration.correct_option_index === selectionIndex;
|
|
428
|
-
game.addTrialData("selection_correct", correct);
|
|
429
|
-
game.addTrialData("trial_index", game.trialIndex);
|
|
430
|
-
game.addTrialData("activity_uuid", game.uuid);
|
|
431
|
-
/**
|
|
432
|
-
* When the trial has completed, you must call game.trialComplete() to
|
|
433
|
-
* 1) Increase the game.trialIndex counter
|
|
434
|
-
* 2) Trigger events that send the trial data to event subscribers
|
|
435
|
-
*/
|
|
436
|
-
game.trialComplete();
|
|
437
|
-
/**
|
|
438
|
-
* When this trial is done, we must remove the presented word and the
|
|
439
|
-
* buttons because they are not persistent across trials. We will
|
|
440
|
-
* create new, different buttons and presented word labels in the next
|
|
441
|
-
* trial.
|
|
442
|
-
*/
|
|
443
|
-
presentationScene.removeChildren([presentedWord, button0, button1]);
|
|
444
|
-
/**
|
|
445
|
-
* Are we done all the trials, or should we do another trial
|
|
446
|
-
* iteration?
|
|
447
|
-
*/
|
|
448
|
-
if (game.trialIndex === game.getParameter<number>("number_of_trials")) {
|
|
449
|
-
game.presentScene(doneScene);
|
|
450
|
-
} else {
|
|
451
|
-
game.presentScene(preparationScene);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
// ==============================================================
|
|
457
|
-
// SCENE: Done. Show done message, with a button to exit.
|
|
458
|
-
const doneScene = new Scene();
|
|
459
|
-
game.addScene(doneScene);
|
|
460
|
-
|
|
461
|
-
const doneSceneText = new Label({
|
|
462
|
-
text: "You have completed all the cli starter trials",
|
|
463
|
-
position: { x: 200, y: 400 },
|
|
464
|
-
});
|
|
465
|
-
doneScene.addChild(doneSceneText);
|
|
466
|
-
|
|
467
|
-
const okButton = new Button({
|
|
468
|
-
text: "OK",
|
|
469
|
-
position: { x: 200, y: 600 },
|
|
470
|
-
});
|
|
471
|
-
okButton.isUserInteractionEnabled = true;
|
|
472
|
-
okButton.onTapDown(() => {
|
|
473
|
-
// Don't allow repeat taps of ok button
|
|
474
|
-
okButton.isUserInteractionEnabled = false;
|
|
475
|
-
doneScene.removeAllChildren();
|
|
476
|
-
/**
|
|
477
|
-
* When the game is done, you must call game.end() to transfer control
|
|
478
|
-
* back to the Session, which will then start the next activity or
|
|
479
|
-
* send a session end event to the event subscribers.
|
|
480
|
-
*/
|
|
481
|
-
game.end();
|
|
482
|
-
});
|
|
483
|
-
doneScene.addChild(okButton);
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
const activity = new {{className}}();
|
|
488
|
-
const session = new Session({
|
|
489
|
-
activities: [activity],
|
|
490
|
-
canvasKitWasmUrl: "canvaskit.wasm",
|
|
491
|
-
sessionCallbacks: {
|
|
492
|
-
/**
|
|
493
|
-
* onSessionLifecycle() will be called on events such
|
|
494
|
-
* as when the session initialization is complete or when the
|
|
495
|
-
* session ends.
|
|
496
|
-
*
|
|
497
|
-
* Once initialized, the below code will start the session.
|
|
498
|
-
*/
|
|
499
|
-
onSessionLifecycle: async (ev: SessionLifecycleEvent) => {
|
|
500
|
-
if (ev.type === EventType.SessionInitialize) {
|
|
501
|
-
await session.start();
|
|
502
|
-
}
|
|
503
|
-
if (ev.type === EventType.SessionEnd) {
|
|
504
|
-
console.log("🔴 ended session");
|
|
505
|
-
}
|
|
506
|
-
},
|
|
507
|
-
},
|
|
508
|
-
activityCallbacks: {
|
|
509
|
-
/**
|
|
510
|
-
* onActivityResults() callback is where you insert code to post data
|
|
511
|
-
* to an API or interop with a native function in the host app,
|
|
512
|
-
* if applicable.
|
|
513
|
-
*
|
|
514
|
-
* newData is the data that was just generated by the completed trial or
|
|
515
|
-
* survey question.
|
|
516
|
-
* data is all the data, cumulative of all trials or questions in the
|
|
517
|
-
* activity, that have been generated.
|
|
518
|
-
*
|
|
519
|
-
* We separate out newData from data in case you want to alter the execution
|
|
520
|
-
* based on the most recent trial, e.g., maybe you want to stop after
|
|
521
|
-
* a certain user behavior or performance threshold in the just completed
|
|
522
|
-
* trial.
|
|
523
|
-
*
|
|
524
|
-
* activityConfiguration is the game parameters that were used.
|
|
525
|
-
*
|
|
526
|
-
* The schema for all of the above are in JSON Schema format.
|
|
527
|
-
* Currently, only games generate schema.
|
|
528
|
-
*/
|
|
529
|
-
onActivityResults: (ev: ActivityResultsEvent) => {
|
|
530
|
-
if (ev.target.type === ActivityType.Game) {
|
|
531
|
-
console.log(`✅ trial complete:`);
|
|
532
|
-
} else if (ev.target.type === ActivityType.Survey) {
|
|
533
|
-
console.log(`✅ question answered:`);
|
|
534
|
-
}
|
|
535
|
-
console.log(" newData: " + JSON.stringify(ev.newData));
|
|
536
|
-
console.log(" newData schema: " + JSON.stringify(ev.newDataSchema));
|
|
537
|
-
console.log(" data: " + JSON.stringify(ev.data));
|
|
538
|
-
console.log(" data schema: " + JSON.stringify(ev.dataSchema));
|
|
539
|
-
console.log(
|
|
540
|
-
" activity parameters: " + JSON.stringify(ev.activityConfiguration)
|
|
541
|
-
);
|
|
542
|
-
console.log(
|
|
543
|
-
" activity parameters schema: " +
|
|
544
|
-
JSON.stringify(ev.activityConfigurationSchema)
|
|
545
|
-
);
|
|
546
|
-
console.log(" activity metrics: " + JSON.stringify(ev.activityMetrics));
|
|
547
|
-
},
|
|
548
|
-
/**
|
|
549
|
-
* onActivityLifecycle() notifies us when an activity, such
|
|
550
|
-
* as a game (assessment) or a survey, has ended or canceled.
|
|
551
|
-
* Usually, however, we want to know when all the activities are done,
|
|
552
|
-
* so we'll look for the session ending via onSessionLifecycleChange
|
|
553
|
-
*/
|
|
554
|
-
onActivityLifecycle: async (ev: ActivityLifecycleEvent) => {
|
|
555
|
-
const activityType =
|
|
556
|
-
ev.target.type === ActivityType.Game ? "game" : "survey";
|
|
557
|
-
|
|
558
|
-
if (ev.type === EventType.ActivityStart) {
|
|
559
|
-
console.log(`🟢 started activity (${activityType}) ${ev.target.name}`);
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
if (
|
|
563
|
-
ev.type === EventType.ActivityCancel ||
|
|
564
|
-
ev.type === EventType.ActivityEnd
|
|
565
|
-
) {
|
|
566
|
-
const status =
|
|
567
|
-
ev.type === EventType.ActivityEnd ? "🔴 ended" : "🚫 canceled";
|
|
568
|
-
console.log(`${status} activity (${activityType}) ${ev.target.name}`);
|
|
569
|
-
if (session.nextActivity) {
|
|
570
|
-
await session.goToNextActivity();
|
|
571
|
-
} else {
|
|
572
|
-
session.end();
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
},
|
|
576
|
-
},
|
|
577
|
-
});
|
|
578
|
-
|
|
579
|
-
/**
|
|
580
|
-
* Make session also available on window in case we want to control
|
|
581
|
-
* the session through another means, such as other javascript or
|
|
582
|
-
* browser code, or a mobile WebView's invocation of session.start().
|
|
583
|
-
* */
|
|
584
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
585
|
-
(window as unknown as any).session = session;
|
|
586
|
-
session.init();
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
// Use IntelliSense to learn about possible attributes.
|
|
3
|
-
// Hover to view descriptions of existing attributes.
|
|
4
|
-
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
5
|
-
"version": "0.2.0",
|
|
6
|
-
"configurations": [
|
|
7
|
-
{
|
|
8
|
-
"type": "chrome",
|
|
9
|
-
"request": "launch",
|
|
10
|
-
"name": "Launch Chrome against localhost",
|
|
11
|
-
"url": "http://localhost:3000",
|
|
12
|
-
"webRoot": "${workspaceFolder}/build"
|
|
13
|
-
}
|
|
14
|
-
]
|
|
15
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "{{appName}}",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"scripts": {
|
|
5
|
-
"serve": "rollup -c rollup.config.mjs --watch --configServe",
|
|
6
|
-
"build": "npm run clean && rollup -c rollup.config.mjs --configProd",
|
|
7
|
-
"clean": "rimraf build dist .rollup.cache tsconfig.tsbuildinfo"
|
|
8
|
-
},
|
|
9
|
-
"private": true,
|
|
10
|
-
"dependencies": {
|
|
11
|
-
"@m2c2kit/addons": "0.3.3",
|
|
12
|
-
"@m2c2kit/core": "0.3.6"
|
|
13
|
-
},
|
|
14
|
-
"devDependencies": {
|
|
15
|
-
"@m2c2kit/build-helpers": "0.3.3",
|
|
16
|
-
"@rollup/plugin-node-resolve": "15.0.2",
|
|
17
|
-
"@rollup/plugin-typescript": "11.1.1",
|
|
18
|
-
"rimraf": "5.0.0",
|
|
19
|
-
"rollup": "3.22.0",
|
|
20
|
-
"rollup-plugin-copy": "3.4.0",
|
|
21
|
-
"rollup-plugin-livereload": "2.0.5",
|
|
22
|
-
"rollup-plugin-serve": "2.0.2",
|
|
23
|
-
"tslib": "2.5.0",
|
|
24
|
-
"typescript": "5.0.4"
|
|
25
|
-
}
|
|
26
|
-
}
|