- Home /
2D Rotation Lerp Back To 0 Degrees, Please Help
Hi all, I'm working on making a 2D platformer game that has a shooting element. What I have is a script that gets the touch input and rotates the gun towards the touch location.
What I need to do is have it always try to move back to 0 degrees gradually. I've tried a Lerp but I couldn't get it to work correctly.
Here is what I have:
for (var touch: Touch in Input.touches) {
mouse_pos = Input.mousePosition;
if (touch.position.x > Screen.width / 2){
mouse_pos = Input.touches[0].position;
mouse_pos.z = 5.23; //The distance between the camera and object
object_pos = Camera.main.WorldToScreenPoint(target.position);
mouse_pos.x = mouse_pos.x - object_pos.x;
mouse_pos.y = mouse_pos.y - object_pos.y;
angle = Mathf.Atan2(mouse_pos.y, mouse_pos.x) * Mathf.Rad2Deg;
angle = Mathf.Clamp(angle, -22, 40);
transform.rotation = Quaternion.Euler(Vector3(0, 0, angle));
}
}
So if you know how I might achieve this I'd greatly appreciate all suggestions or solutions, thank you very much!
Alright so I've got it half working. But now whenever I try to shoot below 0 degrees, it does a full 360 spin around to get back to 0 ins$$anonymous$$d of going back the way it came.
Here's what I have now, any sugestions?:
for (var touch: Touch in Input.touches) {
if (touch.position.x > Screen.width / 2){
mouse_pos = Input.touches[0].position;
mouse_pos = Input.mousePosition;
mouse_pos.z = 5.23; //The distance between the camera and object
object_pos = Camera.main.WorldToScreenPoint(target.position);
mouse_pos.x = mouse_pos.x - object_pos.x;
mouse_pos.y = mouse_pos.y - object_pos.y;
angle = $$anonymous$$athf.Atan2(mouse_pos.y, mouse_pos.x) * $$anonymous$$athf.Rad2Deg;
angle = $$anonymous$$athf.Clamp(angle, -22, 40);
transform.rotation = Quaternion.Euler(0f, 0f, $$anonymous$$athf.Lerp(transform.rotation.eulerAngles.z, angle, Time.deltaTime * 80f));
}
}
if (angle > 0){
angle = 0;
}
if (angle < 0){
angle = 0;
}
transform.rotation = Quaternion.Euler(0f, 0f, $$anonymous$$athf.Lerp(transform.rotation.eulerAngles.z, angle, Time.deltaTime * 6f));
}
Answer by ZefanS · Jan 13, 2016 at 08:42 AM
Basically, the gun rotates back the almost-360-degree way because it's lerping the angle positively only. What you need to do is check where the player is aiming and then pick the closest direction to rotate back to 0 degrees.
If the player is aiming in quadrants 1 or 2, then you can lerp back to 0 the way you already are.
If the player is aiming in quadrants 3 or 4, then you want to rotate the other way (just multiply Time.deltaTime by -6f instead of 6f).
A simple way to check which quadrant the the player is aiming in is to subtract the x and y coordinates of the touch point from the x and y coordinates of the player's location. The signs of the results will indicate the quadrant:
Both positive: 1
Negative x, positive y: 2
Both negative: 3
Positive x, negative y: 4
As a side note, this code always makes angle zero, so it's a bit redundant:
if (angle > 0){
angle = 0;
}
if (angle < 0){
angle = 0;
}
You could just replace it with:
angle = 0;
Right now it looks like you lerp back towards zero whether or not you are detecting any touches. It seems to me like you should add some logic to check and only lerp towards zero when the user isn't touching the screen.
Okay, so here's an implementation that differs slightly from the description above by using Transform.RotateAround() to achieve the rotation and the cross product to calculate which direction to rotate.
#pragma strict
public var mainCamera : Camera;
public var playerTransform : Transform;
//The default position relative to the player
public var defaultPosition : Vector3;
public var rotationSpeed : float;
public var threshold : float;
private var mouse_pos : Vector3;
private var object_pos : Vector3;
private var angle : float;
private var dir : int;
private var xVector : Vector3 = Vector3(1.0f, 0.0f, 0.0f);
private var zVector : Vector3 = Vector3(0.0f, 0.0f, 1.0f);
function Update()
{
Rotate();
}
function Rotate()
{
//Calculate the position of the rotating object relative to the player
object_pos = transform.position;
object_pos.x = object_pos.x - playerTransform.position.x;
object_pos.y = object_pos.y - playerTransform.position.y;
object_pos.z = 0.0f;
if (Input.GetMouseButton(0))
{
//Calculate the position of the mouse cursor relative to the player
mouse_pos = Input.mousePosition;
mouse_pos = mainCamera.ScreenToWorldPoint(mouse_pos);
mouse_pos.x = mouse_pos.x - playerTransform.position.x;
mouse_pos.y = mouse_pos.y - playerTransform.position.y;
mouse_pos.z = 0.0f;
//Calculate the rotation direction and perform the rotation
dir = CalculateRotationDirection(object_pos, mouse_pos);
transform.RotateAround(playerTransform.position, zVector, Time.deltaTime * rotationSpeed * dir);
}
else
{
//Calculate the rotation direction and rotate towards the default position
dir = CalculateRotationDirection(object_pos, xVector);
if (dir != 0)
{
transform.RotateAround(playerTransform.position, zVector, Time.deltaTime * rotationSpeed * dir);
}
else
{
transform.position = playerTransform.position + defaultPosition;
}
}
}
function CalculateRotationDirection(currentPoint : Vector3, targetPoint : Vector3)
{
//Initialize some vectors
var currentVector : Vector3 = Vector3.zero;
var targetVector : Vector3 = Vector3.zero;
//Handle scenarios where the cross product is equal to zero.
if (Vector3.Angle(currentPoint, targetPoint) < threshold && Vector3.Angle(currentPoint, targetPoint) > -threshold)
{
return 0;
}
else if (Vector3.Angle(currentPoint, targetPoint) == 180)
{
return 1;
}
//Calculate the cross product - this tells us the relation between the vectors
var cross = Vector3.Cross(currentPoint, targetPoint);
//Return the direction multiplier based on the cross product
if (cross.z > 0.0f)
{
return 1;
}
else
{
return -1;
}
}
This should now work using sprites in 2D with an arbitrary player position.
Hope this help!
To follow up, I'm not sure if this technique would work with $$anonymous$$athf.lerp. It might make sense for you to look into Transform.Rotate() or Transform.RotateAround(), which is how I would normally go about such a problem.
Thank you for the comment, I really appreciate it!
I understand what you're saying conceptually, but I'm not entirely sure how to go about it code-wise.
Could you expand on this for me please?
It's definitely more complicated than I thought at first. Note this script makes some assumptions that will only work with the test setup I described, so it will need to be modified to handle a moving player.
Thanks, that's alright, no wonder I got stuck then if you had a hard time with it. And the player can't exactly move except for jumping since the background will be scrolling.
I have considered using LerpAngle with my previous script, but can't work out how to do it, this is what I tried earlier:
function Update ()
{
for (var touch: Touch in Input.touches) {
mouse_pos = Input.mousePosition;
if (touch.position.x > Screen.width / 2){
mouse_pos = Input.touches[0].position;
//mouse_pos.z = 5.23; //The distance between the camera and object
object_pos = Camera.main.WorldToScreenPoint(target.position);
angle = $$anonymous$$athf.Atan2(mouse_pos.y, mouse_pos.x) * $$anonymous$$athf.Rad2Deg;
angle = $$anonymous$$athf.Clamp(angle, -22, 40);
angle = $$anonymous$$athf.LerpAngle(-22, 40, Time.deltaTime);
transform.eulerAngles = Vector3(0, 0, angle);
//transform.rotation = Quaternion.Euler(0f, 0f, $$anonymous$$athf.Lerp(transform.rotation.eulerAngles.z, angle, Time.deltaTime * 80f));
//transform.rotation = Quaternion.Euler(Vector3(0, 0, angle));
}
}
angle = $$anonymous$$athf.LerpAngle(0, 0, Time.deltaTime);
transform.eulerAngles = Vector3(0, 0, angle);
}
It doesn't move at all right now for some reason.
That probably explains the problem. I'm working on a fix now.
Alright thanks, again I'm sorry this is so hard, I'm amazed there isn't some simple solution to it.
If there is I'm blind.
Answer by hulahoolgames · Jan 13, 2016 at 08:31 AM
I think the problem might be in using Mathf.Lerp to lerp the angle. Say the current z value is 270 deg and you want it to go to 0 deg. Mathf.Lerp will do a linear interpolation from 270 to 0 and will return all the intermediate values. It does not know you are interpolating on angles. So instead try the following:
Quaternion defaultRot = Quaternion.Euler(0.0f, 0.0f, 0.0f);
transform.rotation = Quaternion.Lerp(transform.rotation, defaultRot, Time.deltaTime * 6f);
I would suggest saving the default rotation as a quaternion and Lerp from current rotation to default using Quaternion.Lerp. Hope this fixes the issue.
Hey thanks a lot for the suggestion!
So if I understand you correctly, you're saying to remove my if (angle < 0) stuff and add the defaultRot Quaternion ins$$anonymous$$d?
I dont know what was the intention of the if(angle < 0) check, since you anyways enforce the angle to be 0. You need to change transform.rotation = Quaternion.Euler(0f, 0f, $$anonymous$$athf.Lerp(transform.rotation.eulerAngles.z, angle, Time.deltaTime * 6f));
to transform.rotation = Quaternion.Lerp(transform.rotation, m_targetRot, Time.deltaTime * 6f);
. Now m_targetRot can be defaultRot that i have shown in the example or any target rotation in Quaternion form you want to move towards. Just make sure you represent the target as a Quaternion and use Quaternion.Lerp to move towards the target.
Just to be more clear here is what i am suggesting. Pardon me for using C#, I am not very good with Javascript:`for (var touch: Touch in Input.touches) {mouse_pos = Input.touches[0].position;mouse_pos.x = mouse_pos.x - object_pos.x;Quaternion target =Quaternion.Euler(0.0f, 0.0f, angle);transform.rotation = Quaternion.Lerp(transform.rotation, target, Time.deltaTime 80f);}}Quaternion default = Quaternion.Euler(0.0f, 0.0f, 0.0f);transform.rotation = Quaternion.Lerp(transform.rotation, default, Time.deltaTime 6f);}`
Your answer
Follow this Question
Related Questions
Get touch position? 0 Answers
Rotate player to LookAt a touch position 2 Answers
How to rotate object 180 degrees smoothly 2 Answers
How can ı rotate camera with touch? 5 Answers