Character Controller move towards Position by X Speed, small issue

Hey there,

i just cant wrap my head around it… its probably because of my miss understanding about CC.Move,

    public class CCWalk : CustomYieldInstruction
    {
        private readonly CharacterController _cc;
        private readonly Vector3 _destination;

        private readonly Animator _animator;
        private readonly AutomotiveConfigSO _config;

        private Transform _owner;
        private float _distanceToDestination = 0f;
        private float _currentSpeed = 0f;

        public override bool keepWaiting
        {
            get { return !MoveToDestination(); }
        }

        public CCWalk(CharacterController cc, Vector3 destination, Animator animator, AutomotiveConfigSO config)
        {
            _cc = cc;
            _destination = destination;
            _animator = animator;
            _config = config;
            _owner = _cc.transform;
        }

        private bool MoveToDestination()
        {
            if (!(_owner.GetDistanceTo(_destination) >= _config.StoppingDistance))
            {
                _animator.SetFloat(MovementAnimationIDs._animIDSpeed, 0f);
                return true;
            }

            _currentSpeed = Mathf.MoveTowards(_currentSpeed, _config.WalkSpeed, _config.WalkSpeed * Time.deltaTime);
            _distanceToDestination = _owner.GetDistanceTo(_destination);

            Vector3 tempDestination = _distanceToDestination <= _config.StoppingDistance
                ? Vector3.zero
                : _owner.ReturnVectorTowards(_destination, _currentSpeed);

            float _finalSpeed = tempDestination == Vector3.zero
                ? 0f
                : _currentSpeed;
         
            _animator.SetFloat(MovementAnimationIDs._animIDSpeed, _finalSpeed);
            _cc.Move(tempDestination * _finalSpeed * Time.deltaTime);
            return false;
        }
    }

So i have made a CustomYieldInstruction, which works pretty good, problem here is, the speed of my CharacterController exceeds the given strength.

_config.Walk // 2f

the point here is, the Mathf.Towards function goes to a maximum value of “2f” but my velocity exceeds that which means that my character is moving quicker.

How do i get the CharacterController velocity?

        Vector3 s = transform.InverseTransformDirection(_characterController.velocity);
        debug_speed = s.z;

Any good solution to this?
I dont understand why CC.Move is moving my character faster then the given speed value.
Would appreciate any help :slight_smile:

Really hard to say. A lot of missing information here/missing relevant code:

  • How and where is this custom yield instruction being used?
  • What does _owner.GetDistanceTo do?

Other notes:

  • Line 35 is really weird
  • Line 42 is really weird and seems kind of pointless
  • The use of a custom yield instruction for any of this seems very strange and dare I say overcomplicated.
1 Like

Hey!

  • The CustomYield is used while interacting with any interactable object which needs the AI/Player to move towards a moveposition.
    Imagine a Bench for example, in an RPG like Gothic/RIsen, i press a button to interact with and it automate the movement of my AI or Player towards the MovePoint where he needs to stand and to play his animation afterwards.

  • _owner.GetDistanceTo gets basically the distance from the interacting AI/Player towards the Destination (Bench / Bed / Fireplace etc…)

  • Line 35 makes the Speed go from 0 to 2 slowly (2f would be the max. walking speed).
    Its like: currStrength = Mathf.MoveTowards(currStrength, maxStrength, recoveryRate * Time.deltaTime);

  • the Point of line 42. is to recover from the speed change, if the AI/Player reaches its destination the speed is not immedeatly at 0f, its maybe at 1f or 0.2… etc.
    After that line the Animator sets the Speed in the Animator tab which makes the player walk at one place if the velocity is set above 0f, that way i just check, if we really reached the destination, set the finalSpeed to 0, its like an insurance.

  • The Use of CustomYield might be a bit too much, but i feel like not, we are doing an RPG and i do interactions in Coroutines which make me use of Yield Instructions, and they are very handy, instead of passing to much data into some Yield Instructions i can basically handle all the logic for its use case in it, and i can use it where ever i want.

I hope i didnt write too much :smile:

I was really hoping to see some code for these things. The descriptions are ok but the details of the code really matter here.

1 Like

Well, the Code looks like this:

        protected override IEnumerator InteractWithPlayer()
        {
            _playerController = GetInteractingEntityComponent<PlayerController>();
            CameraEventChannelSo.RaisePlayEvent(cameraMovePoint.position, cameraMovePoint.eulerAngles);

            yield return new Turn(_playerController.transform, MovePoint.position, _turnDuration);
     
            yield return new CCWalk(_playerController.GetComponent<CharacterController>(),
                MovePoint.position,
                _playerController.GetComponent<Animator>(),
                _playerController.GetComponent<CharacterControllerAutomotive>().AutomotiveConfig);
     
            yield return new Turn(_playerController.transform, GetTurnRotation(), _turnDuration);
            TriggerAnimation();

            yield return new WaitForFixedUpdate();
        }

