2D Games

Michalis Kamburelis edited this page Aug 23, 2018 · 15 revisions

There are 2 basic methods to draw 2D stuff in Castle Game Engine:

Simple: Render using TGLImage

Use TGLImage as your main way to draw. In this approach, you create TGLImage instance for each image, and then draw it in overridden TUIControl.Render method. This is the same approach as we use for our user-interface rendering (various TUIControl 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 TGLImage under the hood, exposing mostly the same features.

  • Manual:

  • The important API reference links: TGLImage and TGLImageCore. TGLImageCore is an ancestor of TGLImage (these 2 classes may be merged into one during CGE 6.6 development; just use TGLImage in your applications and freely use methods documented at both TGLImage and TGLImageCore reference). I sometimes use the name TGLImage and TGLImageCore interchangeably here, as I already merged these two classes into one in my head :)

  • You can render sprites using the appropriate TGLImage.Draw overload (where you specify which part of the source image to render). Or use ready class TSprite (it uses TGLImage underneath).

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

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

  • Besides TGLImage 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 TUIControl instances, you will need to put some additional work to make UI scaling affect your custom TUIControl 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 TGLImage. 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 TGLImage. TCastleImageControl is an UI control that wraps TGLImage 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 TGLImage.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 TGLImage, 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.

Full-featured: Render using TCastle2DScene

Use TCastle2DSceneManager (or T2DSceneManager in CGE <= 6.4). Add TCastle2DSceneManager control to the window, and inside it draw things by creating TCastle2DScene instances.

  • Manual:

  • The engine examples 2d_dragon_spine_game and physics/physics_2d_game_sopwith/ show this approach.

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

  • 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.

  • You can render sprites by constructing your own TIndexedFaceSetNode instances and changing texture coordinates. Or you can use existing utility tools/sprite-sheet-to-x3d/ that converts sprite sheets in various formats to an X3D model. Such model can then be easily loaded to TCastle2DScene, you can play it's animation (using Scene.PlayAnimation method, test with view3dscene menu "Animations -> Names Animations -> ..."). You can look at the X3D file using a text editor to see how the "sprite" is really just an IndexedFaceSet node with animated 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 .

  • To have fixed resolution (regardless of the actual window size in pixels), set TCastle2DSceneManager.ProjectionAuto to false and set either ProjectionHeight or ProjectionWidth. See the 2d_dragon_spine_game example. Or you can use UI scaling ( http://castle-engine.io/manual_2d_user_interface.php#section_scaling ) to just make TCastle2DSceneManager have the same width or height, regardless of the actual window size -- in this case, the projection width or height will be the same, even if you leave TCastle2DSceneManager.ProjectionAuto as true. All the scaling is completely hidden from you, inside TCastle2DSceneManager 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. TCastle2DSceneManager is a descendant of a general TCastleSceneManager. The TCastle2DSceneManager defines projection settings and controls camera in a way that is usually most comfortable for 2D games, but it does not limit the rendering to 2D in any way. You can insert a TCastleScene with 3D content to TCastle2DSceneManager.

    (Or you could insert a TCastle2DScene into a 3D world in TCastleSceneManager. Really, every combination works. The "2D" classes can be mixed, and can even contain, 3D content. The "2D" classes add some comfortable stuff to make 2D rendering more natural (see API docs), but it's nothing limiting.)

  • To use custom shader, use X3D shader nodes. Demo in examples/3d_rendering_processing/display_box_custom_shaders.lpr. You can use ComposedShader node and friends (that override standard engine shaders, see https://castle-engine.io/x3d_implementation_shaders.php ) or Effect node and friends (that extend standard engine shaders, see https://castle-engine.io/compositing_shaders.php ). Various demos as X3D scenes are inside our demo models, see in particular shaders and compositing_shaders subdirectories.

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.

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.