Cross-Engine Concerns
Purpose: Documents core functionality needed to create 3D games like Megabonk with LunyScript, assessing priority and portability across Unity and Godot.
Document Type: Feature Requirements & Portability Risk Assessment
Last Updated: 2025-11-09
Priority & Portability Legend
Priority Levels:
- π΄ Critical - Required for Megabonk MVP
- π‘ High - Important for production-ready games
- π’ Medium - Quality-of-life/advanced features
Portability Risk:
- β
Low - Engines behave similarly, minimal abstraction needed
- β οΈ Medium - Notable differences, needs careful abstraction
- β High - Significant behavioral/capability gaps
Priority: Critical - Core gameplay interaction for Megabonk
Portability Risk: Low (with constraints)
Scope Decision:
- Primary: Input event callbacks (action-based)
- Minimal polling: Key/Mouse only for quick prototyping, debugging, jamming (values not normalized)
- Gamepad/Touch: Only via input action mappings (no direct polling)
- Dead zones/sensitivity: users must tune engine values once after porting
Differences:
- Unity: Input System package (modern)
- Godot: Built-in InputMap; input sampled at start of _process by adapter
- Device polling timing: Unity events fire mid-frame; Godot snapshots input before frame starts
- Mouse capture/lock: Unity requires re-lock on focus loss; Godot persists better but differs per platform
- Mouse delta: Unity reports screen pixels; Godot reports mouse units (high-DPI variation)
Challenges:
- Mouse lock behavior: Unity needs explicit re-locking after focus loss; platform-specific quirks
- Value normalization: Delta vs absolute, pixels vs DPI-independent units vary between engines and devices (check if action map can normalize)
- Gamepad button mapping (Xbox vs PlayStation layouts)
- Touch/multitouch handling inconsistencies (out of scope for MVP)
Impact: With event-driven approach and minimal polling, portability is manageable
Mitigation:
- Input state consistency: Buffer input events in Update or FixedUpdate
- Event callbacks primary: Wrap Unityβs InputAction callbacks and Godotβs _input() into unified event model
- Minimal polling API: Expose only Key.IsPressed() / Mouse.Delta for quick debugging (not production-ready)
- Mouse lock abstraction: Input.Cursor.Lock() / Unlock() handles platform re-locking internally
- Value normalization: Normalize delta vs absolute, pixels vs DPI-agnostic units at abstraction layer if action map doesnβt handle it
- Virtual actions only: Map to logical actions (Jump, Move), not physical hardware
- Document that mouse sensitivity and gamepad dead zones may need per-engine tuning initially
2. Physics & Collision π΄ β
Priority: Critical - Character movement, collision detection
Portability Risk: High
Differences:
- Unity: PhysX (Nvidia); continuous collision detection; layer-based collision matrix
- Godot: Custom 3D physics; different CCD approach; per-object collision masks
Challenges:
- Collision resolution order differs
- Raycasts may hit different objects on same frame (precision/order)
- Sleeping/wake behavior diverges
- Trigger event timing (OnTriggerEnter vs area_entered)
- Velocity inheritance from moving platforms
Impact: Core gameplay mechanics behave differently (jumping, collision responses)
Mitigation:
- Define deterministic collision query semantics (sort results by distance)
- Document non-deterministic scenarios (simultaneous collisions)
- Provide simplified collision shapes to minimize edge cases
- Consider fixed timestep physics wrapper
- Extensive cross-engine testing for critical mechanics
3. Object Lifecycle & Scene Management π΄ β οΈ
Priority: Critical - Spawning, destroying, parenting objects
Portability Risk: Medium
Differences:
- Unity: GameObject/Component; Instantiate/Destroy; deferred destruction
- Godot: Node tree; instance/queue_free; immediate vs deferred free
Challenges:
- Destruction timing (end-of-frame vs immediate)
- Callback order on destroy (OnDestroy vs _exit_tree)
- Parent/child order of lifecycle events
- Prefab/Scene instantiation differences
Impact: Scripts may run on destroyed objects or miss cleanup
Mitigation:
- Force consistent deferred destruction model
- Define strict lifecycle event order (covered in CrossCuttingConcerns.md)
- Validate object existence before access
- Clear parent/child event propagation rules
Priority: Critical - Positioning, rotation, scaling, parenting
Portability Risk: Low
Differences:
- Unity: Transform component; local/world space
- Godot: Node3D with transform property; similar local/global API
Challenges:
- Minimal - both use similar matrix math
- Rotation representation (Quaternion vs Basis) but convertible
- Slight precision differences in deeply nested hierarchies
Impact: Negligible for typical game scenarios
Mitigation:
- Abstract as Luny Transform type
- Use quaternions internally
- Document potential floating-point divergence at extreme scales
5. Rendering & Materials π‘ β
Priority: High - Visual appearance, shaders, lighting
Portability Risk: High
Differences:
- Unity: HDRP/URP/Built-in pipelines; ShaderGraph/HLSL
- Godot: Forward+/Mobile/Compatibility renderers; Visual Shader/GLSL
Challenges:
- Shader language incompatibility (HLSL vs GLSL)
- Material properties differ significantly
- Lighting models diverge
- Post-processing pipelines are engine-specific
- Render order/queue differences
Impact: Games look visually different between engines
Mitigation:
- Do NOT abstract shaders - too complex, accept engine-specific assets
- Provide Luny API for runtime material property tweaks (color, texture, basic params)
- Document that visual appearance requires per-engine artist work
- Focus on functional rendering (visibility, layering) not visual parity
6. Audio π΄ β
Priority: Critical - Sound effects, music
Portability Risk: Low
Differences:
- Unity: AudioSource/AudioListener; mixer groups
- Godot: AudioStreamPlayer3D; bus system
Challenges:
- 3D spatial audio parameters (rolloff curves) differ slightly
- Bus/mixer routing models are different
- Compression format support varies
Impact: Minor volume/spatial differences
Mitigation:
- Simple Luny audio API: play(sound, volume, position, loop)
- Abstract volume/pitch/spatial settings
- Let engines handle advanced mixing internally
- Use portable formats (OGG, WAV)
7. Animation π‘ β οΈ
Priority: High - Character animation, transitions
Portability Risk: Medium
Differences:
- Unity: Animator with Mecanim; AnimationClip; blend trees
- Godot: AnimationPlayer/AnimationTree; keyframe system
Challenges:
- State machine models differ
- Blend tree capabilities not equivalent
- IK systems are engine-specific
- Root motion handling varies
Impact: Animation transitions may feel different
Mitigation:
- Provide simple Luny animation API (play, blend, stop)
- Support basic state machines in script
- Complex animation graphs remain engine-specific
- Focus on functional triggers, not replicating full state systems
8. Asset Loading π΄ β οΈ
Priority: Critical - Load models, textures, sounds at runtime
Portability Risk: Medium
Differences:
- Unity: Resources.Load, AssetBundles, Addressables
- Godot: ResourceLoader, preload(), load()
Challenges:
- Path conventions differ (res:// vs Assets/)
- Async loading APIs differ
- Asset reference serialization incompatible
- Dependency handling varies
Impact: Canβt share asset bundles/packages between engines
Mitigation:
- Define Luny asset path convention (logical IDs)
- Async loading abstraction with callbacks/promises
- Per-engine asset packaging (accept incompatibility)
- Clear documentation on asset organization
9. Camera Control π΄ β
Priority: Critical - Player perspective, camera following
Portability Risk: Low
Differences:
- Unity: Camera component with projection settings
- Godot: Camera3D with similar properties
Challenges:
- Projection matrix conventions (minor)
- Viewport/culling mask differences
- Multi-camera rendering differs slightly
Impact: Minimal for basic camera control
Mitigation:
- Luny Camera abstraction (position, FOV, near/far planes)
- Normalize projection parameters
- Simple API for switching active camera
10. UI System π‘ β
Priority: High - HUD, menus, health bars
Portability Risk: High
Differences:
- Unity: UGUI (Canvas/RectTransform) or UI Toolkit (UXML/USS)
- Godot: Control nodes (anchors/margins)
Challenges:
- Layout systems fundamentally different
- Event handling models diverge
- Canvas rendering differs (screen space vs world space)
- Styling/theming incompatible
Impact: UI cannot be shared between engines
Mitigation:
- Minimal abstraction - accept engine-specific UI
- Provide Luny API for updating UI from scripts (set text, show/hide, progress bars)
- Focus on data binding, not layout/styling
- Consider HTML/CSS-based UI layer (future)
11. Particle Systems π’ β
Priority: Medium - VFX, explosions, trails
Portability Risk: High
Differences:
- Unity: Shuriken/VFX Graph
- Godot: CPUParticles3D/GPUParticles3D
Challenges:
- Parameter models completely different
- GPU particles incompatible
- Emission shapes/curves diverge
- Collision/attraction systems differ
Impact: VFX must be recreated per-engine
Mitigation:
- No abstraction - too complex
- Luny API for triggering/stopping particles only
- Accept that particle systems are engine-specific assets
- Script can position/orient emitters
12. Pathfinding & Navigation π‘ β οΈ
Priority: High - AI movement, NavMesh
Portability Risk: Medium
Differences:
- Unity: NavMesh/NavMeshAgent; baked navigation
- Godot: NavigationServer3D; runtime navigation mesh
Challenges:
- Baking process differs (offline vs runtime)
- Agent parameters not equivalent
- Dynamic obstacles handling varies
- Query APIs differ
Impact: AI pathing may differ slightly
Mitigation:
- Luny navigation API (request path, follow path)
- Abstract agent parameters (radius, height, speed)
- Accept that baking is engine-specific
- Provide path waypoint approach for manual control
13. Raycasting & Spatial Queries π΄ β οΈ
Priority: Critical - Line-of-sight, hit detection, object queries
Portability Risk: Medium
Differences:
- Unity: Physics.Raycast/SphereCast; layer masks
- Godot: PhysicsDirectSpaceState queries; collision masks
Challenges:
- Hit result ordering may differ
- Maximum hits differs
- Layer mask semantics slightly different
Impact: Gameplay queries may return different results
Mitigation:
- Luny query API with deterministic result ordering (by distance)
- Normalize layer/mask system
- Document multi-hit behavior
- Return sorted, validated results
14. Coroutines & Async π΄ β
Priority: Critical - Time-based logic, sequences
Portability Risk: Low
Differences:
- Unity: IEnumerator coroutines; yield return
- Godot: await/signals; async methods
Challenges:
- Syntax differs but semantics similar
- Timing resolution may vary slightly
- Cancellation models differ
Impact: Minimal - both support time-based delays
Mitigation:
- LunyScript provides its own coroutine syntax
- Abstract yield points (wait seconds, wait frames)
- Handle cancellation uniformly
15. Time & Frame Rate π΄ β οΈ
Priority: Critical - Delta time, frame counting
Portability Risk: Medium
Differences:
- Unity: Time.deltaTime/fixedDeltaTime; Update/FixedUpdate
- Godot: get_process_delta_time(); _process/_physics_process
Challenges:
- Fixed timestep values differ by default
- Frame update order differs slightly
- Time scale affects systems differently
Impact: Frame-dependent logic may diverge
Mitigation:
- Luny abstracts deltaTime and fixedDeltaTime
- Enforce consistent fixed timestep (60Hz default)
- Clearly separate frame update vs physics update
- Scale time uniformly
16. Triggers & Events π΄ β οΈ
Priority: Critical - Collision triggers, overlaps
Portability Risk: Medium
Differences:
- Unity: OnTriggerEnter/Exit callbacks
- Godot: area_entered/exited signals
Challenges:
- Event timing relative to physics differs
- Multiple triggers on same frame may fire in different order
- Enter/exit pairing guarantees differ
Impact: Trigger-based gameplay logic may differ
Mitigation:
- Buffer and sort trigger events within frame
- Guarantee enter/exit pairing
- Define Luny trigger lifecycle
- Document race conditions that canβt be eliminated
Priority: Critical - Object classification, filtering
Portability Risk: Low
Differences:
- Unity: String tags + integer layers (0-31)
- Godot: Groups (string-based) + physics layers
Challenges:
- Unity has 32 layer limit, Godot has 32 per collision/render
- Tag vs group model slightly different
- Layer collision matrix representation differs
Impact: Minimal - both support classification
Mitigation:
- Luny tag system (string-based, unlimited)
- Luny layer system (up to 32, collision matrix)
- Map to engine-specific systems at runtime
- Document layer limits
18. Scripting Lifecycle Hooks π΄ β οΈ
Priority: Critical - Initialization, update loops, cleanup
Portability Risk: Medium
Differences:
- Unity: Awake/Start/Update/FixedUpdate/LateUpdate/OnDestroy
- Godot: _init/_ready/_process/_physics_process/_exit_tree
Challenges:
- Callback names differ
- Ordering between objects differs
- Awake vs _ready semantics not identical
- Scene loading callbacks differ
Impact: Initialization order bugs
Mitigation:
- Covered in Event Ordering Contract (CrossCuttingConcerns.md)
- Define Luny lifecycle phases clearly
- Map to engine callbacks consistently
- Document guaranteed order
19. Serialization & Save Games π‘ β
Priority: High - Game state persistence
Portability Risk: High
Differences:
- Unity: JsonUtility, ScriptableObjects, custom serializers
- Godot: JSON, ConfigFile, Resource serialization
Challenges:
- Object reference serialization incompatible
- Type systems differ
- Scene format incompatible
Impact: Save files not portable between engines
Mitigation:
- Accept save file incompatibility
- Provide Luny serialization API for game state
- Use JSON or custom format
- Serialize logical data, not engine objects
- Per-engine mapping layer
20. Network & Multiplayer π’ β
Priority: Medium - Future feature
Portability Risk: High
Differences:
- Unity: Netcode for GameObjects, Mirror, custom
- Godot: High-level multiplayer API, ENet
Challenges:
- Network models completely different
- Replication systems incompatible
- Authority/ownership models diverge
- RPC systems differ
Impact: Multiplayer code cannot be shared
Mitigation:
- Out of scope for MVP
- Design network abstraction in future iteration
- Consider engine-agnostic server with thin clients
Summary: Critical Path for Megabonk
Must Have (MVP):
- Input System (β
Low risk - with event-driven approach)
- Physics & Collision (β High risk - biggest challenge)
- Object Lifecycle (β οΈ Medium risk)
- Transform & Hierarchy (β
Low risk)
- Audio (β
Low risk)
- Asset Loading (β οΈ Medium risk)
- Camera Control (β
Low risk)
- Raycasting & Queries (β οΈ Medium risk)
- Coroutines (β
Low risk)
- Time & Frame Rate (β οΈ Medium risk)
- Triggers & Events (β οΈ Medium risk)
- Tags & Layers (β
Low risk)
- Scripting Lifecycle (β οΈ Medium risk)
Important but Not MVP:
- Rendering/Materials (β High risk - accept engine-specific)
- Animation (β οΈ Medium risk)
- UI System (β High risk - accept engine-specific)
- Navigation (β οΈ Medium risk)
- Serialization (β High risk - defer)
Future:
- Particle Systems (β accept engine-specific)
- Networking (β future design)
Key Portability Insights
Low-Hanging Fruit (β
):
- Transform, Camera, Audio, Tags, Coroutines - straightforward abstractions
Core Challenges (β οΈ):
- Physics determinism is the biggest technical risk
- Input normalization requires careful tuning
- Lifecycle/event ordering needs rigorous specification
Accept Incompatibility (β):
- Rendering, UI, Particles are too complex to abstract
- Let artists create engine-specific assets
- Focus scripting API on functionality, not visuals
- CrossCuttingConcerns.md - Architectural design priorities
- Architecture.md - Full system architecture
- QUESTIONS.md - Unresolved design questions