Unity Mathf.clamp not working as expected? - c#

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

Related

C# Set Rotation of Object to Face Coordinates

I'm making a game in a custom engine using C#. (Not Unity)
I've got a large grid and the x/y coordinates of two objects. The Player object and the Destination object. Along with the player's current rotation in degrees (0-360).
I've become too reliant on existing game engines and cannot work out how to find the rotation I need to put the player at to face the target.
playerRotation;//0 to 360 degrees.
playerX double = 47.43;
playerY double = 43.36;
targetX double = 52.15;
targetY double = 38.67;
My method at the moment is to try and get the distance between the objects by:
float distanceX = Math.Abs(playerX - destinationX);
float distanceY = Math.Abs(playerY - destinationY);
Which seems to work fine. Then I need to rotate the player to face the destination and have them move towards it until distanceX/Y are <= 0.
Edit: I've been messing with Atan2 to try and get the answer.
Vector2 playerCoords = new Vector2(playerX, playerY);
Vector2 targetCoords = new Vector2(targetX, targetY);
double theta = Math.Atan2((targetCoords.yValue - playerCoords.yValue), (targetCoords.xValue - playerCoords.xValue));
theta = theta * (180 / Math.PI);//Convert theta to degrees.
double sigma = playerRotation;//Direction in degrees the player is currently facing.
double omega = sigma - theta;
OutputLog("omega: " + omega);
My output log should be showing me the degrees my player needs to be facing to be facing the target. But it's giving me the wrong results.
Player: (4782, 4172) and
Target: (4749, 4157)
Angle should be about 286~.
But Theta = -155 and omega = 229.
Vector math can be very helpful, and it's not that complicated.
First vectors would be your player position and destination's:
Vector2 playerPos = new Vector2(playerX, playerY);
Vector2 destinationPos = new Vector2(destinationX, destinationY);
Now you can just subtract both vectors, to get a vector which points from one position to the other.
Vector2 delta = destination - playerPos; // Note, it might be the other way around: playerPos - destination
That delta vector has a length, and that is the distance between both points. There is usually a Length and a LengthSquared property available on the Vector class. Be aware however that calculating the length is quite CPU intensive because it uses a square root. If you want to compare that distance to a fixed distance like 200, just use the length squared and compare it to (200 * 200) which is way faster.
You can also use that delta, to let a bullet fly from one position to the other. You just need to normalize delta, there's a method for it, and you have it scaled down to length one. You can now use that delta, multiplied with a speed on each physics cycle to change the bullets position.
Now to get the angle, you can just use:
double angle = Math.Atan2 (delta.Y, delta.X); // Note that y and x are reversed here, and it should be like that.
Note that this angle is in radians, not degrees. A circle in radians starts at -PI and ends at PI. A full circle therefore is 2 * PI. To convert radians to degrees, you can see this question
Edit
I always assume that 12 o'clock is 0 degrees, 3 o'clock is 90, 6 o'clock is 180 and 9 o'clock is 270.
But actually in the cartesian coordinate system things are a bit different. I also made this false assumption in the code below.
But it turned out that I was wrong. See this picture
Now if you look at my sourcecode, all my variables are named incorrectly. If you look at their values however you can see that they match up with the picutre. Therefore the Atan2 correction is as it's supposed to be.
// Using Vector2 from System.Numerics;
internal static double RadianToDegree(double rad)
{
double thetaDegree = rad * (180.0 / Math.PI);
// Convert negative angles into positive ones
// https://stackoverflow.com/a/25725005/7671671
double thetaDegree2 = (thetaDegree + 360) % 360;
return thetaDegree2;
}
internal void Run()
{
// Player: (4782, 4172) and Target: (4749, 4157)
Vector2 player = new Vector2(4782, 4172);
Vector2 target = new Vector2(4749, 4157);
Vector2 delta = target - player;
double theta = Math.Atan2(delta.Y, delta.X);
double thetaDegree = RadianToDegree(theta);
// Given cartesian coordinate system
// positive y is up, negative is down
// positive x is right, negative is left
// Falsely assuming up is 0
// Falsely assuming right is 90
// Falsely assuming down is 180
// Falsely assuming left is 270
Vector2 v0 = new Vector2(0, 1);
Vector2 v45 = new Vector2(0.5f, 0.5f);
Vector2 v90 = new Vector2(0.5f, 0);
Vector2 v180 = new Vector2(0, -1);
Vector2 v270 = new Vector2(-1, 0);
double theta0 = Math.Atan2(v0.Y, v0.X);
double theta45 = Math.Atan2(v45.Y, v45.X);
double theta90 = Math.Atan2(v90.Y, v90.X);
double theta180 = Math.Atan2(v180.Y, v180.X);
double theta270 = Math.Atan2(v270.Y, v270.X);
double result0 = RadianToDegree(theta0);
double result45 = RadianToDegree(theta45);
double resultv90 = RadianToDegree(theta90);
double resultv180 = RadianToDegree(theta180);
double resultv270 = RadianToDegree(theta270);
// result 0 --> 90
// result 45 --> 45
// result 90 --> 0
// result 180 --> 270
// result 270 --> 180
}

