C Scripting
Write native scripts in plain C using the Modu_* C API bridge for access to the engine runtime.
Note
ScriptRuntimeCAPI.h header and compile to native binaries just like C++ scripts. The compiler automatically generates a C++ wrapper to interface with the engine. Most int return values are boolean (1 = success, 0 = fail).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 cover core object state and transforms, rigidbody helpers, animation, input, settings, sprite clip control, and lightweight inspector widgets. If you need the newer ModuCPP inspector DSL, helper facades, or the full high-level movement and dialogue examples, use ModuCPP or native C++ instead.
Quickstart
- Create
Assets/Scripts/Runtime/MyScript.c. - In Inspector, add a Script component and set
Languageto C and assign the file. - Compile: right-click file in File Browser -> Compile Script, or script component menu -> Compile.
- Implement a hook like
Modu_TickUpdateand test in play mode.
Minimal C Example
| 1 | #include "ScriptRuntimeCAPI.h" |
| 2 | |
| 3 | void Modu_TickUpdate(ModuScriptContext* ctx, float dt) { |
| 4 | ModuVec3 pos = Modu_GetPosition(ctx); |
| 5 | pos.x += dt; |
| 6 | Modu_SetPosition(ctx, pos); |
| 7 | } |
Supported Hooks
All hooks are optional. Accepted signatures per hook: void Hook(ModuScriptContext* ctx, float dt), void Hook(ModuScriptContext* ctx), void Hook(float dt), or void Hook().
| Hook | Description |
|---|---|
Modu_Begin | Runs once per object instance |
Modu_TickUpdate | Runs every frame (preferred) |
Modu_Update | Runs only if Modu_TickUpdate is missing |
Modu_Spec | Runs while Spec mode is active |
Modu_TestEditor | Runs while TestEditor is active |
Modu_OnInspector | Called in Inspector panel when object is selected |
Modu_RenderEditorWindow | Called every frame while scripted editor tab is open |
Modu_ExitRenderEditorWindow | Called once when scripted editor tab closes |
C API Reference
Include ScriptRuntimeCAPI.h in your .c script. All functions are prefixed with Modu_.
Object & Transform
| 1 | Modu_GetObjectId(ctx) |
| 2 | Modu_IsObjectEnabled(ctx) |
| 3 | Modu_SetObjectEnabled(ctx, int enabled) |
| 4 | Modu_GetPosition(ctx) // returns ModuVec3 |
| 5 | Modu_SetPosition(ctx, ModuVec3) |
| 6 | Modu_GetRotation(ctx) |
| 7 | Modu_SetRotation(ctx, ModuVec3) |
| 8 | Modu_GetScale(ctx) |
| 9 | Modu_SetScale(ctx, ModuVec3) |
Rigidbody & Collision
| 1 | Modu_SetRigidbodyVelocity(ctx, ModuVec3 velocity) |
| 2 | Modu_GetRigidbodyVelocity(ctx, ModuVec3* outVelocity) |
| 3 | Modu_AddRigidbodyForce(ctx, ModuVec3 force) |
| 4 | Modu_SetRigidbodyRotation(ctx, ModuVec3 rotation) |
| 5 | Modu_SetRigidbodyYaw(ctx, float yawDegrees) |
| 6 | Modu_EnsureCapsuleCollider(ctx, float height, float radius) |
| 7 | Modu_EnsureRigidbody(ctx, int useGravity, int kinematic) |
| 8 | Modu_RaycastClosestDetailed(ctx, ModuVec3 origin, ModuVec3 dir, float distance, |
| 9 | ModuVec3* hitPos, ModuVec3* hitNormal, float* hitDistance, |
| 10 | int* hitObjectId, ModuVec3* hitObjectVelocity, |
| 11 | float* hitStaticFriction, float* hitDynamicFriction) |
Animation
| 1 | Modu_HasAnimation(ctx) |
| 2 | Modu_PlayAnimation(ctx, int restart) |
| 3 | Modu_StopAnimation(ctx, int resetTime) |
| 4 | Modu_PauseAnimation(ctx, int pause) |
| 5 | Modu_ReverseAnimation(ctx, int restartIfStopped) |
| 6 | Modu_SetAnimationTime(ctx, float timeSeconds) |
| 7 | Modu_GetAnimationTime(ctx) // returns float |
| 8 | Modu_IsAnimationPlaying(ctx) |
| 9 | Modu_SetAnimationLoop(ctx, int loop) |
| 10 | Modu_SetAnimationPlaySpeed(ctx, float speed) |
| 11 | Modu_SetAnimationPlayOnAwake(ctx, int playOnAwake) |
Input & Movement
| 1 | Modu_IsSprintDown(ctx) |
| 2 | Modu_IsJumpDown(ctx) |
| 3 | Modu_GetMoveInputWASD(ctx, float pitchDeg, float yawDeg) // returns ModuVec3 |
| 4 | Modu_ApplyMouseLook(ctx, float* pitchDeg, float* yawDeg, |
| 5 | float sensitivity, float maxDelta, float deltaTime, int requireMouseButton) |
Script Settings
| 1 | Modu_GetSettingFloat(ctx, const char* key, float fallback) |
| 2 | Modu_SetSettingFloat(ctx, const char* key, float value) |
| 3 | Modu_GetSettingBool(ctx, const char* key, int fallback) |
| 4 | Modu_SetSettingBool(ctx, const char* key, int value) |
| 5 | Modu_GetSettingString(ctx, const char* key, const char* fallback, |
| 6 | char* outBuffer, int outBufferSize) |
| 7 | Modu_SetSettingString(ctx, const char* key, const char* value) |
Console Logging
| 1 | Modu_AddConsoleMessage(ctx, const char* message, int type) |
| Constant | Usage |
|---|---|
MODU_CONSOLE_INFO | General information |
MODU_CONSOLE_WARNING | Warnings |
MODU_CONSOLE_ERROR | Errors |
MODU_CONSOLE_SUCCESS | Success messages |
Sprite Clips
| 1 | Modu_GetSpriteClipCount(ctx) |
| 2 | Modu_GetSpriteClipIndex(ctx) |
| 3 | Modu_SetSpriteClipIndex(ctx, int index) |
| 4 | Modu_SetSpriteClipName(ctx, const char* name) |
| 5 | Modu_GetSpriteClipName(ctx, char* outBuffer, int outBufferSize) |
| 6 | Modu_GetSpriteClipNameAt(ctx, int index, char* outBuffer, int outBufferSize) |
Inspector UI Helpers
| 1 | Modu_InspectorText(ctx, const char* text) |
| 2 | Modu_InspectorSeparator(ctx) |
| 3 | Modu_InspectorDragFloat(ctx, label, float* value, speed, min, max, format) |
| 4 | Modu_InspectorDragFloat2(ctx, label, float* value, speed, min, max, format) |
| 5 | Modu_InspectorDragFloat3(ctx, label, float* value, speed, min, max, format) |
| 6 | Modu_InspectorCheckbox(ctx, const char* label, int* value) |
| 7 | Modu_InspectorObject(ctx, const char* label, int* objectId) |
Examples
Inspector + Persisted Settings
| 1 | #include "ScriptRuntimeCAPI.h" |
| 2 | |
| 3 | static float speed = 45.0f; |
| 4 | static int enabled = 1; |
| 5 | |
| 6 | void Modu_Begin(ModuScriptContext* ctx) { |
| 7 | speed = Modu_GetSettingFloat(ctx, "speed", speed); |
| 8 | enabled = Modu_GetSettingBool(ctx, "enabled", enabled); |
| 9 | } |
| 10 | |
| 11 | void Modu_TickUpdate(ModuScriptContext* ctx, float dt) { |
| 12 | if (!enabled) return; |
| 13 | ModuVec3 rot = Modu_GetRotation(ctx); |
| 14 | rot.y += speed * dt; |
| 15 | Modu_SetRotation(ctx, rot); |
| 16 | } |
| 17 | |
| 18 | void Modu_OnInspector(ModuScriptContext* ctx) { |
| 19 | Modu_InspectorText(ctx, "Rotate (C API)"); |
| 20 | if (Modu_InspectorCheckbox(ctx, "Enabled", &enabled)) { |
| 21 | Modu_SetSettingBool(ctx, "enabled", enabled); |
| 22 | } |
| 23 | if (Modu_InspectorDragFloat(ctx, "Speed", &speed, 0.1f, 0.0f, 720.0f, "%.2f")) { |
| 24 | Modu_SetSettingFloat(ctx, "speed", speed); |
| 25 | } |
| 26 | } |
Basic Movement
| 1 | #include "ScriptRuntimeCAPI.h" |
| 2 | |
| 3 | void Modu_TickUpdate(ModuScriptContext* ctx, float dt) { |
| 4 | ModuVec3 wasd = Modu_GetMoveInputWASD(ctx, 0.0f, 0.0f); |
| 5 | ModuVec3 vel = { wasd.x * 5.0f, 0.0f, wasd.z * 5.0f }; |
| 6 | Modu_SetRigidbodyVelocity(ctx, vel); |
| 7 | } |
Detailed Raycast
| 1 | #include "ScriptRuntimeCAPI.h" |
| 2 | #include <stdio.h> |
| 3 | |
| 4 | void Modu_TickUpdate(ModuScriptContext* ctx, float dt) { |
| 5 | (void)dt; |
| 6 | ModuVec3 pos = Modu_GetPosition(ctx); |
| 7 | ModuVec3 forward = { 0.0f, 0.0f, 1.0f }; |
| 8 | ModuVec3 hitPos, hitNormal, hitVel; |
| 9 | float hitDist, sF, dF; |
| 10 | int hitId; |
| 11 | |
| 12 | int hit = Modu_RaycastClosestDetailed(ctx, pos, forward, 100.0f, |
| 13 | &hitPos, &hitNormal, &hitDist, &hitId, &hitVel, &sF, &dF); |
| 14 | if (hit) { |
| 15 | char msg[128]; |
| 16 | snprintf(msg, sizeof(msg), "Hit object %d at %.2fm", hitId, hitDist); |
| 17 | Modu_AddConsoleMessage(ctx, msg, MODU_CONSOLE_INFO); |
| 18 | } |
| 19 | } |