Unity - Rotate around object by mouse - c#

I'm hoping someone can clear this up for me, I've literally spent days on this.
I have a need to focus the camera on the front of an object and then allow the player to orbit the object while holding the mouse button. I've managed to get the camera to focus on the front of the object using this:
if (!arrived)
{
//Centre on our object
var newPos = target.transform.position + currentRotation * (distance * target.transform.forward);
transform.position = Vector3.Lerp(transform.position, newPos, 0.25f);
transform.LookAt(target.position);
return;
}
But then I'm looking for the mouse button being held and attempting to rotate around the target object, keeping a consistent distance from it.
I've tried so many different ways but all of them have issues.
I tried:
transform.RotateAround(target.position, Vector3.down, (movementForce * 100000) * Time.deltaTime);
But which works fine horizontally but the same using Vector.left doesn't seem to work vertically.
I've tried:
transform.LookAt(target.position);
Vector3 rotation = new Vector3(Input.GetAxisRaw("Mouse X"), Input.GetAxisRaw("Mouse Y"), 0);
transform.Translate(rotation * Time.deltaTime * 10);
But that moves the camera further away from the target over time.
And finally I'm not trying this:
//Horizontal
Vector3 relativePos = target.position - transform.position; // Vector from camera to player
Vector3 relativePosRight = Vector3.Cross(relativePos, Vector3.left);
transform.RotateAround(target.position, relativePosRight, 100 * Time.deltaTime * Input.GetAxisRaw("Mouse X"));
//Vertical
Vector3 relativePos = target.position - transform.position; // Vector from camera to player
Vector3 relativePosRight = Vector3.Cross(relativePos,Vector3.up);
transform.RotateAround(target.position, relativePosRight, 100 * Time.deltaTime * Input.GetAxisRaw("Mouse Y"));
Which vertically works fine, but when moving the mouse horizontally, that seems to affect it vertically as well and orbits at a weird angle.
Please forgive any obvious mistakes in the above, I've literally resorted to copying and pasting every solution I find online to get the right thing.
Thanks in advance!

What does your scene hierarchy look like? Are you able to make the camera a child of the object you want to orbit, have the camera always looking at the object and then rotate the object with the mouse?
Similar to this answer.
So something like this:
// Move the camera to the correct position/distance from the target.
Camera.main.transform.position = target.position + new Vector3(0,1,0); // 1m above the target.
Camera.main.transform.LookAt(target.position);
Camera.main.transform.SetParent(target);
// Rotate the target with the mouse input.
Vector3 rotation = new Vector3(Input.GetAxisRaw("Mouse X"), Input.GetAxisRaw("Mouse Y"), 0);
target.Translate(rotation * Time.deltaTime * 10);
It might not be exactly what you might want but it visually looks like it is orbiting the object.
Hope that helps!

Related

