Only this pageAll pages
Powered by GitBook
1 of 79

Documentation

Documentation

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

ODK Plugin

Documentation for some of the functionality provided with the ODK plugin

ODK Blueprint Node Library

Building Otherside, one blueprint at a time.

Full ODK Blueprint node library with descriptions for each node. https://odk-blueprints.preview.msquared.io/v9.3

Technical Overview

High-level 101

The Otherside Development Kit (or ODK) is a platform built for the Yuga community to build and share experiences within the Otherside community.

The ODK is built on top of MSquared's World Builder platform. World Builder documentation is available here. The World Builder is built on top of a Blueprint only version of Epic's Unreal Engine, and is architected to allow quick iteration on content, while also providing unparalleled player number and quick and easy travel between worlds.

It's highly recommend that if you intend to develop for the ODK that you familiarize yourself with the basics of the World Builder platform. In particular, sections that are useful to read are:

  • Morpheus networking

  • Difference from native Unreal

The ODK also provides a host of Otherside related content built on top of the World Builder. The majority of the functionality is provided within , which is located within the engine plugins of your ODK Unreal installation

Please peruse the provided content, and use the as means to explore what's available within the ODK.

Quick Start Guide

Welcome to the Otherside Development Kit!

If this is your first time here, we recommend setting up an example project from one of our templates and experimenting with what's possible.

Otherwise take a look at the plugin and templates that are provided with the Otherside Development Kit

It is HIGHLY recommended you read Msquared's documentation to familiarise yourself with some core concepts within the World Builder that the ODK builds on: https://docs.msquared.io/ The sections that are partcularily important are

  • Morpheus networking

Token Gating

Our guide to example the Token Gating system can be found here: We have three examples of Token Gating in the Boneyard Experience.

The clubhouse as pictured above gates entry for Bored Ape holders only, these is a good way of adding areas into your experience that are only accessible by owners of specific NFT collections.

The above shows the Toilet ontop of the clubhouse, we gate this as part of the quest "Drop a Deuce" which can be started via the Quest Giver desk. We gate entry to this Toilet unless the player has a Drop a Deuce token, this is a great example of how to incorporate token gating as part of a quest, where players can only access at a specific stage of the quest or once completed.

The above force field is just bound to requiring having 1 of each of the three coins scattered around the level (x1 bronze, x1 silver and x1 gold) its a good example of how we can leverage token gating until you have certain pickups or maybe items from a vendor.

Creating a Custom Character

In this documentation we've set up a few sections to go over:

- Quick overview of some common 3D terms and approaches

- Basic 3D asset specifications

- General Guides for how to create good character art for Otherside

- A full tutorial of creating a complete avatar collection.

- Overview of how to get your 3D assets to be available in the Otherside.

The Otherside and ODK is built on top of the Msquared network. Full documentation on their Avatar system here → . This guide is meant as a QuickStart resource to guide ODK developers in creating a full NFT collection of Avatars.

While there are many ways to build assets for digital worlds, this information will present comprehensive guidelines on how to make sure you can build and maintain avatar collections for Otherside.

Making Avatars Available in the Otherside

After you have made all the 3D assets for an avatar collection there are four steps to follow to make them available for your holders in the Otherside.

1. Upload GLBs to a Publicly Available Location

To make avatars available to all your holders they must be publicly available. They will be downloaded from the storage controlled by you into the Otherside when a user requests an avatar.

Creating A Simple Avatar Collection

In this guide we will walk through the steps of creating an incredibly simple avatar collection...Boxie!

There are many tools for and techniques for creating 3D avatars, this guide will walk through some of the most straight forward approaches.

Prerequisites

  • Blender 4.5 LTS -

Getting setup with ODK development

Quick-start guide on how to download the ODK and start developing!

Access the download page and click "Install the Launcher".

  1. The ODK Launcher should open and start downloading the ODK.

  2. When the download is complete, the template selection window should open with the option to load an existing project or create a new project from the ODK template:

v6.0

Configuring Player Character Input Mapping Contexts

Date of change: 11/12/2024

Affected Features: Input

What’s broken and why?

The way we configure the default input mapping context on a character is changing to allow us to add IMCs to the base ODK player character.

How to fix it?

Using the Creator Portal

The creator portal is used to create the on-chain tokens and metadata for that token. This includes its name, description, image and any additional fields required for gameplay.


🔹 1. Open the Creator Portal

Go to: 👉

This web interface allows you to define and mint tokens for use in-game. ⚠️ Note: The portal only shows objects created by the currently logged-in user.


Rules of the Road

Some common sense things that we allow/don't allow with Custom Avatars and content in general:

  • Prohibited Content: Otherside bans the use of its platform to publish certain content, including items that infringe intellectual property rights, are stolen, are misleading, or violate laws and regulations. The company encourages users to report any content suspected of violating these rules.

  • Moderation Actions: When prohibited content is reported or detected, Otherside may:

    • Limit the visibility or feature status of the content.

ODK Templates

To provide starting points, and examples for how to build functionality, the ODK is shipped with a handful of template projects.

ODK Empty Template: This template provides the minimum content required to startup an ODK project. Use this project once you're familiar with the ODK process and workflows, and are looking to start a new project.

ODK Boneyard Template: This template provides examples usages of some of the content.

ODK Combat Template: This template provides examples of how you can implement a high-fidelity shooter within the ODK.

