Porting an existing Delphi FMX card game to Castle Game Engine

Hi,

I have an existing card game developed with Delphi and FireMonkey (FMX).

What would be the easiest way to port it to Castle Game Engine?

Is there a recommended approach for reusing as much of the existing Delphi/FMX code and game logic as possible, while replacing the FMX-specific parts with Castle Game Engine?

Any advice or examples of similar projects would be greatly appreciated.

Thanks!

The answer depends on how you used FMX, I would say.

If you have a regular “standard” UI made using FMX, e.g. buttons, edit boxes, and then you draw your game on a canvas → then as a first step, maybe intermediate, I would suggest to use TCastleControl on FMX form (see Engine on a form (VCL, FMX, LCL) using TCastleControl | Manual | Castle Game Engine ). You can surround such TCastleControl with your existing FMX controls, so you can switch your rendering to CGE “gradually”… only in places where you want to switch, and keep the rest of UI using FMX. In this approach, FMX and CGE will work together.

Once it works, as a 2nd step (if you want to move away from FMX completely), then you can “expand” how much space is taken by TCastleControl on your form, and move more of the UI to use CGE UI. For example, buttons using FMX TButton could migrate to using our TCastleButton, and be inside TCastleControl.

Step 3: Working this way, you can eventually have a state when TCastleControl just fills your entire form, and this is your own form → at which point you can change your entire game to just use our TCastleWindow.

Whether this plan makes sense, really depends on your game, how you use FMX. And what are your plans (above, I assumed you don’t want to use FMX, at least not directly; note that CGE TCastleWindow will still use FMX is some cases underneath, for “Delphi on non-Windows” now, but this is hidden from you). You’re welcome to send screenshots of your existing game → this may help me to give better advise, what to replace with what:)

2 Likes

The project is actually made up of several parts:

  • A 3D card game engine that I wrote myself.

  • A UI layer, which has many layers and controls, all built with FMX.

  • A part that uses FMX 3D, which is controlled by my own game engine.

Ultimately, my goal is to migrate the entire project to Castle Game Engine (CGE) and FPC, mainly for Android.

So I’m not just trying to replace the rendering part. I would eventually like to move the whole project away from FMX and have it run completely on CGE/FPC.

There are about 5 screenshots and a video of the current application here:

Google Play – Card horoscope

I think seeing the screenshots and video may give you a better idea of the current architecture and what I’m trying to migrate.

2 Likes

Thanks for the Google Play link with screenshots and video!

So, I think my advise from last post was mostly good:) But it is simpler: you main view is a 3D view with table + cards (and UI on top), so that’s where I would start porting. Also, the information that your goal is to move 100% to CGE (and FPC) is important, and so I would suggest to just start new project and move the code there.

Detailed advises:

  1. Preparations:

    • It would be helpful if you can refactor / move (as much as possible) of the code of your existing project to FMX-independent unit(s). E.g. stuff like card colors, card values, game logic → if this code exists in FMX-independent unit, then it can be reused in CGE version without any effort. The less code you have in FMX forms / FMX frames units, the better.
    • During such refactor, you can keep testing the FMX version, to confirm that it all works.
    • Bonus points if you want to add automated testing of game logic at this point:) There are full-blown testing frameworks (CGE also has one, used for our tests ), but at the beginning all you really need is a program doing asserts, like
    {$assertions on}
    uses MyCards;
    begin
       Assert(Ord(High(TCardColors)) = 3); // there are 4 card colors
       Cards := TCardsList.Create;
       try
         ....
         Assert(TestValidPosition(Cards));
       finally FreeAndNil(Cards) end;
    end;
    
  2. Focus on the “3D view with table + cards”.

  3. Then add missing things:)

    • UI on top of the main view.
    • You may need to convert between “3D positions in viewport” and “UI coordinates” for an arrow I noticed on the video. See examples/viewport_and_scenes/position_from_world/.
    • Additional views.

