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
- Create
Assets/Scripts/Runtime/MyScript.c(or use Project window -> New -> C Script). - In Inspector -> Script component, set
Languageto C and assign the file. - Compile as usual (right-click file -> Compile Script or script component menu -> Compile).
- Implement a hook like
Modu_TickUpdateand 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);
}
}