C# Interop

The CS global gives you access to any C# type.

Basic Usage

Access types through their full namespace:

// Static methods
CS.UnityEngine.Debug.Log("Hello from JS!")

// Static properties
const deltaTime = CS.UnityEngine.Time.deltaTime

// Create instances
const vec = new CS.UnityEngine.Vector3(1, 2, 3)

// Access instance properties
console.log(vec.x, vec.y, vec.z)

// Call instance methods
const normalized = vec.normalized

Your Own Code

Access your game code the same way:

// C# code in your project
namespace MyGame {
    public class GameManager {
        public static int Score { get; set; }
        public static void AddScore(int points) { Score += points; }
    }
}
// JavaScript
CS.MyGame.GameManager.AddScore(100)
const score = CS.MyGame.GameManager.Score

Creating Objects

Use new to instantiate C# classes:

// Create a GameObject
const go = new CS.UnityEngine.GameObject("MyObject")

// Add a component
const rb = go.AddComponent(CS.UnityEngine.Rigidbody)

// Set properties
rb.mass = 2.0
rb.useGravity = true

Generic Types

Create bound generic types with function syntax:

// List<int>
const IntList = CS.System.Collections.Generic.List(CS.System.Int32)
const numbers = new IntList()
numbers.Add(1)
numbers.Add(2)
numbers.Add(3)

// Dictionary<string, int>
const StringIntDict = CS.System.Collections.Generic.Dictionary(
    CS.System.String,
    CS.System.Int32
)
const scores = new StringIntDict()
scores.set_Item("player1", 100)

Async Methods

C# async methods return Promises in JavaScript:

// C#
public class DataLoader {
    public static async Task<string> LoadDataAsync(string url) {
        // ... async loading
        return data;
    }
}
// JavaScript
const data = await CS.MyGame.DataLoader.LoadDataAsync("/api/data")
console.log(data)

Events and Delegates

Subscribe to C# events:

// Get an event
const button = CS.UnityEngine.UI.Button.FindObjectOfType(CS.UnityEngine.UI.Button)

// Subscribe
button.onClick.AddListener(() => {
    console.log("Button clicked!")
})

Create delegates for callbacks:

// Create an Action delegate
const action = new CS.System.Action(() => {
    console.log("Action invoked!")
})

// Pass to C# methods that expect Action
SomeApi.DoSomething(action)

Enums

Access enum values directly:

// Access enum values
const space = CS.UnityEngine.Space.World

// Use in method calls
transform.Translate(new CS.UnityEngine.Vector3(1, 0, 0), space)

// Compare
if (keyCode === CS.UnityEngine.KeyCode.Space) {
    console.log("Space pressed!")
}

Arrays and Collections

Work with C# collections:

// C# arrays
const renderers = go.GetComponentsInChildren(CS.UnityEngine.Renderer)
for (let i = 0; i < renderers.Length; i++) {
    renderers[i].enabled = false
}

// Lists
const list = new (CS.System.Collections.Generic.List(CS.System.String))()
list.Add("one")
list.Add("two")
console.log(list.Count) // 2

// Access by index
console.log(list[0]) // "one"

Type Checking

Check types at runtime:

const component = go.GetComponent(CS.UnityEngine.Component)

// Check type
if (component.GetType().Name === "Rigidbody") {
    // It's a Rigidbody
}

// Or use typeof
const rigidbodyType = CS.UnityEngine.Rigidbody
if (component instanceof rigidbodyType) {
    // It's a Rigidbody
}

Common Patterns

Finding Objects

// Find by type
const player = CS.UnityEngine.GameObject.FindWithTag("Player")

// Find all
const enemies = CS.UnityEngine.GameObject.FindGameObjectsWithTag("Enemy")

// Find component
const camera = CS.UnityEngine.Camera.main

Coroutine Alternative

Since JS has async/await, you often don't need coroutines:

async function fadeOut(renderer, duration) {
    const material = renderer.material
    let alpha = 1.0
    const startTime = CS.UnityEngine.Time.time

    while (alpha > 0) {
        await new Promise(resolve => requestAnimationFrame(resolve))
        const elapsed = CS.UnityEngine.Time.time - startTime
        alpha = 1.0 - (elapsed / duration)
        material.color = new CS.UnityEngine.Color(1, 1, 1, Math.max(0, alpha))
    }
}

Input Handling

function checkInput() {
    if (CS.UnityEngine.Input.GetKeyDown(CS.UnityEngine.KeyCode.Space)) {
        console.log("Jump!")
    }

    const horizontal = CS.UnityEngine.Input.GetAxis("Horizontal")
    const vertical = CS.UnityEngine.Input.GetAxis("Vertical")
}

// Call every frame
function update() {
    checkInput()
    requestAnimationFrame(update)
}
requestAnimationFrame(update)

Performance Tips

  1. Cache type references: Don't look up types repeatedly in hot loops
  2. Batch property access: Get multiple values at once when possible
  3. Use fast paths: Common operations like Time.deltaTime are optimized
  4. Avoid reflection in loops: Pre-resolve types outside the loop
For performance-critical code running every frame (game loops, GPU dispatch, input processing), see the Zero-Allocation Interop guide. The za API eliminates all managed heap allocations after initial setup.