Articles

Classic UO 3D: Comparing Unity and Open Source Engines for a 3D Client

Let’s begin a deep comparison of Godot 4.4.1, three.js, LWJGL, Unity, MonoGame, Raylib, PlayCanvas, Panda3D, and some other notable open-source 3D engines to evaluate their suitability for porting ClassicUO into a full 3D client. I’ll look at performance, rendering capabilities, platform support, ease of asset integration (especially UO’s formats), scripting flexibility, community support, and overall suitability for a complex isometric MMORPG like Ultima Online. The following is effectively notes and is not guaranteed to be correct.

Comparative Evaluation of Engines for a 3D ClassicUO Client

Introduction

Porting the ClassicUO client (a 2D isometric Ultima Online client) into a 3D version requires evaluating game engines and frameworks across multiple criteria. Key considerations include how well each engine handles 3D isometric rendering, integration of legacy UO assets (the .mul/.uop files), flexibility of the shader system, available performance optimizations, the scripting language and ability to reuse ClassicUO’s existing C# code, cross-platform deployment (PC, mobile, browser), the maturity of the community and tooling, suitability for an MMORPG client (networking, large world support), integration with modern UI systems, and the learning curve for developers transitioning from the current ClassicUO codebase.

Below we analyze each candidate engine/framework against these criteria. Summary comparison tables and a shortlist of recommended options follow the detailed evaluations.

Evaluation Criteria

