Raycast in angle direction - unity3d

In unity3d I raycast from player forward. But I want also raycast some angle from forward.
I try this
var dir = this.transform.TransformDirection(new Vector3(Mathf.Sin(angle), 0, Mathf.Cos(angle)));
Physics.Raycast(this.transform.position, -dir, out hit, 9999);
But get wrong result.

Take in mind that by using TransformDirection value of direction that will be raycasted in is compared to transform the script is attached to. If you set angle to zero it will shoot raycast in forward direction of transform the script is attached to. If you want to make the raycast independent of where the transform is facing just do dir like this:
var dir = new Vector3(Mathf.Sin(angle), 0, Mathf.Cos(angle));
Also take in mind in what unit your angle is? Mathf.Sin and Mathf.Cos take in angle value in radians, if you want use angles in degrees (0, 90, 180, 270) you need to convert them to radians before calculating Sin and Cos. To do this just multiply if with Rad/Deg value like this:
angle = angle * Mathf.Deg2Rad;
And do the rest like before. To help you more I need you to provide more info (test results, what you want more specific...).

Related

Rotating rotation value by different normalized vector directions

I have written a script in Unity which takes a SkinnedMeshRenderer and AnimationClip and rotates the vertices in each by a specified number of degrees. It looks mostly correct except that rotations seem to be incorrect. Here is an example bone rotation (in euler angles) in the skeleton along with the correct values that would be needed for the animation to look correct.
With no rotation: (0, 0, -10)
Rotated 90 degrees: (-10, 0, 0)
Rotate 180 degrees: (0, 0, 10)
I have been trying to find a way to rotate these bones to make this conversion make sense with the data I have here, but have come up short. I know I want to rotate these values around the Y axis, but don't actually want the Y value in the euler angle to change. I am aware I could just reorient the root bone around the Y axis and the problem would be solved, but I want to have no rotation in the Y axis. I am "fixing" some older animations that have unnecessary rotation values in them.
var localBoneRotation = new Quaternion(keysX[j].value, keysY[j].value, keysZ[j].value, keysW[j].value).eulerAngles;
var reorientedForward = Quaternion.AngleAxis(rotation, Vector3.up) * Vector3.forward;
localBoneRotation.x *= reorientedForward.x;
localBoneRotation.y *= reorientedForward.y;
localBoneRotation.z *= reorientedForward.z;
var finalRotation = Quaternion.Euler(localBoneRotation);
keysX[j].value = finalRotation.x;
keysY[j].value = finalRotation.y;
keysZ[j].value = finalRotation.z;
keysW[j].value = finalRotation.w;
I have also tried using a matrix and Vector3 but most of the time I end up with values in the Y. Perhaps I am going about this incorrectly. I just need to be able to specify an angle rotation and then have the input data match the final euler angles with each of these data points.

Unity 3D. How to find out where the movement of an object is directed relative to its rotation?

I need to make a 3d top down character controller and animate it.
To begin with, I made a code for the character's movement relative to the camera. And this code works, the character walks and turns well.
Vector3 inputDirection = new Vector3(_input.GetAxis.x, 0, _input.GetAxis.y);
float targetAngle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg + _camera.rotation.eulerAngles.y;
float angle = Mathf.SmoothDampAngle(transformPlayer.eulerAngles.y, targetAngle, ref _turnVelocity, TurnSmoothTime);
transformPlayer.rotation = Quaternion.Euler(0f, angle, 0f);
Vector3 moveDirection = Quaternion.Euler(0f, targetAngle, 0f) * Vector3.forward;
moveDirectionNormalized = moveDirection.normalized;
_characterPlayer.Move(moveDirectionNormalized * SpeedMultiplier * Time.deltaTime);
The character model is a child object of _characterPlayer.
And if, when the character moves, an enemy gets into his radius, then the player's model will turn towards the enemy, and the character himself will go further along the moveDirectionNormalized.
For turns, I wrote the following code:
if (NearestEnemyAtAttackRadius != null)
{
Quaternion rotation = Quaternion.LookRotation(NearestEnemyAtAttackRadius.transform.position - MeshCharacter.position);
MeshCharacter.rotation = Quaternion.RotateTowards(MeshCharacter.rotation, rotation, 800f * Time.deltaTime);
}
else
{
MeshCharacter.localRotation = Quaternion.RotateTowards(MeshCharacter.localRotation, Quaternion.Euler(Vector3.zero), 800f * Time.deltaTime);
}
Now if there is an enemy nearby, my character swings towards the enemy, and if there is no enemy or the enemy has left the radius, then the initial state is returned.
And here my problem begins. I wanted to add an animation of the movement. The character has 4 animations: movement with a tilt to the right, left, forward and backward.
In the Animator, I made a Blend Tree (2d simple direction) with 4 animations. Added 2 Float values MoveDirectionX, MoveDirectionY correctly configured for all motion pos x and pos y.
And if I am in MoveDirectionX, MoveDirectionY will feed the vector moveDirectionNormalized, then it does not work correctly. The values of moveDirectionNormalized do not depend on the repetition of my model in any way, and if my character moves away from the enemy, he looks at the enemy but in fact goes backwards, the animation should turn on as he leans back. But now moveDirectionNormalized does not depend on the repetition of my model, and the character tilt animations randomly switch depending on moveDirectionNormalized. As I understand it, it is necessary to create a new vector that will take into account the rotation of my model, but I do not understand how to do this.
As a result, I want to get a vector where x - shows where the character is moving to the left or right (from -1 to 1) and y - shows the movement forward, backward (also from -1 to 1), and this vector should take into account the rotation of the player. At the moment, in the character model, only y changes in rotation. moveDirectionNormalized returns a value that indicates where the movement is directed relative to world space. And I need to make a vector that will return directions relative to the player's rotation. That is, if the character's gaze is directed at the enemy, and the character himself retreats from him, I would like to receive a vector (0, -1). And if the player's gaze is directed at the enemy and he goes to the right (I remind you that the player is constantly looking towards the enemy, that is, you can walk around the enemy and the character will constantly turn in his direction), then I would like to get a vector (1,0)
I am not 100% sure I understand, but if I do, this should help:
You can use Quaternion.Inverse() on the rotation of your player, and multiply that by the vector.
Vector3 yourVector;
Quaternion inverse = Quaternion.Inverse([player rotation]);
return inverse * yourVector;
Multiplying a quaternion by a vector rotates it with the quaternion, so if you invert it it will cancel out the rotation of the player.

