@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.4
1
+ CLI_VERSION=0.1.5
package/dist/cli.js CHANGED
@@ -389,4 +389,7 @@ await yarg
389
389
  })
390
390
  .demandCommand()
391
391
  .strict()
392
+ // seems to have problems getting version automatically; thus I explicity
393
+ // provide
394
+ .version(cliVersion)
392
395
  .showHelpOnFail(true).argv;
@@ -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
 
@@ -7,8 +7,8 @@
7
7
  },
8
8
  "private": true,
9
9
  "dependencies": {
10
- "@m2c2kit/core": "0.1.2",
11
- "@m2c2kit/addons": "0.1.2"
10
+ "@m2c2kit/core": "0.1.3",
11
+ "@m2c2kit/addons": "0.1.3"
12
12
  },
13
13
  "devDependencies": {
14
14
  "rollup": "2.61.1",
@@ -85,7 +85,7 @@ export default (commandLineArgs) => {
85
85
  port: 3000,
86
86
  }),
87
87
  commandLineArgs.configServe &&
88
- livereload(),
88
+ livereload({ watch: "build", delay: 0 }),
89
89
  ],
90
90
  },
91
91
  ];
@@ -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
- "./fonts/roboto/Roboto-Regular.ttf",
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
- const s = new Scene({
46
- backgroundColor: WebColors.SlateBlue,
47
- name: "firstScene",
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 greeting = new Label({
52
- text: "Hello, {{appName}}",
53
- fontColor: WebColors.WhiteSmoke,
54
- position: new Point(200, 200),
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
- s.addChild(greeting);
103
+ game.addScenes(instructionsScenes);
57
104
 
58
- const starImage = new Sprite({
59
- imageName: "star",
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
- s.addChild(starImage);
121
+ demoPage0.addChild(getReadyMessage);
63
122
 
64
- const messageLabel = new Label({
65
- text: "you clicked the button!",
66
- fontColor: WebColors.Yellow,
67
- position: new Point(200, 600),
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
- s.addChild(messageLabel);
70
- messageLabel.hidden = true;
71
-
72
- const button = new Button( { text: "Click me!", position: new Point(200, 700) });
73
- button.isUserInteractionEnabled = true;
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
- messageLabel.hidden = false;
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
- s.addChild(button);
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("firstScene");
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.4",
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": "16.11.12",
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.3"
42
+ "typescript": "4.5.4"
43
43
  }
44
44
  }