Build games with hot-compiled scripts, real-time physics, and a fully integrated editor. Native C++, open architecture.




All four options can drive the same 2D movement test. The difference is how much engine plumbing each layer exposes to the script author.
High-level gameplay scripting with built-in helpers.
| 1 | add ModuCPP; |
| 2 | add ModuInput; |
| 3 | add ModuEngine; |
| 4 | |
| 5 | public class Movement2DTest : ModuNode |
| 6 | { |
| 7 | public float walkSpeed = 4.0f; |
| 8 | public float runSpeed = 7.0f; |
| 9 | public float acceleration = 18.0f; |
| 10 | public float drag = 8.0f; |
| 11 | |
| 12 | void TickUpdate() |
| 13 | { |
| 14 | Vector2 move = input.WASDNormalized(); |
| 15 | bool running = input.sprint(); |
| 16 | float speed = running ? runSpeed : walkSpeed; |
| 17 | |
| 18 | TryMoveRigidbody2D(ctx, move * speed, acceleration, drag, dt); |
| 19 | obj.UILabel = "ModuCPP 2D Speed: " + IntR(speed); |
| 20 | } |
| 21 | } |
| 1 | #include "ModuCPP" |
| 2 | #include <cmath> |
| 3 | #include <string> |
| 4 | |
| 5 | namespace { |
| 6 | float walkSpeed = 4.0f; |
| 7 | float runSpeed = 7.0f; |
| 8 | } |
| 9 | |
| 10 | void TickUpdate(ScriptContext& ctx, float dt) |
| 11 | { |
| 12 | if (!ctx.object || dt <= 0.0f || !ctx.HasRigidbody2D()) return; |
| 13 | |
| 14 | glm::vec3 move3 = ctx.GetMoveInputWASD(0.0f, 0.0f); |
| 15 | glm::vec2 move(move3.x, move3.z); |
| 16 | |
| 17 | const float len = glm::length(move); |
| 18 | if (len > 0.0001f) { |
| 19 | move /= len; |
| 20 | } |
| 21 | |
| 22 | const float speed = ctx.IsSprintDown() ? runSpeed : walkSpeed; |
| 23 | ctx.SetRigidbody2DVelocity(move * speed); |
| 24 | ctx.SetUILabel(std::string("Native C++ 2D Speed: ") + ModuCPP::IntR(speed)); |
| 25 | } |
| 1 | using System; |
| 2 | |
| 3 | namespace ModuCPP; |
| 4 | |
| 5 | public class Movement2DManaged |
| 6 | { |
| 7 | private float walkSpeed = 4.0f; |
| 8 | private float runSpeed = 7.0f; |
| 9 | |
| 10 | public void Begin(IntPtr ctx, float deltaTime) |
| 11 | { |
| 12 | var context = new Context(ctx); |
| 13 | context.AutoSettingsFrom(this, save: false); |
| 14 | } |
| 15 | |
| 16 | public void TickUpdate(IntPtr ctx, float deltaTime) |
| 17 | { |
| 18 | var context = new Context(ctx); |
| 19 | context.AutoSettingsFrom(this, save: false); |
| 20 | if (deltaTime <= 0.0f || !context.HasRigidbody2D) return; |
| 21 | |
| 22 | Vec3 move3 = context.GetMoveInputWASD(0.0f, 0.0f); |
| 23 | float length = MathF.Sqrt((move3.X * move3.X) + (move3.Z * move3.Z)); |
| 24 | Vec2 move = length > 0.0001f |
| 25 | ? new Vec2(move3.X / length, move3.Z / length) |
| 26 | : new Vec2(0.0f, 0.0f); |
| 27 | |
| 28 | float speed = context.IsSprintDown() ? runSpeed : walkSpeed; |
| 29 | context.Rigidbody2DVelocity = new Vec2(move.X * speed, move.Y * speed); |
| 30 | context.SetUILabel($"Managed C# 2D Speed: {speed:0}"); |
| 31 | } |
| 32 | } |
| 1 | #include "ScriptRuntimeCAPI.h" |
| 2 | #include <math.h> |
| 3 | |
| 4 | static float g_walkSpeed = 4.0f; |
| 5 | static float g_runSpeed = 7.0f; |
| 6 | |
| 7 | void Modu_Begin(ModuScriptContext* ctx) { |
| 8 | if (!ctx) return; |
| 9 | Modu_AddConsoleMessage(ctx, "C bridge 2D movement test ready.", MODU_CONSOLE_INFO); |
| 10 | } |
| 11 | |
| 12 | void Modu_TickUpdate(ModuScriptContext* ctx, float dt) { |
| 13 | if (!ctx || dt <= 0.0f) return; |
| 14 | |
| 15 | ModuVec3 move = Modu_GetMoveInputWASD(ctx, 0.0f, 0.0f); |
| 16 | float len = sqrtf((move.x * move.x) + (move.z * move.z)); |
| 17 | if (len > 0.0001f) { |
| 18 | move.x /= len; |
| 19 | move.z /= len; |
| 20 | } else { |
| 21 | move.x = 0.0f; |
| 22 | move.z = 0.0f; |
| 23 | } |
| 24 | |
| 25 | float speed = Modu_IsSprintDown(ctx) ? g_runSpeed : g_walkSpeed; |
| 26 | ModuVec3 pos = Modu_GetPosition(ctx); |
| 27 | pos.x += move.x * speed * dt; |
| 28 | pos.z += move.z * speed * dt; |
| 29 | Modu_SetPosition(ctx, pos); |
| 30 | } |