Skip to content

Instantly share code, notes, and snippets.

@Furnyr
Last active November 28, 2024 00:42
Show Gist options
  • Save Furnyr/e5e968ff7d5a02dd12b370dbeadaf663 to your computer and use it in GitHub Desktop.
Save Furnyr/e5e968ff7d5a02dd12b370dbeadaf663 to your computer and use it in GitHub Desktop.
Using Unity 2022.3.8f1 with the Discord Embedded App SDK

April 13, 2024 Edit

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

Introduction

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.

Setting up the project

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:

image

Get a Unity build working inside Discord

Inside Unity:

  1. Select WebGL as the target platform in the build settings
  2. Go to Project Settings > Player > Resolution and Presentation and set the WebGL template to minimal
  3. Build the project

Inside the example project:

  1. Place the build files inside client/nested (replacing index.html)
  2. Open the index.html inside client. Inside the iframe tag, replace the src to nested/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.

image

To make the game display as it should follow the next steps:

  1. Open index.html inside the client folder and add the attribute scrolling="no" to the iframe tag to get rid of the scroll bars
  2. Inside the same file, remove <div>Parent iframe</div>
  3. 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>
  1. Open index.html inside the nested folder. Change the style attribute inside the canvas tag to include width: 100vw; height: 100vh; ... so the iframe resizes correctly. *
  2. You can delete index.ts and index.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:

image

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.

Interacting with the Discord client

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:

  1. You can't access the MessageInterface instance within Unity
  2. 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:

image image

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.

@colinloretz
Copy link

Great work on this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment