How to render 2D images/sprites

Platformer

1. How to render 2D images/sprites

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 TCastleScene instances.

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 (Configured For 2D)" and "Scene (Optimal Blending for 2D Models)".

  • Manual:

  • The engine template "2D game" and physics/physics_2d_game_sopwith/ show this approach.

  • The important API reference links: TCastleScene, TCastleSceneCore, TCastleViewport.

  • To construct your own scene (not loaded from external file), you need to create a graph of X3D nodes. Various examples showing it are around the engine, the simpler is http://castle-engine.io/x3d_implementation_geometry2d.php . It instantiates a TRectangle2DNode. One could instead instantiate also TIndexedFaceSetNode which represents a free mesh, with custom texture coordinates and anything else you may need.

  • To render sprites you can load sprite sheets from Starling/Cocos2d formats to TCastleScene. You can play it’s animation just like any other scene (use Scene.PlayAnimation method, test with view3dscene panel "Animations").

    If you want to do it manually, you can also construct your own TIndexedFaceSetNode instances and change texture coordinates.

  • To render pixel-art, set texture filtering to "nearest". It’s easiest to do this by setting Scene.Attributes.MagnificationFilter := magNearest. Alternatively, you could control this on a particular shape using TTexturePropertiesNode, links on http://castle-engine.io/x3d_implementation_texturing.php .

  • You can also load image as a TCastleScene.

  • To have fixed resolution (regardless of the actual window size in pixels), set TCastleViewport.Camera.Orthographic.Width and/or TCastleViewport.Camera.Orthographic.Height. By default they are both zero (which means they follow window size), but if you set them then you explicitly specify the size. Set exactly one of them to non-zero to make the other one automatically adjusted to follow aspect ratio.

    See the "2D game" game template for example.

    You can also use UI scaling ( http://castle-engine.io/manual_2d_user_interface.php#section_scaling ) to just make TCastleViewport have the same width or height, regardless of the actual window size. All the scaling is completely hidden from you, inside TCastleViewport you just work in your preferred coordinates.

  • You can use physics in this approach (see example physics/physics_2d_game_sopwith/).

  • You can mix 2D and 3D freely. The TCastleViewport.Camera defines projection settings and controls camera. The orthographic camera, TCastleViewport.Camera.Orthoraphic, is usually what you want for 2D games. Note that you can insert a TCastleScene with 3D content to TCastleViewport.

  • To use custom shaders, use X3D shader nodes.

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: 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). Or use ready class TSprite (it uses TDrawableImage underneath).

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

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

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

  • To have fixed resolution (regardless of the actual window size in pixels), use UI scaling ( http://castle-engine.io/manual_2d_user_interface.php#section_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.

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 the source of this page in AsciiDoctor (simple wiki-like syntax) and create a pull request to Castle Game Engine WWW (cge-www) repository.