Is there a simple way of creating a spring-like camera rotation out of this script? (Unity C#)

This is the script im using. This is the result im looking for
ex: https://imgur.com/a/1L2wV52
void Update()
{
float mouseX = Input.GetAxisRaw("Mouse X") * Time.fixedDeltaTime * _sensitivityX;
float mouseY = Input.GetAxisRaw("Mouse Y") * Time.fixedDeltaTime * _sensitivityY;
yRotation += mouseX;
xRotation -= mouseY;
xRotation = Mathf.Clamp(xRotation, -90f, 90f);
camHolder.rotation = Quaternion.Euler(xRotation, yRotation, 0);
orientation.rotation = Quaternion.Euler(0, yRotation, 0);
}
The result doesn't have to be exact but somewhat similar, I have tried using lerp but it didn't quite work.. If you have any answers to this, I would really appreciate it, thanks!
To achieve this effect you want to calculate the target rotation from the mouse input and then increase / decrease the rotation of the camera based on it. But you can not dirrectly link the mouse movement to the camera as you did, because it will not look smooth. You have to do rotate it over multiple frames.
You also need to set a maximum delta between last frame cam angle and current frame cam angle.
If you also want to have an overshooting effect, then adding a velocity to the camera movement is the best solution in my opinion.

Player unwanted rotation on the Y axis

Any Quaternion genius could tell me what is wrong with my code ? I have this PlayerRotation script that does two things:
Rotate the player's Y axis localy
Align the player to the surface's normals
The PlayerMovement is only making the player go forward on the transform.forward axis. The problem is that when i start playing and going on some upside down surfaces after a while my player's Y axis gets offset with the joystick randomly, like tilting the joystick straight forward makes the player rotate slightly to the right
input = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
Vector2 inputDir = input.normalized;
//Angle part
if (inputDir != Vector2.zero)
{
targetAngle = Mathf.Atan2(input.x, input.y) * Mathf.Rad2Deg + cam.eulerAngles.y;
Quaternion localRotation = Quaternion.Euler(0f, targetAngle - lastTargetAngle, 0f);
transform.rotation = transform.rotation * localRotation;
lastTargetAngle = targetAngle;
}
//Raycast part
if (Physics.Raycast(transform.position - transform.up * start, -transform.up, out hit, end))
{
Quaternion targetRotation = Quaternion.FromToRotation(transform.up, hit.normal);
transform.rotation = targetRotation * transform.rotation;
}
After many ajustement a concluded that the problem seems to be coming from the raycast part but i dont understand why
My experience with Quaternions is pretty much limited to using FromToRotation and LookRotation and trial and error until I get it working, so I'm not the most knowledgeable about Quaternions but here is what I think is happening:
You are detecting the rotation between the surface normal and the up vector, and than adding that to your current rotation. The problem with this is that even when your current rotation is already aligned with the surface normal you still add that rotation, causing it to over-rotate.
What you should do is either calculate the rotation from your current upwards direction to the surface normal or do something like I did below, which is fairly easy to understand:
Use your current rotation to determine the direction you want to be looking in
lookDirection = transform.rotation * Vector3.forward
And use your targetRotation, to determine the direction you want to be up
upDirection = targetRotation * Vector3.up
And calculate your final rotation using Quaternion.LookRotation
LookRotation(Vector3 forward, Vector3 upwards)
Resulting in your raycast block looking like this:
Quaternion targetRotation = Quaternion.FromToRotation(transform.up, hit.normal);
transform.rotation = Quaternion.LookRotation(transform.rotation * Vector3.forward, targetRotation * Vector3.up);
EDIT: I am at Uni using my craptop so I haven't actually tested this yet.
I know it's an old post and I can't comment on answers yet but Rishaan's answer worked for me I just had to change:
transform.rotation = Quaternion.LookRotation(transform.rotation * Vector3.forward, targetRotation * Vector3.up);
To:
transform.rotation = Quaternion.LookRotation(targetRotation * Vector3.forward, transform.rotation * Vector3.up);

How to make LookAt align the transform.up vector instead of transform.forward?

The problem is that i cannot make a script that makes the enemy rotate so that "up" is pointing against the player, (with that i mean so that it will come to the player with transform.up) And i have not found any working solution yet, (from what i can tell)
I am trying to make a small 2D game, just for my friends to play. What i have tried is making the enemy look at the player and then set the "z" rotation to the same as the "x" rotation and after that reset the "x" and "y" rotation (so that "up" is pointing at the player). But the enemy just goes straight up.
using UnityEngine;
public class Enemy : MonoBehaviour
{
public Transform Player; // The Transform of the Player that you can controll
public float MoveSpeed = 4; // How fast the enemy should go
void FixedUpdate()
{
transform.LookAt(Player);
transform.rotation = Quaternion.Euler(transform.rotation.x, 0, transform.rotation.x);
transform.rotation = Quaternion.Euler(0, 0, transform.rotation.z);
transform.position += transform.up * MoveSpeed * Time.deltaTime;
}
}
So what i want to happen is that the Enemy will move to the Player but the rotation part is not working and it just goes straight up in the air instead of following the player. I might just be stupid and have missed something simple...
By using
transform.rotation = Quaternion.Euler(transform.rotation.x, 0, transform.rotation.x);
transform.rotation = Quaternion.Euler(0, 0, transform.rotation.z);
both times you overwrite the rotation with new values additionally mixing Euler (Vector3) with Quaternion fields ... transform.rotation.x is not the pure rotation around the X-axis you see in the Unity inspector.
It depends a bit on how exactly you want the enemy rotated in the end but you could simply use Transform.RotateAround like
transform.LookAt(Player);
transform.RotateAround(transform.position, transform.right, 90);
LookAt by default results in a rotation such that transform.forward is pointing at the target, transform.right remains pointing right(ish) and transform.up remains pointing up(ish). So all you have to do is rotating around that local X-axis transform.right exactly 90° so now transform.forward points down instead and transform.up points at the target.
which results in
If you than also want to change the other axis you can still additionally rotate it around the local transform.up axis as well e.g. to flip the forward vector "upwards":
transform.LookAt(Player);
transform.RotateAround(transform.position, transform.right, 90);
transform.RotateAround(transform.position, transform.up, 180);
Is there actually a special reason why you are doing that in FixedUpdate and not rather in Update? FixedUpdate should only be used for Physics related stuff.

How to rotate an object along all 3 axes with finger swipe?

I'm working on a simple AR Vuforia application. How can I implement the rotation of an object along all three axes with one finger swipe?
The code I'm currently using has one bug: the rotation of the object depends on its local axes. For example, if I look at the object from the front, everything works as it should, but if I look at the object from the back side, the finger swipe upwards makes it rotate downwards and vice versa.
Here is this script:
public float rotSpeed = 30f;
void OnMouseDrag()
{
float rotX = Input.GetAxis("Mouse X")*rotSpeed*Mathf.Deg2Rad;
float rotY = Input.GetAxis("Mouse Y")*rotSpeed*Mathf.Deg2Rad;
transform.Rotate(Vector3.up, -rotX);
transform.Rotate(Vector3.right, -rotY);
}
This is not what I need, how can I rotate the object according to the finger swipe direction regardless of the angle from which I look at it?
Update
A simple non-AR example, which may help you understand what I need is an iOS game "Space Frontier 2". After a successful launch of the rocket, it lands on the planet and you can rotate the planet with your finger swipe.
Here is the video demo: https://youtu.be/OiNPP1WNIAI
This works nice regardless of your object's rotation, and regardless of your camera position relative to the object:
public float rotSpeed = 30f;
void OnMouseDrag()
{
float rotX = Input.GetAxis("Mouse X") * rotSpeed;
float rotY = Input.GetAxis("Mouse Y") * rotSpeed;
Camera camera = Camera.main;
Vector3 right = Vector3.Cross(camera.transform.up, transform.position - camera.transform.position);
Vector3 up = Vector3.Cross(transform.position - camera.transform.position, right);
transform.rotation = Quaternion.AngleAxis(-rotX, up) * transform.rotation;
transform.rotation = Quaternion.AngleAxis(rotY, right) * transform.rotation;
}
Make sure your camera has the "MainCamera" tag, or assign the camera externally if necessary.
I do not have the exact code with me right now.
But if you did not update your point of view coordinates, this should be the expected result.
Consider a real ball with 2 colors, blue and red, separated vertically.
When you are in front of it, seeing only the blue side, stroking it up will make the blue side go up and the red side appear from the bottom.
Now move behind it, seeing only the red side, and stroke it up again.
The blue face will go down and appear from the bottom.
Unity applies physics to virtual objects the same way we interact with real objects.
So you need to consider your camera position with the object orientation when you apply movements to it.
You need to apply a transformation matrix to your movement based on your camera location related to the object origin orientation.
I hope this is clear enough to put you on tracks to fix it.
I think you have to somehow clamp the rotation to have the desired behaviour. I wrote a script recently to do just that. I did a little modification though.
public float rotSpeed = 30f;
float ClampAngle(float _angle, float _min, float _max)
{
if (_angle < 0f) _angle = 360 + _angle;
if (_angle > 180f) Mathf.Max(_angle, 360 + _min);
return Mathf.Min(_angle, _max);
}
USAGE:
void RotateGameObject()
{
float h = Input.GetTouch(0).deltaPosition.x * Time.deltaTime * rotSpeed*Mathf.Deg2Rad;
float v = Input.GetTouch(0).deltaPosition.y * Time.deltaTime * rotSpeed*Mathf.Deg2Rad;
Vector3 rot = transform.rotation.eulerAngles + new Vector3(-v, h, 0f);
//Change the y & z values to match your expected behaviour.
rot.x = ClampAngle(rot.x, -5f, 20f);
//Clamp rotation on the y-axis
rot.y = ClampAngle(rot.y, -20f, 20f);
transform.eulerAngles = rot;
}
See if that works and of course, try to play with the values.

Rotate GameObject on Z axis while moving forward

Currently I have the following code, and it works 100% for a 3D game, but I can't get it to work for a 2D game.
What it is supposed to do, is once the object gets to the waypoint, it is supposed to rotate towards the new waypoint. What is actually happening is that it is rotating around the x and y axis, and not the z axis. What can I do to make it only rotate on the z axis? The object is supposed to turn as it moves forward based on the turn speed. It shouldn't be an instant turn, unless the turnspeed is a high number. But anyways, again I ask how do I get this code to only roate on the z axis?
void Update () {
if(toWaypoint > -1){
Vector3 targetDir = wayPoints[toWaypoint].position - transform.position;
Vector3 newDir = Vector3.RotateTowards(transform.forward, targetDir, turnSpeed * Time.deltaTime, 0.0f);
transform.rotation = Quaternion.LookRotation(newDir);
}
if(!usePhysics && toWaypoint != -1){
transform.Translate(Vector3.forward * Time.deltaTime * speed);
next();
}
}
Look rotation is not dependable for 2D games as the forward axis is different. One workaround is to parent the sprite to an empty GameObject with the z axis (blue arrow) pointed in the direction that the sprite will be rotating.
Looks like you're using Unity3D, which leaves me wondering why you're messing around with the Z-axis in the first place if you're making a game in 2D editing mode. The Z-axis does literally nothing in that mode, unless you set your two axes to include it somehow. My suggestion to you is to switch to 2D editing if you haven't already, then use Vector2s instead of Vector3s.
Also, try using one of the other axes as your axis of rotation (transform.right or .up instead of .forward).
I found what I was looking for:
void Update () {
if(toWaypoint > -1){
Vector3 vectorToTarget = wayPoints[toWaypoint].position - transform.position;
float angle = Mathf.Atan2(vectorToTarget.y, vectorToTarget.x) * Mathf.Rad2Deg;
Quaternion q = Quaternion.AngleAxis(angle, Vector3.forward);
transform.rotation = Quaternion.Slerp(transform.rotation, q, Time.deltaTime * turnSpeed);
}
if(!usePhysics && toWaypoint != -1){
transform.Translate(Vector3.right * Time.deltaTime * speed);
next();
}
}

Categories

Resources