This article uses Linux. If you aren't running linux, there's a good chance you can still follow along if you have some command-line-fu.
There has been a lot of discussion about deploying PICO-8 cartridges to a native desktop application. Don't get me wrong, I think it's awesome that folks distribute the *.p8.png
file format, but as most game developers know, most gamers honestly just want a double click format. I've done this with Vi8 using PICOLOVE and a bunch of hacks. It's dirty, painful and no one really wants to do that anyway.
I have found a way to do this using Electron.
Note to Reader: I'd like to think this article is a joke for the most part. I mean, we're taking about ~1.3MB of data, and we're making a deployable that's ~40MB. Reader beware, but I think it's reasonable to document this process anyway.
For this article, I will be exporting The Career of Peter to desktop!
So, there's going to be some steps that need to be done, before we can even start.
Here we load our cartridge as we normally do with load foo.p8
and then we use PICO-8's export foo.html
feature to make a website version of the game.
Here's the files we now have:
seppi@seppi7:~/TheCareerOfPeter♠ ls -lah
total 1.4M
drwxr-xr-x 2 seppi seppi 4.0K Oct 21 20:04 .
drwx------ 134 seppi seppi 20K Oct 21 20:00 ..
-rw-r--r-- 1 seppi seppi 4.5K Oct 21 20:04 thecareerofpeter-v18.html
-rw-r--r-- 1 seppi seppi 1.3M Oct 21 20:04 thecareerofpeter-v18.js
-rw-r--r-- 1 seppi seppi 72K Oct 21 20:01 thecareerofpeter-v18.p8
Now we need to "clean up" the cartridge for deployment.
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
- <title>PICO-8 Cartridge</title>
+ <title>The Career of Peter</title>
<meta name="description" content="">
<STYLE TYPE="text/css">
canvas#canvas { width: 512px; height: 512px; }
+body {
+ padding: 0px;
+ margin: 0px;
+}
+
.pico8_el
stuff. .pico8_el {
float:left;
width:92px;
font-size: 9pt;
cursor: pointer;
cursor: hand;
+ display: none;
}
<br>
tags with extreme prejudice. <body bgcolor=#303030>
- <br><br><br>
<center><div style="width:512px;">
</script>
- <br>
<div class=pico8_el onclick="Module.pico8Reset();">
```
Carts</a></div>
- <br>
</div></center>
- <br><br>
</body></html>
Awesome! Now the PICO-8 export should show up nicely in electron!
I then installed globally npm
and updated it with:
sudo npm install npm@latest -g
I then install electron
globally:
sudo npm install electron -g
Finally in the working directory, I run:
npm install electron-prebuilt
and npm install electron-packager
This provides a back-end to getting binaries for every platform and the packaging system that will build the binary. You will now notice a big honking node_modules/
folder. Fun.
Now that we have that, we need to set up the required package.json
for electron. Just put it in your working directory. Here's what I have (it seems to work ...):
{
"name": "the-career-of-peter",
"version": "1.18.0",
"description": "The Career of Peter by Missing Sentinel Software",
"main": "main.js",
"scripts":{
"start":"electron .",
"build":"electron-packager . TheCareerOfPeter --all"
},
"author":"Josef Patoprsty <josefnpat@gmail.com> (http://josefnpat.com)",
"license":"Josef Patoprsty",
"devDependencies": {
"electron-packager":"^8.1.0",
"electron-prebuilt":"^1.3.8"
},
"electron-packager":"^8.1.0"
}
main.js
So, now we need to inform electron how it's supposed to work. I modified an example main.js
I found, and this one works for me.
const {app, BrowserWindow} = require('electron')
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win
function createWindow () {
// Create the browser window.
win = new BrowserWindow({
width: 512,
height: 512,
title: "The Career of Peter",
resizable: false,
maximizable: false,
fullscreenable: false
})
win.setMenu(null);
// and load the index.html of the app.
win.loadURL(`file://${__dirname}/thecareerofpeter-v18.html`)
// Open the DevTools.
//win.webContents.openDevTools()
// Emitted when the window is closed.
win.on('closed', () => {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
win = null
})
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)
// Quit when all windows are closed.
app.on('window-all-closed', () => {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (win === null) {
createWindow()
}
})
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
With this in place you can now test your application before building it with electron .
Now we use our nifty build
script defined in package.json
:
seppi@seppi7:~/TheCareerOfPeter♠ npm run build
> the-career-of-peter@1.18.0 build /home/seppi/TheCareerOfPeter
> electron-packager . TheCareerOfPeter --all
Packaging app for platform linux ia32 using electron v1.3.8
Packaging app for platform win32 ia32 using electron v1.3.8
Packaging app for platform darwin x64 using electron v1.3.8
Packaging app for platform linux x64 using electron v1.3.8
Packaging app for platform mas x64 using electron v1.3.8
WARNING: signing is required for mas builds. Provide the osx-sign option, or manually sign the app later.
Packaging app for platform win32 x64 using electron v1.3.8
Packaging app for platform linux armv7l using electron v1.3.8
Wrote new apps to:
/home/seppi/TheCareerOfPeter/TheCareerOfPeter-linux-ia32
/home/seppi/TheCareerOfPeter/TheCareerOfPeter-win32-ia32
/home/seppi/TheCareerOfPeter/TheCareerOfPeter-darwin-x64
/home/seppi/TheCareerOfPeter/TheCareerOfPeter-linux-x64
/home/seppi/TheCareerOfPeter/TheCareerOfPeter-mas-x64
/home/seppi/TheCareerOfPeter/TheCareerOfPeter-win32-x64
/home/seppi/TheCareerOfPeter/TheCareerOfPeter-linux-armv7l
With that we have tons of builds! You could run one of them like this:
./TheCareerOfPeter-linux-x64/TheCareerOfPeter
And just for "science", let's check out how big these files are:
seppi@seppi7:~/TheCareerOfPeter♠ ls -lah
total 442M
drwxr-xr-x 10 seppi seppi 4.0K Oct 21 20:37 .
drwx------ 134 seppi seppi 20K Oct 21 20:36 ..
-rw-r--r-- 1 seppi seppi 1.7K Oct 21 20:31 main.js
drwxr-xr-x 187 seppi seppi 4.0K Oct 21 20:15 node_modules
-rw-r--r-- 1 seppi seppi 482 Oct 21 20:07 package.json
drwxr-xr-x 3 seppi seppi 4.0K Oct 21 20:34 TheCareerOfPeter-darwin-x64
-rw-r--r-- 1 seppi seppi 116M Oct 21 20:36 TheCareerOfPeter-darwin-x64.zip
drwxr-xr-x 4 seppi seppi 4.0K Oct 21 20:34 TheCareerOfPeter-linux-armv7l
-rw-r--r-- 1 seppi seppi 36M Oct 21 20:36 TheCareerOfPeter-linux-armv7l.zip
drwxr-xr-x 4 seppi seppi 4.0K Oct 21 20:34 TheCareerOfPeter-linux-ia32
-rw-r--r-- 1 seppi seppi 43M Oct 21 20:36 TheCareerOfPeter-linux-ia32.zip
drwxr-xr-x 4 seppi seppi 4.0K Oct 21 20:34 TheCareerOfPeter-linux-x64
-rw-r--r-- 1 seppi seppi 41M Oct 21 20:36 TheCareerOfPeter-linux-x64.zip
drwxr-xr-x 3 seppi seppi 4.0K Oct 21 20:34 TheCareerOfPeter-mas-x64
-rw-r--r-- 1 seppi seppi 114M Oct 21 20:36 TheCareerOfPeter-mas-x64.zip
-rw-r--r-- 1 seppi seppi 4.5K Oct 21 20:32 thecareerofpeter-v18.html
-rw-r--r-- 1 seppi seppi 1.3M Oct 21 20:04 thecareerofpeter-v18.js
-rw-r--r-- 1 seppi seppi 72K Oct 21 20:01 thecareerofpeter-v18.p8
drwxr-xr-x 4 seppi seppi 4.0K Oct 21 20:34 TheCareerOfPeter-win32-ia32
-rw-r--r-- 1 seppi seppi 42M Oct 21 20:36 TheCareerOfPeter-win32-ia32.zip
drwxr-xr-x 4 seppi seppi 4.0K Oct 21 20:34 TheCareerOfPeter-win32-x64
-rw-r--r-- 1 seppi seppi 52M Oct 21 20:37 TheCareerOfPeter-win32-x64.zip
To check out the final result for yourself, mosey on over to The Career of Peter
Howdy; my name's Seppi, and I'm an indie game developer. I am an active member of the LÖVE community. I've been making games for years now, and I'm always interested in helping prospective indie developers out.
One of the paradoxes I've learned over the years, especially as a software developer, is the more I know, the more I realize I don't know. I quote;
Only a fool would take anything posted here as fact.
Questions, comments or insults? Feel free to leave in the comments section, or contact me; you can hit me up on twitter @josefnpat