Tutorial: Designing a 3D world

1. Introduction

In this chaper we show how you can design 3D world using Castle Game Engine editor. We assume you have already read the overview of the viewport and scenes.

2. Complete tutorial code

The complete result of this tutorial is in the Castle Game Engine. Just open the project examples/3d_rendering_processing/viewport_3d_tutorial in the editor. If at any point you get lost, just look there.

3. Create empty project, add a viewport

  1. Run Castle Game Engine editor, create a new project using the Empty template.

    Create new project

  2. Open the data/gamestatemain.castle-user-interface design using the Files panel at the bottom of the editor. Enter data subdirectory and double-click on gamestatemain.castle-user-interface.

    Open the design

  3. Add a TCastleViewport. Resize it as you like, or just set FullSize to true to have it will the entire window.

    You also want to move it in the hierarchy, such that it is underneath the LabelFps component. This way you will see FPS counter over the viewport.

    Add viewport

4. Add a scene showing the 3D car, adjust camera, lights and navigation

  1. Add a TCastleScene to TCastleViewport.Items. It is easiest to do by right-clicking on the Items in the editor hierarchy (panel on the left side), and choosing Add Transform → Scene (TCastleScene) from the menu that appears.

    Add scene After adding a scene

  2. Now you need some 3D assets to actually play with.

    We advise at this point that you take the sample 3D asset of a car and road we have in our examples. Open in your file manager the examples/3d_rendering_processing/cars_demo/data subdirectory of your Castle Game Engine sources. Select there the files:

    • car.bin

    • car.gltf

    • road.bin

    • road.gltf

    • Whole textures/ subdirectory.

    • Optionally, select also car.blend and road.blend. Castle Game Engine doesn’t read .blend files, but you can modify them in Blender and export to glTF, to play with what you can do in 3D.

    Files to copy

    Copy these files into your new project, into the data subdirectory. It is easiest to open it by right-clicking on data in CGE editor, and choosing Open In File Manager from the context menu that appears.

    If you already have some 3D assets (see 3D assets formats we support) you can of course use at this point already.

  3. Try the preview feature of the editor at this point. Just select (click once) the car.gltf file. If you copied everything correctly (including car.bin and textures/), it will display the 3D car model in a preview window that appears in the bottom-right corner.

    Preview

  4. Set your Scene1.URL property to the car.gltf model in your data. To do this, select Scene1 in the hierarchy (left panel of the editor), then look for URL in the object inspector (right panel of the editor), and click on the small button with 3 dots …​ in the edit field next to the URL. This will open a normal dialog window, where you should select the car.gltf in your project’s data.

    Once you accepted this, note that the Scene1.URL changed the castle-data:/car.gltf. In general, this is an URL of the file. It uses a special castle-data protocol to indicate that the file is in the project’s special data directory.

    Note that instead of clicking on the button with 3 dots, you could have also pasted the URL castle-data:/car.gltf in the edit field. It’s a simple text field, you can edit it like any other text field if you need to, and it will reload the loaded model. You can set the URL to empty always to unload the model.

    Added car

  5. While the car loaded, your camera likely doesn’t show anything pretty. That is because the default camera view doesn’t look at the car in a nice way.

    The default camera stands at the 0 0 0 position, and looks in the -Z direction. You can check it out looking at Viewport1.Camera.InitialPosition and Viewport1.Camera.InitialDirection values in the object inspector (you will probably need to expand the Viewport1.Camera properties). And the car is a model positioned over the 0 0 0 point. So the camera looks underneath the car.

    Initial camera position and direction

    Fix it now using the Camera Current := View All command from the "hamburger" menu of the viewport on top. This menu is available whenever you have selected a TCastleViewport or any transformation within the viewport.

    Menu with camera commands

    Next use the menu Camera Initial (stored in the design file) := Current command, so that the game would also start with the new view. You can look at Viewport1.Camera.InitialPosition and Viewport1.Camera.InitialDirection values again, to see that at least the position have changed.

    Better camera position

  6. That is a better view (at least the whole car is visible) but the car is still completely black. That is because we don’t have any lights in the viewport yet. Solve it by turning on the simple "headlight" (light that shines from the camera). To do this, set Viewport1.Items.UseHeadlight value to hlOn.

    In larger applications, you will probably add more lights, designed at specific positions in the 3D world. To do this, right now you have to design the lights in a 3D authoring application (like Blender) and export them to glTF. Set the scene with lights as Items.MainScene to make the lights shine on everything. In a very near future (beginning of 2022), we want to add lights editing directly to the editor, so it will be easier. For now, for this demo, the "headlight" may be enough.

    After turning on the headlight

  7. While the car is now visible, the default camera view (set by the Camera Current := View All command) isn’t very interesting. to improve it, you shoud use some navigation within the editor, and set more interesting camera. To do this, use Change Navigation → Examine (TCastleExamineNavigation) from the viewport hamburger menu. It will add an instance of TCastleExamineNavigation to your design, and automatically make it active for this viewport.

    Change Navigation

    Now switch to the editor mode "Use components as a normal user" to use the examine navigation, just as normal user of your application would be able to use TCastleExamineNavigation.

    Change Mode

    Now you can:

    • Drag with left mouse button to rotate.

    • Use mouse scroll to zoom in/out. Or drag with right mouse button. Or drag with left mouse button with the Ctrl key pressed.

    • Drag with the middle mouse button to move. Or drag with left mouse button with the Shift key pressed.

    Use these features to set some nice view that shows the entire car.

    Nice view

    Once you’re done use the Camera Initial (stored in the design file) := Current command again, to store the new view (camera position, direction, up) to be used as starting camera view during the game.

  8. Decide if (and how) should the user be able to navigate during the game.

    • For now, we recommend you leave the TCastleExamineNavigation instance (called ExamineNavigation1 in your hierarchy) existing in your design, then the user will be able to change camera just like you did.

    • But you can change the navigation to something else, using the Change Navigation → …​ menu commands.

    • In particular, you can set the navigation to None to disallow user from making any changes to the camera. This is reasonable, if you want to have a constant camera view, or implement the navigation yourself (for example moving/rotating the camera by your own code in TStatePlay.Update).

