[RELEASED] Easy Character Movement 2

Ok, thanks for the reply, I will see to upgrade to the new version here!

1 Like

Thank you.

Hi, Iā€™ve been sniffing around for a while and tried to some moderate success to mount the player to another character. What is the best approach to this?

Say I have a horse, creature, vehicle, boat.
What is the best way to freeze the player movement, parent it to another character and give control to the other character?

What Iā€™ve done now is to disable the rigidbody, and all the movement scripts on the player, then enable the input on the other object Iā€™m controlling. Afterwards when dismounting I reenable scripts and rigidbody and turn off the input on the other character. Is there a better ECM2 way?

I really like the new hook-based architecture with the newest release, and I was wondering what your suggestion would be for implementing sort of ā€˜compositeā€™ actions which require multiple button presses. For example, I want my character to not only have a default jump as implemented within the default Character class, but also have the ability to do a high jump if they initiate a jump while crouching, and a long jump if they initiate a jump while crouching and moving (think any sort of classic 3d platformer movement).

Iā€™m sure there are multiple ways to accomplish the same thing, but Iā€™m just curious if thereā€™s a ā€˜more correctā€™ option with the way the new architecture is laid out.

Hello,

You can use the Character Pause method, which disables both the Characterā€™s collisions and Capsule Collisions.

Although ECM2 is a fully kinematic character controller and CharacterMovement is responsible for managing collision detection and response, the Capsule Collider is still recognized by Unityā€™s Physics engine. This means that other dynamic rigidbodies will interact with the Characterā€™s capsule, even if the Character script is disabled.

To solve this, use the Character Pasue method, as this disables both, Character collisions (algorithm) and its Capsule Collider (Unity Physics)

1 Like

Hi, Iā€™m glad you like the new version!

Regarding your question:

I agree there are many ways to handle this. One option, favoring composition, is to create a separate ā€˜Abilityā€™ script for jumping. This script can manage all jumping-related logic, including replacing the built-in jump functionality and adding any extra mechanics you need.

This approach consolidates all the jumping mechanics into a single script, making it easier to manage and modify.

Another approach is to reuse the built-in jump mechanic by extending the Character class and overriding or augmenting its jump-related methods. This way, you can incorporate additional conditions and trigger specific jumps as needed.

In case anyone wants to go swimming with ECM2, I made a tutorial you can check out:

1 Like

Hi @Goodgulf ,

Thatā€™s pretty cool and will definitely help others. Thank you very much for sharing it with us!

1 Like

Hey,

Is there a way to reduce speed depending on character movement direction? Iā€™d like backwards movement to be 50% slower and sideways movement (strafing) to be 25% slower.

Hi @desukarhu ,

For this, I suggest creating a custom character class (extending the Character class) and overriding its GetMaxSpeed method. This will allow us to apply a modifier to the actual character speed (e.g., walking, falling, crouching, etc.). In our case, it will be a directional movement modifier. For example:

public float forwardSpeedMultiplier = 1.0f
public float strafeSpeedMultiplier = 0.75f;
public float backwardSpeedMultiplier = 0.5f;

protected float CalculateDirectionalModifier()
{
    // Compute planar move direction

    Vector3 characterUp = GetUpVector();
    Vector3 planarMoveDirection = Vector3.ProjectOnPlane(GetMovementDirection(), characterUp);

    // Compute actual walk speed factoring movement direction

    Vector3 characterForward = Vector3.ProjectOnPlane(GetForwardVector(), characterUp).normalized;

    // Compute final directional modifier

    float forwardMovement = Vector3.Dot(planarMoveDirection, characterForward);

    float speedMultiplier = forwardMovement >= 0.0f
        ? Mathf.Lerp(strafeSpeedMultiplier, forwardSpeedMultiplier, forwardMovement)
        : Mathf.Lerp(strafeSpeedMultiplier, backwardSpeedMultiplier, -forwardMovement);

    return speedMultiplier;
}

public override float GetMaxSpeed()
{
    float actualMaxSpeed = base.GetMaxSpeed();
    return actualMaxSpeed * CalculateDirectionalModifier();
}

The CalculateDirectionalModifier function uses the dot product to determine how much the character is moving along its forward vector. It later uses this value (ranging from -1 to 1) to compute the final movement modifier. Finally, this speed modifier is applied in the Characterā€™s GetMaxSpeed method, returning the final modified speed.

2 Likes

Would be great if the Demo scene could work without needing to import Cinemachine into the project. We only want to use the first-person controller in our game. The Demo scene has a lot of useful examples to play with, but I cannot load in unless I get Cinemachine and then switch perspectives. (Iā€™m on Unity 6 btw)

Hi @fendercodes

Thanks for the suggestion! Iā€™ll include it in the next update.

Thanks! Separately, I am looking to use Fusion v2 and I imported your example for it. If weā€™re going to be using fusionā€™s Shared Mode for our project, what parts of your example scripts should we port to our own player controller? I am aware that in Shared mode some of the things arenā€™t possible / going to work that might in Client Host mode / dedicated server mode.

Hi @fendercodes

The included Fusion 2 example is designed for a fully server-authoritative mode. In your case, there isnā€™t much to transfer from this example to a shared mode, and itā€™s actually unnecessary. Shared mode is more similar to single-player games than to server-authoritative mode with client-side prediction, making it more straightforward.

To implement shared mode, you basically need to check for authority before moving the character (i.e., before calling SetMovementDirection, Jump, StopJumping, etc.), as demonstrated in the Fusion Shared Mode Basics tutorial. Additionally, ensure that you disable the characterā€™s auto-simulation and interpolation for non-locally controlled characters, as Fusionā€™s NetworkTransform will manage this for remote players.

1 Like

Hi @Krull
Iā€™m trying to get Characters to collide with each other, for organization sake and also for handling collision I have a Default layer (ground, walls and everything world related) and Pushbox (Physics collider used by characters or other pushables in the game), my understanding is that I had to put on the CharacterMovement.CollisionLayers both layers for the characters to start colliding with each other but now they also consider themselves as valid ground, I have tried using the SlopeLimitBehavior on their Pushbox collider to try and override it as NotWalkable but it doesnā€™t seem to make any difference, am I doing something wrong here or is this not possible?

Hi, @BigGreenPillow ,

Thatā€™s correct, the CollisionLayers define all the layers a character should consider during its movement, including the grounding phase.

To solve this, you can make use of the CharacterMovement.ColliderFilterCallback. This allows us to fine-tune collisions based on our own criteria. In your case, you can filter based on specific layers.

Hi Krull,

ā€œ[Obi] solvers now update the simulation in LateUpdate(), but using a fixed timestep. Unless youā€™re also updating the position of the target transform in LateUpdate() and thereā€™s no explicit script execution order set, there should be no lag.ā€

Using Obi Rope Iā€™m having trouble with it syncing to the players hand transform and it sort of lags behind. Is this likely because of the LateFixedUpdate simulate? How should I work around this?

I canā€™t figure this outā€¦ My character slides on the ground after landing from a launch?.. Iā€™ve tried turning off everything else, but it still happens.

Hi @Asarge,

You can configure the script execution order to ensure that a character is updated either before or after Obi, depending on your needs.

Alternatively, you can disable automatic character simulation by setting enableAutoSimulation to false, and then manually trigger simulation by calling the Simulate method.

Either approach should work, I believe

Hi @AustinDrozin,

Could you share your character settings so I can get a better understanding of what could be the cause.

Also is the box a trigger or a collider? as if is a collider this could be the reason of the strange behaviour.