Extending a Character
Custom Character (Inheritance)
One of the notable features of the Character
class is its role as a highly robust foundation for all your game avatars. It enables users to expand its functionality by adding specific features and mechanics tailored to the requirements of their game.
In this example, we will demonstrate how to extend a Character
using inheritance. In this particular case, we will add a Sprint ability.
One of the advantages of inheritance over composition is the ability to extend methods from the base class, allowing us to modify them to suit our purpose. In particular, we will use the GetMaxSpeed
method. As the name suggests, this method returns the maximum speed at which the character can move, depending on its state or movement mode, making it perfect for Sprint.
Furthermore, we will leverage a newly introduced method in version 1.4, namely OnBeforeSimulationUpdate
. This method simplifies the process of modifying the character's state within the character's simulation loop, enhancing the overall flexibility and control over the character controller's functionality.
Firstly, we create our custom character named SprintableCharacter
:
When extending a Character
, it is advisable to adhere to the established conventions of the Character
class by creating a set of methods to control the execution of newly added abilities. Ensure these methods follow the pattern of the character's built-in functions, such as Jump
, StopJumping
, Crouch
, UnCrouch
, and so forth. In this specific scenario, these methods are designed to manage the Sprint ability.
For example:
The SprintableCharacter
class serves as an illustration of this methodology. The variable maxSprintSpeed
defines the character's top speed during sprinting, while the _isSprinting
variable indicates the character's ongoing state. Moreover, the _sprintInputPressed
variable oversees the activation of the ability, commonly triggered by input events such as key-down or key-up actions.
The CheckSprintInput
method adheres to the previously mentioned pattern. Action execution methods, like Sprint
and StopSprinting
, prompt the character to carry out a specific action. This request undergoes processing in the CheckSprintInput
method, where the CanSprint
method determines whether the character is capable of initiating or should cease sprinting.
While not obligatory, it is advisable to embrace a comparable approach when extending a Character
. This methodology is actively utilized by the Character
class to implement its inherent jump and crouch functionalities.
The final step involves modifying the character's maximum speed while sprinting. To achieve this, we extend the character's GetMaxSpeed
method. This extension enables us to return maxSprintSpeed
when the character is in a sprinting state.
Finally, we will expand upon the previously mentioned OnBeforeSimulationUpdate
method to incorporate the management of our sprint ability.
To initiate sprinting, we utilize the Sprint
method, while the StopSprinting
method is employed to conclude a sprint in response to input events:
Here is the entire script:
Character Extension (Composition)
In the previous section, we illustrated the process of extending a Character
through inheritance. Now, we will replicate the same example of adding sprint functionality to a character. However, unlike the previous approach, we will utilize composition.
Composition, in object-oriented programming, involves constructing a class using instances of other classes to achieve the desired functionality. It's worth noting that composition offers certain advantages over inheritance. With composition, components can be dynamically combined to create more flexible and modular designs. This contrasts with the more rigid structure of inheritance, allowing for easier modification and extension of code.
In essence, composition enables the building of complex structures by assembling simpler, independent parts. This approach often leads to more maintainable and scalable code, fostering a design that prioritizes flexibility and reusability.
To achieve this, we'll make use of the recently introduced 'hooks' (events) in version 1.4.0. These hooks empower us to influence the character's state without the need for inheritance from the Character
base class. In particular, the BeforeSimulationUpdated
event essentially serves the same purpose as the previously utilized OnBeforeSimulationUpdate
method.
In essence, the code remains quite similar with some minor modifications. Specifically, the CheckSprintInput
method now directly modifies the Character's maxWalkSpeed
property. This adjustment is necessary because, unlike the previous approach (inheritance), extending the GetMaxSpeed
method is not feasible in this context.
Below is the complete script:
Finally, we will employ the Sprint
and StopSprinting
methods to commence or halt sprinting in response to input events.
As you can observe, the process is quite similar to what we've seen before, with the distinction that now it's the SprintAbility
responsible for initiating or stopping the sprint.
Creating a Custom Movement Mode
The Character
class incorporates various movement modes and allows for the creation of custom modes. Before creating a new movement mode, it is recommended to attempt implementing your desired mechanics on one of the available movement modes. For instance, the jump ability is built upon the falling movement mode, while the crouch mechanic is built upon the walking movement mode.
This approach is employed to leverage and extend the functionality of existing code, making it easier to maintain and expand. By using a base movement mode and adding or modifying specific mechanics, a more modular and flexible system is created.
For example, if you already have a walking movement mode, implementing the crouch mechanic on top of it allows you to reuse the walking logic while only adding the necessary adjustments for crouching.
Similarly, building the jump ability on top of the falling movement mode allows you to reuse the falling logic while introducing specific rules for initiating and controlling jumps. This modular approach not only streamlines development but also ensures consistent and predictable behavior across different movement modes.
To create a new movement mode, you first define an ID for it. For instance:
This identifier will be used to establish the new movement mode using the SetMovementMode
method:
In this example, you've created an enumeration called ECustomMovementMode
, which includes values for different movement modes like None
, Dashing
, Climbing
, etc. This enumeration helps you easily identify and switch between various movement modes.
Next, extend the CustomMovementMode
method. Depending on whether you're developing a derived class from the Character
class (using inheritance) or subscribing to the CustomMovementModeUpdated
event (utilizing composition), this is where you'll implement the logic for the new movement mode.
Using Inheritance:
When inheriting from the Character
class, it is advisable to extend its CustomMovementMode
method:
Using Composition:
On the other hand, when using composition, you should subscribe to its CustomMovementModeUpdated
event.
In both cases, customize the logic inside the CustomMovementMode
method to define how your character behaves in the "Dashing," "Climbing," or other custom movement modes. This flexibility allows for a tailored and dynamic user experience based on different movement scenarios.
To exit a custom movement mode, it is suggested to use the Walking or Falling movement modes, as they are automatically managed based on the character's grounding status.
Additionally, when configuring 'enter' or 'exit' settings for movement modes, it is advisable to use the OnMovementModeChanged
method in the case of inheritance, or its corresponding MovementModeChanged
event when employing composition. These features enable you to execute specific setup actions upon entering or leaving a particular movement mode.
Classes derived from Character
are encouraged to extend the OnCustomMovementModeUpdated
method instead of subscribing to the event for enhanced customization and seamless integration.
Please refer to the included examples for a complete implementation of custom movement modes.
Last updated