Character Controlling

For most games or apps a character / camera controlling is necessary to move in 3D space. This section handles firstperson- and thirdperson character controlling and all expandable components. The framework makes it very easy to implement such behaviour. Without huge coding you can setup everything at design-time.

The TGorillaCharacterController is the basis class for TGorillaFirstPersonController and TGorillaThirdPersonController, but should not be used directly, because it's only an abstraction layer for shared functionality.

All character controllers receive inputs from a TGorillaInputController instance. By those inputs a linked camera component will be moved or users are allowed to interact with your game.

Hotkeys

Because we've linked an input controller to the character controller, we have to define hotkeys to react on.

In short this means: if the input controller detects a hotkey, it will forward the event to the character controller. And then the character controller will react on the hotkey event by a specific operation, like moving forward or shooting.

By default the character controllery already defines standardized hotkeys, like W-A-S-D for movement. But you're allowed to overwrite those hotkeys by your own.

Predefined input hotkeys by the TGorillaCharacterController class are:

Predefined

NAME CODE DEVICE
KEY_FORWARDGORILLA_INPUT_KEY_WKeyboard
KEY_BACKWARDGORILLA_INPUT_KEY_SKeyboard
KEY_LEFTGORILLA_INPUT_KEY_AKeyboard
KEY_RIGHTGORILLA_INPUT_KEY_DKeyboard
KEY_JUMPGORILLA_INPUT_KEY_SPACEKeyboard
KEY_CROUCHGORILLA_INPUT_KEY_CTRL_LEFTKeyboard
KEY_CRAWLGORILLA_INPUT_KEY_ALTGRKeyboard
KEY_BOOSTGORILLA_INPUT_KEY_SHIFT_LEFTKeyboard
KEY_AIMGORILLA_INPUT_KEY_ALTKeyboard
KEY_CUSTOM1Code: GORILLA_INPUT_KEY_QKeyboard
KEY_CUSTOM2Code: GORILLA_INPUT_KEY_EKeyboard
KEY_CUSTOM3Code: GORILLA_INPUT_KEY_FKeyboard
KEY_CUSTOM4Code: GORILLA_INPUT_KEY_CKeyboard
KEY_STARTCode: GORILLA_INPUT_KEY_ESCAPEKeyboard
KEY_MODECode: GORILLA_INPUT_KEY_PAUSEKeyboard
KEY_BACKCode: GORILLA_INPUT_KEY_SHIFT_BACKSPACEKeyboard
KEY_MENU_UPGORILLA_INPUT_KEY_ARROW_UPKeyboard
KEY_MENU_DOWNGORILLA_INPUT_KEY_ARROW_DOWNKeyboard
KEY_MENU_LEFTGORILLA_INPUT_KEY_ARROW_LEFTKeyboard
KEY_MENU_RIGHTGORILLA_INPUT_KEY_ARROW_RIGHTKeyboard
MOUSE_LEFTBUTTONGORILLA_INPUT_MOUSE_LBUTTONMouse
MOUSE_MIDDLEBUTTONGORILLA_INPUT_MOUSE_MBUTTONMouse
MOUSE_RIGHTBUTTONGORILLA_INPUT_MOUSE_RBUTTONMouse
GAMEPAD_MOVE0GamePad
GAMEPAD_MOVE_FORWARDGORILLA_INPUT_GAMEPAD_DPAD_UPGamePad
GAMEPAD_MOVE_BACKWARDGORILLA_INPUT_GAMEPAD_DPAD_DOWNGamePad
GAMEPAD_MOVE_LEFTGORILLA_INPUT_GAMEPAD_DPAD_LEFTGamePad
GAMEPAD_MOVE_RIGHTGORILLA_INPUT_GAMEPAD_DPAD_RIGHTGamePad
GAMEPAD_JUMPGORILLA_INPUT_GAMEPAD_SHOULDER_RIGHTGamePad
GAMEPAD_CROUCHGORILLA_INPUT_GAMEPAD_THUMBSTICK_RIGHTGamePad
GAMEPAD_CRAWLGORILLA_INPUT_GAMEPAD_THUMBSTICK_LEFTGamePad
GAMEPAD_BOOSTGORILLA_INPUT_GAMEPAD_SHOULDER_LEFTGamePad
GAMEPAD_AIMGORILLA_INPUT_GAMEPAD_THUMBSTICK_LEFTGamePad
GAMEPAD_BUTTONAGORILLA_INPUT_GAMEPAD_BUTTON_AGamePad
GAMEPAD_BUTTONBGORILLA_INPUT_GAMEPAD_BUTTON_BGamePad
GAMEPAD_BUTTONXGORILLA_INPUT_GAMEPAD_BUTTON_XGamePad
GAMEPAD_BUTTONYGORILLA_INPUT_GAMEPAD_BUTTON_YGamePad
GAMEPAD_STARTGORILLA_INPUT_GAMEPAD_STARTBUTTONGamePad
GAMEPAD_MODEGORILLA_INPUT_GAMEPAD_MODEBUTTONGamePad
GAMEPAD_BACKGORILLA_INPUT_GAMEPAD_BACKBUTTONGamePad
GAMEPAD_MENU_UPGORILLA_INPUT_GAMEPAD_DPAD_UPGamePad
GAMEPAD_MENU_DOWNGORILLA_INPUT_GAMEPAD_DPAD_DOWNGamePad
GAMEPAD_MENU_LEFTGORILLA_INPUT_GAMEPAD_DPAD_LEFTGamePad
GAMEPAD_MENU_RIGHTGORILLA_INPUT_GAMEPAD_DPAD_RIGHTGamePad

Remark: All available input codes are defined in Gorilla.Controller.Input.Consts.pas.

User-Friendly Events

For individual treatment you can disable or overwrite those predefined hotkeys. The basic character controller component catches all input events, handles them and throws back user-friendly events.

Event Description
OnIdle OnIdle event getting raised when first person controller switches to idle mode.
OnMove OnMove event getting raised in doOnMoveForward, doOnMoveBackward, doOnMoveLeft, doOnMoveRight and DoOnGamePadThumbstickPos methods, used by keyboard hotkeys and gamepad thumbstick movement.
OnRotate OnRotate event getting raised if the view was rotated by DoOnGamePadThumbstickPos or DoOnMouseMove.
OnBoost OnBoost event getting raised when boost mode was activated or deactivated.
OnCrouch OnCrouch event getting raised when crouching was activated or deactivated.
OnCrawlOnCrawl event getting raised when crawling was activated or deactivated.
OnJumpOnJump event getting raised when first person controller is jumping.
OnAim OnAim event getting raised when aiming mode was activated or deactivated.
OnPush OnPush event getting raised when pushing mode was activated or deactivated.
OnPull OnPull event getting raised when pulling mode was activated or deactivated.
OnCustomAction OnCustomAction event getting raised by keyboard custom action hotkeys and gamepad buttons.
OnClick OnClick event getting raised by clicking left, middle or right mouse button.
OnStart OnStart event getting raised when start hotkey was activated.
OnMode OnMode event getting raised when mode hotkey was activated.
OnBack OnBack event getting raised when back hotkey was activated.
OnNavigate OnNavigate event getting raised when menu navigation was triggered.
OnHotKey OnHotKey event getting raised when a registered hotkey was triggered. It's a central callback event for all hotkeys. Parallel to it hotkey specific events will be raised: OnIdle, OnMove, OnBoost, OnCrouch, OnJump, OnCustomAction, OnClick, OnStart, OnMode, OnBack and OnNavigate
OnTriggerPoint If a TGorillaTriggerPointManager component was linked to the first person controller, a trigger-point check will automatically be performed on movement. If then a trigger-point is in reach, the OnTriggerPoint event will be raised.

Movement

Because movement is not always equal in every game, we implemented some easy-to-use property to control it.

Property Description
LockXAxis Lock or unlock X-axis for movement. Enable this property if you don't want the character to force movement on x direction.
LockYAxis Lock or unlock Y-axis for movement. Enable this property if you don't want the character to force movement on y direction.
LockZAxis Lock or unlock Z-axis for movement. Enable this property if you don't want the character to force movement on Z direction.
MoveSideways Activate or deactive to move character sideways on move-left/right hotkeys. If this mode is deactivated, the character will rotate on move-left/right hotkey instead.

FirstPerson Controller

The TGorillaFirstPersonController controls movement of a linked camera component in its basic settings. But you're allowed to add sub controls like a TGorillaModel to display arms or weapons as known in typical ego shooter games.

You can set up a firstperson controller at design-time or at runtime by:

[...]
 
  protected
    FInputCtrl : TGorillaInputController;
    FFirstPersonCtrl : TGorillaFirstPersonController;
 
[...]
 
uses
  Gorilla.Controller.Input.Types,
  Gorilla.Controller.Input.Character,
  Gorilla.Controller.Input.FirstPerson;
 
[...]
 
FInputCtrl := TGorillaInputController.Create(Self);
FInputCtrl.Supported := [TGorillaInputDeviceType.Keyboard, TGorillaInputDeviceType.Mouse];
FInputCtrl.Enabled := true;
 
