Question about character locomotion on ice

I made a few minor changes to the character locomotion class to carry over the move direction from the last update and added an ability to adjust the damping when on ice.
I also added a force added when standing on a slope, something like: m_MotorThrottle += m_GroundRaycastHit.normal * .02f;
This works great and as intended except for the one case where I move from a flat surface to an upward ramp. For example, when standing on a 30 degree slope, the character slides down the slope and the momentum is carried over to the flat surface as well, but as soon as the character hits an upward slope of 30 degrees, it basically stops like it hits a wall. There is a tiny bit of momentum up the slope but not much so it doesn't seem like the collision detection is just stopping it, but maybe playing a factor.

The math on the motorThrottle += i showed above looks right in debug, and the force that is added on a downward slope is the same that is taken away from an upward slope, so I would expect the distance traveled on the upward slope to roughly match the downward slope distance, minus a small damper.

Any idea what might be causing this or where I can focus my troubleshooting?
 
Troubleshooting this more I have found where the additional x and z movement speed is being decreased. It is in the CheckGround() in the Vertical collision detection, specifically it is being set: m_MoveDirection += m_Transform.TransformDirection(localOffset);.
I tried to disable the x and z coordinate displacements, but then the character moves through the ramp, and i believe this is because the y coordinate is not adding enough height because it doesn't know it is supposed to move as far as it is. I am still combing through code understanding to see where I can calculate the additional height needed on the movement, but if you have any suggestions I would appreciate it!
Thanks!
Jake
---
Upon further searching, i found that the ComputeGroundPenetration() is where the offset is calculated and is changing the localOffset which makes the MoveDirection change.

I think that it is pushing the object back in the direction of the overlap instead of pushing it up with the slope of the ramp in this code here:
Code:
if (Physics.ComputePenetration(activeCollider, activeCollider.transform.position + horizontalDirection + offset,
                                                    activeCollider.transform.rotation, m_OverlapColliderHit[0], m_OverlapColliderHit[0].transform.position, m_OverlapColliderHit[0].transform.rotation, out direction, out distance)) {

                    offset += direction.normalized * (distance + c_ColliderSpacing);
                }
I have tried to take out the non y components but not getting the desired outcome. How can I ensure that the horizontal movement is not altered and it is just the vertical offset that changes ?
 
Last edited:
I would not modify the core locomotion component as that will make updating harder and there shouldn't be a reason to with the ability system. Instead I would create a new ability which adds a force to the character when they are on ice, similar to how the slide ability adds a force to the character.
 
Okay, I've switched over to an ability, using the slide as a reference, and am attempting to carry over the velocity from the previous frame to the next frame, but when this runs the speed grows exponentially instead of carrying over the momentum to the next frame:

Code:
/// <summary>
        /// Updates the ability. Applies a multiplier to the horizontal and forward movement values.
        /// </summary>
        public override void Update()
        {
            base.Update();

            // If RequireMovement is true then the character must be moving in order for the ability to be active.
            if (m_RequireMovement)
            {
                StopAbility(true);
                return;
            }

            var groundRaycastHit = m_CharacterLocomotion.GroundRaycastHit;
            // Slide at a constant speed if the slope is within the slope limit.
            var slope = Vector3.Angle(groundRaycastHit.normal, m_CharacterLocomotion.Up);


            m_SlideDirection = m_CharacterLocomotion.m_MoveDirectionThisFrame;
            m_SlideDirection = m_CharacterLocomotion.Velocity * .99f;

            if (m_CharacterLocomotion.Grounded && !isJumping)
            {
                var direction = Vector3.ProjectOnPlane(Vector3.Cross(Vector3.Cross(groundRaycastHit.normal, -m_CharacterLocomotion.Up), groundRaycastHit.normal), m_CharacterLocomotion.Up);
                m_SlideDirection += direction * 0.25f;

                AddForce(m_SlideDirection * m_CharacterLocomotion.TimeScale * TimeUtility.FixedDeltaTimeScaled, 1, false, true);
            }
        }

i thought this would work since the velocity is stored without relation to the time step/scale, but it makes the character go a million miles an hour after a couple seconds.

What would be the best way to keep the velocity of the player constant throughout the frames, without using the built in dampening (because it has throttle direction and I want the velocity to be independent of the players forward axis)
Any help would be awesome.
Thanks again,
Jake
 
It's tough to say without really debugging it, but you are adding a force each frame so there needs to be some type of a dampening value otherwise the character will keep accelerating.
 
Right. If it is not exactly the same force each frame when idle (standing still/constant sliding speed with a slight damping) then it grows way to fast to control. I am trying to identify all of the forces that the TPC will dampen so i know how much to add, is there a variable that would identify easily as the overall force being applied each frame so that I can calculate the exact force needed to maintain the same speed?

Is it correct to use the velocity vector to determine the overall movement from frame to frame? Or should I be using a different one?
 
You can use the MoveDirection variable. Velocity includes time so will be a higher value than what the character actually moves each frame.
 
Okay, thanks.
I used MoveDirection and had a similar issue, the problem was the left over external forces from the previous frame getting added to the complete moveDirection, so I changed it to:

Code:
m_SlideDirection = m_CharacterLocomotion.m_MoveDirectionThisFrame - m_CharacterLocomotion.ExternalForce;
                m_SlideDirection.y = 0;

