# 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`:

```csharp
public class SprintableCharacter : Character
{
    // TODO
}
```

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:

```csharp
public class SprintableCharacter : Character
{
    [Space(15.0f)]
    public float maxSprintSpeed = 10.0f;
        
    private bool _isSprinting;
    private bool _sprintInputPressed;
    
    public void Sprint()
    {
        _sprintInputPressed = true;
    }

    public void StopSprinting()
    {
        _sprintInputPressed = false;
    }

    public bool IsSprinting()
    {
        return _isSprinting;
    }

    private bool CanSprint()
    {
        // A character can only sprint if:
        // A character is in its walking movement mode and not crouched
            
        return IsWalking() && !IsCrouched();
    }

    private void CheckSprintInput()
    {
        if (!_isSprinting && _sprintInputPressed && CanSprint())
        {
            _isSprinting = true;
        }
        else if (_isSprinting && (!_sprintInputPressed || !CanSprint()))
        {
            _isSprinting = false;
        }
    }
}
```

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.

{% hint style="info" %}
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.
{% endhint %}

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.

```csharp
public override float GetMaxSpeed()
{
    return _isSprinting ? maxSprintSpeed : base.GetMaxSpeed();
}
```

Finally, we will expand upon the previously mentioned `OnBeforeSimulationUpdate` method to incorporate the management of our sprint ability.

```csharp
protected override void OnBeforeSimulationUpdate(float deltaTime)
{
    // Call base method implementation
    
    base.OnBeforeSimulationUpdate(deltaTime);
    
    // Handle sprint
    
    CheckSprintInput();
}
```

To initiate sprinting, we utilize the `Sprint` method, while the `StopSprinting` method is employed to conclude a sprint in response to input events:

```csharp
private void Update()
{
    ..

    if (Input.GetKeyDown(KeyCode.LeftShift))
        Sprint();
    else if (Input.GetKeyUp(KeyCode.LeftShift))
        StopSprinting();
}
```

Here is the entire script:

```csharp
/// <summary>
/// This example shows how to extend a Character (through inheritance) to perform a sprint ability.
/// This uses one of the new methods (introduced in v1.4) OnBeforeSimulationUpdate,
/// to easily modify the character's state within Character's simulation loop. 
/// </summary>

public class SprintableCharacter : Character
{
    [Space(15.0f)]
    public float maxSprintSpeed = 10.0f;
    
    private bool _isSprinting;
    private bool _sprintInputPressed;
    
    /// <summary>
    /// Request the character to start to sprint. 
    /// </summary>

    public void Sprint()
    {
        _sprintInputPressed = true;
    }
    
    /// <summary>
    /// Request the character to stop sprinting. 
    /// </summary>

    public void StopSprinting()
    {
        _sprintInputPressed = false;
    }

    public bool IsSprinting()
    {
        return _isSprinting;
    }
    
    /// <summary>
    /// Determines if the character is able to sprint in its current state.
    /// </summary>

    private bool CanSprint()
    {
        // A character can only sprint if:
        // A character is in its walking movement mode and not crouched
        
        return IsWalking() && !IsCrouched();
    }
    
    /// <summary>
    /// Start / stops a requested sprint.
    /// </summary>

    private void CheckSprintInput()
    {
        if (!_isSprinting && _sprintInputPressed && CanSprint())
        {
            _isSprinting = true;
        }
        else if (_isSprinting && (!_sprintInputPressed || !CanSprint()))
        {
            _isSprinting = false;
        }
    }
    
    /// <summary>
    /// Override GetMaxSpeed method to return maxSprintSpeed while sprinting.
    /// </summary>
    
    public override float GetMaxSpeed()
    {
        return _isSprinting ? maxSprintSpeed : base.GetMaxSpeed();
    }

    protected override void OnBeforeSimulationUpdate(float deltaTime)
    {
        // Call base method implementation
        
        base.OnBeforeSimulationUpdate(deltaTime);
        
        // Handle sprint
        
        CheckSprintInput();
    }
    
    private void Update()
    {
        // Movement input
        
        Vector2 inputMove = new Vector2()
        {
            x = Input.GetAxisRaw("Horizontal"),
            y = Input.GetAxisRaw("Vertical")
        };
        
        Vector3 movementDirection =  Vector3.zero;

        movementDirection += Vector3.right * inputMove.x;
        movementDirection += Vector3.forward * inputMove.y;
        
        // If character has a camera assigned...
        
        if (camera)
        {
            // Make movement direction relative to its camera view direction
            
            movementDirection = movementDirection.relativeTo(cameraTransform);
        }

        SetMovementDirection(movementDirection);
        
        // Crouch input
        
        if (Input.GetKeyDown(KeyCode.LeftControl) || Input.GetKeyDown(KeyCode.C))
            Crouch();
        else if (Input.GetKeyUp(KeyCode.LeftControl) || Input.GetKeyUp(KeyCode.C))
            UnCrouch();
        
        // Jump input
        
        if (Input.GetButtonDown("Jump"))
            Jump();
        else if (Input.GetButtonUp("Jump"))
            StopJumping();
        
        // SPRINT input
        
        if (Input.GetKeyDown(KeyCode.LeftShift))
            Sprint();
        else if (Input.GetKeyUp(KeyCode.LeftShift))
            StopSprinting();
    }
}
```

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

