In the Blank template, most of the work is done inside the
Game class, which is the starting point for the application. However, in the Rolling Ball template we have used a different approach to create the behavior.
Game class is now only used to load a first level and to register if the ESC-key is pressed (so that the application can be closed). Most of the logic is placed in the
Player class which is an EntityComponent. Since the
Player is an
EntityComponent, it can be added to an Entity in the Sandbox. This is achieved by pressing Components in the Create Object window and then dragging the
Player (new components will be in the General category by default) into your scene. If you already have an Entity ready for the
Player, you can also select the Entity and use the Add Component button in the properties window to add the
Player-component to the Entity.
Player class is responsible for handling visuals, physics and the input of the player (in this case a ball that can roll around). Since the
Player is an EntityComponent, it can be added to an Entity in the level in the Sandbox from the Add Component menu. The
Player will now be saved automatically whenever the level is saved, and if the game is launched it will be automatically set on the Entity with all of its properties set to the right value. The properties of the
Player are exposed by the EntityPropertyAttribute which are explained in more detail in the EntityProperties chapter.
Once instantiated, the
Player class will set its visuals and physicalize itself. The visuals are handled by the
SetPlayerModel method, which calls LoadGeometry and LoadMaterial with the values that are set according to the
PlayerMaterialUrl. Once the visuals are rendered, it will Physicalize itself by calling the
PrepareRigidbody method where it will set the
Mass of the entity and set the physicalization type to Rigid. Rigid means the object is solid and can move around. By setting it to rigid the
Player is able to move around and have collisions with other physicalized objects. If the
Mass is changed in the Sandbox or during runtime it will run
PrepareRigidbody again to update the
Mass of the PhysicsEntity. For objects that shouldn't move around the physicalization type should be set to Static.
The Player receives its update calls in
OnUpdate, which is called every frame. The EntityComponent has several virtual methods that can be overridden, such as
On each frame, the Player's movement is handled by the
UpdateMovement method. First the user-input is gathered through the Input class. Next the forward direction of the camera is retrieved and transformed (so it will not point up or down). After the forward is calculated then the correct direction of the camera can be calculated using Vector3.Cross in combination with the world's up direction. Once both the forward and the correct direction of the camera have been calculated, then they can be used to add or subtract from the impulse direction (depending on the input that was retrieved earlier). Finally, the impulse direction is multiplied with the
FrameTime to make it framerate independent and the impulse is set to the
After updating the position of the
Player it will update the camera's position. This is handled by the
PlayerView class which is explained in more detail in the PlayerView chapter.
PlayerView class is responsible for moving the camera to the correct position. However, because the
PlayerView is not an
EntityComponent it needs to receive its update calls from the
Player manually. Furthermore, the
PlayerView also uses the
Camera class to calculate and modify the camera position in a scene. It starts by getting the camera's rotation as yaw, pitch and roll axes. The yaw and pitch rotations are changed based on the mouse movement, while the roll rotation is completely removed. The angles are then set back to the rotation of the
Camera. Finally, the forward direction of the
Quaternion is multiplied with the
ViewDistance and subtracted from the
Player's position to set the camera in the correct position.
Some interesting things to note regarding the
- As it is not an EntityComponent it is unable to make use of the EntityPropertyAttribute.
- As a result of 1. a decision was made to store the various properties that the
PlayerViewuses in the
Playerinstead. This way settings can still be fine-tuned in the Sandbox.
Player class has several properties that can be adjusted in the Sandbox. This is done by adding the EntityPropertyAttribute to the properties.
In the template the following properties are already exposed.
Mass- Mass of the Player entity in kg.
MoveImpulseStrength- Strength of the per-frame impulse when holding inputs.
RotationSpeedYaw- Speed at which the Player rotates the camera yaw.
RotationSpeedPitch- Speed at which the Player rotates the camera pitch.
RotationLimitsMinPitch- Minimum limit to the camera pitch.
RotationLimitsMaxPitch- Maximum limit to the camera pitch.
ViewDistance- Determines the distance between the Player and camera.
All of these properties are float properties, but the EntityPropertyAttribute can be used to expose a wide variety of types. By default it will assume the property's type is a primitive type (
bool). By changing the first parameter in the EntityProperty you can adjust how the value is represented in the Sandbox. For example, if you want to also expose the
PlayerGeometryUrl you could do it by using the default
EntityPropertyType.Primitive, but you can also choose to change it to
EntityPropertyType.Geometry. This will still create a text field, but it also gives you the option to open a file-browser to select a file to which the property should refer. For the
PlayerGeometryUrl this will look like the following code.