a cheap knock-off Warioware accepting Recurse Center community contributed microgames
Engine built by clairefro (SP2'25), jackrr (SP2'25), rippy (mini-6'19), cysabi (SP2'25) during NGW'25
Pre-reqs: have node and npm installed
Clone this repo, then:
If you are working on the Game Manager (Engine) itself, npm run dev
# install deps
npm i
# run dev server
npm run devIf you are working on the Game Manager (Engine) itself, npm run dev
# install deps
npm i
# run game-dev server (only plays your game, suppresses splash screen)
npm run game-dev --game=your-game`Games are just directories with a mandatory index.jsand manifest.json, and an optional assets/ dir with any images and sounds.
your-game
assets/
your-image.png
index.js
manifest.json
To add a game, fork this repo and add your game in /games dir, then make a PR. (tip: Use the game template and copy the your-game boiler into /games to get quick-started.)
Run a game-dev server with npm run game-dev --game=your-game to iterate on your game only and bypass the start splash screen.
The manifest.json file declares game metadata and registers assets and instructions
IMPORTANT: name of game must match your game dir name
{
"name": "situps",
"assets": ["situp.png, fart.mp3"],
"instruction": "Situp !",
"author": "Your Name",
"authorLink": "https://your-site-or-rc-directory-page.com (optional)"
}- Situps (image, sound)
- Dodge Block (image)
The p5 library is available to you on this.lib.p5
You can use this to render things to the screen in the draw() method
example from situps game
draw() {
const state = this.state;
const p5 = this.libs.p5;
p5.background(255);
p5.push();
p5.translate(
state.athlete.x + state.athlete.width / 2,
state.athlete.y + state.athlete.height / 2
);
p5.rotate(state.athlete.isDown ? 0 : -p5.PI / 4);
p5.imageMode(p5.CENTER);
p5.image(
this.assets["situp.png"],
0,
0,
state.athlete.width,
state.athlete.height
);
p5.pop();
// Draw counter
p5.textSize(24);
p5.textAlign(p5.LEFT);
p5.fill(0);
p5.text(
`Sit-ups: ${state.athlete.sitUpCount}/${state.requiredSitUps}`,
10,
30
);
if (state.gameOver) {
p5.textSize(48);
p5.textAlign(p5.CENTER);
p5.fill(0, 255, 0); // green
p5.text(state.message, p5.width / 2, p5.height / 2);
}
}Supported formats: .png, .jpg
Save your image assets in /assets dir inside your game, and declare the filenames in your manifest.json assets property
situps
assets/
situp.png
fart.mp3
index.js
manifest.json
manifest.json
{
"name": "situps",
"assets": ["situp.png", "fart.mp3"],
"instruction": "Situp ↑ !",
"author": "Claire Froelich",
"authorLink": "https://www.recurse.com/directory/6727-claire-froelich"
}index.js
p5.image(
this.assets['situp.png'],
0,
0,
state.athlete.width,
state.athlete.height
)Supported formats: .mp3, .wav
Save your sounds in an /assets dir in your game, in declare the filename(s) in your manifest.json assets property.
miRCo uses Howler js to play sound. You can access sound controls from this.libs.sounds
situps
assets/
situp.png
fart.mp3
index.js
manifest.json
manifest.json
{
"name": "situps",
"assets": ["situp.png", "fart.mp3"],
"instruction": "Situp ↑ !",
"author": "Claire Froelich",
"authorLink": "https://www.recurse.com/directory/6727-claire-froelich"
}index.js
// start sound
this.libs.sound.play(this.assets['fart.mp3'])
// stop sound
this.libs.sound.stop(this.assets['fart.mp3'])You can optionally specify a volume for your with the following syntax.
{
"name": "launch",
"assets": [
{ "file": "whirring.wav", "volume": 1.2 },
{ "file": "cheers.wav", "volume": 1.3 },
"penelope.png"
],
"instruction": "Fly!",
"author": "Jack Ratner",
"authorLink": "https://www.recurse.com/directory/6651-jack-ratner"
}Only 4 game controls are allowed: "left", "right", "up", "down"
The player can control with arrow keys, WASD, or CandyCon gamepad controller.
| State | Description | Left | Right | Up | Down |
|---|---|---|---|---|---|
| Just Pressed | Returns true only on the first frame when pressed |
this.input.justPressedLeft() |
this.input.justPressedRight() |
this.input.justPressedUp() |
this.input.justPressedDown() |
| Being Held | Returns true for entire duration button is held down |
this.input.isPressedLeft() |
this.input.isPressedRight() |
this.input.isPressedUp() |
this.input.isPressedDown() |
| Just Released | Returns true only on the first frame when released |
this.input.releasedLeft() |
this.input.releasedRight() |
this.input.releasedUp() |
this.input.releasedDown() |
update(dt) {
if (this.input.isPressedLeft()) {
// move left while left is held
this.state.player.x -= 0.2 * dt;
}
if (this.input.isPressedRight()) {
// move right while right is held
this.state.player.x += 0.2 * dt;
}
if (this.input.isPressedUp()) {
// move up while up is held
this.state.player.y += 0.2 * dt;
}
if (this.input.isPressedDown()) {
// move down while down is held
this.state.player.y -= 0.2 * dt;
}
}or...
update(dt) {
// Check if button was just pressed this frame
if (this.input.justPressedUp()) {
this.jump();
}
// Check if button is being held
if (this.input.isPressedRight()) {
this.moveRight(dt);
}
// Check if button was just released
if (this.input.releasedDown()) {
this.stopCrouching();
}
}You can trigger gamepads to vibrate. If no gamepad is plugged in, nothing will happen.
this.input.gamepad.pulse()
MircoEngine keeps track of which "Round" the user is on based on the number of completed games, starting with Round 0. You have access to the round value in your game, and can use it for logic that increases the difficulty of the game as rounds increase.
this.mirco.round
See examples of round-based difficulty logic in situps
For now, just make a PR with your game (and other games) in the /games dir! Please fork this repo if you want to contribute a game so you can make a PR.
Someday the game submission may move to a separate repo, and fetch all the games would be fetched on launch.
- Typescript?
- make console prettier (help wanted)
- game PR based game submission system that autochecks for unique names, validated manifest.json etc
- add a storyline based on wins/losses?
disco deploy --project mirco