```csharp
/// <summary>
/// Event called before character simulation updates.
/// This 'hook' lets you externally update the character 'state'.
/// </summary>

public event BeforeSimulationUpdateEventHandler BeforeSimulationUpdated;
```

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:

<pre class="language-csharp"><code class="lang-csharp"><strong>/// &#x3C;summary>
</strong>/// This example shows how to extend a Character (through composition) to perform a sprint ability.
/// This one use the new simulation OnBeforeSimulationUpdate event (introduced in v1.4),
/// to easily modify the character's state within Character's simulation loop.
/// &#x3C;/summary>

public class SprintAbility : MonoBehaviour
{
    [Space(15.0f)]
    public float maxSprintSpeed = 10.0f;
    
    private Character _character;

    private bool _isSprinting;
    private bool _sprintInputPressed;

    private float _cachedMaxWalkSpeed;
    
    /// &#x3C;summary>
    /// Request the character to start to sprint. 
    /// &#x3C;/summary>

    public void Sprint()
    {
        _sprintInputPressed = true;
    }
    
    /// &#x3C;summary>
    /// Request the character to stop sprinting. 
    /// &#x3C;/summary>

    public void StopSprinting()
    {
        _sprintInputPressed = false;
    }

    public bool IsSprinting()
    {
        return _isSprinting;
    }

    private bool CanSprint()
    {
        return _character.IsWalking() &#x26;&#x26; !_character.IsCrouched();
    }

    private void CheckSprintInput()
    {
        if (!_isSprinting &#x26;&#x26; _sprintInputPressed &#x26;&#x26; CanSprint())
        {
            _isSprinting = true;

            _cachedMaxWalkSpeed = _character.maxWalkSpeed;
            _character.maxWalkSpeed = maxSprintSpeed;

        }
        else if (_isSprinting &#x26;&#x26; (!_sprintInputPressed || !CanSprint()))
        {
            _isSprinting = false;
            
            _character.maxWalkSpeed = _cachedMaxWalkSpeed;
        }
    }
    
    private void OnBeforeSimulationUpdated(float deltaTime)
    {
        // Handle sprinting
        
        CheckSprintInput();
    }

    private void Awake()
    {
        // Cache character
        
        _character = GetComponent&#x3C;Character>();
    }

    private void OnEnable()
    {
        // Subscribe to Character BeforeSimulationUpdated event
        
        _character.BeforeSimulationUpdated += OnBeforeSimulationUpdated;
    }
    
    private void OnDisable()
    {
        // Un-Subscribe from Character BeforeSimulationUpdated event
        
        _character.BeforeSimulationUpdated -= OnBeforeSimulationUpdated;
    }
}
</code></pre>

Finally, we will employ the `Sprint` and `StopSprinting` methods to commence or halt sprinting in response to input events.

```csharp
private SprintAbility _sprintAbility;

private void Update()
{
    ..
    
    if (Input.GetKeyDown(KeyCode.LeftShift))
        _sprintAbility.Sprint();
    else if (Input.GetKeyUp(KeyCode.LeftShift))
        _sprintAbility.StopSprinting();
}
```

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:

```csharp
public enum ECustomMovementMode
{
    None,
    Dashing,
    Climbing
}
```

This identifier will be used to establish the new movement mode using the `SetMovementMode` method:

```csharp
SetMovementMode(MovementMode.Custom, (int)ECustomMovementMode.Dashing);
```

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:

```csharp
public class CustomCharacter : Character
{
    protected override void CustomMovementMode(float deltaTime)
    {
        // Call base method implementation
            
        base.CustomMovementMode(deltaTime);
            
        // Update dashing movement mode

        if (customMovementMode == (int)ECustomMovementMode.Dashing)
            DashingMovementMode(deltaTime);
    }
}
```

#### Using Composition:

On the other hand, when using composition, you should subscribe to its `CustomMovementModeUpdated` event.

```csharp
protected void OnEnable()
{
    // Subscribe to Character Event
    
    character.CustomMovementModeUpdated += OnCustomMovementModeUpdated;
}

protected void OnDisable()
{
    character.CustomMovementModeUpdated += OnCustomMovementModeUpdated;
}

private void OnCustomMovementModeUpdated(float deltaTime)
{
    // Update dashing movement mode

    if (customMovementMode == (int)ECustomMovementMode.Dashing)
        DashingMovementMode(deltaTime);
}
```

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.

{% hint style="success" %}
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.
{% endhint %}

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.

{% hint style="info" %}
Classes derived from `Character` are encouraged to extend the `OnCustomMovementModeUpdated` method instead of subscribing to the event for enhanced customization and seamless integration.
{% endhint %}

Please refer to the included examples for a complete implementation of custom movement modes.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://oscar-gracian.gitbook.io/easy-character-movement-2/walkthrough/extending-a-character.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
