RigidBody2D AddForce Not Adding Acceleration - c#

I'm trying to add acceleration to an object with a RigidBody2D component using AddForce, but all it seems to do is add a fixed velocity instead.
Here are the RigidBody2D settings:
For testing I've modified the Update function to allow me to add forces and report the object's velocity periodically:
void Update()
{
time += Time.deltaTime;
if (time > 0.5)
{
Debug.Log("Velocity magnitude = " + rigidbody2D.velocity.magnitude);
time = 0;
}
if (Input.GetKeyDown(KeyCode.Q))
{
rigidbody2D.AddForce(Vector2.right);
}
}
What I see is that every time I press Q, the velocity magnitude increases by a fixed 0.02.
I.e. first it doesn't move at all and the velocity is 0, then after pressing Q the velocity magnitude changes to 0.02 and stays at 0.02 while the object trudges slowly to the right. Pressing Q again increases it to 0.04, then pressing once more increases it to 0.06 and so on.
These are results from two days ago. I did the experiment again today and got different velocity increments but the same behavior otherwise.
The velocity changes only when I add a force.
If I added a force of magnitude 1 to an object at rest with a mass of 1 then its velocity would increase by 1 in magnitude every single unit of time (which I think here is seconds).
Instead it's staying fixed.
Using ForceMode2D.Impulse increases the velocity increments from 0.02 to 1, but the behavior stays the same - no acceleration, only one-time increments in speed.
What am I doing wrong?
I've tried looking for similar posts and for the problem in general but the only things I found were either irrelevant or asking the opposite (how to stop acceleration from occurring).
Any help would be appreciated.
The Unity version is 2019.1.1f1.

TL;DR
What you are doing wrong is that you are expecting the wrong physics behavior from the apply force function. That function does what it says, it applies force. It changes the object velocity instantaneously adding to it. The force mode for 2d bodies only tells the engine to consider the object's mass or not.
The problem is the naming Unity uses, AddForce doesn't "add" the force, it just applies the force for a single frame. In the following frames, all forces are zero again. If you want to keep the force, just create your own vector in your script, modify it, and apply it to the object every frame using AddForce.
how are you?
I have created a sample project to reproduce your issue but I couldn't reproduce the velocity staying fixed as you said. I'm not sure how you set up your scene so I made some assumptions. You probably have disabled either gravity on your object or friction, since you are reporting that the velocity is increased.
Acceleration, on the other hand, is a constant force being applied, think of it. If your car is still, and you push it for a short amount of time, the car will move then stop. If you keep pushing on the other hand, adding more and more force, the car will move faster and faster. Now think about your environment, you have a frictionless object. Every time you press Q you add a small force to it. It moves to the right a bit faster but with a constant speed (there is no force to change its velocity).
Here is what you'd want to do. Instead of adding the force only in the frame you pressed Q, add while you are pressing Q. This is what you want to do:
private void FixedUpdate()
{
time += Time.deltaTime;
if (time > 0.5)
{
Debug.Log("Velocity magnitude = " + rigidbody2D.velocity.magnitude);
time = 0;
}
if (Input.GetKey(KeyCode.Q))
{
rigidbody2D.AddForce(speedMultiplier * Time.fixedTime * Vector2.right, ForceMode2D.Force);
}
}
Another problem is the naming Unity uses, AddForce doesn't "add" the force, it just applies the force for a single frame. In the following frames all forces are zero again. If you want to keep the force, just create your own vector in your script, modify it, and apply it to the object every frame using AddForce.
Also, see that I changed Update to FixedUpdate? In Unity, you are recommended to apply all physics changes in that method instead. Update runs as much as it can (once every frame if possible), fixed update instead runs only after the physics thread is done. This means you are not spending CPU time telling the object to move every frame, and instead you are waiting for the physics to be synchronized.

I don't think that's going to add enough force to the object to get it into motion. Normally you need to multiply your direction by a "force" multiple. Try something like rigidbody2D.AddForce(Vector2.right * 1000);. That is, intentianally, probably way too much force but at least you'll know it's moving.

Related

Rotating an object to the movement direction is systematically inaccurate

This is in unity 2019.4.40f1. I'm trying to rotate an object toward it's movement direction. However, I noticed that this is inaccurate and in investigating I noticed that the inaccuracy comes from the actual movement amounts. What I mean is, in the code below, delta.y / delta.x prints out different values every loop iteration even though destination does not change. As a result, Atan2 of course also returns different results. The error every frame is on the 4th 3rd or 4th decimal place, however, over just a few seconds it adds up because it's not random variation but instead seems to be systematic.
Also note that at this stage I'm not actually applying the rotation to the object. My expectation is that the code below should move the object from its position to the destination a little bit every frame, in a straight line. This does appear to be the case visually, but numerically there is enough deviation to cause problems with the angle calculations.
IEnumerator TweenFunc (object myObject, Vector2 destination, float speed)
{
while (condition)
{
currentPosition = myObject.transform.position;
delta = destination - currentPosition;
Vector2 dir = delta.normalized;
frameTime = TimeManager.FrameTime
myObject.transform.Translate(dir.x * frameTime * speed, dir.y * frameTime * speed, 0);
Debug.Log(delta.y / delta.x);
Debug.Log(Mathf.Rad2Deg* Mathf.Atan2(delta.y, delta.x));
yield return true;
}
}
I would like to keep the angle computation consistent. It is possible to pre-compute the angle once, outside of the loop by computing delta first outside of the loop and then getting the angle before entering the loop. However, this solution is not usable in my use-case because the actual angle computation needs to take place in a different part of the code base that is only aware of dir and speed and not destination.
Edit for additional info: When traveling from (1.2, -0.2) to (0.2, -3.2) the angle changes from ~108.43352 to 108.3664. The true angle should be 108.435. While this is only a 10th of a degree, if I actually apply the rotation to the object it becomes visible, especially because most of the change appears to happen toward the end of the movement. I wonder if as dir.x and dir.y go to zero accuracy becomes worse in a systematic way. Also, there is no angle drift when traveling in any of the cardinal directions. There, the accuracy of dir.x and dir.y don't require decimal places since they are either 0 or 1, so that could be what's going on.
I wonder if there's a way to deal with this since the ultimate goal is actually a pretty common thing - to rotate the object toward its movement direction.
Even though the code you show here only does translation an not rotation, in general have in mind that Transform.Translate by default works in local space.
So if (from your title I just assume) you object is rotated this is actually inaccurate since you take a world space delta and then move on it but in local space
=> You want to explicitly pass in
myObject.transform.Translate(dir.x * frameTime * speed, dir.y * frameTime * speed, 0, Space.World);
And then you probably want to rather operate on the actually normalized movement vector and afaik you flipped the order of arguments:
Debug.Log(Mathf.Rad2Deg * Mathf.Atan2(dir.x, dir.y));
I'll add my partial solution answers since I haven't figured out how to counteract the issue directly.
It's possible to cast the x and y movement direction and all subsequent calculations leading to the angle computation as doubles by computing destination - currentPosition normally, casting that to double, and doing the rest of the vector computations (e.g. normalization, Atan2) manually with double precision and using Math rather than Mathf functions. Since object transform positions are still stored as floats there is still error, but this results in a reduction in error in the example of 0.05 of a degree, so it reduces the error in that example by half.
Since the function TweenFunc does not update destination, and it's not expected that game objects should be moved outside of the function while it is active, I can move the computation of dir.x and dir.y outside of the loop, so they are only computed once, at the beginning, at maximum accuracy, which in the example returns -108.4349 and no additional error accumulates further.
This works for producing stable movement directions, but the major downside of this is if somehow the game object is moved in some way outside of this function, then it may never reach its destination and we might therefore never exit the loop in TweenFunc. This can be addressed by adding complexity like checking if movement direction changed by more than 1 degree or some other threshold, and resetting it then.

C#/Unity Camera Follow Jitter due to Time.deltaTime