FFirstPersonCtrl := TGorillaFirstPersonController.Create(FViewport);
FFirstPersonCtrl.Parent := FViewport;
FFirstPersonCtrl.InputController := FInputCtrl;
FFirstPersonCtrl.Camera := FCamera;

You have some properties to optimize or configure your specific character movement:

Property Description
MouseControllerSpeedGet or set mouse controller speed. The value reduces movement speed on mouse interaction. Default value is 0.075.
GamePadThumbstickSpeedGet or set gamepad controller speed, when using thumbsticks to move. The value reduces movement speed on gamepad interaction. Default value is 0.5.
Speed Get or set speed of movement. A basis speed value applied to direction vector. Default value is 1.0.
CrouchingSpeedReductionFactor of reduction on speed if state is fpCrouching. Values between 0.0 - 1.0 are allowed. Default value is 0.15.
RunningSpeedBoostBoost factor on speed if state is fpBoost. Values between 1-1000 are allowed. Default value is 2.0.
JumpingHeight Get or set jumping height value. The value defines the height of jumping. Default value is 4.0.
CrouchingHeight
ShowCursor You can show/hide mouse cursor while first person controller is running. (not working properly)
RotationDragMode Get or set FRotationDragMode value, which defines if camera rotation is only been applied, if mouse down is active.
UseDefaultBehaviour Activate / Deactivate default behaviour on hotkeys triggered.
UseDefaultMovement Activate / Deactivate default movement behaviour. This flag allows to disable default HotKey (W-A-S-D keys) / Gamepad (thumbstick) movement. This has no influence on camera rotation.

ThirdPerson Controller

The TGorillaThirdPersonController is an extension of the TGorillaFirstPersonController but changes camera movement. It's the typical character controller when having a character model with a top down look by camera.

[...]
 
  protected
    FInputCtrl : TGorillaInputController;
    FThirdPersonCtrl : TGorillaThirdPersonController;
 
[...]
 
uses
  Gorilla.Controller.Input.Types,
  Gorilla.Controller.Input.Character,
  Gorilla.Controller.Input.ThirdPerson;
 
[...]
 
FInputCtrl := TGorillaInputController.Create(Self);
FInputCtrl.Supported := [TGorillaInputDeviceType.Keyboard, TGorillaInputDeviceType.Mouse];
FInputCtrl.Enabled := true;
 
FThirdPersonCtrl := TGorillaThirdPersonController.Create(FViewport);
FThirdPersonCtrl.Parent := FViewport;
FThirdPersonCtrl.InputController := FInputCtrl;
FThirdPersonCtrl.Camera := FCamera;

Optionally linkable components

It's usual to handle different things during character controlling. So you'd like to check for special positions in 3D space and execute actions when getting triggered. Or you like to playback sounds on specific states or refer to a sound-map.

The following components are allowed to link to a character controller:

Physics Character Controller

A TGorillaPhysicsCharacterController is a helper layer to manage physics engine, character controller and animation controller. The component handles all the complex management of collision detection and animation playback of a model on basis of a firstperson/thirdperson character controller. Without any programming it's possible to implement a game-typical character controlling, where animations switched automatically on different interactions. On top there is physics collision detection (capsule collider).

Besides easy-to-drop designtime handling, you can setup a physics character controller at runtime.

  [...]
 
  FAssetsManager : TGorillaAssetsManager;
  FMainPackage : TGorillaAssetsPackage;
 
  FPhysics : TGorillaPhysicsSystem;
  FAudioManager : TGorillaFMODAudioManager;
 
  FCharacter : TGorillaModel;
  FPhysCtrl : TGorillaPhysicsCharacterController;
  FAnimCtrl : TGorillaAnimationController;
  FInputCtrl : TGorillaInputController;
  FThirdPersonCtrl : TGorillaThirdPersonController;
  FAudioCtrl : TGorillaAudioManagerController;    
  FTriggerPoints : TGorillaTriggerPointManager;
 
  [...]
 
procedure TGameWindow.LoadCharacter(const AAssets : String);
var LPath : String;
    j : Integer;
    LLay : TGorillaAnimationTransitionLayer;
    LTP : TGorillaTriggerPoint;
