Rotate item: 90 degree rotations - c#

I found this script that lets you click on an item then drag to rotate it, but it rotates based on where your finger is, what can I do to rotate the object 90 degrees like you would in Tetris?
public class RotateDrag : MonoBehaviour {
void OnMouseDrag(){
Vector3 pos = Camera.main.WorldToScreenPoint(transform.position);
pos = Input.mousePosition - pos;
float ang = Mathf.Atan2(pos.y, pos.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.AngleAxis(ang, Vector3.forward);
}
}
Example
In photoshop when you want to rotate an item in a layer you can hold Shift and it will snap to 45° angles as you rotate.
I however would like 90° angle snapping.

What you need to do is just take the direction from the point selected, as the sign on the angle and apply that to a 90 degree rotation thus:
float ang = 90.0 * Math.Sign(Mathf.Atan2(pos.y, pos.x) * Mathf.Rad2Deg);
transform.rotation = Quaternion.AngleAxis(ang, Vector3.forward);

Related

Rotation off by 90 degrees unity

I am trying to rotate my 2d object around my mouse. This is the code I have:
void Update()
{
Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector3 direction = mousePosition - transform.position;
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0, 0, angle);
// Debug.Log(angle);
}
My object, which is an arrow, is by default pointing down, so before I start the script I set its z rotation to 90, so it faces right. If I delete the transform.rotation line, the angle shown will be right, when my cursor is above it says 90, in the left it says 180 etc. So my question is: Why do I need to add 90 degrees to angle to make this actually work? Why doesn't this version work?
Mathf.Atan2(Also see Wikipedia - Atan2)
Return value is the angle between the x-axis [=Vector3.right] and a 2D vector starting at zero and terminating at (x,y).
It would work as expected if your arrow by default would point to the right but yours is
by default pointing down
you could simply add the offset rotation on top like
transform.rotation = Quaternion.Euler(0, 0, angle + 90);
Alternatively if you need this for multiple objects with different offsets either use a configurable field like
[SerializeField] private float angleOffset;
...
transform.rotation = Quaternion.Euler(0, 0, angle + angleOffset);
Or you could rotate it manually to face correctly to the right before starting the app and store that default offset rotation like
private Quaternion defaultRotation;
private void Awake ()
{
defaultRotation = transform.rotation;
}
and then do
transform.rotation = defaultRotation * Quaternion.Euler(0, 0, angle);

Prevent player rotation from changing last second in Unity

Unity using C#
In an top down game where the camera rotation is locked, I have made a character controller which is restricted to 8 axis and will only rotate in 45 degree increments. Think Links awakening. Everything works except when moving in a diagonal direction(using WASD).
When I let go of W & D for example, whichever key I let go of last even if by a fraction of a second, the player will end up facing in that direction rather than the intended up and to the right diagonal direction.
I am looking to modify the following code to add some sort of buffer so that the player remains facing the intended direction when I let go of the 2 keys. I'd like to keep it as simple as possible.
public float defaultSpeed = 6f;
CharacterController controller;
private Vector3 playerMovement;
Vector2 input;
float angle;
Quaternion targetRotation;
Transform cam;
// Start is called before the first frame update
void Start()
{
controller = GetComponent<CharacterController>();
cam = Camera.main.transform;
}
// Update is called once per frame
void Update()
{
Movement();
}
public void Movement()
{
//Get inputs
input.x = Input.GetAxisRaw("Horizontal");
input.y = Input.GetAxisRaw("Vertical");
//Only update if keys are pressed
if (Mathf.Abs(input.x) < deadZone && Mathf.Abs(input.y) < deadZone) return;
//Get camera angle: input to radians - radians to degrees - degrees plus the camera angle - limit angle to 45 degree increments
angle = Mathf.Atan2(input.x, input.y);
angle = Mathf.Rad2Deg * angle;
angle += cam.eulerAngles.y;
angle = Mathf.Round(angle / 45) * 45;
//Rotate Character
targetRotation = Quaternion.Euler(0, angle, 0 );
transform.rotation = targetRotation;
//Move Character
transform.position += transform.forward * defaultSpeed * Time.deltaTime;
}
Make a courotine that checks if a key is released, and waits, say 0.5 seconds for another key to be released. If another key is released, then stay in that direction. Hope it works. Good luck.

Quaternion.Slerp on X and Z axis without Y axis

