as the title suggests I am trying different ways but without solving.
I would need to check if the mouse is between two 2d points in PlayMode.
I thought of using rect to build a rectangle between two vector2 on the fly and check with rect.contains if the mouse position is inside the rectangle. This works if the rectangle is horizontal but the points on the screen are not aligned and it does not seem to me that there is a way to create an oblique rectangle.
I tried to calculate the distance between the two points, to create the rectangle and to move its center to rotate it but it doesn't seem to work. Also it seems that there is no way to display the rectangle on the screen and therefore it is difficult to understand if it retains its original shape after changing some parameters.
Do you have any suggestions? But I need the detection area to be rectangular and not on a single line. Using LineCast it is possible to solve but the detected area is too narrow.
I thought of a rectangle between the two vectors. Imagine a one-pixel line connecting the two vectors. Now increase the thickness of the line up to 10/20 pixels so that the mouse is detected a little before and a little after the original line. Maybe I'm wrong but I see the new rect as a good solution but as far as I looked at all its properties I can't understand how to build it between two vectors without too many lines of code.
You can do some vector math to:
Determine if the test point is not "outside" of the two points.
Calculate the distance from the line segment your point is, and if that's equal or below some threshold, then consider it to be "near" the segment between the two points.
If it's "near" the segment and not "outside" the two points, then consider it to be "between" the points.
For example:
Vector3 point1 = new Vector2(10f, 10f);
Vector3 point2 = new Vector2(50f, 50f);
float closeThreshold = 5f;
Vector3 testPoint = new Vector2(20f, 21f);
Vector3 segmentDirection = (point2-point1).normalized;
Vector3 point1ToTest = testPoint - point1;
Vector3 testToPoint2 = point2 - testPoint;
// determine if testPoint is not "outside" the points
if ( Vector3.Dot(segmentDirection, point1ToTest.normalized) >= 0f
&& Vector3.Dot(segmentDirection, testToPoint2.normalized) >= 0f)
{
Vector3 closestPointOnSegment = point1
+ segmentDirection * Vector3.Dot(point1ToTest, segmentDirection);
// determine if testPoint is close enough to the line segment point1->point2
if ((closestPointOnSegment - testPoint).magnitude <= closeThreshold)
{
// testPoint is "between" point1 and point2
}
}
This will work with Vector3 or Vector2, which will safely convert to Vector3 as shown above.
If you are only concerned with if the point is exactly between the two positions, take the directions from the point you're testing to either end of the line, and if the dot product between those directions is -1, then the point is between them:
Vector3 point1 = new Vector2(-1f,-1f);
Vector3 point2 = new Vector2(1f,1f);
Vector3 testPoint = new Vector2(0f,0f);
Vector3 testTo1 = (point1-testPoint).normalized;
Vector3 testTo2 = (point2-testPoint).normalized;
if (Mathf.Approximately(-1f, Vector3.Dot(testTo1, testTo2)))
{
// testPoint is between point1 and point2
}
This will work with Vector3 or Vector2, which will safely convert to Vector3 as above.
Related
Example Image here
I am trying to find a way to calculate points on my cylinders top circle surface. My situation looks like this, I have a vector which is defining my cylinders direction in 3d room. Then I already calculated me a perpendicular vector with
Vector3.Cross(vector1, vector2)
Now I use the diameter/2 to calculate the point which is lying on the edge of the circular top surface of my cylinder. Now I want to rotate my vector always 90 degrees in order to get 4 points on the edge of the surface. All the 4 vectors defining them should be perpendicular to the cylinders direction. Can you help me how I can rotate the first perpendicular to achieve this?
I already tried:
Matrix4x4.CreateFromAxisAngle(vectorcylinderdirection, radiant)
Then I calculated again cross product but it doesnt work like I want to.
Edit:
public static void calculatePontsOnCylinder()
{
//Calculate Orthogonal Vector to Direction
Vector3 tCylinderDirection = new Vector3(1, 0, 0);
Vector3 tOrthogonal = Vector3.Cross(tCylinderDirection, new Vector3(-tCylinderDirection.Z,tCylinderDirection.X,tCylinderDirection.Y));
Vector3 tNormOrthogonal = Vector3.Normalize(tOrthogonal);
//Calculate point on surface circle of cylinder
//10mm radius
int tRadius = 10;
Vector3 tPointFinder = tNormOrthogonal * tRadius;
//tPointFinder add the cylinder start point
//not yet implemented
//now i need to rotate the vector always 90 degrees to find the 3 other points on the circular top surface of the cylinder
//don't know how to do this
// I thought this should do it
Matrix4x4.CreateFromAxisAngle(tCylinderDirection, (float)DegreeToRadian(90));
}
private static double DegreeToRadian(double angle)
{
return Math.PI * angle / 180.0;
}
In the picture you can see a example, the vector1 is what I need, always rotated 90 degrees and vector2 would be my cylinder direction vector
I possibly have found the correct formula:
Vector3 tFinal = Vector3.Multiply((float)Math.Cos(DegreeToRadian(90)), tPointFinder) + Vector3.Multiply((float)Math.Sin(DegreeToRadian(90)), Vector3.Cross(tCylinderDirection, tPointFinder));
Vector3 tFinal180 = Vector3.Multiply((float)Math.Cos(DegreeToRadian(180)), tPointFinder) + Vector3.Multiply((float)Math.Sin(DegreeToRadian(180)), Vector3.Cross(tCylinderDirection, tPointFinder));
Vector3 tFinal270= Vector3.Multiply((float)Math.Cos(DegreeToRadian(270)), tPointFinder) + Vector3.Multiply((float)Math.Sin(DegreeToRadian(270)), Vector3.Cross(tCylinderDirection, tPointFinder));
Interesting is that if I try it with (1,1,0) as cylinder direction it gives me correct directions but the length is different for 90 degrees and 270.
Here is the code that should solve your problem assuming that the input requirements are satisfied.
float zCutPlaneLocation = 20; // should not get bigger than cylinder length
float cylinderRadius = 100;
Vector3 cylinderCenter = new Vector3(0, 0, 0); // or whatever you got as cylinder center point, given as Vector3 since Point type is not defined
// will return 360 points on cylinder edge, corresponding to this z section (cut plane),
// another z section will give another 360 points and so on
List<Vector3> cylinderRotatedPointsIn3D = new List<Vector3>();
for (int angleToRotate = 0; angleToRotate < 360; angleToRotate++)
{
cylinderRotatedPointsIn3D.Add(GetRotatedPoint(zCutPlaneLocation, angleToRotate, cylinderRadius, cylinderCenter));
}
....
private static Vector3 GetRotatedPoint(
float zLocation, double rotationAngleInRadian, float cylinderRadius, Vector3 cylinderCenter)
{
Vector2 cylinderCenterInSection = new Vector2(cylinderCenter.X, cylinderCenter.Y);
float xOfRotatedPoint = cylinderRadius * (float)Math.Cos(rotationAngleInRadian);
float yOfRotatedPoint = cylinderRadius * (float)Math.Sin(rotationAngleInRadian);
Vector2 rotatedVector = new Vector2(xOfRotatedPoint, yOfRotatedPoint);
Vector2 rotatedSectionPointOnCylinder = rotatedVector + cylinderCenterInSection;
Vector3 rotatedPointOnCylinderIn3D = new Vector3(
rotatedSectionPointOnCylinder.X,
rotatedSectionPointOnCylinder.Y,
zLocation + cylinderCenter.Z);
return rotatedPointOnCylinderIn3D;
}
I just created a console app for this. First part of code should be added in main method.
Working with those matrices seems is not that easy. Also I am not sure if your solution works ok for any kind of angle.
Here the idea is that the rotated points from cylinder are calculated in a section of the cylinder so in 2D than the result is moved in 3D by just adding the z where the Z section was made on cylinder. I suppose that world axis and cylinder axis are on the same directions. Also if your cylinder gets along (increases) on the X axis, instead of Z axis as in example just switch in code the Z with X.
I attached also a picture for more details. This should work if you have the cylinder center, radius, rotation angle and you know the length of the cylinder so that you create valid Z sections on cylinder. This could get tricky for clockwise/counter clock wise cases but lets see how it works for you.
If you want to handle this with matrices or whatever else I think that you will end up having this kind of result. So I think that you cannot have "all" the rotated points in just a list for the entire cylinder surface, they would depend on something like the rotated points of a Z section on the cylinder.
So... I'll try to be as clear as possible, if I let something unclear please let me know.
I have a vector that comes from origin and go to a point in space, and I have an object that I want it's transform.up (Or Y vector) to be colinear with this vector, but the Y rotation of this object is driven by another factor, and I dont want to change it.
So far, what I'm trying to do is project this vector in the local XY and local ZY planes and measure the angles and apply rotation:
float xInclination = Mathf.Atan(Vector3.ProjectOnPlane(orbitMomemtumVector, transform.right).z / Vector3.ProjectOnPlane(orbitMomemtumVector, transform.right).y)*Mathf.Rad2Deg;
float yInclination = Mathf.Atan(initialPos.z / initialPos.x) * Mathf.Rad2Deg;
float zInclination = -Mathf.Atan(Vector3.ProjectOnPlane(orbitMomemtumVector, transform.forward).x / Vector3.ProjectOnPlane(orbitMomemtumVector, transform.forward).y)*Mathf.Rad2Deg;
if (initialPos.x < 0 && initialPos.z > 0)
{
yInclination = 180f - Mathf.Abs(yInclination);
argumentPeriapsis = argumentPeriapsis - yInclination;
}
else if (initialPos.x < 0 && initialPos.z < 0)
{
yInclination = 180f + Mathf.Abs(yInclination);
argumentPeriapsis = argumentPeriapsis - yInclination;
}
else
{
argumentPeriapsis = argumentPeriapsis - yInclination;
}
transform.rotation = Quaternion.Euler(xInclination, (float)argumentPeriapsis, zInclination);
This image shows the problem, I need the Y arrow to be collinear with the blue line
Let me be clear on this, don't use Euler angles in 3d space. In fact, avoid them in 2d games as well. Unless your object truly rotates on a single axis, and you never have to get the angle between two rotations, or lerp your rotations, don't use them.
What you want is Quaternion.LookRotation(A, B).
A being a vector to which Z will be colinear, X being orthogonal to the plane defined by A and B, and Y belonging to that plane.
Followup:
To match other axis to A, there are multiple solutions. First would be to simply apply the lookRotation to a parent object, while the child object is rotated inside to match whatever rotation you want. You can also edit your entire mesh to do so.
The other way is to simply apply another rotation, so that both combined get your desired result, like so:
Quaternion zMatched = Quaternion.LookRotation(zAxisTarget, direction)
Quaternion yMatched = zMatched * Quaternion.AngleAxis(90f, Vector3.right);
transform.rotation = yMatched;
This will rotate the object so that the y axis becomes collinear to the previous z axis.
This is however nor perfect. If you reach this point, you should consider building your own clearer solution based on combining AngleAxis results. But it works well enough.
My issue is that I've been trying to check if a rectangle that is rotated by a certain amount of degrees contain a point, but I wasn't able to calculate that after many attempts with the help of some code samples and examples that I've found online.
What I got is the rectangle (X, Y, Width, Height, Rotation) and the point (X, Y) and I've been trying to create a simple function that lets me instantly calculate that, which would be something something like this:
public bool Contains(Rect Rectangle, float RectangleRotation, Point PointToCheck);
But as I mentioned, I wasn't able to do so, those mathematical calculations that include some formulas I found online are way too much for me to understand.
Could someone help me with calculating this? If you could provide the calculation in C# code form (not formulas) then that would be great! Thanks.
PS: Using the 2D Physics Engine that is available in Unity3D is not a option, my rectangle is not associated with a gameobject that I could attach a 2D collision component to, I need to do this mathematically without the involvement of gameobjects or components.
Edit: I forgot to mention, the rectangle is being rotated by the middle of the rectangle (center/origin).
Rather than checking if the point is in a rotated rectangle, just apply the opposite of the rotation to the point and check if the point is in a normal rectangle. In other words, change your perspective by rotating everything by -RectangleRotation, so that the rectangle does not appear rotated at all.
public bool Contains(Rect rect, float rectAngle, Point point)
{
// rotate around rectangle center by -rectAngle
var s = Math.Sin(-rectAngle);
var c = Math.Cos(-rectAngle);
// set origin to rect center
var newPoint = point - rect.center;
// rotate
newPoint = new Point(newPoint.x * c - newPoint.y * s, newPoint.x * s + newPoint.y * c);
// put origin back
newPoint = newPoint + rect.center;
// check if our transformed point is in the rectangle, which is no longer
// rotated relative to the point
return newPoint.x >= rect.xMin && newPoint.x <= rect.xMax && newPoint.y >= rect.yMin && newPoint.y <= rect.yMax;
}
I have a player position, a pointer indicating the players view direction, a distance and a horizontal and vertical angle. I want to calculate a target position:
that is distance away from the players position
that, from the players view direction, is horizontal angle to
the right and vertical angle up
It's about positioning a Hololens-Application UI in a sphere around the player. The UI should i.e. be 40 degrees to the leftand 20 degrees up from the players view direction.
Edit: Added image to clarify. Given is the Player Pos (pX|pY|pZ), the radius (= length of the black bold line) and both angles in degree.
I'm looking for how to calculate the UI Center position (x?|y?|z?).
You can use Quaternion.Euler to create a rotation based on angles in world space and then get the desired result by multiplying it with a known position.
So by using your example you could find the position like this:
float radius, x_rot, y_rot;
Vector3 forwardDirection, playerPos;
Vector3 forwardPosition = playerPos + (forwardDirection * radius);
Vector3 targetPosition = Quaternion.Euler(x_rot, y_rot, 0) * forwardPosition;
Try check out the docs on Quaternion and Quaternion.AngleAxis for more handy rotation stuff.
Answer by a mathematician:
To calculate the spherical position with the given information (distance between objects, x angle, y angle) you use trigonometry:
float x = distance * Mathf.Cos(yAngle) * Mathf.Sin(xAngle);
float z = distance * Mathf.Cos(yAngle) * Mathf.Cos(xAngle);
float y = distance * Mathf.Sin(yAngle);
ui.transform.position = player.transform.position + new Vector3(x,y,z);
// Set UI in front of player with the same orientation as the player
ui.transform.position = player.transform.position + player.transform.forward * desiredDistance;
ui.transform.rotation = player.transform.rotation;
// turn it to the left on the players up vector around the the player
ui.transform.RotateAround(player.transform.position, player.transform.up, -40);
// Turn it up on the UI's right vector around the player
ui.transform.RotateAround(player.transform.position, ui.transform.right, 20);
assuming you also want the UI to face the player, otherwise you have to set another rotation after this.
No need to calculate it yourself, the Unity API already does it for you (
see Rotate around)
If i am understanding you correctly you want to create a UI that hovers above a point. I recently did a similar thing in my game. and this is how i did it.
if (Input.GetMouseButtonDown(0)) // use the ray cast to get a vector3 of the location your ui
// you could also do this manualy of have the computer do it the main thing is to
// get the location in the world where you want your ui to be and the
// WorldTOScreenPoint() will do the rest
{
RaycastHit hit;
Vector3 pos;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
{
pos = hit.point;
pos.y += yOffset; // use the offset if you want to have it hover above the point
ui.transform.position = cam.WorldToScreenPoint(pos); // use your main cammera here
// then either make your ui vissible or instanciati it here and make sure if you instanciate it
// that you make it a child of your cnavas
}
}
I hope this solves you problem. If i am not understanding what you are trying to do let me know and i will try to help.
Note: if you want to make the ui look farther away when you move away from the point scale the ui down as you move farther away, and scale it up when you get closer.
The diagram in the question is somewhat confusing:
The axes are in the orientation of a right-handed coordinate system, but Unity uses a left-handed coordinate system.
In terms of Euler angles, the part of the image labeled "x Angle" is actually the Y angle (rotation around Y axis), and the part of the image labeled "y Angle" is actually the X angle (around X axis).
The two angles listed use a different sign convention. The Y angle (labeled "x Angle") is following the right-hand rule, while the other angle is not.
Jonas Zimmer has a great answer that follows the conventions in the image, but I'll try to do something a bit less confusing and follows more standard math conventions.
Here is some code for Unity written in C#, in YX rotation order, treating zero angle as forward (+Z), and follows Unity's conventions of a left-handed, Y-is-up, Z-is-forward coordinate system. Increasing Y angle rotates to the right, and increasing X angle rotates down.
public static Vector3 Vector3FromAngleYX(float y, float x)
{
float cosx = Mathf.Cos(x);
return new Vector3(cosx * Mathf.Sin(y), -Mathf.Sin(x), cosx * Mathf.Cos(y));
}
Also, I found this question looking to implement a Godot version, so here is a version for Godot Engine written in GDScript, in YX rotation order, treating zero angle as forward (-Z), and follows Godot's conventions of a right-handed, Y-is-up, Z-is-back coordinate system. Increasing Y angle rotates to the left, and increasing X angle rotates up.
func vector3_from_angle_yx(y, x):
var neg_cosx = -cos(x)
return Vector3(neg_cosx * sin(y), sin(x), neg_cosx * cos(y))
I have the normals of three planes which are at 90 degrees to each other and pass through the origin.
I also have the distances of a point to the three planes. How can I determine the position of the point in C#?
Heres what I have so far...
public static Vector3 RealWorldSpaceToOtherSpace(Vector3 realspace, Vector4 clipplane)
{
// Work out the normal vectors of 3 imaginary planes
Vector3 floor = new Vector3(clipplane.X, clipplane.Y, clipplane.Z);
Vector3 centre = new Vector3(1f, -clipplane.X / clipplane.Y, 0f);
Vector3 wall = Vector3.Cross(floor, centre);
// Get distances from the planes
float distanceToFloor = realspace.y;
float distanceToCentre = realspace.x;
float distanceToWall = realspace.z;
// ?
// ? do stuff here
// ?
// return ????
}
Since the planes are at 90 degrees to each other, and pass through the origin, their normals form an orthogonal coordinate system with the same origin. Thus the distance to each plane is the coordinate of the point along the axis corresponding to the plane's (normalized) normal, in the new coordinate system.
Hence find the normalized normals of each plane, multiply by the corresponding distance to the plane, and add up to find the point.
A caveat is that you need to know which side of each plane the point is on; if it is on the other side of the normal, put a minus sign in front of the corresponding distance.
When given the distance from the point to one plane. The point can now be at any one point inside a plane that is at the defined distance from your first plane, so you have narrowed down the position from anywhere within the 3 dimensions to anywhere with these subset of the 3rd dimension which only has 2 dimensions..
When you are given the distance from the point to a second plane. You now have that same information, a plane at which the point can be. You will notice that both of these plains intersect and form a line. There's no need to store this line. Just know that you have narrowed down the possible point's position to 1 dimension.
Finally you are given the distance from the point to the third plane. You can create the new plane at the defined distance from the original plane , and this plane should intersect the other 2 new planes. All three planes will intersect at one point, which will be the position of the point.
Thankfully we can add the distance to the planes vector in order to create the new planes. And then we can finally get the point's position by using each of the plane's relevant dimensions in order to get the point's position.
public static Vector3 RealWorldSpaceToOtherSpace(Vector3 realspace, Vector4 clipplane)
{
// Work out the normal vectors of 3 imaginary planes
Vector3 floor = new Vector3(clipplane.X, clipplane.Y, clipplane.Z);
Vector3 centre = new Vector3(1f, -clipplane.X / clipplane.Y, 0f);
Vector3 wall = Vector3.Cross(floor, centre);
// Get distances from the planes
// Distance is represented as a Vector, since it needs to hold
// the direction in 3d space.
Vector3 distanceToFloor = new Vector3(0f, realspace.y ,0f,);
Vector3 distanceToCentre = new Vector3( realspace.x ,0f,0f,);
Vector3 distanceToWall = new Vector3(0f,0f, realspace.z );
// Create planes that contain all the possible positions of the point
// based on the distance from point to the corresponding plane.
Vector3 pointY = floor + distanceToFloor;
Vector3 pointX = centre + distanceToCentre;
Vector3 pointZ = wall + distanceToWall;
// Now that we have all 3 point's dimensions, we can create a new vector that will hold its position.
Vector3 point = new Vector3(pointX.x,pointY.y, pointZ.z);
return point
}
Note that there is a plane struct in Unity.