Why does Physics2D produce different results for AddForce() versus velocity? - c#

Why does Gravity in Physics2D engine for Unity2D act different when these two following lines of codes are implemented alternately?
For example, I have attached to my player sprite a Player Controller C# script:
private float speed = 500f;
RigidBody2D playerChar = null;
And then to make my character walk:
Vector2 vec = new Vector2 (Input.GetAxis("Horizontal"), 0);
playerChar.AddForce(vec * speed);
And the Gravity is set to 50
Result 1: My character avatar falls down normally.
Meanwhile when I do:
Vector2 vec = new Vector2 (Input.GetAxis("Horizontal"), 0);
playerChar.velocity = (vec * speed);
And the Gravity is still set to 50
Result 2: My character now takes a long time to fall (it slowly "floats" down).
Why is that?

This is because you're forcing the y component of the rigidbody's velocity to zero.
When you add force, it adds, it doesn't replace.
When you set the velocity, you're specifically setting it to a Vector2 that has a y value of 0, gravity then kicks in on the fixed update cycle and adds a small amount of gravity, causing your player to fall slowly. Then Update happens again and you force the y value back to 0 once more.

Related

How do I make Rigidbody2D.MovePosition move a gameobject in local space?

I found a way to find what the title says for Rigidbody but not for Rigidbody2D, since the original method involves using Transform.TransformDirection(), which only functions on Vector3 while Rigidbody2D.MovePosition functions on Vector2. I essentially need a bullet to move forward, with two more bullets moving forward but rotated at a 45 degree angle difference.
How would i go about doing this?
Your question reminded me of a game I made for a game jam a while ago so I checked the code, and it seems I used Quaternion.AngleAxis to rotate the bullets.
I'm assuming you have a reference to the prefab you want to clone (in this example, it's projectilePrefab), as well as a firePoint Transform that represents the position you want to shoot from and the rotation of the middle projectile.
// Middle Bullet
GameObject mBullet = Instantiate(projectilePrefab, firePoint.position, firePoint.rotation);
var mRb = mBullet.GetComponent<Rigidbody2D>();
middleRb.AddForce(mRb.transform.up * velocity, ForceMode2D.Impulse);
// Left Bullet
GameObject lBullet = Instantiate(projectilePrefab, firePoint.position, firePoint.rotation);
// Rotate here
lBullet.transform.up = Quaternion.AngleAxis(-45, Vector3.forward) * firePoint.transform.up;
var lRb = lBullet.GetComponent<Rigidbody2D>();
lRb.AddForce(lBullet.transform.up * velocity, ForceMode2D.Impulse);
// Right Bullet
GameObject rBullet = Instantiate(projectilePrefab, firePoint.position, firePoint.rotation);
// Rotate here
rBullet.transform.up = Quaternion.AngleAxis(45, Vector3.forward) * firePoint.transform.up;
var lRb = lBullet.GetComponent<Rigidbody2D>();
lRb.AddForce(lBullet.transform.up * velocity, ForceMode2D.Impulse);
Let me know if you run into any issues with this code, I can't test it right now.
I'm assuming you have a bullet prefab, and that you are instantiating 3 bullets at once, but want 2 of them to be at -45 and 45 degrees respectively.
//bullet is whatever prefab you have
var bl = Instantiate(bullet);
var bl = Instantiate(bullet);
bl.transform.rotation = //Set rotation here to 45 deg
var bl = Instantiate(bullet);
bl.transform.rotation = //Set rotation here to -45 deg
Between Vector2 and Vector3 exists an implicit conversion and vise versa. Both types can more or less be used exchangeable which will either create a Vector3 where z is simply 0 or create a Vector2 only using the x and y and simply ignoring the z.
You can simply pass a Vector3 as parameter to Rigidbody2D.MovePosition and it will implicitly convert it to a Vector2 ignoring the Z component.

moving 2d object unity

I have 1 year experience of coding in C++ , but just yesterday i've started learning Unity.I saw it needs C# so this won't be too different.Right now , I am trying to move a 2d object , but i really want to understand how it works and not just copy some lines of code.So , this is how they do it :
//
float horizontal = Input.GetAxis("Horizontal");
//which i assume gets the x coordonate of my object;
myRigidBody.velocity = new vector2(horizontal , myRigidBody.velocity.y);
//
And i don't think i get the idea of this code.I read that velocity takes 2 values (x,y) but i am not quite sure what it is doing with them.And getAxis takes a value from [-1,1] which i also don't understand.If you could help me i'd be grateful.
Input.GetAxis("Horizontal"); returns the current value for the axis named Horizontal as defined in Unity's Input manager.
Also, new vector2(horizontal, myRigidBody.veloicty.y); is probably a typo, because the type of Rigidbody.velocity is Vector3. And Vector2 is an object that has an implicit conversion to Vector3.
Anyway, myRigidBody.velocity = new Vector2(horizontal , myRigidBody.velocity.y); creates an instance of a Vector2 type where the x component is horizontal, and the y component is the current y component of the rigidbody's velocity, and then it assigns that Vector2 to the rigidbody's velocity.
GetAxis("Horizontal") returns the values from left (-1,0,0) and right (1,0,0) when you press the arrows keys. Anyway, you'll get the values interpolated and not a sudden change. Try the code below and see how it looks on the inspector
public float horizontal;
private void Update()
{
horizontal = Input.GetAxis("Horizontal");
}
As for the velocity, you "force change" the velocity of the rigidbody in any direction you want x,y (horizontal,vertical axis). You can also multiply your axis values with a speed variable.

How do I Instantiate Objects In Unity?

I am making a top down game in Unity, how do I instantiate objects based on the players current position from the top of the camera view in Unity?
So far I have tried:
Instantiate(prefab, player.transform.position * 2 * Space.World)
but this didn't work. This just spawned objects from the center of the camera.
Space is simply an enum and has nothing to do with what you are trying! (Its purpose is to define the relative transform space for e.g. Transform.Rotate or Transform.Translate)
in that enum afaik
Space.World simply has the int value 0
Space.Self has the int value 1
so what you actually do is
Instantiate(prefab, player.transform.position * 2 * 0);
which equals
Instantiate(prefab, Vector3.zero);
which means the object is instantiated at World position 0,0,0.
Also using
Instantiate(prefab, player.transform.position * 2);
looks a bit strange. Are you sure you want to duplicate the actual position of the player? This would mean the spawned object is always on a line with the player and the World 0,0,0 and always double as far from the center as the player.
To me it sounds more like you rather want to spawn something in front of the player ... depending on the player's view direction (in topdown games usually player.transform.up or player.transform.right) so I guess what you are trying to do instead is something like
Instantiate(prefab, player.transform.position + player.transform.forward * 2);
which would instead spawn the object 2 Unity units in front of the player object
After your comment it sounds like instead you want to spawn the object at the player position on the X-axis but "over it" on the Y-axis so
Instantiate(prefab, player.transform.position + Vector3.Up * 2);
maybe you'll have to tweak the 2 depending how your player can move and how far it has to be to be "off screen". Alternatively you could also use a bit more complex using Camera.ScreenToWorldPoint
// get players position
var playerPos = player.transform.position;
// get camera's position
var cameraPos = Camera.main.transform.position;
// get difference on Z-axis
var cameraDistance = cameraPos.z - playerPos.z;
// Get the world 3d point for the upper camera border
// don't care about the X value
// and as distance we use the z-distance to the player
var cameraTopPoint = Camera.main.ScreenToWorldPoint(new Vector3(0, Camera.main.pixelHeight, cameraDistance);
// use the players X-axis position
cameraTopPoint.x = player.transform.x;
// to spawn it exactly on the Screen border above the player
Instantiate(prefab, cameraTopPoint);
// or to spawn it a bit higher
Instantiate(prefab, cameraTopPoint + Vector3.Up * 1);
Update
you said you want the prefab to be spawned on the "opposide" of the player position on the X axis.
If your Camera is static on world x=0 than this is actually quite simple:
cameraTopPoint.x = -player.transform.x;
If your camera is moving or not on x=0 than we have to calculate it already on the screen position level:
// get players position
var playerPos = player.transform.position;
// additionally convert the player position to screen space
var playerScreenPos = Camera.main.WorldToScreenPoint(playerPos);
var centerX = Camera.main.pixelWidth / 2.0f;
// get the difference between camera an player X (in screen space)
var differenceX = Mathf.Abs(playerScreenPos.x - centerX);
// here calculate the opposide side
var targetX = centerX + differenceX * (playerScreenPos.x < centerX ? 1 : -1);
// or alternatively if you want e.g. that the prefab always
// spawn with a bit of distance to the player even though he is very close
// to the center of the screen you could do something like
//var targetX = centerX + centerX / 2 * (playerScreenPos.x < centerX ? 1 : -1);
// Get the world 3d point for the upper camera border
// with the calculated X value
// and as distance we use the z-distance to the player
var cameraTopPoint = Camera.main.ScreenToWorldPoint(new Vector3(targetX, Camera.main.pixelHeight, playerScreenPos.z);
// to spawn it exactly on the Screen border above the player
Instantiate(prefab, cameraTopPoint);
// or to spawn it a bit higher
Instantiate(prefab, cameraTopPoint + Vector3.Up * 1);
Other Update
Ok so you want the prefab always spawn next to the player in a certain distance. The side depends on which side of the screen the player is so you could actually use the first approach again but just add the desired distance:
// Adjust this is the Inspector (in Unity units)
public float spawnDistanceToPlayer;
...
// get players position
var playerPos = player.transform.position;
// additionally convert the player position to screen space
var playerScreenPos = Camera.main.WorldToScreenPoint(playerPos);
var centerX = Camera.main.pixelWidth / 2.0f;
// Get the world 3d point for the upper camera border
// with the calculated X value
// and as distance we use the z-distance to the player
var cameraTopPoint = Camera.main.ScreenToWorldPoint(new Vector3(playerScreenPos.x, Camera.main.pixelHeight, playerScreenPos.z);
// now add or reduce spawnDistanceToPlayer
// depending on which side of the screen he is
cameraTopPoint.x += spawnDistanceToPlayer * (playerScreenPos.x < centerX ? 1 : -1);
// OR if your camera sits static on X=0 anyway you also could compare
// the player position directly without calculating in screenspace:
cameraTopPoint.x += spawnDistanceToPlayer * (playerPos.x < 0 ? 1 : -1);
// to spawn it exactly on the Screen border above the player
Instantiate(prefab, cameraTopPoint);
// or to spawn it a bit higher
Instantiate(prefab, cameraTopPoint + Vector3.Up * 1);
Typed on smartphone so might not be "copy-paste-able" ;)

Control begin and end point object between 2 points

im trying to move a object in unity between 2 points, and at the moment it kinda works strange, i read the documentation and it says that the object begin point is (0,0,0) so my object goes under my other mesh that i have there, and the end point i can actually control, in my case it is 10, i want the object to move between 1.5 and 10(not 0 to 10)
i have this
void Update () {
transform.position = new Vector3(transform.position.x,Mathf.PingPong(Time.time,10.0f), transform.position.z);
}
when i try to put speed on the ball doing this:
void Update () {
transform.position = new Vector3(transform.position.x,Mathf.PingPong(Time.time,10.0f) * 10, transform.position.z);
}
the object does not colide and goes back at the end point it just stop looping and never came back how can i correct this 2 problems?
If your object has a collider, I suggest you move it via its Rigidbody rather than its Transform, to avoid potential collision issues. Try this:
public float MinY = 1.5f; // y position of start point
public float MaxY = 10f; // y position of end point
public float PingPongTime = 1f; // how much time to wait before reverse
public Rigidbody rb; // reference to the rigidbody
void Update()
{
//get a value between 0 and 1
float normalizedTime = Mathf.PingPong(Time.time, PingPongTime) / PingPongTime;
//then multiply it by the delta between start and end point, and add start point to the result
float yPosition = normalizedTime * (MaxY - MinY) + MinY;
//finally update position using rigidbody
rb.MovePosition(new Vector3(rb.position.x, yPosition, rb.position.z));
}
Here you have a better control on the distance to travel, and the speed.
Actually I didn't get exactly what are the problem you faced. But don't forget here and in your try, that you are directly modifying the position of the object, not adding forces or else.
Hope that helps you.
I think you simply misunderstood how the Mathf.PingPong method works :
first argument t is the value you want to "clamp" between 0 and the given length : this is were you want to put the Time.time as you did since this value will increase over time and therefore perpetually oscillate. If you want to increase/decrease the oscillation speed you have to multiply it.
second argument length is the max value of the "clamp" : if you want to increase/decrease the distance (in your case) you have either set it to 0 and multiply the whole Mathf.PingPong(...) by a value or directly give it the wanted value (both implementations will have a different effect.
Mathf.PingPong(Time.time * speed, 1.0f) * value : speed will affect the oscillation speed / value will affect the max value reached AND the speed / time to complete the oscillation (back and forth) will remain the same as value changes and decrease as speed increases
Mathf.PingPong(Time.time * speed, value) : speed will affect the oscillation speed / value will affect the max value reached BUT NOT the speed / time to complete the oscillation (back and forth) will increase as value increases and decrease as speed increases
About your other problems :
If you want to move your object between 1.5 and 10 you have to write something like this :
transform.position = new Vector3(transform.position.x, 1.5f + Mathf.PingPong(Time.time, 10.0f - 1.5f), transform.position.z);.
Also if you want to detect collision, avoid setting position manually as it will mess up with Physics and cause weird behaviors. Best way to move your object while keeping physic working is to do as #Heldap said using Rigidbody.MovePosition.

Correct rotation with Quaternion

I have some problems with a rotating marble.
I've tried it with Matrix.CreateFromYawPitchRoll and Matrix.CreateRotation but there were some problems, I think it's due to the Gimbal lock effect.
So, I've tried using quaternions instead, but nothing changed.
When moving on only an axis it works fine, but when the rotation occurs on two different axes the marble still rotates on wrong axes.
Here's my code:
// declarations
Vector3 Position = Vector3.Zero;
Vector3 Rotation = Vector3.Zero;
Quaternion qRotation = Quaternion.Identity;
AbsoluteBoneTransforms = new Matrix[Model.Bones.Count];
Model.CopyAbsoluteBoneTransformsTo(AbsoluteBoneTransforms);
In the Update method:
Position += speed;
Rotation = speed * MathHelper.ToRadians(-1.5f);
Quaternion rot = Quaternion.CreateFromAxisAngle(Vector3.Right, Rotation.Z) *
Quaternion.CreateFromAxisAngle(Vector3.Backward, Rotation.X);
qRotation *= rot;
And in the Draw method:
effect.World = AbsoluteBoneTransforms[mesh.ParentBone.Index] *
Matrix.CreateFromQuaternion(qRotation) * Matrix.CreateTranslation(Position);
What's wrong? Is it wrong to use Quaternion.CreateFromAxisAngle on multiple axes?
EDIT
I've tried calculating directly the axis of rotation of my marble, instead of using combination of multiple axes:
angle += speed.Length() * angularVelocity;
qRotation = Quaternion.CreateFromAxisAngle(Vector3.Cross(speed, Vector3.Up), angle);
qRotation.Normalize();
angle is a float that keeps track of the current movement.
This solution doesn't seem to create Gimbal lock, but marble rotations aren't correct, it seems that the rotating speed is not constant, but became faster and slower over time, I can't understand why.
If I "concatenate" the quaternions I get every frame using
qRotation *= Quaternion.CreateFromAxisAngle(Vector3.Cross(speed, Vector3.Up), angle)
the Gimbal lock effect is still visible.
Here's how I've tackled that:
I'm assuming speed is a vector representing the direction the ball is rolling and whose magnitude represents the rate it is traveling in that direction.
Vector3 axis = Vector3.Cross(speed, Vector3.Up);
float angle = speed.Length();//factor by delta time if neccesary
Quaternion rotationThisFrame = Quaternion.CreateFromAxisAngle(axis, angle * (1/radiusOfBall));
then you can concatenate that to your qRotation. Also, you may need to normalize your quaternion after concatenation.
Update: The correct answer to this question/thread was reversing the order that quaternions concatenate in. With respect to XNA, matrices combine left to right, quaternions combine right to left.

Categories

Resources