I am trying to rotate the Player about X, Y, and Z axis. The Y axis should not move from last angle. Example, if I rotate 45 degree's to the left, the player should not rotate back to 0. The players X and Z axis rotate a maximum of 30 degrees, then when Input is no longer in use, settle to 0.
Through trial and error, I have finally gotten my Y angle to not Slerp back to 0. However, X and Z, still consider Y to be 0 degree's. The player is rotated (assume 45 degree's to the left), but movement along X and Z is as if Y is 0 degree's.
I've been reading articles and threads, and watching video's across multiple domains, including but not limited StackOverflow, Unity forums, Unity API, and YouTube video's.
Video of Current Game - notice the engine exhaust - X and Z never change to the new normal of the Camera view / Player Y direction.
void Update()
{
if(!controller.isGrounded)
{
//Three degree's
moveDirection = new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Thrust"), Input.GetAxis("Vertical"));
moveDirection *= speed;
//rotate around Y-Axis
transform.Rotate(0, Input.GetAxis("Yaw") * rotationSpeed, 0);
float currentY = transform.eulerAngles.y; //save Y for later
//rotation around X and Z
float tiltAroundX = Input.GetAxis("Vertical") * tiltAngle;
float tiltAroundZ = -1 * (Input.GetAxis("Horizontal") * tiltAngle);
Quaternion targetRotation = Quaternion.Euler(tiltAroundX, currentY, tiltAroundZ);
Vector3 finalRotation = Quaternion.Slerp(transform.rotation, targetRotation, smooth).eulerAngles;
finalRotation.y = currentY; //reintroduce Y
transform.rotation = Quaternion.Euler(finalRotation);
controller.Move(moveDirection * Time.deltaTime);
}
After further research that lead me along different avenues, I discovered that there were two issues. Both issue's revolved around the fact that the Z-axis was never being normalized to the new Y-axis degree after rotation. #Ruzihm, solved the issue of Rotation. I solved the then visible issue of movement. Which became readily visible once rotation was working properly.
In essence, the Z-axis (transform.forward) must be recalculated after any change in the Y-axis rotation (Vector3.up). Once you have the new normal (transform.forward), the movement vector needed to flattened to the plane to keep the player from diving into the surface of the world. Thank you #Ruzihm for all your assistance.
Here is the new code:
//Three degree's
moveDirection = new Vector3(Input.GetAxis("Horizontal"),
Input.GetAxis("Thrust"),
Input.GetAxis("Vertical"));
//Normalize the movement direction and flatten the Plane
moveDirection = transform.TransformDirection(moveDirection);
moveDirection = Vector3.ProjectOnPlane(moveDirection, Vector3.up);
moveDirection *= speed;
// collect inputs
float yaw = Input.GetAxis("Yaw") * rotationSpeed;
float pitch = Input.GetAxis("Vertical") * tiltAngle;
float roll = -1 * (Input.GetAxis("Horizontal") * tiltAngle);
// Get current forward direction projected to plane normal to up (horizontal plane)
Vector3 forwardCurrent = transform.forward
- Vector3.Dot(transform.forward, Vector3.up) * Vector3.up;
// Debug to view forwardCurrent
Debug.DrawRay(transform.position, forwardCurrent * 2, Color.white);
// create rotation based on forward
Quaternion targetRotation = Quaternion.LookRotation(forwardCurrent);
// rotate based on yaw, then pitch, then roll.
// This order prevents changes to the projected forward direction
targetRotation = targetRotation * Quaternion.AngleAxis(yaw, Vector3.up);
// Debug to see forward after applying yaw
Debug.DrawRay(transform.position, targetRotation * Vector3.forward, Color.red);
targetRotation = targetRotation * Quaternion.AngleAxis(pitch, Vector3.right);
targetRotation = targetRotation * Quaternion.AngleAxis(roll, Vector3.forward);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, smooth);
controller.Move(moveDirection * Time.deltaTime);
There seem to be some incorrect assumptions about the order of rotations that apply when working with Euler angles. Roll is applied, then pitch, then finally yaw. This means that keeping the same yaw then setting the roll and pitch to zero (or even just changing roll) can completely change the flattened direction you're facing.
It may help to rotate by yaw, flatten the forward direction (aka project it to a completely horizontal plane) Then create a rotation based off that (using Quaternion.LookRotation) which you can then rotate by each axis manually.
if(!controller.isGrounded)
{
//Three degree's
moveDirection = new Vector3(Input.GetAxis("Horizontal"),
Input.GetAxis("Thrust"),
Input.GetAxis("Vertical"));
moveDirection *= speed;
// collect inputs
float yaw = Input.GetAxis("Yaw") * rotationSpeed;
float pitch = Input.GetAxis("Vertical") * tiltAngle;
float roll = -1 * (Input.GetAxis("Horizontal") * tiltAngle);
// Get current forward direction projected to plane normal to up (horizontal plane)
Vector3 forwardCurrent = transform.forward
- Vector3.Dot(transform.forward,Vector3.up) * Vector3.up;
// Debug to view forwardCurrent
Debug.DrawRay(transform.location, forwardCurrent, Color.white, 0f, false);
// create rotation based on forward
Quaternion targetRotation = Quaternion.LookRotation(forwardCurrent);
// rotate based on yaw, then pitch, then roll.
// This order prevents changes to the projected forward direction
targetRotation = targetRotation * Quaternion.AngleAxis(yaw, Vector3.up);
// Debug to see forward after applying yaw
Debug.DrawRay(transform.location, targetRotation * Vector3.forward, Color.red, 0f, false);
targetRotation = targetRotation * Quaternion.AngleAxis(pitch, Vector3.right);
targetRotation = targetRotation * Quaternion.AngleAxis(roll, Vector3.forward);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, smooth);
//debug new forward/up
Debug.DrawRay(transform.location, Transform.forward, Color.blue, 0f, false);
Debug.DrawRay(transform.location, Transform.up, Color.green, 0f, false);
controller.Move(moveDirection * Time.deltaTime);
}
This may be considered a partial answer because being able to determine a "flattened forward" direction and reorder the process of applying component rotations is useful to answering your question but may not be enough to get the full effect you want depending on the details.
As a sidenote, you may want to consider using Quaternion.RotateTowards instead of Quaternion.Slerp if you want to ensure that it will actually reach the target rotation instead of infinitely approach it.

