I'd want to rotate the character along the y-axis (green) of another gameobject.
Here's a picture of my system.
I'd want to rotate the character in the direction indicated by the red arrow (see figure, it is not x axis).
I looked at other questions and tried a lot of code,
but I still didn't achieve the desired results.
This is my latest code that created the picture above.
The picture on the right is what I want, but the picture on the left is not.
Any advice is appreciated. Thank you!
IEnumerator RotateToDirection(){
var targetRotation = new Vector3(0, ArrowObject.transform.localEulerAngles.y, 0);
var elapsedTime = 0.0f;
var waitTime = 1f;
while (elapsedTime < waitTime)
{
elapsedTime += Time.deltaTime;
Character.transform.localEulerAngles
= Vector3.Lerp(Character.transform.localEulerAngles, targetDirection, (elapsedTime / waitTime));
yield return null;
}
}
Don't use eulerAngles
When you read the .eulerAngles property, Unity converts the Quaternion's internal representation of the rotation to Euler angles. Because, there is more than one way to represent any given rotation using Euler angles, the values you read back out may be quite different from the values you assigned. This can cause confusion if you are trying to gradually increment the values to produce animation.
From your images it sounds like you want the character's forward axis to align with another objects up axis so you would probably use e.g. Quaternion.LookRotation something like e.g.
var startRotation = Character.transform;
var targetDirection = ArrowObject.transform.up;
var targetRotation = Quaternion.LookRotation(targetDirection);
for(var timePassed = 0f; timePassed< 1f; timePassed += Time.deltaTime)
{
Character.transform.rotation = Quaternion.Lerp(startRotation, targetRotation, timePassed / 1f);
yield return null;
}
Character.transform.rotation = targetRotation;
Related
I am making a 3d side scroller. need to rotate gun on z axis to face mouse on x,y plane. This is What I have so far. Tried lookat and angle. They sort of worked but would not point correctly. only half of the screen. I just think I am missing something.
void FixedUpdate()
{
screenPosition = Input.mousePosition;
screenPosition.z = 1; //mainCamera.nearClipPlane + 1;
worldPosition = mainCamera.ScreenToWorldPoint(screenPosition);
worldPosition.z = 0;
//Angle?????
transform.rotation = Quaternion.Slerp(rb.transform.rotation,
Quaternion.Euler(0,0,angle), 0.7f);
}
In order to get the angle there is no need to convert to world space.
Indeed actually it is way easier to go the other way round and convert your objects position into screen space.
Then you can simply use the Mathf.Atan2 on the pixel space delta
var mousePosition = Input.mousePosition;
var objectScreenPosition = mainCamera.WorldToScreenPoint(rb.position);
var direction = mousePosition - objectScreenPosition; // No need to normalize btw
var angle = Mathf.Atan2(direction.x, direction.y) * Mathf.Rad2Deg;
// since this seems to be a rigidbody I would rather use this
rb.MoveRotation(Quaternion.Slerp(rb.rotation, Quaternion.Euler(0, 0, angle), 0.7f);
// NOTE: Or in case your rb is actually Rigidbody2D then rather simply
rb.MoveRotation(Mathf.Lerp(rb.rotation, angle, 0.7f));
this is my direction vector
new Vector3(target.transform.position.x - projectile.position.x, 0, target.transform.position.z - projectile.position.z).normalized
I tried multiplying it by Quaternion.AngleAxis(45, Vector3.up) but that simply doesn't work
All other orientations like Vector3.left, right, etc. don't help either
The only thing I could observe is the way that the angle changes when I move the target or projectile
You were close. Use cross product to get the axis you need, use that in AngleAxis, then finally apply that rotation to your starting direction:
Vector3 RotateTowardsUp(Vector3 start, float angle)
{
// if you know start will always be normalized, can skip this step
start.Normalize();
Vector3 axis = Vector3.Cross(start, Vector3.up);
// handle case where start is colinear with up
if (axis == Vector3.zero) axis = Vector3.right;
return Quaternion.AngleAxis(angle, axis) * start;
}
I have made a projectile shooting system. I want to shoot an object from position A to B following an indicated path. Everything is working fine except for one thing. The velocity applied on the object is calculated based on the distance between A and B, the value of time is 1 to travel this distance. Meaning that the farther I hit, the quicker it goes. I want to have control of the force applied. Meaning that it should go with my set speed whether I hit near or far. Tried normalizing the velocity and multiplied it by my custom force value, but then it moves away from its trajectory.
(See this link below, no matter how close or far we hit the object. It goes with the same speed while following the trajectory indicated. I want to develop this functionality.
https://play.google.com/store/apps/details?id=com.ghakilo.trickytrack)
Vector3 calculateVelocity(Vector3 target, Vector3 origin, float time)
{
Vector3 distance = target - origin;
Vector3 distanceXZ = distance;
distanceXZ.y = 0f;
float Sy = distance.y;
float Sxz = distanceXZ.magnitude;
float Vxz = Sxz / time;
float Vy = 0f;
Vy = Sy / time + 0.5f * Mathf.Abs(Physics.gravity.y) * time;
Vector3 result = distanceXZ.normalized;
result = result * Vxz;
result.y = Vy;
return result;
}
Physics time!
Velocity is a vector whose magnitude is speed.
If you want to fix the speed at which your projectile starts, that only leaves the direction of the velocity in your hands, so you need to calculate the direction in which you want to yeet your projectile.
If you're like me, you shoot straight at the target, so you'd set the direction vector to go from the origin to the target. Which is simply target - origin, or what you calculate as distance. I'm going to call this direction because that's what it really is being used for.
Now to use this as the direction vector for your velocity, convert distance to a unit vector (not sure how you do this in the unity framework, but direction.Normalize()?)
Then multiply this by the speed to get your velocity vector!
Vector3 calculateVelocity(Vector3 target, Vector3 origin, float speed)
{
Vector3 direction = target - origin;
direction.Normalize();
Vector3 result = direction * speed;
return result;
}
First add rigidbody to your bullet and set the gravity scale to .5
and add this code to your bullet prefab
Vector3 calculateVelocity(Transform target, Transform origin, float time)
{
Vector3 direction = target.transform.position - origin.transform.position;
float distance = Vector3.Distance(origin.transform.position, target.transform.position);
result = direction.normalized * distance * speed * Time.deltaTime;
return result;
}
I have steering wheel that is controlled by physical daydream controller(it works similar to wii controller). I use this code to do it:
void Update() {
transform.localRotation = GvrController.Orientation;
transform.localRotation = new Quaternion(0.0f, 0.0f, -transform.localRotation.y, transform.localRotation.w);
}
I need to mess with axis, beacause default position of the controller isn't good for a steering wheel.
But in 3-axis angle between maximum rotation to the left and to the right is 180 degrees. In this range everything is fine, but if I rotate a little bit more this values change to negative and everything is messed up. What can i do to allow the player to rotate only in this range(0 - 180 on z axis of 3-axis rotation)?
EDIT: The main problem is that the values of rotation after crossing 0 or 180 change to negative values, which are the same for both, but in different order. After crossing 0 it s form -1 to -180 and and for 180 its -180 to -1.
Firstly, we need a value that we can actually clamp. We'll get that from the eulerAngles.z field (as a typical onscreen wheel rotates about z - you might need to change that to some other field depending on the controller):
void Update() {
// Get the angle:
float angle = GvrController.Orientation.eulerAngles.z;
// The magic - clamp it:
if(angle < -180f){
angle = -180f;
}
else if(angle > 180f){
angle = 180f;
}
// Apply it as a new rotation:
transform.localRotation = Quaternion.Euler(0f,0f,angle);
}
Try this:
if (transform.eulerAngles.z > 180)
transform.eulerAngles = new Vector3(transform.eulerAngles.y, transform.eulerAngles.y, 180);
else if (transform.eulerAngles.z < 0)
transform.eulerAngles = new Vector3(transform.eulerAngles.y, transform.eulerAngles.y, 0);
If anyone wonders I found a solution, based on a script from Luke's answer. I realized that the values that change to negative are fine, only thing wrong with them is that they are negative. So this is the working script:
transform.localRotation = GvrController.Orientation;
float angle = -transform.localRotation.y;
if (angle < 0.0f) {
angle = Mathf.Abs(angle);
}
transform.localRotation = new Quaternion(0.0f, 0.0f, angle, transform.localRotation.w);
Try this:
If (transform.rotation > 180)
transforn.rotation = 180;
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.