Trouble rotating vector inside unity - c#

I'm making a game where you rotate your player and change gravity. the problem im having is to do with actually putting the rotation of the player, the keyboard inputs of the player and the current gravity together.
Line 1(Quaternion.FromToRotation) works completely fine until gravity is down(gravityDir(0, -1, 0)) or up(gravityDir(0, 1, 0)) and in that case the y component of movementInput will be inverted.
I can't just check if its one of the two and flip it back because I need it to be able to gradually change from one value to the other or otherwise be in between a different angle(like (1, 1, 0) for instance)
movementInput is just wasd put into a vector(w-s being x, a-d being z).
gravityDir is a vector for gravity(0, -1, 0) for down (0, 1, 0) being up (1, 0, 0) being positive x direction etc.
headAttach is a GameObject that controls the players head, I'm getting its y value in the code to rotate the movementInput vector so I can "move in the direction i'm facing".
movementInput = Quaternion.FromToRotation(gravityDir, Vector3.up) * movementInput;
movementInput = Quaternion.AngleAxis(headAttach.transform.localRotation.eulerAngles.y, transform.up) * movementInput;
rb.AddForce(movementInput * 5 * movementSpeed);

Don't use FromToRotationin this case, because you care about more than the direction of up. You also care about the directions of forward/right.
Instead, use LookRotation. You need to find the closest direction to the direction that the player is looking at (I assume that's headAttach.transform.forward) which is perpindicular to the direction of gravity. You can use cross products for that. Then, use that direction for the forward parameter of LookRotation and the direction opposite gravity for up:
Vector3 movementInput = Vector3.forward * Input.GetAxis("Vertical")
+ Vector3.right * Input.GetAxis("Horizontal");
Vector3 upDir = -gravityDir;
Vector3 lookDir = headAttach.transform.forward;
Vector3 rightDir = Vector3.Cross(upDir, lookDir);
Vector3 forwardDir = Vector3.Cross(rightDir, upDir);
movementInput = Quaternion.LookRotation(forwardDir, upDir) * movementInput;
rb.AddForce(movementInput * 5 * movementSpeed);

Related

Unity3D Adjusting CUBE "endRotation" on right ( x or z) axis

I am new in Unity3D scripting especially when it comes to specifics of Transform, Quaternions, Vector3.
Down there is a script. When I roll the cube it flips in the right direction. However when cubes' local axis doesn't match the world axis the cube's endRotation is performed on the wrong axis.
Can anybody help me to resolve this issue : "endRotation" would flip the cube on right axis regardless of the relation between LOCAL axis <=> WorldAxis.
I was trying to resolve it for a week. Of cause no success.
Further down there code and video.
if (Input.GetAxis("Horizontal") > buttonDown)
{
StartCoroutine(FlipTheCube(Vector3.right));
return
}...
.
.
.
public IEnumerator FlipTheCube(Vector3 direction)
{
startFliping = false;
float rollStartTime = 0;
float rollAngle = 90;
float halfWidth = transform.localScale.z / 2;
Vector3 pointAround = transform.position + (Vector3.down * halfWidth) + (direction * halfWidth);
Vector3 rollAxis = Vector3.Cross(Vector3.up, direction);
Quaternion rotation = transform.rotation;
Quaternion **endRotation** = rotation * Quaternion.Euler(rollAxis * rollAngle);
Vector3 endPlacement = transform.position + direction;
float oldAngle = 0;
while (rollStartTime < rollDurtnTime)
{
yield return new WaitForEndOfFrame();
rollStartTime += Time.deltaTime;
float newAngle = (rollStartTime / rollDurtnTime) * rollAngle;
float rotateThrough = newAngle - oldAngle;
oldAngle = newAngle;
transform.RotateAround(pointAround, rollAxis, rotateThrough);
}
transform.position = endPlacement;
transform.rotation = **endRotation**;
startFliping = true;
}
Here is a link to the youtube video
First part of video If cubes local axis is matching world axis the "endRotation" is flipping correctly Second part of video as soon cubes local axis is not matching world axis "endRotation" goes wrong.
Your problems lie within these lines:
Vector3 rollAxis = Vector3.Cross(Vector3.up, direction);
Quaternion rotation = transform.rotation;
Quaternion endRotation = rotation * Quaternion.Euler(rollAxis * rollAngle);
// ...
transform.RotateAround(pointAround, rollAxis, rotateThrough);
First problem is Euler vs angle/axis usage.
If you've only been rotating against Vector3.right, Vector3.forward, and their negatives you might not have even noticed this yet.
In the first part, you use rollAxis as an Euler angle representation, and in the second part as an axis.
The problem is that for a rotation, its axis is usually not the same as a Euler representation of that rotation! For example, the rotation made by rotating around (0.7, 0.0, 0.7) by 90° is completely different from an Euler rotation of (63°, 0°, 63°)!
Instead, just use rollAxis consistently as an angle/axis representation. You can get the quaternion form using Quaternion.AngleAxis.
Second problem is global vs local rotation.
This is a problem whose effects you're definitely already noticing.
In the first part, you apply rollAxis as a local rotation. This is because rollAxis is the second term of the * operator.
In the second part, RotateAround rotates around a global axis defined by rollAxis.
Keep it global or local for both. Global is simpler, and it appears that's what you're trying to do (judging from transform.position + direction), so you should have rotation as the second term of the * operator.
Altogether, the problems can be fixed by just changing the Quaternion endRotation = rotation * Quaternion.Euler(rollAxis * rollAngle); line:
Vector3 rollAxis = Vector3.Cross(Vector3.up, direction);
Quaternion rotation = transform.rotation;
Quaternion endRotation = Quaternion.AngleAxis(rollAngle, rollAxis) * rotation;
// ...
transform.RotateAround(pointAround, rollAxis, rotateThrough);
See here for more information about the quaternion * operator.
I suggest you using DoTween asset (https://assetstore.unity.com/packages/tools/animation/dotween-hotween-v2-27676):
DORotate(Vector3 to, float duration);
Using tweeners will save you a lot of time writing your own animations.

Unity3D make object follow another but to move further

So, here is what i have:
SteamVR's hand gameobject
3D sphere.
what i want:
The sphere to move to same direction/position as the hand does, but it to move further with a multiplier. E.g. i move the VR controller and the hand moves 1 unit. I want that the sphere moves to the same direction in a same amount of time but e.g. 2 units. how do i do this?
i tried simple
sphere.transform.position = ControllerToFollow.position +2f;
but then the sphere is always offset.
position is a Vector3, which is essentially 3 floats - you can't plus a Vector3 with a float unless you overload the + operator. Otherwise what you can do is the following:
Vector3 followPos = new Vector3(ControllerToFollow.position.x + 2f,
ControllerToFollow.position.y + 2f,
ControllerToFollow.position.z + 2f);
sphere.transform.position = followPos;
If you only want it to follow on one axis, then you can do the following:
Vector3 followPos = new Vector3(ControllerToFollow.position.x + 2f, // Follow on x Axis
ControllerToFollow.position.y, // Y axis is the same
ControllerToFollow.position.z); // X Axis is the same
sphere.transform.position = followPos;
Edit: I think I understand your problem better now. Here's a better version.
if (Vector3.Distance(sphere.transform.position, ControllerToFollow.position) >= 2f)
{
// Code that makes the sphere follow the controlling
}
Just track the movement Delta of the hand and multiply it by a certain multiplier.
At the beginning of the manipulation store
private Vector3 lastControllerPosition;
...
lastControllerPosition = ControllerToFollow.position;
then in every frame compare
var delta = ControllerToFollow.position - lastHandPosition;
// Don't forget to update lastControllerPosition for the next frame
lastControllerPosition = ControllerToFollow.position;
Now in delta you have a movement of the controller since the last frame. So you can assign it to the sphere with a multiplier using Transform.Translate
sphere.transform.Translate(delta * multiplier, Space.World);
or simply using
sphere.transform.position += delta * multiplier;

Quarternions in Unity

I am programming an 6DOF space-shooter in 3D with Unity and C#. I am using mouse and keyboard for the movement. With the mouse I use Quarterions for the rotation in X, Y and Z axis.
I tried the following code:
void Update()
{
float test = Input.GetAxis("Pitch");
float test2 = Input.GetAxis("Yaw");
float test3 = Input.GetAxis("Roll");
rotX += test;
Quaternion q1 = Quaternion.AngleAxis(rotX, Vector3.right);
rotY += test2;
Quaternion q2 = Quaternion.AngleAxis(rotY, Vector3.up);
roll += test3;
Quaternion q3 = Quaternion.AngleAxis(roll, Vector3.forward);
transform.rotation = q1 * q2 * q3;
}
The code works, but not for all the angles. It looks that over every 90 degrees the movement is not correct (x-axis and y-axis turn around). I expect that this code works for every angle, what is wrong in this code?
You are using absolute (world) vectors as your frame of reference. change Vector3.up to transform.up, Vector3.forward to transform.forward and Vector3.right to transform right, this way you will be rotating along local axis, and gimbal lock should be less problematic.
Order of operations will still matter, but for relatively small delta it should work as expected.
I think you are doing the right thing multiplying the quaternions before assigning the new rotation, otherwise each local vector would change with each operation - the behaviour would be slightly different
As #Zambari said, make sure you're rotating around local axes. But also, you seem to be storing the orientation in Euler angles, rotX, rotY, rotZ and then setting that as the absolute rotation. This is bad. What you want is to apply the delta/modification to the transform's stored quaternion:
void Update() {
var yaw = Quaternion.AngleAxis(Input.GetAxis("Yaw"), transform.up);
var pitch = Quaternion.AngleAxis(Input.GetAxis("Pitch"), transform.right);
var roll = Quaternion.AngleAxis(Input.GetAxis("Roll"), transform.forward);
transform.rotation = transform.rotation * yaw * pitch * roll;
}
note: the order of multiplication on the final line may be incorrect.
After saying all that, if all you want is to control your character, try (angles in degrees):
transform.Rotate(xAngle, yAngle, zAngle, Space.Self);

Moving a sprite using math in Unity

I am currently making a game in Unity, where characters are moving towards a point at the bottom of the screen, from a random x-position at the top of the screen. As they have to keep a constant speed (and later on possibly be able to move in specific patterns), I cannot use Vector3.Lerpfor this.
Instead, I tried using some simple math. StaticInfo.instance.GetPlayerPosition() is the targeted position. The code happens in the character's FixedUpdate().
float aVal = (myTransform.localPosition.y - StaticInfo.instance.GetPlayerPosition().y) /
(myTransform.localPosition.x - StaticInfo.instance.GetPlayerPosition().x);
float degrees = Mathf.Atan(aVal) * Mathf.Rad2Deg;
Vector3 newPos = new Vector3(myTransform.localPosition.x - (distance * speed * Mathf.Cos(degrees)),
myTransform.localPosition.y - (distance * speed * Mathf.Sin(degrees)),
1);
myTransform.localPosition = newPos;
My problem is when these characters are created (instantiated from prefab), they make a small 180-degrees loop before moving in the wanted direction. After that, they move exactly as wanted.
Is using math the best solution? And if it is, why does it do a funky drift when initialized?
This may be a more simple solution to what you are trying to achieve:
Vector3 moveDirection = (StaticInfo.instance.GetPlayerPosition() - myTransform.localPosition).normalized;
Vector3 newPos = myTransform.localPosition + moveDirection * distance * speed;
myTransform.localPosition = newPos;
If you want the object to point in the direction of the movement you can do:
myTransform.rotation = Quaternion.Euler(0, 0, Mathf.atan2(moveDirection.y, moveDirection.x)*Mathf.Rad2Deg - 90); // may not need to offset by 90 degrees here;

Translate object until collision

I want my object to keep moving in the target's direction forever or until it collides, the collision part I have already handled it; However, I am having problems with the movement part.
I first try to rotate my target using these lines of code
Vector2 diff = target - transform.position;
float angle = Mathf.Atan2(diff.y, diff.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0.0f, 0.0f, angle);
This works perfect and my object rotates in the direction I want it to.
In my Update method I have the following
if (isMoving)
{
Vector2 f = transform.forward;
transform.position = Vector3.MoveTowards(transform.position, target + Vector3.forward, speed * Time.deltaTime);
}
Now this runs but fails to accomplish the goal and I know why, it makes sense but not sure how to fix it. The object moves to the point in the correct direction but I don't want it to stop at the target, I want it to keep going.
I also tried
rb.MovePosition(rb.position + f * Time.deltaTime * speed);
rb is a rigidbody2D
as well as
rb.AddForce(rb.position + f * Time.deltaTime * speed);
But in both cases the object rotates but never moves
I also used translate and same behavior as MovePosition
P.S. It's a 2D game
After looking over the internet, I didn't find anything that really does what I am looking for, you are not able to translate a kinematic object forever (Obviously you have to handle that the object should get destroyed at some point or something, but that's not the issue). So I went ahead and decided to write my own library to make this simple. I was able to achieve my goal using the line equation. Simple I took these steps to solve my issue.
At start I get the slope between 2 points
Calculate the y-intercept
translate the object using the line equation with a fixed X value (step), every x value find the corresponding y value and translate the object to it.
Repeat step 3 in a FixedUpdate and that's it.
Of course there is more to it, handling cases where x = 0 which will give a 0 division for the slope or y = 0, etc... I solved all of that in the library. For anyone interested you can check it out here EasyMovement
If you don't want the library then here is a simple code that will do it.
//Start by Defining 5 variables
private float slope;
private float yintercept;
private float nextY;
private float nextX;
private float step;
private Vector3 direction; //This is your direction position, can be anything (e.g. a mouse click)
In the Start Method
//step can be anything you want, how many steps you want your object to take, I prefer 0.5f
step = 0.5f;
//Calculate Slope => (y1-y2)/(x1-x2)
slope = (gameObject.transform.position.y - direction.y) / (gameObject.transform.position.x - direction.x);
//Calculate Y Intercept => y1-x1*m
yintercept = gameObject.transform.position.y - (gameObject.transform.position.x * slope);
Now in FixedUpdate
//Start by calculating the nextX value
//nextX
if (direction.x > 0)
nextX = gameObject.transform.position.x + step;
else
nextX = gameObject.transform.position.x - step;
//Now calcuate nextY by plugging everything into a line equation
nextY = (slope * nextX) + yintercept;
//Finally move your object into the new coordinates
gameObject.transform.position = Vector3.MoveTowards(gameObject.transform.position, new Vector3(nextX, nextY, 0), speed * Time.deltaTime);
Keep in mind this won't do the heavy lifting, there are a lot of conditions that needs to be taken into consideration.

Categories