C++ Scripting
Direct native scripting using ScriptContext API.
Overview
C++ Scripting in ModuEngine V6.8 provides direct access to the native ScriptContext API for developers who need fine-grained control over engine behavior. This is distinct from ModuCPP, which is a high-level scripting language that transpiles to C++ and offers a more declarative, gameplay-focused API.
If you're building game scripts, start with ModuCPP. Use native C++ scripting when you need:
- Direct access to low-level engine systems
- Maximum performance with no transpilation overhead
- Deep integration with existing C++ codebases
- Custom engine extensions and subsystems
ScriptContext API
The ScriptContext struct is the central interface for C++ scripts. It provides methods to query and modify scene objects, handle input, manage physics, and access engine subsystems.
Fundamental Methods
| 1 | // Object access |
| 2 | ModuNode* ScriptContext::GetObject() const; |
| 3 | SceneObj* ScriptContext::GetSceneObject() const; |
| 4 | |
| 5 | // Transform |
| 6 | void ScriptContext::SetPosition(const glm::vec3& pos); |
| 7 | glm::vec3 ScriptContext::GetPosition() const; |
| 8 | void ScriptContext::SetRotation(const glm::vec3& euler); |
| 9 | glm::vec3 ScriptContext::GetRotation() const; |
| 10 | |
| 11 | // Physics (2D) |
| 12 | void ScriptContext::SetRigidbody2DVelocity(const glm::vec2& vel); |
| 13 | glm::vec2 ScriptContext::GetRigidbody2DVelocity() const; |
| 14 | |
| 15 | // Physics (3D) |
| 16 | void ScriptContext::AddRigidbodyImpulse(const glm::vec3& impulse); |
| 17 | void ScriptContext::SetRigidbodyVelocity(const glm::vec3& vel); |
Input Queries
| 1 | bool ScriptContext::IsKeyHeld(const std::string& key) const; |
| 2 | bool ScriptContext::IsKeyPressed(const std::string& key) const; |
| 3 | bool ScriptContext::IsKeyReleased(const std::string& key) const; |
| 4 | |
| 5 | glm::vec3 ScriptContext::GetMoveInputWASD(float upAxis = 0.0f, float downAxis = 0.0f) const; |
| 6 | bool ScriptContext::IsSprintDown() const; |
| 7 | |
| 8 | float ScriptContext::GetMouseDelta(float speed = 1.0f) const; |
| 9 | glm::vec2 ScriptContext::GetMousePosition() const; |
Scene Queries
| 1 | ModuNode* ScriptContext::FindObjectByName(const std::string& name) const; |
| 2 | std::vector<ModuNode*> ScriptContext::FindObjectsByTag(const std::string& tag) const; |
| 3 | |
| 4 | // Children |
| 5 | void ScriptContext::GetChildren(std::vector<ModuNode*>& out) const; |
| 6 | ModuNode* ScriptContext::GetChild(int index) const; |
| 7 | int ScriptContext::GetChildCount() const; |
Native Hook Signatures
Native C++ scripts export specific hook functions that the engine calls at key points in the lifecycle.
Lifecycle Hooks
| 1 | // Called when the script first loads |
| 2 | extern "C" void Script_OnLoad() { |
| 3 | // Engine initialization |
| 4 | } |
| 5 | |
| 6 | // Called when the scene object enters play mode |
| 7 | extern "C" void Script_OnBegin(ScriptContext& ctx) { |
| 8 | // Setup logic |
| 9 | } |
| 10 | |
| 11 | // Called every frame |
| 12 | extern "C" void Script_OnTickUpdate(ScriptContext& ctx, float deltaTime) { |
| 13 | // Per-frame logic |
| 14 | } |
| 15 | |
| 16 | // Called after rendering |
| 17 | extern "C" void Script_OnLateUpdate(ScriptContext& ctx, float deltaTime) { |
| 18 | // Post-render logic |
| 19 | } |
| 20 | |
| 21 | // Called when the scene object is destroyed |
| 22 | extern "C" void Script_OnEnd(ScriptContext& ctx) { |
| 23 | // Cleanup |
| 24 | } |
Collision Hooks
| 1 | extern "C" void Script_OnCollisionEnter(ScriptContext& ctx, ScriptContext& otherCtx) { |
| 2 | // Triggered when this object collides with another |
| 3 | } |
| 4 | |
| 5 | extern "C" void Script_OnCollisionStay(ScriptContext& ctx, ScriptContext& otherCtx) { |
| 6 | // Called every frame while objects are in contact |
| 7 | } |
| 8 | |
| 9 | extern "C" void Script_OnCollisionExit(ScriptContext& ctx, ScriptContext& otherCtx) { |
| 10 | // Triggered when collision ends |
| 11 | } |
| 12 | |
| 13 | extern "C" void Script_OnTriggerEnter(ScriptContext& ctx, ScriptContext& otherCtx) { |
| 14 | // For trigger colliders (one-way collision) |
| 15 | } |
Editor Hook
| 1 | // Render custom inspector UI (using ImGui) |
| 2 | extern "C" void Script_OnInspector(ScriptContext& ctx) { |
| 3 | static float speed = 5.0f; |
| 4 | ImGui::SliderFloat("Speed", &speed, 0.0f, 20.0f); |
| 5 | } |
ImGui in Scripts
You can render immediate-mode UI (ImGui) directly from C++ scripts using the built-in ImGui integration. This is commonly used for debug visualization and inspector customization.
Basic ImGui Usage
| 1 | #include <imgui.h> |
| 2 | |
| 3 | extern "C" void Script_OnInspector(ScriptContext& ctx) { |
| 4 | ImGui::TextWrapped("Hello from script!"); |
| 5 | |
| 6 | static float value = 0.5f; |
| 7 | ImGui::SliderFloat("Value", &value, 0.0f, 1.0f); |
| 8 | |
| 9 | static bool enabled = false; |
| 10 | ImGui::Checkbox("Enabled", &enabled); |
| 11 | |
| 12 | if (ImGui::Button("Click me!")) { |
| 13 | // Button pressed |
| 14 | } |
| 15 | } |
Manual Inspector
For data-driven workflows, you can manually define inspector fields using the engine's field system. These fields are serialized in the scene file and exposed in the editor.
Field Types
| Type | C++ Type | Editor Widget |
|---|---|---|
| Float | float | Slider / Input |
| Int | int | Slider / Input |
| Vec3 | glm::vec3 | XYZ Fields |
| String | std::string | Text Input |
| Bool | bool | Checkbox |
| Object Reference | ModuNode* | Drag-Drop Object |
| 1 | // Declare fields at the top level |
| 2 | static float moveSpeed = 5.0f; |
| 3 | static int maxHealth = 100; |
| 4 | |
| 5 | extern "C" void Script_OnInspector(ScriptContext& ctx) { |
| 6 | // Expose fields to inspector |
| 7 | ImGui::SliderFloat("Move Speed", &moveSpeed, 0.0f, 20.0f); |
| 8 | ImGui::SliderInt("Max Health", &maxHealth, 1, 500); |
| 9 | } |
Troubleshooting
Script not being called
Null ScriptContext
Performance Issues
- Avoid heavy allocations in tight loops (TickUpdate)
- Cache frequently-accessed pointers
- Use glm::length() instead of manual distance calculations
- Profile with the engine's built-in profiler