Player unwanted rotation on the Y axis - c#

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

Related

Unity 3D - Moving an Object around a circle circumference facing forward

I am using Unity3D to move the player around the circumference of a large circle facing inwards towards the centre at all times.
The following code works by using transform.forward
// Rotate the forward vector towards the target direction
Vector3 newDirection = Vector3.RotateTowards(transform.forward, targetDirection, singleStep, 0.0f);
and then I move the player sideways by using
characterController.Move(horizontalSpeed * transform.right * Time.deltaTime);
I have been stuck on how to get the player to move around the circumference but facing forward with the left of the player facing the centre of the circle.
My code is as follows
void Start()
{
characterController = GetComponent<CharacterController>();
}
void Update()
{
// Determine which direction to rotate towards
Vector3 targetDirection = GameObject.Find("Platforms").transform.position - transform.position;
// The step size is equal to speed times frame time.
float singleStep = 1 * Time.deltaTime;
// Rotate the forward vector towards the target direction by one step
Vector3 newDirection = Vector3.RotateTowards(transform.forward, targetDirection, singleStep, 0.0f);
// Draw a ray pointing at our target in
Debug.DrawRay(transform.position, newDirection*50, Color.red);
// Calculate a rotation a step closer to the target and applies rotation to this object
transform.rotation = Quaternion.LookRotation(newDirection);
CheckIfOnGround();
characterController.Move(velocity * Time.deltaTime);
}
Replace transform.forward to transform.left

Unity - Rotate around object by mouse

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!

How to add avoidance and chase behavior in Unity3D

I would like to implement the type of behavior where a group of prey is evading from a predator like this Game
I tried to write the script this way but I don't get the desired motion the prey just moves forward.
public Transform target;
public float damping;
public float drivespeed;
void Update () {
transform.Translate(Vector3.forward * Time.deltaTime * -drivespeed);
Quaternion rotation = Quaternion
.LookRotation(target.position - transform.position);
transform.rotation = Quaternion
.Slerp(transform.rotation, rotation, Time.deltaTime * damping);
}
Looks like you want
transform.Translate(transform.forward * Time.deltaTime * -drivespeed);
instead of
transform.Translate(Vector3.forward * Time.deltaTime * -drivespeed);
(Vector3.forward being a world forward vector, not the object's forward vector.)
It could be that the transform.translate is being called before the rotation logic but it's hard to tell what's going wrong here.
Is the object rotating correctly?
If so is the transform moving forward ignoring the rotation?
Are drivespeed and damping variables != 0?

Unity - Sprite Rotation + Get Angle

Im trying to get a sprite rotation by key input (arrow down or up).
The point is lifting the arrow (sprite) to choose the angle. Its like a system of a golf game, actually.
So far i tried:
void Update () {
if (Input.GetKey(KeyCode.UpArrow)){
transform.Rotate (Vector3.forward * -2); }
if (Input.GetKey(KeyCode.DownArrow)){
transform.Rotate (Vector3.forward * +2); }
}
I will need the angle, since it will be related to a "shot" part i will be doing next. My point is setting the right angle with the keys up and down.
I can move the "arrow" sprite with my code, but i cannot set the max angle (90), minimum (0) and get the angle to use in the shot ^^
Hard question to answer without just simply giving you the code. This code works by assuming your character's forward vector is actually it's right vector (common in 2d sprite games) in order to shoot in the other direction, rotate your objects y axis 180.
float minRotation = 0f;
float maxRotation = 90f;
float rotationSpeed = 40f; //degrees per second
//get current rotation, seeing as you're making a sprite game
// i'm assuming camera facing forward along positive z axis
Vector3 currentEuler = transform.rotation.eulerAngles;
float rotation = currentEuler.z;
//increment rotation via inputs
if (Input.GetKey(KeyCode.UpArrow)){
rotation += rotationSpeed * Time.deltaTime;
}
else if (Input.GetKey(KeyCode.DownArrow)){
rotation -= rotationSpeed * Time.deltaTime;
}
//clamp rotation to your min/max
rotation = Mathf.Clamp(rotation, minRotation, maxRotation );
//set rotation back onto transform
transform.rotation = Quaternion.Euler( new Vector3(currentEuler.x, currentEuler.y, rotation));
If you were making a golf game, you'd set the ball's velocity to be transform.right * shotPower

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