Game: In a simple 2D Portrait Game made in Unity, I have a GameObject (Player) that has a fixed location and which is moving upwards. The Camera follows the Player and animated Obstacles are spawning from time to time moving left to right. The attached Screenshot shows the Scene.
The Problem:
The Movement is not smooth, as it seems like the Player is jittering. I think I already identified one of the causes: Big variation of Time.deltaTime. Average value is 0.0167, but I had variations. Minimum was 0.00177, maximum value was 0.2249519.
Settings:
Target Framerate is 60. I use Unity 2019.4.2f1 and as build target an iPhone X with iOS 14.2.
Scripts
public class Player: MonoBehaviour
{
float speed = 5f;
void Update()
{
transform.Translate(0,speed*Time.deltaTime,0);
}
}
public class CamFollow : MonoBehaviour
{
public Transform Player;
private Vector3 FollowVector;
void LateUpdate()
{
FollowVector = Player.position - new Vector3(0, -4.0f, 10);
transform.position = Vector3.Lerp(transform.position, FollowVector, Time.deltaTime * 4f);
}
}
Note: I need to use Lerp, because the Player may lower or increase the speed for one second, then the camera gently moves to the new position, before changing back. For the Obstacles I don't have a Script. They are moving, by using the Animation Component. For the Obstacles I only loop a change of the x value of the position.
My alternative solutions:
1. Changing the value for Time.deltaTime to a constant value of 0.01666667f:
void Update()
{
transform.Translate(0,speed*0.01666667f,0);
}
This makes the Player Object jitter a lot in the Unity Editor but only a little on the device
2. Using Fixed Update both for the Camera Follow and the Player Movement
This makes the movement and camera follow perfectly smooth, but the animated objects jitter a lot. I know Unity wants to adress the deltaTime issue in one of the next updates. But there should be a solution for my problem, so did anybody have a similiar problem, which could be solved? I prefer the 2nd alternative, because the movement looked really smooth and nice, so can I somehow make the animation part of "fixedUpdate"?
The variation in the 'deltaTime' is to be expected.
The variation is large on the PC because you are running on a complex computer with a complex operating system and lots of other applications running simultaneously, each with a multitude of threads, which every once in a while want to do some work. Thus, the scheduler of the operating system cannot guarantee that you are going to get a time slice at the precise moment that you want it in order to render your next frame.
The variation is smaller on the mobile device because it is a much simpler machine with a lot less going on, so the scheduler is able to give you time slices close to the precise intervals that you are asking.
You are already taking this variation into account when you do
transform.Translate( 0, speed * Time.deltaTime, 0 );
This is a fundamental technique in game development: the frame rate is never constant, so the distance by which you must move an object on each frame depends on the precise amount of time elapsed between this frame and the previous frame.
So, that part is fine.
Your problem lies in
transform.position = Vector3.Lerp( transform.position, FollowVector, Time.deltaTime * 4f );
Here you are passing Time.deltaTime * 4f for parameter t of Vector3.Lerp(). I have no idea what you are trying to accomplish, but the number you need to pass there needs to be a gradual transition between 0 and 1, and instead you are passing a randomly varying number of seconds multiplied by some magic constant 4. This does not look correct.
A couple of options that I can think of:
Always use 0.5 for t so that the camera always rushes to the right position and then slows down as it gets closer to it.
Calculate a separate speed vector for the camera, then move the camera using a translation just as you do for the player.

Unity player collision with horizontal small colliders

I know the title isnt very explanatory, but here's my problem:
I have a player (merely a cube with a rigidbody, a collider and a movement script), and I have a floor made of small 1 by 1 by 1 cubes (cubes with box colliders).
For some reason unknown to me, when my player cube falls and tries to collide horizontally with the floor, he just phases through... But want him to get blocked by the cubes just like it does vertically. Any help would be greatly appreciated ;)
heres how the scene looks like
heres a cube object
heres the player object
Here's a gif of the player going through the floor
Here's my c# player movement script (I know its very bad, but I prefer to put this here just in case its linked to my problem) :
void ApplyMovement()
{
transform.position += new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
}
If you need any more info to help me just tell me, I'll provide it as fast as I can.
The value of
new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"))
is framerate-dependent. This means the faster the framerate the faster your object will move. This is usually not what you want. Instead use Time.deltaTime
// Adjust the speed in the inspector
public float speedMultiplicator = 1;
//...
new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")) * speedMultiplicator * Time.deltaTime
to get a framerate-independent movement. Also see the example in Input.GetAxis.
When dealing with RigidBody never change the transform.position (and rotation) directly!
If you would want to make the RigidBody jump to a certain position rather use
rigidBody.position = transform.position + ...;
However what you want is a smooth movement in order to keep the collision detection intact. So you should use RigidBody.MovePosition instead
rigidBody.MovePosition(transform.position + ...);
You don't have to deal with force etc.
Also add a RigidBody component to the floor objects. Even ifnthe object is not moving this improves the collision detection (at the cost of performance in the Physics engine ofcourse). And since the objects are not supposed to move set
isKinematic -> true
use Gravity -> false
you can also set freeze position and freeze rotation.
On both floor and player objects set Collision Detection -> Continous Dynamic. This improves the collision detection and looks for collisions also between frames.
Be aware, however, that dynamic detection is quite expensive so use it only if there is still trouble with too fast objects.
On the player you might want to also use interpolate as well.
Finally
Note that continuous collision detection is intended as a safety net to catch collisions in cases where objects would otherwise pass through each other, but will not deliver physically accurate collision results, so you might still consider decreasing the fixed Time step value in the TimeManager inspector
to make the simulation more precise, if you run into problems with fast moving objects.
For more information see the RigidBody manual and the Colliders manual
I recreated the scenario you described on my end. I put your "ApplyMovement" code in "Update". I was basically able to reproduce your results.
It seems to me that the issue might be Freezing Position X/Z on the Player. I think since you are doing that, you're telling the Rigidbody component that it is not allowed to modify the X/Z positions of the objects as part of it's collision resolution and physics simulation. When I turn those two off, I get results that are (I think) closer to what you're looking for.
One note: your "ApplyMovement" code is frame-locked, meaning your player will move at different speeds at different frame rates. To solve this, you'd need to multiply your Input.GetAxis values by Time.deltaTime.
Also, if your player moves too fast, it'll still be able to pass through collisions and cause odd behavior, so be sure to limit the max movement rate of the player to some reasonable value.
You should be applying a force to the Rigidbody of the character instead of directly manipulating the transform.position (this is preventing the physics engine from resolving the collisions). You're also freezing the X and Z position on the Rigidbody; you don't want that because it entirely prevents the Rigidbody from manipulating those values.
Instead of transform.postion += ... first get a reference to the Rigidbody somewhere in your script (best done in an Awake() method):
private Rigidbody _body;
private void Awake() {
_body = GetComponent<Rigidbody>();
}
Then make sure the vector built from your inputs is being applied to the object's "movement," not its position:
Vector3 inputVector = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
// You can multiply this vector by a float to adjust "speed"
_body.ApplyForce(inputVector, ForceMode.VelocityChange);
Finally, you should read up on the different ForceMode options and decide which one fits your preferences: https://docs.unity3d.com/ScriptReference/ForceMode.html
Don't forget this should happen in a FixedUpdate(), not an Update() (all physics operations should be done in FixedUpdate())
As a side note, there is also the possibility your rigidbodies still up moving too quickly and passing through each-other. Even when you're using forces and velocities, this is possible so if your run into this case down the line, investigate Collision Detection Modes. TLDR; they're settings for performance vs accuracy trade-offs.

