I have a relatively complicated math problem I need to solve for a game I'm working on in Unity. I've tried a couple things but nothing has worked.
Basically, I need to apply an offset rotation (Quaternion) to a parent, where the result of this rotation is to move its child in a given direction.
To explain it better, the problem would be simple if it could be guaranteed that the parent's forward vector was pointed at the child. Then I would simply create a ghost position by adding the desired direction to the child, and then use a LookAt rotation to rotate the parent to look at that ghost position. (The child doesn't need to be put in a specific position, it just needs to move generally in that direction)
What makes this complicated is that A. the parent could be at any rotation, and B. the child could be at any position relative the parent.
For context, I'm working on a procedural animation system and I'd like to have the bones bend in the direction of the Agent's velocity. With the IK'd bones this is easy, just move the IK. But for the actual bones, I need a way to move a bone in a direction by rotating its parent's bone.
Thanks for any help!
First, we need to have the child's current position and the target position in the coordinate system of the parent. It sounds as if the child's position already is expressed in this coordinate system. Then, if the target position is in world coordinates, you simply do this with the inverse parent world transform:
pTargetLocal = parent.worldMatrix^-1 * pTarget
Once we have this, we want to find a rotation R, such that pCurrentLocal is rotated towards pTargetLocal. Assuming unit vectors (as rotations preserve lengths), this equals:
parent.worldMatrix * pTargetLocal = parent.worldMatrix * R * pCurrentLocal
pTargetLocal = R * pCurrentLocal
Once we have R, we just need to update parent.worldMatrix = parent.worldMatrix * R.
This can be solved by representing R in axis-angle format. The axis would be axis = Vector3.Cross(pCurrentLocal, pTargetLocal) and the angle is angle = Vector3.Angle(pCurrentLocal, pTargetLocal). You can either calculate a quaternion or a matrix from these parameters and multiply it with the current parent transform.
I assumed that the rotation center is the origin of the parent's local coordinate system. You could also rotate about another center to incorporate a translation component.
Related
I have a Unity AR project using Vuforia engine. What I am trying to achieve is to have the AR object always stand upright in the view whether the image target is horizontal on a table or or vertical on a wall.
Currently the object is sitting on the image target no matter which orientation
Hope that makes sense,
Thanks
I always use Vector3.ProjectOnPlane for this and then you can simply assign axis directions to Transform.up and Transform.right (below I explain why right and not maybe forward)
public void AlignObject(Transform obj, Transform imageTarget)
{
obj.position = imageTarget.position;
// Get your targets right vector in world space
var right = imageTarget.right;
// If not anyway the case ensure that your objects up vector equals the world up vector
obj.up = Vector3.up;
// Align your objects right vector with the image target's right vector
// projected down onto the global XZ plane => erasing its Y component
obj.right = Vector3.ProjectOnPlane(right, Vector3.up);
}
The assumption for this is: The target is usually never rotated in the Z axis. If you want it upright on a wall you would usually rotate it around its X axis.
Therefore we can assume the the image target will never be rotated more then 90° on the Z axis (in which case the mapped vector would flip about 180°) and thus if we map the right vector down onto the global XZ plane it always still points in the correct direction regardless of any rotations in Y and X axes.
If we would use the forward instead we take the risk that due to tracking inaccuracies the vertical targets forward vector actually points a tiny little bit towards us so when we map it down onto the XZ plane it points backwards not forwards and the object is flipped by 180°.
So using right works for a horizontal and a vertical target.
I have two points--let's say, A(x1,y1,z1) and B(x2,y2,z2). What I do need is to move both of those points into direction of the Vector AB while pressing the Horizontal axis. How can I achieve that using the Translate method? Like I need to write something like this:
A.translate(pointingVector, Space.World)
The point B moves automatically fitting the transform of the point A, so no need to move it as moving the A point would also move the AB vector without changing its length and direction.
If I understand what you're saying correctly, then this is simply a matter of moving the 2 points along the Vector that they create. So first you need to create the directional Vector:
// Gets a vector that points from the first point's position to the second's.
var (or Vector3) direction = p1.position - p2.position;
Then just increase the 2 points' position by that vector whenever you press the horizontal axis:
p1.position += direction * Time.deltaTime
I have different kind of objects with variable dimensions and placed at different position in a scene. I want to focus/display each object with the camera with a hard code rotation (north rotation). Like with specific camera rotation I want to focus the object that it completely show to the camera and center of the screen. For this reason, I have write this code snippet that
1
Get the position behind the specific focus object using
TransformPoint
Provide the elevation using the largest extent of the bound so that it display maximum area of the object.
Assign the position and the fixed rotation
Vector3 dest = destination.transform.TransformPoint(0, 0, behindPositionDistance);// GetBehindPosition(destination.transform, behindPositionDistance, elevation);
Debug.Log(dest);
float eleveMax = Mathf.Max(destination.GetComponent<MeshRenderer>().bounds.extents.x, destination.GetComponent<MeshRenderer>().bounds.extents.z);
dest = new Vector3(dest.x, eleveMax, dest.z);
camera.transform.position = dest;
camera.transform.rotation = lookNorth;
But the problem is, it is not accurately working with all objects as every object is at different position and dimension. I want to focus the full object using the camera but without changing the rotation.
Maybe create an empty gameobject as a child of your objects. Use that as your Camera position at runtime.
If you can use an orthographic camera, you can set the camera's orthographicSize dynamically based on the size of the bounds of the GameObject. This is probably the easiest solution.
If you need a perspective camera, you can get the Planes or corners of the camera frustum via GeometryUtility.CalculateFrustumPlanes(Camera.main) or Camera.CalculateFrustumCorners and use the results from either to manually test that your Bounds is entirely inside.
With a perspective camera, you could also compute the necessary distance from the object based on the size of the object's bounds and the FOV of the camera. If I'm not mistaken, this would be something along the lines of distance = 0.5 * size / tan(0.5 * fov), but the function doesn't have to be precise; it just needs to work for your camera. Alternatively, you could keep distance constant and compute FOV from the size of the object, although I wouldn't recommend that because frequent FOV changes sound disorienting for the viewer; my point is that there are many options.
I am trying to instantiate a 3d plane game object inside a 3d plane, but the parent 3d plane's rotation could be anything, the child plane should be exact parallel to the parent 3d plane but it should keep its own rotation also.
I have tried following script, but it did not work
child.rotation = child.rotation * parent.rotation;
here I have a child plane game object names as B (rotation: -90, 0, 0) and I am making another bigger plane game object (rotation: anything) as its parent. but the child plane sometimes comes parallel to its parent plane, sometimes it doesn't when the parent plane is at some different angle/rotation.
Explanation:
In below screenshot I have set child plane rotation manually in editor just by rotating 90/180 degree in required angle, the scale is automatically getting adjusted while dragging that object from world to as a child (local).
I have tried below code snippets to achieve the needful, but didn't work.
var chile = Instantiate(Resources.Load<GameObject>(prefabsPath), parent);
var t = child.transform;
t.position = pos;
t.rotation = parent.rotation;
Also I have tried
t.forward = parent.forward;
Also this one
child.SetParent(parent); // wasn't passing parent in Instantiate
above one was working perfectly in terms of scaling accordingly, but stil I wasn't able to fix rotation.
#derHego answer,
the child plane is getting stretched like below,
I experimented attaching child to different size parent planes, I found its getting stretched according to its parent scale, if parent's y is greater than x, then child's y is also getting bigger than its x with the same ratio as in parent.
How do I fix this?
I assume you want both planes facing with e.g. their local Y axis in the same direction while they still might be rotated against each other around that face normal.
Depending on the direction your planes are facing you can use e.g.
child.up = parent.up;
to make both planes parallel (= pointing to the same sirection) in this case I assumed your default plane lays flad on the floor so it is facing up in Y direction
e.g.
public class Example : MonoBehaviour
{
public Transform child;
private void Start()
{
child.up = transform.up;
}
}
(Green plane is parent, white plane is child)
After your update it looks like actually you could simply do
var child = Instantiate(prefab);
child.rotation = parent.rotation;
var scale = child.lossyScale;
child.SetParent(parent);
var invertParentScale = new Vector3(1 / parent.lossyScale.x, 1 / parent.lossyScale.y, 1 / parent.lossyScale.z)
child.localScale = Vector3.Scale(scale, invertParentScale);
I am messing about in XNA and have run into a problem. I have a 48 * 48 sprite that I can keep track of its location in the game world by the top left corner of the sprite.
I want to be able to rotate the square and still keep track of the same point. For instance if I rotate 90degrees clockwise and the orginal X position was 200 the new X position should be 200 + 48(the size of the width of the image). Its fine for 90 degrees I am able to work that out in my head but each one in between is the problem!
I know there is probably some kind of formula to work this out.
Any help would be great! Oh the square is rotating on its center.
I'm just using spriteBatch.Draw()
spriteBatch.Draw( animations[currentAnimation].Texture,
Camera.WorldToScreen(WorldRectangle),
animations[currentAnimation].FrameRectangle,
color, rotationScale , new Vector2((float)frameHeight/2, (float)frameWidth/2), effect, TileMap.characterDepth);
If you have to keep track of a moving rotating sprite you can't use the top left corner, but its centroid. You already draw your sprite using the centroid to rotate it.
The problem is that the second parameter of your Draw call is a Rectangle, you sholud use a Vector2 position, instead.
You're building your application on top of a 3D graphics library. 3D graphics libraries are very good at solving this kind of problem! Break it down into smaller operations and let the library do the work for you.
First: it's easiest to think about these kinds of questions when you're working in model space rather than world space. In other words: you don't need to worry about where the rotating point is in absolute terms, you only need to worry about where it is relative to the untransformed model (in this case, your sprite without any rotation or translation).
So where is that? Simple:
var pt = new Vector3(-frameWidth / 2f, -frameHeight / 2f, 0f);
Your point of origin is the center of your sprite, so the center of your sprite in model space is (0, 0). This means that the top left corner of your sprite is half the width of the sprite in the negative x direction, and half the height of the sprite along the negative y direction.
Now create an object that represents the desired transformation. You can do this by creating a rotation matrix using XNA's built-in methods:
var transformation = Matrix.CreateRotationZ(MathHelper.ToRadians(90f));
Now apply the transformation to your original point:
var transformedPt = Vector3.Transform(pt, transformation);
This is still in model space, remember, so to get world coordinates you'll need to transform it into world space:
var transformedWorldX = transformedPt.X + spritePosition.X;
var transformedWorldY = transformedPt.Y + spritePosition.Y;
And there you go.