I recently published the first complete version of a project that can help with creating activities with Unity. If you want to check it out here's the repository: Dissonity
This file contains information about my experience using Unity with the Embedded App SDK, so if you plan to make a Discord activity with Unity you may find this helpful. I am not a professional developer though, and the SDK has become public just recently, so take my words with a pinch of salt.
I used Unity 2022.3.8f1, I haven't tested other versions but it may work similarly.
This is written on March 4, 2024.
I started with the nested messages example. This example is helpful to understand how we could host a Unity project inside an activity. First of all, you may need to add some type dependencies that are not included in the project such as @types/node
or @types/uuid
. I still encountered some type errors here and there but it could have been my TypeScript config. I set types to the variables that required it and the project was (almost) error-clean.
You may also encounter an error in the example project when executing the build script: "The define key "process.env.ProgramFiles(x86)" contains invalid identifier "ProgramFiles(x86)
". I suspect it's related to Windows. My fix was to, instead of excluding the CLIENT_SECRET
variable, only inject the variables that start with PUBLIC_ and update your code as needed.
At this point, when testing the application inside Discord (read this if you don't know how) you should see something like this:
Inside Unity:
- Select WebGL as the target platform in the build settings
- Go to
Project Settings
>Player
>Resolution and Presentation
and set the WebGL template to minimal - Build the project
Inside the example project:
- Place the build files inside
client/nested
(replacing index.html) - Open the
index.html
insideclient
. Inside the iframe tag, replace the src tonested/index.html
Now, when the application is started, the Express server will try to send the Unity build but it will fail because the server isn't set up properly. For the server to send the proper content headers to Discord you can modify the express.static
call inside the server index.js
with something like:
app.use(express.static(path.join(__dirname, "../client"), {
setHeaders: (res, path) => {
if (path.endsWith(".gz")) {
res.appendHeader("Content-Encoding", "gzip");
}
if (path.endsWith(".wasm.gz") || path.endsWith(".wasm")) {
res.appendHeader("Content-Type", "application/wasm");
}
if (path.endsWith(".js.gz") || path.endsWith(".js")) {
res.appendHeader("Content-Type", "application/javascript");
}
if (path.endsWith(".data") || path.endsWith(".mem")) {
res.appendHeader("Content-Type", "application/octet-stream");
}
}
}));
If you did everything right and start the activity you should now see the Unity game inside Discord, although poorly formatted.
To make the game display as it should follow the next steps:
- Open
index.html
inside the client folder and add the attributescrolling="no"
to the iframe tag to get rid of the scroll bars - Inside the same file, remove
<div>Parent iframe</div>
- Inside the same file, replace the style tag to the following or something that achieves the same:
<style>
body {
margin: 0px;
background-color: #000000;
padding: 0px;
}
iframe {
display: block;
border: 0px;
height: 100vh;
width: 100vw;
}
</style>
- Open
index.html
inside the nested folder. Change the style attribute inside the canvas tag to includewidth: 100vw; height: 100vh; ...
so the iframe resizes correctly. * - You can delete
index.ts
andindex.js
from the nested folder since they are no longer necessary
* (Notice you'll have to set this again if you replace this file when testing another build). You don't need to replace this index.html
everytime you make a new build unless you change the Unity project settings, but you may want to write a script to make sure this file is set up properly.
If you followed the steps correctly and launch the activity you should now see the game formatted as it should:
Congratulations! Your Unity game is now running within Discord, but we aren't quite done yet. We need a way for the Discord client to send data to your Unity game.
The Embedded App SDK is located outside the game build and runs JavaScript. Unity can call JavaScript functions via .jslib
files inside the Assets/Plugins
folder.
The example project includes a MessageInterface so the child iframe (where your game is running) can communicate with the Embedded App SDK (in the parent iframe), but there are still a few problems:
- You can't access the MessageInterface instance within Unity
- I couldn't maintain access to an instance inside a
.jslib
since I found that Unity only adds the properties that are functions to the build, so I couldn't just create the instance inside Unity and save it as a property (?)
My final implementation was to listen for messages from the parent iframe inside the .jslib. Inside Unity, a .jslib function is called, that then sends a message to the parent, that gets data from the SDK and sends a message to the child (where the Unity .jslib is), and then the data is sent back to Unity and it's ready to be used.
Although, it's important that the .jslib has access to the unity instance that's created by calling createUnityInstance
inside the build's index.html. You can do something like the following to maintain access to the instance:
var unityInstance;
createUnityInstance(<...>, {
<...>
}).then(instance => {
unityInstance = instance;
});
To make development inside Unity easier I created a static class that contains all the methods needed to interact with the Embedded App SDK (let's call it "StaticSDKBridge") and a script that is within the scene that receives the data from the .jslib (let's call it "DynamicSDKBridge")
Here are some very professional schemas to visualize the idea:
This is still a little simplified, since you'll need to handle things like converting data back and forth between the .jslib and your C# scripts or making the .jslib listen for messages when the game starts, but that's essentially how my implementation works. From there, you can extend the parent's index.ts
from the example project to call everything you need inside the SDK.
If you want to take a better look of how this works you can check out the repository I linked at the beginning of this file.
You may find valuable information about Unity and web scripting here.
If you want to contact me, I'm in the Discord Developers server as nyrrren
.
Great work on this!