Felling a tree in a random direction?

I cannot figure out how to make a tree with a random Y-rotation fall after cutting it down. I want it to fall until it is perpendicular to the ground (90 degrees), but I want it to be random which direction it falls in.
The pivot is on the bottom of the tree, so if I just rotate Z-axis to 90 degrees then it looks like it falls, but I want to randomize the direction, I tried some stuff but its not doing what I expect:
public void Fall(float duration)
{
int xOrZ = Random.Range(0, 1);
float randomRot = Random.Range(0,90);
Vector3 rotation = Vector3.zero;
if (xOrZ == 0)
rotation = new Vector3(90, 0, randomRot);
else
rotation = new Vector3(randomRot, 0, 90);
mTransform.DORotate(rotation, duration);
}
I figured if I make sure one of the axis is 90 then it will always fall to the ground but that didnt work at all.
Since the pivot of your object is at the bottom, it is easier to Lerp the transform.up vector of the tree GameObject in a way that it starts to tilt. You can pick a point inside a unit circle using Unity's Random.InsideUnitCircle. This would return a Vector2, then you can multiply each component with your right and forward vectors to find where the tip of the tree will be when it falls. Now, the vector between the tree object's position and the falling point should be your new up vector for the tree object. Then you can run a simple Coroutine that Lerps the up vector of the tree object to the newly calculated up vector. The Lerp allows you to define the duration and you can cut the Coroutine off once the distance between the desired up vector and the current up vector is below a certain threshold. If you use this code and call the Fall function just as you desired it would work:
using System.Collections; // for IEnumerator
public GameObject treeObject; // assuming this is your tree object
public void Fall(float duration)
{
// pick a random point on the circle to match the up vector
Vector2 pointOnCircle = UnityEngine.Random.insideUnitCircle * treeObject.transform.localScale.y;
// find the fall point, assuming the pivot of the object is at the bottom
Vector3 fallPoint = treeObject.transform.position +
pointOnCircle.x * treeObject.transform.right +
pointOnCircle.y * treeObject.transform.forward;
// find the target up vector
Vector3 updatedUpVector = Vector3.Normalize(fallPoint - treeObject.transform.position);
// Start the coroutine to tilt the up vector to the desired target
StartCoroutine(UpdateUpVector(treeObject, updatedUpVector, duration, 0.001f));
}
public IEnumerator UpdateUpVector(GameObject target, Vector3 upVector, float duration, float threshold = 0.001f)
{
// the target vector and up vector would get closer to each other until the threshold is hit
while(Vector3.Distance(upVector, target.transform.up) > threshold)
{
target.transform.up = Vector3.Lerp(target.transform.up, upVector, duration * Time.deltaTime);
yield return new WaitForEndOfFrame();
}
}
The code above results with this, using a cylinder whose pivot is at the bottom. Notice that the tree is falling towards the random point on the unit circle that is around the transform.position of the tree object.:

Unity3D make object follow another but to move further

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;

Unity 3D Set object in cone

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;

Something similar to Mathf.Clamp-functions?

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);

Categories