For each engine or framework, we examine:

  1. 3D Rendering & Isometric View Support: Can it render a 3D scene efficiently and accommodate an isometric or top-down camera angle?
  2. Asset Pipeline Compatibility: How easily can Ultima Online’s legacy assets (textures, maps, animations in .mul/.uop files) be imported or used? Can the engine be extended to load custom asset formats?
  3. Shader System Flexibility: Does it support custom shaders or materials to replicate UO’s visual style (e.g. hue shifting) and to implement modern effects if desired?
  4. Performance & Optimization: What tools or engine features exist for optimizing performance (culling, batching, LOD, profiling)? Can it handle the potentially large number of objects in an MMO scene?
  5. Scripting & Extensibility (C# logic reuse): What language is used for game logic? Can ClassicUO’s existing C# code be leveraged, or would a rewrite be needed? How extensible is the engine for custom systems?
  6. Cross-Platform Support: Which platforms are supported (Windows, Linux, macOS, browsers, mobile)? Are there limitations (e.g. web support requiring WebGL/WebAssembly or lack of mobile support)?
  7. Community & Ecosystem: How large and mature is the community? Are there plenty of tutorials, documentation, and plugins/tools available? Is the engine actively maintained?
  8. MMORPG Client Suitability: Does it provide relevant features for an online persistent world client (e.g. networking libraries, large world streaming, entity management)? Or can those be added?
  9. UI System Integration: What UI toolkit is available and can it be used to recreate Ultima Online’s interface (drag windows, paperdoll, inventory grids, etc.)?
  10. Learning Curve & Onboarding: How steep is the learning curve for ClassicUO developers? (Consider familiarity with language, engine complexity, and available learning resources.)

Each engine is evaluated with these points in mind. Citations to documentation or known projects are included to substantiate key claims.

Engine/Framework Analyses

Unity

1. 3D Rendering & Isometric Support: Unity is a fully featured 3D engine with a proven track record for various game perspectives, including isometric. Setting up a 3D isometric view is straightforward – for example, using an orthographic camera at a 45° angle or a perspective camera with a tilted angle. Unity’s renderer can handle complex scenes with lighting and shadows, but one can also keep it simple (unlit materials for a classic look). Many isometric RPGs have been made in Unity, so it natively supports this style. The engine also provides a Tilemap system (for 2D) that could potentially be used for isometric ground tiles, though a 3D terrain would more likely be a mesh or set of plane objects. In summary, Unity’s 3D capabilities are more than sufficient, and configuring an isometric camera is trivial.

2. Asset Pipeline Compatibility: Unity’s asset pipeline is robust for standard formats (images, audio, 3D models like FBX/OBJ/GLTF). It does not directly support Ultima Online’s proprietary .mul/.uop files, but it is highly extensible. One can write editor scripts or custom asset importers in C# to process these files. In fact, ClassicUO’s data and logic can be integrated into Unity – this has been proven by the MobileUO project, which “uses the ClassicUO client code inside of Unity” (Check Out “MobileUO”, a Unity-Based Mobile Client for Ultima Online – The Ultima Codex). This means the existing C# code that loads UO artwork and maps can be reused to feed Unity textures and meshes at runtime. For example, MobileUO dynamically loads UO map and art assets and displays them in Unity. Alternatively, one could run a conversion outside Unity to turn UO art into PNGs or 3D model equivalents and then import, but the dynamic approach has been shown to work. Unity’s Editor also allows adding custom menu items or windows – a team could create tools to browse UO assets within Unity. Overall, while not plug-and-play, Unity’s pipeline can be extended or bypassed (by runtime loading) to accommodate UO assets with relatively minimal friction, especially given that ClassicUO’s C# asset-loading routines can be brought into the Unity project.

3. Shader System Flexibility: Unity offers a very flexible shader system. It supports ShaderLab with HLSL for writing custom shaders, and a node-based Shader Graph for those who prefer visual shader authoring. This means the team can easily create shaders to emulate the original 2D look (e.g., unlit alpha-tested shaders for sprites) or to enhance it (e.g., adding normal-mapped lighting to terrain or special FX shaders for spells). Unity’s standard rendering (URP/HDRP) includes many features, but one can use the simpler Built-in render pipeline or URP for a project like this to keep things lightweight. Specific UO effects like palette hue shifts can be implemented via shader (for instance, passing a hue vector to an object’s material to tint it). Unity has built-in materials for common needs and supports multi-pass shaders if needed (for example, drawing terrain transitions or underlays). The presence of Shader Graph means even without deep shader coding knowledge, developers can prototype effects visually. If targeting a retro look, Unity can simply use unlit textures and still benefit from its batching (where many sprites share a material) (Does Monogame support 3D? – Community | MonoGame). In short, Unity’s shader system is highly capable and user-friendly, able to replicate UO’s aesthetic or enable modern visuals.

4. Performance & Optimization: Unity is well-known and liked for its performance profiling tools and optimization features. By default, Unity will perform frustum culling (not rendering objects outside the camera view). It also supports Level of Detail (LOD) groups for models, and has mechanisms for batching draw calls: static batching (combine meshes flagged as static in the editor) and dynamic batching (automatically batch small meshes that share a material). Additionally, Unity supports GPU instancing for drawing many identical objects efficiently. For an MMO world, these features help (e.g., all grass tiles using one material can be instanced). Unity also has an Occlusion Culling system (precomputed) to avoid rendering objects blocked by others, which could be useful in towns with many buildings (though less so in open fields). The Unity Profiler lets developers profile CPU and GPU usage per frame in great detail (down to individual script methods and draw calls), which is invaluable for tuning performance. Memory profiling tools are also available. A Unity client can be optimized to handle large numbers of entities – for example, using object pooling to reuse GameObjects or using ECS/DOTS for massive crowds (if the team chooses). Unity’s newer Data-Oriented Tech Stack (ECS) is not mandatory, but it offers further performance for large-scale simulations at the cost of complexity. Even with classic Unity GameObjects, projects like MobileUO have shown good performance on mobile devices, which speaks to Unity’s ability to handle UO’s scope when optimized (Check Out “MobileUO”, a Unity-Based Mobile Client for Ultima Online). That said, Unity does have runtime overhead (monobehavior scripts, GC allocations to watch for, etc.), so the team will need to use best practices (caching references, minimizing per-frame allocations) to avoid issues like garbage collection spikes. Fortunately, these practices are well-documented in the community (Does Monogame support 3D? – Community | MonoGame). In summary, Unity provides many out-of-the-box tools to achieve acceptable performance and to diagnose any bottlenecks during development.

5. Scripting & Extensibility (C# Reuse): Unity uses C# for scripting, running on a Mono or .NET runtime (Unity 2021+ supports .NET 6 in experimental builds, and by 2025 may have full .NET Core support). This is a major advantage for ClassicUO: the entire client logic is in C#, and much of it can be ported or even directly used in Unity. ClassicUO’s code (game state management, networking, UI logic, etc.) can be integrated into Unity with some refactoring to fit Unity’s component model. For example, one might create a UOGame manager MonoBehaviour that holds core systems (like the World data, Packet handler, etc.) and update them each frame. Many data structures and algorithms (e.g., pathfinding, combat calculations) can be reused with little change. As noted, MobileUO took exactly this approach, reusing ClassicUO’s code in Unity (Check Out “MobileUO”, a Unity-Based Mobile Client for Ultima Online – The Ultima Codex). Unity’s extensibility is another plus: one can call native code plugins if needed (for performance-critical parts or reuse of existing C++ libraries), and use reflection or scripting to create developer commands, etc. Unity also supports Editor scripting – the team can create custom inspectors or tools to assist development (for instance, a debugger window for the UO protocol or a visual map editor if needed). The large .NET ecosystem is available, meaning if the team wants to use libraries (for JSON, for encryption, etc.), they usually can. In terms of engine extensibility, Unity can be extended via Packages and the Asset Store also provides add-ons (though Asset Store packages are often more geared to general game development than a specific MMO client like UO). Because Unity uses C#, ClassicUO developers will find the language and many .NET APIs identical, greatly flattening the learning curve compared to engines with other languages. They will, however, have to adapt to Unity’s component-and-GameObject approach, and likely reorganize some of their code to fit Unity’s update loop or event-driven model. Still, given the magnitude of ClassicUO’s existing codebase, Unity offers the highest degree of code reuse among the evaluated engines (Check Out “MobileUO”, a Unity-Based Mobile Client for Ultima Online – The Ultima Codex). Extensibility for custom needs (like handling UO-specific UI dragging or custom network encryption) is achievable either through C# or plug-in C++ code as needed – Unity places few limits on what you can integrate.

6. Cross-Platform Support: Unity is one of the most cross-platform engines available. It supports Windows, macOS, and Linux for standalone builds, and has dedicated support for iOS and Android (mobile). It also can deploy to WebGL (running the game in a browser via asm.js/WebAssembly). For ClassicUO, this means a Unity-based 3D client could reach not only PC players but potentially mobile players (as MobileUO demonstrates) and even run in a browser (with some caveats). On desktop, Unity’s builds are reliable and perform well using native graphics APIs (DirectX, OpenGL, Metal, or Vulkan depending on platform and settings). On mobile, Unity provides touch input support, UI scaling, etc., which would make it feasible to have a mobile UO client (MobileUO already proved Unity’s viability on mobile by delivering UO on iOS/Android (MobileUO – the first mobile client for Ultima Online! – GitHub) (Play Now – UOAlive)). For the web, Unity’s WebGL build would allow a “play in browser” experience, which ClassicUO currently also offers via web (their WebAssembly build) – however, Unity WebGL has limitations (no threading by default, and it requires the UO server to be accessible via WebSockets or HTTP since direct socket access isn’t allowed from browser). Achieving a browser-based Unity client might require some adaptation of the network layer (Unity’s HLAPI won’t apply, so one would use websockets or a web proxy for the UO protocol). Still, having the option of a WebGL build is a unique advantage that engines like Unreal or Panda3D lack in 2025. In terms of distribution, Unity games are easy to distribute – for instance, one could provide a standalone downloadable client for each OS, and possibly an official mobile app. The Unity Editor itself runs on Windows/macOS (and Linux in 2025 as a supported platform), so developers on any OS can work on the project. In summary, Unity covers all target platforms that a UO client might reasonably need (with the only slight complication being the networking on WebGL). This breadth of platform support means a Unity-based ClassicUO 3D client could serve the community on their platform of choice without a complete rewrite for each.

7. Community Maturity & Tooling: Unity has a massive community and a very mature ecosystem. There are countless tutorials, documentation pages, and forum answers for virtually any problem a developer might encounter. For ClassicUO developers, this means if they run into a challenge (say, how to efficiently render many sprites, or how to implement a drag-and-drop UI), there’s a high chance that solutions or best practices are documented online. Unity’s Asset Store is a rich resource as well – while one must be cautious about introducing third-party assets into an open-source project (license compatibility and such), the Asset Store items can provide learning examples or even free assets that could help (for instance, there might be free shaders or UI skins that could be adapted to UO). The engine’s tooling is excellent: the Editor with its Scene view, Inspector, animation editor, etc., greatly speeds up development and tweaking. The UI builder (UGUI) is visual, the particle system editor can create effects for spells or fire, etc., with immediate feedback. Unity also has Play Mode in the editor, allowing quick iteration (you can run the game in the editor to test changes). For debugging, aside from the Profiler, Unity supports attaching C# debuggers (via Visual Studio, etc.) to step through code. The community has also developed many add-ons specifically useful in RPG/MMO contexts (for example, dialogue systems, inventory systems, etc. – not all relevant to UO, but indicative of available help). It is worth noting Unity suffered some community turmoil in late 2023 with a proposed fee policy, but as of 2025 Unity remains widely used (and the company adjusted its policies after community feedback). For an open-source project, using Unity is a bit unusual (since the engine itself isn’t open source), but it’s not unprecedented – you can host the game code and Unity project files on GitHub, but contributors do need to download Unity to actually run the project. The Unity Personal license is free for all and would cover ClassicUO unless it somehow generated significant revenue (which it does not, being a free client). So cost is essentially zero to the project, and the only barrier is ensuring contributors have Unity (which is a large but easily obtainable download). In summary, Unity’s community and tooling are a big plus: it minimizes the risk of getting stuck on technical issues and maximizes productivity through its polished development environment.

8. MMORPG Client Suitability: Unity was not originally built for MMO development, but it has been used for many online games and its flexibility allows it to serve as a UO client. The networking aspect will not use Unity’s high-level network API (which is geared toward Unity-to-Unity communication and is unsuitable for UO’s server protocol). Instead, the ClassicUO team would implement networking using C# sockets (System.Net.Sockets) or a third-party networking library like Mirror (an open-source Unity networking library) in a client-only mode. ClassicUO’s existing network code (packet handling, encryption, etc.) can run inside Unity’s scripting environment without issue – for example, the MobileUO project integrated ClassicUO’s network code to connect to shards (Check Out “MobileUO”, a Unity-Based Mobile Client for Ultima Online – The Ultima Codex). Unity doesn’t impose any constraints on opening TCP connections from the client (except on WebGL where you’d use WebSockets). So the client can communicate with existing UO servers just as the ClassicUO client does. Managing a large world state (hundreds of items/mobiles in view) in Unity will rely on the data structures ported from ClassicUO (e.g., some dictionary of World objects). Unity can handle that amount of game objects, though care should be taken to destroy/create objects efficiently as the player moves – which ClassicUO already handles logically (Unity’s GC will take care of cleaning up C# objects, and pooled GameObjects can mitigate overhead of frequent creation). Entity management can be done via standard GameObjects (each mobile or item could be a GameObject with appropriate components for rendering, click detection, etc.), or via a more optimized DOTS approach if needed. But likely, given UO’s moderate active object counts, GameObjects will suffice with proper pooling. Unity also offers features that can benefit an MMO client: for example, its NavMesh system could be used client-side to allow NPC path previews or improved pathfinding visualizations (though server does actual pathfinding, the client could use it for quality-of-life features like path previews). The Localization tools could help if supporting multiple languages for client messages. Another MMO-specific need is large world support – Unity can handle a world the size of Britannia; one might use floating origin (shifting the world coordinate zero as the player moves to avoid floating-point precision issues) if necessary, but UO’s map sizes (like 6144×4096 tiles) in Unity units is on the order of a few million units, which might require origin shifting for absolute precision but relative movements and local interactions would be fine. Unity doesn’t have built-in sectoring of world, but the ClassicUO client logic already manages world chunks and range-based updates, which can be reused. In terms of persistency, that’s all on the server side – the client just needs to maintain state per session, which Unity can do easily (and even save settings to disk using PlayerPrefs or writing files for configuration). Multithreading: Unity allows separate threads for things like networking (you cannot directly modify Unity scene objects from another thread, but you can handle socket IO on a worker thread and then synchronize results to the main thread). This is exactly how one could integrate the ClassicUO network loop – run it on a separate thread or use Unity’s Update loop to pump it periodically. Unity’s modern .NET also supports async/await, which could integrate with a networking loop (for example, awaiting data read without freezing the main thread). All told, Unity can be made to function as a robust UO client, and projects like MobileUO have demonstrated key pieces of this (networking, asset loading, basic UI) on even constrained devices. The engine doesn’t give MMO-specific infrastructure (that wasn’t expected on a client anyway), but it doesn’t hinder connecting to an existing MMO backend.

9. UI System Integration: Recreating Ultima Online’s user interface (paperdoll, inventory gumps, health bars, etc.) is a critical part of the client. Unity provides the Unity UI (uGUI) system for 2D interfaces. It’s a GameObject-based UI system where you create a Canvas and place UI elements (Text, Image, Button, etc.) as children. This system would work well for UO’s UI: for instance, one can create a Draggable Window prefab that contains an Image for the window background (using the UO gump texture), and other Images/Buttons for close buttons, etc. Unity UI supports event handlers for click, drag, drop, so implementing dragging an item from one container to another is feasible with this system. The ClassicUO project already has the logic for UI interactions (in C#), which could be adapted to work with Unity’s UI events. Alternatively, developers could bypass Unity’s higher-level UI and draw the UI manually with a simpler approach (like rendering to a RenderTexture or using immediate mode GUI), but that’s likely unnecessary because Unity UI is quite capable. It handles clipping, scrolling areas, tooltips, and can be customized heavily. If any part of UO’s UI is very custom (for example, the paperdoll paperdoll slots or drag-and-drop paperdoll clothing), the team can create custom Unity UI components or use existing ones (like toggle buttons for paperdoll slots, etc.) to mirror the functionality. Unity UI is resolution-independent, which is helpful since modern clients may run at much higher resolutions than the original. The UO art (designed for 640×480 originally) can be scaled or panel arrangements adjusted to suit larger displays. Unity also offers a newer UI Toolkit (similar to web CSS/HTML) that could be used for certain parts of the UI, but uGUI is more than sufficient and probably easier given the need to use many custom art assets. Another benefit: the Text rendering in Unity UI can use TrueType fonts, meaning text like journal or overhead names could be drawn with crisp modern fonts or even the classic UO pixel font if desired (Unity can import bitmap fonts or one could create a shader for the exact pixel font rendering). For more dynamic HUD elements, Unity allows rendering world-space UI (for example, floating health bars over mobiles can be done with world-space Canvas). In sum, Unity’s UI system can handle all of UO’s interface requirements, and doing it in Unity likely involves less effort than the ClassicUO team already spent writing their custom UI system using FNA. They would basically reimplement the UI using Unity’s framework, but guided by the existing design and even reusing assets (since ClassicUO already has extracted gump images which can be imported into Unity easily). The result can be very faithful to the original interface, or enhanced (Unity UI could allow subtle improvements like smoother window dragging, scalable UI, etc.).

10. Learning Curve for ClassicUO Developers: For a team coming from a custom C# codebase (ClassicUO on FNA), Unity presents a moderate learning curve. On one hand, the language remains C#, which is a huge relief – no need to learn C++ or a scripting language. Many .NET libraries and concepts will carry over. On the other hand, Unity is a large engine with its own API and “way of doing things.” Developers will need to get familiar with the Unity Editor (which is quite intuitive after some use) and concepts like GameObjects, Components, Scenes, Prefabs, etc. They’ll also need to learn how to translate some patterns: for example, instead of their game loop pushing draw calls to a SpriteBatch as in XNA, they now let Unity handle drawing and instead update object states and UI in Unity’s Update events. State management might shift to rely more on Unity’s serialization (e.g., Unity can serialize fields on objects for saving settings). The team might have to break some of their subsystems into Unity-friendly pieces (for instance, where ClassicUO might have one monolithic UI manager, in Unity it might be split into multiple Canvas hierarchies or a series of MonoBehaviours). These adaptations require understanding Unity’s component-based architecture, but Unity’s documentation includes guides for those coming from other frameworks (even from XNA/FNA backgrounds). A notable point: because Unity is widely used, there are many community members who have likely tried similar projects (for example, there are some fan projects bringing old RPGs or MMO clients into Unity – not specifically UO beyond MobileUO, but analogous tasks exist). The wealth of knowledge and the familiarity of C# means the ClassicUO devs could become productive in Unity relatively quickly – certainly within weeks they could have basic rendering and input working, and within a few months they might transfer most client features. The Editor might actually speed up some tasks (like UI layout or tweaking animations) compared to coding them by hand. So while Unity is a big engine, the parts of it needed for this project (2D UI, 3D graphics, event handling) are well-documented and commonly used. The team will have to avoid getting lost in the myriad of features and focus on what matters for a UO client. One potential challenge is maintaining an open-source workflow: using Unity means contributors must use Unity to run the project, which is a free but large tool, and merges often involve scene or prefab files which are not as diff-friendly as code. This requires some project discipline (for instance, one might keep most logic in code and only use scenes/prefabs for things that benefit from them to minimize merge conflicts). Many open projects manage using Unity by coordinating who works on which scenes/assets. This is more of a project management consideration than a pure learning issue. Overall, the learning curve is manageable – certainly easier than learning a new programming language or a lower-level engine – and the Unity community’s size ensures that when questions arise, answers are readily available. Unity Technologies also provides extensive official tutorials (e.g., “Unity for programmers” guides) which the team can use to get up to speed (Porting your Unity knowledge to Godot Engine – Andy Touch).

Summary: Unity is a strong candidate for porting ClassicUO to 3D. Its strengths include the ability to reuse the existing C# codebase heavily (Check Out “MobileUO”, a Unity-Based Mobile Client for Ultima Online – The Ultima Codex), a powerful rendering and UI toolkit that can replicate UO’s look and feel, and extensive support and documentation. It handles cross-platform targets (PC, mobile, web) which aligns with ClassicUO’s goal of broad accessibility. The engine’s polished tools could significantly accelerate development of certain features (UI design, visual effects) compared to building from scratch. Potential drawbacks are Unity being closed-source (which might conflict with some open-source purism, though it’s free to use) and the engine’s overhead (a Unity client will consume more resources than a custom lightweight client – something to consider for players on older machines, though it should still run fine given UO’s modest demands). There’s also the need to fit the project into Unity’s paradigm, which can require refactoring and careful design. However, given that Unity has already been used to create a functional UO client on mobile using ClassicUO’s code as a base (Check Out “MobileUO”, a Unity-Based Mobile Client for Ultima Online – The Ultima Codex), it stands as a proven, viable solution with a balanced short-term and long-term outlook for this modernization project.

Godot 4.4.1

1. 3D Rendering & Isometric Support: Godot 4 is a modern open-source game engine with significantly improved 3D capabilities (using Vulkan internally by default). It can comfortably render fully 3D scenes with lights, shadows, and high performance. Setting up an isometric or top-down view in Godot is straightforward. One approach is to use a 3D scene with a camera set at a 45° angle (and possibly an orthographic projection for a true isometric look). Godot supports both perspective and orthographic cameras out of the box. Another approach is to use Godot’s TileMap node in isometric mode – Godot 4’s TileMap supports “isometric” and “custom” tile orientations, which could be used for a more grid-based approach if one treats each UO tile as a tile in Godot. However, since we want a 3D client, we might use actual 3D meshes or sprites in a 3D world. Godot’s rendering engine can handle hundreds of textured planes or low-poly models easily. It also supports GPU particle systems, volumetrics, and other modern 3D features – likely beyond what a UO client would initially need, but it means there’s headroom for visual enhancements. Notably, Godot is used in some projects for isometric games already (there are community tutorials for isometric camera setups) (Isometric Basics in Godot 4.2 (Tilemap Setup, Stacking, Half Blocks)). The engine has features like baked lightmaps and GI probes if global illumination is desired, but those can be ignored for a classic look. Overall, Godot’s 3D engine is fully capable of rendering an Ultima Online world in isometric perspective. Any needed custom behavior (like always facing the camera for sprites) can be achieved via scripting or using Godot’s billboard material feature for sprites.

2. Asset Pipeline Compatibility: By default, Godot can import standard formats (PNG, WAV, glTF 3D models, etc.), but it does not know how to read .mul or .uop files from Ultima Online. The good news is Godot is highly extensible and open source, so one can create custom importers or just load data via code at runtime. For integrating UO assets, there are a couple of approaches:

  • Custom Importer Tool: Godot Editor allows writing EditorPlugins in GDScript or C# that can define a new resource type. The team could, for instance, write a tool that reads UO’s art files and converts them into Godot resources (textures, mesh libraries) when the project is being worked on. This might be complex, but it’s possible to do in C# within Godot’s editor, leveraging the fact that ClassicUO already has C# code to parse .mul files. Essentially, the engine could run ClassicUO’s asset loading in the editor to spit out Godot-friendly formats (for example, create Godot .res or .tres files for each texture or model).
  • Runtime Loading: Alternatively, skip editor import and load assets at runtime like ClassicUO does. Godot 4 with C# can call into any .NET libraries, so theoretically one could embed ClassicUO’s asset reading code directly and on game start, read UO asset files from disk and generate Godot Textures, Materials, Meshes on the fly. Godot’s API allows creating images and textures from raw pixel data, and mesh surfaces from arrays of vertices. This is similar to what ClassicUO does with FNA (creating textures from byte data). The performance might be acceptable if done asynchronously during load screens. A hybrid approach might also work (convert some assets offline, load others on demand).

Godot is open source, so if some core engine change were beneficial (though unlikely needed), the team could even modify the engine – but more practically, they can use GDNative / GDExtension to write native code loaders if C# was not enough. However, with C# available, using the existing C# logic to handle UO’s binary formats is a major plus. It’s worth noting that Godot’s scene system doesn’t demand that all assets be imported via the editor – one can script the creation of resources at runtime freely.

In terms of asset types: UO’s world art is basically textures for terrain and statics, and possibly 3D models if using the Third Dawn art (though ClassicUO uses the 2D art). For a 3D client, the team might introduce new 3D models for some objects, but initially they may just texture map the old sprites onto 3D surfaces. Godot can handle that. UO’s maps could be turned into heightmaps if a 3D terrain was desired (the old client doesn’t use true heightmaps except for the 3D client assets, but one could derive a heightmap from altitude data). Alternatively, leaving the terrain flat and layered as in the 2D client is also possible (e.g., draw ground tiles as flat mesh grid, place static objects as textured quads or meshes above). All of this can be orchestrated via script using the data from .mul files.

In summary, while Godot doesn’t natively understand UO formats, it provides the means to integrate them. The team can reuse ClassicUO’s parsing code in C# within Godot, thanks to Godot 4’s support for C# (which uses .NET 6). By Godot 4.2, C# projects can even export to mobile platforms (Current state of C# platform support in Godot 4.2 – Godot Engine) (Current state of C# platform support in Godot 4.2 – Godot Engine), which indicates the C# integration is solid. This means the heavy lifting of reading UO’s legacy files can occur in familiar C#, and then the data fed into Godot’s scene as needed. That workflow is quite feasible and keeps the pipeline mostly in code (which the ClassicUO devs are used to), rather than forcing a manual import of hundreds of assets through an editor.

3. Shader System Flexibility: Godot 4 uses a unified shading language (closely based on GLSL, but abstracted to work on Vulkan/DirectX). Developers can write custom shaders for materials using Godot’s shader language, which supports 3D (spatial) shaders, 2D canvas shaders, and compute shaders. This means the team can implement custom visual effects as needed. For a UO client, one common need is color hueing (tinting certain textures). In Godot, that could be as simple as adjusting a material’s albedo tint or writing a one-line shader uniform to modulate color. If the team wants to emulate the exact 8-bit palette shifts of the original, they could even use a lookup texture or a shader function. Godot also has a Visual Shader Editor (like a material editor) if one prefers node-based shader creation, though writing the shader in code might be more straightforward for specific effects.

Godot’s default rendering features include PBR materials, but one can make very basic unlit materials for retro looks. For example, ground tiles could use an unshaded texture to appear like the classic client (no lighting). Or one could enable some lighting to give a slight depth to 3D objects – Godot supports both fully dynamic lighting and lightmaps. The shader system is flexible enough to handle special things like animated water, glow effects, transparent ghosts, etc. A specific example: Ultima Online’s client uses additive blending for the “night sight” effect and other glows. In Godot, one can write a shader or simply set a material to additive blending mode to achieve that.

Since Godot is open source, if something were truly lacking in the shader department (which is unlikely), one could extend it. But Godot 4’s shading language covers most needs and even supports advanced features like ray tracing (in 4.4) if one ever considered something wild. There’s also ShaderMaterial and ShaderInclude support, meaning one can reuse shader code across multiple materials easily.

In summary, Godot’s shader system is quite powerful and on par with Unity’s in terms of capability, albeit using code or its own shader graph rather than Unity’s extensive ShaderGraph GUI. For the ClassicUO team, writing a few simple shaders (or even mostly using fixed materials with slight color mods) should be well within reach. They’ll be able to customize the rendering of terrains, statics, and sprites to closely match the traditional look or to incorporate enhancements (e.g., slight normal mapping on ground tiles for better lighting, if they wanted – completely optional). The ease of custom shader development in Godot is moderate: it requires some knowledge of shader programming, but given the team’s technical background and the extensive documentation Godot provides (and community shaders to learn from), it should not pose a big hurdle. Many typical effects might not even need custom shaders since Godot allows blending modes and tinting via material parameters directly.

4. Performance & Optimization: Godot 4 made large strides in 3D performance compared to Godot 3.x. It introduced a modern rendering backend (Vulkan), better utilization of threads, and more optimized rendering of many objects. By default, Godot will perform view frustum culling on objects with a visual instance – it won’t render what’s outside the camera view (Optimizing 3D performance — Godot Engine (latest) documentation …). This is crucial for an open world like UO’s, ensuring that only nearby terrain and objects are drawn.

For further optimization:

  • Godot supports GPU instancing for meshes (if you have many of the same MeshInstance with the same material, it can instance them in a single draw call). For UO, where many objects share the same graphic (think of a forest with identical tree sprites or many copies of a certain decorative object), using MultiMesh or the GPU instancing option on the MeshInstances could batch these and reduce draw calls significantly.
  • There is a LOD system available through the Godot API (you can assign multiple meshes with different detail levels to a MeshInstance and Godot will switch based on distance). This could be used if high-detail 3D models are introduced. For sprites, LOD might not be needed beyond maybe disabling far-away small objects.
  • Godot 4 doesn’t yet have an out-of-the-box occlusion culling solution (like Unity’s baked occluders), but the community and future versions might introduce something. However, in an open map like UO, occlusion culling is less critical except perhaps in dungeons or towns with many buildings (where you don’t want to draw interiors of houses that are off-screen or obscured). Some of that can be managed by logic (e.g., don’t spawn or draw items inside buildings unless the player is near). The ClassicUO logic could assist here (since the server anyway only sends nearby items).
  • Physics: Godot has a physics engine, but an MMO client might only use it for trivial things (like maybe click-raycasts or some dynamic environmental effects). If not used, turning off physics on objects can save overhead.

Godot provides a profiler and monitors for FPS, draw calls, etc. While perhaps not as granular as Unity’s profiler, it does allow developers to see where time is spent (script, physics, rendering) and how many objects are in play. In Godot 4, the RenderingDevice allows asynchronous loading and such, which can help with streaming in assets dynamically without stalling frames.

One potential performance challenge is that Godot is not (as of 4.x) as battle-tested in massive scenes as Unity/Unreal. Some users have noted areas to optimize when having thousands of objects (there have been improvements, but Unity might still edge it out in handling huge object counts). However, with smart use of MultiMesh for repeating tiles or objects, one can mitigate this. For example, the terrain could be one chunk = one MeshInstance (like combining multiple tiles), drastically reducing object count. The static decorations could be grouped by region. These are things the ClassicUO team can implement since they have control over how to represent the world in Godot – they might mimic the client’s map sector concept to create one Godot StaticBody or MultiMesh per sector containing many elements, thereby reducing node count.

Memory management in Godot (with C#) will be similar to .NET memory management – the team should be mindful of allocations, but they can use .NET’s GC and value types where appropriate. Godot’s C# is using .NET 6, which has seen performance improvements over older Mono.

Godot also supports running code in threads for things like pathfinding or network handling, which can keep the main thread free for rendering and input.

Given that Ultima Online’s scale isn’t billions of polygons or anything, and the fact ClassicUO runs well even with many objects (due to efficient sprite drawing), we can be optimistic that Godot can meet the performance needs. It may require some custom optimization (like how to render a large static world cheaply), but nothing insurmountable. Also, since Godot is open source, if certain engine-level performance issues are encountered, the team or contributors could potentially patch them or configure around them.

In summary, Godot’s performance toolkit includes basic culling (Optimizing 3D performance — Godot Engine (latest) documentation …), instancing, LOD, a decent profiler, and the possibility to do custom culling or logic in GDScript/C# if needed (like you could write a script to hide very far away entities or use a simpler representation – similar to a custom LOD system). The optimization effort would likely focus on how to manage thousands of world objects gracefully, but the engine provides mechanisms to assist. Considering community feedback, Godot 4’s 3D performance is a big improvement, and it continues to get better with each minor release, which is promising for a long-term project.

5. Scripting & Extensibility (C# logic reuse): Godot 4 supports scripting in C# 10 (.NET 6), which is a huge boon for this project. (It also supports its native GDScript, but the presence of C# means the ClassicUO team can stick to the language they already use). As of Godot 4.2, C# can be exported to Android/iOS, and by Godot 4.4.1 we can assume it’s quite stable. Using C# in Godot means the team can port large portions of ClassicUO’s code almost verbatim. For instance, data models for entities (mobiles, items) can be reused; packet handling code can likely be transplanted with minor adjustments (the main change is likely using Godot’s API for things like reading files or doing drawings, but the logic itself remains). The ability to reuse code is second only to Unity in this list. A lot of ClassicUO’s code is not engine-specific (e.g., handling the UO protocol, game mechanics like skill timers, macro logic, etc.), and all of that could run inside Godot’s C# environment (GitHub – ClassicUO/ClassicUO: ClassicUO – an open source implementation of the Ultima Online Classic Client.). Code that was tied to FNA (like drawing functions) would be replaced by Godot equivalents (which would be a necessary adaptation in any engine).

Godot’s C# integration is via GDextension and the .NET runtime, meaning one can also use .NET libraries. If ClassicUO uses any .NET libraries (for example, for configuration or compression), those can likely be included. However, caution is needed: Godot’s engine API will be different from XNA – the team will have to rewrite rendering and input portions. But this rewrite is much smaller than a total rewrite in a new language; it’s more about using a different library. Think of it like porting from one UI framework to another in C# – the structure can remain, just the API calls change.

Besides C#, Godot still offers GDScript, which is a high-level Python-like script. The team could use GDScript for some glue or quick iterating if desired (for example, some might find UI layout easier in GDScript), but they could also do everything in C#. Godot lets C# and GDScript interact, but sticking primarily to one language might simplify development.

Extensibility: Being open source, Godot allows modifications on the engine level. But even without touching engine source, you can add C++ via GDNative/GDExtension or use C libraries through PInvoke (since .NET allows calling DLLs). For example, if there was a C library for UO file parsing or a particularly performance-critical bit, they could integrate it. But likely, pure C# will suffice.

Godot’s editor is also extensible via C# or GDScript. The team could write custom editor tools if needed (e.g., a visual map debug tool). This could make development smoother but is optional.

A notable difference from Unity: Godot doesn’t impose a component system with Update methods – instead, you attach scripts to nodes and can override callbacks like _Process (per-frame) or _PhysicsProcess. You can also create singleton AutoLoad scripts for global managers. This is quite flexible and not far from the way ClassicUO organizes things (ClassicUO has singletons for certain subsystems; those could map to Godot AutoLoad singletons easily). The team will need to learn Godot’s API (for scene tree, signals, etc.), but they can still structure their game as they see fit. They might choose to mirror some of ClassicUO’s architecture (like a World class to hold all entities, etc.) and simply integrate that with Godot’s scene graph as needed.

In terms of using C#: one should note that as of Godot 4.2, not all platforms supported C# export (web was not yet working for C#). By 4.4 or later, it might improve (NativeAOT is being explored for mobile/web support) (Current state of C# platform support in Godot 4.2 – Godot Engine) (Current state of C# platform support in Godot 4.2 – Godot Engine). But desktop platforms are fine. So if a browser client is needed, currently Godot might require using GDScript for the web build or waiting until .NET 8 AOT for WebAssembly is fully integrated. This is a slight consideration: Unity can do WebGL now, whereas Godot C# might not quite yet. However, Godot GDScript could be used for a web build if absolutely needed, or perhaps by the time this project is far along, Godot’s .NET to Web export will exist (the Godot blog indicated plans with .NET NativeAOT to support web in the future (Current state of C# platform support in Godot 4.2 – Godot Engine)).

Summarily, the ability to use C# in Godot makes it one of the top contenders for reusing ClassicUO’s logic, second only to Unity. It combines that with the freedom of open source and no license fees. The ClassicUO devs would have to adapt to Godot’s methods (just as they would to Unity’s), but they can keep writing in C# and even share code between the existing client and the new one during a transition period if needed. That significantly de-risks the porting of complex subsystems (like the network protocol handling, which is quite involved and already implemented in ClassicUO’s C#).

6. Cross-Platform Support: Godot is renowned for its cross-platform nature. A Godot project can be deployed to Windows, Linux, macOS easily. It also supports Android and iOS (with Godot 4, mobile support for .NET was finalized in 4.2 (Current state of C# platform support in Godot 4.2 – Godot Engine) (Current state of C# platform support in Godot 4.2 – Godot Engine)). For web, Godot can export to HTML5/WebAssembly (using WebGL for rendering and threading support via WebAssembly threads where available). The caveat, as mentioned, is that the C# scripting may not yet export to web or was experimental by 4.2 (because Mono runtime on WebAssembly had limitations). If by 4.4.1 that remains unsolved, one path is maintaining a GDScript version for web – but that could be a lot of duplicate work. It might be acceptable to not have a web client immediately, focusing on desktop and mobile (which cover the majority of use cases, since ClassicUO’s current web client is more a novelty than a widely used platform).

On desktop and mobile, Godot’s engine will utilize Vulkan or appropriate graphics APIs (it has fallback to OpenGL for older devices). For example, on Windows it can use Vulkan or Direct3D via ANGLE, on macOS it can use Vulkan via MoltenVK or fallback to OpenGL/Metal, on Android Vulkan, etc. The Godot editor itself runs on Windows/Linux/macOS, meaning developers on any of those can contribute (unlike Unity which until recently didn’t support Linux editor officially – but it does now; still, Godot’s small footprint is nice for open-source contributors).

For mobile: Godot’s support means a 3D UO client could run on Android phones/tablets and iPhones/iPads. The engine handles touch input events (which can be mapped to UO interactions like tapping to target or dragging items). UI scaling is something to manage (Godot has a CanvasLayer and stretch settings to accommodate different screen sizes). These are solvable and similar to what one would do in Unity or a custom mobile client. Performance on mobile would depend on complexity of scenes and whether advanced effects are used; since UO art is not very heavy, a phone could likely handle it (MobileUO via Unity already proved mobile viability, and Godot is known to run even on low-end hardware fairly well).

Cross-platform deployment in Godot is straightforward – it produces small binaries (the engine binary + game PCK). The open-source license (MIT) means there’s no restriction or fees for distribution.

One consideration: if the team really wants a browser client but C# in web isn’t ready, they could consider writing the game logic in GDScript or use a subset of logic in GDScript for the web build. Alternatively, Godot 4 supports exporting to WebAssembly with the Mono runtime via interpreting, but it might have limitations (like no threads which can hinder heavy networking unless using async). The Godot blog mentioned exploring .NET NativeAOT for web which might by 2025 be more mature (Current state of C# platform support in Godot 4.2 – Godot Engine). So, by the time a 3D ClassicUO is ready, that gap might close.

In summary, Godot covers all major platforms relevant to UO players: Windows (majority of players), Linux and Mac (the ClassicUO community has many Linux/Mac users who rely on its cross-platform nature), and mobile (which is a growth area for UO if made accessible). It likely can also cover web in the near future, though that might lag behind Unity’s web support slightly. Since ClassicUO itself already listed support for Browser, Windows, Linux, macOS via FNA (GitHub – ClassicUO/ClassicUO: ClassicUO – an open source implementation of the Ultima Online Classic Client.), using Godot would maintain that broad support (with a question mark on browser for the moment if sticking to C#). Given that open-source community projects value multi-platform, Godot aligns well here, with the advantage of no external dependencies (players wouldn’t need to install .NET or anything to run the Godot-based client; it’s self-contained).

7. Community Maturity & Tooling: Godot’s community has grown explosively in recent years, especially after 2022/2023. It has an active user base, detailed documentation, and an accessible development ethos (being open source). While its community is not as large as Unity’s, it is very passionate and helpful. There are many tutorials, Q&A on Godot forums, Reddit, etc., and since it’s open source, one can even read engine code to understand behaviors.

For an MMO client specifically, Godot does not have off-the-shelf modules (like how Unity might have third-party networking libraries), but the ClassicUO project likely doesn’t need those since it has its own systems. The Godot community has tackled similar problems in pieces: e.g., there are tutorials on creating multiplayer games in Godot (though usually focusing on Godot’s high-level multiplayer API which likely wouldn’t be used here), guides for large open world handling (some users have done large terrain streaming in Godot), and a wealth of knowledge on 2D UI, input, etc. The official documentation covers the engine’s nodes and APIs thoroughly, and for C# specifics, the docs also list how to use the C# bindings (plus one can always refer to GDScript docs and translate to C# as needed since the API names are the same). The ClassicUO team might have to rely more on community support for niche issues (like optimizing thousands of sprite draw calls in Godot) – but being open source, if something isn’t performing, they can profile it and possibly even contribute a fix or improvement to the engine (or request it). The Godot developers and community are quite responsive to issue reports, especially if you can provide a test case.

In terms of tooling:

  • The Godot Editor is a full IDE for scenes and assets, similar to Unity but lighter. It allows visual editing of scenes, UI, etc., which the team can use as much or as little as they want. For an open-world game, they might not place terrain blocks by hand in the editor (that will be generated from UO data instead), but they could design UI layouts or test scenes within it. The editor’s UI system has a visual layout tool which could be handy for reconstructing UO’s UI (dragging controls to approximate positions, etc., then refining in code).
  • Godot’s debugging includes a remote scene tree inspector (to see nodes at runtime), a console, and the ability to do print debugging or attach C# debuggers (MonoDevelop or VSCode with Mono debugging can attach to Godot’s C#).
  • The Godot profiler as mentioned can show performance metrics like frame time, though it is not as comprehensive as Unity’s (it does show draw calls, memory, etc., but not per-line script timing out of the box until you instrument with @profile decorators or such). Still, it’s sufficient to optimize a project like this.

One thing to note: Godot’s engine and community emphasize openness and collaboration, which might attract contributors who prefer an open tech stack (some might have been hesitant to contribute to a Unity-based client due to Unity’s proprietary nature). With Godot, every contributor can easily set up the project (just download Godot editor, which is a small binary, and open the project – no accounts or licenses needed). This aligns with ClassicUO’s open-source spirit.

Maturity-wise, Godot 4 is relatively new (released in 2023), but by 4.4 (2025) it has stabilized with several patches, and many early bugs have been ironed out. The core systems relevant to this project (3D rendering, C# support, UI, input) are stable. Some specialized features are still evolving (like Occlusion Culling, advanced GI), but those are not dealbreakers. The engine is being actively developed, so improvements in performance and features can be expected during the project’s life – an advantage if the timeline is long-term, as the project can benefit from engine upgrades without extra cost.

8. MMORPG Client Suitability: Using Godot as an MMO client is certainly possible, though it doesn’t provide MMO-specific infrastructure out of the box – we rely on implementing ClassicUO’s client logic. Key aspects:

  • Networking: Godot offers a high-level multiplayer API for Godot-to-Godot networking (with an ENet-based system), but that is not applicable to connecting to a UO shard. Instead, one would use Godot’s low-level networking via StreamPeerTCP or even directly using .NET’s System.Net.Sockets within C#. The straightforward route is to literally run the ClassicUO network code in the Godot game: open a TCP socket, send/receive packets, decode them. Godot’s C# environment should allow that (and if not, one could PInvoke to OS sockets or use Godot’s TCP classes). So, the client will still handle encryption, compression, and packet assembly exactly as ClassicUO does. Godot doesn’t get in the way here – it even has methods to do packet I/O in GDScript for simpler cases, but using the tried-and-tested ClassicUO C# networking code is ideal.

  • One consideration: threads. In ClassicUO, the network reading might be on a separate thread. In Godot C#, you can spin up a System.Threading.Thread to listen to the socket and then push events to the main thread via CallDeferred or Godot signals. This is workable. Alternatively, use Godot’s StreamPeerTCP in an asynchronous manner via its get_available_bytes pooling in _Process. Either way, it’s doable, and performance of .NET sockets is fine for UO’s traffic volume (which is quite low by modern standards).

  • On Web (if aiming for web): direct TCP won’t work; one would need WebSocket. Godot’s core can do WebSocket connections as well. So if that scenario arises, it’s solvable with a proxy or by teaching the client to speak WebSocket to an intermediary.

  • World Streaming: Ultima Online world is large, but the server only sends the nearby area. The client must load map data (which is on disk) and static art for each new zone as the player moves. ClassicUO handles this by reading from the .mul files on the fly. In Godot, one could do similarly – whenever the player enters a new map sector, load the terrain and statics for that sector. This could be done in C# using ClassicUO’s file reading code, then create Godot objects for them. The performance of loading, say, an 18×18 tile area with statics on the fly should be fine (it’s mostly reading a few hundred kilobytes from disk and creating a few hundred objects). Godot can handle adding/removing objects dynamically during gameplay (especially if done in a controlled manner, possibly yielding over frames to avoid hitches). And since ClassicUO already has logic to cache and only update sectors when needed, that logic can be reused to manage Godot nodes.

  • If the team wanted to pre-load more of the map into memory for seamlessness, they could – but that increases memory usage. Probably sticking to roughly what ClassicUO does (keeping surrounding sectors in memory) is fine.

  • Entity Management: Godot’s scene graph could potentially mirror the game world. For instance, you might have a top-level node for “World” and child nodes for “Sector (x,y)” and under those, nodes for each item/mobile. However, thousands of nodes could be heavy – another approach is not to map 1:1 each item to a node unless needed. Some static decor that never changes could be combined into a single VisualInstance (like one MultiMesh for all grass in a region). Mobiles and interactive objects would definitely be nodes so they can have scripts for interaction. Godot is flexible in that you can have a data-only representation (C# classes not attached to nodes) and only create Node representations for things that need rendering or collision. So the team might maintain the “WorldState” as pure C# classes (like they do now) and then sync that with the scene nodes for display. This separation can actually be clean – e.g., an Item class with properties exists in logic, and when in view, an ItemSprite node is instantiated to show it. This is similar to how ClassicUO separates game objects from UI objects.

  • UI and Input: Godot’s UI system (Control nodes) can handle the drag-drop, gumps, text inputs, etc., needed for an MMO interface. It also supports gamepad or touch events, which might be forward-looking for new input methods. For an MMO, complex UI (skills window, journal, options) needs to be built – Godot’s UI nodes plus some custom code will be used, but as discussed in the UI section below, Godot can certainly do it.

  • One possible hurdle: Godot’s text input handling for chat – but Godot does have LineEdit and TextEdit controls for single-line and multi-line input, which can be adapted for chat and command entry, etc.

  • Other MMO Features: Things like macro recording (ClassicUO has a macro system) can be implemented similarly (Godot can capture input events and the logic can run in C# as before). Multi-client support (running multiple game instances) is more of a launcher concern, but Godot being small, one could run multiple instances if needed (though Unity could as well).

  • Scripting in-game (if custom Razor-like scripts) would be external to engine, so unaffected by engine choice.

  • File patching/updating: Godot games can be distributed as PCK files; updating could be done by distributing new PCK or using an external patcher (similar to how ClassicUO updates itself). This is a solvable deployment detail.

In short, Godot doesn’t offer MMO-specific high-level functionality (like a built-in chat system or friends list – but ClassicUO has those covered via server communications anyway). What it offers is a flexible framework to build the client-side features on. The ClassicUO devs will carry over their robust implementations of MMO client requirements (like handling massive item lists in containers, or ensuring the client doesn’t choke on big player vendor shops, etc.), and use Godot’s rendering and UI to present them. Godot’s open nature even allows tweaking things like engine tick rate if needed (to match the 20fps logic of UO’s server, for example, though typically the client can run faster and interpolate).

9. Integration with Modern UI Systems: Godot’s UI system is based on Control nodes in a scene tree and is very capable of constructing game interfaces. It is somewhat similar conceptually to Unity’s UI: you have a root Control (like a Canvas), and you can anchor elements, use containers for automatic layout, etc. UO’s interface can be implemented in Godot by creating custom Control scenes for each “gump” or window. For example, one could create a PaperdollWindow.tscn scene that is a Control with sprites for the paperdoll background and equipment slots, and code to handle drag-and-drop of items onto it. Each such UI element can be instantiated when needed and added to a CanvasLayer so it’s drawn above the world scene.

Godot supports:

  • Drag and drop: There is an API for starting a drag (Control.SetDragData) and handling drop (Control.CanDropData and DropData). This is perfectly suited to implement dragging items between containers or onto the game world. The team will likely need to manage the logic (e.g., what happens if you drop an item onto a paperdoll slot – call the server move request, etc.), but Godot gives the mechanism to get those events.
  • Custom drawing: For complex UI like drawing the backpack grid or the status bars, one can either compose with individual Controls (like each item slot is a Button with an icon) or do custom drawing in a Control’s _Draw callback for efficiency. Godot’s ImmediateGUI or CanvasItem draw functions let you draw primitives if needed (though probably not necessary for UO’s UI except maybe the backpack grid background).
  • Text and Fonts: Godot supports dynamic fonts (vector or bitmap). ClassicUO uses pixel fonts (like classic UO journal font); these can be imported as bitmap fonts in Godot so that the chat and journal look authentic. The UI system also supports outlines or shadows on text, which could replicate the black outline UO uses on some text. Internationalization (Unicode support) is also present, which might help if shards allow Unicode characters – ClassicUO already extended some support for that.
  • UI Scaling: Because many UO UI elements are fixed-size bitmaps, at high resolutions they might appear small. Godot can easily scale UI either globally or per element (e.g., use a Container with stretch, or set a custom transform on the UI root). The team can offer a UI scale slider to users – since Godot UI is resolution-independent, that’s straightforward (just scale the CanvasLayer). This modern convenience would be nice for 4K monitor users of the client.
  • Themes and Styling: Godot’s UI can be themed using a Theme resource. The default style might not match UO (since UO has very custom art), but since we’ll be using UO’s art for most controls, we might not use general theme for much beyond default font maybe.
  • Tooltips: Godot provides a builtin tooltip mechanism for Controls (each control can have a tooltip string that appears on hover). The client can leverage that for item info hover text.
  • Modal dialogs, etc.: Also possible (MessageBoxes with OK/Cancel can be easily made).
  • Inventory management: The container gump can be a Control that holds child Controls for each item (with their sprite and maybe quantity label). Items in UO containers are placed at specific x,y positions (because in UO, you can arrange items freely in the gump). This is actually a bit uncommon for modern UI (most modern UIs grid-align inventory), but Godot can handle free placement: each item icon Control can be given exact coordinates within the container Control. This is just setting RectPosition of the child control. So that system can be reproduced 1:1. Godot’s UI doesn’t restrict you to grid unless you use a GridContainer (which we wouldn’t; we’d place by item.X, item.Y from the packet).
  • HUD: UI like health bars over characters can be done either with Control nodes that track a world position (using Control.RectPosition = world_to_viewport(character_position)) or by using Godot’s Viewport and CanvasLayer trickery. An easier method: Godot has a Marker2D node which can represent a 2D point in 3D space projected into a canvas – that can be used for overhead text or bars. Or simply compute projection in code each frame. Either way, mix of 2D UI and 3D world is possible.

What about things like scaling the game world window or switching between fullscreen and a windowed game area (the old client had a fixed game window you could resize)? Godot can manage multiple viewports – one could render the 3D world to a sub-viewport if they wanted a UI frame around it. But simpler is just to have the game world be the background and overlay UI on top; then if you want a smaller game view, you could letterbox it with UI panels. Either approach is doable.

Developer UI: For debugging, Godot’s ability to create editor plugins could allow development overlays (like highlighting packets received or showing a grid). But that’s extra – not required for shipping.

Overall, Godot’s UI system can handle the complexity of UO’s interface. The ClassicUO team will have to reimplement UI in this system, but they would have to reimplement UI in any new engine (unless they embed an external UI library). The benefit is Godot’s UI is high-level (no need to manually draw everything pixel by pixel as one might in a custom engine) and very flexible, so this work, while significant, is certainly tractable.

10. Learning Curve & Onboarding: For ClassicUO developers, shifting to Godot requires learning a new engine and possibly some new language if they opt to use GDScript for parts. However, since they can use C#, the language and many libraries remain the same. This is a huge relief as they can port code over without also translating logic into, say, C++ or JavaScript. They will need to learn:

  • Godot’s Node and Scene system (how scenes are composed of nodes, how the tree is organized, how to load/unload scenes).
  • The Godot Editor workflow (importing assets, using the inspector, etc.). The editor is generally considered user-friendly and lightweight. Since ClassicUO devs may not be used to a visual editor (ClassicUO is mostly code-driven), there might be an adjustment to trusting an editor for certain setups. But they can still do a lot in code if they prefer (Godot allows dynamic creation of nodes without making them in the editor).
  • Godot API for C#: It’s essentially a .NET binding to the Godot Engine. They’ll use classes like Godot.Texture, Godot.MeshInstance3D, etc., and connect signals possibly via code. The naming and structure differs from XNA/FNA, so there’s learning of new API calls. But the official documentation provides examples in GDScript which can be pretty directly translated to C# with minor syntax changes (the Godot C# docs show how signals and such map to C# events, etc.).
  • They might need to learn some GDScript basics anyway, because a lot of community help and examples are in GDScript. GDScript is easy to read for a C# dev (it looks like Python), so they can likely interpret examples and implement in C# analogously. If they run into an issue that a plugin solves but the plugin is in GDScript, they could even incorporate GDScript in their project since Godot allows mixing. But ideally they keep most in one language for consistency.
  • Godot’s way of handling Input (via InputMap and actions) which is quite handy: they can define input actions (like “moveforward”, “openstatus”) and assign keys or mouse events to them in the project settings. This can help implementing configurable keybinds like ClassicUO has. They’ll adapt their input handling to use Godot’s system or query Input.IsActionPressed() in code.
  • Godot’s performance tuning: understanding how to use Godot’s profiler, how to use the yield or await ToSignal for asynchronous operations (like wait for a resource to load). Also, since they will be dealing with both engine objects (Nodes) and pure data objects, they need to be mindful of Godot’s ownership model (in C#, when you new a Node you must add it to scene tree or free it to avoid leaks, etc. – similar concept to memory management they had but now with engine objects).
  • The community fosters good practices which they can pick up – for example, using signals for decoupling. ClassicUO’s code might currently call directly into UI, but in Godot they might use signals (events) to notify UI to update. This is a design shift, but one that could improve maintainability.

The Godot learning curve is often described as moderate: easier than engines like Unreal, maybe on par with or slightly above Unity (Unity has more decades of documentation, but Godot is simpler in architecture). A plus is that Godot’s entire feature set is accessible and not a black box – reading the official demos or example projects can accelerate learning. The ClassicUO devs will likely need to build a small prototype in Godot first (maybe load a map chunk and display a mobile moving) to get a feel for it, then iterate from there. The turnaround times with Godot (it’s lightweight, fast to start) are conducive to experimentation.

Onboarding new contributors might actually become easier: Godot’s editor has less overhead than Unity (no heavy install, no accounts), and being open source, contributors can compile the engine if they need to debug something at engine-level. Also, a lot of gameplay logic will remain in familiar C# classes, so contributors who know the ClassicUO code will recognize parts of it in the new project as well.

One area to be mindful of is bugs or missing engine features, as Godot 4 is relatively new – but since it’s open source, the team is not blocked; they can try fixes or workarounds. For example, early Godot 4 had some C#-specific bugs (like long assembly load times) which by 4.2 got fixed. By 4.4.1, it should be quite stable. The Godot QA for C# is not as battle-tested as GDScript, so a learning aspect might be dealing with any C# integration quirks (though none glaring come to mind beyond platform support which we discussed).

Summary: Godot presents a moderate learning curve largely because it’s a new engine to learn, but it uses a familiar language and offers an accessible, well-documented environment. The ClassicUO developers will need to adjust to a different workflow (more editor usage, scene graph thinking, signals, etc.), but none of these are alien concepts in software development. With Godot’s growing popularity, they can find many guides on how to implement RPG features, UI, etc., which align with what they need. The fact that they can leverage their existing code greatly smoothens the transition. In comparison to other options like Unreal or even Bevy, Godot’s learning curve is much gentler, making it a viable choice for a team that wants to modernize but not reinvent everything from scratch or dive into low-level programming.

Pros: Open-source (MIT licensed) engine with no royalties; supports C# scripting allowing extensive reuse of ClassicUO code and knowledge (GitHub – ClassicUO/ClassicUO: ClassicUO – an open source implementation of the Ultima Online Classic Client.); capable 3D renderer suitable for isometric view; good built-in tools for UI and scene management; active community and continuous improvements; lightweight and highly customizable (engine source available); cross-platform reach including potential mobile and web (with some consideration for C# on web) (Current state of C# platform support in Godot 4.2 – Godot Engine) (Current state of C# platform support in Godot 4.2 – Godot Engine).

Cons: Less out-of-the-box polish and assets compared to Unity (team must implement many systems, though they have them from ClassicUO); Godot 4 is newer, so some features are still maturing (e.g., lack of built-in occlusion culling, C# on WebAssembly still experimental); smaller community than Unity/Unreal (though very supportive, it might have fewer MMO-specific examples to draw on); learning and adapting to Godot’s framework will take some time and refactoring effort.

MonoGame (Lightweight XNA Framework)

1. 3D Rendering & Isometric Support: MonoGame is a low-level framework that reimplements Microsoft XNA 4.0. It provides the basics for rendering, audio, input, etc., but without an engine-level scene graph or editor. Using MonoGame for a 3D client means the team would be largely on their own to handle rendering of an isometric scene. MonoGame can do 3D – XNA had support for 3D graphics (it has Model classes, BasicEffect for simple lighting, the ability to draw primitives and use custom shaders). Isometric or top-down perspective is achievable by setting up the appropriate camera projection (MonoGame’s BasicEffect has parameters for view and projection matrices, so one can use a custom orthographic projection matrix to simulate isometric). In practice, many XNA/MonoGame developers have made 3D games, even some isometric ones (though 2D was more common). There’s no built-in tilemap or isometric helper, so everything from how to draw the terrain grid to how to order drawing of sprites (for proper overlap) would need to be coded directly. The ClassicUO team already solved those problems in 2D (like sorting by draw order), but implementing it in 3D means doing similar logic but with additional considerations like maybe perspective projection sorting.

MonoGame doesn’t include fancy rendering features – it’s basically a wrapper over DirectX/OpenGL calls. The team would manually manage the draw calls each frame (like they do with FNA). For example, to draw the world, they might iterate over visible tiles and call spriteBatch.Draw for sprites or graphicsDevice.DrawPrimitives for model meshes. They can certainly achieve an isometric camera by using an orthographic projection and appropriate transforms for objects (XNA’s math libraries would help create the matrices). There are community extensions like MonoGame.Extended that offer some higher-level features (like camera2D, basic tilemap, etc.), but nothing specific to 3D isometric – they would be writing the book on that likely.

In summary, MonoGame gives the freedom to render whatever and however, but provides no out-of-the-box support tailored to isometric 3D. The ClassicUO developers, being intimately familiar with how the 2D client draws, could adapt that knowledge: for instance, they might still treat the world as a 2D grid for draw ordering, but for 3D visuals they might draw ground tiles as quads, and draw entities as 2D billboards or 3D models in sorted order. They have to code all that explicitly.

2. Asset Pipeline Compatibility: One advantage of MonoGame is that ClassicUO is already built on a similar framework (FNA, which is an XNA-like library) (GitHub – ClassicUO/ClassicUO: ClassicUO – an open source implementation of the Ultima Online Classic Client.). In fact, ClassicUO uses FNA specifically because XNA-style code works well for UO’s needs. Therefore, the asset handling (loading .mul files, converting textures to Texture2D, etc.) is already implemented in ClassicUO and would work almost unchanged in MonoGame. MonoGame content pipeline could be used to preprocess assets (it can import sprite images or models to an .xnb format), but the ClassicUO approach of loading from the original data files at runtime could continue. MonoGame gives the developer full control to open files, read bytes, and create textures – which ClassicUO does with FNA’s Texture2D.SetData() calls. So code reuse is extremely high here: literally, ClassicUO’s asset-loading codebase (which is in C#) could be transplanted into a MonoGame project and it would function with minimal changes, since MonoGame’s Texture2D and SpriteBatch API is almost identical to XNA’s which FNA replicates.

For supporting new 3D assets, MonoGame can load 3D models via its content pipeline (e.g., .FBX files can be processed into Model objects). Alternatively, one can manually load model data (MonoGame provides a VertexBuffer and IndexBuffer class if you want to roll your own model loader). If the plan is to gradually introduce 3D models (say for some objects or characters), MonoGame can accommodate that, but the team would have to either use the content pipeline (which is an offline step using the MonoGame Content Pipeline Tool to convert assets) or integrate a library like Assimp via C# to load models at runtime.

Another aspect: MonoGame (and XNA) typically uses the content pipeline for building assets into its own compressed format (.xnb). ClassicUO opted to just use the original assets directly for flexibility. They can maintain that approach – no need to convert UO files to something else; their code can read .mul archives, decode images, and create XNA Texture2D objects on the fly. This is precisely how ClassicUO on FNA works and it’s a proven method for them.

3. Shader System Flexibility: MonoGame supports HLSL shaders through the concept of Effect files. XNA had a system where you’d write an HLSL shader in a .fx file and compile it (via the content pipeline or at runtime) into an Effect which can be applied when drawing. MonoGame retains this capability. ClassicUO’s 2D engine likely didn’t use many custom shaders beyond maybe a hue adjustment or some post-processing (in fact, ClassicUO uses regular sprite drawing, perhaps with palette changes done on CPU). If moving to 3D, the need for shaders might increase (for example, to handle lighting on 3D models, or to simulate the multi-layered terrain transitions, etc.). MonoGame’s BasicEffect covers simple cases (textured lit/unlit geometry). For anything custom, the team can write an HLSL shader. This is lower-level than Unity’s or Godot’s approach – you’re writing raw shader code similar to how you would in DirectX9/11. But since ClassicUO devs might not have extensive shader experience, they could start with using BasicEffect for any 3D models (which gives basic lighting and texturing), and use the built-in sprite batch for sprites (which under the hood uses a basic shader for texture mapping). If they need something like smooth palette hueing, they might implement that by swapping palettes on the CPU as they do now, or write a simple shader to index a palette. MonoGame gives total control: you can intercept drawing and do whatever in a shader, but you have to implement it.

No visual editor exists for shaders; it’s write HLSL, compile, test. The MonoGame community likely has resources on writing custom effects (and XNA literature is applicable). For example, to replicate UO’s night-time lighting (darkness with light sources), one could either pre-compute lightmaps or use a shader to modulate brightness based on some light values. In XNA, one might do a multi-pass approach or a shader that samples a light texture. The team would need to develop these techniques themselves, as MonoGame won’t have them ready-made.

4. Performance & Optimization: MonoGame is essentially as performant as you code it. It’s a thin layer over graphics API calls (DirectX11 on Windows, OpenGL on others, potentially Vulkan via community FNA forks or experimental MonoGame versions). The performance is largely determined by the efficiency of the game’s code and how well it uses the GPU.

  • Culling: MonoGame doesn’t auto-cull anything (aside from not drawing what you don’t tell it to). The team would have to implement view frustum culling in code. They can use XNA’s BoundingFrustum and BoundingBox classes to test which objects are within the camera view and only draw those. ClassicUO already does a form of culling (only drawing items in range of the player’s screen). Extending that to 3D simply means also not drawing things behind the camera (in an isometric top-down, behind camera culling is less of an issue since camera covers 360 around, but for a limited viewport it’s basically the same region logic as 2D).
  • Batching: MonoGame’s SpriteBatch is highly optimized for 2D – it batches all draws between Begin() and End() that share the same texture into one or few draw calls. ClassicUO leverages this, drawing many map tiles in a single batch. That can continue. For 3D, if they use Model class, each ModelMesh might be a separate draw unless they combine them. They might manually batch by writing their own drawing routine that draws all ground tiles as one set of triangles using a custom vertex buffer (which is advanced but doable). Essentially, optimization is manual: combine static geometry, use instancing if helpful (MonoGame does have an Instancing sample using shaders to draw many of the same mesh).
  • LOD: There’s no inherent LOD system; the team would have to implement any kind of detail reduction (like not drawing distant small objects).
  • Profiling: MonoGame doesn’t have a built-in profiler GUI. The team would rely on external profilers or add logging to measure frame times. They could integrate something like MiniProfiler for .NET or even make a simple in-game display of stats (like they do in ClassicUO’s debug mode). They will have to identify bottlenecks (e.g., if drawing too many individual sprites – they have the knowledge from 2D optimization to handle that).
  • MonoGame (and XNA) can run at pretty high speeds if used well – after all, XNA games like Stardew Valley or Celeste run great even with lots of entities, but those are 2D. For 3D, some developers have made even FPS in XNA. It’s possible to achieve good performance; it just requires more fine-tuning by the devs compared to engines that do it for you. The ClassicUO team is already quite performance-conscious (ClassicUO runs significantly faster than the original client by using modern techniques). They would bring that ethos to MonoGame 3D as well, essentially building an optimized renderer specifically for UO’s needs. They might for instance decide to pre-batch the static map into large meshes to avoid per-tile draw overhead – that’s something completely under their control with MonoGame.

Memory management is manual as well. They’ll have to be careful about generating garbage (like don’t new a million small objects each frame). But since they control all game loops, they can ensure performance-critical sections are efficient. ClassicUO likely has already optimized some things like avoiding per-frame LINQ allocations, etc., which they can carry on.

One big plus: Because ClassicUO already runs on a similar paradigm (game loop pushing out draw calls), a lot of their performance considerations and algorithms can remain. The difference is possibly increased complexity in visuals (3D models, more textures loaded at once for a full scene). The team will have to manage memory of textures and models (e.g., unloading sectors that are far away, etc. – which they already do with 2D art to some extent). With MonoGame, nothing stops them from implementing a caching system for textures or models, since they have full control.

5. Scripting & Extensibility (C# logic reuse): This is MonoGame’s greatest strength in this context. ClassicUO is built on FNA (an XNA reimplementation in C#) and the logic is entirely C# (GitHub – ClassicUO/ClassicUO: ClassicUO – an open source implementation of the Ultima Online Classic Client.). Adopting MonoGame (which is very close to XNA in API) means the entire ClassicUO codebase can be ported with minimal changes. In fact, large parts might run out of the box: networking code (which uses .NET sockets), data structures, the game state management, the macro system, etc., none of those depend on FNA specifically. They will, of course, need to modify the rendering portions – ClassicUO uses FNA/SDL for graphics and input. Switching to MonoGame means rewriting the input handling to use MonoGame’s Keyboard.GetState() / Mouse.GetState() and mapping that to game actions. But that’s straightforward and quite similar to how it might be now.

MonoGame is not an engine, so there is no “engine-imposed” architecture to learn. It’s basically like continuing with ClassicUO but with a different rendering backend. The team is already the “engine” in that scenario – they decide how to structure everything. This can be as flexible as they want. If they prefer to keep the code structure almost identical to ClassicUO, they can. For example, ClassicUO likely has an update loop that moves mobiles, and a draw loop that blits sprites; in MonoGame they will implement Game.Update() to handle game state updates and Game.Draw() to issue draw calls for world and UI, mirroring what they did manually before. MonoGame’s Game class provides a structure (Update and Draw with a timing mechanism), which is essentially what they had in ClassicUO’s own main loop.

Extensibility: Because it’s just C#, they can use any .NET library. If they want to embed, say, an HTML viewer for some UI, they could integrate a Chromium control (though doing that inside a MonoGame window is not trivial – but as an example of general .NET usage). They can also link the ClassicUO code as libraries. They might even consider if they can directly reference some of ClassicUO’s assemblies (if they modularized it) and only replace the subsystems needed for 3D. However, likely they’ll copy code into a new project and adapt it.

MonoGame itself is open source (MIT) and can be extended if needed (you could edit the framework or add new platform support). But that’s rarely needed. Extensibility mainly means the team has to implement additional features themselves – but that also means no constraints.

Scripting beyond C#: if they wanted, they could embed a scripting language for user macros or mods (like IronPython or MoonSharp for Lua). This is independent of MonoGame though – it’s a design choice that could apply to any engine. Given ClassicUO already has a macro system coded in C#, they might stick to that rather than expose a scripting API to players (which could be a feature but also a potential for cheating unless carefully sandboxed).

6. Cross-Platform Support: MonoGame is cross-platform. It supports Windows (DirectX), Windows (OpenGL), Linux (OpenGL), macOS (OpenGL or Metal via MoltenVK, not sure if fully supported by 2025 but likely OpenGL works), as well as Android and iOS via Xamarin/Mono (MonoGame has an Android backend and an iOS backend). Also, consoles like Xbox, PlayStation, Switch have been supported through MonoGame (some commercial titles use MonoGame on those, but that requires agreements with those platform holders). For the purposes of UO, console is not relevant, but mobile is somewhat relevant. ClassicUO’s FNA backend already allowed building for Linux and macOS which the team did. MonoGame would allow the same.

One thing: ClassicUO has a web client (via WebAssembly + SDL). MonoGame itself doesn’t have an official WebAssembly target as of now – however, there have been experiments (like using Blazor/WebAssembly to run MonoGame, or an external project called MonoGame Wasm). It is not mainstream. If a browser client is needed, MonoGame is not the easiest path. It would likely require compiling the game logic to WASM and using WebGL manually or using a community port of XNA to WebGL (there was an issue thread on MonoGame’s GitHub about WASM support, possibly targeting .NET 7/8 improvements). So, web support with MonoGame is at best experimental. However, since ClassicUO achieved a browser version via WebAssembly (likely by compiling the .NET runtime and their code with AoT ahead-of-time), it’s possible they could attempt similar with MonoGame (some have done FNA on WebAssembly by utilizing WebGL context via SDL). It’s complex, though, and not officially supported by MonoGame. If a web client is not a priority, then MonoGame covers all needed desktop platforms out of the box. For mobile, the team would have to adapt UI for touch (like on-screen keyboard or different input scheme), similar to what MobileUO did in Unity but now they’d do it at a lower level. MonoGame doesn’t have an engine UI system, so implementing mobile UI would be manual (or rely on things like Xamarin Forms in conjunction, which complicates the architecture).

MonoGame’s tooling for cross-platform is essentially compile for each target (maybe using Xamarin for mobile). It’s not as seamless as Unity’s one-click export, but it’s doable with some project setup. The ClassicUO team is used to multi-targeting .NET (they use .NET Core which is multi-plat, and they probably build separate binaries for each OS). They could do similarly with MonoGame – either multi-target their project for platform-specific bits or make use of .NET Core 6/7’s cross-platform nature and the fact MonoGame is a .NET library which will call the proper underlying APIs on each platform.

7. Community & Ecosystem: MonoGame’s community, while smaller than Unity/Godot, is composed of XNA veterans and indie developers. The framework is stable and well-documented through XNA’s legacy documentation (MonoGame aims to be functionally identical to XNA, so even Microsoft’s old XNA docs apply in most areas). There are forums and Discords for MonoGame, and a number of samples and open source games to reference. However, community-provided high-level features are limited. For example, if the team wanted a GUI library, there are a few (like Nez framework has some UI, or GeonBit.UI for MonoGame). They might consider using a library for UI rather than coding all GUI from scratch. GeonBit.UI is an example: it’s an open-source UI library for MonoGame with panels, buttons, etc., skinnable via textures. It could potentially speed up development of the interface. If# Comparative Evaluation of Game Engines for a 3D ClassicUO Client

Introduction

Ultima Online’s ClassicUO client is a 2D isometric game client with a rich legacy codebase in C#. Porting it to 3D opens up possibilities for modern visuals and new features, but choosing the right engine or framework is critical. We need to evaluate engines on multiple facets:

  • 3D rendering capabilities (especially for isometric or top-down views)
  • Asset pipeline (importing or converting UO’s legacy .mul/.uop asset formats)
  • Shader flexibility (to recreate UO’s look and add modern effects)
  • Performance & optimization tools (culling, batching, LOD, profiling, etc.)
  • Scripting language & extensibility (ability to reuse ClassicUO’s C# logic)
  • Cross-platform support (Windows, Linux, macOS, browser/Web, mobile)
  • Community & ecosystem (maturity of tools, docs, and community support)
  • MMORPG features (suitability for an online persistent world client, networking)
  • UI system integration (recreating UO’s complex interface in the new engine)
  • Learning curve (for developers familiar with ClassicUO’s architecture)

We will compare the following engines/frameworks:

  • Godot 4.4.1 – Open-source engine, recently enhanced 3D and C# support.
  • three.js – Popular WebGL JavaScript library for 3D in browsers.
  • LWJGL (Lightweight Java Game Library) – Low-level Java framework, offering OpenGL/Vulkan access.
  • Unity – Industry-standard engine with rich features and C# scripting.
  • MonoGame – Open-source XNA framework (C#), close to ClassicUO’s current tech.
  • Raylib – Simple C library for game programming, multi-language bindings.
  • PlayCanvas – Web-based 3D engine (JavaScript/TypeScript), strong in-browser support.
  • Panda3D – C++ engine with Python API, used historically for some MMOs.
  • Other Notables (Bevy, Ogre3D, Stride, Unreal Engine, etc.) – Briefly considered for context.

Each engine is analyzed in detail. Summary comparison tables are provided for a quick overview of strengths/weaknesses, and the report concludes with a shortlist of recommended engines for the ClassicUO 3D project.


Detailed Analysis of Engines

Godot 4.4.1

1. 3D Rendering & Isometric Support: Godot 4 has a modern Vulkan-based 3D renderer capable of high-quality graphics. Setting up an isometric view is straightforward – developers can use an orthogonal camera angle or tilt a perspective camera to 3/4 view. Godot supports both orthographic and perspective projections, making it easy to achieve the classic UO “angled top-down” look. Community examples show Godot handling isometric tilemaps and scenes. It also supports features like GPU particles, physical lighting, etc., though a UO client might opt for simpler visuals. Bottom Line: Godot can easily render a 3D world with an isometric camera, and it efficiently handles large 3D scenes with thousands of objects, especially with its built-in frustum culling.

2. Asset Pipeline Compatibility: Godot can import standard assets (textures, models) through its editor, but Ultima Online’s .mul/.uop formats are custom. Being open-source and flexible, Godot allows us to write custom importers or load assets via code. We can integrate ClassicUO’s C# asset-loading code directly into Godot (thanks to C# support) – for example, reading UO art files at runtime and creating Godot Textures/Materials from them. Godot’s C# API would let us feed pixel data into textures or generate meshes on the fly. Alternatively, we could write a one-time converter tool (in C# or GDScript) to turn UO assets into commonly used formats (PNG images for textures, OBJ/GLTF for models if any) and import those. Either approach is feasible. Bottom Line: While not natively aware of UO files, Godot’s extensibility and C# scripting let us reuse ClassicUO’s asset handling, making UO asset integration very achievable.

3. Shader System Flexibility: Godot uses a powerful shader language (similar to GLSL) and a visual Shader Editor. We can write custom shaders for unique UO effects – e.g., color hue shifts for clothing (a hallmark of UO’s customization) can be done by passing a hue parameter to a shader that tints the base texture. Godot supports shader features like normal mapping, specular, and even compute shaders if needed. For a UO client, simpler shaders (unlit or minimally lit materials for a classic feel) should suffice, but if we want enhancements (water shaders, lighting for day/night cycles, etc.), Godot can handle it. The shader system is considered user-friendly and highly flexible, with easy access to uniforms and built-in functionality (like billboard rendering for sprites). Bottom Line: Godot’s shader system is more than capable – it gives us the freedom to recreate UO’s 2D look or push beyond it, with relative ease of development.

4. Performance & Optimization: Godot 4 offers improved performance features:

  • Frustum culling: Objects outside the camera view aren’t rendered.
  • GPU instancing: Many identical objects (e.g., tree sprites) can be drawn with one draw call.
  • Multi-threading: Rendering and physics are threaded behind the scenes, and we can also offload work (like asset decoding) to threads.
  • LOD (Level of Detail): Godot supports LOD via multiple meshes per object, or we can implement our own logic to hide distant detail.
  • Profiling: Godot has a built-in profiler/monitor for frame time, draw calls, and memory usage.
    Godot’s 4.x releases focused on optimizing 3D rendering (addressing prior versions’ slowdowns in large scenes). For example, tests have shown Godot can handle thousands of static objects at good framerates if instanced properly. One relevant optimization: UO’s world is essentially a grid – we can combine static geometry (like terrain tiles or static objects) into larger chunks to reduce object count, something we have the control to do. Godot doesn’t automatically batch static meshes like Unity’s static batching, but using multi-meshes or manually combining meshes yields similar results. Bottom Line: With mindful practices, Godot can achieve high performance for an MMO-scale scene, and it provides the tools (culling, instancing, profiler) to optimize. Being open-source, if we hit a specific limitation, we even have the option to patch or extend the engine.

5. Scripting & Extensibility (C# Reuse): One of Godot 4’s standout features is C# support (.NET 6). We can write game logic in C#, which means we can port ClassicUO’s existing C# code with minimal changes. All the complex systems (skills, combat, macros, network protocol handling, etc.) can largely be reused, saving enormous development time. For instance, ClassicUO’s packet handling and game state classes can run in Godot’s C# environment essentially as-is, only now hooked up to Godot Nodes for visuals. In Godot, scenes are structured as Nodes (with attached scripts), but we can still maintain separate C# classes for core logic and use Godot Nodes for rendering and input – a clean separation. Extensibility: Godot is open source (MIT licensed), so if we need a custom engine feature (say, a special rendering technique or low-level optimization), we can implement it via modules or GDNative without waiting on a vendor. We can also integrate other .NET libraries (for example, if we want to use an existing pathfinding or physics library, or even a web library for an in-game browser). Godot’s Editor itself is extensible: we could create tools (in C# or GDScript) for developers, like a UO map viewer or item property inspector, within the Godot Editor to help development. Bottom Line: Godot allows unprecedented code reuse and flexibility for our C# project, combining the familiarity of our existing codebase with the benefits of a modern engine.

6. Cross-Platform Support: Godot supports Windows, Linux, macOS fully (both editor and export). It also supports Android and iOS exports, which means a Godot-based 3D UO client could run on mobile devices – important for reaching new audiences or matching the functionality of projects like MobileUO. Godot can export to the web (HTML5/WebAssembly) as well. Currently, C# in web exports is experimental (due to .NET in WebAssembly), but progress is being made using technologies like .NET NativeAOT for Web. By the time our project is mature, it’s likely that C# will export smoothly to WebAssembly, but even if not, we have options (like maintaining a GDScript build or using a web proxy for networking). Given ClassicUO already has a web client, we’d aim to preserve that – Godot could achieve it, potentially with some adjustments for using WebSocket (since WebAssembly can’t use raw TCP). On desktop, Godot uses Vulkan or OpenGL and can even fall back to GLES2 for older hardware. On macOS, it supports Metal via MoltenVK. Bottom Line: Godot is excellent for cross-platform: one codebase can target all desktops, mobile OS, and the browser, aligning with ClassicUO’s cross-platform ethos.

7. Community & Ecosystem: Godot’s community is thriving and rapidly growing. It has comprehensive documentation and an official class reference, plus countless tutorials, Q&A forums, and an active Discord. The engine is under active development with frequent improvements (4.x releases adding features and fixing bugs). For tooling, Godot provides an integrated editor (scene editor, script editor with debugger, asset manager). While not as extensive as Unity’s Asset Store, the Godot ecosystem has a community asset library (Godot Asset Library) with plugins and demos (e.g., there are add-ons for MMO-style chat, or inventory systems, that could provide inspiration or components). The open-source nature also means our project could benefit from contributions by engine developers if we stumble on engine issues; we’re not stuck with a black box. Maturity: Godot 4 is relatively new (released 2023), but by version 4.4.1 many initial kinks have been ironed out and it’s considered production-ready for 3D projects. Bottom Line: The Godot community is supportive and accessible, and being open-source, it fits well with ClassicUO’s community-driven development. We won’t lack for learning resources or help when needed.

8. MMORPG Client Suitability: Godot doesn’t have a built-in MMO networking solution (nor do most engines – MMO server tech is usually custom). However, we have ClassicUO’s networking code which we can integrate. Godot’s low-level networking (and .NET sockets) let us connect to UO servers with TCP, just as ClassicUO does. We might run the network listener in a separate thread or utilize Godot’s StreamPeerTCP with async checks. A persistent world client also needs to manage a lot of entities: Godot can handle dynamic creation/removal of objects as the player moves through the world. We can use a sector-based approach (only instantiate objects near the player, destroy ones that go out of range) – this logic is already in ClassicUO. Features like map streaming (loading terrain as you walk) can be implemented by reading UO’s map files and updating the scene accordingly. For instance, when the player crosses into a new map sector, our C# code loads that sector’s tiles and statics and creates Godot nodes for them. Godot is efficient at instantiating scenes, but we will profile to ensure no stutters – possibly pre-loading adjacent sectors. Client-side prediction or complex physics aren’t major concerns (UO is not physics-heavy), simplifying things. For persistent state (like skill values, journal logs), Godot doesn’t impose restrictions – we can save to files or use an embedded database if needed. Bottom Line: Godot is flexible enough to handle an MMO client’s unique demands. It provides the building blocks (network access, scene management, I/O) and leaves the MMO-specific logic to us, which we already have from ClassicUO. The engine won’t be a bottleneck in maintaining a continuous connection to a server and visualizing a large, ever-changing game world.

9. UI System Integration: Ultima Online’s interface (paperdolls, backpacks, health bars, gumps, etc.) is elaborate. Godot’s UI system (Control nodes) is very capable for recreating this:

  • We can create draggable windows by making Control nodes with drag behaviors (Godot supports draggable UI out of the box via Drag and Drop events).
  • Static UI textures (the graphic elements of UO gumps) can be used as StyleBox or TextureRect in Godot to skin windows exactly like UO.
  • Buttons, labels, checkboxes: Godot has those controls; we’d skin them with UO graphics (for example, the paperdoll’s “virtue” icons can be buttons).
  • Container gumps: UO’s container windows allow free-form item placement. In Godot, we can absolutely position child Controls to mirror the item positions (since Godot UI isn’t limited to grid layouts unless we choose). We’ll get events when an item icon Control is dragged, and we can handle dropping it on the game world or another container by calculating drop coordinates – similar to how ClassicUO’s UI logic works.
  • Text entry and fonts: Godot supports custom fonts (including importing bitmap fonts or using dynamic fonts with outline). We can bring in UO’s classic font or use a modern one. Text input for chat or naming pets can use Godot’s LineEdit or TextEdit controls, which handle focus and keyboard input (including IME for international text).
  • Overhead text and health bars: We have options, such as using ViewportTextures to draw text in the world, or simpler, using a canvas layer and projecting world coordinates to screen for each label. Godot’s World2D/World3D provides functions to project 3D points to screen coordinates, enabling custom placement of 2D UI elements in the game view (for example, the “floating names” above characters).
  • Scaling and responsiveness: Godot’s UI can be anchored and scaled, so we can support different resolutions easily (players could resize their game window and UI can adjust or we allow UI scaling preferences).

Godot’s UI uses a scene system too – we can create reusable scenes for UI elements (like one scene for the generic Gump window with title bar, close button, etc.), and instantiate it with different content for paperdoll vs. spellbook etc. ClassicUO’s UI code can inform the implementation (for logic like “when a window is dragged partially offscreen, nudge it back”). We essentially get to reimplement the UI with a higher-level toolkit, which might actually simplify some parts (Godot handling the dragging and clipping, for instance). Bottom Line: Godot’s UI system is powerful enough to handle UO’s complex interface. It will require effort to rebuild all the UI, but we have the assets and reference logic from ClassicUO. The end result can closely mimic (or even enhance) the original UI, and possibly be more flexible (UI scale, theming options for players, etc., are easier to provide in Godot’s framework).

10. Learning Curve & Onboarding: ClassicUO developers are versed in .NET/C# and have built a client largely from scratch. Shifting to Godot means learning:

  • Godot Editor and Scene/Nodal architecture – a change from purely code-driven design to using an editor for some aspects (though Godot allows dynamic creation entirely from code too).
  • Godot-specific API – names and ways to do things (e.g., Node.Process vs. ClassicUO’s game loop, signals instead of events in some cases, etc.).
  • Best practices in Godot – like how to avoid hiccups by using call_deferred or how to manage memory of Nodes.

Fortunately, using C# means the language and a lot of .NET library usage stays the same. Godot’s learning curve is generally considered moderate; many Unity or XNA developers pick it up quickly by following documentation and tutorials (the official docs have sections like “Godot for Unity developers” which could be analogous for us). We should anticipate a few months of ramp-up where developers experiment with small prototypes – e.g., load a map sector and display it, or implement one UI window – to get familiar. The Godot community and docs are very helpful in this regard.

One of Godot’s advantages is an intuitive editor – once learned, it can speed up development (e.g., visually placing UI, tweaking lighting). And since our project is open-source, having a lightweight engine like Godot (no heavy license or gigabytes of install) means contributors can get started easily: just download Godot (a 50 MB download), open the project, and hit play. There’s no barrier of proprietary tools or accounts.

Bottom Line: The team will need to learn Godot’s way of doing things, but C# support smooths that transition immensely. The learning overhead is justified by the long-term maintainability and improved workflows Godot provides. After initial familiarity, development should accelerate thanks to Godot’s integrated tools. And since Godot is free and open, any interested community developer can jump in and contribute without friction, which aligns with the project’s collaborative nature.


three.js (WebGL/JavaScript)

1. 3D Rendering & Isometric Support: three.js is a popular JavaScript library for 3D graphics in web browsers, built on WebGL. It’s capable of impressive visuals, including isometric views. Setting up an isometric camera in three.js is straightforward: you would use an OrthographicCamera (for true isometric projection) or a PerspectiveCamera with a tilt. There are code examples of isometric games using three.js (e.g., small isometric RPG demos). The library supports all fundamental 3D features: meshes, lighting, shadows, skeletal animation, etc. Running in a browser, it leverages the GPU via WebGL (and WebGPU support is emerging in three.js as well). Bottom Line: three.js can render the UO world in 3D in-browser, enabling an instantly accessible client via a webpage. It’s fully capable of isometric rendering and can handle moderately complex scenes, though a browser environment has more constraints than native.

2. Asset Pipeline Compatibility: three.js typically loads assets via web-friendly formats (like JSON, glTF, images). UO’s assets (in .mul files) are not directly usable. We’d need to pre-process or convert them to web formats. One approach: write a conversion script (maybe using Node.js or even C# offline) to unpack UO art:

  • Terrain and static art could be converted to PNG sprites or texture atlases.
  • If we incorporate 3D models (not present in classic UO assets, but if we create some), they’d likely be in a format like glTF.

Alternatively, we could host a server-side component that serves UO asset data via web requests to the client. However, that adds complexity and reliance on an internet host for assets. It might be easier to require the user to supply their UO asset files by drag-dropping them onto the page or using a file input, then use WASM (WebAssembly) to parse them in the browser (there are .NET runtime in WASM possibilities or we write a JS parser). In fact, someone on Hacker News mentioned “a heavily modified port of ClassicUO for WebAssembly” – that suggests a path to reuse ClassicUO’s C# code by compiling it to WASM. But if we embrace three.js, we’d likely rewrite asset loading in JavaScript or WASM.

Bottom Line: Getting UO assets into three.js is doable but requires a custom pipeline – either a pre-conversion to modern formats or an in-browser parse via WASM. This is more complex than engines with native C# support because we can’t directly reuse the asset code without compiling it to WASM or translating it to JS.

3. Shader System Flexibility: three.js provides a material system with many built-in shaders (Phong, Lambert, PBR, etc.) and also allows custom shaders via the ShaderMaterial or NodeMaterial system. We can write GLSL code for custom needs (like UO hue shifts or special effects). However, debugging shaders in a browser can be trickier (though Chrome’s dev tools and sourcing of shaders have improved). There’s also support for post-processing (effect passes like bloom, color grading). If we want UO’s classic look, we might not need heavy shaders – just textured surfaces for terrain and sprites – but if we want modern touches (e.g., dynamic lighting for day/night or spells), three.js can handle it with custom or built-in materials. Bottom Line: three.js’s shader support is robust; anything WebGL can do, we can do in three.js. It’s flexible but requires knowledge of WebGL/GLSL. The engine won’t limit shader complexity beyond what the user’s GPU and WebGL spec allow.

4. Performance & Optimization: Running in a browser, we face performance limits (no multi-threaded rendering like native engines, and JS execution can be slower than C++/C#). But three.js is highly optimized in JavaScript and WebGL:

  • It does frustum culling by default on objects.
  • It supports instanced meshes (drawing many of the same object with one call).
  • LOD can be managed (there is a LOD object to switch detail based on distance).
  • Being WebGL, it relies on the GPU; draw calls should be minimized similarly to other engines.
  • Browsers now support WebAssembly threads with SharedArrayBuffer (if we compile some parts to WASM, potentially networking or asset loading could happen on separate threads).

Large scenes: Ultima Online’s visible area is not huge (~18×18 tile area typically, plus some radius), which is fine. But if we were to show the entire screen full of the world, we’d need to ensure efficient culling beyond that range. The game logic would control that anyway (we won’t create objects server hasn’t sent). three.js has a scene graph that, if naively loaded with too many objects, could slow down updates and culling. We’d need to use techniques like merging static geometry, or using instanced geometry for repeated tiles, to keep draw calls low (three.js can draw thousands of instanced meshes at good frame rates, especially with WebGL2). Another factor is texture memory – loading all UO art might be heavy for the browser, but we can load on demand (like load textures for nearby objects only).

Profiling: We’d use browser profilers and three.js’s own stats (Stats.js can show FPS, etc.) for performance tuning. And because it’s all on the web, iterative optimization is quick (just refresh the page to test changes).

Bottom Line: A three.js client can be optimized to run well on modern browsers, but we have to be careful with JavaScript’s single-threaded nature for game logic and rendering. UO’s game logic isn’t extremely CPU-intensive by today’s standards, so it might be fine. Many WebGL games run complex scenes – it’s feasible, but likely won’t match the raw performance of a compiled native engine. For moderate scenes (100s of objects), three.js can easily hit 60 FPS on a desktop; on mobile browsers it might be lower due to weaker CPUs/GPUs.

5. Scripting & Extensibility (C# logic reuse): three.js uses JavaScript or TypeScript for scripting. This means ClassicUO’s C# code cannot be used directly. We would have to rewrite the client logic in JS/TS, or attempt to compile it to WebAssembly and interface with it. Rewriting a complex codebase like ClassicUO (networking, combat logic, UI logic) in JS is a significant endeavor, prone to new bugs. On the other hand, using WebAssembly (like porting ClassicUO’s C# via Blazor or .NET WASM) could allow reuse – this is essentially what one user hinted at (a heavily modified ClassicUO for WASM). But mixing that with three.js would be complex (we’d have a WASM module doing game logic and JS doing rendering; they’d need to communicate via calls or shared memory buffers).

Extensibility: On the web, we have access to the vast world of JavaScript libraries. We could incorporate things like React for UI (or directly manipulate HTML/CSS for interface), use existing JS networking libs, etc. The modding story in JS might be simpler (people could open dev tools and script, though that’s double-edged for cheating). Also, deploying updates is trivial – just update the hosted JS.

However, the development team will need strong JS/TS skills. If they mostly know C#, there’s a ramp-up to become equally proficient in JS. TypeScript can ease that (giving static typing, classes, etc., similar to C# style).

Bottom Line: Code reuse is a big issue with three.js. It essentially implies a full rewrite of ClassicUO’s client code in a new language (or an untested WASM integration). This risk and effort are significant. While three.js itself is just a rendering library (we can implement any custom logic around it), the lack of C# means we lose the direct leverage of the existing project.

6. Cross-Platform Support: The big selling point of three.js is immediate cross-platform via the browser. Any device with a web browser and WebGL support can run the client – Windows, Mac, Linux, smartphones, tablets, even some smart TVs. We wouldn’t need to package separate builds; we host it and users connect. It also lowers the barrier for new players (no installation, just “click and play”). However:

  • Desktop browsers vary: Chrome, Firefox, Edge all support WebGL well. Safari on Mac/iOS has historically been finicky with WebGL but has improved; WebGPU is coming which three.js is preparing for.
  • Mobile browsers: they can run three.js, but performance might be limited. Also, iOS Safari sometimes has memory limits on WebGL contexts.
  • If we want a standalone (say on Steam or similar), we could wrap it with Electron or a WebView, but that’s basically shipping a browser anyway.

Cross-platform in terms of input: three.js can handle mouse, keyboard easily (through DOM events). It can handle touch events for mobile, and even gamepads via the Gamepad API. So input flexibility is there.

Offline vs Online: If it’s purely browser-based, it requires the user to have the UO game files. We might handle this by asking for a file upload or pointing to their installed game path via some local file access (browsers have limited direct filesystem access for security). Alternatively, we could host the art assets if legally permissible (likely not, since UO assets are proprietary), or have a user provide them in a one-time process that stores them in browser storage. These are surmountable but important details.

Bottom Line: three.js yields superb platform reach (anything with a browser) with zero install, aligning with modern software distribution. It’s ideal for accessibility but might face slightly less optimal performance on weaker devices and requires careful handling of asset distribution due to the proprietary nature of UO files.

7. Community & Ecosystem: three.js has a large community of web developers and extensive documentation and examples. There are many tutorials (ranging from basics to advanced techniques like shader usage or performance tuning). The project is very active on GitHub, with frequent updates. For an open-source UO client, using three.js might attract web developers from the community who aren’t into typical game engine development but know JS well. On the other hand, some of the ClassicUO dev community with mainly C# experience might not be comfortable jumping into JS.

As for tooling: development can be done in any code editor (VS Code with the excellent TypeScript support, etc.). The browser’s dev tools serve as our “engine inspector” – you can inspect memory, performance, even live-edit shaders or code. While that’s powerful, it’s not as integrated as, say, Unity’s editor (no scene view unless we code one in the app, for instance). But one could create debug overlays or an in-browser UI for developer commands.

There are also add-on libraries: e.g., three.js libraries for GUI (dat.GUI) for debugging UI, or physics libraries (Cannon.js, etc.) if needed. The web stack is huge, so leveraging existing solutions (for instance, using a web-based UI library for the game UI instead of writing one from scratch in WebGL) is possible – e.g., overlay HTML/CSS UI on top of the WebGL canvas for chat windows or inventory (some hybrid approach could simplify UI development significantly by using HTML elements for UI and only using WebGL for the game world).

Bottom Line: The three.js/web community is strong, and many resources exist. It’s a different ecosystem (web dev vs game dev), so we must be prepared to navigate that. The open-source nature is a plus (the whole stack is open standards).

8. MMORPG Client Suitability: With a browser-based client, networking is a bit different:

  • Browsers cannot open arbitrary TCP sockets; they use WebSockets (or WebRTC) for server communication unless we rely on an extension. Thus, connecting to a UO server (which expects a TCP connection on a port with UO protocol) requires either:

    • A WebSocket proxy server that speaks WebSocket to the client and TCP to the UO server (there are UO gateway projects that do this).
    • Or running the client logic in WASM where it could theoretically use TCP if browser allows (currently not allowed due to security).
      Given that some have done UO in WebAssembly, they likely used a WebSocket proxy approach (converted the UO server protocol to WebSocket, similar to how Ultima Online Forever’s web client works).

    So, we’d need to include a networking solution: either instruct shard operators to provide a WebSocket endpoint, or run a community proxy server (with potential latency added). Networking in JS itself is fine (via WebSocket APIs, which are event-driven and efficient).

Other MMO aspects:

  • Persistent connection: Browsers can maintain WebSockets as long as the page is open. Need to handle disconnect/reconnect gracefully in UI.
  • Large world & memory: The browser can store data in memory and also persistent storage (IndexedDB, etc.) if we want to cache assets. We must be mindful of memory usage to avoid the browser tab crashing, but tens to a couple hundred MB is usually fine.
  • Security: A browser client is harder to secure from cheaters because JS can be inspected/modded by the user. But since ClassicUO is open source, security through obscurity isn’t a goal anyway. We’d rely on server-side anti-cheat as usual.

Bottom Line: It is possible to have a fully functional MMO client in the browser. The main adjustments are network communication via WebSockets and dealing with the stateless nature of web pages (ensuring smooth reconnection if needed, etc.). Several HTML5 MMOs exist, proving the viability. It adds some architecture overhead (proxy or server changes), but yields a highly accessible client.

9. UI System Integration: We have two main paths for UI in a three.js context:

  • In-canvas UI using three.js: e.g., drawing 2D elements via sprites or using a library like three-mesh-ui (which can render text in 3D). This is quite low-level for complex UI like UO’s, and reinventing draggable windows in WebGL might be time-consuming.

  • HTML/CSS overlay: This is a strength of web apps – we can simply create HTML elements for UI windows. For example, the paperdoll could be an HTML <div> with an <img> background and absolutely positioned <img> for each equipment slot, etc. Drag-and-drop between HTML elements and the WebGL canvas can be coordinated (e.g., catch a dragstart on an item icon in HTML, and on drop, if it’s over the canvas, send a command to three.js to drop the item in the world). Similarly, container gumps could be HTML elements with CSS for border styling and scrollbars for scrollable content.

    • We could style these with CSS to mimic UO graphics by using the UO images in <img> tags or CSS backgrounds.
    • Using HTML/CSS would likely accelerate UI development enormously because we can leverage existing behaviors (windows that can be moved, text entry fields, etc.) and simply apply styles.
    • However, keeping UI state in sync with game state would be an extra layer to manage (but that’s also true in any engine).
    • There’s also the possibility of using a UI framework like React or Vue for modularity, but vanilla HTML/JS might suffice given UO’s UI is not dynamic by modern app standards (mostly static panels).

    Web UI also naturally handles text (for chat), form input, etc., and can be easily made responsive to different screen sizes (via CSS media queries or flexible layouts).

    The main challenge is handling the game viewport: we have a WebGL canvas for the 3D world and HTML elements for UI on top. Need to ensure clicks are dispatched correctly (e.g., click on canvas vs click on UI). That’s solvable by stopping event propagation appropriately.

Bottom Line: A web approach likely gives the fastest path to implementing UI because we can leverage HTML/CSS. It might even allow copying some ClassicUO UI code logic into JS fairly directly, because UI event handling (drag, drop, click) can be similar. The aesthetic can closely match or even use the exact UO bitmaps. This hybrid approach (three.js for world, HTML for UI) can yield a very usable interface.

10. Learning Curve & Onboarding: If the team’s background is mostly C#, moving to JavaScript/TypeScript is a learning curve. TypeScript eases some pain by adding static types (we can define classes for GameObject, Mobile, etc., similar to C# classes). The asynchronous nature of JS (callbacks, promises) for things like network or file operations is another paradigm to get used to (though C# has async/await which is conceptually similar to JS promises).

Onboarding contributors might actually broaden – web developers who might not want to dive into Unity or Godot could easily join a web project. Conversely, some of our current contributors might drop off if they don’t want to work in JS.

There is also the matter of tooling differences: debugging in a browser vs. an IDE. Chrome DevTools is very powerful, but some prefer an IDE with breakpoints; fortunately VS Code can debug TypeScript in Chrome via extensions.

A big plus: no special software needed beyond a text editor and a browser – very low barrier for contributors. And the iteration cycle is quick (edit code, refresh browser). Automated testing could even be done via headless browser if needed.

Bottom Line: A three.js/TS codebase requires a different skill set. It’s a trade-off: we lose immediate familiarity but gain the web’s ease of access. If the team is willing to invest in learning or if new contributors join with web expertise, the project could thrive. But the risk of a full rewrite in a new language is non-trivial – it could slow development initially as bugs are ironed out and features reimplemented.


LWJGL (Lightweight Java Game Library)

1. 3D Rendering & Isometric Support: LWJGL is a low-level framework exposing OpenGL, Vulkan, and OpenAL to Java. It’s not a game engine but a collection of bindings. Using LWJGL for a 3D client means building virtually everything ourselves (like using OpenGL calls to render geometry). It can certainly render 3D scenes and an isometric perspective is purely how we set up the camera matrices in OpenGL/Vulkan. But unlike Unity or Godot, LWJGL gives us no scene management or rendering pipeline – we’d write shaders, manage vertex buffers, and so forth directly. We could choose to use a higher-level library on top of LWJGL (for example, jMonkeyEngine or libGDX use LWJGL under the hood, providing an engine on top; BDX as listed is LibGDX/Blender integrated, but let’s assume using LWJGL directly or with minimal framework).

If we opt for pure LWJGL:

  • We must handle window creation and input (LWJGL provides that but raw).
  • We must load assets (likely via custom code or additional libraries for image decoding).
  • We must implement culling, transformations, etc.

Bottom Line: LWJGL itself is powerful (it’s basically as powerful as OpenGL or Vulkan gets) but requires writing our own engine. We can achieve anything graphically, but at the cost of coding everything, including isometric view logic, from scratch. Given the scope, LWJGL might be too low-level unless paired with a higher-level library.

2. Asset Pipeline Compatibility: Java can read files easily, and we could port ClassicUO’s asset reading code to Java (rewriting it, since the languages differ). Alternatively, perhaps use JNI (Java Native Interface) to call the existing C# or use .NET in Java (not straightforward). Another path: use JVM languages like Kotlin or Scala for some conciseness, but that doesn’t solve code reuse. If using something like jMonkeyEngine (a Java engine on LWJGL), it might have built-in image and model loaders, but not .mul support obviously.

We would likely write a Java loader for .mul and .uop files (maybe referencing their format documentation or using ClassicUO code as a guide). That is a similar effort to what ClassicUO originally did (which took time and careful reverse engineering). The plus is Java’s standard library can handle things like bit-level file IO, and we can include libraries (there might be a library for bitmap handling or DDS if needed). If we chose LibGDX or jMonkeyEngine:

  • LibGDX is a popular Java game framework that has 3D capabilities (though more known for 2D). It uses LWJGL on desktop. It has a texture and model pipeline, but again we’d have to feed it UO assets.
  • jMonkeyEngine (JME) is a full 3D engine in Java (scene graph, etc.), built on LWJGL. It might be more comparable to Unity/Godot in features, minus the editor tooling. It can import models, has a GUI system (NiftyGUI), etc. If considering Java, JME is likely a better starting point than raw LWJGL. JME’s community is smaller but it’s open-source and has an active core.

However, the question specifically lists LWJGL, suggesting evaluating it as a raw option. In raw form, it’s akin to writing in C++ with OpenGL but in Java – extremely flexible, but all workload on us.

Bottom Line: Asset integration would be custom-built. We have to weigh the effort of rewriting asset loaders in Java vs. the benefit. It’s less ideal than engines where we can reuse existing code or tools.

3. Shader System Flexibility: With LWJGL, we directly use GLSL (for OpenGL) or SPIR-V (for Vulkan). That’s total flexibility – we can write any shader we want, but with no hand-holding. Debugging shaders might rely on external tools (like RenderDoc or IDE plugins). If using JME, it has a material system where you define shaders and parameters in material definition files. JME and LibGDX also support shader injection if we go that route. But in pure LWJGL, it’s as flexible as it gets because we manage the GPU pipeline manually.

Bottom Line: LWJGL offers maximum shader flexibility (just like any custom engine), at the cost of needing in-house expertise in shader programming. Achieving specific UO effects or new graphical features is possible, but nothing is pre-made – we must implement all from scratch.

4. Performance & Optimization:

  • LWJGL (OpenGL/Vulkan) gives us direct access to performance features. We can implement efficient culling (in CPU or GPU), use instanced drawing, and optimize to the metal.
  • There’s no built-in profiler beyond what we integrate (we can use Java profilers or OpenGL debuggers).
  • Java’s performance is generally fine for high-level logic, and LWJGL is essentially a thin Java wrapper to native calls, so rendering performance is mostly bound by GPU and native drivers.
  • We need to manage things like our own spatial partitioning if we want to cull large numbers of objects quickly (e.g., a quadtree for world).
  • We also might leverage GPU for tilemap rendering (like one could do a single shader drawing all terrain in one draw if clever, etc. – but that’s advanced).

One concern is Java garbage collection in a real-time environment – but modern JVMs are quite good, and careful coding (reusing objects, using primitive collections) can mitigate hitches. Also, LWJGL encourages using off-heap memory for vertex buffers which the GC doesn’t touch frequently.

Bottom Line: LWJGL potentially allows very high performance if we have the expertise to implement everything optimally. But it’s also easier to make performance mistakes (inefficient loops in Java or too many draw calls) because we don’t have an engine automatically batching or culling for us. It gives the ultimate control, which is a double-edged sword.

5. Scripting & Extensibility (C# logic reuse): Using LWJGL means writing the client in Java (or Kotlin, etc.). We cannot directly reuse C# code. We’d have to rewrite ClassicUO’s logic in Java or find a way to bridge .NET and Java (there are some obscure bridges but likely not worth it). This means rewriting networking code, game mechanics, etc. – effectively a ground-up reimplementation, akin to the original ClassicUO effort but in Java.

Extensibility-wise, Java has a huge ecosystem. We could integrate existing libs for UI (JavaFX or Swing for some overlay? though mixing with OpenGL is tricky), or use scripting languages on JVM (like Jython for modding, etc.). But those add complexity.

One might argue ClassicUO’s C# logic could be preserved by running a .NET runtime and syncing with Java, but that’s quite complex (embedding CoreCLR in a Java app is not common). So assume a rewrite.

Given the magnitude of ClassicUO’s feature set, a rewrite in a different language is an enormous task likely taking man-years. It would risk replicating bugs or losing subtle behaviors.

Bottom Line: LWJGL means very low code reuse. We’d essentially be implementing ClassicUO “from scratch” in Java, which is a huge disadvantage compared to engines that support C#.

6. Cross-Platform Support: Java is inherently cross-platform. LWJGL-based apps can run on Windows, Linux, macOS easily (JVM on each plus native bindings provided by LWJGL). Java on mobile is tricky: LWJGL doesn’t run on Android or iOS (different graphics API, and LWJGL is more desktop-focused). LibGDX would allow cross-platform including Android/iOS via different backends (it actually can cross-compile to iOS with RoboVM or similar, and to Web with WebGL via TeaVM or GWT). If we went with LibGDX or JME:

  • LibGDX can target desktop, Android, iOS (and web via WebGL with some limitations). It uses LWJGL on desktop, and its own thing on mobile.
  • JMonkeyEngine has Android support, but iOS support is less clear (maybe via Avian VM or RoboVM in past).

However, if focusing on LWJGL, likely it’s a desktop client only (Windows/Linux/macOS). That covers the majority of current ClassicUO users. The browser platform would be lost (ClassicUO’s web version wouldn’t carry over unless we did something separate). Mobile would require a different client (though possibly a Java client on Android could share some code, not iOS though).

Bottom Line: Desktop cross-platform is excellent (just need Java or bundle a JRE). Mobile would require extra effort or different tech. Browser is out unless we cross-compile via something like TeaVM (which has been used to make LibGDX games run in web, but performance and complexity might not be ideal). Given our user base, desktop is primary, but losing web (and maybe mobile) would be a drawback.

7. Community & Ecosystem: LWJGL is a low-level tool with a smaller direct community (mostly devs using it via higher-level engines). If we consider LibGDX or JME:

  • LibGDX has a sizable community, lots of tutorials (mostly 2D focus, but some 3D usage). It’s a proven framework for indie games. It has modules for graphics, audio, input, and some basic 3D scene stuff. It lacks an editor (level design often done via code or Tiled for 2D).
  • JMonkeyEngine is a full 3D engine with a community and documentation; it has an SDK (NetBeans-based) which is somewhat maintained. It might align better for a 3D game like UO. They have plugins (like networking, AI, etc.), though we might not use those since we have our systems.

The Java game dev community is smaller than Unity or Godot’s, but it exists and is passionate. However, contributions to an open-source UO client might be less from existing ClassicUO community (who are likely .NET folks) and more from new Java enthusiasts if any.

The tooling (if not using an engine like JME) would be basic – a Java IDE (IntelliJ, Eclipse) and debugging tools. We’d be writing a lot of code without an editor GUI to visualize scenes or UI.

Bottom Line: Without a higher-level framework, we lack productive tools. Even with something like JME, the ecosystem is less mainstream. It could be done, but developer productivity and external contributions might suffer compared to using a well-documented, accessible engine like Godot or Unity.

8. MMORPG Client Suitability:

  • Networking: Java can handle sockets easily with its standard library. UO’s protocol can be reimplemented (or we find a Java UO client open-source? There was an old project called “Iris2” but that was C++ Ogre3D, not Java). We’d implement encryption, packets, etc., similar to how ClassicUO did but in Java.
  • Large world management: With raw OpenGL (or even JME), we have to manage dynamic loading/unloading of regions. That’s a coding task but straightforward conceptually. JME for instance has a scene graph where attaching/detaching nodes for different zones is possible.
  • Stability: The Java VM is quite stable for long-running applications, and a properly coded client would run indefinitely connected to a server. We’d have to ensure no memory leaks (loitering references can cause memory to build up until GC maybe eventually collects it).
  • Client customization: If a goal is to allow community mods/plugins, a Java client could possibly load external JARs for mods. But ClassicUO’s approach has been to integrate features directly rather than official plugin support (besides Razor).
  • Performance on large crowds: There may be concern if a huge battle with 100s of players appears, Java might have some CPU overhead vs C++, but likely the GPU is the limiting factor, not language, for drawing so many figures.

Bottom Line: A Java/LWJGL client can meet MMO requirements, but everything has to be built up with no pre-made MMO features. It’s basically writing an MMO client from scratch (which is exactly what ClassicUO did in C#, so it’s doable, but doing it again in another language is redundant work unless Java offers a unique advantage, which it doesn’t strongly here).

9. UI System Integration: Java offers a few paths for UI:

  • ImGui (Immediate Mode GUI) has Java bindings (LWJGL includes Nuklear and Dear ImGui bindings). We could overlay an ImGui interface for debugging, but styling it to look like UO would be hard. For the actual game UI, ImGui’s style might not match the aesthetic.
  • Custom UI in OpenGL: We could render UI by drawing textured quads for UI elements (like ClassicUO draws gumps via sprites). This essentially replicates ClassicUO’s UI rendering in OpenGL – doable but time consuming. We’d need to handle window dragging, resizing, etc., via our own code (again).
  • Use Java GUI frameworks: Swing or JavaFX – but mixing those with an OpenGL game loop is problematic (could consider drawing Swing components to images and texturing them in OpenGL, which is hacky). Not recommended for a high performance game context.
  • JME’s GUI: JMonkeyEngine has Nifty GUI (XML-based, somewhat outdated) and newer community GUI frameworks. They could be used to construct UO-like windows with relative ease if one is comfortable with them.
  • LibGDX GUI: LibGDX has Scene2D UI, a reasonably good 2D GUI library with layout containers, buttons, etc. We could definitely use that if on LibGDX, skinning it with UO textures. Scene2D can handle dragging windows, lists, text input, etc. This might be a sweet spot if doing Java – use LibGDX’s UI for interface, and either LibGDX or LWJGL directly for 3D world (LibGDX can integrate with its own UI easily since it handles the OpenGL context).

So, if we went raw LWJGL with no libraries, we’d have to create UI from scratch, which is a large undertaking (basically porting the UI code as well, which exists in ClassicUO but in C#). If we use a Java game framework (LibGDX or JME), we get some UI tools that could be repurposed. It’s still a significant project to implement the unique aspects of UO’s UI (the freeform container placement, the grid legacy container art, etc.), but at least those frameworks provide the base (drag & drop events, text boxes, etc.).

Bottom Line: Without an engine, UI development in Java/LWJGL = lots of custom code. With a supporting library, it’s a bit easier, but still a lot of custom skinning and logic. ClassicUO’s UI knowledge can guide the implementation, but not directly port code. This is again a reinvention of already-solved problems.

10. Learning Curve & Onboarding:
The ClassicUO team would have to move from C# to Java. The languages are similar (curly braces, OOP), but differences in memory management (though both GC, Java lacks structs, has different library patterns) and lack of LINQ, etc., mean adapting code. They also need to learn the chosen Java framework (OpenGL calls or JME’s API, etc.). This is significant, but possibly less so than moving to JavaScript, since Java at least is closer to C# in syntax and static typing. However, they’d lose the familiarity of .NET libraries (like certain collections, tasks) and have to find Java equivalents.

From a community perspective, current ClassicUO devs who are comfortable in .NET might not be enthusiastic about switching to Java. Conversely, maybe some contributors out there might prefer Java – but the existing ClassicUO is already well established, so switching languages could risk fragmenting interest.

Tooling: Java IDEs are solid (IntelliJ IDEA is excellent). Debugging is easy (set breakpoints, inspect variables, etc.). They’d miss .NET’s tools but gain Java’s, which are comparable.

Project maintenance: Introducing Java in a historically .NET-centric UO community might complicate build processes (currently ClassicUO builds with .NET CLI; a Java client would need JDK installed, etc., but that’s not a big deal).

Bottom Line: The learning curve is high, effectively building a new client from the ground up. It’s arguably higher risk and slower than using an engine that aligns with our existing code (Unity/Godot/MonoGame). Unless there’s a compelling reason to choose Java (e.g., to avoid .NET or to leverage some Java-specific tech), it seems not the most efficient path.


Unity

1. 3D Rendering & Isometric Support: Unity is a mature engine capable of rendering cutting-edge 3D. Isometric and top-down views are well-supported: many Unity games use orthographic cameras for 2.5D or isometric gameplay. We can set an orthographic camera rotation to 45° to get a true isometric projection or use perspective with a slight tilt for a modern touch. Unity excels at both fully 3D worlds and 2D overlay on 3D (which suits UO’s needs). Unity’s rendering pipelines (Built-in, URP, or HDRP) can be selected based on desired graphics vs. performance. Likely, the Universal Render Pipeline (URP) or even the simpler Built-in pipeline is sufficient for UO-level graphics (keeping it lightweight). Unity can handle large scenes, dynamic lighting if we want day/night cycles, and special effects for spells, etc., using its particle system and shader capabilities. Bottom Line: Unity’s renderer is more than up to the task; we’ll have no trouble achieving an isometric 3D look and can scale visuals from a classic feel to more modern effects as desired.

2. Asset Pipeline Compatibility: Unity has a robust asset pipeline but no built-in support for .mul/.uop. However, we have precedent: MobileUO is a Unity project that integrated ClassicUO’s asset loading directly (Check Out “MobileUO”, a Unity-Based Mobile Client for Ultima Online – The Ultima Codex). They effectively embedded the ClassicUO C# code to load UO art in Unity at runtime. This means we could reuse that approach: include the ClassicUO file reading code in the Unity project, and at startup, load the necessary data (map files, animations, etc.) into Unity textures and meshes. Unity allows creating textures from byte arrays and meshes from custom vertex data, so dynamically loading assets is fine. Additionally, Unity editor scripting could be used to create importers (if we ever want to import UO assets into the Unity Editor for design-time usage, we could write an EditorWindow to load .mul files and generate Prefabs, etc.). But dynamic loading might suffice to keep it simple (no conversion needed, just directly use the existing data format). Bottom Line: Unity’s flexibility with C# means we can integrate UO’s asset pipeline by reusing ClassicUO code. This was proven feasible by the MobileUO project, demonstrating Unity can indeed drive UO content (Check Out “MobileUO”, a Unity-Based Mobile Client for Ultima Online – The Ultima Codex).

3. Shader System Flexibility: Unity’s shader system is one of its strengths. We can write custom HLSL shaders or use Unity’s Shader Graph (a node-based shader editor). If we want to replicate the original 2D look, we might just use unlit shaders (which are simple). If we want fancier visuals (like normal-mapped terrain for subtle depth, or reflective water surfaces), Unity can do that too. For UO, one specific need is hueing (recoloring textures on the fly for dyed clothing, etc.). In Unity, that can be done by using a shader parameter to shift hue or using palette lookup. We can implement a hue shader that takes an index or color and tints the base texture accordingly, very doable in HLSL. Unity’s Standard Shader might be overkill; a custom simplified shader could be more efficient for a largely unlit environment. Also, Unity supports multi-pass shaders, render queue control, etc., which can help draw things like paperdoll overlays in correct order. Bottom Line: Unity offers extensive shader flexibility; any visual effect we need can be achieved, and we have the tools (Shader Graph, extensive documentation) to implement custom shaders for unique UO requirements.

4. Performance & Optimization: Unity has many built-in optimization aids:

  • Frustum culling and occlusion culling (with precomputed data for static objects).
  • Level of Detail (LOD) systems for 3D models.
  • Batching: Static and dynamic batching to reduce draw calls when possible.
  • GPU Instancing for drawing many of the same mesh with one call.
  • Profiling tools: Unity Profiler for CPU/GPU, memory profiler, etc., to identify bottlenecks.

Given UO’s map is large but only a portion is rendered at once, we can utilize these features. For instance, we can mark environment objects as static so Unity can batch them, or we can manually combine tiles into chunks. Unity’s terrain system might even be used if we convert UO maps to a heightmap (though UO uses static tiles rather than a continuous heightmap, so maybe not directly). We likely will implement our own world streaming: as the character moves, instantiate new chunks of the world and destroy old ones. Unity can handle dynamic object spawning well, especially if we pool objects (e.g., reuse a pool of generic “mobile” objects rather than constant create/destroy). MobileUO (Unity) runs on mobile at decent performance, indicating Unity can handle UO’s scope on even limited hardware.

Unity’s overhead: It’s a heavier engine than say MonoGame, but hardware nowadays can handle it. We have fine control over quality settings to ensure even modest PCs can run it (disable expensive lighting, shadows, etc.). Also, Unity’s new DOTS/ECS could be considered for managing large numbers of entities more performantly, but that might complicate using our existing code. Likely not needed unless we profile and find thousands of objects to be an issue – which in an MMO client, typically the object count is moderate (maybe a few hundred in range at a time, which Unity can handle with classic GameObjects).

Bottom Line: Unity provides strong performance tools and has been used for games far more demanding than a 3D UO client. With mindful use of its features (culling, batching, pooling), we can achieve smooth performance. And we have great diagnostics via Unity’s profiler to help ensure that.

5. Scripting & Extensibility (C# Reuse): Unity uses C# for scripting, which is a massive boon. We can integrate a huge portion of ClassicUO’s code directly. In fact, the MobileUO project embedded ClassicUO’s logic into Unity (Check Out “MobileUO”, a Unity-Based Mobile Client for Ultima Online – The Ultima Codex), which means much of the heavy lifting (packet processing, game rules, etc.) can just run inside the Unity game loop. We would adapt it by replacing parts (like rendering calls, which go to Unity’s rendering, and input handling to Unity’s input API) but leave core logic intact.

We might restructure some code to better fit Unity’s component model (for example, attach scripts to GameObjects for interactive entities), but we can still have central managers (Unity doesn’t forbid singletons or static managers, although a pure ECS would prefer you not). ClassicUO’s update loop can be driven by a Unity MonoBehaviour’s Update function calling into the existing game systems.

Extensibility: Unity’s ecosystem allows integration of many libraries (e.g., we could use an existing pathfinding library from the Asset Store if needed, or use Cinemachine for fancy camera control, etc.). We can also use native plugins if needed (like calling OS functions or existing C++ libraries). And since it’s C#, using .NET libraries is trivial (for example, if we want a particular math or AI library, just add the DLL). Unity’s editor extensibility means we could create custom tools for editing the world or debugging packets in a friendly way.

The flip side: Unity is not open-source (though large parts like the engine code are accessible in C++ via reference, you can’t modify it freely). But it’s so feature rich that modifying engine source is rarely needed. And the C# side (Mono runtime) is flexible.

Bottom Line: Unity likely offers the highest code reuse of all options. We can avoid reinventing logic and focus on integration and enhancements. This significantly reduces development time and risk, since we know the existing code works (for UO’s rules/protocol). The team remains in their comfort zone (C#/.NET).

6. Cross-Platform Support: Unity is legendary for cross-platform: Windows, macOS, Linux standalone builds are standard. Mobile (iOS/Android) is well-supported – MobileUO leveraged that to run on phones. WebGL export works, albeit with constraints (no threads, need WebSocket for network). So Unity matches ClassicUO’s current platform range and extends it (Unity also can do consoles, but that’s out of scope for UO likely).

One caution: Unity WebGL doesn’t allow direct TCP sockets, so like the three.js case, a WebSocket proxy would be needed for a web client. But since ClassicUO has a separate WASM build for web, maybe maintaining that separate or transitioning it is okay. We might not prioritize a web build if we have a good standalone client, but it’s nice that it’s possible.

Deployment: We can distribute the Unity client as a typical game download (like other UO freeshards do with their custom clients), or possibly as an App for mobile via app stores (with caution on IP rights, etc.).

Bottom Line: Unity ensures we won’t leave any user platform behind: a single codebase can yield clients for PC, Mac, Linux, Android, iOS, and even Web. This fits the UO community which is diverse in platform use, and it future-proofs us (e.g., if VR or console was ever a weird consideration, Unity could do that too with the same content).

7. Community & Ecosystem: Unity’s community is massive and extremely mature. There are countless tutorials, StackOverflow answers, YouTube guides, etc., for anything we encounter. The Asset Store contains many tools that could help (some free, some paid – we’d likely stick to free or our own due to open-source nature). For example, there might be free inventory UI frameworks or shaders for certain effects that we can use or learn from.

Unity’s editor tooling is top-notch: visual scene editor, animation timeline (could animate 3D models for UO creatures if we go that route), particle editor (for magic effects), etc. These will increase our productivity and allow non-programmers (or less technical contributors) to assist, e.g., an artist could tweak lighting or layout some scene.

Support & Licensing: Unity’s engine is free for our likely scale (even if ClassicUO had donations, it’s far below Unity’s revenue cap; and even then, there’s Unity Plus/Pro if needed). In late 2023, Unity had some controversy over runtime fees, but by 2025 they revised terms to largely spare open-source and non-monetized projects. We should double-check license compliance: likely we can’t open source Unity’s parts (which we wouldn’t, just our code and project files), but it’s standard to use Unity for open-source projects (there are many MIT-licensed Unity game codebases, the user just needs Unity to open it or use distributed binaries).

Community familiarity: Many developers have Unity experience, so onboarding someone who knows Unity but not ClassicUO will be easier than someone who knows neither Unity nor the ClassicUO code – they can leverage their Unity skills to be productive (designing UI in the Editor, etc.) while learning the specifics of UO.

Bottom Line: Unity’s ecosystem is a huge plus. It ensures we are supported by a wealth of knowledge and tools, reducing the likelihood of being stuck on a technical problem and potentially speeding up development through existing solutions. It’s a very “safe” choice in terms of community and documentation.

8. MMORPG Client Suitability: Unity isn’t specialized for MMO clients, but it’s been used in many networked games. For our needs:

  • Networking: We won’t use Unity’s built-in networking (which is for Unity-to-Unity multiplayer). Instead, we integrate ClassicUO’s networking (C# sockets). Unity happily allows System.Net usage. We might run the network receive loop on a separate thread to not stutter the main thread (ClassicUO likely already does similar). The only difference from the current ClassicUO would be that in Unity’s WebGL build, .NET sockets won’t work – but Unity provides a WebSocket library for that scenario if needed. On standalone, no issue.
  • Large world streaming: Unity can load scenes additively or we can generate the world on the fly. We might not use Unity’s scene streaming, because UO’s world is not easily partitioned into pre-made scenes. Instead, we’ll likely instantiate GameObjects for terrain and statics around the player as needed. Unity can handle many GameObjects, but we should pool and reuse them to avoid GC overhead from constant destruction. We can also use Unity’s Jobs or coroutines to load areas gradually to avoid frame spikes.
  • Entity management: Each UO item/mobile could be a GameObject with appropriate components (like a sprite renderer or mesh filter + collider). That provides an easy way to manage position, rendering, and click interactions. The overhead of GameObject per item might be fine (Unity can usually handle a few thousand active objects without issue, especially if many are simple). If needed, critical performance parts (like movement or effects) can be coded in a more data-oriented way or via DOTS, but likely not needed initially.
  • Persistent world client: The client doesn’t need to save world state (server does that), but we may allow user settings to persist (Unity’s PlayerPrefs or a simple file for settings can do).
  • Multi-client: Running multiple instances of Unity might use more RAM per instance than ClassicUO did, but still probably okay (ClassicUO’s memory footprint is small; Unity’s might be a couple hundred MB base, still fine on modern PCs if someone runs two clients).

One MMO-specific concern: client patching. ClassicUO distributes via its own launcher. For Unity, we could either integrate with that (download updated Unity assetbundles or new binaries) or rely on Unity’s content update pipelines. Since the project is open-source, players often compile themselves or get a pre-built binary when needed. We can figure out distribution (maybe a launcher that just checks GitHub releases).

Bottom Line: Unity doesn’t hinder any MMO client features; it provides a solid base to implement them. We already have the needed logic from ClassicUO, it’s just a matter of hooking it up. Unity’s stability and widespread use in long-running games give confidence it can handle the continuous connection and heavy UI of an MMO.

9. UI System Integration: Unity’s UI (UGUI) is well-suited to recreate UO’s interface:

  • Windows & Dragging: We can create UI panels (the UI.Panel or just an Image + LayoutGroup) for gumps. Unity UI supports dragging via the IDragHandler interface on UI components. Implementing a draggable window that stays within screen bounds is straightforward. We can reuse UO’s gump graphics by slicing them into 9-slice sprites for scalable windows or just fixed-size images.
  • Buttons, Checkboxes, etc.: Unity UI has Button, Toggle, Slider, etc. We can customize their appearance with our sprites. For example, the paperdoll “options” button can be a Unity Button with the UO gear icon image.
  • Text: Unity UI Text or TextMeshPro (TMP) can be used for crisp text. TMP especially would allow us to import pixel fonts or any font and render text sharply at any size. So journal, chat, labels can use TMP and even apply effects (outlines, drop shadows as needed).
  • Containers: For the backpack or any container, we have to display arbitrary item icons at specific X,Y positions. In Unity UI, that’s simply setting the anchored position of an Image element (representing the item) within the container panel. Unity doesn’t constrain children to a grid unless we use a layout group, which we wouldn’t for freeform placement. We will need to handle the click/double-click on those item icons (Unity’s event system can detect clicks on UI Images with a RaycastTarget).
  • Cursor changes: Unity can change the cursor icon, or we might implement the paperdoll dragging by having a special dragging icon follow the mouse (easy: just an UI Image that moves with cursor during drag).
  • Overlays: Health bars over mobiles could be done with world-space UI canvases attached to each mobile GameObject, or with a single screen-space canvas that positions elements each frame. Unity UI is flexible to do it either way.
  • Hotkeys and macros: Unity’s input system or old Input manager can be used to capture keystrokes. We can then have our C# code respond by opening UI or triggering actions. Unity doesn’t natively have a macro system (that’s game-specific), so we rely on porting ClassicUO’s macro logic.
  • UI scaling: Unity UI can scale with screen resolution if we set anchors and use its Canvas Scaler appropriately (e.g., scale with screen size option). We can also allow user adjustable scale by adjusting that Canvas Scaler in settings.

One thing to manage: UO’s interface is very custom, so likely we will be writing a fair bit of UI code to mimic the logic (for example, how container gumps handle new items, how the skill window populates, etc.). But Unity’s UI provides the building blocks (visual components and event system) so we’re not doing raw drawing of UI like ClassicUO had to. It will be more declarative and toolable (we can build some UI in the editor visually, then wire it to code).

Bottom Line: Unity’s UI system can faithfully recreate or even enhance Ultima Online’s UI. We’ll do a straightforward implementation panel by panel, guided by ClassicUO’s UI code. Many UO freeshards have made Unity-based clients with custom UIs, indicating it’s a known path. The result should be an interface that UO players find familiar, with possibly improved responsiveness (Unity UI is quite optimized, and can benefit from modern features like high DPI scaling).

10. Learning Curve & Onboarding: The ClassicUO team works in C#, which transfers directly to Unity. They will need to learn Unity’s paradigms:

  • Unity Editor usage (prefabs, scenes, inspector, etc.).
  • Component-based design (attaching scripts to GameObjects instead of large monolithic loops, though they can still use manager scripts if desired).
  • Life-cycle functions (Start, Update, OnDestroy).
  • Some of Unity’s gotchas (e.g., needing to use UnityWebRequest for web access if we do any, or understanding how to avoid GC churn in Unity specifically).

But given they use .NET and are familiar with game loops and engine concepts, I suspect the learning will be quick. Unity is well-documented, and the team can look up “Unity for XNA developers” or similar resources. In fact, Microsoft had a guide for porting Unity knowledge to other engines and vice versa. There’s also possibly knowledge from the MobileUO project (the developer might share insights). The ClassicUO devs likely have some exposure to Unity (it’s so common in gamedev).

Onboarding new team members might become easier since many game developers know Unity or can pick it up easily. Also, artists or designers from the community could help (for instance, someone might help redesign UI layouts or improve 3D models if we involve any, without needing to code).

One consideration: Unity project files (scenes, prefabs) are not diff-friendly. For an open-source project with potentially multiple contributors, merge conflicts in scenes can happen. We can mitigate by modularizing (e.g., each UI window is a prefab, so people can work on different UI prefabs in separate branches). Or use Unity’s newer YAML-based scene format and source control best practices (Unity has a version control guide).

Bottom Line: Unity’s learning curve for this team is moderate and well worth the capabilities gained. They stay in C#, leverage existing code, and learn an industry-standard toolset. The initial adjustment period will be overshadowed by how much time they save not having to build engine features themselves.


MonoGame (FNA/XNA Framework)

1. 3D Rendering & Isometric Support: MonoGame is the open-source implementation of Microsoft XNA 4.0. ClassicUO already uses FNA, which is an XNA-like framework in C#. So the team is deeply familiar with the XNA style. MonoGame supports 3D rendering (XNA had a full 3D API). To do an isometric 3D world, we would:

  • Use a perspective or orthographic projection matrix with the desired angle.
  • Manually draw geometry (MonoGame has a GraphicsDevice for drawing primitives and a SpriteBatch for textures).

Since MonoGame is a framework and not an engine, it doesn’t provide high-level scene structures. But we can reuse our knowledge: ClassicUO’s engine draws a 2D scene in sorted order; we can extend that to 3D by computing depth sorting or just layering things in proper order since UO uses mostly billboard sprites. For example, ground tiles could be drawn first (flat quads), then statics, then mobiles (each possibly a textured quad oriented toward camera or maybe 3D models if replaced). We’ll be coding a lot of the draw logic, but it’s basically what ClassicUO already does (just in 3D space rather than screen space).

Bottom Line: MonoGame can certainly render a 3D isometric world, but it’s a manual process. The familiarity of XNA means we know how to do it (set up BasicEffect for 3D, etc.), but there’s no visual editor or automatic camera, etc. It’s more coding, less design tool.

2. Asset Pipeline Compatibility: Huge advantage here: ClassicUO’s code already runs on an XNA-like pipeline (FNA) and reads UO’s .mul/.uop data directly. MonoGame would be almost identical: we can carry over all the asset loading logic and just plug it into MonoGame’s Texture2D and SoundEffect creation calls. MonoGame even has an optional content pipeline (MGCB) but we might bypass it and load raw data at runtime like ClassicUO does, to avoid converting assets. The code to load and parse UO files can be reused 1:1 since it’s C#. So the translation is trivial: replace some FNA specific classes (if any) with MonoGame’s (which are typically same signatures).

Bottom Line: Asset pipeline is nearly solved already. We just transplant ClassicUO’s asset code. We might compile it as a library and reference it, or integrate code files. Either way, MonoGame will accept the textures and data we feed it the same way FNA did.

3. Shader System Flexibility: XNA/MonoGame uses HLSL Effects. We can write .fx files (HLSL code with technique passes) and load them as Effect objects. ClassicUO might not have used custom shaders heavily (maybe just basic blending). For 3D, we might want to write a few shaders: e.g., one for animating water or a special one for drawing many tiles in a single batch via instancing.

MonoGame’s capabilities are essentially what we code: if we need something like normal mapping, we have to implement it. But since our likely visual target is not AAA graphics, BasicEffect (which supports directional light, texture, and vertex coloring) might suffice for environment rendering. For anything BasicEffect can’t do (like palette shifting for hues), we’ll write an HLSL shader. This requires shader knowledge but XNA’s documentation and samples cover writing custom effects. We can also compile effects offline to XNB, or compile at runtime.

Bottom Line: MonoGame offers full shader control if we invest the effort. It’s lower-level than Unity’s approach (no node editor, but writing HLSL directly). For a small team, focusing on minimal necessary shaders (like an uber-shader for all static sprites with tinting) might be the approach.

4. Performance & Optimization: MonoGame is essentially as efficient as our code:

  • It will draw exactly what we ask. ClassicUO’s current performance is very good, which suggests the team is skilled at optimizing XNA-style code (e.g., batching draws).
  • We have to handle culling of off-screen objects ourselves. ClassicUO already likely does that (only draws objects within the window).
  • We might have to be more careful with 3D in terms of overdraw (lots of translucent surfaces like trees might slow down if drawn unsorted, etc.). But we can sort or turn off blending where not needed.
  • Profiling: We’ll rely on external profilers or custom logging. XNA had the Reach vs HiDef profile concept, but that’s outdated. MonoGame doesn’t have fancy built-in profilers for game logic, so we’d need to measure manually or use .NET profilers.
  • MonoGame uses .NET (Core or Framework depending on setup), so GC could be an issue if we allocate too much per frame. But the ClassicUO code has likely already been optimized to avoid heavy per-frame allocations, since FNA would have similar issues.
  • The advantage: the team can optimize things on a case-by-case basis given they control the draw loop intimately. The disadvantage: we lose out on automatic things like Unity’s static batching – but we could implement our own combining if needed.

MonoGame, being an open-source and fairly simple layer, also means if something’s slow, we can dive into our own code to fix it (no mysterious engine overhead beyond what .NET itself has).

Bottom Line: The performance with MonoGame is primarily dependent on our own optimizations. ClassicUO in 2D has proven extremely lightweight. The 3D version will be heavier, but still likely well within the capacity of MonoGame if carefully done. We must be ready to profile and tweak rendering performance ourselves, instead of expecting the engine to do it.

5. Scripting & Extensibility (C# Reuse): This is MonoGame’s strongest point for us. We can reuse nearly all of ClassicUO’s code. ClassicUO is itself built on an XNA-like model; moving to MonoGame is almost a lateral move, not a rewrite:

  • All the game logic, networking, data structures: no change needed.
  • Rendering code: small changes (instead of FNA’s spriteBatch, use MonoGame’s spriteBatch which is basically identical; use MonoGame’s effect classes if FNA’s differ slightly).
  • Input handling: similar (MonoGame’s Keyboard.GetState vs FNA’s, which is same).
  • Audio: MonoGame can play wave data similarly, though audio is not a big part of UO aside from sound effects.

MonoGame being C# as well means even for new 3D parts, we remain in the language we know. We can also incorporate .NET libraries easily (just like with Unity and Godot). If we needed physics or pathfinding, we could use a C# physics lib or manually code something.

Extensibility: MonoGame has no editor, but since we have full source control, we can shape the project structure as we want. We can also allow modding by C# scripting (e.g., via Roslyn to compile scripts) if desired – not that ClassicUO did, but possible.

Bottom Line: MonoGame allows maximum reuse of ClassicUO’s existing C# code with minimal porting effort. It’s essentially continuing with our current approach but adding 3D capabilities. This reduces risk and keeps development linear rather than starting fresh.

6. Cross-Platform Support: MonoGame supports Windows, Linux, macOS (via OpenGL or DirectX where applicable). It also supports Android and iOS (MonoGame has Xamarin support). ClassicUO is already cross-platform; MonoGame would maintain that. For web, MonoGame is not officially supporting WebAssembly yet (though some have experimented with it via Blazor or WebGL ports). So if we want a browser version, MonoGame is not as straightforward as Unity/Godot. But we could maintain the existing ClassicUO WASM (2D) for web if necessary until MonoGame web becomes viable.

Mobile: If needed, we could use MonoGame to produce an Android app (and iOS with some extra work). It’s definitely been done by others for games. In fact, some forks of ClassicUO (like MobileUO) went Unity for mobile because it was easier than MonoGame on mobile. So our mobile path with MonoGame might need more from-scratch UI for touch, etc., but it’s possible.

Bottom Line: Desktop support is solid (same as current). Mobile support is possible but would require additional platform-specific tuning. Web support is not ready yet for MonoGame, so that is a drawback if we care about a browser client.

7. Community & Ecosystem: MonoGame’s community is moderate in size. It’s used by quite a few indie games (Stardew Valley is on MonoGame now, etc.). There are forums (community.monogame.net) with discussions, and some community libraries (MonoGame.Extended offers features like camera, tilemaps, GUI helpers). Documentation exists but is not as comprehensive as Unity/Godot because many rely on old XNA documentation (still relevant 95% of the time). There’s the XNA Creators Archive content which is useful for learning (3D samples, etc.).

Tooling: No editor, but Visual Studio or VSCode provides a good development environment. The content pipeline has a GUI tool if we use it, but we might not.

Third-party libraries: We might use MonoGame.Extended (for things like easier input binding, maybe its GUI if any, or cameras). There is also ImGui.NET that can integrate Dear ImGui in MonoGame for debug UI. For in-game UI, some use Nez or other frameworks, but likely we’ll implement our own to match UO’s style.

Maturity: MonoGame is stable and has been around for a decade (based on XNA which is older). It’s not going anywhere and is MIT licensed. FNA is another path (FNA is what ClassicUO uses, a fork of XNA too); but FNA focuses on 2D and might not have official 3D support. MonoGame is more mainstream for 3D.

Bottom Line: MonoGame has a smaller but focused community. We won’t find UO-specific advice, but XNA’s legacy provides a lot of guidance. We do lose out on fancy editor tools and an ecosystem of plugins, meaning more work done by coding rather than configuring.

8. MMORPG Client Suitability: Since ClassicUO already is an MMO client built on a similar stack, MonoGame is inherently suitable:

  • Networking: We continue using .NET sockets; no barriers.
  • Entity management: ClassicUO likely already has a world object list, updated each frame. We’d keep that, now with maybe an extra step of updating 3D positions or animations.
  • Continuous running: Should be fine; XNA games can run for hours/days; memory management is under our control.
  • Multithreading: We can thread the network and maybe other tasks as needed; .NET threads or tasks are available just like in ClassicUO.
  • Large world streaming: We manage it ourselves, likely similar to ClassicUO’s approach of keeping nearby map chunks loaded and releasing far ones. That logic can remain unchanged or be tuned for 3D if necessary (maybe loading more distant sectors for horizon visuals).
  • UI complexity and overhead: We will reimplement UI (discussed below) but performance-wise, UO’s UI can be drawn with SpriteBatch which handles batching easily. The overhead for dozens of UI gumps is trivial for modern GPUs.

Bottom Line: There’s practically no question of suitability – ClassicUO demonstrates that an XNA-style framework is already suitable for an MMO client. Moving to 3D doesn’t change the fundamental viability; it just means adding rendering of an extra dimension.

9. UI System Integration: ClassicUO currently uses FNA to draw UI elements manually (blitting textures and handling input). In MonoGame, we could take two routes:

  • Manual UI (retained from ClassicUO): We could port the entire UI system of ClassicUO (all the code that manages windows, controls, input, etc.) into the 3D client almost as-is. This means we’d still be using an immediate mode or custom retained mode UI handled by C# logic, drawing with SpriteBatch each frame. That code is working today in ClassicUO, so porting it is less work than writing a new UI. We would just adjust any differences in how input is polled or how drawing is done (MonoGame’s SpriteBatch usage is the same though).

    • The question is, do we want to maintain that code or use something like a library? ClassicUO’s UI code might not integrate easily with 3D elements (like dragging an item from a 3D world to a 2D UI might need some bridging logic).
  • Use a GUI library: There are a few for MonoGame:

    • GeonBit.UI (as we found, an extension for MonoGame) – it provides a UI system inspired by RPGs with panels, etc. It could be skinned with UO textures. It might simplify making windows and handling basic interactions, but we might struggle to replicate every nuance of UO’s UI (like freeform item placement in containers).
    • MonoGame.Extended has a basic UI but likely not as feature-rich as we need.
    • Nez is a framework built on MonoGame with an ECS and some UI capabilities (though Nez might be more for in-game HUD than complex UI).

    Considering ClassicUO already has a UI implementation that the team knows well, reusing it might actually be faster and ensure fidelity (it’s been honed to mimic the original client). They could lift that system into the 3D client, and then gradually, if desired, enhance it or replace parts. For instance, keep the logic but maybe render it in a slightly different way.

MonoGame has no issue drawing 2D UI on top of 3D: after finishing 3D drawing (with GraphicsDevice.Clear() and drawing world via DrawPrimitives or models), you can call spriteBatch.Begin() in Orthographic projection and draw UI sprites. We can even draw the 3D view to a render target and present it within a UI frame if we wanted (like the game view window concept). But probably we just fill the screen with world and overlay UI.

Bottom Line: We can straightforwardly port ClassicUO’s UI code to MonoGame, preserving all functionality. This leverages years of refinement already done. It might require some tweaks for 3D interactions (like targeting spells in 3D or context menus positioned properly in screen space), but that’s manageable. The alternative (adopting a third-party UI lib) might not be as precise in replicating UO’s interface and could introduce new learning curves or limitations.

10. Learning Curve & Onboarding: The team already basically knows MonoGame by virtue of knowing FNA/XNA. The differences are minor (some namespace changes, maybe slight API differences but mostly identical). Many classic devs consider porting from XNA to MonoGame as trivial (especially since FNA is a fork of XNA too). So, learning curve is minimal. They will have to get into more 3D math if they haven’t (like matrix transforms, etc.), but they might already be comfortable with that too.

Onboarding new contributors: likely similar to ClassicUO – it’s code-centric. There’s no fancy editor to learn, but someone has to be comfortable navigating a codebase, understanding update/draw calls, etc. People with XNA/MonoGame experience (there are fewer nowadays, but some exist) could jump in. Others might have to familiarize themselves with game dev fundamentals more than if it were Unity where many concepts are more visual or documented widely.

However, since this stays in pure C#, any current ClassicUO contributor would find the environment very familiar. This continuity could be valuable to keep momentum. Also, from a licensing perspective, keeping it all MIT (MonoGame is MIT) and not introducing proprietary tech might appeal to open-source purists in the community.

Bottom Line: MonoGame is essentially zero transition for the team’s coding skills – they continue to do what they’ve been doing, just extending into 3D. This means less initial slowdown and no new tech stack risk. It’s a conservative choice technologically, focusing on leveraging existing strengths.


Raylib

1. 3D Rendering & Isometric Support: raylib is a simple C library for game development, often used in education and small projects. It provides straightforward functions to draw shapes, models, and textures. Raylib does support 3D rendering: it has functions like BeginMode3D() to set up a 3D camera, and you can draw models or basic shapes (cubes, spheres), as well as draw billboards (sprites that always face camera). Setting up an isometric camera would involve using Camera3D with appropriate fovy and orientation (raylib’s camera can be positioned and pointed as needed – isometric is just one configuration). Raylib’s 3D capabilities are not as advanced as Unity/Godot, but they are sufficient for moderately complex scenes:

  • It can load models (OBJ, etc.) and textures easily.
  • It handles lighting via shaders if needed (the default material is basic).
  • There’s support for perspective and orthographic projections.

Given raylib’s simplicity, we’d have manual control similar to MonoGame: we’ll be calling functions to draw each object in the scene each frame. It doesn’t have a scene graph or culling system; we need to handle that logically (though it’s easy to loop through objects and decide to draw or not).

Bottom Line: Raylib is capable of rendering a 3D isometric world, but it’s more low-level than an engine – appropriate if we want fine control with minimal overhead, but less feature-rich out of the box.

2. Asset Pipeline Compatibility: Raylib can load image files (PNG, BMP, etc.), but not UO’s .mul/.uop. We would need to write C (or C++) code to parse UO files. Alternatively, since raylib has bindings in many languages (including C# via Raylib-cs, Python, etc.), we could use a binding and then perhaps reuse ClassicUO’s code via those languages. For instance, Raylib-cs is a C# binding for raylib. In theory, we could use Raylib-cs and incorporate ClassicUO’s C# code. That’s interesting: it might give a combination of MonoGame’s code reuse with raylib’s simplicity. However, Raylib-cs might not be as battle-tested, and mixing high-level ClassicUO code with low-level raylib calls may have challenges.

If using raylib in C or C++, we’re basically rewriting everything in C/C++, which is a massive undertaking (like the Java case, but with manual memory management too).

Alternatively, treat raylib as just a rendering layer: one could embed a .NET runtime or use raylib’s Lua binding or Python binding to handle game logic in a higher-level language. Actually, Raylib + Python could be a path (the Pirates Online remake mentioned in search uses Panda3D/Python, but one could imagine a Python + raylib approach). But Python likely too slow for heavy client tasks, and C# via Raylib-cs seems more plausible.

Raylib doesn’t have an asset “importer” concept beyond providing some loaders for common formats (images, models). For UO, we’d need to decode the proprietary formats into something raylib can use (e.g., arrays of color for textures). This is doable if we can either port or reuse code.

Bottom Line: If using raylib directly in C/C++, asset pipeline integration is from scratch. If using a binding like Raylib-cs, we might salvage some code, but that path is less trodden. It’s not as straightforward as Unity/MonoGame where we already have the code running.

3. Shader System Flexibility: Raylib supports shaders; it basically allows loading GLSL shader strings and sending uniforms. It’s not as automated as Unity, but since it’s OpenGL under the hood, you can do what OpenGL can. For example, raylib’s examples show applying custom shaders to models and using postprocessing shaders. We could write shaders for hue shifts or any special effects. But raylib’s philosophy is to keep things simple, so it doesn’t come with fancy built-in materials – we’d have to implement or use what’s given (there’s a basic lighting shader that comes with raylib for Phong shading on models).

Bottom Line: Raylib offers basic shader support, enough to implement what we need but requiring us to manage the details (compiling, setting shader params, etc.). It is flexible in that no aspect is locked down (we can fully customize), but lacks high-level tools.

4. Performance & Optimization: Raylib is designed to be simple and efficient for small to mid-size games:

  • It doesn’t automatically cull or LOD; we need to code culling logic if needed.
  • It uses OpenGL under the hood (3.3 or ES2), which can handle a good amount of objects as long as draw calls are controlled. Raylib does batch 2D draws under the hood if using its sprite drawing functions.
  • For 3D, if we use DrawModel for each object, each call is a draw call. We could gain performance by merging static geometry or using DrawMeshInstanced if raylib offers something like that (I recall raylib might not have instancing high-level function, but one can drop to OpenGL calls if needed since it’s open).
  • Raylib is single-threaded for the most part. We could multi-thread logic on our side, but rendering will happen on one thread.
  • It is written in C, so it’s very close to metal and has minimal overhead. Also, since we can compile it with different graphics backends (OpenGL ES for web, etc.), it’s versatile.
  • It’s been used on embedded systems and low-end hardware, so likely fine on any modern PC.

However, an MMO client can have many objects. If we are naive (draw each tile and item individually), it might be okay up to some number; beyond that, might need optimization (like combining the ground tiles into one mesh per chunk as we often consider).

Raylib does provide a simple profiler in its examples (just a function to get frame draw time or using external profilers). But nothing like Unity’s tools; we rely on printouts or external analyzers.

Bottom Line: Raylib can deliver good performance for moderately sized scenes if we code carefully. The lack of built-in culling means we must be mindful to not draw what isn’t visible. But given UO’s usual active area, it might be fine. The simplicity could make it easier to reason about performance – fewer abstraction layers.

5. Scripting & Extensibility (C# logic reuse): If we choose Raylib-cs (C#), potentially we can reuse ClassicUO code similarly to MonoGame. Raylib-cs would allow using the raylib API from C#, so our project could still be mostly C#. The difference from MonoGame: raylib’s API is function-based, not object-oriented, and quite minimal. We would basically replace MonoGame’s spriteBatch with raylib’s DrawTexture, and MonoGame’s model drawing with raylib’s DrawModel. This could work, and we’d still have .NET for logic.

If we go with Raylib in C++ or C, we’d have to rewrite all logic in that language. That’s a massive drawback, akin to rewriting in Java or more, because C has no high-level constructs. That seems an unlikely choice given we have a huge C# codebase already.

So likely, if raylib is considered, using a binding to maintain C# logic would be prudent. We should check the maturity of Raylib-cs: raylib is often used with C# successfully for smaller projects (like on Windows).

Raylib doesn’t impose structure; we can integrate any libraries we want. If we use C#, we have .NET libraries at disposal. If using C++, we could integrate other C++ libs (but that’s starting over essentially).

Bottom Line: Raylib can be extensible and allow code reuse if using C# bindings, but that’s a less common path (we’d depend on the binding’s reliability). If not using C#, then extensibility suffers due to rewriting everything.

6. Cross-Platform Support: Raylib supports Windows, macOS, Linux (desktop via OpenGL), Web (HTML5) via Emscripten (OpenGL ES 2.0 context, many examples of raylib in web), Android and iOS (again, via OpenGL ES). It’s known to run on Raspberry Pi and even some microcontrollers (with reduced features). So it covers all target platforms. But if using C#, Raylib-cs on mobile or web would require that platform to support .NET (e.g., Blazor WebAssembly for web, Mono or .NET runtime on mobile). That complicates cross-platform for the binding approach:

  • We can compile the C# to WASM via dotnet (like Blazor) and have it call into a raylib compiled to WASM (there’s synergy since raylib is C and can be compiled to WASM; hooking that with C# WASM maybe tricky).
  • On mobile, we could use Xamarin/Mono and compile Raylib-cs for Android (should be feasible if the underlying native raylib is compiled for Android and bound).

This is doable but complex. Alternatively, if we accept writing in C for cross-platform, raylib can unify the code on all platforms without another runtime. But then we lose C# reuse.

However, given ClassicUO’s broad platform support, any new client should strive for that too. Raylib theoretically can meet that, but practically, using raylib in a managed environment across all those is an unknown challenge. Possibly a reason to lean away from raylib if not sure.

Bottom Line: Raylib the library is very cross-platform, but our choice of language binding will affect our cross-platform story. If executed right (like using Raylib-cs with .NET 7 which supports iOS/Android and WASM via AOT), it could work. It’s a bit riskier or at least requires careful technical planning, compared to Unity/Godot/MonoGame which have already solved cross-platform C# for us.

7. Community & Ecosystem: Raylib’s community is smaller, but very friendly (the creator, Ramon, is active). It’s popular in some circles (game jams, educational projects). There are a bunch of examples in the raylib repository, which is great for reference. There isn’t an extensive library of plugins or tools (the whole point of raylib is to avoid complexity). We’d likely code everything ourselves or find generic C libraries to help (like libraries for text layout if needed).

Raylib being plain C might dissuade some contributors who prefer an OOP approach, but ironically, some might find its straightforward style easier (no hidden systems, just function calls). If we go with Raylib-cs, then to contributors it’s just another C# project, which is comfortable.

There’s no built-in editor or advanced tooling; debugging is via traditional means. But the simplicity means there’s also less “engine” to learn. A developer sees a loop of BeginDrawing, BeginMode3D, draw stuff, EndMode3D, draw UI in 2D, EndDrawing. It’s clear.

Bottom Line: The raylib community and ecosystem is minimalistic. Great for not getting overwhelmed, but also means the onus is on us to implement anything beyond basics. For an MMO client, that’s a lot of beyond-basics to implement (UI, networking integration, etc.). So community help would likely be more in moral support and low-level tips, not ready solutions.

8. MMORPG Client Suitability:

  • Networking: Raylib doesn’t provide networking; we’d use standard sockets. In C, that means using Berkeley sockets or a library like ENet or get user to run some proxy if needed for Web. If using .NET (Raylib-cs), then we have System.Net or our existing code for networking.
  • Concurrency: In C, we could use pthreads or similar if needed for networking thread. In .NET, we use tasks/threads as before.
  • Large world: We have to implement logic for world streaming, entity management, etc., from scratch or port from ClassicUO. Nothing in raylib specifically helps with large worlds except that it’s fast and low-level. We’d code something akin to ClassicUO’s chunking.
  • Memory: Raylib doesn’t do dynamic allocation for you except inside its functions; we manage game memory. If C, careful to avoid leaks or fragmentation. If C#, GC again.
  • UI responsiveness: Achieving a smooth UI in raylib might be easier than in heavy engines because it’s lightweight, but it depends on our code structure.

Bottom Line: Raylib as a foundation won’t hinder making an MMO client, but it doesn’t assist either. We’d lean heavily on our own code for everything. It’s essentially like writing a custom engine specifically for UO, with raylib as the rendering/input/audio API. That’s possible (ClassicUO itself did similar with FNA). It might yield a very optimized, no-frills client, but development effort is high and redundant with what’s already been done.

9. UI System Integration: Raylib provides some basic UI drawing:

  • It has simple text drawing, rectangle drawing, etc. There’s an included header raygui (optional immediate-mode GUI for simple interfaces like buttons, sliders) but it’s not styled for game HUDs, more for tool UIs.
  • We’d likely do what ClassicUO does: manage UI windows via code and draw them as textures. Raylib can render text, and we can blit UO gump images with DrawTextureRec to draw parts of a texture.
  • For drag-and-drop, we would track state in code (if an item is being dragged, etc.) and simply draw it at mouse position if so, and on drop, handle accordingly.
  • There’s no event system; we’d poll mouse position and clicks via GetMousePosition and IsMouseButtonPressed each frame and determine which UI element that hits (we can implement a rudimentary hit test by iterating windows top to bottom).
  • This is essentially reimplementing the UI logic of ClassicUO, possibly exactly how it’s done now (ClassicUO’s UI code likely does something similar but using FNA’s capabilities). We could try to port ClassicUO’s UI code to C (which is painful) or replicate logic in C#. If using Raylib-cs, port the UI code mostly as-is since it’s C# already. That could be the best route: incorporate ClassicUO’s UI module into a Raylib-cs environment, adjusting rendering calls to raylib’s.

No high-level UI library is readily integrated with raylib aside from raygui (which is not suitable for game UI complexity of UO). So, heavy lifting is on us.

Bottom Line: We’d implement UI manually (or reuse ClassicUO’s code via C#). This is doable and likely successful because we have a blueprint (ClassicUO’s own UI). But it’s a considerable chunk of work nonetheless. Unity/Godot with built-in UI systems might handle a lot of boilerplate that here we handle ourselves.

10. Learning Curve & Onboarding: If doing raylib-cs, the learning curve for current devs is very low: they stay in C# and just learn the raylib API (which is quite small and straightforward). If doing raylib in C/C++, the team might face a huge learning curve (assuming they’re not as proficient in C/C++ and dealing with low-level details).

Raylib’s simplicity is nice for small teams because one can grasp the entire operation quickly. But making a complex program in it requires discipline in structuring code, since it’s not enforced by the engine design.

Onboarding new contributors depends on chosen language:

  • If C#, likely similar crowd as ClassicUO (they can pick up raylib-cs quickly).
  • If C++, maybe some new contributors (some game devs prefer C++), but likely it would alienate those comfortable with the current C# codebase.

Given the existing code and team are C#, using C would squander a lot of existing expertise and code.

Bottom Line: Raylib with C# binding could be an acceptable path, offering low-level control with high-level code reuse. But it’s not mainstream, so we’d be forging a somewhat unique approach. It might combine some of MonoGame’s and Unity’s advantages (code reuse and simplicity), but it lacks the advanced features and big community of Unity/Godot. The learning curve for that approach is moderate (learn a new API but keep language), with some uncertain cross-platform bits to solve.


PlayCanvas

1. 3D Rendering & Isometric Support: PlayCanvas is a web-based engine (open-source) written in JavaScript, using WebGL/WebGPU. It’s more of an engine than three.js, with a full scene graph, physics, and an online editor if desired. It can definitely handle 3D isometric views – one can set up an isometric camera easily. PlayCanvas is known for its efficiency in browsers (it’s one of the fastest WebGL engines). It has powered some relatively large-scale 3D games in browser. The fact it supports WebGPU means future-proofing for performance.

Bottom Line: In terms of rendering, PlayCanvas is as capable as Unity’s WebGL export, but running natively in JS. It’s suitable for isometric RPGs (some demos exist of similar genres).

2. Asset Pipeline Compatibility: Being web-focused, it loads assets via URLs or file uploads. UO assets in custom format would require either converting them to formats PlayCanvas likes (like glTF models or standard images) or writing a loader in JS (maybe not directly supported, but we could fetch arraybuffers of the mul files and parse them in JS). The PlayCanvas Editor expects you to upload assets, but as an open-source engine, we can bypass the editor and code loading by ourselves. Possibly we could integrate some WebAssembly to parse UO files (like compile a portion of ClassicUO in .NET WASM, and have it output images in memory to feed to PlayCanvas). This is quite complex, though.

Bottom Line: PlayCanvas doesn’t natively know UO formats; similar workarounds as three.js needed (convert offline or parse in browser). The advantage is PlayCanvas has an editor pipeline we might not even use because of our custom assets.

3. Shader System Flexibility: PlayCanvas has a material system and also allows writing custom GLSL chunks or full shaders. It supports physical materials, etc., but we can create simple unlit ones. WebGL shaders are fully usable. The engine likely abstracts a lot, but custom shader injection is possible (documented in their engine reference). Also, with WebGPU coming in, it supports WGSL (WebGPU shader language) in future.

Bottom Line: We have enough shader flexibility to do what we want, but we’d be writing or adjusting shader code in JS (or via their editor).

4. Performance & Optimization: PlayCanvas, being web-based, has similar considerations as three.js, but it’s been optimized at the engine level:

  • It does culling, batching of static meshes, LOD (if set up).
  • It has a pretty advanced runtime for a JS engine (they often tout how smooth it runs on mobile browsers).

We’d still be limited by the browser environment for things like threads (though web workers could be used for loading data). But PlayCanvas likely surpasses a naive three.js implementation because it’s tuned for game scenarios.

Bottom Line: In browser, PlayCanvas is about as good as it gets for performance. It aims to approach native performance. Still, absolute performance will be lower than native engines, but sufficient for moderately complex scenes.

5. Scripting & Extensibility (C# reuse): PlayCanvas uses JavaScript (and supports TypeScript) for scripting. So same issue as three.js: we can’t directly use C# code. We’d be rewriting or using some interop. There is no built-in way to run C# inside PlayCanvas. So we’d likely rewrite the logic in JS/TS, akin to a three.js approach but now within PlayCanvas’s framework (meaning we get some structure but still rewriting logic).

Extensibility: The engine is open source, so we can modify it if needed (though not trivial). There is a community of plugins (like integrating with certain services, or editor extensions). But not relevant to UO domain.

Bottom Line: Code reuse is basically nil unless we do a web assembly trick. That’s a big con similar to three.js approach.

6. Cross-Platform Support: Only as good as web – but that’s quite good. Any device with a modern browser can run PlayCanvas content. PlayCanvas is focused on WebGL deployment, but you can wrap it into mobile apps via Cordova or similar if needed. So in effect, covers all platforms via the browser or minimal wrappers.

Bottom Line: Very accessible (just like three.js). Cross-platform via web, which means easy sharing and potentially reaching mobile without app install.

7. Community & Ecosystem: PlayCanvas has a smaller community compared to Unity/Godot, but it’s actively used for web-based projects. They have forums, decent documentation, and the source is on GitHub. One potential downside: PlayCanvas has an online editor which is proprietary (though the engine is MIT). We don’t have to use it, but many community discussions assume you might use it. We might stick to code only.

The ecosystem isn’t huge, but there are some extensions for things like skeletal animation and glTF pipelines.

Bottom Line: Community is okay, but we won’t find UO-specific help. It’s more niche than general JS (three.js has more broad usage).

8. MMORPG Client Suitability:

  • Networking: Same as any browser approach: require WebSockets to connect. PlayCanvas doesn’t have a built-in UO protocol library, so write our own or use existing general ones.
  • Large world management: It has a scene hierarchy; we might load different zones as separate entities or just have a big world entity pool we move around. The engine likely can handle a few hundred objects active with ease, and for more you’d rely on culling.
  • Persistence: We could use browser storage for settings as needed. No issues.
  • It being an engine might ease some tasks like physics if we needed (for collision of clicks maybe, though 2D projection math might suffice).

Bottom Line: It’s doable similar to three.js, with the slight overhead that we might be working within the engine’s patterns (which is fine). Nothing stands out as a blocker except the standard web limitations.

9. UI System Integration: PlayCanvas doesn’t directly manage HTML UI as part of engine (that’s separate layer like any web content). It has a 2D screen space UI element system (much like Unity’s UGUI but in JS). That is beneficial: we can create UO UI within PlayCanvas using their UI elements (buttons, text, etc.). Or we could, like three.js, overlay HTML. Since PlayCanvas scenes are often embedded in a webpage, mixing HTML for UI might even be easier (just like with three.js).

Given PlayCanvas’s UI system exists, using it might keep everything in one canvas, but using HTML/CSS might speed up dev. We could choose either path; both are viable. It’s similar trade-offs to three.js.

Bottom Line: We can implement UI either in PlayCanvas’s UI or HTML. Either way, it’s a rewrite of UI in a web context.

10. Learning Curve & Onboarding: Similar to three.js, we’d need to get comfortable with JavaScript/TypeScript and the PlayCanvas API. The team’s C# experience doesn’t carry over. PlayCanvas might reduce the amount of from-scratch code (since it has an engine architecture to hook into), but we still rewrite game logic.

Bottom Line: Steep for existing team, moderate if new devs with web skills come onboard. Arguably a bit easier than raw three.js because engine handles more, but still a full reimplementation needed.


Panda3D

1. 3D Rendering & Isometric Support: Panda3D is a veteran open-source engine (C++ core, Python API) used in projects like Disney’s Toontown Online and Pirates of the Caribbean Online. It can render large 3D scenes with good performance. It uses a traditional scene graph. Setting an isometric camera is trivial (just set lens to orthographic and angle it or use a fixed camera transform). Panda3D has handled isometric games (there’s an example of an isometric view in some community code, though not specifically UO-like). It supports terrain, particles, shaders, etc. Not cutting-edge graphics like Unreal, but solid enough.

Bottom Line: Panda3D can definitely render a UO world in 3D; it’s been used for MMOs, indicating it handles such use cases.

2. Asset Pipeline Compatibility: Panda has its own model format (.egg or .bam), but can import common formats via converters. For UO assets, no built-in support. We’d probably write Python code to read .mul files using Python file IO (or call out to C++). Python is quite capable for parsing binary data. There might even be existing code (some freeshard projects might have tried Panda, not sure). We could also convert assets offline to something like a folder of PNGs for artwork and let Panda load those textures.

Bottom Line: Some coding required, but Python might allow reuse of ClassicUO logic if we translate it. Still, a fair bit of integration work, albeit in Python which is high-level.

3. Shader System Flexibility: Panda3D has a shader generator for basic stuff and supports custom GLSL shaders. We can write our own for any advanced effect. Historically, it was a bit behind on fancy graphics, but nowadays it supports normal mapping, shadow mapping, etc., especially with custom shaders. So implementing a hue shift shader or other effects is possible.

Bottom Line: We have enough shader capability in Panda3D, though the engine by default might be more fixed-function unless we opt in to custom pipeline. But since we likely want full control for look, writing a few shaders is fine.

4. Performance & Optimization: Panda3D is optimized for real-time 3D (it handled MMOs with lots of simultaneous players). It has:

  • View frustum culling,
  • Possibly occlusion culling via an octree (not sure if automatic),
  • Level-of-detail node support,
  • Portals/zone culling for interiors,
  • A built-in performance analysis tool PStats which is excellent for profiling performance (both engine and Python code).
  • It’s multi-threaded in C++ for certain operations, and you can multi-thread Python tasks if careful (though Python has GIL, Panda offers “task chain” thread pools for running tasks like AI in parallel).

One concern is Python speed for large loops (like iterating thousands of objects). But a lot of heavy lifting (rendering, culling) is done in C++ inside the engine. If needed, we can move some logic to C++ or use Cython.

Bottom Line: Panda3D was literally used for MMORPG clients, so it’s proven for performance in that domain. Our use-case likely falls well within its capabilities if we optimize where needed. Its profiling tool will help ensure we meet frame targets.

5. Scripting & Extensibility (C# reuse): Panda3D’s primary scripting is Python (though you can also use C++ directly or even C# through bindings, but not common). We cannot directly reuse C# code. We’d need to rewrite ClassicUO’s logic in Python. Python is easier to write than C++ or JS perhaps, but it’s still a rewrite. The complexity of the client (especially network protocol and all subsystems) is large, so rewriting in Python, while perhaps quicker to code, could introduce performance issues or bugs. However, one big plus: Python is very agile for development; iterating features might be faster (no compile, just run and test; can even live update code to some extent).

Extensibility: we can interface with C++ libs if needed (via Panda’s interrogate system or Python’s ctypes). But likely not needed except possibly for performance-critical bits (could consider rewriting networking in C++ if Python overhead too high, etc.). Python has libraries for UI, data structures, etc., but in a game context we might stick to engine’s capabilities.

Bottom Line: Without code reuse, Panda3D is a full rewrite in Python (with maybe some copy-paste logic adapted from C# to Python). This is a moderate risk. The plus side is that Panda’s been used for similar tasks, so conceptually we know it’s viable, but it’s a lot of work and potential for new issues.

6. Cross-Platform Support: Panda3D runs on Windows, Linux, macOS out of the box. It can be compiled for iOS/Android (not sure how smooth that is; there are reports of it working but not as commonly done as desktop). Web: there was progress on WebGL/WebAssembly support (some experimental ports), but it may not be production-ready. If needed, since engine is open, one could attempt Emscripten compile, but it’s complex.

Given UO’s userbase, PC support is crucial; Panda does that well. Mobile could possibly be done by embedding Python on mobile which is tricky; not Panda’s strong suit. So not as good for mobile or web as Unity/Godot. But for PC, it’s fine.

Bottom Line: Great for desktop, iffy for mobile/web. If those are secondary, not a big problem. A 3D UO client might primarily target PC anyway.

7. Community & Ecosystem: Panda3D has a smaller community (much smaller than Unity, smaller than Godot). But it’s quite dedicated, and the engine has been around a long time (so forums have answers to many questions). The documentation is decent, though sometimes outdated. A big plus: any time we see how something was done in Toontown or Pirates (which share DNA with UO in being older MMOs), we know Panda handled it. Actually, some modern fan remakes of those games still use Panda, meaning the engine is maintained to work on modern systems.

Tooling: Panda3D doesn’t have a fancy editor; you mostly code. There was a scene editor in the past but not widely used now. You might use Blender for making models or Tiled for making maps, but in our case we have assets.

One nice thing: It’s open-source, so if a bug in engine is encountered, one could patch it or at least report and possibly see a fix.

Bottom Line: A smaller but relevant community. We might not find UO-specific guidance but can glean MMO client insights from their docs and the fact it’s done similar scale projects.

8. MMORPG Client Suitability: Panda3D was literally built for MMOs by Disney. It has networking libraries (it had a distributed object system in Python, though we might not need that exactly for a client). It definitely can maintain the needed network connection (just use Python sockets or Panda’s own network classes). It’s proven in managing large numbers of entities, culling them, etc.

Notably, Panda includes PStats which can measure performance of tasks in detail, useful for optimizing an MMO client where lots of things happen.

One caution: Python might slow down some things if not careful, but that can be mitigated by writing critical loops in C++ if needed or optimizing Python logic (perhaps not all 1000 items need to be processed every frame, etc., which is true in any engine).

Bottom Line: Very suitable – perhaps the engine on this list historically closest to our use case (since it was used for Toontown which is a 3D MMO with an interface, etc.). It’s a strong candidate if one isn’t deterred by Python or rewriting.

9. UI System Integration: Panda3D has a GUI system called DirectGUI which is basic but covers buttons, labels, sliders, etc. It’s not the prettiest or most flexible, but you can style it with images. Toontown’s interface (somewhat simpler than UO but had chat, inventories) was done in Panda with custom UI.
We could:

  • Use DirectGUI: define frames for gumps, assign textures, manage layout manually (DirectGUI doesn’t do complex layouts automatically, but UO’s UI is anyway absolute positioning mostly).
  • Or integrate another Python GUI (some have tried using PyQt or Kivy, but for a game it’s easier to use DirectGUI or roll something).

DirectGUI will let us put clickable images on screen, text, drag them (with a bit of code; it supports dragging IIRC). If it’s insufficient, we could render UI using the 2D sprite system like ClassicUO does (draw textures at positions and handle input manually).

Given the team would likely be coding anyway, maybe they implement the UI logic in Python similarly to how it’s done in C# now, using Panda’s low-level 2D rendering for performance (which might be needed if many UI elements).

Bottom Line: Panda3D gives some UI tools but not as high-level as Unity’s. We’d be doing moderate amount of UI coding, but on the bright side, Python’s rapid dev might help iterate. And lots of reference exists from those older games that had inventory etc. Possibly, even pieces of those clients are open in fan projects we could learn from.

10. Learning Curve & Onboarding: The team would need to be comfortable with Python. If they aren’t already, that’s a shift in language (though Python is one of the easier languages to learn). The concepts of engine usage might actually be easier than Unity because it’s code-driven and they are used to code-driven. But debugging Python can be different (though PStats helps for performance, and Python exceptions are usually clear).

Onboarding new contributors might be mixed: some might love Python (so easier to contribute), others might not like it for a large project. But Panda’s use in education means many devs have at least dabbled in it.

Bottom Line: Moderate learning curve: new language for team, but straightforward engine architecture. Could accelerate some tasks (UI prototyping, etc.) due to Python’s speed of development. Risk of runtime errors (Python is dynamically typed, could cause issues that are caught at runtime instead of compile-time).


Other Notables (Bevy, Ogre3D, Stride, Unreal, BDX, etc.)

Bevy (Rust):

  • Pros: modern Rust-based ECS engine, very performant and cross-platform (even WASM).
  • Cons: Rust learning curve for team, immature UI (though it’s growing), no built-in editor. Code reuse is zero (would rewrite in Rust).
  • The team would likely find Rust quite unfamiliar and we’d lose C# code.

Ogre3D:

  • Pros: C++ rendering engine, very flexible and powerful (Iris2 UO client used Ogre successfully).
  • Cons: C++ (no reuse, heavy dev burden), Ogre is low-level (need to build game logic around it, and UI).
  • Iris2 (2007 era) suggests Ogre can do UO, but they used Lua for logic then. We’d need to either do similar or C++.
  • Community is smaller now and Ogre had slowed development in recent years (though OGRE v2 is more optimized but less featureful).

Stride (formerly Xenko):

  • Pros: C# engine, editor similar to Unity, completely open-source (MIT). Good graphics (Silicon Studio’s engine).
  • Supports Windows, maybe other desktops; not sure about mobile currently (likely yes though might need work).
  • Can reuse C# code, likely with some adaptation (Stride’s API is different from Unity’s, but still C#).
  • Cons: Smaller community than Unity/Godot, fewer tutorials. Editor only on Windows at the moment (development on Mac/Linux possible via code but not sure).
  • However, Stride offers a nice compromise: open source and C#.
  • For UO: Would be a strong candidate. It has good 3D, and since it’s .NET, should integrate our code decently.
  • It is not as battle-tested as Unity though. But it’s a promising engine and being actively improved by community/Trail.

Unreal Engine:

  • Pros: Top-tier graphics, lots of tools, could theoretically make a stunning UO client; networking is robust (but geared for shooter replication, not needed for us).
  • Cons: C++ or Blueprints. Blueprints not suitable for complex logic of an MMO client, C++ would be a near-total rewrite. There are some C# plugin experiments but not mainstream.
  • Overkill in many ways, and heavy for older PCs. Also, editor is heavy for devs not used to it.
  • Unless photorealistic UO or we had already a C++ core, Unreal is likely not a fit. Too high a learning curve and mismatch for 2D-heavy UI aspects (though UMG is there for UI).

BDX (LibGDX+Blender):

  • A niche engine integrated with Blender.
  • Pros: Quick prototyping by designing in Blender and pressing play as described.
  • Cons: Java-based (like LibGDX), small community, possibly not actively maintained.
  • It’s interesting but probably not robust enough for a whole MMO client; it’s more of a pipeline thing than an engine built for large scale.

**Lightweight engines like *Torque 3D* or BYOND etc. might exist, but none offer the combination of features and ease we need better than the ones already discussed.


Summary Comparison Tables

Below, we provide a concise comparison of key criteria across the engines/frameworks discussed. This will highlight strengths and weaknesses side by side:

Feature Support Table
Engine/Framework 3D & Isometric Asset Pipeline (UO) Shader Flexibility Performance Tools C# & Code Reuse Cross-Platform UI System Networking & MMO Community & Docs
Unity Excellent 3D; easy ortho camera. Extensible: load via C# (MobileUO example) (Check Out “MobileUO”, a Unity-Based Mobile Client for Ultima Online – The Ultima Codex). Very high (ShaderGraph, HLSL). Strong profiler, batching, LOD, etc. Yes (C#) – reuse most logic. Windows, Mac, Linux, WebGL, iOS, Android. UGUI (rich, WYSIWYG editing). Sockets via C#; proven in MobileUO. Huge community, tons of resources.
Godot 4.4.1 Great 3D (Vulkan); ortho camera. Extensible: C# load or custom importer. High (GLSL, visual editor). Good (culling, some batching; growing tools). Yes (C#) – reuse most logic. Win, Mac, Linux; iOS/Android (C# in 4.2+); Web (C# experimental). Control nodes UI (powerful). Sockets via C#; engine ok with many objects. Growing fast; good docs, open-source.
MonoGame Full 3D via XNA API; manual camera. Native support via code – reuse ClassicUO’s loaders. High (write HLSL Effects). Manual (no built-in profiler; use .NET tools). Yes (C#) – reuse nearly all code. Win, Mac, Linux; iOS/Android (Xamarin); Web (not officially yet). None built-in; custom or libraries (e.g., GeonBit.UI). Sockets via .NET; essentially ClassicUO in 3D. Moderate; relies on XNA docs; active forums.
Raylib Good basic 3D; manual setup. Code needed; possible with Raylib-cs to reuse C# logic. Moderate (GLSL, no editor). Minimal built-in; must profile via external. In C: No (rewrite); in C#: Possibly (Raylib-cs binding). Win, Mac, Linux; Web (WASM); Android, iOS (with effort). Basic (or custom immediate mode). Sockets via libc or .NET; nothing built-in. Small but friendly; minimal docs beyond examples.
three.js (Web) Good 3D (WebGL/WebGPU); ortho camera easy. Must convert or fetch & parse in JS. High (custom GLSL possible). Browser dev tools; manual tuning. No (JS rewrite or WASM hack). Any browser (Desktop/Mobile). HTML/CSS or custom WebGL UI. WebSockets; must proxy for UO TCP. Large web dev community; many examples.
PlayCanvas (Web) Great WebGL 3D; has editor. Must convert or use JS to parse. High (full WebGL shader support). Good engine profiling; optimizations built-in. No (JS/TS only). Any browser; wrappers for mobile. Yes (Engine UI system + HTML). WebSockets; engine not MMO-specific but capable. Moderate; good docs; smaller than three.js.
Panda3D Good 3D (proven in MMOs); ortho camera. Must write Python loader or convert assets. High (GLSL; some fixed-function helpers). PStats profiler; culling, LOD available. No (Python or C++); rewrite in Python likely. Win, Mac, Linux; (Mobile possible, Web experimental). DirectGUI (basic) or custom. Sockets via Python; used in MMOs before. Small but experienced; OK docs; open-source.
Stride Excellent 3D (like Unity level); has editor. Extensible via C# (no built-in UO support). High (HLSL, effect system). Good (similar to Unity in many ways). Yes (C#) – reuse possible with some adaptation. Win target; other platforms (Linux, Android, iOS) in progress. UI system exists but less mature than Unity’s. Sockets via C#; no built-in MMO network. Small but growing (ex-Xenko users); good C# docs.
Unreal Engine Top-tier 3D; ortho possible but engine is heavy 2D UI. Must write importer or preconvert; C++ required. Very high (Material Editor, HLSL). Excellent (profiles, automated LOD, etc.). No (C++ or Blueprints; C# only via unofficial plugin). Win, Mac, Linux, iOS, Android, Web (via Pixel Streaming or experimental WebAssembly). UMG UI (powerful visual UI editor). Sockets via BSD/C++; overkill but possible. Huge community, but steeper learning (C++).
Ogre3D Great 3D renderer; isometric camera user-setup. Must integrate custom loader (C++ needed). High (material system, custom shaders). Good (paging, LOD, but engine-level only). No (C++ or Python via wrappers); rewrite needed. Win, Mac, Linux; mobile via OGRE; no Web. CEGUI or MyGUI often used, or custom. Sockets via Boost or custom; no built-in net. Small now; was big in 2000s; docs exist but aging.
Strengths & Weaknesses Table

To highlight the key pros and cons for each option in context of this project:

Engine Key Strengths Key Weaknesses
Unity – Maximum C# reuse (ClassicUO code integrates) (Check Out “MobileUO”, a Unity-Based Mobile Client for Ultima Online – The Ultima Codex).
– Robust 3D + polished tools (editor, profiler, etc.).
– Huge community & multi-platform (including mobile/web) support.
– Rich UI system for complex UO interface.
– Proprietary engine (though free to use, not open-source).
– Heavier runtime (might use more resources than a custom thin client).
– Learning Unity engine paradigms needed (for those new to it).
Godot 4 – Open-source (MIT) engine with modern 3D (Vulkan).
– C# support for code reuse.
– Lightweight, runs on modest hardware.
– Good UI system and active development, tailored by community needs.
– Fewer out-of-the-box assets/tech than Unity (some systems still maturing).
– C# on web not fully stable yet (could limit browser client initially).
– Smaller community (though growing fast).
MonoGame – Very high code reuse (essentially ClassicUO’s natural evolution).
– Full control over rendering and logic (no “black box”).
– Cross-platform .NET core.
– Minimal overhead, can be highly optimized for UO specifics.
– No editor or high-level tools: more manual work for rendering, UI, etc.
– Smaller community and few pre-built modules (must implement features).
– Web target not mature (could lose browser client).
Raylib – Simple API, low-level performance.
– Multi-language: could use C# binding for reuse while keeping things lightweight.
– Very permissive open-source (zlib license), no bloat.
– Great for understanding and custom tweaking.
– No high-level features: everything from UI to scene management must be coded.
– Uncommon approach (C# with raylib) might face uncharted issues.
– Cross-platform with C# binding is untested waters (especially on Web/Mobile).
three.js (Web) – Ultimate accessibility (runs in browser, one click play).
– Large web community, many resources.
– Can leverage HTML/CSS for UI easily.
– WebGL 2 / WebGPU provides decent graphics possibilities.
– Requires complete rewrite in JS/TS (lose C# code).
– Debugging and maintaining large JS codebase can be challenging.
– Performance limited by browser single-threading (could be an issue in crowded scenes).
PlayCanvas (Web) – Browser-based with a structured engine (less reinventing wheels vs three.js).
– Online editor could aid collaborative development.
– Highly optimized for WebGL, good performance reports.
– Same rewrite issue (must implement in JS/TS).
– Editor is proprietary SaaS (though engine is OSS) – may or may not use it.
– Smaller community than three.js; less flexible outside its paradigm.
Panda3D – Proven MMO engine (Toontown, Pirates).
– Python allows rapid development and easy tweaking.
– Powerful profiling (PStats) and a lot of built-in functionality for world, AI, etc.
– True open-source longevity (20+ years).
– Would need rewrite in Python (time-consuming and potential performance hotspots).
– Python knowledge required; may not be familiar to current devs.
– Not as “trendy” – smaller dev pool to draw help from; editor is minimal.
– Mobile/web not as strong.
Stride – C# from top to bottom (engine and game) – can modify engine if needed.
– Editor experience similar to Unity, but fully open-source.
– Good balance of high-level tools with code control.
– Still less used, means smaller community and possibly a few engine bugs.
– Editor only officially on Windows, which could affect cross-OS dev.
– Documentation and tutorials fewer than Unity/Godot.
Unreal – Extremely powerful rendering and polish.
– Could produce a very high-fidelity 3D UO (if desired).
– Networking, physics, etc., all robust if needed.
– C++ development complexity and huge rewrite required.
– Overkill for 2D-heavy UI and simple graphics of UO – would be fighting the engine’s scale.
– Higher hardware requirements for players and devs.
Ogre3D – Very customizable C++ engine; Ogre2.x optimized for large scenes.
– Used historically for a 3D UO client (Iris2).
– No license cost, lower-level control for custom needs.
– C++ codebase, no existing UO code reuse.
– Need to integrate many subsystems (input, sound, GUI, networking) separately – Ogre is just graphics.
– Diminished community, many devs moved to higher-level engines.

From these comparisons, we can derive a short list of most suitable candidates:

  • Unity – High success probability with moderate effort; leverages existing code, huge tool support, multi-platform reach. Great for long-term maintainability due to popularity (easier to find devs) but not open-source.
  • Godot 4 – Very promising: open-source, supports C#, good 3D and UI, actively improving. Some early adopter risk but aligns well with the project’s community-driven nature and code reuse needs.
  • MonoGame – Safe and familiar: essentially continuing with what has worked (C# XNA paradigm) but extending to 3D. No fancy tools, but minimal rewrite and full control. Could be a top choice if maintaining complete ownership of tech is priority.
  • Stride – A dark horse: open-source C# engine with an editor. Combines benefits of Unity (C#, editor) with open-source. Slightly smaller ecosystem but technically sound, might deserve consideration if team is comfortable experimenting a bit.
  • Panda3D – If the team is willing to shift to Python, Panda3D could expedite some MMO-specific aspects and has proven capability. But loss of C# code and team skillset mismatch are concerns.
  • Web-based (three.js/PlayCanvas) – Offers something unique (play in browser) but at cost of rewriting and potential performance trade-offs. Might be better as a complementary project (like an experimental web client) rather than the main client, unless web delivery is a key goal.

Recommendations and Shortlist

Considering the analysis, the top recommendations for porting ClassicUO to 3D are:

  1. UnityRecommended for quickest path to a working 3D client with rich features and broad platform support. Unity lets us reuse our C# code extensively, and many UO community members are already experimenting with Unity (MobileUO) proving viability. The learning curve is manageable, and the development speed with Unity’s tools (editor, profiler, UI designer) will likely outweigh its heavier runtime. If immediate results and mobile support are priorities, Unity stands out. The community and asset ecosystem also ensure long-term maintainability (many devs can step in, abundant learning resources). Possible concern: proprietary nature – but since the client is open-source and free, Unity’s free license suffices, and using Unity doesn’t impede our GPL-inherited code (Unity’s TOS allows open-source usage, we just don’t open source Unity’s parts, only our code).

  2. Godot 4Recommended for an open-source, community-driven approach aligned with ClassicUO’s spirit. Godot offers a strong blend of code reuse (via C#) and freedom (no black boxes, engine code can be modified). It has enough 3D and UI capability to handle the project, and its rapid growth suggests improving support and performance over time (which we benefit from for free). The slightly smaller ecosystem means the team might need to solve some issues themselves, but given ClassicUO’s community backing, that could be acceptable. Key advantage: no risk of licensing or corporate changes affecting us; truly our own engine/community synergy. It might require a bit more initial ramp-up (especially to ensure performance is optimized for large scenes), but it’s likely worth it for maintainability and independence. By the time this project is in full swing, Godot’s platform support for C# will have further matured (perhaps including web via WASM, eliminating one of Unity’s remaining unique advantages).

  3. MonoGameRecommended for a purist approach that maximizes continuity and control. This path leverages virtually everything done so far and simply adds a 3D layer to it. The development process will be very code-centric and requires building or porting systems (like a 3D camera, possibly a new rendering pipeline for 3D objects), but the team’s familiarity with the code means they can anticipate and handle this. It avoids reliance on heavy external engines and keeps the project lightweight. This is ideal if the goal is to modernize while keeping the client footprint small and performance high on even older machines (important for some UO players). Downside: lacks the fancy editors, meaning slower to design new content (but since UO content is mostly pre-made, we don’t need a level editor). Also, out-of-the-box support for new platforms like Web is missing, which might or might not be a dealbreaker.

  4. StrideConsider as an alternative to Unity/Godot if we want open-source but with an integrated editor and staying in C#. Stride could be a hidden gem: it’s C#, supports .NET 6+, and has a Unity-like editor interface. Being MIT licensed, it aligns with open ethos. The team could reuse code (though some adaptation to Stride’s API and component model would be needed). The community is smaller, which means potentially more self-reliance – but since Stride was a professional engine (Xenko) originally, it’s quite capable technically. We should prototype a bit to see how easily ClassicUO code fits. If it fits well, Stride might yield a best-of-both-worlds solution (open-source + modern engine features + code reuse). It’s not as battle-tested as Unity or Godot, so there’s a risk, but given ClassicUO is not a commercial venture, trying Stride could be worthwhile if Unity’s proprietary nature or Godot’s relative newness to 3D are concerns.

Other engines like Panda3D or a web solution can be secondary considerations or long-term experiments:

  • Panda3D could be an interesting experiment for a Python-based client, but likely not first choice because it forfeits our existing code and requires the team to pivot language.
  • A Web client (three.js/PlayCanvas) might be something to pursue in parallel for a lite client accessible anywhere, but perhaps not as the main project due to the rewrite involved. Alternatively, a Unity WebGL or Godot WASM build could cover the browser angle without a separate codebase, albeit with the need for a proxy server for networking.

In conclusion, for a long-term reimplementation aiming to modernize the client while preserving UO’s gameplay and community modifiability, Unity or Godot 4 emerge as the top choices. They both allow a high degree of code reuse, have strong cross-platform support (ensuring players on all OS – and even mobile or browser – can be reached), and have active communities to support development over the coming years.

Unity might get us to a working prototype fastest and ensure polish, whereas Godot might align better with the open-source ethos and give us more control in the long run (no dependency on Unity’s decisions, which could be important given community sensitivities and the project’s non-commercial nature).

MonoGame is the conservative route – virtually guaranteed to work given it’s an extension of our current tech, but will require more custom work for 3D and UI which Unity/Godot provide out of the box. If the development team is small and comfortable with that trade-off, MonoGame could result in a simpler, leaner finished product that’s easier to maintain by that small team (less engine complexity, albeit more custom code to maintain). It’s essentially a question of whether we want to build upon an engine (Unity/Godot/Stride) or be the engine builders (MonoGame/raylib).

Shortlist – Final Ranking (with rationale):

  1. UnityHigh confidence, low risk, feature-rich. Best for quickly achieving a fully functional 3D ClassicUO with minimum surprises. Backed by evidence (MobileUO) and ideal for cross-platform, including mobile. Great if development speed and broad compatibility are top priority.

  2. Godot 4Open-source future-proofing. Best for aligning with the community-driven nature of ClassicUO and avoiding any external dependencies. There’s a bit more ramp-up, but C# support mitigates that. Excellent if keeping the project 100% open-source and community-controllable is a priority, while still utilizing modern engine capabilities.

  3. MonoGameContinuation of current approach. Best for those who value continuity, performance, and fine-grained control over every aspect. The result will be custom-tailored to UO (no unused bloat), though development will require more work especially for new 3D aspects. Good if the team is comfortable shouldering engine development tasks and wants to keep the client light and exactly as needed.

  4. StrideWorth evaluating as a potential middle-ground. If initial tests show ClassicUO code integrates well, Stride could give us Unity-like productivity without vendor lock-in. Keep in mind the smaller community; use if Unity is undesirable but Godot’s current capabilities don’t fully convince.

These choices all consider short-term feasibility (getting a prototype running, showing a 3D or pseudo-3D UO world) and long-term maintainability (can volunteers maintain and extend this client for years to come?). Unity and Godot score highly on maintainability but via different philosophies (one via widespread usage, the other via open-source longevity). MonoGame scores on maintainability in the sense of simplicity and known quantity (no engine updates to worry about, as it’s mostly our code).

Finally, as a strategic note, the team could even decide to develop in parallel on two fronts: for example, start a Unity prototype to quickly have something playable and test gameplay in 3D, while also contributing to a Godot or MonoGame version to see which progresses better or is more liked by contributors. Given time and community interest, the best path will reveal itself.

However, if we must choose one: Unity gets the slight edge for now as the fast-track to success (with minimal reinvention), while Godot 4 is extremely attractive for the ethos and might become equally as efficient as Unity within a year or two of engine maturing – making it a strong contender especially if the community prefers an open-source engine environment.


Summary Recommendation: Focus on Unity or Godot 4 for the 3D ClassicUO port, due to their C# compatibility, comprehensive feature sets (3D, UI, tools), and cross-platform reach. Unity offers a proven, immediate solution (already partially validated by projects like MobileUO (Check Out “MobileUO”, a Unity-Based Mobile Client for Ultima Online – The Ultima Codex)) and would likely yield a stable client relatively quickly. Godot 4, on the other hand, offers independence and open-source purity with slightly more initial effort, which could pay off in long-term community ownership of the client. MonoGame remains a reliable backup or parallel path, ensuring that if engine-oriented approaches encounter issues, the project can still succeed by essentially evolving the existing client with 3D capabilities in a familiar framework.

Michael Ten

Michael Ten is an author and artist. He is director of Tenoorja Musubi, and practices Tenqido.