Unity: How to stop object from spinning when rotating towards object? - c#

When I try to rotate an object towards a point, it starts spinning, but I want it to be able to freely spin, while still rotating towards that point. I can't just clamp the y rotation, because obviously it's already rotating towards a point and setting the absolute y rotation while doing so and you can't access an object's relative angles in Unity. As seen here, the blue gizmo always faces the top of the sphere (the centre of the sphere is the point I'm rotating towards). I want it to be able to move freely.
Here's my rotation code:
Quaternion dir = Quaternion.LookRotation((target - transform.position).normalized) * Quaternion.Euler(new Vector3(-90, 0, 0));
transform.rotation = Quaternion.Slerp(transform.rotation, dir, smooth);
Does anyone know how to fix this?

Related

Problem with 3rd person camera rotation in unity

I'm trying to make the camera rotate around an object according to the movement of the mouse, and this is what I've come up with:
void Start()
{
direction = new Vector3(1f,0f,0);
direction.Normalize();
Debug.Log(direction);
mousePosCurr = Input.mousePosition;
}
void Update()
{
mousePosPrev = mousePosCurr;
mousePosCurr = Input.mousePosition;
mouseVector = (mousePosCurr - mousePosPrev);
mouseVector = mouseVector * sensitivity;
direction = Quaternion.Euler(mouseVector.y, mouseVector.x, 0) * direction;
direction.Normalize();
Debug.Log(direction);
this.transform.position = (target.position + tOffset + (direction * distance));
transform.LookAt(target.position + tOffset, Vector3.up);
}
Now, I'm sure that there are at least a million problems with this code, however the main one I'm facing right now is that the x rotation does not work correctly, unless the y rotation is in a specific direction.
What's happening is basically that the more I rotate in the y axis the more "inprecise" the x movement becomes. So for example, when the camera is south of the object I'm rotating it around, the x rotation works exactly as it should, you move the mouse up, and the camera points up and viceversa, but when it's west of it the movement of the mouse required for the vertical rotation is no longer vertical, but diagonal, and when it's north of the object the movement is inverted: moving the mouse up makes the camera point down and viceversa.
This seems most likely caused by the fact that regardless of the horizontal rotation of the direction vector, I'm still rotating along the x axis in the same direction, as if the camera was south of the object.
However, even having (hopefully) identified the problem, I'm still clueless as to how to solve it, so I'm asking for help here.
Note: I just noticed that I probably used the term "rotation" wrong in this post. When I write "rotation of the camera", I'm actually referring to the rotation of the "direction" vector.
alright so I am not the best in Unity so this might be completely useless but when I try to rotate my camera around an object I create an empty game Object and make the camera a child of it, like this
so that I can just just rotate the Game Object to rotate the camera around a fixed point maybe your code will work

Draw a line from object to where camera is facing in Unity

I am starting out to create a minigolf game in Unity. I want to aim by draggin the screen with my mouse which rotates the camera around the ball. Basically I want to draw an aim line (line renderer or any other good method) from the ball to where the camera is pointing. Right now I just draw the line I want from beginning, but in the end I want it to rotate with the camera like the red line in picture below.
The code for rotating the camera is working great, where previousCameraPosition is set when I click the mouse and Rotate() is called when mouse button is being held down:
private void Rotate()
{
// Get the movement vector
Vector3 cameraMoveDirection = previousCameraPosition - mainCamera.ScreenToViewportPoint(Input.mousePosition);
// Rotate around the ball
mainCamera.transform.position = ballObject.transform.position;
// Make the rotations
mainCamera.transform.Rotate(new Vector3(1, 0, 0), cameraMoveDirection.y * 360);
mainCamera.transform.Rotate(new Vector3(0, 1, 0), -cameraMoveDirection.x * 360, Space.World);
// Set the camera a certain amount away from the ball
mainCamera.transform.Translate(new Vector3(0, 0, -ballObject.transform.position.z*2.5f));
// Update the previousCameraPosition during the movement
previousCameraPosition = mainCamera.ScreenToViewportPoint(Input.mousePosition);
}
I tried to create a helper object which was set to rotate exactly like the camera which made the line exactly like I wanted it, except it was backwards (180 degrees wrong, facing into the camera instead of away) and it was 1 unit long since it was normalized. I am stuck on this for far too long now, any help or guidance would be very appreciated.
First you can simply use the camera's forward vector without having to deal with any Quaternion rotation at all:
var forward = Camera.main.transform.forward;
Then what you actually seem to want is not exactly this direction but rather map it onto a flat XZ plane:
var actualForward = Vector3.ProjectOnPlane(forward, Vector3.up).Normalized;
then finally you can use this direction to draw your line e.g. between the points
var startPoint = ball.position;
var endpoint = startPoint + actualForward * lineLength;

How to make an object move straight from random directions in Unity

I have a ball object in Unity 2D, this ball spawns at random positions on the screen and I want it to move in a 'straight-line depending on its direction e.g. if it's from the left it goes to the right or if it's from the top it goes to the bottom. I'm not sure if I should use transform.position/addforce/velocity to accomplish this and what direction I should use.
The following are what I have tried so far (I've tried using all directions)
ballRigidBody.AddForce(transform.up * speed);
ballRigidBody.velocity = Vector2.up * speed;
transform.position+=Vector2.up;
In Unity3D transform.forward is the go to variable when you want to move forward.
In Unity2D transform.right is the standard.
You can move your ball to its right with this code:
void Update()
{
GetComponent<Rigidbody2D>().velocity = transform.right * speed;
}
If you run this code the ball will always move into the direction of the red arrow.
You can see the arrows by selecting the move tool on the top left and then clicking on your ball inside the editor.
If you want to have your ball to always follow the green arrow you have to use transform.up.
transform.right and transform.up are both used relative to your object.
That means by changing the z rotation of your object you can modify the direction it should go in.
You can try out the following code to see your ball move to its right and rotate slowly on its z axis. This will cause the ball to move in a circle since it is still following the red arrow.
void Update()
{
GetComponent<Rigidbody2D>().velocity = transform.right * speed;
float speedRotate = 100;
transform.Rotate(Vector3.forward * speedRotate * Time.deltaTime);
}
When you always want to move an object to the right, no matter the rotation, you use Vector3.right instead of transform.right.
From your other comments I understand that you want your ball to always face into the direction in the middle of the screen. You can just rotate it to the middle of the screen when it spawns by using this code:
void Start()
{
transform.right = new Vector3(0, 0, 0) - transform.position;
}
This will get the ball to face the 0,0,0 position inside of your world. If 0,0,0 is in the middle of your screen it will face that direction. Otherwise you have to find out which coordinate your middle of your screen has.
I'm not sure what you're asking, but you can do ballRigidbody.velocity = transform.foward*speed, to make it go in the current direction it is facing.
If you want a ball you spawn on the left side of the screen to move to the right only, then set velocity like rb.velocity = Vector3.right * speed, and so on with each direction.
In this case, you should first check which position the ball spawned at (Top, Bottom, Left, Right), then from that decide the movement direction. If the ball spawns at the bottom, the direction to go up should be Vector3.up for example.
After you get the force, you can use rigidBody.AddForce(direction * speed); to move the ball.

How to rotate around a corner when the distance is unknown

I feel like this is a solvable problem but I am just to stupid to solve it.
I want to able to move a player around a cube in third person in a 3D game. Before I worry about the corners where 3 edges of the cube meet I would first like to be able to move around two edges. If I use the LookAt method while jumping around a corner some problems occur with the vertical rotation of the player. So if he enters the trigger of the edge he turns around 180 degree somehow. That is why I am searching for a different solution.
(Green rectangle is the player. The dotted area is a trigger.)
So what I am given on the entry of the trigger is the edge position P2 and obviously the player position P3. The player should be able to stop the velocity while being mid air, so he could just fall down to the edge thanks to the gravity. That means that I can not just lerp it from the entry point to some exit point. The entry and exit points are dynamic.
What I already calculated is the distance from the player to the edge. Therefore I could tell if the player is closer to the right surface or the left surface judging by whether X or Y is higher.
But if the player enter on a higher position the numbers are also higher.
I am very confused about how to solve this. I need to lerp rotate the player around the corner P2 as long as he is within the two dotted lines.
Here's something to get you started. This will rotate your player so that their down is pointing at the edge, and their forward vector tries to remain unchanged as much as possible. This will mean that moving in playerTransform.forward will move them where they are facing but will rotate them so that as they move around the edge, they will rotate around it.
transform playerTransform;
Vector3 edgeWorldDirection;
Vector3 edgeWorldPosition;
Vector3 playerWorldPosition;
// Determine up direction
Vector3 player2Edge = edgeWorldPosition - playerWorldPosition;
Vector3 playerUpDirection = (
Vector3.Dot(player2Edge, edgeWorldDirection) * edgeWorldDirection
- player2Edge).normalized;
// rotate player to align local up with playerUpDirection and forward with
// previous forward as much as possible.
Vector3 playerRightDirection = Vector3.Cross(playerUpDirection, playerTransform.forward);
// handle player looking "up"
if (playerRightDirection == Vector3.zero) playerRightDirection = player.Transform.right;
Vector3 playerForwardDirection = Vector3.Cross(playerRightDirection, playerUpDirection);
playerTransform.rotation = Quaternion.LookRotation(playerForwardDirection, playerUpDirection);
For corners it's actually easier because your up is based on the location of the corner, you don't have to calculate the closest point on a line.
transform playerTransform;
Vector3 cornerWorldPosition;
Vector3 playerWorldPosition;
// Determine up direction
Vector3 playerUpDirection = (playerWorldPosition - cornerWorldPosition).normalized;
// rotate player to align local up with playerUpDirection and forward with
// previous forward as much as possible. Same logic here as previously.
Vector3 playerRightDirection = Vector3.Cross(playerUpDirection, playerTransform.forward);
// handle player looking "up"
if (playerRightDirection == Vector3.zero) playerRightDirection = player.Transform.right;
Vector3 playerForwardDirection = Vector3.Cross(playerRightDirection, playerUpDirection);
playerTransform.rotation = Quaternion.LookRotation(playerForwardDirection, playerUpDirection);
Depending on how you're handling movement & player orientation, you may not actually want to rotate the player's transform, but with the results of Quaternion.LookRotation(...) you can multiply the local direction the player tries to go by that rotation to get the world direction that would correspond to.:
Vector3 localDesiredMove;
Quaternion boxOrientationQuat = Quaternion.LookRotation(
playerForwardDirection,
playerUpDirection);
Vector3 worldBoxOrientedMove = boxOrientationQuat * localDesiredMove;

Arrow rotating to face cursor needs to only do so while inside an angle made by two given directions

I have a 2d arrow rotating to always face the a target (the target in this case is the cursor), the pivot is my player character. I need to restrict this arrow to only follow the target if it is inside an angle of the player, an example would be 90 degrees, so it would only follow if the cursor is in the top right part of the screen.
I have worked with vector directions and methods such as Vector2D.angle, but they all seem to have some restriction i can't workaround, Vector2D.angles restriction is that the 3rd position it uses to calculate the angle is the world center(0, 0), my player is mobile so that doesn't work.
So i think what im asking is if theres a way to store an angle, and then check if something is within that.
Here is the code i use for rotating my arrow, theres more to the script but i removed the unnecesary parts:
public float speed;
public Transform target;
void Update()
{
Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector2 direction = target.position - transform.position;
target.position = mousePosition;
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
Quaternion rotation = Quaternion.AngleAxis(angle, Vector3.forward);
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, speed * Time.deltaTime);
Sorry if this is formatted poorly, its my first time posting here, thank you a million times if you are able to help me, i have been stuck on this for days.
Was asked to clarify question so here is an attempt:
This picture shows an example of what i mean, the arrow is rotating around the center of the circle, (it is rotating so it always points towards my cursor). What i need is a way to restrict it so it only points towards the cursor if it is within a specific angle (red lines in picture), that's what im having trouble with. I can't find a way to store this threshold and i can't seem to find a way to compare the cursors direction with it.
I think it would be possible if it was possible to choose a custom center for Vector2D.angle, but that doesn't seem to be the case.
I hope this clarifies what my question, i may just very well be stupid and overlooking something obvious but i really can't find a way to make this possible.
thanks again.
Important fields/inputs
First, we need to know the boundary directions in world space:
public Vector2 boundaryDirectionLeft;
public Vector2 boundaryDirectionRight;
The important piece is that the angle made clockwise from boundaryDirectionLeft to boundaryDirectionRight is the region the arrow shall remain inside. In the case of your image, boundaryDirectionLeft could be Vector2.up and boundaryDirectionRight could be something like new Vector2(1f,-1f).
We also need to know which local direction the arrow is facing before any rotation is applied. E.g., if the arrow is always pointing with the local red arrow axis (the local right direction), this would be Vector2.right:
public Vector2 localArrowDirection;
And lastly, we need a top speed for our rotation, so we don't warp the arrow around when it's time to rotate. A good value to start with might be 360f, 360 degrees per second. Try experimenting with different values to find one that you like:
public float maxRotationSpeed;
The update procedure
In Update, determine the direction to the target:
Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector2 direction = mousePosition - transform.position;
Then, we need to know where the arrow is currently pointing. We can use Transform.TransformVector to find where the local localArrowDirection is pointing in world space:
Vector2 currentDirection = transform.TransformVector(localArrowDirection);
Then, determine the signed angle from boundaryDirectionLeft to the target direction, the same from boundaryDirectionLeft to boundaryDirectionRight, and the same from boundaryDirectionLeft to the current facing direction:
float directionAngle = Vector2.SignedAngle(boundaryDirectionLeft, direction);
float boundaryAngle = Vector2.SignedAngle(boundaryDirectionLeft, boundaryDirectionRight);
float currentAngle = Vector2.SignedAngle(boundaryDirectionLeft, currentDirection);
These values range from [-180,180], but we want them expressed in the range [0,360) to make the math easier later, so we can add 360f and use Mathf.Repeat on that sum:
directionAngle = Mathf.Repeat(directionAngle+360f, 360f);
boundaryAngle = Mathf.Repeat(boundaryAngle+360f, 360f);
currentAngle = Mathf.Repeat(currentAngle+360f, 360f);
At this point directionAngle is how many clockwise degrees from boundaryDirectionLeft that target is, and boundaryAngle is how many boundaryDirectionRight is, and currentAngle is the same for what direction we're currently facing.
So, now, we need to know how to properly clamp the angle between 0 and boundaryAngle. Anything too far above boundaryAngle is actually closer to the left boundary and should be clamped to the left boundary. In fact, since everything is between 0 and 360, anything higher than boundaryAngle+(360f-boundaryAngle)/2f is closer to the left. So, we just set anything higher than that to be 0 degrees away from boundaryDirectionLeft:
if (directionAngle > boundaryAngle + (360f - boundaryAngle)/2f)
{
directionAngle = 0f;
}
So, now we can clamp directionAngle with a high of boundaryAngle (it is already bottom clamped at 0f, so we can use Mathf.Min here):
directionAngle = Mathf.Min(directionAngle, boundaryAngle);
Now we can limit the angular difference between directionAngle and currentAngle using our maxRotationSpeed:
float deltaAngle = Mathf.Clamp(directionAngle-currentAngle,
-maxRotationSpeed * Time.deltaTime,
maxRotationSpeed * Time.deltaTime);
Now we can rotate the transform deltaAngle degrees clockwise (in world space):
transform.Rotate(0f,0f,deltaAngle,Space.World);

Categories