Dont get confused by the “GetComponent” its an overwritten function from me, caching the components in a list, that way i dont really have to get it from the GO but just filter it out of my list.

but lets simplify it and put it the easiest way possible and forget about the above snipped:

    [SerializeField] private AutomotiveConfigSO _config;
    [SerializeField] private Transform _destination;

    private Animator _animator;
    private CharacterController _characterController;

    private void Start()
    {
        _characterController = GetComponent<CharacterController>();
        _animator = GetComponent<Animator>();

        StartCoroutine(WalkToTarget());
    }

    private IEnumerator WalkToTarget()
    {
        yield return new CCWalk(_characterController, _destination.position, _animator, _config);
    }

And the Yield looks as before:

using TDSStudio.Util;
using UnityEngine;

namespace TDSStudio.Movement
{
    public class CCWalk : CustomYieldInstruction
    {
        private readonly CharacterController _cc;
        private readonly Vector3 _destination;

        private readonly Animator _animator;
        private readonly AutomotiveConfigSO _config;

        private Transform _owner;
        private float _distanceToDestination = 0f;
        private float _currentSpeed = 0f;

        public override bool keepWaiting
        {
            get { return !MoveToDestination(); }
        }

        public CCWalk(CharacterController cc, Vector3 destination, Animator animator, AutomotiveConfigSO config)
        {
            _cc = cc;
            _destination = destination;
            _animator = animator;
            _config = config;
            _owner = _cc.transform;
        }

        private bool MoveToDestination()
        {
            if (!(_owner.GetDistanceTo(_destination) >= _config.StoppingDistance))
            {
                _animator.SetFloat(MovementAnimationIDs._animIDSpeed, 0f);
                return true;
            }

            _currentSpeed = Mathf.MoveTowards(_currentSpeed, _config.WalkSpeed, _config.WalkSpeed * Time.deltaTime);
            _distanceToDestination = _owner.GetDistanceTo(_destination);

            Vector3 tempDestination = _distanceToDestination <= _config.StoppingDistance
                ? Vector3.zero
                : _owner.ReturnVectorTowards(_destination, _currentSpeed);

            float _finalSpeed = tempDestination == Vector3.zero
                ? 0f
                : _currentSpeed;
        
            _animator.SetFloat(MovementAnimationIDs._animIDSpeed, _finalSpeed);
            _cc.Move(tempDestination * _finalSpeed * Time.deltaTime);
            return false;
        }
    }
}

And the code for ReturnVectorTowards?

1 Like

Here you go, i hope it helps, but the Vector towards function is not the problem i would say, since it does work, just the speed of which the CharacterController moves, is way to fast as the speed im given

        public static Vector3 ReturnVectorTowards(this Transform traget, Vector3 destination, float speed)
        {
            var offset = destination - traget.position;
            return offset.magnitude > .1f ? offset.normalized * speed : Vector3.zero;
        }

Small Edit:

I found a small function in my Helper class i wrote a while ago, with that, the movement speed is correct, but i still would love to figure it out with CharacterController,

here is the working code without the CharacterController:

        public static void MoveTowardsTarget(this Transform movingObject, Vector3 destination, float speed)
        {
            float step = speed * Time.deltaTime;
            movingObject.position = Vector3.MoveTowards(movingObject.position, destination, step);

            //Look at Direction
            destination.y = movingObject.position.y;
            Quaternion targetRot = Quaternion.LookRotation(destination - movingObject.position, Vector3.up);
            movingObject.rotation = Quaternion.Slerp(movingObject.rotation, targetRot, Time.deltaTime * 2f);
        }

Ok, after beeing a bit stupid, i forget actualy to normalize the destination …

Well i gues it can happen to anyone.

Working Code is here in case, anyone misses it:

private bool MoveToDestination()
        {
            if (!(_owner.GetDistanceTo(_destination) >= _config.StoppingDistance))
            {
                _animator.SetFloat(MovementAnimationIDs._animIDSpeed, 0f);
                return true;
            }

            _currentSpeed = Mathf.MoveTowards(_currentSpeed, _config.WalkSpeed, _config.WalkSpeed * Time.deltaTime);
            _distanceToDestination = _owner.GetDistanceTo(_destination);

            Vector3 tempDestination = _distanceToDestination <= _config.StoppingDistance
                ? Vector3.zero
                : _owner.ReturnVectorTowards(_destination, _currentSpeed);

            float _finalSpeed = tempDestination == Vector3.zero
                ? 0f
                : _currentSpeed;
           
            _animator.SetFloat(MovementAnimationIDs._animIDSpeed, _finalSpeed);
            _cc.Move(tempDestination.normalized * _finalSpeed * Time.deltaTime);
            return false;
        }

@PraetorBlue thank you for your time and effort, you make me rethink the whole process again :slight_smile: