I’m following this Unity’s tutorials about writing your custom gravity for a 2D platformer (and later on in the series it explains collisions, etc.). The code is pretty straightforward:
(1) Since I’m moving the rigidbody2D’s position, I cannot measure the rb2d’s velocity because it’s always going to be 0. Now, If I wanted to cap the maximum fall speed for the rigidbody2D (so that the velocity doesn’t keep increasing infinitely), what would be the best way to detect if the rigibody2D’s velocity/movement has exceeded a certain limit?
This works, but there’re some problems related: imagine the player falls for a while and hits the ground, then the player stops and the isGrounded variable becomes true, so far so good. Now, if the player falls again, somehow the _move.magnitude is remembered (it’s 0.33f), so as soon as the player falls again, it will fall at maximum fall speed.
(2) the second question is somehow related: given this custom system with the rb2d.move, is there a nice way to detect if the rigidbody2d is going upwards (+) in the y axis (similar to the Rigidbody2D.velocity.y) or downwards (-)? Likewise, if there’s a way to detect this it’d be possible to detect the horizontal movement as well (similar to the Rigidbody2D.velocity.x)
I also tried that, but it doesn’t matter whether the player is going upwards or downwards, that my velocity is going to be always negative. This screws the jump, because the jump exceeds the limit going upwards, so that the velocity gets capped, and then the player falls upwards at a continuous speed.
Check the magnitude of the vector, not the vector itself.
That said, setting the position of a rigid body seems to defeat the point of a rigid body. Couldn’t you disable gravity for that rigid body and instead influence it’s velocity?
The point of using a rigidbody2D is to help detect collision, but having your own/custom gravity, movement and collisions’ rules (it’s very well explained in the Unity’s series). The rigidbody is set to kinematic, so it won’t be affected by Unity’s physics. The velocity here is the velocity for gravity only
I thought this if (_move.magnitude < 0.33f) was checking the magnitude of the vector. How would I go about and check the magnitude then? (sorry if it’s a nooby question)
Use MovePosition, don’t set the Rigidbody2D pose directly. At least this way you’ll get interpolation working correctly. No idea who wrote that tutorial but it’s wrong to do that.
He wants you to check the magnitude of the velocity, not the move vector. I don’t quite understand what you mean by velocity would always be negative, you said you wanted to cap the fall speed so it doesn’t really matter that it’s negative.
For figuring out what direction your object is going, you would need to change how your code works. Instead of calling Movement() with a bunch of different methods, you would just need to set one value with every method, and actually move it at the end.
Your code also looks like it wouldn’t even compile.
That’s a pretty old tutorial and I can’t help but feel that information is outdated or incorrect. Rigid bodies should be moved within the scope of the physics system, either by directly influencing the velocity (not best practice) or by using the supplied methods such as MovePosition.
Thanks for the tip, I changed this rb2d.position = rb2d.position + _move;
to this: rb2d.MovePosition(rb2d.position + _move.normalized * _distance);
That should do it, right?
Oh, I understand. The velocity, as you see in the first chunk of code I posted, is a variable for the gravity velocity. After computing it, it always grows in the negative since it’s being multiplied by the gravity value which is -9.81f.
No, it does compile, and it also works as intended. At the end of the day, it’s been coded by Unity’s staff, so they won’t make such mistakes in a tutorial series.
Yes, it’s old (and as you said probably outdated/incorrect). Unfortunately, I didn’t find any other tutorials about writing custom gravity and addressing collisions like this tutorial (with “groundNormals”, so it won’t detect the player as grounded when colliding with a wall because the angle of the groundNormals will exceed the angle limit you’ve setup).
Yes, I updated it to rb2d.MovePosition(rb2d.position + _move.normalized * _distance);
which I think is the correct/updated version for this tutorial.
Nonetheless, because I’m not using rigidbody.velocity but instead rigidbody.MovePosition, is there a way/alternative to detect its current velocity?
The thing is that Mathf.Abs will always be positive (right?), so it will be hard to distinguish whether the character is going upwards or downwards. Correct me if I’m wrong, this is just my guess.
Ok, so I’ll be checking then the if (velocity.y > -16.66f) or with your suggestion: if (Mathf.Abs.velocity.y < 16.66f)
I’ll be testing it and report the results when I find the whole correction to the code. Thanks to all, I’ll keep you updated.
I still have the same problem by checking the magnitude. if (velocity.magnitude < 16.66f) and else if (velocity.magnitude >= 16.66f)
When jumping (launches the character upwards), it reaches the else if statement, therefore the code mentioned here ↓
which is _move = Vector2.up * -0.33f;. Once this is applied, the player continues going upwards forever at a constant speed. I’m going to rework the jump with the rb2d.MovePosition and report the results, because this could very well be a problem of the jump implementation.
That’s the point of clamping or capping the “gravity velocity” so that the player won’t have increasing gravity being applied when exceeding a threshold.
I just tried changing the code to _move *= 0.33f; but this stops the player in mid-air.
I’m sure of it. This was my first question: what would be the “official” or standard way (by Unity or senior developers) of clamping/capping the max fall speed when writing custom gravity for 2D platformers as the one shown in this Unity tutorial series?
PS: Btw, since the tutorial I’m following is a little outdated, if anyone knows of an updated tutorial for writing custom physics/gravity for 2D platformers please let me know, I’ll appreciate it
I should apologise; I probably should be answering posts at this time of night (midnight).
Nonetheless, I would honestly just use the pre-supplied methods such as Vector2.MoveTowards or Vector2.SmoothDamp which will approach a target but not exceed it.
Yep, perfect. Now you can use interpolation and it’ll be smooth.
No need to “detect” it, you’re giving it a position change of “_move.normalized * _distance” (effectively a speed of “_distance”) and velocity is distance/direction over time so V = “_move.normalized * _distance” / “Time.fixedDeltaTime”. This is the “velocity” you’re giving it each simulation step.
There’s also nothing stopping you just setting its velocity instead so “rb.velocity = (_move.normalized * _distance) / Time.fixedDeltaTime”. The extra thing that velocity does is disable (temporarily) any linear/angular drag too.
This is very instructive! I don’t know why I got the idea that moving a character’s position directly won’t be computed with physics formulae. But yes, it makes total sense. Thanks.
By the way, I noticed that some behaviors have been altered after I changed the code to Rigidbody2D.MovePosition. One is the X movement. Before I explain the issue, I’ll share the whole code of the 2017 Unity tutorial in spoiler, in case you want to have a look at it:
Check code here
In the 2017 Unity tutorial, the Movement() function is called twice for each time step (one in line 59, and another one in line 63), once to handle the velocity.x, and another time to compute the velocity.y. It only works as intended if you set the rb2d.position directly. However, by using rb2d.MovePosition the second call for the Movement() function will set the x value back to 0, therefore the character won’t move at all (and sometimes it jitters from left to right while staying at the same spot). Do you have any idea what the incompatibility might be? or how could I rework it to make it work with rb2d.MovePosition?
I have no idea. I didn’t write that tutorial nor do I know what is going on in it. MovePosition will happen during the simulation step, maybe something else is reading Rigidbody2D.position before it’s been updated. No idea.