You want to eventually switch to FPC+Android. I would suggest to identify and change the Pascal code to work with both FPC and Delphi, for some time (whole CGE compiles with both FPC and Delphi, so this is possible :slight_smile: ). But to make it easier, you can also just focus at the beginning on making the game working with CGE with Delphi for Windows. You can make a desktop window have portrait-like size (to simulate phone) using code like in examples/network/tcp_connection/client/code/gameinitialize.pas, see also Window size and fullscreen | Manual | Castle Game Engine . Once you will have everything working, using CGE, with Delphi + Windows, then porting it to FPC + Windows and then to FPC + Android could be done.

These are hints that come to my mind.

I hope this is useful. Take a look at above links and examples, and let me know if something is unclear, I can expand on any topic here. Let me know in general if you will be stuck at any point, we can help:) From what I can see, your game should be completely doable in CGE:)

2 Likes

Thank you, @michalis

That was a very good and comprehensive explanation. Fortunately, most of the game logic is already organized into separate units.

One issue that just came to mind is that my game is multilingual. However, in some previous tests I did with Castle Game Engine, it didn’t support right-to-left languages. I don’t know if that is still the case or not, but it isn’t important for getting started.

What is important, though, is whether it is possible to implement multilingual support.

I’ll get started, and if I run into any problems, I’ll definitely ask.

1 Like

Sure:)

Our recommended way is our localization approach, see:

If that is not enough, CGE could probably be integrated with any other localization system – CGE core components provide the necessary points (like TCastleComponent.TranslateProperties) and other translation systems could be build on top of it.

As for right-to-left languages: Admittedly I don’t have experience with them, and our UI controls may not support necessary properties. Right now, TCastleLabel always renders left-to-right. I’m happy to improve it, feel welcome to submit an issue (on forum or GitHub issue) when you get to it and see things are missing. Along with feature requests, in this case it may be best to compare what other APIs offer (e.g. FMX, or game engines like Godot, Unity) in terms of right-to-left languages rendering, as we may want to be consistent.

2 Likes

Regarding RTL languages, I have actually tested CGE, and currently it does not support right-to-left text properly.

FMX has a similar problem. Unfortunately, this issue has not really been fixed over the years. For several years, the usual recommendation has been to use Skia, but I don’t find Skia to be a particularly good solution for this problem either.

I actually have some experience fixing RTL-related issues in FMX myself, so I may be able to provide some useful information when I get to this part of the migration.

I don’t have much experience with how other frameworks or game engines have solved RTL text rendering, so I’m not sure what approach they use. I can investigate that as well when the time comes.

I started the migration with the easier part and created the game board first.

2 Likes

In CGE, the elements look a bit blurry, and adjusting the lighting is quite difficult.

The image below is the output from FMX.


Card movement is much smoother in CGE than in FMX.

** One issue that I just remembered and haven’t tested with version 13 yet (only with the release version): sometimes the table would disappear without any changes to the table itself or even rebuilding the app.

1 Like

Do you maybe use non-power-of-2 textures? For textures, CGE by default resizes them to the nearest “power of 2” size to enable using mipmaps for them.

You can take a look does your log contain messages like this: Resizing 2D texture "%s" from %dx%d to %dx%d

If yes, and you want to avoid it (not scaling to power of 2, also means no mipmaps → but it will not matter if you never zoom too far from the table) you can use TextureProperties.GuiTexture field. After loading the model from 3D file, you want to find all TImageTextureNode in your models (use MyScene.RootNode.EnumNodes, and add to each texture a TTexturePropertiesNode instance with GuiTexture set to true:

TexProperties := TTexturePropertiesNode.Create;
{ Do not force "power of 2" size, which will prevent mipmaps but also prevent scaling to power of 2 size. }
TexProperties.GuiTexture := true;
MyImageTexture.TextureProperties := TexProperties;

Maybe it’s because we use by default PBR materials (TPhysicalMaterialNode) which means PBR lighting equation, and it behaves differently than Phong materials TMaterialNode and Phong lighting model?

You can switch to Phong lighting model to have a version more consistent with what FMX lighting is doing:

  • If you load from glTF, set global GltfForcePhongMaterials from GltfForcePhongMaterials to true
  • If you build models using X3D nodes from Pascal code, just make sure to use TMaterialNode and not TPhysicalMaterialNode

Hope this helps:)

1 Like