Unity3D - Relative position on y-axis independent of rotation? - unity3d

I have an object (A) with another object (B) next to it. I am trying to calculate the "height" of object B, so that i can position another object at that height relative to the position of object A. I know this sounds like gibberish (i'm a bit tired) so i have put diagrams to try and explain.
So in the left image the yellow line represents what i am trying to calculate. I have an position (orange) on the surface of a cylinder (grey) (calculated position using mesh data) which i am trying to use to calculate the radius of the object (black line). To do this i need a position at the center of the object (grey) at the same height (red dot) so i can calculate the direction from one to the other and use the length (.magnitude) as the radius.
My problem is i can't work out, how i can calculate the height (yellow line) without rotation having any effect.
I currently use projectOnPlane however if i rotate the object as seen in the second image, the radius decreases significantly when it should be consistent as the object is not changing size.
Vector3 RadDirection = (Vector3.ProjectOnPlane(orangePoint, grey.transform.up) - Vector3.ProjectOnPlane(grey.transform.position, grey.transform.up));
float radius = RadDirection.magnitude;
Any help would be much appreciated, thanks.
**UPDATE: The grey block in the diagram is a vector3 position rather than a game object. The radius calculation i am trying to do happens during runtime so i can't parent an object to the grey and review the inspector.
**UPDATE 2: Sorry, something i should have mentioned. The object i'm doing this on will not always be a perfect cylinder, it could be something such as a wine glass, where i need to calculate the radius of the glass not the stem. Another example could be a chemistry beaker which normally tapers to a point, so i would need to calculate the radius at the height of the orange point. Sorry i should have put that in the question.
Here's a diagram to illustrate what i mean in update 2. Again the orange dot is acting as a visual representation of a Vector3 position on the surface of the object's (in this case a beaker) mesh.
**Update 3: I appear to have solved the issue and so i have posted my answer below but at the time of writing i can't accept it (have to wait 2 days) and so cant close/answer the question. I would like to thank everyone that contributed and tried to help me solve this problem. I hope i can help you all someday :)

You can use transform.TransformDirection() or transform.InverseTransformDirection()
To get the height take the result of the subtraction and set the X, Z coordinate to zero:
Vector3 height = orangeBox.position - greenPoint.position;
height.x = 0;
height.z = 0;
Complete solution:
Vector3 direction = orangeBox.position - greenPoint.position;
direction = greyBox.transform.InverseTransformDirection(direction);
direction.x = 0;
direction.z = 0;
height = direction.magnitude;
redPoint.position = greenPoint.position + greyBox.transform.up * height;

I appear to have solved my problem using the following:
public static Vector3 ProjectPointOnLine(Vector3 linePoint, Vector3 lineVec, Vector3 point)
{
//get vector from point on line to point in space
Vector3 linePointToPoint = point - linePoint;
float t = Vector3.Dot(linePointToPoint, lineVec);
return linePoint + lineVec * t;
}
I found this here, it has lot's of other useful looking functions:
http://wiki.unity3d.com/index.php/3d_Math_functions
I basically pass in the origin of the beaker (green point) as the "linePoint", the upwards direction of the beaker "lineVec" with beaker.transform.up and finally i pass in the world vector3 point on the surface of the beaker mesh (orange point) it returns back a point in the middle of the beaker at the same height as my orange dot. I then just subtract the one from the other and take the magnitude as the radius. The radius value calculated is correct in that's it the value i was expecting and only changes after the sixth decimal place during rotation which gives plenty of accuracy as i only need three or four decimal places.
I'm happy to provide further details or help if anyone else needs help doing this.

Related

Unity Face Object With Off-center Pivot Point?

I'm trying to create a "billboard" effect where a quad/gameObject always faces a target object (the camera) around the Y axis. This is working fine as per the code below, but I want to add an optional offset to the pivot point on the X axis.
So rather than rotating around the center point (default behaviour), I want the quad to rotate around a new point thats n units off of the center point, while still facing the target object.
This will run in Update().
Currently Working Code Without Offset
transform.LookAt(camera.transform, Vector3.up);
transform.localEulerAngles = new Vector3(0, transform.localEulerAngles.y, 0); // Only affects Y Axis.
The offset I need is calculated by the function below. It is tested to be correct by moving a child GameObject by this value.
Both the leftObj and rightObj are children of the GameObject I want to be rotating.
public float GetCenterPos()
{
Vector3 left = leftObj.transform.localPosition;
Vector3 right = rightObj.transform.localPosition;
Vector3 center = (left + right) / 2f;
return center.x;
}
Top Down View of My Problem
I have tried combinations of RotateAround, but I can't figure out how to get it to face the correct object and what the pivot should be relative to the offset.
I have also googled around, and I can't find a solution to this problem that I feel is relatively simple.
To recap: I don't need a rotational offset, and I don't want to add an extra parent to change the pivot like many other answers suggest. The offset gets calculated dynamically in Update.
Thank you for any help.
I've been tinkering with it for a while, and I came up with this solution. It's not ideal because it requires storing a reference to the starting position, which breaks some of the other movement functionality I need, but it does answer my original question.
Before beginning the code above (either in start or before a bool flag is set, whatever) store a reference to the object's localPosition (startPos)
Then before calling LookAt, adjust the position to take into account the offset.
transform.localPosition = new Vector3(startPos.x + offset, transform.localPosition.y, transform.localPosition.z);
transform.LookAt(camController.transform, Vector3.up);
transform.localEulerAngles = new Vector3(0, transform.localEulerAngles.y, 0);
To clarify, the reason why I need a reference to startPos is because otherwise I would be adding the offset every frame, resulting in the object just moving constantly, rather than using a consistent value.
I just set the startPos before and after toggling the "billboard" functionality to keep it updated. Not ideal, but it does work.

Which rotation is shown in the Inspector?

The chest bone of my player can be rotated while aiming.
Now I wanted to evaluate how much (minimum and maximum rotation) I should let the chest be rotatable.
To do that, I allowed all degrees of rotation and took a look at the Inspector.
For example, the minimum value that the chest should be rotatable to the left should be Y=-15.
At Y=-15 (seen in the Inspector), it still looked natural.
Now I wanted to code this.
To my surprise, chest.localRotation.Y was a completely different value than what the Inspector is showing.
I have then taken a look at the chest variable and extended the view.
I just can't see the rotation value that the Inspector is showing.
How should I go on in this case, please?
I'm using this to rotate the bone:
Chest.LookAt(ChestLookTarget.position);
Chest.rotation = Chest.rotation * Quaternion.Euler(Offset);
Thank you!
The reason why it doesn't work:
Quaternion is not a human readable value.
One Quaternion is allways unique but can have multiple (infinite?) different representations in Euler space! The other way round one Euler represents allways exactly one Quaternion value.
If you look at the docs it explicitly says
Don't modify this directly unless you know quaternions inside out.
Than as said what you see in the inspector is the localRotation in relation to the parent Transform.
Better said it is one of the many possible Euler inputs that result in the Quaternion. What you see in the debug at localEulerAngles is another possible Euler representation. Unity usually in localEulerAngles also gives you only values > 0.
It seems that the chest anyway will only rotate around the Y axis, right?
If this is the case you can simply get the Angle between the chest's original forward vector and the target. It is way easier to handle Vector3 values than Quaternions ;)
It seems to be the same use case as in this post
// get the target direction
Vector3 targetDir = ChestLookTarget.position - Chest.position;
// Reset any difference in the Y axis
// since it would change the angle as well if there was a difference I the height
// between the two objects
targetDir.y = 0;
// however you currently rotate
// instead rotate only the Vector3 variable without applying it to the transform yet
Vector3 newDir = Vector3.RotateTowards(Chest.forward, targetDir, RotationSpeed * Time.deltaTime, 0.0f);
// Compare the target direction to the parents forward vector
float newAngle = Vector3.Angle(Chest.parent.transform.forward, newDir);
if (newAngle > MaxRotationAngle)
{
// What should happen if angle gets bigger?
return;
}
// If angle still okey set the new direction
Chest.rotation = Quaternion.LookRotation(newDir);

Why am I getting an incorrect vector when trying to find HingeJoint2D.anchor in world space?

In the scene, I have a long chain of children that are connected via hinge to their parent. For my code, I need the position of the hinge anchors in world space, so I use:
public Vector2 hingeVector => hinge.anchor + (Vector2)gameObject.transform.position;
For the first hinge, that code gives the correct position. But for the second hinge this happens:
The red point is the vector I get, the blue point is the actual position. As you can see, it's a somewhat small but still problematic difference.
Is there any way I can fix this? I couldn't find anything like this online.
You need to add the object's rotation
The anchor values are axis aligned and aren't affected by rotation, but in order to calculate the anchor point in world space, knowing the transform's position, you need to rotate the anchor point values by the object's rotation then add it to the position:
Vector2 p = hinge.anchor.Rotate(gameObject.transform.rotation.eulerAngles.z)
+ (Vector2)gameObject.transform.position;

Move Cannon.JS Body Toward Dynamic Position?

I'm having a hard time wrapping my head around how to do this.
What I'm trying to do, is move a physics body, by the means of velocity, toward another body, until it reaches and hits said body. (Think AI following a player)
One solution is the following:
body.velocity.x = target.position.x-body.position.x;
body.velocity.z = target.position.z-body.position.z;
This has issues, though. One being that the velocity is higher depending on the distance between the two bodies. I would prefer a fixed speed. Another way would be to use the following example: Position a body in cannon.js relative to local rotation
But in this case, I need a way of rotating the quaternion, so it faces the target position, this with only knowing the 2 positions of the bodies.
So the question is, how do I calculate the velocity OR direction of the body, so that it can follow and collide with the target body, by using a fixed speed/velocity?
To get a fixed speed, .normalize() your velocity vector and then scale (.mult()) the result by the speed you want. The result will be a vector that always has the length you want.
To get a quaternion that makes your body face in a certain direction, you can use Quaternion.setFromVectors(u,v). This method creates a quaternion which will rotate u so it points in the same direction as v. If you set u to your forward vector and v to the direction you want the body to look, you will get the correct "look at" behavior. Note that the "forward" vector might be different for your game.
// Compute direction to target
var direction = new CANNON.Vec3();
target.position.vsub(body.position, direction);
direction.y = 0;
direction.normalize();
// Get the rotation between the forward vector and the direction vector
var forward = new CANNON.Vec3(0,0,1);
body.quaternion.setFromVectors(forward, direction);
// Multiply direction by 10 and store in body.velocity
var fixedSpeed = 10;
direction.mult(fixedSpeed,body.velocity);

Unity3D angle between vectors/directions on specific axis

I have two directions and i am trying to calculate the angle on a specific axis. The object from which the directions are derived is not a static object so i'm struggling to get my head round the maths to work out what i need to do.
FYI, I have the start and end points that i have used to calculate the directions if they are needed.
Here's a diagram to show what i am looking for:
The above image is from a top-down view in unity and it shows the angle i want.
The problem can be seen from the above image, which is that the directions are not on the same height so i can't use the vector3.angle function as it won't give me the correct angle value.
In essence i want to know how much would i have to rotate the red line to the left (top view) so that it would line up with the blue (top-view).
The reason i need this is as i am trying to find a way of getting the side-to-side angles of fingers from my leap motion sensor.
This a generic version of my other question:
Leap Motion - Angle of proximal bone to metacarpal (side to side movement)
It will provide more specific information as to the problem if you need it and it has more specific screenshots.
**UPDATE:
After re-reading my question i can see it wasn't particularly clear so here i will hopefully make it clearer. I am trying to calculate the angle of a finger from the leap motion tracking data. Specifically the angle of the finger relative to the metacarpal bone (bone is back of hand). An easy way to demonstrate what i mean would be for you to move your index finger side-to-side (i.e. towards your thumb and then far away from your thumb).
I have put two diagrams below to hopefully illustrate this.
The blue line follows the metacarpal bone which your finger would line up with in a resting position. What i want to calculate is the angle between the blue and red lines (marked with a green line). I am unable to use Vector3.Angle as this value also takes into account the bending of the finger. I need someway of 'flattening' the finger direction out, thus essentially ignoring the bending and just looking at the side to side angle. The second diagram will hopefully show what i mean.
In this diagram:
The blue line represents the actual direction of the finger (taken from the proximal bone - knuckle to first joint)
The green line represents the metacarpal bone direction (the direction to compare to)
The red line represents what i would like to 'convert' the blue line to, whilst keeping it's side to side angle (as seen in the first hand diagram).
It is also worth mentioning that i can't just always look at the x and z axis as this hand will be moving at rotating.
I hope this helps clear things up and truly appreciate the help received thus far.
If I understand your problem correctly, you need to project your two vectors onto a plane. The vectors might not be in that plane currently (your "bent finger" problem) and thus you need to "project" them onto the plane (think of a tree casting a shadow onto the ground; the tree is the vector and the shadow is the projection onto the "plane" of the ground).
Luckily Unity3D provides a method for projection (though the math is not that hard). Vector3.ProjectOnPlane https://docs.unity3d.com/ScriptReference/Vector3.ProjectOnPlane.html
Vector3 a = ...;
Vector3 b = ...;
Vector3 planeNormal = ...;
Vector3 projectionA = Vector3.ProjectOnPlane(a, planeNormal);
Vector3 projectionB = Vector3.ProjectOnPlane(b, planeNormal);
float angle = Vector3.Angle(projectionA, projectionB);
What is unclear in your problem description is what plane you need to project onto? The horizontal plane? If so planeNormal is simply the vertical. But if it is in reference to some other transform, you will need to define that first.