begin
  // setup character assets path
  LPath := AAssets + IncludeTrailingPathDelimiter('mymodel');
 
  // create physics character controller (PCC)
  // a PCC is able to connect a character controller with a physics system
  // because with the terrain a collider was added, so that we can walk
  // with our character on it. the PCC will automatically add a capsule collider
  // for collision detection
  FPhysCtrl := TGorillaPhysicsCharacterController.Create(FViewport);
  FPhysCtrl.Parent := FViewport;
  // here we can control the gravity - default value = 25
  FPhysCtrl.GravityScale := 3;
 
  // create character model
  // this will just be a visual instance for our character
  // the controlling will be done by the physics character controller and the
  // third person character controller
  FCharacter := TGorillaModel.LoadNewModelFromFile(FPhysCtrl, FMainPackage,
        LPath + 'mymodel.dae', []);
  FCharacter.Parent := FPhysCtrl;
  // we don't want to interact with the model - so we deactivate any raycast tests.
  FCharacter.SetHitTestValue(false);
  // we always want to see our character, so we disable frustum culling check
  // nevertheless this should not be necessary
  FCharacter.FrustumCullingCheck := false;
  // position character model inside of PCC capsule collider (may be obsolete - depending on your model)
  FCharacter.RotationAngle.Point := Point3D(-180, 90, 0);
  FCharacter.Position.Point := Point3D(0, 8, 0);
  FCharacter.Scale.Point := Point3D(0.75, 0.75, 0.75);
 
  // load all animations used by animation controller
  FCharacter.AddAnimationFromFile(LPath + 'mymodel-idle.dae');
  FCharacter.AddAnimationFromFile(LPath + 'mymodel-walk-forward.dae');
  FCharacter.AddAnimationFromFile(LPath + 'mymodel-walk-backwards.dae');
  FCharacter.AddAnimationFromFile(LPath + 'mymodel-walk-left.dae');
  FCharacter.AddAnimationFromFile(LPath + 'mymodel-walk-right.dae');
  FCharacter.AddAnimationFromFile(LPath + 'mymodel-run-forward.dae');
  FCharacter.AddAnimationFromFile(LPath + 'mymodel-run-backwards.dae');
  FCharacter.AddAnimationFromFile(LPath + 'mymodel-run-left.dae');
  FCharacter.AddAnimationFromFile(LPath + 'mymodel-run-right.dae');
 
  [...]
 
  // we start the first animation in our model - this should be the basis animation
  // inside of the first model file.
  if Assigned(FCharacter.AnimationManager) and Assigned(FCharacter.AnimationManager.Current) then
    FCharacter.AnimationManager.Current.Start();
 
  // animation controller setup
  // create animation controller for handling animation transitions
  // animation transitions define how to switch from one animation to another
  // and which inputs / hotkeys are triggering
  FAnimCtrl := TGorillaAnimationController.Create(FViewport);
  // link the model to the animation controller, to apply animation transitions to
  FAnimCtrl.Model := FCharacter;
 
  // add all defined animation transitions
  LLay := FAnimCtrl.AddLayer('WalkingAnimations');
  LLay.AddTransition('idle-forward > walk-forward', 'mymodel-idle.dae', 'mymodel-walk-forward.dae', true, KeyboardMoveForward, [fpMoving], tmImmediatly);
  LLay.AddTransition('idle-backward > walk-backward', 'mymodel-idle.dae', 'mymodel-walk-backward.dae', true, KeyboardMoveBackward, [fpMoving], tmImmediatly);
  [...]
 
  // set the default animation to be played at the beginning
  FAnimCtrl.DefaultAnimation := 'mymodel-idle.dae'; // the filename of your idle animation
 
  // create an audio manager controller to automatically playback sounds on
  // specific hotkey actions
  FAudioCtrl := TGorillaAudioManagerController.Create(Self);
  FAudioCtrl.AudioManager := Self.FAudioManager;
  FAudioCtrl.AddHotKeyMapping('RUN_FORWARD',  SOUND_RUN,
        TGorillaCharacterControllerHotKey.KeyboardMoveForward, [fpMoving,fpBoost]);
  FAudioCtrl.AddHotKeyMapping('RUN_BACKWARD', SOUND_RUN,
        TGorillaCharacterControllerHotKey.KeyboardMoveBackward, [fpMoving,fpBoost]);
  FAudioCtrl.AddHotKeyMapping('WALK_FORWARD',  SOUND_WALK,
        TGorillaCharacterControllerHotKey.KeyboardMoveForward, [fpMoving]);
  FAudioCtrl.AddHotKeyMapping('WALK_BACKWARD', SOUND_WALK,
        TGorillaCharacterControllerHotKey.KeyboardMoveBackward, [fpMoving]);
 
  // create a trigger points manager and some coordinates to be triggered
  FTriggerPoints := TGorillaTriggerPointManager.Create(Self);
  // we don't want to check against camera, but againts the character model
  FTriggerPoints.RelatedControl := FCharacter;
  FTriggerPoints.Enabled := true;
 
  // wheel #1 trigger point
  LTP := FTriggerPoints.AddTriggerPoint('Wheel1', Point3D(-10, 0, 10), 0.25, 30);
  LTP.LookInDirection := false;
  LTP.Kind := TGorillaTriggerPointKind.StaticTriggerPoint;
  LTP.Data := TValue.From<String>('Press [E] to rotate wheel #1');
  LTP.Tag  := 1;
  LTP.OnTriggered := DoOnWheelTriggered;
  LTP.OnUnTriggered := DoOnWheelUnTriggered;
 
  // wheel #2 trigger point
  LTP := FTriggerPoints.AddTriggerPoint('Wheel2', Point3D(15, 0, -20), 0.25, 30);
  LTP.LookInDirection := false;
  LTP.Kind := TGorillaTriggerPointKind.StaticTriggerPoint;
  LTP.Data := TValue.From<String>('Press [E] to rotate wheel #2');
  LTP.Tag  := 2;
  LTP.OnTriggered := DoOnWheelTriggered;
  LTP.OnUnTriggered := DoOnWheelUnTriggered;
 
  // position the physics controller in 3D physics space - we let the capsule
  // collider drop from -75.0 onto the terrain.
  FPhysCtrl.Position.Point := Point3D(0, -5, 0);
  // link physics controller with character
  FPhysCtrl.Model := FCharacter;
  // resize the capsule collider for better scaled size regarding the scene size
  FPhysCtrl.SetSize(6, 16, 6);
 
  // create a third-person controller
  // this is the basic input handler for characters
  // this will control movement depending on inputs (by the input controller)
  FThirdPersonCtrl := TGorillaThirdPersonController.Create(FPhysCtrl);
  // attach the character controller to the physics character controller (PCC)
  // so it can automatically move it on interaction
  FThirdPersonCtrl.Parent := FPhysCtrl;
  // link the camera to the controller for automatic camera movement
  FThirdPersonCtrl.Camera := FCamera;
  // link the input controller to process all keyboard inputs, to let the
  // character move, jump or whatever
  FThirdPersonCtrl.InputController := FInputCtrl;
  // link the audio manager controller for automatic sound playback on hotkeys
  FThirdPersonCtrl.AudioManagerController := FAudioCtrl;
  // link triggerpoints manager for auto-detection of game coordinates
  FThirdPersonCtrl.TriggerPoints := FTriggerPoints;
 
  // deactivate default behaviour, because we're attaching the character controller
  // to the physics character controller and the physics system
  // we need to deactivate to allow physics system interaction with the character
  FThirdPersonCtrl.UseDefaultMovement := false;
  // reduce default speed, because of large scene scale
  FThirdPersonCtrl.Speed := 15;
 
  // set camera position a bit more far away for better optics
  FCamera.Position.Point := Point3D(25, 0, -20);
  FCamera.Target := FCharacter;
 
  // link animation controller to physics character controller (PCC)
  // the PCC will then automatically dispatch all input events to the animation
  // controller to playback the correct animation transition.
  FPhysCtrl.AnimationController := FAnimCtrl;
  // link the physics system to PCC, to interact with physics world (terrain and objects)
  FPhysCtrl.Physics := FPhysics;
  // link the character controller to PCC, to let the PCC know, what to control.
  FPhysCtrl.Controller := FThirdPersonCtrl;
end;    

Movement

Since the latest version of Gorilla3D (v0.8.3.1995) we support kinematic character movement. While previous versions worked completely on physics computation, the latest change added a more stable way.

The true physics collision detection for characters works, but has bad sideeffects. The most serious problem was stuttering characters which bounced off the terrain. Also it was impossible to “climb” up steep hills because physics always dragged you down again. Even if this is more realistic, the usability was horrible for classic character movement.

So in the latest component state the “UseRayCasting” property is activated. This changes the character controller from a dynamic rigid body to a kinematic body. Here we now cast a ray to detect the current position on objects instead of letting physics do the job. Nevertheless interaction with static or dynamic objects is still available.

Remark: In case you need the old behaviour, just switch “UseRayCasting” to FALSE.

Control you raycast-movement by some properties like Slop, RayDirection and RayOffset. Especially the slopping value allows you to control f.e. the max. height of stairs to be able to walk.

PropertyDescription
SlopThis only affects computation if UseRayCasting is enabled. The slop value is the maximum height a movement ray direction. Adjust this value to your specific scene, especially when walking of stairs or similar static objects. Default value is 0.5.
RayDirectionThe RayDirection defines in which direction the controller will be adjusted. Default direction is (0, 1, 0), which means downwards.
RayOffsetGet or set ray offset from where the raycasting is started. The offset will be computed from current physics character controller position.

Next step: Prefabs