How to render 2D games with images and sprites

Platformer

1. How to render 2D things

Warning
This page compares 2 possible approaches to 2D rendering in CGE. This page is really not a good introduction into "making 2D games in CGE". For such introduction, instead follow the manual from the beginning, in particular learn about viewports, scenes and how to use them for 2D.

Use TCastleViewport. Add TCastleViewport control to the window, and inside it draw things by creating TCastleTransform descendants (like TCastleScene, TCastleImageTransform, TCastlePlane).

You should call TCastleViewport.Setup2D on the viewport to easily make it suitable for 2D games, and call TCastleScene.Setup2D to easily make it suitable for 2D games. When using CGE editor, it already has in the menu components "Viewport (2D)" and "Scene (Optimal Blending for 2D Models)".

Advice when to use:

  • This approach is extremely versatile, so this is the approach I advice if you plan to draw something more than images.

  • This approach is also easy, if you mostly load 2D models from existing files (Spine JSON, X3D exported from Blender or sprite-sheet-to-x3d). But it is a little more work to construct your own X3D graph — as there are simply a lot of X3D nodes that you can use. But it pays off in my experience, you really can do everything. Our Cat-astrophe Games games ("Dragon Squash", "Escape from the Universe", "The Unholy Society") are all implemented using this approach.

  • This approach allows engine to take care of animations, physics, and other cool stuff for you.

1.2. Simple (imperative) option: Render using TDrawableImage

Use TDrawableImage as your main way to draw. In this approach, you create TDrawableImage instance for each image, and then draw it in overridden TCastleUserInterface.Render method. This is the same approach as we use for our user-interface rendering (various TCastleUserInterface instances). The main advantage of this approach is simplicity: you just draw 2D images.

A similar approach is to draw your game using multiple TCastleImageControl instances. TCastleImageControl is a simple user-interface control that draws images, using TDrawableImage under the hood, exposing mostly the same features.

  • Manual:

  • API reference: TDrawableImage.

  • You can render sprites using the appropriate TDrawableImage.Draw overload (where you specify which part of the source image to render). There’s also a class TSprite (it uses TDrawableImage underneath), but it is deprecated now, please don’t use it.

  • To render pixel-art, set TDrawableImage.SmoothScaling to false.

  • Numerous engine demos use TDrawableImage. Example isometric_game draws simple map using it.

  • Besides TDrawableImage there are also simple drawing helpers like DrawRectangle.

  • To have fixed resolution (regardless of the actual window size in pixels), use UI scaling. However, while it scales correctly all the existing TCastleUserInterface instances, you will need to put some additional work to make UI scaling affect your custom TCastleUserInterface descendant. This is documented at the bottom of http://castle-engine.io/manual_2d_ui_custom_drawn.php — basically, look at your ScreenRect and scale all coordinates by UIScale before passing them to TDrawableImage. So the scaling is not hidden from you in this case — you get the information you need, but you need to put some code to make it happen correctly.

    To have the scaling automatically applied, you can use TCastleImageControl instead of directly drawing with TDrawableImage. TCastleImageControl is an UI control that wraps TDrawableImage underneath, and allows to control the image like a normal UI control: with anchors, automatically applied scaling and so on.

  • To use custom shader, set TDrawableImage.CustomShader or TCastleImageControl.CustomShader. Demo in examples/images_videos/image_render_custom_shader.lpr.

  • You can use batching using TDrawableImage.BatchingBegin and TDrawableImage.BatchingEnd. It works completely automatically by grouping rendering the same image many times into one draw call.

Advice when to use: This approach is very easy to start. You have relatively small API to learn. You just learn how to use TDrawableImage, and you draw inside your own TMyControl.Render however you like. If all you really want is a flexible API to draw images — this is it.

2. Why there are 2 approaches to render 2D images/sprites

Because they are both useful :)

  • Drawing using TDrawableImage is imperative.

  • Settings things up using TCastleScene is declarative.

The declarative approach is more powerful (the engine can do automatically a lot of more stuff for you, this way).

The imperative stuff is simpler to use, and enough for simple use-cases. I wondered about removing this approach, but it seems many people like it, and it is enough for many use-cases.

3. Can these methods be combined, to render using TDrawableImage within TCastleScene?

I plan to enable rendering using TDrawableImage into a TCastleScene one day. Then you could render user interface into TCastleScene, rotate this TCastleScene, and have easy user-interface in 3D. This is part of roadmap .


To improve this documentation just edit this page and create a pull request to cge-www repository.