I have a Vector2-variable where vector.x and vector.y have been clamped so that both are only allowed a value between 0 and 4. Obviously, this leaves me to having a quadratic space around the coordinate 2, 2 to move a gameobject around on.
But what I want to achieve is having a circular space to move a gameobject on, which means that I need it to clamp the value of vector.x+vector.y to be a total value in between 0 and 4, but I can't seem to make it work. Is there another function I should use instead of clamp?
This is what I have at the moment:
Vector2 pos = rigidbody2D.transform.position;
pos.x = Mathf.Clamp (pos.x, 19.5f, 24);
pos.y = Mathf.Clamp (pos.y, 3.3f, 6);
rigidbody2D.transform.position = pos;
Instead of clamping each axis individually, how can I give them a total minimum- and maximum-value?
There is a similar function for Vectors: Vector2.ClampMagnitude and Vector3.ClampMagnitude
You can only specify a maximum length, so you have to take care of the minimum yourself. The problem with the minimum value is, that the function would not know what to do if the vector has a length of 0. In which direction should the vector point to achieve the minimum length?
Limit input to a circle
If you only want to limit the movement to a circle, you don't need a minimum value. Instead define the center as (2,2) and limit the movement to a radius of 2.
Vector2 center = new Vector2(2f, 2f);
Vector2 moveBy = new Vector(4f, 7f); // set this to whatever your input is
moveBy = Vector2.ClampMagnitude(moveBy, 2f);
Vector2 newPosition = center + moveBy;
newPosition will be inside a circle with a radius of 2 around your center at (2,2)
Limit given position to a circle
If you want to clamp a given position to a circle, you can slightly modify the version above. It's like putting the object on a leash and pull it back when it leaves the circle.
Vector2 center = new Vector2(2f, 2f);
Vector2 position = new Vector2(6f, 5f); // outside your desired circle
Vector2 offset = position - center;
Vector2.ClampMagnitude(offset, 2f);
position = center + offset;
Unfortunately There is no in-built function available same as clamp function. but if you don't want to use CLAMP function then you can use IF statements.
if(pos.x > 19.5f )
{
//put logic here
}
if( pos.x < 24f)
{
//put logic here
}
if(pos.y > 3.3f)
{
//put logic here
}
if( pos.y < 6f)
{
//put logic here
}
you can do this. but in my opinion Mathf.Clamp() is the best option for Restricting values in unity,if conditions will not be accurate as Mathf.Clamp() Function.
Not C#, but here's a snippet I use for limiting the touchpad knob to the area of the touchpad in a libgdx project of mine:
Vector2 maxPos = new Vector2(screenX, screenY);
if (maxPos.dst(new Vector2(Settings.touchpadPositionX, Settings.touchpadPositionY)) > painter.getTouchpadSize())
{
maxPos.sub(Settings.touchpadPositionX,Settings.touchpadPositionY);
maxPos.nor();
maxPos.scl(painter.getTouchpadSize());
maxPos.add(Settings.touchpadPositionX,Settings.touchpadPositionY);
}
painter.updateTouchpadPos((int)maxPos.x, (int)maxPos.y);
Related
So, here is what i have:
SteamVR's hand gameobject
3D sphere.
what i want:
The sphere to move to same direction/position as the hand does, but it to move further with a multiplier. E.g. i move the VR controller and the hand moves 1 unit. I want that the sphere moves to the same direction in a same amount of time but e.g. 2 units. how do i do this?
i tried simple
sphere.transform.position = ControllerToFollow.position +2f;
but then the sphere is always offset.
position is a Vector3, which is essentially 3 floats - you can't plus a Vector3 with a float unless you overload the + operator. Otherwise what you can do is the following:
Vector3 followPos = new Vector3(ControllerToFollow.position.x + 2f,
ControllerToFollow.position.y + 2f,
ControllerToFollow.position.z + 2f);
sphere.transform.position = followPos;
If you only want it to follow on one axis, then you can do the following:
Vector3 followPos = new Vector3(ControllerToFollow.position.x + 2f, // Follow on x Axis
ControllerToFollow.position.y, // Y axis is the same
ControllerToFollow.position.z); // X Axis is the same
sphere.transform.position = followPos;
Edit: I think I understand your problem better now. Here's a better version.
if (Vector3.Distance(sphere.transform.position, ControllerToFollow.position) >= 2f)
{
// Code that makes the sphere follow the controlling
}
Just track the movement Delta of the hand and multiply it by a certain multiplier.
At the beginning of the manipulation store
private Vector3 lastControllerPosition;
...
lastControllerPosition = ControllerToFollow.position;
then in every frame compare
var delta = ControllerToFollow.position - lastHandPosition;
// Don't forget to update lastControllerPosition for the next frame
lastControllerPosition = ControllerToFollow.position;
Now in delta you have a movement of the controller since the last frame. So you can assign it to the sphere with a multiplier using Transform.Translate
sphere.transform.Translate(delta * multiplier, Space.World);
or simply using
sphere.transform.position += delta * multiplier;
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.
I am working on a first person shooter game and I want to limit the camera rotation. I have used Math.clamp to clamp the angle but after angle reaches to 0 it clamps to maximum angle. It was supposed to not clamp until -24f. How can I fix that.
public void CameraRotate(Vector3 camrotate){
cam.transform.Rotate (camrotate*Time.deltatime);
temporaryangle = cam.transform.eulerAngles;
temporaryangle= new Vector3 (Mathf.Clamp(cam.transform.eulerAngles.x,-24f,55f),cam.transform.eulerAngles.y,cam.transform.eulerAngles.z); // camrotate means a vector and cam is the camera
cam.transform.eulerAngles= temporaryangle;
}
You can shift the comparison to a range that doesn't involve the wrap around at 0 / 360, by adding an offset before the comparison (let's say 180f), and removing it after:
var tmpOffset = 180f;
var clampedX = cam.transform.eulerAngles.x + tmpOffset;
clampedX = Mathf.Clamp(clampedX, tmpOffset - 24f, tmpOffset + 55f);
clampedX -= tmpOffset; // return to the correct value
temporaryangle = new Vector3 (clampedX, cam.transform.eulerAngles.y, cam.transform.eulerAngles.z); // camrotate means a vector and cam is the camera
I'm looking to place an object at a specific position relative to another :
This new object has to be placed in the pink zone, and I only know the minimum and max distance of placement, an angle relative to my first object forward direction (maxAngle in degrees), and the position of this first object.
I already know how to check if an object is placed in the pink zone, but not set its position in this zone. So I took the code to check an object in the cone, but I can't get how to transform it to set the position in the cone.
float distance = Random.Range(minDistance, maxDistance);
float angle = maxAngle *= Mathf.Deg2Rad;
float coneRadius = distance * Mathf.Tan(angle);
Vector3 vect = firstObject.transform.position - targetObject.transform.position;
targetObject.transform.position = new Vector3(angle, 0, firstObject.transform.position.z + distance);
If you can give me clues, it'll be very cool.
The trick is to move the local position and then straighten...
This is indeed a basic technique in Unity or any transform-based scene engine.
Create the new object, "newb".
(1) Position the object exactly at the "+" in your image.
(2) Choose your angle
angle = Random.Range(-maxAngle, maxAngle);
(3) Twist newb by that much:
newb.transform.eulerAngles = new Vector3( 0f, 0f, angle);
(4) Choose your distance:
distance = Random.Range(minDistance,maxDistance);
(5) Then offset the LOCAL position of newb by that much:
newb.transform.Translate(0f, 0f, distance, Space.Self);
And then the trick:
Note that "newb" will be "twisted", so make it sit straight:
newb.transform.eulerAngles = Vector3.zero;
I am encountering an issue when updating a bullet's position when I rotate the ship that shoots it. Currently the code almost works except that at most angles the bullet is not fired from where i want it to. Sometimes it fires slightly upward than the centre where it should be, and sometimes downwards. I'm pretty sure it has something to do with the trigonometry but I'm having a hard time finding the problem.
This code is in the constructor; it sets the bullet's initial position and rotation based on the ship's position (sp.position) and its rotation (Controls.rotation):
this.backgroundRect = new RotatedRectangle(Rectangle.Empty, Controls.rotation);
//get centre of ship
this.position = new Vector2(sp.getPosition().X + sp.getTexture().Width/2,
(sp.getPosition().Y + sp.getTexture().Height / 2));
//get blast pos
this.position = new Vector2((float)(this.position.X + (sp.getTexture().Width / 2 *
Math.Cos(this.backgroundRect.Rotation))), (float)(this.position.Y +
(sp.getTexture().Width / 2 * Math.Sin(this.backgroundRect.Rotation))));
//get blast pos + texture height / 2
this.position = new Vector2((float)(this.position.X + (this.texture.Height / 2 *
Math.Cos(this.backgroundRect.Rotation))), (float)(this.position.Y +
(this.texture.Height / 2 * Math.Sin(this.backgroundRect.Rotation))));
this.backgroundRect = new RotatedRectangle(new Rectangle((int)this.position.X,
(int)this.position.Y, this.texture.Width, this.texture.Height), Controls.rotation);
speed = defSpeed;
Then in the update method I use the following code; I'm pretty sure this is working fine though:
this.position.X = this.position.X + defSpeed *
(float)Math.Cos(this.getRectangle().Rotation);
this.position.Y = this.position.Y + defSpeed *
(float)Math.Sin(this.getRectangle().Rotation);
this.getRectangle().CollisionRectangle.X = (int)(position.X + defSpeed *
(float)Math.Cos(this.getRectangle().Rotation));
this.getRectangle().CollisionRectangle.Y = (int)(position.Y + defSpeed *
(float)Math.Sin(this.getRectangle().Rotation));
Also I should mention that my ship had not been working correctly when I rotated it since the origin (center of rotation) was (0,0). To remedy this I changed the origin to the center of the ship (width/2,height/2) and also added those values to the drawing rectangle so that the sprite would draw correctly. I imagine this could be the problem and suppose there's a better way of going around this. The game is in 2D by the way.
The problem is that the sprites are not drawn on the positions they are supposed to be. When calculating the center position, you assume that the sprite is not rotated. This causes some trouble. Furthermore, rotating an arbitrary vector is not as easy as you did. You can only rotate a vector by multiplying its components with sin/cos when it is on the x-axis.
Here is how you can rotate arbitrary vectors:
Vector2 vecRotate(Vector2 vec, double phi)
{
double length = vec.Length(); //Save length of vector
vec.Normalize();
double alpha = Math.Acos(vec.X); //Angle of vector against x-axis
if(vec.Y < 0)
alpha = 2 * Math.PI - alpha
alpha += phi;
vec.X = Math.Cos(alpha) * length;
vec.Y = Math.Sin(alpha) * length;
return vec;
}
Make sure, the angle is in radians.
With that you can calculate the correct position of the ship:
Vector2 ShipCenter = sp.Position() + vecRotate(new Vector2(sp.getTexture().Width/2, sp.getTexture().Height/2), Controls.Rotation);
You can use the same function to determine the position of the bullet's center. I don't exactly know, where you want to place the bullet. I will assume, it is at the center of the right edge:
Vector2 offset = new Vector2(sp.getTexture.Width/2, 0);
this.position = ShipCenter + vecRotate(offset, Controls.Rotation);
If you then need the position of the upper left corner of the bullet, you can go further:
this.position -= vecRotate(new Vector2(this.texture.Width/2, this.texture.Height/2), Controls.Rotation)
However, as I stated, it is probably easier to handle the ship's and bullets' positions as central positions. Doing so, you need far less trigonometry.