How can i move a cylinder right and left?

Between two points or only to the left nonestop or only to the right nonestop.
In this code i spin the cylinder but i can't move it to the sides:
using UnityEngine;
using System.Collections;
public class MakeTwoPoints3D : MonoBehaviour
{
public float speed = 10f;
public float delta = 15.5f; // Amount to move left and right from the start point
public float moveSpeed = 5.0f;
private Vector3 startPos;
void Start()
{
startPos = transform.position;
}
void Update()
{
transform.Rotate(Vector3.up, speed * Time.deltaTime);
transform.position += transform.right * Time.deltaTime * moveSpeed;
}
}
If i make transform.right it will move the cylinder in circle on place up and down in circle. If i make transform.up it will move it to me i mean like forward but to the camera but at least it will move it. And if i make transform.Forward again it will make circles and will remove the cylinder in circles up down.
I can't figure out how to move it to the sides.
You need use Vector3.right instead of transform.right.
void Update()
{
transform.Rotate(Vector3.up, speed * Time.deltaTime);
transform.position += Vector3.right * Time.deltaTime * moveSpeed;
}
When you use transform.right, the Vector3 will adopt the local rotations of that object's transform. Meaning, if the object was rotated 45 degrees around the Y axis, your transform.right vector would be on an angle. If you keep translating an object along it's local axis while you rotate it, it will travel in a circle.
On the other hand, Vector3.right is always in world space so it will always face "true" right.

Rotate cameran around a gameobject in unity3d

I want to rotate camera around an fbx object when a key is being pressed using unity 3d.How it do? I tried some examples but its not working. First i create a game object and add main camera child of it.
public class CameraOrbit : MonoBehaviour
{
public Transform target;
public float speed = 1f;
private float distance;
private float currentAngle = 0;
void Start()
{
distance = (new Vector3(transform.position.x, 0, transform.position.z)).magnitude;
}
void Update()
{
currentAngle += Input.GetAxis("Horizontal") * speed * Time.deltaTime;
Quaternion q = Quaternion.Euler(0, currentAngle, 0);
Vector3 direction = q * Vector3.forward;
transform.position = target.position - direction * distance + new Vector3(0, transform.position.y, 0);
transform.LookAt(target.position);
}
}
I dont have access to unity at the moment so i might have messed something up.
The idea is keep an angle that you change based on input. Create a Quaternion from the angle (the Quaternion say how to rotate a vector to a certain direction), then rotate a Vector to that direction. Starting from the targets position move in that direction a certain distance and then look at the targets position.
This only implements rotation around the y axis, if you want rotation around the x axis all you need is another angle variable and then change to this Quaternion.Euler(currentAngleX, currentAngleY, 0);

Categories