Unity 2D scripted friction equation

I am working on the friction for a square object for after it is done being pushed by the player. The block's movements are controlled purely through script using transform.Translate(velocity) each frame and only a boxcollider2D. I am speculating that because i am controlling the block's movements each frame manually is the reason i can't seem to get the boxcolliders2D's physics material 2D friction to work.
Basically, I am trying to recreate the built in physics similar to rigidbodies by using transform.translate, or use the built in physics if possible. So far, i've landed on something like this
newVelocity.x -= ( 1 / (newVelocity.x * newVelocity.x));
In an attempt to reduce the velocity.x each frame until it hits 0 and shorts out. Obviously, this falls apart when the velocity.x becomes a decimal number. I'm stumped on what i should be doing to achieve a friction similar to rigid bodies where it slows down a little at first and then much faster towards the end as it stops. Any and all help appreciated, thanks in advance!
You could compare the signs of the velocity before and after calculating the change in velocity, and set the velocity to zero when the sign changes, like so:
newVelocity.x -= ( 1 / (newVelocity.x * newVelocity.x));
if(Mathf.Sign(previousVelocity.x) != Mathf.Sign(newVelocity.x)) {
newVelocity.x = 0f;
}
One thing, though, is that normal friction is not calculated using the above equation, it's calculated as follows:
newVelocity.x -= frictionCoefficient*Time.fixedDeltaTime;
And with this, you wouldn't need to compare the velocity to the previous velocity as you can just say if the velocity is low, it's zero. You could still compare the previous velocity sign stuff if you wanted to though, but this is probably more efficient
if(newVelocity.x < 0.01f) {
newVelocty.x = 0f;
}

XNA 2D game - behaves differently on friend's computer

I made a simple networking xna game using c# and lidgren. But velocity of objects are different on friend's computer - resolution of window is the same, fps are the same (both 60), application is the same, but he's character is just slower for some reason. For some time his velocity is equal to mine, but then it slows down again.
Other friends do have equal velocities as I do. What could be a problem? The program adjusts movement according to fps, so there is probably no problem in fps.
Without seeing your code its hard to say for sure.
The program adjusts movement according to fps
Well it's generally better to think of animation as a function elapsed since the last update rather than frames per second. That way, the animation will take the same amount of real world time to complete regardless of FPS whether it be fixed or variable update.
Movement deltas should take into account time elapsed since last game update not last/current FPS.
e.g.
public Vector3 Velocity;
public Vector3 Position;
public void Update(GameTime gameTime)
{
float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
// Apply acceleration
Vector3 acceleration = force / Mass;
Velocity += acceleration * elapsed;
// Apply psuedo drag
Velocity *= DragFactor;
// Apply velocity
Position += Velocity * elapsed;
.
.
.
You said you're using lindgren to make it multiplayer? Maybe it's rubberbanding due to latency. Try checking it on 2 computers on lan to see if it resolves the issue.
Possible there's different settings to lindgren to specify how to handle syncing or prediction.

Categories