And everything seems to work fine if moving slowly, but as soon as the character moves relatively fast, he bounces off the base of any incline ramp instead of moving up the ramp. I tried setting the step limit high and 90 slope limit but it didn't make a difference. Still trying to figure out a solution.
 
The more I dig into it, I can't help but question the way that ground check handles overlaps for this type of scenario.
inside GroundCheck( ), there is a function ComputeGroundPenetration which takes the overlap of the colliders and adds this to the offset value. In the case of an object moving up a ramp, this is causing a large horizontal force opposite of the horizontal movement of the character.

There is a piece that tries to keep a constant velocity:
Code:
offset = (horizontalDirection + offset).normalized * horizontalDirection.magnitude - horizontalDirection;
If an object is moving fast, say its horizontalDirection = (0,0,0.7), and the ramp is roughly 40 degrees, this results in an offset of (0, .5, -0.2), and when this is added to the external force, the 0.7 horizontal direction gets eliminated very quickly, within a few frames, and stops the character at the base of the ramp.

Also, after the code checks to see that it is clear of collisions in ComputeGroundPenetration, it proceeds to eliminate the y coordinate value from the offset:
Code:
// Reset any upwards offset to 0. It will later be added back in after the gravity forces have been detected.
                            if (localOffset.y > 0) { localOffset.y = 0; }
and then when the singleCast occurs to be used to check for distance to the ground:
Code:
// One more cast should be performed to determine the ground object now that the character is in a non-overlapping position.
                            if (SingleCast(activeCollider, m_GravityDirection * (m_MaxStepHeight + c_ColliderSpacing), Vector3.ProjectOnPlane(m_MoveDirection, -m_GravityDirection) + m_Transform.up * (m_MaxStepHeight + c_ColliderSpacing)))
                            {
                                m_CombinedRaycastHits[i] = m_RaycastHit;
                            }
, the y isn't included like it was in the previous raycast, and it isn't picking up the slope, but the ground underneath as a result, and aligning the player to that surface, which is the root cause of why the character is just stopping and getting stuck right when it hits the base, because it aligned with the grouind under the ramp and is stuck in the ramps collider.

This isn't being triggered unless the speed is high enough, because that is the only time I have seen the compute penetration register an overlap..
Have you seen any instances of fast moving characters being stopped at the foot of a ramp? Is the y-elimination I mentioned above in the wrong spot possibly?

Thanks,
Jake
 
I haven't seen that specific issue but I have seen a case where the character was able to move through convex meshes. The fix for this ended up being to use compute penetration within the horizontal cast and it may help this situation as well. This change will be in the next version, 2.1.2.
 
Moving compute penetration to horizontal did seem to help move past specific spots that stuck before, but now seeing other issues introduced. One issue i see that is causing this is that the y coordinate offset is being set to 0 and it says it will be added later once gravity is solved, but i can't find where it is added later. this is the code inside of Character Locomotion:
Code:
// Reset any upwards offset to 0. It will later be added back in after the gravity forces have been detected.
                            if (localOffset.y > 0) { localOffset.y = 0; }
anytime I experience an overlap in the y direction, everything seems to be fine up to this point, and the offset is wiped out and the character moves through the floor.
Is there another y offset I am not seeing?

I am able to fix this with modifying GroundCheck a bit, and have tested various scenarios without finding any issues, but want to make sure I didn't just miss somewhere else that the y offset is set.
 
The vertical velocity is added in DeflectVerticalCollisions. What changes did you make to GroundCheck?
 
There is a section in DeflectVerticalCollisions that adds an offset but it is potentially less than the value that is zeroed out from before, and can be less than the players distance to the ground + skin width, that's why I am confused on the y coordinate offset and that's where I am seeing the player move through the ground.

For the changes, I have put in a check to see if the moveDirection is negative and localoffset is positive, this is the case when it is not offsetting enough for the full penetration value to be offset by the new calculated value so I added:

Code:
if(m_MoveDirection.y < 0 && m_Transform.TransformDirection(localOffset).y > 0)
                                m_MoveDirection.y = m_Transform.TransformDirection(localOffset).y;

and then further down I added a new statement to check if the item is grounded, because the new offset wasn't detecting it being grounded, below the other grounded check:

Code:
grounded = Mathf.Abs(m_Transform.position.y + m_MoveDirection.y - m_GroundRaycastHit.point.y) < (m_MaxStepHeight + c_ColliderSpacing);

This seems to have fixed my issue with falling through the ground that I have seen for the last half year when falling at high speeds.
I reverted some of the changes I made to make the upward ramps work with my ice implementation, because I never could get it to function 100% accurately and short term it is not important in my timeline, but will have to revisit at some point. But I was basically allowing the y coordinate to be changed in the calc penetration without modifying the x coordinates, because it was reading the wrong plane in the ramp collider, the side of the collider and not the face, so it was reading the slope as 90+ the actual slope, and always rejecting it from moving forward. Plus when going off the end of the ramp when it did make it, it didn't propel into the air like one would expect, I think I will have to implement more of a physics/vehicle approach with the velocity up the ramp.

It has been driving me crazy all week so I'm sure I will look at it this weekend some more once I get some distance from it and approach it with a fresh mind.
 
Top