5. Run the game

Run the game at this point! Just press F9 in the editor.

Game running

6. Play animation

There is a simple animation called wheels_turning defined in the car.gltf model. Let’s configure the scene to automatically play it.

To do this, just edit the AutoAnimation property. You can choose one of the existing animations using the combo box there. Leave the AutoAnimationLoop property at default value of true to make the animation looping.

Playing animation

Note
During the game, you can switch the animation to a different one. You can do this by setting the AutoAnimation and AutoAnimationLoop from code, but usually it is more comfortable to use the PlayAnimation method. Chapter Writing code to modify scenes and transformations shows how to do this.

7. Add a road and more cars

Your viewport may contain many scenes. This is how you can design 3D game levels using the Castle Game Engine editor — just add various TCastleTransform (including TCastleScene) instances to the viewport, and arrange them (move, rotate, scale) as needed.

  1. Add another scene to the viewport (right-click on the Items property of the viewport).

    Call it SceneRoad (edit the Name property in object inspector, or press F2 in the hierarchy to edit the name).

    Set its URL to point to the castle-data:/road.gltf model.

    Add another scene Edit scene name Set road URL

  2. Edit the car scene name too, to make it more obvious in the hierarchy. Right now it is called Scene1, we recommend to edit it to SceneCar now.

  3. Add more cars. While you could add new TCastleScene instances, by repeating the process above, it is easier to use "Duplicate" command available when you righ-click on the SceneCar. (Key shortcut to do this is Ctrl+Shift+D.)

    New car scenes, named like SceneCar1, SceneCar2…​, will appear at the same position as the original car. Use the "Move/Select Transform" tool to show a "gizmo", to move each car to a unique position. Click and drag on one of the arrows to move the car.

    You can observe that moving the car using gizmo also updates it’s Translation property in the Layout tab. You can also move the car by just editing the numbers there, to manually input X, Y, Z movement values.

    Remember that you can adjust the camera to see more 3D space, to keep all the cars in view. Use the Camera Initial (stored in the design file) := Current command to set the best initial camera.

    Duplicate scenes Move scenes View and edit the Translation values as numbers

