@m2c2kit/cli 0.1.4 → 0.1.5
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/.env
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
CLI_VERSION=0.1.
|
|
1
|
+
CLI_VERSION=0.1.5
|
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,9 @@ This project was generated with the m2c2kit CLI version {{cliVersion}}.
|
|
|
4
4
|
|
|
5
5
|
## Development server
|
|
6
6
|
|
|
7
|
-
Run `npm run serve` from the command line for a development server. Browse to `http://localhost:3000/`. The app will automatically compile and reload when you change source files.
|
|
7
|
+
Run `npm run serve` from the command line for a development server. Browse to `http://localhost:3000/`. The app will automatically compile and reload when you change source files. If you get an error on the reload, edit `rollup.config.js` and increase the delay parameter (unit is milliseconds) in this line:
|
|
8
|
+
|
|
9
|
+
livereload({ watch: "build", delay: 0 })
|
|
8
10
|
|
|
9
11
|
## Debugging
|
|
10
12
|
|
|
@@ -6,16 +6,32 @@ import {
|
|
|
6
6
|
Sprite,
|
|
7
7
|
Point,
|
|
8
8
|
Label,
|
|
9
|
+
LabelHorizontalAlignmentMode,
|
|
10
|
+
Shape,
|
|
11
|
+
Rect,
|
|
12
|
+
GameData,
|
|
9
13
|
} from "@m2c2kit/core";
|
|
10
14
|
|
|
11
|
-
import {
|
|
12
|
-
Button
|
|
13
|
-
} from "@m2c2kit/addons";
|
|
15
|
+
import { Button, Instructions } from "@m2c2kit/addons";
|
|
14
16
|
|
|
15
17
|
const game = new Game();
|
|
16
18
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
19
|
(window as unknown as any).game = game;
|
|
18
20
|
|
|
21
|
+
// game parameter defaults to be used if values are not provided
|
|
22
|
+
// default parameters are not part of the m2c2kit engine, because parameters
|
|
23
|
+
// are different for each game that might be written. Thus, define them here
|
|
24
|
+
const defaults = {
|
|
25
|
+
ReadyTime: 1000,
|
|
26
|
+
TrialNum: 3,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// trial data variables names and types must be defined
|
|
30
|
+
// valid types are "number", "string", "boolean", and "object"
|
|
31
|
+
const demoTrialSchema = {
|
|
32
|
+
correct: "boolean",
|
|
33
|
+
};
|
|
34
|
+
|
|
19
35
|
game
|
|
20
36
|
.init({
|
|
21
37
|
showFps: true,
|
|
@@ -24,11 +40,12 @@ game
|
|
|
24
40
|
// note: using 2:1 aspect ratio, because that is closer to modern phones
|
|
25
41
|
width: 400,
|
|
26
42
|
height: 800,
|
|
43
|
+
defaultParameters: defaults,
|
|
44
|
+
trialSchema: demoTrialSchema,
|
|
27
45
|
// set stretch to true if you want to fill the screen
|
|
28
46
|
stretch: false,
|
|
29
|
-
fontUrls
|
|
30
|
-
|
|
31
|
-
],
|
|
47
|
+
// Roboto is included by default. Leave fontUrls unchanged, unless you want to use different font
|
|
48
|
+
fontUrls: ["./fonts/roboto/Roboto-Regular.ttf"],
|
|
32
49
|
// each svgImage below, you specify either a tag of an svg in the property "svgString" (as I do below)
|
|
33
50
|
// or you specify a URL to an svg in the property "url", such as url: 'https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/android.svg'
|
|
34
51
|
svgImages: [
|
|
@@ -42,55 +59,237 @@ game
|
|
|
42
59
|
],
|
|
43
60
|
})
|
|
44
61
|
.then(() => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
62
|
+
// callback provided to onTrialComplete is called when each trial is done
|
|
63
|
+
game.onTrialComplete(
|
|
64
|
+
(trialNumber: number, data: GameData, trialSchema: object) => {
|
|
65
|
+
console.log(`Trial number ${trialNumber} completed.`);
|
|
66
|
+
console.log(`Current Game data is: ${JSON.stringify(data)}`);
|
|
67
|
+
console.log(`Trial schema is: ${JSON.stringify(trialSchema)}`);
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
// callback provided to onAllTrialsComplete is called when all game trials are done
|
|
72
|
+
game.onAllTrialsComplete((data: GameData, trialSchema: object) => {
|
|
73
|
+
console.log(`All trials completed.`);
|
|
74
|
+
console.log(`Complete Game data is: ${JSON.stringify(data)}`);
|
|
75
|
+
console.log(`Trial schema is: ${JSON.stringify(trialSchema)}`);
|
|
48
76
|
});
|
|
49
|
-
game.addScene(s);
|
|
50
77
|
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
78
|
+
const instructionsScenes = Instructions.Create({
|
|
79
|
+
sceneNamePrefix: "instructions",
|
|
80
|
+
backgroundColor: WebColors.White,
|
|
81
|
+
nextButtonBackgroundColor: WebColors.Black,
|
|
82
|
+
backButtonBackgroundColor: WebColors.Black,
|
|
83
|
+
instructionScenes: [
|
|
84
|
+
{
|
|
85
|
+
title: "{{appName}} Demo",
|
|
86
|
+
text: "For this activity, you will click the red rectangle. Open the developer console to see the trial data.",
|
|
87
|
+
textFontSize: 24,
|
|
88
|
+
titleFontSize: 30,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
title: "{{appName}} Demo",
|
|
92
|
+
text: "Press START to begin!",
|
|
93
|
+
textFontSize: 24,
|
|
94
|
+
titleFontSize: 30,
|
|
95
|
+
textAlignmentMode: LabelHorizontalAlignmentMode.center,
|
|
96
|
+
nextButtonText: "START",
|
|
97
|
+
nextButtonBackgroundColor: WebColors.Green,
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
// this is the scene to go to after the last instructions scene
|
|
101
|
+
postInstructionsScene: "getReadyScene",
|
|
55
102
|
});
|
|
56
|
-
|
|
103
|
+
game.addScenes(instructionsScenes);
|
|
57
104
|
|
|
58
|
-
|
|
59
|
-
|
|
105
|
+
// start at 0
|
|
106
|
+
game.trialNumber = 0;
|
|
107
|
+
|
|
108
|
+
const demoPage0 = new Scene({
|
|
109
|
+
// Because this is the scene after the instructions, we must give
|
|
110
|
+
// it a name and provide it to postInstructionsScene, above
|
|
111
|
+
name: "getReadyScene",
|
|
112
|
+
backgroundColor: WebColors.White,
|
|
113
|
+
});
|
|
114
|
+
game.addScene(demoPage0);
|
|
115
|
+
|
|
116
|
+
const getReadyMessage = new Label({
|
|
117
|
+
text: "Get Ready",
|
|
118
|
+
fontSize: 24,
|
|
60
119
|
position: new Point(200, 400),
|
|
61
120
|
});
|
|
62
|
-
|
|
121
|
+
demoPage0.addChild(getReadyMessage);
|
|
63
122
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
position: new Point(200,
|
|
123
|
+
// example of how to use an image. The image must be previously loaded
|
|
124
|
+
const starSprite = new Sprite({
|
|
125
|
+
imageName: "star",
|
|
126
|
+
position: new Point(200, 500),
|
|
68
127
|
});
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
button.onTap(() => {
|
|
75
|
-
console.log("you tapped it!");
|
|
76
|
-
button.run(
|
|
128
|
+
demoPage0.addChild(starSprite);
|
|
129
|
+
|
|
130
|
+
// demoPage0.setup() has a callback that is executed each time this scene is shown
|
|
131
|
+
demoPage0.setup(() => {
|
|
132
|
+
demoPage0.run(
|
|
77
133
|
Action.Sequence([
|
|
134
|
+
// Get the wait duration from the default game parameters, defined above
|
|
135
|
+
Action.Wait({ duration: game.getParameter("ReadyTime") }),
|
|
78
136
|
Action.Custom({
|
|
79
137
|
callback: () => {
|
|
80
|
-
|
|
81
|
-
},
|
|
82
|
-
}),
|
|
83
|
-
Action.Wait({ duration: 500 }),
|
|
84
|
-
Action.Custom({
|
|
85
|
-
callback: () => {
|
|
86
|
-
messageLabel.hidden = true;
|
|
138
|
+
game.presentScene(demoPage1);
|
|
87
139
|
},
|
|
88
140
|
}),
|
|
89
141
|
])
|
|
90
142
|
);
|
|
91
143
|
});
|
|
92
144
|
|
|
93
|
-
|
|
145
|
+
// these entities (demoPage1 through wrongMessage) can be defined outside of a setup()
|
|
146
|
+
// because they exist through multiple trials
|
|
147
|
+
// Their position and how they respond to interactions may differ across trials,
|
|
148
|
+
// and that logic will be written within a setup()
|
|
149
|
+
const demoPage1 = new Scene({ backgroundColor: WebColors.LightGray });
|
|
150
|
+
game.addScene(demoPage1);
|
|
151
|
+
const redRect = new Shape({
|
|
152
|
+
rect: new Rect({ width: 150, height: 100 }),
|
|
153
|
+
fillColor: WebColors.Red,
|
|
154
|
+
});
|
|
155
|
+
demoPage1.addChild(redRect);
|
|
156
|
+
const blueRect = new Shape({
|
|
157
|
+
rect: new Rect({ width: 150, height: 100 }),
|
|
158
|
+
fillColor: WebColors.Blue,
|
|
159
|
+
});
|
|
160
|
+
demoPage1.addChild(blueRect);
|
|
161
|
+
const correctMessage = new Label({
|
|
162
|
+
text: "CORRECT!",
|
|
163
|
+
position: new Point(200, 500),
|
|
164
|
+
});
|
|
165
|
+
correctMessage.hidden = true;
|
|
166
|
+
demoPage1.addChild(correctMessage);
|
|
167
|
+
const wrongMessage = new Label({
|
|
168
|
+
text: "WRONG!",
|
|
169
|
+
position: new Point(200, 500),
|
|
170
|
+
});
|
|
171
|
+
wrongMessage.hidden = true;
|
|
172
|
+
demoPage1.addChild(wrongMessage);
|
|
173
|
+
|
|
174
|
+
// demoPage1.setup() has a callback that is executed each time this scene is shown
|
|
175
|
+
// we will randomly decide on what side the red rectangle is shown
|
|
176
|
+
demoPage1.setup(() => {
|
|
177
|
+
let redOnLeft = true;
|
|
178
|
+
if (Math.random() > 0.5) {
|
|
179
|
+
redOnLeft = false;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (redOnLeft) {
|
|
183
|
+
redRect.position = new Point(100, 300);
|
|
184
|
+
blueRect.position = new Point(300, 300);
|
|
185
|
+
} else {
|
|
186
|
+
redRect.position = new Point(300, 300);
|
|
187
|
+
blueRect.position = new Point(100, 300);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// helper function to record the user's choice and
|
|
191
|
+
// decide if we are done
|
|
192
|
+
function recordUserInput(choseRedRect: boolean) {
|
|
193
|
+
game.addTrialData("correct", choseRedRect);
|
|
194
|
+
game.lifecycle.trialComplete(
|
|
195
|
+
game.trialNumber,
|
|
196
|
+
game.data,
|
|
197
|
+
game.trialSchema
|
|
198
|
+
);
|
|
199
|
+
game.trialNumber++;
|
|
200
|
+
// have we finished the number of trials?
|
|
201
|
+
if (game.trialNumber < game.getParameter<number>("TrialNum")) {
|
|
202
|
+
game.presentScene(demoPage0);
|
|
203
|
+
} else {
|
|
204
|
+
game.presentScene(endPage);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
redRect.isUserInteractionEnabled = true;
|
|
209
|
+
redRect.onTap(() => {
|
|
210
|
+
redRect.run(
|
|
211
|
+
Action.Sequence([
|
|
212
|
+
Action.Custom({
|
|
213
|
+
callback: () => {
|
|
214
|
+
// once a choice is made, don't allow additional taps
|
|
215
|
+
redRect.isUserInteractionEnabled = false;
|
|
216
|
+
blueRect.isUserInteractionEnabled = false;
|
|
217
|
+
},
|
|
218
|
+
}),
|
|
219
|
+
// short animation to shrink and grow button
|
|
220
|
+
Action.Scale({ scale: 0.8, duration: 250 }),
|
|
221
|
+
Action.Scale({ scale: 1, duration: 250 }),
|
|
222
|
+
// Show the "CORRECT" message
|
|
223
|
+
Action.Custom({
|
|
224
|
+
callback: () => {
|
|
225
|
+
correctMessage.hidden = false;
|
|
226
|
+
},
|
|
227
|
+
}),
|
|
228
|
+
Action.Wait({ duration: 1000 }),
|
|
229
|
+
// Handle the logic of recording data, and deciding if we should
|
|
230
|
+
// do another trial in recordUserInput
|
|
231
|
+
Action.Custom({
|
|
232
|
+
callback: () => {
|
|
233
|
+
correctMessage.hidden = true;
|
|
234
|
+
recordUserInput(true);
|
|
235
|
+
},
|
|
236
|
+
}),
|
|
237
|
+
])
|
|
238
|
+
);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
blueRect.isUserInteractionEnabled = true;
|
|
242
|
+
blueRect.onTap(() => {
|
|
243
|
+
blueRect.run(
|
|
244
|
+
Action.Sequence([
|
|
245
|
+
Action.Custom({
|
|
246
|
+
callback: () => {
|
|
247
|
+
redRect.isUserInteractionEnabled = false;
|
|
248
|
+
blueRect.isUserInteractionEnabled = false;
|
|
249
|
+
},
|
|
250
|
+
}),
|
|
251
|
+
Action.Scale({ scale: 0.8, duration: 250 }),
|
|
252
|
+
Action.Scale({ scale: 1, duration: 250 }),
|
|
253
|
+
Action.Custom({
|
|
254
|
+
callback: () => {
|
|
255
|
+
wrongMessage.hidden = false;
|
|
256
|
+
},
|
|
257
|
+
}),
|
|
258
|
+
Action.Wait({ duration: 1000 }),
|
|
259
|
+
Action.Custom({
|
|
260
|
+
callback: () => {
|
|
261
|
+
wrongMessage.hidden = true;
|
|
262
|
+
recordUserInput(false);
|
|
263
|
+
},
|
|
264
|
+
}),
|
|
265
|
+
])
|
|
266
|
+
);
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
const endPage = new Scene();
|
|
271
|
+
game.addScene(endPage);
|
|
272
|
+
const doneLabel = new Label({
|
|
273
|
+
text: `This will be reassigned in the setup() callback. If you see this, something went wrong!`,
|
|
274
|
+
position: new Point(200, 300),
|
|
275
|
+
});
|
|
276
|
+
endPage.addChild(doneLabel);
|
|
277
|
+
|
|
278
|
+
const againButton = new Button({
|
|
279
|
+
text: "Start over",
|
|
280
|
+
position: new Point(200, 400),
|
|
281
|
+
});
|
|
282
|
+
againButton.isUserInteractionEnabled = true;
|
|
283
|
+
againButton.onTap(() => {
|
|
284
|
+
game.initData(game.trialSchema);
|
|
285
|
+
game.presentScene(demoPage0);
|
|
286
|
+
});
|
|
287
|
+
endPage.addChild(againButton);
|
|
288
|
+
|
|
289
|
+
endPage.setup(() => {
|
|
290
|
+
doneLabel.text = `You did ${game.trialNumber} trials. You're done!`;
|
|
291
|
+
game.lifecycle.allTrialsComplete(game.data, game.trialSchema);
|
|
292
|
+
});
|
|
94
293
|
|
|
95
|
-
game.start("
|
|
294
|
+
game.start("instructions-01");
|
|
96
295
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@m2c2kit/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "m2c2kit command line interface",
|
|
5
5
|
"module": "dist/cli.js",
|
|
6
6
|
"files": [
|
|
@@ -33,12 +33,12 @@
|
|
|
33
33
|
"yargs": "17.3.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@types/node": "
|
|
36
|
+
"@types/node": "17.0.1",
|
|
37
37
|
"@types/prompts": "2.0.14",
|
|
38
38
|
"@types/yargs": "17.0.7",
|
|
39
39
|
"copyfiles": "2.4.1",
|
|
40
40
|
"rimraf": "3.0.2",
|
|
41
41
|
"tslib": "2.3.1",
|
|
42
|
-
"typescript": "4.5.
|
|
42
|
+
"typescript": "4.5.4"
|
|
43
43
|
}
|
|
44
44
|
}
|