Rotate a quaternion / Change the axis it rotates around

Conceptually a quaternion can store the rotation around an axis by some degree.
Now, what is the numerically most robust and least calculation intensive way to rotate the axis?
For example when i have a quaternion, which rotates 90 degrees around the y-axis and i want those 90 degrees to be applied to some other arbitrary axis, described by a normalized vector.
EDIT: Since this also came up i added an answer on how to correctly change the axis a quaternion rotates around with another quaternion.
It is a bit unclear what your actual goal is by doing what you describe.
In order to actually keep the angle but change the axis you would use Quaternion.ToAngleAxis, alter the axis and then pass it back into Quaternion.AngleAxis
like e.g.
Quaternion someRotation;
someRotation.ToAngleAxis(out var angle, out var axis);
var newAxis = Vector3.up;
var newRotation = Quaternion.AngleAxis(angle, new axis);
Or you rotate an existing Quaternion by another one using * like
Quaternion newRotation = someRotation * Quaternion.Euler(90, 0, 0);
which would take the existing rotation and rotate it by 90° around the X axis.
#derHugo's solution, solves the problem i initially asked, but the seconds part of his answer isn't doing what he seemed to be describing. To rotate a quaternions axis of rotation with another quaternion you would need to apply the rotations differently.
E.g. you have a quaternion yQuaternion, which rotates 90° around the y-axis and want to rotate, it's rotation axis by 90° around the x-axis (which would result in a quaternion rotating 90° around the z-axis) you'd have to do the following.
// The quaternion, we want to "rotate"
var yQuaternion = Quaternion.Euler(0f, 90f, 0f);
// The quaternion we want to rotate by.
var xQuaternion = Quaternion.Euler(90f, 0f, 0f);
var result = xQuaternion * yRotation * Quaternion.Inverse(xQuaternion);
What happens here is that we first rotate backwards to our desired axis, then apply the rotation we want to use and then revert the rotation we initally applied.
NOTE: I'm quit sure, that saying "rotate a quaternion" isn't a valid term when talking about this quaternion operations.

unity3d - Clamp rotation value

