I’m pretty new to Unity, but familiar with C#. As an experimental project I wanted to make a character that floats around 2 units off the ground, and if the player tries to go vertically up/down it would gently slow them down until eventually they let go of the keys and it would push them back to their original position at y=2.
This was all fine until I also added the ability to jump, using Rigidbody.AddForce with ForceMode.Impulse. For various reasons - and I have tried very hard to include it - I’ve set it so that the player does not experience gravity, instead relying on the method I mentioned above to push them back towards y=2 if they get too high.
However, upon jumping, the player rises into the air, all my debug lines indicate that the “player is too high” method is firing correctly and that the player is about to be moved using Rigidbody.MovePosition back towards the ground… but they don’t. They stay in the air, and I can’t figure out why.
My only thought here is the MovePosition method can’t fire until the AddForce method has “finished” doing what it’s doing, and has gotten confused by the lack of gravity, but that doesn’t make sense either - it’s set to ForceMode.Impulse so it should just be a quick one-time thing.
I should mention as well that the (snipped-out) methods I have in Update(), which allow for manual up/down movement and which also use Rigidbody.MovePosition, do still work to move the character after the character has jumped - but then the method that’s supposed to be guiding them to y=2 guides them back to y=4!
I’m honestly so vexed. It’s like jumping using AddForce just breaks the method in ways I don’t understand.
If anyone has any insight from the code below, I’d love to hear it. Thanks!
public class PlayerController : MonoBehaviour
{
public float vertSpeed = 5.0f;
private float correctiveSpeed = 5.0f;
private Rigidbody playerRb;
void Start()
{
playerRb = GetComponent<Rigidbody>();
}
void Update()
{
// snipped out the code here for brevity's sake. Not relevant to issue.
}
private void FixedUpdate()
{
if (Input.GetKeyDown(KeyCode.Space))
{
// Make the player jump. The scalar here (currently set to 10) results in a
// different "stuck point" when used. Setting it to 10 gets them stuck at y=4.
// Setting it to 20 gets them stuck at y=6.
playerRb.AddForce(Vector3.up * 10, ForceMode.Impulse);
}
// Check to see if there's ground below the player that they're supposed to float over.
if (Physics.Raycast(transform.position, Vector3.down, out RaycastHit hit, 100.0f))
{
if (hit.collider.gameObject.CompareTag("FloatOn"))
{
float dist = hit.distance;
if (dist < 2) // player is closer to the ground than desired, should go up
{
float scalar = 2.0f - dist;
vertSpeed = 5 * ((2.0f - (scalar*2)) / 2);
if (!Input.GetKey(KeyCode.DownArrow) && !Input.GetKey(KeyCode.UpArrow))
{
Vector3 upwardForce = Vector3.up * correctiveSpeed * Time.deltaTime * scalar;
playerRb.MovePosition(transform.position + upwardForce);
}
}
else if (dist > 2) // player is further from the ground than desired, should go down
{
// scalar: how far are they from where they should be?
float scalar = dist - 2.0f;
// slows the player's ability to move up as they get further from y=2, reaching zero at y=4
vertSpeed = Mathf.Max(5 * ((2.0f - (scalar * 2)) / 2),0);
// downwardForce: a vector3 that will be used to push the player back down
Vector3 downwardForce = Vector3.down * correctiveSpeed * Time.deltaTime * scalar;
// debug check for my own sanity. After getting stuck, downwardForce.y is around -0.2 and transform.position.y is around 4.
Debug.Log("downwardForce.y: "+downwardForce.y+" | transform.position.y: "+transform.position.y);
// if the player isn't pressing any keys, move the rigidbody downwards. This originally used transform.Translate and that didn't work either.
if (!Input.GetKey(KeyCode.DownArrow) && !Input.GetKey(KeyCode.UpArrow))
{
Debug.Log("Attempting to move from " + transform.position.y + " to " + (transform.position.y + downwardForce.y));
// The below line does not work. Everything indicates that the rigidbody should be trying to move downwards here and instead it hangs at y=4 rather than heading back to y=2.
playerRb.MovePosition(transform.position + downwardForce);
}
}
}
}
}
}