Skip to content

Target Code Architecture

Problems in mixing DOTS and Managed code

  • To retrieve data from DOTS, queries are needed
    • Query is not efficient to be created from outside systems
    • SystemAPI is not accessible outside of systems
  • Managed code breaks DOTS features
    • Make it not Burstable, and reduce performance
    • Incompatible with Jobs, and reduce performance
  • Making DOTS changes from Managed code
    • Require queries and archetypes, which is not readily available in managed code
    • Changes made is out of system orders. So the changed value is only available next frame. Multiple changes could lead to multiple frames delay.
  • Detect changes on DOTS data
    • Require ReactiveComponent, which sequentialise DOTS code
    • Direct detection is limited because of requirement of queries
  • Mixed of Hybrid Baking and SubScene Baking
    • Hybrid Baking is executed at runtime, while SubScene Baking is executed in Editor time. This makes sharing data between the two difficult.

Hybrid Architecture

The game consists of three layers: DOTS, Managed and Bridge. - DOTS Layer uses Pure-DOTS implementations with ECS and HPC# (High Performance C#). It mostly deal with game simulation and logics, processing player inputs, configs and backend data. It computes data and events for the Bridge Layer. - Managed Layer use all other Unity and C# features. It mostly deal with visualization, capturing player inputs and baking config data. - Bridge Layer facilitates data communication between the two layers. Captures data from the DOTS Layer and populates the Managed Layer with it, similarly, it also fowards and converts data from the Managed Layer into the DOTS Layer.

Why

  • To get the best of both worlds (Unity & DOTS) while not affected by DOTS shortcommings.
  • Encapsulation and abstraction in each layer simplify the complexity of writing code.

DOTS Layer

Pure DOTS, strictly using only Burstable code. No usage of any C# feature that is outside of High Performance C#

Managed Layer

This layer has full access to all C# features and should not use DOTS at all. The idea is to utilise all that C# has to offer and not get restricted by DOTS' shortcomings, or the incompatibility of two different programming paradigms (Object Oriented vs Data Oriented). In this layer we can do everything that DOTS is not good at or not designed for: - Object oriented programming - Async - UI - Other system features: sound, payment, plugin, etc... - Visualization that can't be done in DOTS using Game Objects - The game flow starts and ends in this layer

Bridge Layer

This layer has access to both DOTS and all other Unity features. This layer facilitate data communication between the other two layers.

Pull Data from Managed Layer

  • Data is pulled from Managed Layer and store in singleton entities. Runtime data is updated every frame.
  • The data output out from Managed Layer should be in DOTS-compatible formats.
  • This is designed to limit managed components, which is causing problems in burstifying and jobifying code.

Push Data to Managed Layer

  • Events happened in the DOTS Layer (value changes, new entities, ...) are translated to a list of event objects so that the Bridge Layer can consume in the next frame and forward them to the Managed Layer. Only subscribed events will be translated.

Pull Data from DOTS Layer

  • Systems are used to expose data using query. Public method on these systems are exposed via interfaces. Data could be retrieve with keys such as Entity or other ids.
  • This abstraction layer helps insulate Managed Layer from using Systems and get restricted by DOTS shortcommings.

Push Data to DOTS Layer

  • For occasional data changes, this happen via Commands. Method to create these commands are available in this layer. Commands are used to create changes to the game, which happens in DOTS Layer
  • Every-frame data changes should not be pushed to DOTS Layer, it should be pulled instead.

Modules and Layers

Each module could have assemblies representing different layers. The Bridge Layer should depend on both DOTS and Managed layer to be able to properly integrate them. Note that Managed Layer could also refer to DOTS Layer, mainly to use the Components as data parameters.

block-beta
    columns 1
    block:ML["Managed Layer\n \n "]
        columns 5
        MMA["Module A"]
        space
        space
        space
        MMC["Module C"]
        MMC --> MMA
    end
    space
    block:BL["Bridge Layer\n \n \n \n \n \n "]
        columns 5
        space
        space
        BMB["Module B"]
        space
        space
        BMA["Module A"]
        space
        space
        space
        BMC["Module C"]
        BMB --> BMA
        BMB --> BMC
    end
    space
    block:DL["DOTS Layer\n \n \n \n \n \n "]
        columns 5
        DMA["Module A"]
        space
        DMB["Module B"]
        space
        space
        DMB --> DMA
    end
    BMA --> MMA
    BMB --> MMC
    BMC --> MMC

    BMA --> DMA
    BMA --> DMB
    BMB --> DMB
    BMC --> DMB


Module groupping could be flexible

block-beta
    columns 1
    block:ML["Managed Layer\n \n \n "]
        columns 5
        MMA["Module A"]
        space
        space
        space
        MMC["Module C"]
        MMC --> MMA
    end
    space
    space
    block:BL["Bridge Layer\n \n \n \n "]
        columns 5
        space
        BM_AB["Module AB"]
        space
        BM_BC["Module BC"]
        space
        BM_BC --> BM_AB
    end
    block:DL["DOTS Layer\n \n \n \n \n \n "]
        columns 5
        space
        space
        DMB["Module B"]
        space
        space
        DMA["Module A"]
        space
        space
        space
        DMC["Module C"]
        DMB --> DMA
        DMB --> DMC
        DMA --> DMC
    end
    BM_AB --> MMA
    BM_BC --> MMC

    MMA --> DMA
    MMC --> DMC

    BM_AB --> DMA
    BM_AB --> DMB
    BM_BC --> DMB
    BM_BC --> DMC

Migration Strategy