8. Run the game again

Run the game again at this point, to test that you can view the road and animated cars. Just press F9 in the editor.

Game running with many cars and road

9. Walk around the level

Now that we have constructed a simple 3D world, composed of multiple scenes (cars and road), it would be nice to walk around in this world using typical FPS (first-person shooter) controls.

  1. We recommend first to duplicate the SceneRoad a few times, to have a large floor area to walk on.

    Multiple road scenes to create a large floor

  2. Switch navigation mode to Walk. It will remove the ExamineNavigation1 instance from your design, and add WalkNavigation1.

    Switch to walk navigation

  3. Change the TCastleWalkNavigation.MoveSpeed property of your WalkNavigation1 to move faster (both at design-time, and for user during the game). We recommend to set it to 10 now, which means you will move with speed of 10 units (meters) per second. This is not realistic (people are not that fast), but it allows for comfortable testing. Players in games often move unrealistically fast.

    Adjust walk navigation MoveSpeed

  4. Switch to "Use components as a normal user." mode using the toolbar, if you’re not in this mode already.

  5. Temporarily turn off TCastleWalkNavigation.Gravity property. While gravity doesn’t work anyway at design-time (so your camera will not "fall down" at design time), but turning off Gravity is still useful for now to make the C (move down) and Space (move up) keys work more naturally.

  6. Move around the level now, using the "walk" navigation keys.

    • Use A W S D keys for typical 3D game movement (forward, backward, strafe).

    • Use C key to move down.

    • Use Space key to move up.

    • Drag with left mouse button to move and rotate.

    • Use mouse scroll to raise your head / bow down.

    Using these keys, place the camera at some nice position above the floor, such that it looks at cars.

    Initial camera for walking

  7. Change the TCastleWalkNavigation.Gravity back to true, to make gravity work in game.

  8. Use Camera Initial (stored in the design file) := Current again, to store the current camera as a starting view view for the game.

  9. If your camera is a little tilted to the side, we recommend to reset the initial camera InitialUp to be a straight (0,1,0) vector. This makes movement for user most natural. Select the Viewport1 in the hierarchy, then expand the Camera in the object inspector on the right, and paste 0 1 0 as the value for InitialUp.

    Use Camera Current := Initial (stored in the design file) to set the InitialUp as the current camera up, to immediately preview the newly set camera.

    Reset camera up

  10. Increase the TCastleWalkNavigation.PreferredHeight of the WalkNavigation1 component. The default value is 1.6, which means that player’s height is 1.6 meters when standing — this corresponds to a normal (not very tall) human. For games, reasonable (but still somewhat realistic) values could be considered between 1.6 and 2.0 (players in games are often tall).

    For this demo, for now, we recommend you set this value to a big number: 4.0. This is not realistic at all (humans are rarely 4 meters tall :) ), but it will allow to test walking quickly, and will allow you to "walk over" cars. It also matches the unrealistic

    Adjust PreferredHeight

10. Run the game - final test!

Run the game again with F9. Walk, using the same keys, or mouse dragging, as before. Use mouse scroll to raise / bow your head.

As gravity is active now, the C key does crouch and Space key does jump.

Walk over the cars and jump over them to test collisions and gravity.

Game running with walking

11. Further things

11.1. Lighting

Because of the way headlight works (it is a simple directional light), and the fact that we don’t have any additional lights on the level — the road is completely dark when looking straight. You need to bow your head (use mouse scroll) to have a bit brighter road.

This should be fixed by adding additional lights to the level.

Right now: You can add lights in Blender to the road asset.

  1. Copy road.blend to road_with_lights.blend.

  2. Add lights in Blender to road_with_lights.blend.

  3. Export it to road_with_lights.gltf (be sure to select to include "Lights" at glTF export dialog).

  4. Change one road piece, like the initial SceneRoad, to load from road_with_lights.gltf (instead of road.gltf).

  5. Set SceneRoad as Items.MainScene to make the lights shine on all the other scenes — cars and other road pieces.

Future engine versions of Castle Game Engine will enable to just add lights using the editor.

Adding lights in Blender Exporting lights from Blender Setting new road as MainScene Brighter world with more lights

11.2. "Mouse look" navigation

The walk navigation is easiest if you use mouse look. It will allow to move the mouse to rotate around.

11.2.1. Turn on by default

You can activate this by just setting WalkNavigation1.MouseLook property to true. Setting this property doesn’t change the navigation in the editor, but once you run the game the mouse look will work. The game will grab your mouse, and you will rotate by moving the mouse.

Note that, as we grabbed the mouse but didn’t provide any way to "escape" from it, you cannot easily close the game window by clicking on the usual "close" button on the window frame. Just press Alt+F4 on most systems to close it.

11.2.2. Allow to turn on/off using code

Instead of having the mouse look just active by default, we can make it active e.g. only once you click the right mouse button. Clicking the button again could deactivate it.

Such behavior has to be implemented using the code. Chapter Writing code to modify scenes and transformations describes how to edit the game code. We don’t want to get ahead of ourselves…​ But actually it is quite easy. Just edit the code/gamestatemain.pas file, and

  1. Add CastleCameras unit to the uses clause in the interface.

  2. Declare WalkNavigation1: TCastleWalkNavigation; inside the private section of the TStateMain, under the comment { Components designed using CGE editor, loaded from gamestatemain.castle-user-interface. }.

  3. In TStateMain.Start implementation, add this code:

    WalkNavigation1 := DesignedComponent('WalkNavigation1') as TCastleWalkNavigation;
  4. In TStateMain.Press implementation, add this code:

    if Event.IsMouseButton(buttonRight) then
      WalkNavigation1.MouseLook := not WalkNavigation1.MouseLook;

11.3. More precise collisions

The collisions work correctly in this simple demo, as both the road and cars have simple shapes that can be approximated using boxes. In general, your levels will probably have more complicated shapes. To make any shape work nicely with default navigation collisions, set the Spatial property of your scenes to include the ssDynamicCollisions flag. We recommend to include also the ssRendering flag to optimize the display of scenes with multiple shapes.

Do this for all your scenes, both cars and roads. Note that you can select multiple scenes in the hierarchy with Ctrl and then edit the Spatial property of all selected scenes at one.

Edit Spatial of all scenes

11.4. Multiple instances of the same scene (using TCastleTransformReference)

It is possible to optimize the usage of resources, when you want to instantiate the same resource multiple times, and each instance has the same state (which means that it is static, or plays the same animation).

  1. Remove the car copies you made with "Duplicate" command earlier. You’ll add copies in a different way. Leave only one car, SceneCar.

  2. To make things more intuitive in the following points, remove any translation you may had on SceneCar. You can do this using "Reset Transform" in the "Layout" tab of the object inspector. You could also just type 0 0 0 into the SceneCar.Translation value.

    Note
    If you need to translate even this one car, then add an intermediate transformation like TransformCar. Add this to Viewport.Items, and drag SceneCar to be a child of TransformCar. Then you can change translation of TransformCar to move your 1st car.
    Note
    You can also just disregard this advise, and leave non-zero translation on SceneCar. It works, but it means that each reference also has a shift — you’ll see what we mean by this next. It may be non-intuitive at the beginning.
  3. Add a TCastleTransformReference to the Viewport.Items.

    1. Set it’s Reference property to point to the SceneCar.

    2. And move it wherever you like.

    3. Change it’s name to be something better than default TransformReference1. We propose to change it to SceneCarReference1.

  4. Duplicate the SceneCarReference1 as many times as you want to, and translate each instance differently.

Done. Now you can have lots of cars — yet they all share the same data, by referencing (in memory) the same SceneCar instances. This is great to conserve resources and speed when you need a lot of instances of something. E.g. this would be great for trees on a big outdoors level.


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.