I have an object that rotates according to mouse position, but I want to clamp it so it doesn't get further or lower than certain value. Here is my code:
void LookAt () {
float distance = transform.position.z - Camera.main.transform.position.z;
Vector3 position = new Vector3(Input.mousePosition.x, Input.mousePosition.y, distance);
position = Camera.main.ScreenToWorldPoint(position);
position.x = Mathf.Clamp(position.x, -70, 70);
position.z = Mathf.Clamp(position.z, -70, 70);
Vector3 target = new Vector3 (position.x, transform.position.y, position.z); // Use current object positin.y
transform.LookAt(target);
}
But unfortunately it doesn't work, it keeps rotating 360.
Edit:
This is a 3D top-down game, I have a tank and I want to rotate it's upper half. The code I wrote above works perfect for the job, but now I don't know how to limit it so the barrel( the part I'm rotating) always facing upwards where the enemies will come from. 70 or whatever are just random values I was testing, first I want to figure what exactly the proper code is, then determining the values is the easy part.
Actually, the problem is that you're clamping a position, not a rotation. You're having it look at a certain point, but limiting that point rather than the angle that it will need to rotate to meet it. You'll have to use trigonometry to calculate the angle it wants to point in (more specifically, the atan2 function), clamp that value to (-70, 70), and then apply that rotation to the object (using euler angles). Do you require further clarification on any of these steps?
Cheers.
P.S. Note that atan2 returns a value in radians, but your range and euler angles use degrees.
You limit your target position by 70 units in world space, which is usually a lot, but depends on your game scale. What I think you wanted to do is to limit mouse position by 70 pixels around the screen center. (Please, provide this remarks in the question itself, so we won't have to guess). However, because you used the same variable both for screen space and world space position of the target, you likely got confused and clamped the position after converting it to world space.
Also, you made the y coordinate of the target to be the same as the object. But this means that the object would have to rotate 360 degrees every time the target passed it. I assume that what you wanted to do instead is to assume that the target location is located on camera place.
void LookAt () {
var cursorPosition = new Vector3(Input.mousePosition.x, Input.mousePosition.y, camera.main.nearClipPlane);
cursorPosition.x = Mathf.Clamp(position.x, -70, 70);
cursorPosition.z = Mathf.Clamp(position.z, -70, 70);
var targetPosition = Camera.main.ScreenToWorldPoint(cursorPosition);
transform.LookAt(targetPosition);
}
Please, provide details about your reasoning and desired behavior when you ask to find errors in your code.

Determining if quarternion rotation is clockwise or counter clockwise

I am using the following code to handle rotating my player model to the position of my mouse.
void Update() {
// Generate a plane that intersects the transform's position with an upwards normal.
Plane playerPlane = new Plane(Vector3.up, transform.position);
// Generate a ray from the cursor position
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
// Determine the point where the cursor ray intersects the plane.
// This will be the point that the object must look towards to be looking at the mouse.
// Raycasting to a Plane object only gives us a distance, so we'll have to take the distance,
// then find the point along that ray that meets that distance. This will be the point
// to look at.
float hitdist = 0f;
// If the ray is parallel to the plane, Raycast will return false.
if (playerPlane.Raycast(ray, out hitdist)) {
// Get the point along the ray that hits the calculated distance.
var targetPoint = ray.GetPoint(hitdist);
// Determine the target rotation. This is the rotation if the transform looks at the target point.
Quaternion targetRotation = Quaternion.LookRotation(targetPoint - transform.position);
// Smoothly rotate towards the target point.
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, speed * Time.deltaTime); // WITH SPEED
//transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, 1); // WITHOUT SPEED!!!
}
I would like to be able to determine if the rotation is clockwise or counter-clockwise for animation purposes. What would be the best way of handling this? I'm fairly unfamiliar with quaternions so I'm not really sure how to approach this.
Angles between quaternions are unsigned. You will always get the shortest distance, and there's no way of defining "counter-clockwise" or "clockwise" unless you actively specify an axis (a point of view).
What you CAN do, however, is to take the axis that you're interested in (I assume it's the normal to your base plane.. perhaps the vertical of your world?) and take the flat 2D components of your quaternions, map them there and compute a simple 2D angle between those.
Quaternion A; //first Quaternion - this is your desired rotation
Quaternion B; //second Quaternion - this is your current rotation
// define an axis, usually just up
Vector3 axis = new Vector3(0.0f, 1.0f, 0.0f);
// mock rotate the axis with each quaternion
Vector3 vecA = A * axis;
Vector3 vecB = B * axis;
// now we need to compute the actual 2D rotation projections on the base plane
float angleA = Mathf.Atan2(vecA.x, vecA.z) * Mathf.Rad2Deg;
float angleB = Mathf.Atan2(vecB.x, vecB.z) * Mathf.Rad2Deg;
// get the signed difference in these angles
var angleDiff = Mathf.DeltaAngle( angleA, angleB );
This should be it. I never had to do it myself and the code above is not tested. Similar to: http://answers.unity3d.com/questions/26783/how-to-get-the-signed-angle-between-two-quaternion.html
This should work even if A or B are not Quaternions, but one of them is an euler-angle rotation.
Two dimensional quaternions (complex numbers) have a signed angle. But, the more correct way to think about complex numbers is with an unsigned angle which is relative to either the XY oriented plane or the YX oriented plane. I.E. a combination of an unsigned angle an an oriented plane of rotation.
In 2D there are only two oriented planes of rotation so the idea of a "signed angle" is really just a trick to get both the unsigned angle and the oriented plane of rotation packed into a single number.
For a quaternion the "signed angle" trick cannot be used because in 3D you have an infinite number of oriented planes you can rotate in, so a single signed angle cannot encode all the rotation information like it can in the 2D case.
The only way for a signed angle to make sense in 3D is with reference to a particular oriented plane, such as the XY oriented plane.
-- UPDATE --
This is pretty easy to solve as a method on a quaternion class. If all you want to know is "is this counter clockwise", then since we know the rotation angle is from 0 to 180, a positive dot product between the quat's axis of rotation and the surface normal should indicate that we're rotating counter clockwise from the perspective of that surface. And a negative dot product indicates the opposite. Ignoring the zero case, this should do the trick with much less work:
public bool IsCounterClockwise( in Vector3 normal ) => I*normal.X + J*normal.Y + K*normal.Z >= 0;