Hi all,
I know questions of this nature have popped up before on the forums, however I’m finding most people’s solutions are for those using AddForce and Rigidbody2D’s built in velocity to move their characters around.
I am handling things very similarly to a Unity Learn tutorial where custom gravity and collision detection is used.
Attached is a segement of my code to a 2d side scroller controller which is having issues with jumping; I am having problems getting consistent jumping physics. The height of the object appears to be inconsistent. Also, without using Input.GetKey (rather than GetKeyDown), the jumping sometimes doesn’t occur at all.
This leads me to believe that my object is sinking into the ground a little or something - the height of the jump is inconsistent because for the few frames where the object is stuck in the ground, the velocity reduces too quickly for the whole jump to be taken into effect. But I just can’t figure out why my object would be sinking in the first place…
The code is based largely on Recorded Video Session: 2D Platformer Character Controller - Unity Learn << this tutorial. I’ve tried to move away from direct velocity manipulation and start with acceleration, as this would provide more control over the feel of character movement, however the translation hasn’t been as straight forward as I thought it would be. When I run the PhysicsObject and PlayerPlatformerController scripts from the tutorial, things are behaving perfectly.
Any help would be greatly appreciated.
//move and rotate the object
void FixedUpdate()
{
externalAcceleration = Vector2.zero;
inputAcceleration = Vector2.zero;
SetRotation();
//GetInput();
ComputeVelocity();
finalMove = MoveCast(deltaPosition.x * moveAlongGround, false);
SetPosition(finalMove);
grounded = false;
finalMove = MoveCast(deltaPosition.y * Vector2.up, true);
SetPosition(finalMove);
}
//get the input
void Update()
{
GetInput();
}
//get the raw input in {x}
private void GetInput()
{
inputVector.x = InputAccelModifier * Input.GetAxis("Horizontal");
//externalAcceleration = Vector2.zero;
//inputAcceleration = Vector2.zero;
if (Input.GetKey(KeyCode.Space))
{
jump = true;
}
if (Input.GetKeyDown(KeyCode.LeftShift))
{
sideBoost = true;
}
}
//modify the input acceleration, add gravity and other external forces
private void ComputeVelocity()
{
moveAlongGround = new Vector2(groundNormal.y, -groundNormal.x);
if (inputVector.x == 0)
{
//drag relative to the ground normal
if ((grounded)) //&& (groundNormal.y > minDragNormalY) && (groundNormal.y < minGroundNormalY))
{
externalAcceleration.x -= GroundDrag * totalVelocity.x;
inputAcceleration.x = -GroundDrag * totalVelocity.x;
}
else
{
//externalAcceleration.x -= AirDrag * totalVelocity.x;
}
}
else
{
inputAcceleration.x += inputVector.x;
}
if (jump)
{
if (grounded)
{
externalVelocity += JumpModifier * groundNormal;
}
//externalAcceleration += JumpModifier * groundNormal;
jump = false;
}
if (sideBoost)
{
externalAcceleration.x += SideBoost;
sideBoost = false;
}
externalAcceleration.y -= GravityModifier;
inputVelocity += inputAcceleration * Time.deltaTime;
inputVelocity = inputVelocity.magnitude > MaxInputVelocity ? Vector2.ClampMagnitude(inputVelocity, MaxInputVelocity) : inputVelocity;
externalVelocity += externalAcceleration * Time.deltaTime;
externalVelocity = externalVelocity.magnitude > MaxExternalVelocity ? Vector2.ClampMagnitude(externalVelocity, MaxExternalVelocity) : externalVelocity;
totalVelocity = inputVelocity + externalVelocity;
deltaPosition = (inputVelocity + externalVelocity) * Time.deltaTime;
}
//casts to avoid collision - returns the length to move in the input direction
private Vector2 MoveCast(Vector2 move, bool yMovement)
{
float distance = move.magnitude;
int hitCount = rbody.Cast(move, hitBuffer, distance + ShellRadius);
for (int i = 0; i < hitCount; i++)
{
Vector2 currentNormal = hitBuffer[i].normal;
if (currentNormal.y > minGroundNormalY)
{
grounded = true;
if (yMovement)
{
grounded = true;
groundNormal = currentNormal;
}
}
float projection = Vector2.Dot(totalVelocity, currentNormal);
if (projection < 0)
{
//VV turning these on can cause a direction to
//'lose' its function; holding down right does nothing
//and the character does not move right
//inputVelocity = inputVelocity - projection * currentNormal;
//externalVelocity = externalVelocity - projection * currentNormal;
}
float modifiedDistance = hitBuffer[i].distance - ShellRadius;
move = modifiedDistance < distance ? move.normalized * modifiedDistance : move.normalized * distance;
}
return move;
}
//actually move the rigidbody
private void SetPosition(Vector2 move)
{
if (move.magnitude > minMoveDistance)
{
rbody.position += move;
}
}
I know that it is usually said that you should avoid using Rigidbody2D.position or its direct transform to modify the object’s position, however this is the approach used within an official Unity tutorial.
I did try to use MovePosition before, but it took me a little bit of confused debugging to realize that MovePosition cannot be used in this way since it has problems being called twice within the same FixedUpdate.