Disable buying, selling, swapping, or transferring of the item.

  • Remove Otherside’s fee from such content, so it isn’t monetized.

  • Fully delist tokens, NFTs, or collections and hide them from discovery if the content poses user harm, making them inaccessible on the platform.

  • Intellectual Property Infringement: Otherside follows laws such as the DMCA for copyright-related removals. Reports must be submitted with specific details, and content is taken down upon verification. Repeat infringers risk losing their accounts.

  • Misleading Content: The platform prohibits content that impersonates brands, celebrities, or other collections in ways that mislead users, including fake verification marks or harmful links. Collections must clearly state if they are unofficial, and users can report misleading items.

  • Other Categories (Hate, Exploitation, Spam, etc.): Otherside prohibits hate speech, spam, sexual exploitation, and similar harmful content. Users can report prohibited material directly from the platform.

  • Moderation Process: Otherside uses a combination of user reports, automated detection, and manual review by a specialized Trust and Safety team. The team employs tools like image recognition and collection analysis to flag or remove problematic content and responds to reports, aiming to do so within a certain time frame.

  • Transparency and Enforcement: Otherside explicitly reserves the right to moderate, delist, or hide content and disable features at its discretion for any violations of policies laid out in their Terms of Service.

  • The ODK is further subject to the terms found at https://yuga.com/terms.

    Difference from native Unreal
    Actor pooling
    Actor pooling
    ODK plugin
    Template Projects
    ODK Plugin Content
    ODK plugin
    The Templates page within the ODK launcher

    🦴 Base Skeleton for Otherside

    Key Concepts for 3D Characters
    Technical Specifications
    Asset Production Best Practices
    Creating A Simple Avatar Collection
    Making Avatars Available in the Otherside
    Avatars | Morpheus Docs
    212KB
    ODK_Base_Skeleton.fbx
    Open
    2. Create and Upload MMLs to a Publicly Available Location

    The MMLs contain some information about what to do with an the GLB. A simple MML that only points to a single GLB would look like:

    You should create 1 MML for each token in your collection.

    3. Update collection metadata to include an MML Field

    Each token needs to have an additional field in it's data call MML. For example if I had a fake collection called Boxie it might have metadata that looks like:

    This is what allows Otherside games to pull in a users avatar that they have in their wallet.

    4. Contact Yuga to Whitelist your Contract

    Currently collections are added via a whitelist once steps 1-3 are complete we can review and add the contract address to our whitelist to make the avatars available for anyone who has them in their wallet.

    Please fill out this form to initiate a review https://forms.gle/NNzGR22wVgBJZECbA .

    Once your form is filled out, please join the Otherside discord to open a ticket and we'll use that channel to relay any feedback. If you do not open a ticket, we won't be able to integrate your collection into Otherside, so please make sure this step is completed.

    <m-character src="https://fake-collection-api.io/glbs/1.glb"></m-character>
    {
            "id": 0,
            "name": "Boxie 0",
            "image": "https://fake-collection-api.io/images/token_id.jpg",
            "mml": "https://fake-collection-api.io/mml/token_id.mml",
            "attributes": [
                {
                    "value": "classic box",
                    "trait_type": "head"
                },
                {
                    "value": "metallic purple",
                    "trait_type": "upper"
                },
                {
                    "value": "dark",
                    "trait_type": "lower"
                }
            ]
        }

    MML Avatar Tool - https://github.com/mml-io/avatar-tools (only if completing the pipeline section)

    • node.js v20.x - https://nodejs.org/dist/v20.111/node-v20.11.1-win-x64.zip / https://nodejs.org/dist/v20.11.1/node-v20.11.1-darwin-x64.tar.gz

  • ODK Unreal Editor (optional for testing)

  • Resources

    https://www.blender.org/download/lts/4-5/
    212KB
    ODK_Base_Skeleton.fbx
    Open
    4MB
    avatar_demo.blend
    Open
    If this is your first time developing for the ODK, we recommend you use the "ODK Boneyard Template" as the starting template. Click on the template to create a project from it
  • Give your project a name, and click "Create New Project". The template will download and open automatically.

    The ODK Boneyard Template
  • Once open, you're able to use Play-in-Editor to test and develop your experiences!

  • Creating an online world

    It only takes a quick few steps to create a deployment from your project content that others can join.

    1. Click the "Upload Content" button within the editor dashboard

    2. Give your world a description, and ensure that the Boneyard map is selected. Then click the "Upload" button

    3. Your project will then be cooked, packaged and uploaded to the Otherside dashboard. In future uploads, this process will be quicker as not all content will need to be recooked. Once complete, click the "View upload in dashboard" button

    4. That will take you to the ODK dashboard, which will display your world's uploads. You can then click "Launch World" to create a deployment from your uploaded content, and share that with others to join

    Dashboard
    ODK Templates View
    You should override GetDefaultInputMappingContexts in the following way. Create a new local variable in your function and configure it to have the extra input mapping contexts you need.

    How to test it?

    Ensure your input mappings are being used in PIE.

    BPM_ODK_PlayerCharacterBase > Server_TransferObjects

    Date of change: 21/01/2025

    Affected Features: Transfer of Data Objects to player collection

    What’s broken and why?

    The event Server_TransferObjects has been deprecated and should be replaced with Server_TransferDataObjects. This event allows for a client side callback with a success/failure message.

    How to fix it?

    Please use the BindToDataTransferComplete function and the Server_TransferDataObjects event instead of the Server_TransferObjects event. When calling the BindToDataTransferComplete function it will return the current auth user ID and a TransferID that will be required for the server transfer.

    How to test it?

    After replacing the deprecated events in your blueprints, you will receive a callback with a success or faulure message.

    🔹 2. Add a Granting Wallet

    To mint or grant tokens, you need Gas—a small transaction fee.

    • Scroll to the bottom of the page

    • Paste in a granting wallet address (your producer will provide one)

    • Click Save

    💡 Improbable provides internal wallets that are pre-funded to allow token operations during development.

    🔹 3. Create your token

    Click Define new Object. This will take you to the Object creation flow. The objects are created on a per project basis. This means that you will have to select your project from the drop-down before the template buttons are activated.

    🔹 4. Granting your token

    Once your token has been defined, you'll want to start crediting it to deserving users. There are two ways to achieve this currently:

    • Within the Creator portal you can use the Grant button to grant an instance of your token to any provided wallet.

    • Within Unreal you can use the RequestGrantToken on the user's BPMC_ODK_WalletComponent to grant the token to the user. Item Id is the token id.

    https://launch.otherside.xyz/creator

    FAQ

    1. How do I get access to the ODK?

    Please reach out to your representative at Yuga who can organise your onboarding, as part of this process you need to supply all emails that will need access to the ODK tooling.

    Once you have been given access please follow the quick start guide which can be found here: https://docs.otherside.xyz/odk-documentation/documentation/quick-start-guide/getting-setup-with-odk-development

    1. When setting up a new project I get this error "Error getting space required: Error: Expected string at Index 6"

    Please log in with Otherside and NOT Msquared as shown in I am unable to Login to the ODK Launcher

    1. I am unable to Login to the ODK Launcher

    Please ensure that you are logging in with the email that you have sent to your Yuga contact to get whitelisted. If this step has been completed please ensure that you are logging in via Otherside and not Msquared on the Launcher. There is also a known issue where it may take two attempts to login. If you are still facing problems then please reach out to your Yuga representative.

    1. I am unable to enter admin mode within the engine.

    Please review the

    For any role that you want to have access to the Inspector (historically just Director) add the Capabilities.Morpheus.InspectorEnabled capability to the GrantedCapabilities field within your data table for those roles.

    1. When I open the Launcher it get stuck on the spinner

    First you want to confirm that you have the latest launcher installed which you can download from the dashboard, then if that doesnt resolve it please follow the guide for clearing credentials here:

    1. I am unable to sign in via the Editor

    Please follow the guide here:

    If you continue to encounter sign-in failure, please collect your unreal logs and share them in your ODK support channel.

    1. I am unable to open the web overlay within my experience/or my account details are incorrect on privy on the overlay/ or I am getting errors when opening the overlay.

    With the move to using Privy the way you access deployments has changed, if you go in via the Otherside Dashboard you won't go through the Privy auth process, so when access deployments please append the following link with your WorldID and ProjectID. Please note you don't need the [] on either project ID or world ID

    We've noticed on rare occasion the web browser can fail to process the content URL that it's attempting to display. We're continually working to resolve these types of errors. If you do encounter this error, please toggle the web browser to resolve (pressing Tab). If the error persists, please raise it within your ODK support channel.

    Technical Specifications

    Below are the technical requirements and supported features for avatars in Otherside. Following these ensures your assets import cleanly and display correctly.


    File Requirements

    3D Asset

    • 3D Model Format: GLB (binary glTF)

    • Texture Formats: JPG or PNG

    • Shading Model: PBR (Physically Based Rendering)

    MML File

    • UTF-8 encoding


    Supported Texture Channels

    Only the Basecolor map is required. Other maps (Normal, ORM, Emissive) are optional but can greatly enhance visual fidelity.

    👉 Important: Only include Emissive if your avatar actually has glowing elements.

    Breaking Changes

    Please find the breaking changes for each ODK version listed as a sub page to this one.

    ODK has M2 as a dependency, so please always refer to MSquared Breaking Changes when upgrading versions.

    Below is the ODK to MSquared version guide

    ODK Version
    MSquared Version

    v9.2

    v38.1.3

    v9.1

    v38.1.2

    v9.0

    v38.1.2

    Avatars

    As part of being part of the ODK ecosystem, you get some fairly extensive Avatar management out of the box. There's a few pieces to what's available, that we'll go through here.

    What avatars are available?

    Currently we've proved avatar implementation for the following collections:

    1. Bored Ape Yacht Club

    2. Meebits

    3. Kodas

    4. Moonbirds

    There are also individual avatars meshes available for the following collections:

    1. Mutant Ape Yacht Club

    2. Voyagers (Otherside)

    If you log into an experience with an account that has a wallet linked with a relevant token, you'll be able to select that avatar as your in-game character from the .

    Both the Koda and Moonbirds collections also have unique Animation Blueprints to allow custom animations (beyond the standard Otherside animations set).

    More collections will be brought online as the ODK matures.

    How are the avatars implemented?

    To provide dynamic access to a vast array of dynamic characters, at runtime we download and encode the relevant character meshes from their definitions. This implementation allows us to utilize our Carnival renderer which allows us to scale the number of unique characters in a world at once to 10,000+ !

    How is it implemented within the ODK?

    Current supported avatar types are tracked within the BPE_AvatarType enum within Unreal.

    The flow for selecting a player is as follows.

    1. Retrieve the player's current selected avatar token via the KVStore within BPC_ODK_ProfileOverlayHandling .

    2. Once the avatar type has been verified, request the character load on BPMC_AvatarInfo. This component manages loading the specific GLB on the M2M_CharacterAssetComponent as well as replicating the avatar type information out to observing clients.

    3. BPM_ODK_PlayerCharacterBase listen for avatar info updates, and then calls

    If you're interested in observing when a player's avatar type changes, then bind to the AvatarTypeUpdated event on the BPMC_AvatarInfo component.

    If you're interesting in modifying the default avatar behaviour on clients, then overriding the RefreshAvatarTypeAnimState on your ODK character will allow you to react to the default avatar behaviour.

    Max Texture Size: 2048 × 2048

  • Max Tri/Poly Count: 50,000

  • Max Avatar File Size (on disk): 20 MB. Recommended File Size: <10 MB.

  • Physical Size: Max Height 2 meters Min Height 1 meter

  • Emissive

    Defines areas of the model that emit light (e.g., glowing eyes, neon strips). Use sparingly, only when necessary.

    sRGB

    E

    Channel

    Description

    Colorspace

    Token

    Basecolor

    Defines the visible color and pattern of the model. This is the only required channel.

    sRGB

    BC

    Normal

    Adds surface detail without increasing polygon count. For example, wrinkles in clothing or grooves in armor.

    Linear

    N

    ORM (Packed)

    A single texture that combines three grayscale maps: Occlusion – Adds baked-in shadows and depth. Roughness – Dark = shiny surface, Bright = rough surface. Metalness – Dark = non-metal (plastic, paint), Bright = metallic. Packing these maps into one texture reduces file size and improves performance.

    Linear

    ORM

    v8.2

    v37.0.0

    v7.0

    v34.1.1

    v6.1.0

    v33.0.3

    v6.0.0

    v33.0.1

    v5.0.0

    v31.0.0

    v4.0.0

    v30.0.0

    v3.1.0

    v29.0.1

    v3.0.0

    v28.0.1

    v2.0.0

    v26.0.0

    v1.1.0

    v25.0.0

    v1.0.1

    v24.0.0

    v1.0.0

    v24.0.0

    RefreshAvatarTypeAnimState
    on all clients whenever an update is observed. This functionality is currently responsible for setting the correct ABP + crowd details for the avatar.
    Web Browser Avatar tab
    GLB
    breaking changes
    https://docs.otherside.xyz/odk-documentation/documentation/quick-start-guide/faq/guide-on-clearing-credentials
    https://docs.otherside.xyz/odk-documentation/documentation/quick-start-guide/faq/guide-on-clearing-credentials

    Role Promotion

    Role promotion can be used to change the game experience for an individual player. It can be used to give some players extra abilities or visuals. In the Meebits Combat Gym, the player who has the highest score on each team gets promoted to the "Titan" role. This role simply increases the player's size. This serves as a demonstration as to how a role can be granted to a player. Inside BPM_TitanPromoter, this simple logic is run on the server in order to promote a player to a "Titan".

    Setup Guide

    This guide walks through how to set up a new flow.


    🎭 Step 1: Place the Task Flow Actor

    Start by placing a BP_ODK_TaskFlow actor in your level.

    This actor is responsible for:

    Characters

    Let's talk about the characters who you'll run into on Otherside

    Kodas

    Kodas are the main characters of Otherside. They use Soma to create forms and Chaos to take them away. If someone owns a Koda, they can play as that Koda.

    In the Resource game, Kodas have special uses that gives players advantages.

    Voyagers

    Kodas create Voyagers out of Soma or Chaos. They are the exoskeletons for these two forces, enabling them to move around and explore Otherside.

    Soon, these Voyager exoskeletons will be upgraded to be customizable exoskeletons for Soma and Chaos.

    Custom Characters

    In the world of Otherside, Kodas and Voyagers work together so that people can bring in their own characters and build their own worlds.

    In this documentation, we'll sometimes refer to Custom Characters as Third Party Avatars (not created by Yuga Labs or one of its partners).

    In the mainline Otherside experience, the use of custom characters must be unlocked.

    In the ODK experiences, all custom characters are available to experience designers. While experience designers may place some limits, the core technology is designed for maximum interoperability.

    There are only 10,000 Kodas.
    There are only 10,000 Kodas

    Moderation

    The ODK provide text and voice chat moderation services that allow you to moderate users within your worlds. The moderation page is available here.

    Please reach out to Yuga to request access to the moderation page.

    Trouble Shooting

    MML format is correct and the GLB is valid but it is not loading in the Otherside.

    Double check that the MMLs are encoded with UTF-8 and not a something like UTF-8 with BOM.

    Avatar prepared but not loading in ODK/Otherside

    Check the output logs in editor tab for more details on possible errors. - Make sure the rig uses the exact bone naming and positions from the reference skeleton. - Do not include any additional bones or unsupported structures. - Make sure your GLB files are publicly accessible (avoid CORS issues)

    ODK Widget

    The ODK widget has some useful built in functionality. Defining IMCs You can use InputMappingContexts to define the IMCs that should be enabled when this widget is Activated. All other IMCs will be disabled. When the widget is Deactivated, the IMCs enabled previous to the widget being Activated will be re-enabled. UI Mode You can define the UI mode that widget needs when Activated. Generally, if you need some button input for your widget, you will want to use input mode GAME_AND_UI. Getting the last input type GetLastInputType can be used to find out what the last input type was (Gamepad/Keyboard etc).

    Combat Template

    Widget Input

    You can derived your widgets from WBP_ODK_Widget. The InputMappingContexts property WBP_ODK_Widget can be used to configure which IMCs should be active when the widget is on screen. This will use the BPC_ODK_InputComponent to push a new IMC state onto the stack when the widget is displayed on screen. When the widget is closed, the state will be removed enabling whatever IMCs where active before the widget was opened.

    ODK Input Management

    Initializing the flow

  • Coordinating triggers, conditions, tasks, and actions

  • Tracking and updating the flow's progression

  • It is placed in the level as an actor so that only tasks that can be completed in the current map are shown on the HUD.

    You’ll configure a few key variables on this actor:

    • WaitForBootflowToFinish – Whether to wait for bootflow before initializing

    • TriggerDelay – Optional delay (in seconds) before the flow begins

    • FlowTrigger – The executor that kicks off the flow (see below)

    • TaskFlowDataAsset – The asset that defines your actual tasks and logic


    🎯 Step 2: Assign a Flow Trigger

    The FlowTrigger variable must be set to an executor that inherits from BP_ODK_FlowTrigger_ExecutorBase or BP_TaskFlowTrigger_TokenId when setting up a Quest.

    The InitializeTaskFlowExecutor event should be used as you would BeginPlay, to set up the trigger and make bindings etc.

    Once your trigger logic finishes, it must call FlowTriggerCompleted

    This tells the system:

    • Where to start (via Task Index, e.g. resume from save)

    • How to proceed (StartFlow, RetryFlow, CompleteFlow)

    You can use an existing trigger executor or make your own to suit your feature.


    📦 Step 3: Create and Configure the Task Flow Data Asset

    Create a new data asset that inherits from PDA_ODK_TaskFlow.

    This defines the structure and logic of your flow. Inside the asset, configure:

    • Flow Name

    • Conditions – (flow-level)

    Example of a condition executor
    • Tasks – An array of individual task executors

    • Retry – Optional executor to handle retry logic

    • Actions – Optional logic fired at various moments

    • Custom Details – Optional data for flow-specific needs

    This asset is what makes the flow dynamic and reusable.


    🧩 Step 4: Define Each Task

    Each task should be a class derived from BP_ODK_TaskFlow_Task.

    You’ll configure:

    • Whether the task is enabled

    • Optional conditions (task-level)

    • One or more Complete Task Triggers

      • This is the most important step. The Complete Task Triggers contain all the logic to define if the task is complete

    • Optional Startup Delay

    • Optional Actions tied to task lifecycle

    Each task should:

    • Implement InitializeTaskFlowExecutor()

    • Call TaskTriggerExecutionComplete() when finished


    🔁 Step 5: Add Retry Logic (Optional)

    If your flow should support retries (e.g. when a player fails, cancels or completes the flow), create a retry executor that inherits from BP_ODK_RetryTaskFlow_ExecutorBase.

    This executor:

    • Is triggered after a FlowTrigger completes with a retry state

    • Should call RequestRetryTaskFlow() when ready to restart

    You can implement logic like displaying a retry prompt or auto-restarting silently.


    ⚙️ Step 6: Add Actions (Optional)

    Actions are “one-shot” events that happen at defined flow or task points.

    To create one, derive from BP_ODK_TaskFlowAction_ExecutorBase, and implement InitializeTaskActionExecutor

    Attach them to:

    • Task-level or flow-level lifecycle events

    • Visuals, sound effects, UI, cleanup, etc.

    These don’t block progression and are never cached.


    🧪 Debugging

    Enable verbose output with the live config value ODK.TaskFlows.LoggingEnabled

    This will print task and flow events to the log, which is helpful when troubleshooting setup issues.

    Team Select

    Feature Overview

    The team select UI allow players to change team. Use the "N" key to open the UI.

    Technical Overview

    All functionality can be found in WBP_TeamSelect.

    Key Concepts for 3D Characters

    Key Concepts for 3D Characters

    Before you make a character, it helps to know a few basics. Don’t worry—you don’t need to be an expert! These ideas will just give you the vocabulary to follow the steps later.


    Visual Effects

    How to add visual effects to characters via ODK components

    The ODK uses the BPC_ODK_NiagaraEffectsManagerComponent to play local and replicated effects on characters. The BPMC_ODK_GucciTrailHandler is an example showing how we add and remove effects to characters that own a Koda pendant NFT.

    Inside BPMC_ODK_GucciTrailHandler::TryApplyGucciTrailEffect, we use the BPC_ODK_NiagaraEffectsManagerComponent to add the Gucci Niagara visual effect to both characters feet using AddNiagaraEffect. It's parameters are as follows: Niagara System - The Niagara system to add. Socket - The socket on the character mesh to attach the Niagara system to. Relative Transform - The transform which should be applied to the Niagara system relative to the socket. This lets you offset/rotate or scale the visual effect in relatioin to the defined socket. Is Persistant Effect - If true, the visual effect will play continuously until removed. If false, the effect will run once and then be automatically removed. Replicate - Should the effect be visable to other players as well.

    Wallet Views

    Overview

    Wallet views allow you to filter the tokens in your wallets. If you want to get all koda cam photo tokens, you can use wallet views for that.

    To get tokens in a view, simply call GetWalletView on the BP_ODK_WalletWorldService. You will need to pass in the WalletViewClass for the wallet view you care about.

    v8.3

    Live Config Changes

    Date of change: 16/07/2025

    Affected Features: Users with existing ODK projects

    What’s broken and why?

    Currently ODK schema is embedded within the `ODK` section within the project.schema. For legacy reasons, the ODK schema was instantiated with each template, rather than stored with the plugin content itself. To ensure you're operating with the latest, correct ODK schema, please integrate the latest schema here into your project. This process will be corrected in the ODK v9 release so users don't need to manually manage this at that point

    Setting Up Blender

    If you haven't already downloaded and installed blender go ahead and do that now.

    We are using Blender 4.5 so all menu placement and controls will be aligned to this version of Blender. You are welcome to use another version, some things might have changed slightly in the program based on which version you are using.

    v9.4

    Morpheus Array Type Redefinition

    Date of change: 08/12/2025

    Affected Features: Morpheus Arrays

    What’s broken and why?

    Morpheus Arrays in BPs will no longer have their inner type default to integers.

    This is due to a refactor in the Morpheus Arrays inner type to integrate with Unreal's redirectors preventing crashes when redirected types are used as an Inner type to the MorpheusArray.

    How to fix it?

    UI Mode

    Documentation for ODK UI mode control

    When opening a widget or in other various scenarios, you may want to the mouse to become visable and allow the player to interact with widgets on screen using the mouse. To do this, use the blueprint function library functions: MarkContextNeedsUIMode and UnmarkContextNeedsUIMode respectively.

    You must provide a "Context" when calling these functions. This context represents the object who wants UI mode to be enabled/disabled. If multiple calls are made to MarkContextNeedsUIMode with different contexts, calling UnmarkContextNeedsUIMode once with one of the contexts will not disable UI mode. The system will always ensure the highest UI mode is enabled that at least on context needs.

    Physics Objects

    Feature Overview

    BPM_ServerPhysicsObject is a blueprint actor that can be derived from to create gameplay props that have server auth physics. This means that any player can interact with the object and have the physics affects get replicated to all other players. Examples of derived BPM_ServerPhysicsObject classes include: BPM_Dice, BPM_Ball.

    Character Input

    The default IMCs used by derived BP_ODK_PlayerCharacterBase assets can be configured by overriding the GetInputMappingContexts function. Simply return a list of IMCs with a configured priority. You can call the base class function to include the IMCs used by the base if you like.

    Vertices (The Dots)

    A vertex (plural: vertices) is a single point in 3D space.

    • Imagine pinning down corners on a piece of fabric—those pins are like vertices.

    • Vertices are connected to form edges (lines), and edges combine to make faces (triangles).

    • A mesh is made of thousands of these little triangles.

    • More vertices = more detail, but also a heavier file.

    👉 You can think of vertices as the “atoms” of a 3D character—everything is built from them.


    Mesh (The Skin)

    A mesh is the 3D shape of your character. Think of it as the “skin” made of many small triangles stitched together.

    • The mesh defines what your character looks like on the outside.

    • High-polygon meshes (lots of vertices) look smoother, but can be heavy and slow to load.

    • Lower-polygon meshes (fewer vertices) load faster, but may look blockier.

    • For games, we want a balance: lightweight but still nice-looking.


    Skeleton (The Bones)

    The skeleton, sometimes called an armature or rig, is a hidden set of bones inside your mesh.

    • Just like real bones, these control how your character moves.

    • When the skeleton moves, the mesh moves with it.

    • Without a skeleton, your character is a statue that can’t animate.


    Weighting (Skinning)

    Weighting (or skinning) tells the computer which parts of the mesh follow which bones.

    • For example: your hand mesh should follow the hand bone, but not the foot bone.

    • Weights are often painted in colors (blue = no influence, red = full influence).

    • This ensures smooth bending—like your elbow flexing without breaking the arm mesh.


    Textures & Materials (The Paint and Clothing)

    • A texture is a 2D image (like a picture) wrapped around the mesh, giving it color, details, or patterns.

    • A material tells the game how the texture should look—shiny, matte, transparent, etc.

    • Example: The texture is the “jeans fabric image,” while the material makes it look like real denim.


    GLTF / GLB (The Package)

    • GLTF is a standard file format for sharing 3D models.

    • GLB is the “binary” version—everything (mesh, skeleton, textures, animations) is bundled into one file.

    • Think of GLB as a zip file for your character, ready for upload.

    MML (The Description)

    • MML stands for Metaversal Markup Language. It's like an HTML file but instead of a website it is a 3D object or scene

    Tips for Success

    Here are some things to keep in mind as you build your avatar. Following these guidelines will save time and help your character look great once it’s in Otherside.


    1. Character Proportions

    Animations in Otherside are built for humanoid characters (like Apes or Voyagers).

    👉 If proportions are too far from human, animations may look strange.

    • Example: If your character’s arms reach the ground, a clapping animation will look broken.

    Guidelines for best results:

    • Overall height must be between 1 meter and 2 meters.

    • Head, body, and limb sizes should be similar to human proportions.

    • Stylized designs (bigger heads, slightly shorter legs) are fine, as long as the character still feels humanoid.

    ⚠️ Note: For characters like Kodas, Yuga created a custom animation set to match their proportions. Custom animations are not available for other collections, so sticking to humanoid shapes is key.


    2. Avoid Transparency

    Transparent objects are difficult to support and may not display correctly in all situations.

    • If possible, design around transparency instead of relying on it.

    • Features like glowing, metallic, or opaque materials are much easier to implement reliably.

    👉 You’ll have a smoother experience if you avoid transparency altogether.


    3. Work End-to-End and Iterate

    Building an avatar has many steps, and each character can present unique challenges.

    A great strategy is to:

    1. Start simple (a rough block out).

    2. Get it running in Otherside as soon as possible.

    3. Refine and improve in small steps.

    This way you’re always testing your work in context, and you won’t waste time polishing something that doesn’t export or animate correctly.


    👉 By focusing on proportions, smart material choices, and iterative testing, you’ll give yourself the best chance of success.

    1. Rough Block Out

    Start by building a basic silhouette of your avatar and attaching it to a skeleton.

    • This doesn’t need to be fancy—simple shapes like cubes and spheres are enough.

    • Weighting is easy here: you can assign each shape’s vertices to a single bone.

    • Export this rough version early to check:

      • Proportions (does it look human-like enough for animations to work?)

      • Skeleton setup

      • Scale and export settings

    👉 Spend time here adjusting proportions until it feels right. It’s much faster to test and tweak now than later.


    2. Material Testing

    If you plan to use special materials—like metallic surfaces, glowing parts, or transparency—test them on your block out first.

    This gives you a quick preview of how these materials behave in-game and helps you decide how to build the final look.


    3. Asset Planning

    Use the information from your block out and material tests to plan your actual assets.

    • Single avatar: Prepare reference images and refine your block out into the real design.

    • Avatar collection: Decide how traits will fit together (hair, clothing, accessories, etc.) and collect references for each piece.

    Good planning here saves a lot of time later in production.


    4. Pipeline Planning

    If you’re creating a collection, think about how traits will be combined into GLBs.

    • A good starting point: keep all the traits inside a Blender project.

    • Use Blender’s built-in Python scripting to export specific trait combinations into GLBs, guided by your metadata.

    This step ensures your process can scale smoothly from one avatar to many.


    5. Asset Production

    Now it’s time to create the real content.

    • Model and sculpt your meshes.

    • Bind (weight) them to the skeleton.

    • Create and apply textures.

    If you’re just making a single avatar, all of this can happen directly in Blender. For larger projects, you may use a more complex pipeline, but the goal is the same: by the end of this step, you’ll have meshes skinned to the skeleton and textures ready for export.


    6. QA (Quality Assurance)

    Always test your avatars before release in an Otherside/ODK map.

    • Check weighting (do joints bend smoothly?).

    • Test animations (does the character walk, run, and emote correctly?).

    • Verify materials (are they displaying as expected?).

    At Yuga, we use an internal testing map where multiple models can be loaded with different animations applied. This helps us quickly spot issues and fix them before launch.


    👉 Following this process will help you avoid surprises later and give you confidence that your avatar is ready to shine in Otherside.

  • Find the broken BPs containing a Morpheus Array

  • Re-pick their correct Inner Type.

  • Re-compile the blueprint and save it.

  • How to test it?

    Make sure Morpheus Arrays are working as expected and all blueprints are compiling fine.

    Team Select UI
    https://www.blender.org/download/lts/4-5/
    Example of GetInputMappingContexts
    55KB
    boxie_metadata.json
    Open

    Analytics

    ODK provides an out of the box analytics integration with Mixpanel. You're able to register your own Mixpanel project with Unreal, or reach out to Yuga to be added to their ODK Mixpanel project.

    To enable Mixpanel analytics for your world you need to:

    1. Add BP_ODK_Mixpanel_AnalyticsWorldService to your additional world services within your world settings.

    2. Add a mixpanel project token to live config at : project : ODK.Analytics.MixpanelProjectToken

    How to fix it?

    Retrieve the latest `project.schema.json` from the ODK. Accessible here, or within any v8.2 template. Merge this file with the existing project.schema.json that lives at <project_workspace>\Config\LiveConfig\Schemas. If you haven't made any local project edits, you can safely just replace the file, otherwise you should merge the content with your local changes.

    Editor Sign-in Changes

    Date of change: 16/07/2025

    Affected Features: PIE session

    What’s broken and why?

    The way you login needs to be updated to use our new authentication system

    How to fix it?

    For any existing project, you'll need to modify your authentication settings. 1. Open to Editor Preferences -> General: Sign In Settings 2. Expand Per Client Sign In Settings 3. Modify each client's sign-in settings to: "Custom" : "https://o7e.dev/api/editor/login" 4. Restart your editor

    How to test it?

    Start a PIE session and ensure your client can connect to your local deployment.

    29KB
    project.schema.json
    Open

    AddNiagaraEffect returns an Id for the effect that should be used when attempting to remove the visual effect. An example of this can be seen inside BPMC_ODK_GucciTrailHandler::RemoveGucciTrailEffect whereRemoveNiagaraEffect is called with the relevant EffectId.

    Creating Wallet Views

    You can define new wallet views by creating a new child of BP_ODK_WalletView. Then all you need to do is override IsRelevantToken on the wallet view to filter for tokens your view cares about. Here is an example of koda cam photos:

    Example filtering logic

    IMPORTANT: You can create your own custom views but should only do so if there is not an existing view that already filters in the same way. Having duplicate filter logic in two views will reduce the efficiancy of the system unnecessarily.

    Checking Tokens

    You can use IsTokenDataRelevantToView to check if a token fulfils the filter criteria of a wallet view. In the case below, we check if the token represents ownership of an emote.

    Binding to Wallet View Events

    You can bind to event on wallet views to be informed when tokens of a particular type change in your wallet. In the case below, we want to be informed whenever koda cam photos have been added or removed from a wallet. This particular event batches adds and removes together so that over a given frame, if there are multiple add and remove token events, the event will only be broadcast once.

    Example of grabbing tokens in a view
    Handling UI Mode Changes

    MarkContextNeedsUIMode is a wrapper around the BPC_ODK_UIModeComponent component attached to BP_ODK_PlayerControllerBase. The BPC_ODK_UIModeComponent is not repsonible for the effects of UI mode change (showing the cursor for example). It is simply responsible for the maintance of the current UI mode state. BP_ODK_PlayerControllerBase handles the result of UI mode changes here:

    Handling of UI mode changes on the BP_ODK_PlayerControllerBase .

    If you require different logic to be perfomed when the UI mode changes, you can override the functionality is HandleUIModeChangeRequest.

    Example of function usage
    Technical Overview

    Forces are applied to the BPM_ServerPhysicsObject using the server RPC Server_ApplyImpulse. You can use this to apply a regular impulse and angular impulse to your object (move the object and spin the object).

    Inside BPM_Dice, you can see Server_ApplyImpulse is used to apply a random impulse and angular impulse to the dice when the dice actor is interacted with.

    BPM_Dice example

    ODK Selfie Cam

    Selfie Cam

    The Selfie Cam example is made up of several modular pieces to showcase the various features required for the end-to-end flow.

    At a high-level, the system is composed of the following key components and functions:

    BPC_SelfieCamComponent

    A component attached to the BP_ODK_PlayerCharacterBase, responsible for toggling selfie camera mode. It spawns the BP_SelfieCameraActor.

    BP_SelfieCameraActor

    This actor contains a CineCamera component. Once spawned, it attaches to the player's hand socket and communicates its location to the Animation Blueprint via a Blueprint Interface. This enables the Animation Blueprint to sync the hand's location using an IK chain. The actor also manages the Camera UI and binds to its events.

    BPC_HighResShot

    Another component on BP_ODK_PlayerCharacterBase, with a Take Screenshot event that allows specifying location, format, and resolution. It can display an optional preview widget, which is customizable and works with a Blueprint Interface for swapping. The component also includes the following Event Dispatchers: OnHighResShotSaved, OnFlashShown, OnFlashClosed, and OnPreviewShown.

    BPFL_ScreenshotTools

    After a screenshot is saved, the image can be uploaded to the user collection using the User Collection Upload Screenshot function from the BPFL_ScreenshotTools library.

    Using the Selfie Cam

    Important Note: The BP_AttachmentManagerODK singleton must be defined in your level's world settings > Non Morpheus singletons section. The Koda Cam system relies on this manager to perform mesh attachments to both character and crowd actor instances. If the singleton is not present, attached meshes will fail to render, and the avatar selfie stick functionality (for short-arm configurations) will not execute correctly.

    The Selfie Cam can be activated using the IMC_SelfieMode Enhanced Input Mapping with the following actions:

    IA_ScreenshotCamera_Toggle - Enable/Disable the Selfie Camera

    IA_RotateCamera - Rotate the camera when active

    IA_ZoomCamera - Changes to field of view to simulate camera zoom

    IA_SelfieMode_TakeSnapshot - Save the image to disk

    Live Config

    The selfie cam is capable of saving high resolution images to disk and uploading them. This could lead to several large files being uploaded by multiple users. To mitigate any risk, several Live Config values have been added:

    ODK.SelfieCam.Enabled - Allows for disabling the activation of the Selfie Cam. If this is set to false, when a user presses the toggle shortcut, the BP_SelfieCamActor will not be spawned. ODK.Collections.AllowImageUpload - Users will stil be able to use the Selfie Cam, but the Image Upload option will be disabled.

    Material Setup And Testing

    In this section we will go through how to properly setup a material in blender such that it exports properly to the GLB.

    There is extensive information in the Blender docs about this if you want to read more: https://docs.blender.org/manual/en/4.5/addons/import_export/scene_gltf2.html

    The first step we want to take is opening up Preferences

    Navigating to Add-ons and then glTF 2.0 format and enabling Shader Editor Add-ons

    This will allow us to export occlusion maps if we have them.

    Switch to the Shading tab to begin making our first material.

    Select a mesh in the viewport and click the New button on the top of the Shader Editor window to create a new Material. I am going to rename this material BaseMaterial.

    Let's add a new node glTF Material Output we won't need it immediately but we can get it setup now in case we need it in the future.

    Just to test how this works let's make this material chrome like.

    • Adjust Metallic up to 1.0

    • Adjust Roughness down to 0.1

    Then export the glb, run it through the avatar web tool and set the path in the ODK Unreal Editor to the newly downloaded GLB.

    Boxie now has a chrome head!

    Any specific material settings should be tested at this point to help inform what values any texture maps you create should follow.

    Note about Transparency

    Currently the avatar web tool removes any transparency from materials. If you want to use transparency you'll have to use the CLI tool that we will cover in the pipeline portion.

    Transparency is pretty limited in game for avatars. There are two types that are supported.

    • Dithered - well suited for decals or cutting holes in geometry.

    • Blended - This can create semi-transparent materials however it does not support reflections so materials like glass are hard to recreate. Additionally there can be some Z-Buffer issues when using this method so if this is enabled the entire object that has this material applied to it should be expected to be transparent.

    You can switch between these two settings in the Details panel in the Materials section in the dropdown Render Method

    Asset Production and QA

    We cover best practices for asset production and QA in Asset Production Best Practices.

    For this simple example the final Blender file ended up looking like the images below. All textures, meshes, and materials were made simply so I could finish this guide in a reasonable amount of time.

    QA

    The QA process you take depends greatly on how your collection is structured. I would recommend taking the approach for review we did in the section and adapting that for your collection. You could:

    • Create a UI in engine to switch between avatars either via local files or use the load GLTF from URL node and have them uploaded to a public location.

    • Set the blueprint to cycle through all avatars or a subset of avatars and render that to a video file from unreal to review the avatars.

    If working with a large collection it helps to determine what the smallest subset of avatars represent all traits, and then add some spot checking of random avatars to hedge against faulty assumptions in the minimum trait list.

    Boneyard Template

    The boneyard is a space that the ODK development team used to develop ODK functionality and serves as a space where we can showcase example content and use cases to the ODK community.

    All content within this space should be used as an example for how we have achieved aspects of functionality.

    BPM_ODK_Example_PlayerCharacter

    The companion Morpheus Actor of BP_ODK_Example_PlayerCharacter. This Blueprint extends from BPM_ODK_PlayerCharacterBase and should be used for all replication logic within your world.

    BP_ODK_Example_PlayerCharacter

    This Blueprint extends from BP_ODK_PlayerCharacterBase and should be used to add your own character logic.

    BP_ODK_Example_PlayerController

    This Blueprint allows easy extension of the player controller. It should be used when you wish to inherit M2 base functionality while also adding your own logic.

    Wallets

    Documentation regarding wallets

    Our wallets system allows developers to interact with a player's owned (or delegated) tokens. BP_ODK_WalletWorldService and BPMC_ODK_WalletComponent are the main classes you may need to interact with.

    BPMC_ODK_WalletComponent can provide you with the WalletAddress fo the player. BP_ODK_WalletWorldService can be for most functionality regarding tokens.

    For any wallet that's associated with a user's account, we automatically listen for all tokens within the wallet, and store those details within the BP_ODK_WalletWorldService . Any dynamic changes are also tracked at runtime, and you can bind to the following events to respond to token changes:

    • OnTokenAdded

    • OnTokenRemoved

    • OnTokenBalanceUpdated

    For more sophisticate token handling, it's recommend that you utilize .

    Token Gating

    Used to lock features behind definable conditions, often regarding token ownership.

    An example of a Token Gate

    In-world gating uses the base class BPM_GatedAreaBase.

    Note: In order to use this in the editor, the blueprint needs access to profile details, therefore, you must be signed in and not in offline mode.

    This can be done by opening Editor Preferences > Sign-In Settings and adding your credentials.

    This feature intends to allows devs to limit access to gameplay. Players are limited on a condition. This condition usually concerns token ownership, however, the system allows for completely generic gating. BPM_ODK_GatedArea_Plane is a fully working example that you can use to start gating access to areas of your map. If you plan to create you own type of gated area, we recommend looking at this asset to understand the workings of the system. It has a property GateCondition that can be used to configure who can navigate into an area.

    Gating Conditions

    Gating conditions can be added to the gated area actor by configuring the GateCondition property in the details panel of the BPM_ODK_GatedAreaBase. In the following example, the gated area is gated on whether a player has any token on the ethereum block chain.

    There are many types of gate condition that are defined in the ODK. The BP_ODK_GateCondition_AND and BP_ODK_GateCondition_OR are key conditions that allow you to combine conditions together for more complex gating. The following shows a gate condition that requires ownership of both a bored ape and a mutant ape (defined by giving a contract address).

    If you can not find the condition you need defined within the ODK, you can create your own condition by deriving a new object from BP_ODK_GateCondition and overriding the IsConditionSatisfied function in your new asset.

    Arcade Machine

    The arcade machine is an example of how you can link to an external URL via the web browser. In this example we link to TopTrader.xyz which is done by using the blueprint BP_Interactable_OpenURL and calling OpenWebBrowser with the desired URL on the BP_WebBrowserWorldService

    Toptrader.xyz being opened via the Web Browser

    Coins

    Coins in the Boneyard are examples of how you can implement collectables on chain, these pickups grant players tokens to their wallets.

    BP_ODK_PickupExample is an example blueprint where you can see how this is achieved, using the RequestGrantToken node we define a token to grant and the target will be defined by the WalletComponent that will contain the address of the players wallet.

    Metaverse Markup Language

    Overview

    This is a curated list of resources () for Metaverse Markup Language (MML): tools, docs, editors, and more.

    is Metaverse Markup Language, developed by Msquared.

    Quests

    🧠 Conceptual Overview

    The ODK Quest system builds on top of the Task Flow system to offer a blockchain-integrated questing experience. Each quest is defined by a set of sequential tasks, with a unique on-chain Fungible Token acting as both the activator and the progress tracker.

    A quest typically looks like this:

    1. The player is granted a Quest token to begin.

    Authentication

    Glyph

    Otherside uses a user-friendly wallet called . Glyph simplifies web3 onboarding for both users and developers by using traditional web2 methods of account creation (ie. using a gmail account).

    Glyph facilitates login, wallet integration, fiat onramping, and onchain actions with gas sponsorship. Built for developers, it's scalable and easy to integrate.

    It's built on Privy technology which allows for common SSO's to be used in the sign up process like Google. More information about the service can be found here:

    What do we use Glyph for?

    We use Glyph in the Otherside to allows users to seamless create wallets that they can use within the experience, Glyph also allows linking to third party wallets e.g Metamask. Alongside this we also use Privy to manage user accounts within the Otherside Platform.

    To create an account, please first reach out to your Yuga support contact, and provide your (and your teams) email address details. We can then credit them with the appropriate permissions. Once complete, please browse to the to create your account.

    To create an account, or login. Click the "Login" button, and then enter the email details you provided above. You'll be sent a confirmation code to your email to validate your login.

    ODK Notifications

    The ODK notification system allows for developers to display notifications on a players HUD.

    The Notification Singleton

    By default, the BPM_NotificationsSingletonODK singleton is set in the World Settings. This can be extended and replaced to allow for custom functionality. The singleton serves as a notifications manager, broadcasting notifications upon receiving a request.

    ODK Video/Millicast Screens

    Documentation for ODK Video/Millicast Screens

    The video screens in the ODK are intended to be a seamless solution for playing video alongside important millicast streams. The BPM_VideoPlayerWithMillicastBase actor is a base class for these screens but will not function without a mesh to display the video on.

    BPM_VideoPlayerWithMillicastCurvedScreen is an example of how the base class can be extended to show the stream on a mesh in the world.

    The important function to consider here is “GetMeshComponentToDisplayOn” and should be overwritten to define a static mesh component and MaterialIDs

    In this example, the video is displayed on the curved video screen mesh and assigned to material IDs 0 and 1. This allows for the image to be shown the correct orientation on the back side of the mesh.

    Guide on Clearing Credentials

    If you are having problems signing in within the Unreal Editor then please follow this guide to try and resolve the issue.

    Step 1

    Ensure you are using the custom sign -in method in Editor > Sign-In Settings as shown below, this should be set too:

    Web Browser

    The overlay is a fully featured Chromium browser that is used in the ODK to render UI elements, this allows us to get all the benefits of web based development and also have a unifying set of menus across all ODK experiences.

    Web Browser functionality is controlled via the BPFL_ODK_WebBrowser function library, and the BP_WebBrowserWorldService .

    Pressing "Tab" within an experience will open the Player Profile overlay menu, which is available in every experience within the Otherside Platform. This overlay displays the Players progress and inventory as they explore the Otherside. This page consists of the following sub pages:

    Overview

    • This section gives the player a high level view of their progress within the Otherside, it will track key metrics, show their avatar, display badges and allow them to show off their prized NFT's on this page.

    Persistence

    Documentation regarding persistence in the ODK

    The persistence system in the ODK can be used to store data remotely. Examples of it's use might be: storing player progression or storing high scores for a mini game. On the backend, the storage system is simply a map of keys and values as string. You could imagine that for the high scores example, you might want to store the data using "high_scores" as a key and a json encoded object string for the data in this form.

    The Persistence Service

    The main class to be concerned with is BP_ODK_PersistenceWorldService. This world service provides functions in the following flavours: ReadValue, RegisterInterest and Save. The full list is: - ReadIntValue - ReadJsonValue - ReadStringValue - RegisterIntValueInterest - RegisterJsonValueInterest - RegisterStringValueInterest - SaveIntToPersistence - SaveJsonToPersistence - SaveStringToPersistence

    ODK Interaction

    Documentation regarding ODK interactions

    The ODK interaction system facilitates interactions between the player character and in world props.

    Basic Interaction Flow

    Player characters have the BPC_ODK_InteractionComponent attached to them. All interactable actors have a BPC_ODK_InteractableComponent attached to them. The BPC_ODK_InteractionComponent periodically polls interactable components to see which is the current

    Each task completion earns them an additional token.

  • When the player holds the full set, the quest is complete.

  • These tokens act as stateful markers for player progress, making it easy to track and synchronize quests across game sessions and platforms.


    🪙 Quest Tokens as Progress

    See Creating a Quest token

    ODK uses Fungible Tokens for quests, meaning players can hold more than one. Token balance reflects quest progress:

    • 1 token: Quest activated

    • 2+ tokens: Tasks completed

    • X tokens: Quest finished (X = tasks + 1)

    The token balance drives the task flow, ensuring only the correct task is active at each stage.


    🛠 Creator Web Panel

    Quests are created and managed via the Creator Portal.

    This allows you to:

    • Define quest metadata (name, image, description, group, etc.)

    • Add tasks and rewards

    • Mint and grant tokens to test users

    Once a quest token is minted, its Token ID (formatted like 33139:0xABC...:1) can be used in Unreal to connect the flow logic.


    🎮 In-Game Setup

    In Unreal, each quest is implemented using a BP_ODK_TaskFlow actor and a data asset derived from PDA_ODK_TaskFlow. Tasks should use BP_ODKTaskFlow_QuestTaskBase to ensure:

    • The task sends the next token upon completion

    • The system waits for the new balance before progressing

    This ensures the task flow is always aligned with the player’s on-chain token state.


    🧩 Token Sync & Management

    Quests rely on the TokenHandlers map in the player’s BPMC_ODK_WalletComponent. These allow the game to respond to token changes (e.g. showing a notification, starting a flow, granting a reward).

    By default, Quest and Achievement handlers are included, and you can extend or replace these for custom behavior.


    🎁 Rewards

    When a quest is completed (i.e. all tokens received), additional reward tokens can be granted. These should be defined in the quest’s metadata and triggered via the BP_TaskFlowAction_GrantAchievementFromAttachedToken executor on the final task.


    🚀 Quest Activation

    You can activate a quest by granting the initial token. This can be done:

    • Via Blueprint (BPFL_ODK_WalletHelpers)

    • From a UI panel or NPC interaction

    • From a LiveConfig-driven quest browser

    Wallet Views
    BPC_ODK_InteractableComponents
    the player can interact with in the world. When the player inputs the "Interact" input action event, the
    BPC_ODK_InteractionComponent
    will inform the currently interactable
    BPC_ODK_InteractableComponent
    that it needs to execute it's logic.

    Setting up a new Interaction

    If you want to set up a new prop actor to have interactions, the simplest method is to attach a BPC_ODK_InteractableComponent_WidgetPopup to your actor. Once this is done, select the component in the "Viewport" and move it to an appropriate position on your actor. This should be a location where you expect the camera to be looking when the player interacts with the object. Now, you can hook into events on the component to execute your behaviour: - OnInteract: Execute your custom interaction logic - OnFocused: Marks the interactable component as the active target, allowing it to receive interaction input when the player presses the designated key. - OnUnfocused: Removes the component as the active target, preventing it from receiving interaction input. Lastly, we can configure the BPC_ODK_InteractableComponent_WidgetPopup properties: - Interaction Distance: the distance within which the player character needs to be to interact with this component. - Priority: If multiple interactables are able to be interacted with at one time, the highest priority interactable will take precedence. - Widget Transform: Allows you to configure where the popup widget will be displayed in relation to your actor. We recommend setting the widget to sit above your actor

    Further Testing

    Task Flow

    🧠 Conceptual Overview

    The Task Flow system allows you to define a sequence of tasks that guide a player through an experience—such as a quest, tutorial, or onboarding sequence—using a structured flow of triggers, conditions, and actions. Each task is handled one at a time, and the player must complete them in order to progress.

    The core components of the system are:


    🔁 Triggers

    A trigger defines when the task flow should begin. For example, the flow might start when:

    • A player receives an on-chain token

    • A player overlaps a specific volume in the world

    • The game reaches a certain boot state

    Triggers often work with conditions to check whether the flow is allowed to start. For example, the trigger might fire when a player enters a zone, but the condition ensures it’s only valid on Wednesdays.


    ✅ Conditions

    Conditions exist at both the flow level (whether the sequence should begin at all) and the task level (whether a specific task is ready to activate).

    These conditions are logic blocks you can define—for example:

    • “The player has not completed this flow before”

    • “It is daytime in the game world”

    • “The player has reached a specific location”

    Conditions must complete successfully for their corresponding flow or task to proceed.


    🎯 Tasks

    A task defines a goal that the player must accomplish to move forward in the flow. For example:

    • Task 1: Jump three times (track jump count from a movement mode change)

    • Task 2: Perform a specific emote

    Each task must signal when it's complete using the Task Trigger Execution Complete event, which tells the Task Flow system to move to the next step.

    All tasks are initialized using the Initialize Task Flow Executor event.


    ⚡ Actions

    Actions are optional, fire-and-forget logic blocks that execute at key moments in the flow lifecycle. You can use them to:

    • Show a VFX or sound at task start

    • Spawn an item when a task begins

    • Hide UI or clean up actors when a task ends

    Actions can fire on events like:

    • Flow Start / Complete

    • Task Start / Complete / Skipped

    They are lightweight and don’t report back to the task system—they simply execute and move on.


    This system is designed to be flexible, data-driven, and extendable—whether you’re guiding the player through an onboarding sequence, building a narrative quest chain, or triggering progression gates.

    When interfacing with these functions, you will need to pass in a string "Key" parameter and an enum "Scope" value. The scope parameter allow you to limit the access of this data. The enum has these values: - World: Only players on this world have access to this key's persistence data - Project: Players can access the persistence data on any world that share the same project. - Organization: Players can access the persistence data on any world that share the same organization.

    As a brief reminder your organization will contain all of your experiences. You may have multiple projects in your organization corresponding to different experiences. An example might be you have a concert experience and shooter game experience as part of your organization. In a given project, you may have multiple worlds. This might be a different world for a variety of maps in a shooter game.

    The RegisterInterest and ReadValue functions have callback params to let you know when the data has been recieved. In the case of ReadValue callbacks, they contain a "Value" parameter and a "Success" parameter. The key may not exists in the database in which "Success" will be false. RegisterInterest will not immediately get the value for you. Instead it will listen for any future updates to the value and inform you when it changes by calling the callback you provided.

    Server and Client Stores

    Different key value stores are used for different users. This means that you will only be able to access a player's data on the auth client. Similarly, the server has it's own store which can not be accessed by clients.

    This allows for the same key to be used across multiple stores. That is to say that multiple users and the server can all use the same key if so wished.

    "high_scores" : "{"simon": 10, "david": 3}"
    MML Basics
    1. Metaverse Markup Language (MML) - Official Site: Open-source web technologies for building multi-user metaverse experiences using HTML and JavaScript.

    2. MML Documentation: Comprehensive guides and references for MML elements.

    3. MML Discord: Join the community on Discord to discuss MML and connect with other developers.

    4. Main MML Repository: Metaverse markup language for describing 3D multi-user interactive metaversal objects and experiences based on HTML.

    Building with MML

    • MML Editor: An interactive editor for building and testing MML documents.

    • 3D Web Experience: Packages to run web-based, multi-user 3D web experiences supporting MML.

    • Esbuild Plugin MML: An esbuild plugin that bundles JavaScript/React sources into HTML documents for MML.

    • MML React Starter Project: Example of a server that serves a live MML document using React.

    • : Example server for serving live MML documents via WebSocket.

    • : Implements an MML Guided Tour with live, multiplayer experiences.

    • : Uses MML, 3D Web Experience, and React for interactive 3D web experiences.

    • : Minimal 3D playground powered by MML for creating live, multiplayer experiences.

    • : UI overlay for quick access to MML resources and project sharing.

    MML Resources

    • MML Blog: Stay updated with the latest news, tutorials, and announcements related to MML.

    • MSquared Blog: News and updates from MSquared, with frequent MML-related updates.

    • Introducing Crowd Support in Web Worlds: Mass concurrency and Crowd Support in MML.

    • AI-Powered NPC with MML: Guide on integrating AI to power NPCs using MML and OpenAI.

    • : A live, searchable index of creators, assets, and activity across worlds and chains, with trusted snapshots and runtime policy enforcement to power interoperable MML experiences.

    • : Fan, engagement, and loyalty platform with MML support.

    Examples and Exploration

    • MML Examples: Demonstrations of MML capabilities and project templates.

    • MML Editor Explore: Explore examples and code snippets within the MML Editor.

    • 3D Web Experience Examples: Examples showcasing the 3D Web Experience with MML by TheCodeTherapy.

    • Crazy Run 2: Obstacle course game with leaderboard and achievements, created entirely in MML.

    Streaming

    • Live Streaming to the Metaverse: How to stream events inside of MML using Cloudflare.

    • Cloudflare Stream: Service for hosting streams integrated with MML.

    • Dolby OptiView: High-quality live streaming and playback service.

    • OBS (Open Broadcaster Software): Free and open-source software for video recording and streaming, compatible with MML setups.

    Somnia

    • Somnia Playground: A hub for creativity and immersive experiences using MML.

    • Somnia Blockchain: High-speed EVM blockchain with native MML support.

    On-chain and State Services

    • Etherbase: A backend read/write service for EVM contracts that makes it easy to emit events, set state, and execute functions. Designed to be MML-compatible for wiring live on-chain data into MML experiences.

    Avatars and Rigging

    • GLTF Avatar Exporter: Tool for exporting avatars in GLTF format for use in MML.

    • MML Avatar Tools: Web tool for fixing mesh, skeleton, and material issues in avatars.

    • MML Blender Extension: Blender extension for MML avatar preparation.

    • Sandbox Converter App: Web application that converts various types of The Sandbox avatars into MML-wrapped GLBs, producing assets ready to drop into MML scenes.

    • : React component library for building fully-rigged 3D avatars powered by PlayCanvas, plus a minimal Next.js app as a live playground/reference integration—useful in MML pipelines.

    • : Tools for preparing game-ready rigs in Blender.

    • : Blender addon for optimized rigging.

    • : Comprehensive guide and tutorial for creating MML avatars.

    • : NFT collection with MML-ready avatars.

    • : MML Conversion of Mcbess Celmates avatar.

    • : MML Conversion of Broadside avatar.

    • : Another Broadside avatar conversion in MML.

    • : MML Conversion of Sappy Seals avatar.

    • : A quick guide and tool to get started with MML avatars at scale.

    • : Batch web tool to generate multiple MML files from GLB URLs.

    • : VRM to MML avatar conversion.

    • : VRM to MML Avatar conversion.

    • : Online tool for testing and creating avatars.

    • : Additional resources for creating avatars with MML.

    • : Detailed guide for creating MML avatars using Blender and free rigging tools.

    • : A detailed reference for M-Character elements in MML.

    • : A video tutorial covering avatar creation techniques.

    originally assembled by Directive Creator
    MML

    Account management

    To access and manage your Glyph account, click on the profile button in the top-right hand corner.

    Glyph automatically creates a wallet for you when your account is created. We use this wallet for all ODK experience token granting. If you are awarded tokens from participating within ODK events, they'll be credited to your privy wallet. You can also link additional wallets to your privy account, using the "Add a wallet" button. This is required for the WebApp and Unreal experiences to read which tokens you have ownership over and credit you with the appropriate entitlements that are attributed to those tokens. For instance, if you want to be able to switch to your BAYC (or any Yuga) avatar in build, you'll need to link to your wallet that contains the BAYC token.

    For more on how we interface with your wallet within experience, please see Wallets.

    Glyph
    https://www.privy.io/
    WebApp
    Setting up the HUD widget

    In order to display notifications on-screen, you must implement a notifications widget on your WBP_HUD. An example of this is the WBP_NotificationsDisplayODK widget which contains a vertical box for containing the notification widgets and the functionality to bind to the singleton broadcasts and then add the notifications to the container.

    The example widget allows for custom notifcation widgets, but assumes that these widgets extend from WBP_ODKNotificationsBase.

    Custom Widgets

    WBP_ODKNotificationsBase is intended to be used alongside WBP_NotificationsDisplayODK to create custom widgets for notifications. This base class contains functions for setting up the custom widget and helper functions for optionally downloading the notification images.

    The SetupNotification event should be overidden to set textures and text from the notification payload.

    Sending Notifications

    Notifications can be sent using the helper functions in BPFL_ODKNotifications.

    Send ODK Notification To Local Player

    This function sends a notification to the authoritative player with the following payload:

    Notification Type

    This allows for grouping of notifications. For example: If you have multiple notifications of the same type, such as collecting an item, any new notifications will replace the existing notification in the list to avoid notification spamming.

    Notification Time

    How long to display the notification on-screen if it is not dismissed by the user.

    ODK Notification Struct

    Contains information relevant to the notification such as a Title, Content text, image etc.

    The struct also contains an Additional Data Morpheus packed struct object which allows for including any information not covered by the struct. It is then up to the developer to unpack this object and use the data as they wish.

    any struct type can be packed inside this Additional Data object
    This shows the struct being unpacked after being sent as a notification.

    Send ODK Notifcation To All Players

    This node takes in the same parameters as the local notification, but will be displayed on all users screens.

    BPM_VideoPlayerWithMillicastBase

    The base class contains several configurable variables to help with the setup of the videos and millicast.

    The Receiver name will be used on the Millicast Control panels to set which screen to display millicast content on.

    The media texture and media player variables can be reused across multiple screens to keep them in sync, or a unique media player/texture can be created if the user would like media to continue uninterrupted while millicast streams are played on other screens.

    The playlist variable contains a list of M2FileMediaSource assets that reference local video files. The player will loop through the playlist until interrupted by a live Millicast stream, at which point it will switch to the millicast stream texture. When the stream ends, the screen will switch back to the playlist and continue.

    Step 2

    Clear the sign in credentials by going Tools > Clear Credentials

    After doing this step you can try to relogin with the editor, if this still fails then redo Step 2 and proceed to Step 3.

    Step 3

    Clear your web session by deleting the webcache folders from project/Saved Folder

    After doing this Restart the Unreal Editor if the issue persists please move ontpo Step 4

    Step 4

    Close the Unreal editor and open Windows Credentials Manager

    Step 5

    Select WIndows Credentials

    Step 6

    Delete any credentials starting with Improbable, M2 or Morpheus

    Reopen the Unreal Editor and you should be prompted to re sign in, which should resolve your issue

    https://o7e.dev/
    Avatar
    • This is where players can change their avatar from, it will display all avatars that are available within their collection, its worth noting that this functionality will not work if players are forced to render a specific avatar for a experience.

    Inventory

    • The inventory consist of all tokens that the player has earned as they voyage through the Otherside, this will consist of Avatars, Emotes, Badges, Items and any other tokens that they have gathered.

    Quests

    • Quests can be triggered within experiences, and once a quest is start it will automatically be added to this section.

    Badges

    • Badges are rewards players can get by achieving certain things within Otherside

    Travel

    • The travel tab allows players to navigate around the Otherside universe

    For information on how to use the web browser to open links to your web pages, see the section on Boneyard Example Content here.

    Example of linking to an external page
    The profile Overlay that opens when pressing TAB
    World Builder
    An example of overrideing IsConditionSatisfied to check whether a player has any token from an array of configurable NFT Chins.

    Switching To Preview ODK Versions

    Please use this guide for instruction of how and when to access the preview stream.

    The ODK Launcher has two streams which are:

    Public: This version of the ODK is released to all developers.

    Preview: This version we grant early access to new ODK features for early testing. Once the preview version has been publically released you will need to switch back to Public and we will remove you from the Preview stream. If you need to access a preview version of the ODK you will need to reach out to your Yuga Representative who can organise this. Developers need to be given special access permissions to use this stream.

    If you are switching to Preview then please follow this guide for how to do so.

    Guide

    1. Log into the Launch via Otherside Login, and NOT Msquared Login

    2. Select ODK Alpha from the web project drop down

    3. Click the settings cog in the top right and select Preview in the update channel drop down

    4. Click the Top left button that loads the launcher version installer menu

    5. Click Add Installation

    6. From the version dropdown select ODK Chapter 8.1 Preview and click Install

    Input Component

    Documentation for ODK input component

    BPC_ODK_InputComponent is the main manager of input in the ODK. This component lives on the BP_ODK_PlayerControllerBase asset.

    Enabling and disabling IMCs BPC_ODK_InputComponent can be used to control which IMCs are active. BPC_ODK_InputComponent uses a stack of states where each state contains a set of IMCs. When a state is active, all enabled IMCs are disabled and all IMCs on the newly enalbed state are enabled. PushInputMappingContextState - can be used to add an new state to the stack. This state will become active. RemoveInputMappingContextFromState - can be used remove a state from the stack. The previous state in the stack will become active. AddInputMappingContextToState - can be used to add an additional IMC to an existing state. If the state is active, the IMC will be enabled. RemoveInputMappingContextFromState - can be used to remove an IMC to an existing state. If the state is active, the IMC will be disabled.

    Getting the last input type BPC_ODK_InputComponent can be used to get the last input state (Gamepad/Keyboard) by calling GetLastInputType or hooking into the event OnInputTypeUpdated.

    Note, there exists a "On Posses Input Mapping Contexts" on the base BP_ODK_PlayerCharacterBase . Please do not use this config. It won't work within the ODK and will be removed in a future iteration.

    Scoreboard

    Feature Overview

    The scoreboard shows the number of kills and deaths for each player. The scoreboard updated whenever: the scoreboard UI is opened, a player joins a team, a player is killed and whenever a player joins/leaves the game. Use the "Z" key to open the UI.

    Technical Overview

    All functionality can be found in WBP_Scoreboard.

    Respawn Points

    Feature Overview

    Spawn point zones are added to the map for each team. When a team is selected, the character will spawn at the associated teams spawn point.

    Technical Overview

    AJ_PlayerStartVolumes have been placed around the map and tagged with either: RED, BLUE or GREEN. On BeginPlay, BPM_ODK_ExampleCombatCharacter caches all player start volumes on auth clients. When BPM_ODK_ExampleCombatCharacter is respawned, we use our team to find the appropriate player start volumes and teleport to a random player start in the player start volumes.

    Continuous Development

    Updates on Continuous Otherside Development

    2025.12.11

    New Features

    • KodaCam

    Setting Up The Boxie Collection

    Since this collection doesn't actually exists we are going to make it ourselves. Feel free to skip this portion and just grab the trait metadata from the resources .

    The Boxie collection only has three different traits categories:

    • Head

      • classic box

      • sphere

    NFTs / Tokens

    NOTE: The creator portal is in Alpha and only avaliable to specific developers, please talk to your Yuga representative if you have a need to access this.

    The ODK Token System provides a unified way to track and synchronize player progress, rewards, achievements, and quests across the blockchain and experience.

    Tokens can be used for persistent state, with each token type having a specific purpose and behavior.


    🔍 What Is a Token?

    In the context of ODK, a token is an on-chain data object that represents something the player owns.

    Token are:

    Movement Modes

    Documentation regarding character movement in the ODK

    The ODK seperates different types of movements into different conceptual movement modes. Walking, grinding and gliding are examples of different movement modes available in the base ODK.

    Movement modes are controlled via a component on the BP_ODK_PlayerCharacterBase render target. By default, this component is BPC_ODK_MovementComponent, however this can be overriden on BP_ODK_PlayerCharacterBase by setting the MovementComponentClass property.

    The BPC_ODK_MovementComponent controls which movement mode should be active at a given time. A mapping is defined on this component between a "movement mode id" and a movement mode component class. This allows for easy overriding and addition of movement mode behaivours.

    Configurable Input Action Keybindings

    Adding a new configurable input action To enable configurable keybindings for an input action, you should configure the following on the InputAction asset. You first may want to add the IM_MappingGroup modifier. This defines what context the input action can be used. This is used to automatically reset deuplicate input action keys that are in the same mapping group. As an example, the jump and move forward input action have been added to the same mapping group so that if the user can not add the same key for both.

    You then will need to add PlayerMappableKeySettings to the input action's user settings. The following should be configured: Name - just the name of your input action asset. Display Name - The text to be shown to the player in UI. Display Category - The category your input action will be put under in the keybindings UI.

    Lastly, you will need to configure you input action inside whatever IMCs it is added to. You should probably add keys for both keyboard and the gamepad. The following should be configured: Name - This should be the name of the input action asset appeneded with either "Keyboard" or "Gamepad" respectively. Additionally, if your input action needs to be added to multiple IMCs, due to the fact that the "Name" property must be unique accross the project, you can append a different number to the end of the name in each IMC.

    3 New Single Player Challenges
  • Multi-objective Daily Tasks

  • 19 New Daily Tasks

  • 7 KodaCam Daily Tasks

  • Map function

  • Replay the 'A Spark Reborn' any time from the World Travel overlay

  • Challenge Leaderboard Reset

  • Added 'Show Player Names' toggle Graphics Settings menu

  • Content Updates

    • Rebalanced chest speeds for all chest chaser Challenges

    • Profile overlay is now bound to “P” (previously Tab)

    • Map is bound to Tab

    • “70s dance” renamed to “Ritualistic Dance”

    • Descriptive Tags added Challenge Entry Portals so players can understand what the challenge contains

    • Reoriented the Compass so the Koda Temple is North from the starting area

    • Reduced chance of getting the same Daily Tasks multiple days in a row

    • Reworded 'A Spark Reborn' initial tasks to make them easier to understand

    Bug Fixes

    • Fixed an XP exploit related to World travel

    • Challenge Entry portals sometimes didn't appear as completed when they were

    • Removed broken textures on Challenge Entry Portals

    • Improved collision on Swamp train

    • Made it clearer that Daily tasks don’t award XP at max level

    • Corrected grammatical issues in Daily Tasks & Badges

    • Fixed issue with Badge names being truncated when awarded

    • Fixed issues with Daily Tasks failing to claim

    • Fixed issues with Badges sometimes failing to award

    • Fixed getting stuck in 'A Spark Reborn' when chain is down

    • Fixed Server Crash in Morpheus Array

    • Removed invisible gems in gem collection Challenges

    • Collision improvements across all Challenges

    • Fixed soccer ball not appearing when a soccer event is active

    • Fixed 'A Spark Reborn' occasionally awarding double XP

    • Fixed Bubble deeplinks

    • Re-enabled trails on soccer ball

    • Avatar clipping with assets in Gaming room

    • Grounded floating grass outside the Crystal Caves

    • Updated collision around Nexus spawn point

    • Fixed visual distortion on the black hole in Fortune’s Rise IV

    • Fixed issue with users getting stuck in Challenge chests

    • Fixed visual distortion when viewing grind rails from certain angles

    • Fixed visual distortion when viewing Challenge entry portals from certain angles

    • Fixed performance micro spikes during normal gameplay

    2025.09.12

    Dress Code avatar system for Clubhouse

    • This should prevent non-Ape and MAYC avatars from appearing in Clubhouse

    Free Bubble mode (Walkie Talkie)

    • User can now talk to each other without constraints of Bubble

    • Still uses Bubble system

    Listen Mode - Users can now listen to any Public bubble without joining

    Bubble List improvements and fixes

    • Resolved duplicate Bubbles appearing in Browse Bubbles

    • Resolved duplicate user entries

    • Placeholder name tags (different to privy id issue)

    • Scrolling improvements

    • Correct profile picture should appear

    • Fix for Bubbles UI being greyed out if User is kicked

    • Bubble UI and buttons have received some TLC polish updates

    • Bubbles are now sorted by attendance in Browse Bubbles menu

    • Bubbles should no longer appear with jagged edges

    • Bubbles notifications will now appear on far right of screen if Bubbles menu is closed

    Other notable fixes:

    • Launch pads should no longer put User in somersault animation

    • Fix to volume sliders in Settings menu

    2025.09.05

    Hotfix follow-up.

    • Updated skybox with islands

    • Change to make gliding easier when jumping from the ground / low rocks

    • Fix for white characters on low graphics settings

    • Fix for solid cylinder volumes for co-op emotes on low settings

    • Black screen on initial loading of GFN reduced

    • Bubbles UI getting overlapped by host disconnect message

    • Users are now unable to interact with each other in Bubbles with F

    2025.09.03

    This update finishes the migration of existing worlds to UE 5.5. The focus was addressing community feedback around UI improvements and environment upgrades.

    • Updated Social Spaces to UE 5.5 and ODK v9.1

    • Reworked Settings menu for better responsiveness

    • Updated Settings UI to match ODK

    • Fixed Broken bubbles character animation due to UE 5.5 update

    • Fixed Bubbles mesh not appearing as soon as Bubble is created due to UE 5.5 update

    • Fixed Players aren't falling when they leave Bubble or when Bubble is popped due to UE 5.5 update

    • Reworked Tokengating system (new version working for Clubhouse)

    • Add Bubble collisions volume around clubhouse to prevent Bubbles overlapping token gate area

    • Upgraded Swamp Train route and added with functional trains

    • Revised Train Stops

    • Fixed lighting for Medium graphics setting

    • Added Live Config option to allow 'F to interact' in Bubbles

    • See Bubble User count in participant list

    • Disabled current SFX in Bubbles UI when changing Movement Mode

    • Improved Medium graphic Shadow performance

    • Re-added Bubble Backflip emote

    • Set default movement speed for players to Sprint

    • [Platform Web update - not content] GFN Disconnect fix

    MML Starter Project
    MML Guided Tour
    MML React Space
    MML Playground
    MML Starter Project Overlay
    MSquared Atlas
    Open Page
    Avatar Creator
    Game Rig Tools
    Surface Heat Skinning
    Avatar Documentation
    Grillz Gang
    Mcbess Celmates Avatar Set
    Broadside Avatar 1
    Broadside Avatar 2
    Sappy Seal
    MML Avatar Starterkit
    MML Avatar Starterkit Web Tool
    Toothpaste Avatar
    Salt and Pepper Avatars
    Avatar Tools (online tool)
    Further MML Avatar Creation Resources
    Blender Workflow Docs
    M-Character Reference
    Video Tutorial
    Scoreboard UI
    Caching player start volumes in BPM_ODK_ExampleCombatCharacter
    Respawn logic in BPM_ODK_ExampleCombatCharacter
  • smiley face

  • Upper

    • classic red

    • classic blue

    • classic purple

    • metallic red

    • metallic blue

    • metallic purple

    • polka dots

    • stripes

  • Lower

    • dark

    • light

  • Let's make a collection of 100 boxies. This is going to be a very rudimentary collection generation in python.

    You can run this script directly in Blender by:

    • Changing the view to the scripting tab

    • Create a new text block by clicking the New Button

    • Paste the script above into the text block, you can change the variable OUTPUT_FILE to a location that makes sense for you, like in a folder for this project. Then press the run button.

    This should save out a JSON file that is structured like an NFT collection metadata.

    Resources
    55KB
    boxie_metadata.json
    Open
  • Minted and granted via the Creator Portal

  • Stored in the player’s wallet

  • Used to trigger or gate gameplay systems

  • Synced in real-time between web, blockchain, and in-game logic

  • All tokens follow a consistent format for their Token ID:

    Example:

    Component
    Description

    33139

    Chain ID (e.g. APECHAIN)

    0x...

    Contract address for your experience

    3

    Unique Token ID


    🧱 Supported Token Types

    Each token is defined by its type field in metadata, which determines how it is handled in-game. This type is also used by the TokenHandlers map on the player wallet to route token logic.

    Type
    Purpose

    quest

    Sequential multi-task experiences with reward support (See: )

    achievement / badge

    Recognitions of completion, progress, or milestones

    image

    Selfie/image tokens — used for storing screenshots or custom UGC

    item

    Inventory items (experimental or project-specific)

    custom

    Any developer-defined use case (requires custom TokenHandler)

    Each type can have its own UI, reward logic, and flow triggers.


    🛠 Where Tokens Fit in Your Project

    Tokens can:

    • Start quest flows (by triggering BP_ODK_TaskFlow)

    • Unlock achievements or badges

    • Be displayed in user profiles or overlays

    • Gate access to in-game systems or areas

    • Be granted via Blueprint, server-side logic, or web interfaces


    • 📜 Quest Tokens – Multi-step task sequences with XP and rewards

    • 🏆 Achievement Tokens – One-time accomplishments or stats

    • 🎖️ Badge Tokens – Visual trophies or collectibles

    • 🖼️ Image Tokens – Player-submitted images (e.g. selfies, UGC)

    • ⚙️ Custom Token Types – Extending the system for new behaviors

    A description of other useful properties: -
    Initial Movement Mode
    - The movement mode your character will start in. -
    Default Movement Mode
    - The movement mode that your character can default to after leaving a movement mode. -
    Startup Movement Modes
    - Movement modes that will be initialized at the start of the game rather than created dynamically when first requested. This has very niche uses.

    Each movement mode is wrapped up in it's own component that derives from BPC_ODK_MovementModeComponentBase.

    BPC_ODK_MovementModeComponentBase components as a good general rule should manage these areas:

    • Input - Handling enhanced input actions that modify the behaivour within a movement mode.

    • Character movement - The logic that actually moves the character.

    • Movement mode validity - Whether it is appropriate to be able to enter the movement mode. This is comunicated through the CanEnterToMovementMode function.

    • Ending the movement mode - Ensuring the movement mode is ended when appropriate.

    GlidingMovementMode Example

    We will now consider the BPC_ODK_GrindingMovementMode as an example.

    Here we can see how input is processed in the grinding movement mode. The only valid input action on a grinding rail is "Jump". First, we override the BPC_ODK_MovementModeComponentBase base function AddInputBindings. Inside this function we can call AddInputAction for each input we want to process. We can bind to the IA_ODK_Jump input action listening to the "Triggered" event and setup our callback. Inside the callback we perform the appropriate logic. In this case stopping the grinding and making the character jump. You can see that the callback has a "Value" paramater that is sometimes useful when processing input. You should use the value corresponding to the "Value Type" on the given input action.

    Adding a binding for an input action
    Processing an input action to make the character jump. The "Value" param is not used but is show here for demonstration.

    Ending the movement mode can be done by simply requesting a new movement mode. Inside the BPC_ODK_GrindingMovementMode StopGrinding function, TrySetDefaultMovementMode is called on the ODK movement component. We could transition to any movement mode we liked at this point but recommend going back to a the default movement mode.

    ODK Base UI

    ODK Base UI is a flexible UI framework designed for building reusable and dynamic widget controls. It simplifies logic binding, value syncing, and visibility control by pairing each control with an Executor.

    Controls are placed into UI layouts like any other widgets, but their functionality is entirely data-driven via executors.

    This keeps consistency thoughout the Otherside platform, allowing for devs to reuse existing designed controls.


    🔁 How It Works

    Each control can be paired with:

    • A Value Executor (handles functionality and state)

    • An optional Visibility Executor (handles when to show/hide the control)

    • An optional Registered Control Name (for simple runtime access via the UI system)


    🎮 Control Executors

    The Executor is a Blueprint class that drives the behaviour of a control. Every executor supports the following functions:

    Function
    Description

    This system makes each control self-contained and keeps your UI logic clean and reusable.


    👁️ Visibility Executors (Optional)

    A Visibility Executor is a separate class that defines when a control should be visible.

    It implements:

    • GetVisibility → Returns a visibility state (Visible, Collapsed, etc.)

    Use cases include:

    • Hiding the player roles dropdown unless the user is an admin

    • Only showing Gamepad settings when a controller is connected.


    🏷️ Registered Control Name

    Every control has an optional RegisteredControlName.

    This name:

    • Registers the control with the BP_WorldService_ODKUI

    • Allows it to be found or replaced dynamically at runtime (See function library below)


    🧰 Runtime Editing

    The Blueprint Function Library BPFL_ODKBaseUI gives you helpers for modifying UI areas at runtime.

    Notable functions:

    Function
    Purpose

    This system is especially useful in:

    • Hot-swapping controls during a session

    • Extending shared base widgets without making duplicates


    Examples

    🧪 Example: Roles selector

    • Control Type: Dropdown

    • Executor: BP_SettingsExecutor_PlayerRoles

      • GetCurrentValue → returns a string array of available roles

    Setting up the quest in-game

    🎮 Unreal Setup (Gameplay Logic)

    The Unreal side of an ODK Quest handles how the player interacts with and completes tasks, and ensures that quest progress reflects the player's on-chain token balance. The system uses BP_ODK_TaskFlow actors, Quest-specific task executors, and wallet integration to create a fully synced experience.

    For the creation of the Quest token see Creating a Quest token


    🔹 1. Add a BP_ODK_TaskFlow Actor to the Level and create a Task PDA

    The outlines exactly how to set this up

    In your new PDA_ODK_TaskFlow:

    • Add a new item to the Tasks array

    • Derive each Complete Task Triggersfrom BP_ODKTaskFlow_QuestTaskBase(described below) — these define what in-game conditions complete the task (e.g. jumping, running)


    🔹 2. Use the Correct Base Class: BP_ODKTaskFlow_QuestTaskBase

    For quests, each task must derive from BP_ODKTaskFlow_QuestTaskBase.

    This subclass is essential because:

    • It handles sending the token to the user when a task is completed

    • Unlike standard tasks, it does not automatically progress

    • Instead, it waits for the player to receive the token, then:

      • Reinitializes the flow trigger

    This means:

    • Token ownership becomes the single source of truth for progress

    • You don’t risk out-of-sync task flow states

    • Tasks remain paused until the player actually holds the correct number of tokens

    All derived tasks should still call:

    ...once the task’s completion criteria has been met.


    🔹 3. Getting Quest Token IDs in Tasks

    To associate tasks with the correct Token ID, you need a way to retrieve the ID. This can be done per task — but that quickly becomes repetitive.

    Instead, use the built-in system based on BP_GetTokenIDBase, which has two helpful options:

    • BP_GetTokenIDFromCustomDetails

    • BP_GetTokenIDFromLiveConfig

    Recommended: BP_GetTokenIDFromCustomDetails

    • Each task checks the custom details of the Data Asset

    • You define a single source of truth inside the asset, and all tasks reference it

    This means less setup repetition and centralized management.

    Alternate: BP_GetTokenIDFromLiveConfig

    • Allows dynamically retrieving token IDs from a live config

    • Useful for centralized overrides or managing IDs per environment


    🔹 4. Activating a Quest

    Quests are only activated when the player receives the first token.

    This should be granted as part of your experience — how you do this is project-specific.

    The easiest way is via:

    In the ODK sample, tokens are granted by Interacting with an NPC. This opens a web overlay with available quests.


    🔹 5. Granting the Achievement (Reward Tokens)

    If your Quest Token includes a rewards array (tokens to grant upon completion), you can automate this via:

    We recommend this is assigned to the TaskCompleteActions of the final task, but could also be assigned to FlowFinishedActions.

    Why? Because technically the flow never completes in the traditional sense — it’s driven by token sync, and the player could hold the final token before the system registers a full flow completion event. Adding it to the final task will speed up execution.

    This approach ensures:

    • Rewards are reliably granted

    • The grant is tied to the token logic, not just the internal task flow state

    🔹 6. Displaying Quest notifications

    By default, the player's BPMC_ODK_WalletComponent, found on BPM_ODK_PlayerCharacterBase, contains a TokenHandlers map.

    This map listens for changes to tokens and routes behavior accordingly.

    Out of the box, the wallet includes:

    • "achievement" handler

    • "quest" handler

    The map’s key must match the type field in the token metadata — for quests, this is "quest".

    When a token is added, removed, or updated:

    • The appropriate handler is triggered

    • You can use this to:

      • Display UI

      • Fire off effects

    If you need additional behaviour, you can subclass the default handler and override its logic.

    Emotes

    Emotes allow players to express themselves in game with a various animations.

    When interacting with emotes in the ODK, the main asset to interact with is the BPMC_ODK_EmotesComponent. This is a component on the ODK morpheus player character BPM_ODK_PlayerCharacterBase.

    Defining Emotes

    You can define an emote by creating a new PDA_ODK_Emote asset.

    Name - Display name used in UI Emote Id - Used to help unlock emotes via token ownership. The token metadata will specify the emote id that it represents ownership of. Icon - Icon used in UI Type - An enum that lets code categorise emotes. Currently the types we support are: STANDARD and COOP. Emote Executor - An object that runs logic to start the emote

    Currently, emotes come in two flavours: standard emotes and coop emotes. Stardard emotes simply play an animation on you player character. Coop emotes allow you to play animations synced with another player character. The way these are handled are very different. The emote executor allows for completely different behaivours to be executed by different emotes whilst being handled the same way in the reset of our logic.

    Setting Emote Selection

    The emotes available to a player in your game can be configured on the BPC_ODK_EmotesComponent on your player render target actor. You can configure the DefaultEmoteSelection property on the component by selecting the PDA_ODK_Emote assets you desire.

    Starting and Stopping Emotes

    PlayEmote and CancelCurrentEmote can be used to start and stop emotes regardless of whether they are standard or coop emotes.

    Overriding Emote Selection

    You can override what emotes are available by using the override functions: OverrideEmoteSelection, RemoveEmoteSelectionOverride and, RemoveAllEmoteOverrides. This can be useful for limiting what emotes are available in different contexts. You might only want a couple emotes available whilst the player is in a tutorial but allow all emotes to be played after the tutorial is completed. This can help achieve that. When you apply an emote selection override, a new state will be added to a stack containing the override emotes. When you no longer want to override the emote selection, removing the override will remove the state from the stack returning you to the original emote selection.

    Filtering Emote Selection

    You can use GetFilteredEmotes, to get a filtered list of emotes that currently playable. This would take into account emote selection overrides. Filtering can be used in conjunction with overrides. You could for example filter the current emote selection for all coop emotes and then apply an emote selection override to only include those coop emotes.

    Teams

    Feature Overview

    The Meebit template comes with an example implementation of teams. Players can join different teams to shoot players on different teams.

    Technical Overview

    A player's teams is stored on a morpheus actor component: BPMC_TeamComponent. This component has a client auth replicated property Team that handles replicated of state to other clients. Teams are defined in the E_Team enumeration. Data regarding teams can be configured in DT_TeamData which uses the S_TeamData structure. This strucure allows you to configure a display name shown to players and a color for a given team.

    Tutorials:

    Adding a Team

    Adding a team is easy! Simply add a new enumeration value to E_Team betweem NONE and INVALID values. Then configure a new data table entry in DT_TeamData. Lastly, you will need to update some simple helper functions so that blueprint logic knows how to access/use your new team. Inside BPFL_TeamHelpers you will to update GetTeamIndex and GetTeamName, adding values. Once you have added your new team!

    Scannable Objects

    BPC_Scannable 📸🔍

    The BPC_Scannable component extends the Selfie Cam system by allowing specific actors in the world to be "scanned" whenever a screenshot is taken. This makes it possible to tag screenshots with contextual metadata about visible objects — such as quest items, characters, or interactables.


    import random
    import json
    
    SEED = 92310
    OUTPUT_FILE = "boxie_metadata.json"
    
    
    """
    List of tuples where the first item is the name of the trait
    and the second item is the total supply of that trait.
    """
    
    head = [
    	("classic box", 60),
    	("sphere", 30),
    	("smiley face", 10)
    ]
    
    upper = [
        ("classic red", 18),
        ("classic blue", 18),
        ("classic purple", 18),
        ("metallic red", 12),
        ("metallic blue", 12),
        ("metallic purple", 12),
        ("polka dots", 6),
        ("stripes", 4),
    
    ]
    
    lower = [
    	("dark", 60),
    	("light", 40)
    ]
    
      
    random.seed(SEED)
    
    def populate_list(trait_list):
        """
        Create a list of the full population of traits
        """
        
        populated_list = []
      
        for trait_name, trait_count in trait_list:
            populated_list.extend([trait_name for _ in range(trait_count)])
    
        return populated_list
    
      
    # populate all lists and shuffle them
    head_populated = populate_list(head)
    random.shuffle(head_populated)
      
    upper_populated = populate_list(upper)
    random.shuffle(upper_populated)
    
    lower_populated = populate_list(lower)
    random.shuffle(lower_populated)
    
    collection = []
    
    for token_id in range(100):
        token_data = {
            "id": token_id,
            "name": f"Boxie {token_id}",
            "image": "https://fake-collection-api.io/images/token_id.jpg",
            "mml": "https://fake-collection-api.io/mml/token_id.jpg",
            "attributes": [
                {"value": head_populated[token_id], "trait_type": "head"},
                {"value": upper_populated[token_id], "trait_type": "upper"},
                {"value": lower_populated[token_id], "trait_type": "lower"},
            ],
        }
    
        collection.append(token_data)
    
    with open(OUTPUT_FILE, "w") as f:
        json.dump(collection, f, indent=4)
    
    <ChainID>:<ContractAddress>:<TokenID>
    33139:0x1122334455667788991122334455667788991100:3
    ODK Quests
    HandleValueUpdate → sets the players new role
  • Visibility Executor: BP_ODK_UIControlVisibility_UserHasSpecificRole

  • Initialise

    Called on setup. Used for binding events or caching references, much like the Begin Play event.

    GetCurrentValue

    Returns the control’s current state (e.g. current volume or mouse sensitivity).

    HandleValueUpdate

    Called when the user changes the control through the UI. Developers should implement functionality here (e.g. setting the mouse sensitivity to the current slider value).

    ODKBaseUI_GetRegisteredAreaByName

    Find a control or UI area by searching for the Registered Name

    ODKBaseUI_ReplaceWidget

    Replace a widget or control at runtime

    ODKBaseUI_ReplaceWidgetByClass

    Find a widget that matches the supplied class and replace it with a newly contructed widget.

    ODKBaseUI_AddControlToRegisteredArea

    Finds an area by name and adds a newly constructed widget to it.

    ODKBaseUI_AddTabToNamedTabList

    Add tabs dynamically to a tab group

    E_Team enumeration
    DT_TeamData data table
    GetTeamIndex and GetTeamName helper functions

    Uses the user’s token balance as the task index

    Trigger game logic

    Task Flow documentation
    You'll need to define a custom detail entry in the Quest Data Asset to use this.
    System Overview 🛠️

    When the BPC_Scannable component is attached to an actor, the Selfie Cam performs the following checks:

    1. Bounding Box Generation 📦

      • The component collects the actor’s visible primitives (Static Mesh Components, Skeletal Mesh Components, etc.).

      • A bounding box is created around these primitives.

      • The system samples points across this bounding box (center, corners, etc.) to use for visibility testing.

    2. Camera Frustum Test 🎥

      • The bounding points are projected into screen space.

      • If the required number of points are inside the active camera frustum, the actor is considered visible.

      • By default:

    3. Optional Line Trace Validation 🎯

      • If Line Trace Bounding Points is enabled, each point is validated with a line trace:

        • The trace runs from the camera position to the bounding point.

        • If the line is blocked, the point is considered occluded.

    If an actor passes all checks, it is marked as scanned for that screenshot.


    Metadata Inclusion 🗂️

    Once an actor has been successfully scanned:

    • Its metadata is attached to the screenshot payload.

    • This data is passed to the On Screenshot Taken delegate on the Player Character’s BPC_ODK_ScreenshotComponent.

    • Example flow:

      • Player presses IA_SelfieMode_TakeSnapshot.

      • Screenshot is saved locally and minted.

      • Delegate fires → returns metadata including any scannable actors in view.


    Customization ⚙️

    The BPC_Scannable component exposes several options for tailoring how actors are scanned and represented:

    Scan Parameters

    • Scan Range (float) → Maximum detection distance from the camera (default: 1000).

    • Required Points (int) → Number of bounding box points that must be inside the frustum (default: 1).

    • Line Trace Bounding Points (bool) → Enables occlusion checks for more accurate results.

    Metadata

    • Friendly Name (FString, Exposed Variable)

      • If set, this is included in the screenshot’s metadata JSON.

      • If not set, a fallback name is auto-generated from the actor’s name.

    • GetScanInfo Override 🔧

      • Developers can subclass BPC_Scannable and override the GetScanInfo function.

      • This allows returning a custom JSON object with arbitrary fields.

      • Example: rarity, quest state, or any game-specific attributes.

    Bounds Modifiers

    • Origin Modifier (FVector) → Shifts the generated bounds center.

    • Extent Modifier (FVector) → Expands or shrinks the generated bounds extents.

    • Useful for fine-tuning where an actor’s scannable area is relative to its meshes.


    Example Workflow 🚀

    1. Setup

      • Attach BPC_Scannable to any actor you want to be detectable in Selfie Cam mode.

    2. Configure

      • Adjust Scan Range and Required Points to match how “strict” the scanning should be.

      • Enable Line Trace Bounding Points if you need occlusion accuracy (e.g. actors behind walls shouldn’t be detected).

    3. Take Screenshot

      • Player enters Selfie Mode (IA_ScreenshotCamera_Toggle).

      • Camera checks for nearby scannable actors.

      • If visible, bounding box points pass frustum/trace tests → actor metadata is collected.

    4. Handle Metadata

      • When the screenshot is taken, the On Screenshot Taken delegate on BPC_ODK_ScreenshotComponent fires.

      • This delegate returns:


    👉 With this system in place, screenshots taken by players don’t just capture visuals — they also capture contextual metadata about the world, making them perfect for collectibles, quests, or social sharing features.

    Further Testing

    It is a good idea to test how your avatar is going to work in the Otherside by setting up a small test scene to preview some animations.

    In order to do this we are going to:

    • Create a new ODK project

    • Create a new blueprint that will

      • Load a GLB into the ODK at runtime

    Token Handlers

    Token Handlers are objects that react to token changes inside the ODK wallet. They live on the wallet component of the Morpheus player character and define what happens when a token is added, removed, or updated — such as triggering a quest, showing some UI, or playing a VFX.

    The system is modular, data-driven, and highly extendable via Blueprints.


    🔩 Where Token Handlers Live

    Token Handlers are configured on the player's wallet component: 📍 BPMC_ODK_WalletComponent Found on: BPM_ODK_PlayerCharacterBase

    Trigger Execution Complete
    BPFL_ODK_WalletHelpers → GrantTokenToWallet
    BP_TaskFlowAction_GrantAchievementFromAttachedToken
    Scan Range = 1000 units
  • Required Points = 1 (i.e. only one point needs to be inside the camera view).

  • You can:

    • Ignore specific actors using a tag.

    • Ignore specific component classes under Advanced > Component Classes to Ignore (defaults include Niagara systems, Groom components, widgets, nameplates, and others).

    The screenshot image data.
  • Any attached metadata from scannable actors.

  • or your project’s subclass.

    Inside the component, you’ll find a map:

    Each entry defines a filter set with an arbitrary name and the handler that should respond if a token passes that filter.


    Example Use Case:

    • Filters for tokens with type == "quest"

    • Handler: BP_ODKTokenHandler_Quest - Shows Quest UI information


    🧪 How Filtering Works

    Each filter in the filter set must implement the function:

    This function inspects the token metadata and determines if it matches your logic.

    If ALL of the filters return true, the assigned Token Handler is activated.


    🧰 Built-in Example Filters

    You can find reusable example filters in the plugin:

    🧾 BP_ODKTokenFilter_TokenType

    • Checks the type field in token metadata

    • Accepts a whitelist of strings

    • Example: match type == "quest" or type == "achievement"


    🧮 BP_ODKTokenConditionGroup

    • Used to group filters together in more advanced logic

    • Contains:

      • Filters[]: array of AND'd filters - All filters must pass

      • OR Filter: a single filter that acts as an OR condition.

      The OR filter is a single filter because it allows nesting of ConditionGroups, allowing complex condition trees

    Example:


    🛠 Creating Your Own Filters

    To create custom filters:

    1. Inherit from BP_ODKTokenFilterBase

    2. Implement IsRelevantToken

    3. Add any parameters you want to expose (e.g. tags, substrings, reward keys)

    Custom filters give you full control over how tokens are routed, enabling:

    • Campaign-specific logic

    • Filtering by metadata fields like XP, group, or even token balance


    🎯 Creating Your Own Token Handlers

    Handlers inherit from BP_ODKTokenHandlerBase and define what happens when a relevant token event occurs.

    Each handler receives the TokenUpdate function containing the filtered token and the update type (Add, Update, Remove etc)

    Token handlers allow for custom logic for a filtered token, for example:

    For all tokens with "type" "quest"

    • Fire off UI or sound

    • Trigger Task Flows

    For all tokens with "type" "badge"

    • Start cinematics

    • Queue unlocks

    • Animate overlays


    ✅ Best Practices

    • Use meaningful filter set names ("quest notifications", "achievement unlocked")

    • Reuse modular filters (e.g. shared TokenType filters)

    • Keep filters lightweight — heavy logic should live in handlers

    • Avoid putting too much logic inside IsRelevantToken — keep it as a pass/fail gate


    🧵 Summary Flow

    Map<string, BP_ODKFilterSet>
    IsRelevantToken(TokenData) → bool
    ( TokenType == "quest" ) 
      OR 
    ( ConditionGroup
        (TokenType == "achievement")
                AND
        (ChainID == "33139")
    )
    1. Token is added/removed/updated
    2. Each Filter Set checks if the token matches its filter(s)
    3. If matched → corresponding Token Handler is triggered

    Apply animations

    Let's start by creating a new project. Start the ODK Launcher and make sure you are on the Templates Tab.

    Once in the project use the Content Browser to navigate to the map called Empty_P, double click on the icon to open the map.

    Create a new folder called Blueprints at the root of the content folder.

    Right click in the empty window to and click Blueprint Class.

    Select Actor

    Name the blueprint BP_AvatarPreview and then double click on the blueprint icon to open up the editor. Once in the editor change the visible tab to EventGraph

    Press the + next to Variables to add a new variable called Animations

    Change the type to Animation Asset

    Make the variable public by toggling the eye next to it.

    With the variable still selected in the details panel on the right update the variable type to be Array.

    We are going to add two more variables:

    • GLB Path

      • Type - File Path - Single

      • Public

    • Spacing

      • Type - Float - Single

      • Public The Variables section should now look like:

    In the EventGraph press tab to add a node glTF Load Asset from Filename

    Drag and drop GLB path from the Variables panel into the Event Graph and select Get GLB Path

    Right Click on the GLB Path pin and click Split Struct Pin

    Now you can connect the pin on GLB Path to the file name on the Load Asset from Filename node.

    The next node we will be adding is the Load Skeletal Mesh Recursive node. You will have to uncheck the Context Senstive checkbox in the top right hand corner of the node search window to find it.

    Connect the pins as shown in the images below to the Load Asset node.

    Pull a connection out from the Loader Config Pin on the glTF Load Asset from Filename. Reenable the Context Sensitve check box and scroll to the bottom and select Make GlTFRuntime Config.

    In the dropdown for Transform Base Type choose YForward

    Press the down arrow at the bottom of the Load Skeletal Mesh Recursive node and drag out a connection from the Skeletal Mesh Config Pin. Scroll to the bottom and select Make GlTF Runtime Skeletal Mesh Config

    From the Skeleton dropdown select SKEL_UE5Mannequin

    From the Variables panel drag Animations out to the Event Graph and select Get Animations.

    From the Pin on Animations pull off a connection and then select For Each Loop.

    Connect the Exec pin from load skeletal mesh to the For Loop.

    Add an Add Skeletal Mesh Component Node

    Pull a connection off of the Relative Transform Pin and select Make Transform

    Split the Location Struct Pin

    Next we are going to multiply the Spacing variable by the Array Index from our Loop node. We can also connect the Loop Body Pin o the Loop node to the Exec Pin of the Add Skeletal Mesh Component.

    If you haven't Compiled the blueprint yet we can do that now and then set a default value of 200.0 for our Spacing

    On our Skeletal Mesh Component, pull out a connection from the Return Value of the Add Skeletal Mesh Component node and select `Set Animation Mode.

    In the dropdown for In Animation Mode set it to Use Animation Asset

    Also pulling from the Return Value of the Add Skeletal Mesh Component node add an Override Animation Data node, connecting the In Anim to Play to the Array Element of the for loop.

    Again pulling a connection from the Add Skeletal Mesh Component node add a Set Skeletal Mesh Asset with the New Mesh Pin connected to the Return Value of Load Skeletal Mesh Recursive.

    Back in our main level editor window, drag and drop the BP_AvatarPreview into the level.

    In the details panel in the bottom right of the screen we can an animation by pressing the + Next to the Animations label.

    For our first test animation I am just going to add a Range of Motion or ROM animation sequence.

    Next we can click on the three dots next to the GLB Path field to select the GLB we downloaded from the avatar web tool.

    Once that has been entered press the green arrow at the top of the level editor to preview.

    We can add more animations to the the Animations list to be able to preview many animations at once. Using emotes is a good way to test extreme poses.

    We have successfully brought a character the whole way from nothing to a properly scaled working avatar for the Otherside. Now as we make small tweaks we can quickly see them represented in our test scene by replacing the file that the test scene is referencing.

    Pipeline Planning

    For this collection we have 3 Different Trait types. We can combine our separate meshes into a unified mesh for each trait type, and apply the appropriate material. We end up having 1 mesh object for each trait in our outliner.

    In order to export unique avatars we will want to only enable the meshes that make up a specific token. We can create a python script in Blender that reads in the JSON we created earlier and enable and disable visibility to export specific token glbs.

    The easiest way to do this would be to enforce a naming convention when creating meshes in the outliner something like:

    {trait_type}_{trait_name}

    i.e.

    head_smiley_face

    So the general steps would be:

    • Read in collection metadata

    • Loop through each token

      • hide all meshes

      • enable meshes from token metadata

      • export GLB from Blender.

    Installing Avatar Tools CLI

    • Download the standalone Binary for node.js v20.11.1

      • Windows

      • Mac

    • Extract the .zip and place it in a folder relative to your blender file

    • open up a terminal and change the directory to the folder you just extracted i.e. cd /path/to/avatar-tools-main

    • add node_bin to PATH

      • Windows set PATH=..\node_bin;%PATH%

    Below is a very basic script that can be run within Blender to export all avatars for a collection. There is plenty of room for improvement to fit specific needs and workflows but this should give a foundational look at how to create a simple process for generating all unique avatars.

    Block Out Character

    Setup Reference

    We are going to roughly block out the dimensions of our character and manually step through the entire process to get an avatar rendered in the ODK.

    We want our Boxie character to be around the height of a Voyager so roughly ~1.8 meters tall. Too tall or wide and the character may not fit through certain areas or other assumptions might break too small traversal will look very strange and again some assumptions may break. That is why making characters between 1 m and 2 m is a hard requirement.

    If you aren't already there switch to the Layout view in Blender and delete the Camera, Light, and Default Cube. This is also a good opportunity to save your file.

    Let's download the Avatar Height Guide from above, and then in Blender create a Plane. We are going to apply the Height Guide to the plane to get a rough idea of how our avatar will compare to ones already in the Otherside.

    If the sidebar on the right isn't already visible click on the small right arrow < to open it up.

    We are going to edit the scale settings so the image is approximately to scale. It isn't pixel perfect but it will get you in the ballpark. Set the Scale X value to 3.825 and the Scale Y value to 2.150.

    Now we can add the height guide to the plane so we can reference it when doing our block out. Switch over to the Shading tab.

    With the plane selected press the New Button on top of the Shader Editor panel.

    You can go ahead and just delete the node labelled Principled BSDF and then drag and drop the Avatar Height Guide file into the Shader Editor.

    Connect the Color pin on the right side of the Image Node to the Surface pin on the left side of the Material Output node.

    If you notice the color looks a little muddy. This is because by default Blender uses a View Transform called AgX. This view transform is useful for photoreal results in renders from Blender. Since we aren't going to be rendering our characters in blender we want to switch the View Transform to Standard.

    In the properties panel on the right click on the Render Properties tab which looks like the back of a DSLR camera.

    At the bottom there is a section called Color Management. Set this to Standard

    Nice! Let's jump back over to the layout tab. At first you will not be able to see the image. We can easily change this by clicking on the dropdown in the top right hand side next to the four little sphere icons.

    For now we are going to change our Lighting to Flat and change the Object Color to Texture

    Now we can align the feet of the avatars in the guide to be aligned to 0 on the X axis. For this we will need to rotate the plane 90 degrees about the X-Axis and scootch it up 0.785 m along the Z-axis. I am also going to move it slightly to the right so the voyager is aligned with the center of the scene, 0.18 m along the X-axis.

    Blocking Out

    For my block out I am going to use 10 different primitive cubes scaled to represent the head, torso, arms and legs.

    The simplest way to transform 3D objects is to click on the transform type on the left hand side. To work faster using keyboard shortcuts refer to the Blender documentation ->

    You can add cubes just like we added the Plane above.

    I added my cubes and lined it up pretty closely to the Voyager. I am not feeling very creative so I am not going to deviate from that general shape, but you do have some flexibility to change the proportions slightly to better match your character.

    I am going to hide the avatar height guide by pressing the eyeball next to the plane in the outliner and switch the viewport back from flat lighting to studio lighting to get a better look at the shape of Boxie.

    Take this opportunity to name the boxes something logical. The standard when creating characters is if you are using Left/Right is to always use the characters Left/Right and not the viewers. I also know what is coming next so I named the cubes for what bones will drive them once we get to weighting.

    If you feel confident you are done with the reference image, you can delete that now.

    Weighting

    We call this weighting because it involves the weighted influence of a bone to every single vertex. For example with Curtis the stomach area might have weights from multiple spine bones so that it deforms smoothly as the spine twists.

    There is a hard limitation in the Otherside that there can only be 4 bone influences per vertex. You don't have to manage this yourself, Blender will handle that on export of the GLB.

    Obviously manually assigning weights to individually vertices is usually not feasible as there could be thousands of vertices and almost 90 bones.

    In most cases you will use some sort of tool to help with weighting, usually starting with an automatic weight tool, and then if there are still issues using a process called weight painting to fix specific problem areas.

    Blender has some automatic weighting tools and there are several free and paid addons that can may work better such as:

    https://superhivemarket.com/products/voxel-heat-diffuse-skinning

    or free one from the same author

    https://github.com/meshonline/Surface-Heat-Diffuse-Skinning

    This guide is for the simplest avatar collection so instead we are going to rigidly bind our skeleton. This means each box of Boxie will only be influenced by one bone. We will completely side step automatic weighting and weight painting and instead manually assign full weights to one bone for each box.

    Download the ODK_Base_Skeleton from and import it into Blender.

    When moving between different software packages sometimes one may use one meters and another uses centimeters or one has the up axis as Z and the other has it as Y. In this case we can easily work around this by applying transformations in Blender to fix those issues.

    With the newly imported Armature selected we can Apply -> All Transforms.

    I am also going to to rename the armature for organization.

    The ODK Base Skeleton doesn't exactly line up with our character. We will need to adjust the bones so that it fits better.

    To enable moving bones select the armature and change the mode from Object Mode to Edit Mode

    Armature Editing Tips

    You can select bones in the viewport or in the outliner.

    To move a bone and all it's children select the bone, and with the mouse in the viewport press Shift - G and then from the list select Children

    Be careful when editing the Armature if you have symmetry mode enabled. It can change the orientation of joints causing the arms to have offset rotations.

    Holding down the Alt key while rotating the scene with the middle mouse button will snap the view into Orthographic mode which makes it easier to line up the bones.

    Now that the armature is lined up we can move onto binding the meshes to the armature. This process:

    • Makes the meshes children of the armature object

    • Creates vertex weights for each object

    Since we are manually setting the weights we are going to skip the automatic weighting and create empty vertex weights.

    Make sure we are back in Object Mode and select the armature object. While holding down shift select the rest of the objects.

    With the cursor over the 3D Viewport press Ctrl + P to open the parenting menu and select With Empty Groups

    The meshes should now be children of the armature object and have vertex weights assigned to them. Currently all vertex weights should be assigned to zero.

    To start editing vertex weights click on the vertex weight tag next to the object, you can filter the list in the Properties Panel by unfurling the filter bar below all the weights and typing. I intentionally named all the meshes the same as the bones I was going to bind them to.

    With the appropriate vertex weight group selected make sure you are in Weight Paint mode and either select Set Weight from the dropdown menu at top of the viewport, or press Ctrl + X on the keyboard.

    Repeat these steps for all the other meshes that need to be rigidly bound. To switch between meshes that are activated for weight painting you can click on the dot next the mesh in the Outliner.

    Export and Test

    We can now export a GLB. I am going to only items that are visible, that way if you still have the reference image in the scene it will be skipped.

    Make sure the format is glTF Binaryt (.glb) and under Include Visible Objects is checked.

    To do a quick test of the avatar open up in a browser and drag the avatar into the window.

    There shouldn't be any errors in the bottom left hand window and if you press Use Sample Animation the avatar should animate cleanly.

    This tool will also do some minor adjustments and reposing to ensure that the avatar will work in the Otherside. To save the fixed avatar from the tool press the export button in the top right hand corner.

    v9.x

    Legacy Skin Dependency

    Date of change: 15/08/2025

    Affected Features: UX Skins

    What’s broken and why?

    M2 has deprecated their skinning system which the ODK still has dependencies on. This dependency will be removed in a subsequent ODK release.

    How to fix it?

    When updating your existing ODK project, please add the following entry to DefaultM2Deprecated.ini

    Creating a Badge token

    A Badge Token is a simple on-chain collectible or reward that represents an achievement, status, or cosmetic marker for a player. Unlike quests or tasks, it doesn’t track progress or drive gameplay — it's often used for visual trophies, event attendance, or milestone recognition.

    At the time of writing, the Creator Portal doesn’t have a dedicated “Badge” template — so you’ll start with an Image template and update the metadata manually.


    🔹 1. Create a New Badge Token

    https://docs.blender.org/manual/en/4.5/scene_layout/object/editing/transform/introduction.html
    https://mml-io.github.io/avatar-tools/main/tools/gltf-avatar-exporter/
    Resources
    run the exported GLB through the Avatar Tools CLI

    Change the folder name from node-v20.* to node_bin

  • Download the .zip from https://github.com/mml-io/avatar-tools

  • Mac/Linux export PATH=../node_bin;$PATH

  • run npm install

  • run npm run build --workspace packages --workspace clis --workspace tools

  • https://nodejs.org/dist/v20.111/node-v20.11.1-win-x64.zip
    https://nodejs.org/dist/v20.11.1/node-v20.11.1-darwin-x64.tar.gz

    Click “Define New Object”

  • Select your experience from the dropdown

  • Choose the “Image” template (this is used as a base to define a badge)

  • You’ll now see a basic definition form.


    🔹 2. Fill Out the Initial Metadata Form

    🏷️ Required Fields:

    Field
    Description

    name

    Display name of the badge

    description

    Short description of what the badge is

    image

    The display icon for the badge

    After filling out the form you will be taken to the metadata output.

    We need to define this token as a badge. The image template does not contain a type field:


    🔹 3. Add the "type": "badge" Field

    Add the line "type": "badge", (remembering to include the ,) to the metadata

    🧾 Final Badge Token Metadata:

    📌 Why this matters: The type field tells the in-game wallet and TokenHandlers how to process this token. Without it, the badge will not trigger the correct logic or appear in badge-specific views.


    🔹 4. Set Price & Transferability

    Before minting:

    • Price → Set to 0 for development and testing

    • Transferable:

      • ✅ True for dev/debug (allows deletion/resets)

      • 🚫 False for production (prevents trading badge ownership)


    🔹 5. Mint the Token

    After saving the metadata:

    • Click “Mint”

    You’ll return to the Creator dashboard and see the generated Token ID, in this format:

    Example:


    🔹 6. Preview & Access Metadata

    • Click the badge’s image on the dashboard to review metadata

    • You can update metadata at any time using the "Update Metadata" button

    The badge token is now ready to be granted via Blueprint or automatically in-game using a Task Flow or Token Handler setup.

    import os
    import sys
    import subprocess
    import json
    from pathlib import Path
    import bpy
    
    # set these folders/files relative to this blender file
    WORKING_DIRECTORY = Path(bpy.data.filepath).parent
    COLLECTION_METADATA = Path(WORKING_DIRECTORY, "boxie_metadata.json").as_posix()
    OUTPUT_DIRECTORY = Path(WORKING_DIRECTORY, "glb_export").as_posix()
    
    def get_node_binary_dir():
        return Path(WORKING_DIRECTORY, "node_bin").as_posix()
    def get_avatar_tool_dir():
        return Path(WORKING_DIRECTORY, "avatar-tools-main").as_posix()
    def tokenize_trait_names(token_data):
        trait_names = []
        
        for trait in token_data.get("attributes", []):
            trait_type = trait["trait_type"]
            trait_name = trait["value"].replace(" ", "_")
            
            tokenized_name = f"{trait_type}_{trait_name}"
            trait_names.append(tokenized_name)
        return trait_names
            
    
    def solo_meshes_by_names(mesh_names):
        for obj in bpy.data.objects:
            if obj.type != "MESH":
                continue
            if obj.name in mesh_names:
                obj.hide_viewport = False
            else:
                obj.hide_viewport = True
    
    def export_glb(output_path):
        
        output_name = Path(output_path).stem
        output_dir = Path(output_path).parent
        temp_output_path = Path(output_dir, output_name+"_tmp").with_suffix(".glb").as_posix()
        
        bpy.ops.export_scene.gltf(
            filepath=temp_output_path,
            export_format="GLB",
            export_skins=True,
            export_current_frame=True,
            export_animations=False,
            export_normals=True,
            export_tangents=True,
            export_image_format="AUTO",
            export_jpeg_quality=100,
            use_visible=True,
            # export_draco_mesh_compression_enable=True,
        )
        if Path(output_path).exists():
            Path(output_path).unlink()
        conform_glb(temp_output_path, output_path)
        
        Path(temp_output_path).unlink()
        
    def conform_glb(input_path, output_path, skip_transparent_check=True):
        node_bin = get_node_binary_dir()
        env = os.environ.copy()
        env["PATH"] = f"{node_bin}{os.pathsep}{env['PATH']}"
        
        if sys.platform == "win32":
            args = ["npm.cmd"]
        else:
            args = ["npm"]
        
        args.append("run")
        args.append("convert")
        args.append("--")
        args.append("-i")
        args.append(input_path)
        args.append("-o")
        args.append(output_path)
        
        if skip_transparent_check:
            args.append("--skip-remove-transparency-from-materials")
            
        results = subprocess.run(args, cwd=get_avatar_tool_dir(), env=env)
        print(results)
        if results.returncode:
            output_encoded = results.stderr or results.stdout
            raise Exception("Error Conforming Mesh")
        
    
    def export_from_token_data(token_data, output_path):
        mesh_names = tokenize_trait_names(token_data)
        solo_meshes_by_names(mesh_names)
        export_glb(output_path)
    
    def show_all_meshes():
        for obj in bpy.data.objects:
            if obj.type != "MESH":
                continue
            obj.hide_viewport = False
    
    def export_collection(data_path, output_directory):
        
        # load collection data from JSON
        if not Path(data_path).exists() or not Path(data_path).is_file():
            raise Exception("Invalid Collection Data")
            
        with open(data_path, "r") as f:
            collection_data = json.load(f)
            
        
        # create directory if it does not exists
        output_dir_path = Path(output_directory)
        output_dir_path.mkdir(exist_ok=True, parents=True)
        
        for token_data in collection_data:
            # build glb export path
            output_path = Path(output_dir_path, str(token_data["id"]) + ".glb")
            output_path_str = output_path.as_posix()
            
            print("Exporting", token_data.get("name", token_data.get("id")), "to", output_path_str)
            export_from_token_data(token_data, output_path_str)
        show_all_meshes()
    
    if __name__ == "__main__":
        export_collection(COLLECTION_METADATA, OUTPUT_DIRECTORY) 
        
        
    {
      "name": "Badge name",
      "description": "This is a badge",
      "image": "https://image.url"
    }
    jsonCopyEdit{
      "name": "Badge name",
      "description": "This is a badge",
      "type": "badge",
      "image": "https://image.url"
    }
    <ChainID>:<ContractAddress>:<TokenID>
    33139:0x1122334455667788991122334455667788991100:5

    Domain Check

    Date of change: 15/08/2025

    Affected Features: Deployment access

    What’s broken and why?

    Nothing is broken, however we now do a domain check when accessing your world within the web app to ensure that the world you are accessing is pointing to the same domain that you're accessing it from. This is essential to ensure correct functionality of the ODK. If you do access a world that is running on a different domain, you'll see this message. Simply select the "Redirect" button to be taken to the correct domain, and then enter your world.

    Emotes Refactor

    Date of change: 15/08/2025

    Affected Features: Emotes

    What’s broken and why?

    The coop emote and standard emote flows have been unified to some degree. There may be more unification work in future. The BPC_ODK_EmotesComponent has become the morpheus actor component BPMC_ODK_EmotesComponent and now lives on the BPM_ODK_PlayerCharacterBase. Some functions have had an API update. Following this documentation should help with those change: https://docs.otherside.xyz/odk-documentation/documentation/odk-plugin/emotes

    How to fix it?

    You will now need to grab the component off the BPM_ODK_PlayerCharacterBase.

    Updated allowed avatar collection list

    Date of change: 13/08/2025

    Affected Features: Avatar management

    What’s broken and why?

    To unify where allowed avatar collections are retrieved, we've moved the definition into the backend. This means that if you need to modify your avatar collections going forward (outside of Yuga's defaults) then you'll need to request an update to your project.

    How to fix it?

    Delete any overrides you have for your project.json to ODK.AvatarSelector.AllowListCollectionContractsArray , and if you need specific collections filtered for your project, please reach out within your nearest Yuga support channel.

    Token ID management rework

    Date of change: 12/08/2025

    Affected Features: Token management

    What’s broken and why?

    To support token management across multiple chains (not just ape chain), we've introduced the concept of an "experience group" that defines what chain + contracts your world should be operating on. This information is provided on startup, and you're able to retrieve it via BP_ODK_WalletWorldService as you normally would (previously the information was hardcoded within live config). The one difference is that TokenIds themselves will need to be updated dependent on the chain you're operating on. That means that utilities like BP_GetTokenIDBase and BP_GetTokenIDFromLiveConfig have been updated to be able to provide two token ids (the correct ID will be automatically grabbed dependent on the chain you're operating on).

    How to fix it?

    For each instance of BP_GetTokenIDBase , be aware that you'll need to provide a curtis token id if/when you start operating on curtis. For each instance of BP_GetTokenIDLiveConfig , this object now expects an array of two numbers (instead of just a single number). Go through any definitions in live config, and update the schema to provide an array of two numbers. For instance:

    Project Live Config Location Update

    Date of change: 12/08/2025

    Affected Features: Live Config

    What’s broken and why?

    Previously the project.schema.json was versioned with project content, and both ODK and project values were added to this single file. This meant whenever you took an ODK update, projects needed to manually merge any new ODK config changes into their project.schema.json. We've now split the schema file and it'll be versioned with the ODK plugin itself. This means the project.schema.json file is available for exclusive use of the project, and you'll not need to do any manual merging going forward.

    How to fix it?

    Navigate to your project's project schema file (<ProjectDir>/Config/LiveConfig/Schemas/project.schema.json) and remove the "ODK" object blob from the file. If you've not added any of your own live config overrides to the project schema, you can remove the file entirely.

    How to test it?

    Start a PIE session in editor, and confirm that you see the green "schema generated successfully" toast appear.

    New Bootflow Singleton

    Date of change: 12/08/2025

    Affected Features: Startup flow

    What’s broken and why?

    To help simplify the startup logic, we've added a new bootflow singleton that coordinates retriving dynamic world information, and providing it to server/clients as part of the bootflow process.

    The singleton is called BPM_ODK_BootflowSingleton and currently provides two purposes, but will be extended in future to solidify the ODK bootup experience.

    1. Retrieve smart chain contract details and then replicate them out to users. This allows developers to easily switch between different chains + contracts. You can listen for this event in your internal logic by using the WaitForCondition node and waiting on ExperienceDataSet

    2. Retrieve allowed avatar contract list and apply before user avatar is applied. This ensures your clients adhere to the current avatar contract policy deploy by Yuga. You can listen for this event in your internal logic by using the WaitForCondition node and waiting on AllowedAvatarsRetrieved

    How to fix it?

    For any previously created levels, please add the BPM_ODK_BootflowSingleton to the AdditionalSingletons array within the World Settings. For any new levels the singleton will be added automatically.

    How to test it?

    Ensure your bootflow starts correctly, and you're able to apply your set avatar model.

    Movement Mode Refactor

    Date of change: 29/07/2025

    Affected Features: Movement Modes

    What’s broken and why?

    As part of an effort to make movement modes more extendable and to simplify their structure, some breaking changes have been made. A new movement mode component (BPC_ODK_MovementComponent) has been added to the render target to help manage movement modes The main breaking changes are: - BPE_Y_MovementModes has been removed. Checking for a given movement mode can be done by either querying the new BPMC_ODK_AnimVarsComponent MovementMode if needed on non auth clients or BPC_ODK_MovementComponentBase::GetCurrentMovementModeId on auth clients. - BPMC_ODK_AnimVarsComponent has an updated event dispatcher: OnAnimationMovementModeUpdated. Ensure any code triggered when the anim vars component updates it's movement mode use this new event dispatcher are fixed up. - If you need to change to a movement mode, you can use BPC_ODK_MovementComponentBase::TrySetMovementMode. - You may need to change update some of your bindings to listening to the new event dispatcher: BPC_ODK_MovementComponentBase::OnMovementModeUpdated. - If you have custom movement modes, it may be worth converting them to use the new ODK movement mode system. Documentation can be found here: https://docs.otherside.xyz/odk-documentation/documentation/odk-plugin/movement-modes

    How to test it?

    Ensure your logic behaves as it did before these changes.

    Improved Mixpanel Analytics to support other data types

    Date of change: 17/07/2025

    Affected Features: Mixpanel Analytics

    What’s broken and why?

    To allow support for more data types, in this case Int, Floats and Bools, we have added an enum to the event struct that allows you to tell the API which data type you wish to use.

    How to fix it?

    If you have existing mixpanel event structs being created (S_ODK_AnalyticsEventData), you'll need to open, recompile and save any BP that is creating them. They default to String type, so nothing else will change. However, you may want to take this opportunity to swap any Ints, Floats or Bools over. Just select the type you want from the Make Struct node, then hit the show more arrow at the bottom of the node to reveal the types. Here is an example of each type:

    How to test it?

    On you Mixpanel dashboard, you can view your events. If you select "Full JSON" view, you'll see that any event that has been swapped over will now be its correct type.

    Removal of BPFL_ODK_WidgetHandler.

    Date of change: 01/07/2025

    Affected Features: UI Mode

    What’s broken and why?

    The API has been modified to more generic allowing generic uobject context to be used to request UI mode rather than widgets

    How to fix it?

    Simply use MarkContextNeedsUIMode and UnmarkContextNeedsUIMode instead of the old library helper fucntions.

    How to test it?

    Ensure UI mode works as you intended.

    Creating a Quest token


    🔹 1. Create a New Quest Token

    • Click “Define New Object”

    • Select your experience from the dropdown

    [/Script/JunoCoreUI.J_UISkinsSettings]
    bEnableLegacySkinningSystem=True
    "TokenIDLiveConfigName": {
    	"type": "array",
    	"items": {
    		"type": "number"
    	},
    	"default": [1, 8],
    	"description": "On apechain, the token is 1. On curtis, it is 8."
    }
  • Choose the “Quest” template

  • You’ll now see the Quest definition form


  • 🔹 2. Fill Out the Quest Metadata Form

    🏷️ General Fields

    Field
    Purpose

    name

    Display name of the quest

    description

    Summary of what the quest is about

    image

    URL to the image shown in UI

    type

    Must be "quest" — this enables quest tracking in-game


    📦 quest Object Fields

    Field
    Description

    group

    Logical group for filtering in UI or in-game

    xp

    Optional XP awarded on completion

    tasks[]

    List of task definitions, each with a title and description

    rewards[]

    Optional: Array of Token IDs to grant on quest completion. For help creating a badge, see

    After completing the form, you will be presented with the generated metadata


    🧾 Example Quest Token Metadata


    📌 Important: completionQuantity will be automatically set to one more than the number of tasks This accounts for:

    • 1 token to start the quest

    • 1 token per completed task

    Example: 3 tasks → completionQuantity: 4 → 1 token to start + 3 tokens for each completed task.

    Before minting:

    Once development is complete this token should be non-transferable so that users cannot trade progress or restart the quest, but during the development process it is better to set this to transferable so that it can be deleted from a users wallet and allow the user to retry the quest.

    • Price → usually 0 for dev

    • Transferable:

      • ✅ True during dev — allows testing/resetting progress

      • 🚫 False for production — prevents replaying quests via trading tokens


    🔹 6. Mint the Token

    After saving, click “Mint”.

    You’ll be returned to the creator dashboard and shown a Token ID in this format:

    Example:

    Part
    Meaning

    33139

    Chain ID (APECHAIN)

    0x1122...

    Contract address for your experience

    :1

    Unique identifier for this token


    🔹 7. Preview & Access Metadata

    • You can click the token’s image on the dashboard to view metadata.

    • This page reflects the JSON you just configured and is used in-game by clients and UI.

    • You can update the token metadata at any time using the "Update Metadata" button

    Asset Production Best Practices

    • General Approach 3D Asset Production

    • Generalized QA Approach

      • Load GLBs into an M2 Editor

    Production Guidelines

    Below are some general guidelines that have helped the Yuga create Avatar collections. This aren’t hard requirements but should help in the production of 10s of thousands unique avatars.

    General Approach 3D Asset Production

    Concept Art

    • Clarity of Design: Does the concept art clearly convey the character’s design, including front, side, and back views?

    • Consistency with Brief: Does the character design align with the initial brief and the project’s artistic style?

    • Detail Level: Are the details sufficient to guide the modeling process?

    Modeling

    • Proportions: Are the character’s proportions accurate and consistent with the concept art?

    • Topology Quality: Is the topology clean, with appropriate edge flow for animation and deformation?

    • Detail Level: Does the model capture all necessary details while maintaining a reasonable polygon count?

    • Maya Mesh Clean up: To use the Mesh Cleanup

    1. Select the Mesh:

      1. First, select the mesh object you want to clean up. You can do this by clicking on the object in the viewport or selecting it from the Outliner.

    2. Open the Mesh Cleanup Tool:

      1. Go to the top menu and navigate to

    This tool is handy for preparing a mesh for further processing, especially when dealing with 3D models that need to be optimized for gaming or other real-time applications.

    If you encounter specific issues during cleanup, you can adjust the options or manually fix the geometry where necessary.'

    Here are key areas to consider as 3D Modeler:

    1. Topology and Geometry

    • Clean Topology: Ensure the mesh is clean, with evenly spaced polygons and no unnecessary vertices or edges. This helps with smoother animation and better performance.

    • Avoiding N-gons: Stick to quads or tris, as N-gons can cause issues during subdivision or when deforming the model.

    • Edge Flow: Proper edge flow is crucial for smooth deformations in animations, especially in character modeling.

    2. UV Mapping

    • Efficient UV Layout: Avoid stretching or overlapping UVs to ensure textures map correctly and appear consistent.

    • UV Packing: Maximize texture space by efficiently packing UVs, but avoid distorting the model in the process.

    • Seams Placement: Place UV seams in less visible areas to minimize noticeable texture breaks.

    3. Scale and Proportion

    • Consistent Scale: Ensure all models are scaled consistently to avoid issues during the integration into scenes or with other models.

    • Correct Proportions: Keep the model's proportions accurate, especially when working from concept art or real-world references.

    4. Detail Level

    • Appropriate Detail for Context: Balance the level of detail according to the model's use case (e.g., close-up vs. background assets).

    • Normal Maps for Detail: Use normal maps to add fine details without increasing polygon count.

    5. Texture Quality

    • High-Resolution Textures: Ensure textures are of high resolution and free from artifacts, especially for hero assets.

    • Consistent Texturing: Maintain consistent texturing across the model, avoiding visible seams or inconsistencies.

    6. Performance Optimization

    • Polygon Count: Keep the polygon count as low as possible without sacrificing quality, especially for real-time applications.

    • LOD (Level of Detail) Models: Create multiple levels of detail for the model to ensure it performs well at different distances.(Consider if LOD option is required)

    7. File Organization

    • Naming Conventions: Use clear and consistent naming conventions for all assets, materials, and textures.

    • Layer Management: Organize your layers and groups logically to make the file easy to navigate.


    Sculpting

    1. Detail Accuracy: Are the high-resolution details well-defined and true to the concept art?

    2. Subdivision Levels: Are the subdivision levels managed correctly to provide smooth transitions between levels of detail?

    3. Baked Maps: Are normal maps and other baked maps free from artifacts and accurately represent the sculpted details?

    SubD modeling

    Subdivision surface (SubD) modeling is a technique that allows you to create smooth, high-resolution surfaces from a low-resolution base mesh. To achieve optimal results in SubD modeling, there are several important rules to follow:

    1. Use Quads Exclusively

    • Stick to four-sided polygons (quads) as much as possible. Quads subdivide cleanly, creating smooth surfaces without causing artifacts. Avoid using triangles and n-gons, as they can create pinching and distortion when subdivided.

    2. Maintain Even Polygon Distribution

    • Keep the topology evenly distributed across the model. This ensures that when the mesh is subdivided, the surface remains smooth and consistent. Avoid areas with overly dense or sparse polygons, as this can result in uneven smoothing.

    3. Ensure Good Edge Flow

    • Design your edge loops to follow the natural contours and anatomy of the model. This helps maintain smooth deformations, especially in areas that bend or flex, such as joints. Proper edge flow also aids in adding detail to specific areas without affecting the entire model.

    4. Use Edge Loops for Definition

    • Add edge loops strategically to define sharp edges and detailed areas. Placing an additional edge loop close to an existing one can help control the sharpness of the resulting subdivided edge. This technique is useful for creating crisp corners or specific details in the model.

    5. Avoid Overlapping Vertices

    • Ensure that no vertices are overlapping or too close together. Overlapping vertices can cause issues during subdivision, leading to artifacts or unintended surface distortions.

    6. Minimize Pole Usage

    • A pole is a vertex where more than four edges meet. While sometimes unavoidable, poles should be minimized and placed in less noticeable areas of the model. Poorly placed poles can cause pinching and other artifacts when the mesh is subdivided.

    7. Use Crease and Hard Edges Sparingly

    • Some 3D software allows you to mark edges as creased or hard, preventing them from being smoothed during subdivision. Use this feature sparingly, as overuse can result in unnatural transitions between smooth and sharp areas.

    8. Keep Base Mesh Simple

    • Start with a simple base mesh before adding details. SubD modeling is most effective when the initial mesh is clean and simple, allowing for easy adjustments and predictable results after subdivision.

    9. Check Topology with Subdivision Preview

    • Regularly preview your model with subdivision applied to ensure that the topology holds up and that there are no unexpected issues. This helps catch potential problems early in the modeling process.

    10. Maintain Model Symmetry

    • When working on symmetrical models, ensure that the symmetry is maintained throughout the modeling process. This simplifies the modeling workflow and ensures that both sides of the model behave consistently when subdivided.

    11. Plan for UV Mapping

    • Keep in mind how the topology will affect UV mapping. Clean, evenly distributed quads will make UV unwrapping more straightforward, reducing the potential for stretching or distortion.

    12. Avoid Non-Manifold Geometry

    • Non-manifold geometry (edges shared by more than two faces or disconnected vertices) can cause problems during subdivision and rendering. Always check your model for non-manifold elements and correct them as needed.

    By following these SubD modeling rules, you can create high-quality, smooth models that are well-suited for animation, rendering, and other advanced workflows.

    SubD Examples:

    Retopology

    Optimization: Has the polygon count been optimized without losing important details?

    Edge Flow: Is the edge flow appropriate for animation, particularly around joints and areas of high deformation?

    Topology Efficiency: Is the topology efficient, minimizing unnecessary polygons while maintaining shape integrity?

    In 3D retopology, there are several common mistakes to avoid to ensure the model is optimized for animation, performance, and texturing. Here are some key things not to do:

    1. Overly Dense Topology: Avoid creating too many polygons, especially in areas that don’t require high detail. This can make the model unnecessarily heavy and hard to manage.

    2. N-Gons and Triangles: N-gons (polygons with more than four sides) and triangles should generally be avoided, particularly in areas that will deform during animation. They can cause unpredictable shading and deformation issues. Aim for quads (four-sided polygons) as much as possible.

    3. Uneven Distribution of Polygons: Don’t create a topology with uneven polygon distribution. This can result in poor deformation during animation and can make it difficult to achieve smooth surface details.

    By avoiding these mistakes, you can create a clean, efficient, and functional topology that will serve well in the final stages of the production pipeline.

    UV Unwrapping

    • Seam Placement: Are seams placed strategically to minimize visibility in prominent areas?

    • UV Layout: Is the UV layout efficient, maximizing texture space and minimizing stretching?

    • Packing Efficiency: Are UV islands packed effectively to ensure high-resolution textures?

    Hard surface UV examples: straighten UVs where you can.

    Texturing (Hand painted)

    When creating 3D hand-painted textures, particularly for games or stylized art, there are several key rules and best practices to follow to ensure a consistent and high-quality result. Here’s a guide to help you with the process:

    1. Understand the Style:

      1. Art Direction: Know the style you're aiming for. Hand-painted textures often emphasize stylization over realism, with exaggerated colors and simplified details.

      2. Reference Gathering: Collect references of the style you're targeting, whether it’s cartoony, fantasy, or another specific aesthetic.

    2. UV Mapping:

    Rigging

    • Bone Structure: Is the skeleton appropriately structured for the character’s anatomy and intended range of motion?

    • Skinning: Does the character deform correctly during animation, with smooth transitions and minimal distortions?

    • Control Rig: Are the controls intuitive and functional for the animators, allowing for a wide range of expressions and movements?

    Animation

    • Cycle Quality: Are standard animation cycles (e.g., walk, run, idle) smooth, natural, and loop seamlessly?

    • Custom Animations: Do custom animations meet the character’s role requirements, showing personality and purpose?

    • Fluidity: Are the animations fluid and believable, with no noticeable errors or jerks?

    Generalized QA Approach

    Our approach to QA’ing large collections quickly and effectively.

    • Most collections minimum number of Avatars that represent all traits is between 50-300, we iterate on that subset of avatars fixing issues until all traits have been validated in an Assembled Avatar.

    • Then we add that list to a larger list (usually 25% of total collection size) to identify any issues that would have been missed in the 50-300 QA group.

    We feel this is a good compromise between speed and coverage, however each collection poses unique challenges this QA strategy might not be applicable to all cases.

    Load GLBs into an M2 Editor

    To test avatars as close to how they will appear being loaded into the game from an external source without going through any ODK tooling you can use the gltf runtime plugin to load GLBs into unreal. You can load an individual GLB into the editor by dragging and dropping into the content browser.

    Check Textures

    Do a visual inspection of textures to ensure they look correct.

    Common Issues:

    • Lines appear at UV seams, items appear concave when they should appear convex or vice versa.

      • The normal map format for GLB is Y+, this is common for opengl shaders. This is different than directx or Y-

    • Lines appear at UV seams, depth of detail is not as deep as it is supposed to be.

    Check for Overlapping Polygons

    Based on topology and weighting it is very common for overlapping polygons to intersect. For example if the topology and weighting of a t-shirt doesn’t exactly match the topology the body may clip through the shirt while the avatar is deforming. There are two main routes to remediate this issue.

    • Model the traits/slots so when the body has the t-shirt, there are no polygons on the body under the shirt.

    • Create overlap masks that hide polygons under the overlapping slot.

    Check Skin Weighting

    Visually identify any portions of the Avatar which is not deforming as expected with the set of animations that are included in the ODK .

    Checking every single animation sequence may not be feasible, if not checking against the entire list, skipping variations of the same type of animation, i.e. idle and clapping is fine. Prioritize locomotion for general gameplay, however the range of motion will be pushed to the limit in emotes, i.e. any of the dancing.

    Creating an animation blueprint that loops through animations, or allows the selection of specific animations may be useful if opening up animations to a QA team natively on ODK/M2, but loading a skeletal mesh into unreal with the UE5 skeleton set as the mesh skeleton will allow you to play animations on the mesh in the Editor quickly and easily.

    v8.2

    Updates to ODK's project schema

    Date of change: 02/07/2025

    Affected Features: Users with existing ODK projects

    What’s broken and why?

    Currently ODK schema is embedded within the `ODK` section within the project.schema. For legacy reasons, the ODK schema was instantiated with each template, rather than stored with the plugin content itself. To ensure you're operating with the latest, correct ODK schema, please integrate the latest schema here into your project.

    How to fix it?

    jsonCopyEdit{
      "name": "Example Quest",
      "description": "Do your exercises",
      "image": "https://generatedimage.link",
      "type": "quest",
      "quest": {
        "type": "global",
        "group": "whitespace",
        "displayType": "quantity",
        "completionQuantity": 4,
        "xp": 123,
        "tasks": [
          {
            "title": "Jump 3 Times",
            "description": "Perform 3 jumps to get your body moving!"
          },
          {
            "title": "Run 500m",
            "description": "Run a total distance of 500 meters. Cardio is key!"
          },
          {
            "title": "Glide for 100m",
            "description": "Glide gracefully through the air for at least 100 meters!"
          }
        ],
        "rewards": [
          "33139:0x1122334455667788991122334455667788991100:3",
          "33139:0x1122334455667788991122334455667788991100:4"
        ]
      }
    }
    ChainID:ContractAddress:TokenID
    33139:0x1122334455667788991122334455667788991100:1
    tool in Maya, follow these steps:
    Mesh
    >
    Cleanup
    .
  • Adjust Cleanup Options:

    1. The Cleanup Options window will pop up. Here, you can choose specific issues to clean up. The options include:

    2. Remove Geometry: For deleting specific types of geometry like non-manifold edges, lamina faces, zero area faces, etc.

    3. Tessellation Cleanup: For correcting tessellation problems, like polygonal faces with more than four edges.

    4. Repair Geometry: Fixes issues such as zero length edges and zero geometry area.

  • Set the Operation Mode:

    1. Select Matching Polygons: This highlights the problematic areas in the mesh without making changes, allowing you to see where issues exist.

    2. Cleanup Matching Polygons: Automatically fixes the problems based on the criteria you set.

    3. Cleanup Non-Planar Faces: Specifically targets and fixes non-planar faces.

  • Execute the Cleanup:

    1. Once you've set your options, click Apply or Cleanup to execute the operation.

  • Review the Results:

    1. After the cleanup, review the mesh to ensure that the issues have been resolved. You can use the Select Matching Polygons option again to double-check.

  • Ignoring Edge Flow: Proper edge flow is crucial for smooth deformation, especially in areas that bend or stretch. Avoid creating random or chaotic edge loops that don’t follow the natural contours of the model.
  • Pinching and Stretching: Don’t create areas where edges are too close together (pinching) or too far apart (stretching). This can lead to issues with texturing and shading, as well as poor deformation in animations.

  • Overcomplicating the Mesh: Avoid adding unnecessary loops or geometry that doesn’t contribute to the form or function of the model. This can lead to increased file size and more complex rigging and animation processes.

  • Inconsistent Polygon Sizes: Keep polygon sizes as consistent as possible, especially in areas that require smooth deformation. Large variations in polygon size can cause issues with normal maps and other texturing processes.

  • Ignoring the Silhouette: Don’t neglect the silhouette of the model when retopologizing. Ensure that the topology supports the overall shape and form, maintaining the original design's integrity.

  • Neglecting UV Layout Considerations: Retopology should consider the eventual UV layout. Avoid creating complex topology that would make UV unwrapping difficult.

  • Overlapping Geometry: Ensure that there are no overlapping vertices, faces, or edges. Overlapping geometry can cause issues in rendering, texturing, and animation.

  • Efficient UV Layout: Ensure your UVs are well laid out with minimal stretching. This will make painting easier and more accurate.

  • Texture Space Optimization: Use as much of the UV space as possible to maximize texture resolution, avoiding empty areas in the UV layout.

  • Consistent Texel Density: Maintain consistent texel density across the model, so the details are uniform in size.

  • Base Colors and Gradients:

    1. Block in Base Colors: Start by laying down base colors, defining the overall color scheme of the object. This serves as a foundation for further details.

    2. Gradient Application: Apply subtle gradients to avoid flatness. Gradients can suggest light direction or simply add visual interest.

  • Painting Details:

    1. Use of Brushes: Work with soft and hard brushes to create sharp edges where needed and soft transitions elsewhere. Experiment with different brush settings for unique textures.

    2. Material Definition: Clearly define different materials (wood, metal, cloth) by painting in their characteristic details and surface qualities.

    3. Hand-Painted Shading: Paint light and shadow directly onto the texture. This is a key part of the hand-painted style, as it doesn’t rely on real-time lighting.

    4. Highlights and Speculars: Manually add highlights to areas where light would naturally catch, avoiding the need for dynamic lighting. Stylized highlights can enhance the visual appeal.

  • Color and Contrast:

    1. Color Harmony: Stick to a defined color palette to maintain harmony across the texture. Avoid over-saturating or under-saturating colors unless the style specifically calls for it.

    2. Contrast: Add contrast by differentiating between light and dark areas. This adds depth and helps the texture read better at a distance.

  • Detailing and Wear:

    1. Texture Details: Add small details like scratches, wear and tear, or seams where appropriate. These details can make a texture feel more lived-in and believable.

    2. Edge Highlighting: Use edge highlights to emphasize the edges of objects, making them stand out more.

    3. Consistent Detailing: Ensure that the level of detail is consistent across the entire texture, preventing any one area from looking too busy or too plain.

  • Avoiding Procedural Looks:

    1. Hand-Paint Everything: Avoid procedural or automatic tools as much as possible. The charm of hand-painted textures comes from their personal, crafted feel.

    2. Break Repetition: Add small variations to prevent patterns from looking too uniform or repeated.

  • Final Touches:

    1. Texture Review: Continuously review the texture on the model to see how it looks from various angles and distances.

    2. Polishing: Once the main texture work is done, add any final touches like subtle noise, color adjustments, or small details that enhance the overall look.

    3. Testing in Engine: If the texture is for a game, always test it in the engine to see how it looks under different lighting conditions and in the context of the game.

  • At some point in the pipeline the texture is being set to an sRGB Gamma Curve. Make sure the image transfer function is raw/linear.
  • Item is shinier than it is supposed to be.

    • At some point in the pipeline the texture is being set to an sRGB Gamma Curve. Make sure the image transfer function is raw/linear.

  • Texture appears blocky

    • Try to avoid using JPG compression with Normal Maps, especially on almost flat surfaces.

    • If normal map is fine check compression settings on other texture maps.

  • Check Textures
    Common Issues:
    Check for Overlapping Polygons
    Check Skin Weighting
    GitHub - rdeioris/glTFRuntime: Unreal Engine Plugin for loading glTF files at runtime
    Retrieve the latest `project.schema.json` from the ODK. Accessible here, or within any v8.2 template. Merge this file with the existing project.schema.json that lives at <project_workspace>\Config\LiveConfig\Schemas. If you haven't made any local project edits, you can safely just replace the file, otherwise you should merge the content with your local changes.

    Connect Button on the Dashboard redirects to https://o7e.dev/ instead of the GFN Stream.

    Date of change: 30/06/2025

    Affected Features: Otherside Dashboard and GFN streams.

    What’s broken and why?

    Due to us needing to authenticatee with Privy we need the Connect button on the dashboard to send the user via the web app rather then the legacy GFN streaming page. This means that users on older versions of the ODK will be unable to launch streams via the dashboard.

    How to fix it?

    Upgrade your project to ODK 8.2, this will then ensure that you are using Privy as authentication in your project and the Connect button will work again

    Bubbles Content Moved

    Date of change: 27/06/2025

    Affected Features: Bubbles

    What’s broken and why?

    The bubbles chat feature has been isolated into it's own template within the ODK ecosystem.

    How to fix it?

    If reliant on the bubbles feature, please integrate the Bubbles Template from the ODK Launcher, and migrate the content within to your project.

    ODK Settings Menu

    Date of change: 18/06/2025

    Affected Features: Pause/Settings Menu

    What’s broken and why?

    The default Pause/Settings menu widget is now set on the PlayerController > BPC_ODK_PauseMenuControlComponent and the default widget has been changed from the M2 settings menu to WBP_ODKSettingswidget that leverages the ODK Base UI

    How to fix it?

    If you have previously used a custom settings widget, this should be set on the BPC_ODK_PauseMenuControlComponent component.

    Inspector Admin Capability Update

    Date of change: 12/06/2025

    Affected Features: Inspector Admin

    What’s broken and why?

    As part of the class hierarchy rework, the way we determine if you can access the Inspector has been updated.

    How to fix it?

    For any role that you want to have access to the Inspector (historically just Director) add the Capabilities.Morpheus.InspectorEnabled capability to the GrantedCapabilities field within your data table for those roles.

    Retirement of Avatar Selector

    Date of change: 11/06/2025

    Affected Features: Avatar Selector

    What’s broken and why?

    As we've moved to using the Web UI for more in-game functionality, we've retired the existing avatar selector (BPC_AvatarSelector and associated content).

    How to fix it?

    If you were previously relying on explicitly triggering the avatar selector during gameplay, instead you should use the Web UI for selecting a player character. To do so call OpenAvatarOverlay on the BP_WebBrowserWorldService and the web browser will present the user's avatars to them.

    How to test it?

    Validate that your users can correctly update their avatars when you trigger the avatar selector.

    Crowd Audio Component Change

    Date of change: 11/06/2025

    Affected Features: Crowd Audio

    What’s broken and why?

    We have moved away from the soon to be depreciated crowd audio component and have our own ODK version.

    How to fix it?

    Simply switch out the BPMC_CrowdAudio component to BPMC_ODK_CrowdAudioComponent. They should have the same interface.

    Attachments Component

    Date of change: 02/06/2025

    Affected Features: Attachments

    What’s broken and why?

    Logic that handled adding attachments to the player has been moved to a new morpheus component: BPMC_ODK_AttachmentsComponent. This functionality was moved off BPM_ODK_PlayerCharacterBase. This means you may have some bad references in your projects

    How to fix it?

    Simply grab the component off your morpheus player character and use that instead of directly interfacing with BPM_ODK_PlayerCharacterBase.

    UI Mode Component

    Date of change: 02/06/2025

    Affected Features: UI Mode

    What’s broken and why?

    The UI mode world service has been moved to a component on the player controller. Some functions that were previously on the player controller have been moved to this component. Additionally, now it supports changing UI mode from anywhere, not just widgets. If you interfaced with the UI mode world service directly, you should now go through the player controller component.

    How to fix it?

    Update you blueprint logic to use the new player controller component.

    Update to Profile Data Provider class

    Date of change: 23/05/2025

    Affected Features: Profiles

    What’s broken and why?

    With the move to a new authentication backend, we have a new mechanism for retrieving your user's profile infomation.

    How to fix it?

    1. For any prior levels, you need to manually update the "Profile Data Provider Class" to BP_ODK_WPProfileDataProvider

    How to test it?

    Log in and validate that your characters are retrieving their player names and other profile information correctly.

    Moved default emote definition to BPC_ODK_EmotesComponent

    Date of change: 20/05/2025

    Affected Features: Emotes

    What’s broken and why?

    Previously, the default emote array was defined in the roles Data Table, while CoOp emotes were specified in the BPMC_CoopEmote component and purchasable emotes in the BPC_ODK_EmotesComponent. These separate configurations have now been unified into a single location: the BPC_ODK_EmotesComponent on the Player Character.

    This consolidation simplifies emote management by centralizing all emote definitions—default, purchasable, and CoOp—in one place and enables runtime overrides of the emote list, for both default and CoOp emotes.

    The current roles data table definitions will still work, but will likely be deprecated in the future and this workflow will no longer be supported.

    How to fix it?

    • Remove the emote definition from the roles data table. • Create a Data Asset of type PDA_ODKEmoteCollection with your defined emotes • On your Player Character, find the BPC_ODK_EmotesComponent and assign the Data Asset to the ODKEmoteCollection variable

    How to test it?

    Play in editor and bring up your emote wheel. The defined emotes should appear on the wheel.

    Koda Lod Level replacement with BPDA_LODLevelsByAvatar

    Date of change: 18/05/2025

    Affected Features: Avatars

    What’s broken and why?

    We've modified the way crowd animations are setup per avatar type. If you had previously customized the Koda Lod Levels property on a derived class of BPM_ODK_PlayerCharacterBase, you'll need to update your usage.

    How to fix it?

    1. Create a new DA from BPDA_LODLevelsByAvatar

    2. Set your existing BPDA_LODLevels properties per avatar

    3. Set your new DA as the default instance on your derived BPM_ODK_PlayerCharacterBase class

    How to test it?

    Validate that your characters use the correct ABP content when being rendered in the crowd

    Editor Sign-in Changes

    Date of change: 18/05/2025

    Affected Features: PIE session

    What’s broken and why?

    The way you login needs to be updated to use our new authentication system

    How to fix it?

    For any existing project, you'll need to modify your authentication settings. 1. Open to Editor Preferences -> General: Sign In Settings 2. Expand Per Client Sign In Settings 3. Modify each client's sign-in settings to: "Custom" : "https://o7e.preview.msquared.io/api/editor/login" 4. Restart your editor

    How to test it?

    Start a PIE session and ensure your client can connect to your local deployment.

    Jump Component

    Date of change: 15/05/2025

    Affected Features: Player Character

    What’s broken and why?

    We moved some of our jump logic inside a component to manage jumping.

    How to fix it?

    Interface with the new component to enable and disable jumping.

    Text Chat Update

    Date of change: 14/05/2025

    Affected Features: Text Chat

    What’s broken and why?

    We have updated text chat in the ODK to use a purely unreal base solutions.

    How to fix it?

    You will need to add WBP_ODK_TextChat to your HUD to use the text chat functionality.

    How to test it?

    Test the newly added widget works in game.

    Persistence Update

    Date of change: 13/05/2025

    Affected Features: Persistence

    What’s broken and why?

    The persistence world service has had it's API updated.

    How to fix it?

    Simply update your blueprint logic to use the new API. The biggest change is that now after registering interst in a value, you will not have the callback executed with the current value. You should use the read API after subscribing to get the initial value.

    How to test it?

    Ensure your persistence logic is still working.

    Removal of Vending Machines

    Date of change: 02/05/2025

    Affected Features: Vending Machines

    What’s broken and why?

    Removed the user collection vending machines. New vending machines should use the overlay rather than in game UI. An example of the overlay vending machine is in the new Boneyard Template.

    How to fix it?

    Not necessarily something you can fix. Vending machines would have to be redesigned from scatch.

    Asset removals

    Date of change: 18/03/2025

    Affected Features: Various

    What’s broken and why?

    There are a number of redundant assets in the ODK we are removing. Downstream projects may be using these and could potentially be affected.

    List of assets: - BPMC_PlayerTags - BPMC_PickupManager now move to the base template from the ODK plugin

    How to fix it?

    This probably wont affect anyone. If it does reach out to your Yuga Representative with any problems.

    Interaction Refactor

    Date of change: 17/03/2025

    Affected Features: Interaction System

    What’s broken and why?

    We wanted to revist the interaction system and make sure it was up to date. Some configuration properties on interactable component may need updating.

    How to fix it?

    The only thing that should be broken are the configuration properties on the interactable component.

    How to test it?

    Reconfigure the configuration properties on the interactable component if needed.

    Selfie Camera Refactor

    Date of change: 26/02/2025

    Affected Features: Selfie Camera

    What’s broken and why?

    Wanted to refactor the selfie cam to make some paths simpler. If you have tinkered with the core selfie cam classes (which you likely have not) you may be affected. How we structure data regarding scanned objects in screenshots has changed. This will affect anyone attempting to read this data.

    How to fix it?

    Reach out to your Yuga Representative with any problems.

    World Services update

    Date of change: 21/02/2025

    Affected Features: Widget Handler and Persistence Manager

    What’s broken and why?

    We are moving these system over to use the "World Service" pattern that was not available when first created. If you are using BP_PersistenceManager or BPC_ODK_WidgetHandlerComponent directly, you will run into issue.

    How to fix it?

    Instead of using BP_PersistenceManager, you can now call GetPersistenceWorldService from BPFL_ODK_PersistenceWorldService. The returned world service should have the same API as the old BP_PersistenceManager. If you were using BPC_ODK_WidgetHandlerComponent, you should instead use BPFL_ODK_WidgetHandler. It has a similar API that should be easy to move over.

    Reparenting of Base Classes

    Date of change: 26/02/2025

    Affected Features: Core player classes updated to remove BP_Origin_PlayerCharacter , BPM_Origin_PlayerCharacter , BP_M2_PlayerCharacterBase , BPM_M2_PlayerCharacterBase, BP_PlayerController , BP_ODK_PlayerControllerBase, J_CharacterBase and JM_CharacterBase from our hierarchy.

    What’s broken and why?

    To provide a less opinionated, and more streamlined base ODK experience, and in conjunction with M2, we've deprecated some content that was consider superfluous for ODK purposes. For the purposes of this change, these include the player character classes mentioned above. Any class that derives from the BP_ODK_PlayerCharacterBase ,BPM_ODK_PlayerCharacterBase , BP_M2_PlayerCharacterBase , BPM_M2_PlayerCharacterBase, J_CharacterBase , JM_CharacterBase , BP_ODK_PlayerControllerBase, BP_PlayerController or interacts with those classes might be impacted. Any content functionality that has been removed from the base hierarchy, you're free to move to your project's character classes.

    How to fix it?

    Any components or variables that were resident on the classes listed above, that are still in use by your project, will need to be migrated to your base class version of that class. For example if you were using the BPMC_ApproachabilityFollowTarget component in your morpheus actor character class, you'd readd an instance of that component to your project's verison of the class, and then fixup any references. If in doubt, please reach out in support. Any reference to these class needs to be updated. This includes casts and properties.

    How to test it?

    Run a linter pass on your Blueprint content that confirms all content compiles as expected. Additionally make sure all your game features work as intended.

    29KB
    project.schema.json
    Open
    Creating a Badge token