Documentation

C Scripting

Write native scripts in plain C using the C API bridge for access to the engine runtime.

Note

C scripts use the ScriptRuntimeCAPI.h header and compile to native binaries just like C++ scripts. The compiler automatically generates a C++ wrapper to interface with the engine.

Overview

You can write native scripts in plain C (.c files). The Modularity compiler generates a C++ bridge automatically and links it with your C object file, producing a shared library.

C scripts have access to the same runtime systems as C++ scripts through the Modu_* C API, making them a lightweight alternative when you prefer C or want to integrate existing C code.

Quickstart

  1. Create Assets/Scripts/Runtime/MyScript.c (or use Project window -> New -> C Script).
  2. In Inspector -> Script component, set Language to C and assign the file.
  3. Compile as usual (right-click file -> Compile Script or script component menu -> Compile).
  4. Implement a hook like Modu_TickUpdate and test in play mode.

Minimal C Example

MyScript.ccpp
#include "ScriptRuntimeCAPI.h"

void Modu_TickUpdate(ModuScriptContext* ctx, float dt) {
    (void)dt;
    ModuVec3 pos = Modu_GetPosition(ctx);
    pos.x += 0.01f;
    Modu_SetPosition(ctx, pos);
}

C API Reference

Include ScriptRuntimeCAPI.h in your .c script. All functions are prefixed with Modu_.

Object & Transform

Modu_GetObjectId(ctx)
Modu_IsObjectEnabled(ctx)
Modu_SetObjectEnabled(ctx, bool)
Modu_GetPosition(ctx)
Modu_SetPosition(ctx, pos)
Modu_GetRotation(ctx)
Modu_SetRotation(ctx, rot)
Modu_GetScale(ctx)
Modu_SetScale(ctx, scale)

Rigidbody & Collision

Modu_SetRigidbodyVelocity(ctx, vel)
Modu_GetRigidbodyVelocity(ctx, out_vel)
Modu_AddRigidbodyForce(ctx, force)
Modu_AddRigidbodyImpulse(ctx, impulse)
Modu_SetRigidbodyRotation(ctx, rot)
Modu_SetRigidbodyYaw(ctx, yaw)
Modu_EnsureCapsuleCollider(ctx, height, radius)
Modu_EnsureRigidbody(ctx, useGravity, kinematic)
Modu_RaycastClosest(ctx, ...)
Modu_RaycastClosestDetailed(ctx, ...)

Input & Movement

Modu_IsSprintDown(ctx)
Modu_IsJumpDown(ctx)
Modu_GetMoveInputWASD(ctx, pitch, yaw)
Modu_ApplyMouseLook(ctx, ...)
Modu_GetPlanarYawPitchVectors(ctx, ...)

Script Settings

Modu_GetSettingFloat(ctx, key, fallback)
Modu_SetSettingFloat(ctx, key, value)
Modu_GetSettingBool(ctx, key, fallback)
Modu_SetSettingBool(ctx, key, fallback)
Modu_GetSettingString(ctx, key, fallback)
Modu_SetSettingString(ctx, key, value)

Inspector Helpers

Modu_InspectorText(ctx, label)
Modu_InspectorSeparator(ctx)
Modu_InspectorDragFloat(ctx, label, value, speed)
Modu_InspectorDragFloat2(ctx, label, value, speed)
Modu_InspectorDragFloat3(ctx, label, value, speed)
Modu_InspectorCheckbox(ctx, label, value)

Console & Utility

Modu_AddConsoleMessage(ctx, text, type)
Modu_MarkDirty(ctx)
Modu_SetFPSCap(ctx, enabled, cap)
Modu_FindObjectByName(ctx, name)
Modu_FindObjectById(ctx, id)
Modu_ResolveObjectRef(ctx, ref)

UI Helpers

Modu_IsUIButtonPressed(ctx)
Modu_SetUIInteractable(ctx, bool)
Modu_GetUISliderValue(ctx)
Modu_SetUISliderValue(ctx, value)
Modu_SetUISliderRange(ctx, min, max)
Modu_SetUILabel(ctx, label)
Modu_SetUIColor(ctx, color)

Supported Hooks

All hooks are optional. Implement only the ones your script needs:

Modu_Begin(ModuScriptContext* ctx, float dt)
Modu_TickUpdate(ModuScriptContext* ctx, float dt)
Modu_Update(ModuScriptContext* ctx, float dt)
Modu_Spec(ModuScriptContext* ctx, float dt)
Modu_TestEditor(ModuScriptContext* ctx, float dt)
Modu_OnInspector(ModuScriptContext* ctx)
Modu_RenderEditorWindow(ModuScriptContext* ctx)
Modu_ExitRenderEditorWindow(ModuScriptContext* ctx)

Note

Modu_Begin runs once per object instance. Modu_TickUpdate runs every frame and is preferred over Modu_Update. Modu_Spec/Modu_TestEditor run only while their global toggles are enabled.

Examples

Simple Movement

Movement.ccpp
#include "ScriptRuntimeCAPI.h"

void Modu_TickUpdate(ModuScriptContext* ctx, float dt) {
    ModuVec3 wasd = Modu_GetMoveInputWASD(ctx, 0.0f, 0.0f);
    ModuVec3 vel = {wasd.x * 5.0f, 0.0f, wasd.z * 5.0f};
    Modu_SetRigidbodyVelocity(ctx, vel);
}

Logging

Logger.ccpp
#include "ScriptRuntimeCAPI.h"

void Modu_Begin(ModuScriptContext* ctx, float dt) {
    (void)dt;
    Modu_AddConsoleMessage(ctx, "Script started!", MODU_INFO);
}

void Modu_TickUpdate(ModuScriptContext* ctx, float dt) {
    static int frameCount = 0;
    frameCount++;
    if (frameCount % 60 == 0) {
        Modu_AddConsoleMessage(ctx, "60 frames passed", MODU_SUCCESS);
    }
}

Per-Script Settings

Settings.ccpp
#include "ScriptRuntimeCAPI.h"

void Modu_OnInspector(ModuScriptContext* ctx) {
    float speed = Modu_GetSettingFloat(ctx, "speed", 5.0f);
    Modu_InspectorDragFloat(ctx, "Speed", &speed, 0.1f);
    Modu_SetSettingFloat(ctx, "speed", speed);
}

void Modu_TickUpdate(ModuScriptContext* ctx, float dt) {
    float speed = Modu_GetSettingFloat(ctx, "speed", 5.0f);
    ModuVec3 wasd = Modu_GetMoveInputWASD(ctx, 0.0f, 0.0f);
    ModuVec3 vel = {wasd.x * speed, 0.0f, wasd.z * speed};
    Modu_SetRigidbodyVelocity(ctx, vel);
}

Raycasting

Raycast.ccpp
#include "ScriptRuntimeCAPI.h"

void Modu_TickUpdate(ModuScriptContext* ctx, float dt) {
    (void)dt;
    ModuVec3 pos = Modu_GetPosition(ctx);
    ModuVec3 forward = {0.0f, 0.0f, 1.0f};
    
    ModuRaycastResult result = Modu_RaycastClosestDetailed(ctx, pos, forward, 100.0f);
    if (result.hit) {
        char msg[256];
        snprintf(msg, sizeof(msg), "Hit at distance: %f", result.distance);
        Modu_AddConsoleMessage(ctx, msg, MODU_INFO);
    }
}