Move toward transform.forward, ignoring rotation - c#

This code is attached to a camera:
transform.position += Input.GetAxis("Vertical") * transform.forward * mod;
transform.position += Input.GetAxis("Horizontal") * transform.right * mod;
I've used .forward and .right because the player is able to rotate the camera on the Y axiz, thus no matter which way the camera is facing the forward key will always move the camera relatively forward, the left key will move it relatively left and so on. Just to clarify - I want it to move in local space, not world space so forward is the direction it's facing, not necessarily Vector3(0,0,1).
The camera is free roaming and has no parent or target to follow.
The problem is the camera has an X rotation of 45 to look down. Using transform.forward will thus modify it's height. There is also some scroll code to change the cameras height so I don't want to set the hight to a fixed value. How can I limit the movement to X and Z only, preferably without parenting it it an empty?
Update: this code works but it's not pretty, I'm sure there is a more elegant solution using Vector3.Project, as xyLe_ is suggesting. If I figure it out I'll add to his answer.
mod = Time.deltaTime * 30;
float yPos = transform.position.y;
pos = transform.position;
pos += Input.GetAxis("Vertical") * transform.forward * mod;
pos.y = yPos;
pos += Input.GetAxis("Horizontal") * transform.right * mod;
pos += Input.GetAxis("Mouse ScrollWheel") * transform.up * mod * -30;

I might talk nonsense right now, but it seems pretty simple to me:
Vector3 desiredDirection = Vector3.Normalize(new Vector3(transform.forward.x, transform.position.y, transform.forward.z))
Just project transform.forward onto the x-z plane that is settled at the height of transform.position.y. Doing this (usually) results in a slightly shorter vector, so you have to renormalize it.
To visualize:
If I understand you correctly, transform.forward corresponds to the black vector and you want to have the green vector. By setting the y component of the black vector to the y component of the red dots positions y component, you get the purple vector, which has to be renormalized.

Even though this has been solved, the solution below should help other people having this problem.
Here is is a proper way to move toward transform.forward while ignoring its rotation:
1.Get the transform.forward.
2.Convert it to Local Space with InverseTransformDirection
3.Set the y axis to 0;
4.Normalize it with dir.Normalize();
5.Append the new pos to transform.position.
public float mod = 2;
void Update()
{
//Get Forward face
Vector3 dir = transform.forward;
//Convert to local Space
dir = transform.InverseTransformDirection(dir);
//Reset/Ignore y axis
dir.y = 0;
dir.Normalize();
//Append the new pos to the `transform.position`.
transform.position += dir * Input.GetAxis("Vertical") * mod;
transform.position += Input.GetAxis("Horizontal") * transform.right * mod;
}
Another Method(Not Recommended):
Since the camera is tilted to 45 angle:
1.Rotate it to -45
2.Move the camera normally
3.Rotate it back to 45
public float startingOffset = 45;
public float mod = 2;
// Update is called once per frame
void Update()
{
//Rotate it to -45
transform.Rotate(new Vector3(-startingOffset, 0, 0));
// Move the camera normally
transform.position += Input.GetAxis("Vertical") * transform.forward * mod;
transform.position += Input.GetAxis("Horizontal") * transform.right * mod;
//Rotate it back to 45
transform.Rotate(new Vector3(startingOffset, 0, 0));
}
Both these should work but the preferred way should be the first one.

Related

How can I rotate a steering wheel to its original rotation in Unity3D?

I have been searching for an answer for hours, and I cant find the solution. I have a script that rotates a steering wheel when the car is turning. It has maximum rotation of 120 and a minimum rotation of -120. I have been trying to rotate with RotateTowards and other Quaternion methods but I can't figure them out. How can I make it when the turning angle is not zero and turning keys (a and d) are not pressed, it goes to its original rotation of 0, 0, 0, at a certain speed?
Here's my script for the steering wheel:
if (horizontalInput > 0)
{
wheel.transform.Rotate(Vector3.forward*Time.deltaTime*wheelspeed);
rotations -= wheelspeed;
}
else if (horizontalInput < 0)
{
wheel.transform.Rotate(-Vector3.forward * Time.deltaTime * wheelspeed);
rotations += wheelspeed;
}
And here is my (very bad) script for the minimum and maximum rotation of the steering wheel:
angle += Input.GetAxis("Horizontal") * Time.deltaTime*10;
angle = Mathf.Clamp(-120, angle, 120);
wheel.transform.localRotation = Quaternion.AngleAxis(angle, Vector3.forward);
angle += Input.GetAxis("Horizontal") * Time.deltaTime * 400;
angle = Mathf.Clamp(120, 0, angle);
wheel.transform.localRotation = Quaternion.AngleAxis(angle, Vector3.forward);
You should do something like this for the steering wheel script:
float rotateBack = 0f;
float angle = 0f;
void Update(){
angle += Input.GetAxis(“Horizontal”) * Time.deltaTime * 10;
angle = Mathf.Clamp(angle, -120, 120);
if (Input.GetAxis(“Horizontal”) == 0 && angle != 0f){
angle += rotateBack * Time.deltaTime;
if (angle > 0){
rotateBack = -10f;
}
else{
rotateBack = 10f;
}
}
transform.rotation = Quaternion.Euler(0f, 0f, angle);
}
What this does is setting a variable of angle to the input times a value to increase turning speed. Then it clamps if so it doesn’t go over 120, or under -120. Then, if there is no input and the angle isn’t zero: it will change the variable by how much to rotate back. The rotate back variable is set to either positive or negative based on which direction the steering wheel needs to be turned. The only problem with this script that I could see is the angle not being exactly 0, continuing the if statement. If this error affects your game, use the Mathf.Round(); function.
(If there are any errors, comment on this post.)
I would do what #ken is doing, but refactor the speed factors and other constants into fields so they are more easily changed and also use Mathf.MoveTowardsAngle to move the angle back to its neutral position.
[SerializeField] float rotateBackSpeed = 3f; // degrees per second
[SerializeField] float rotateSpeed = 10f; // degrees per second
[SerializeField] float angle = 0f; // degrees
[SerializeField] float minAngle = -120f; // degrees
[SerializeField] float maxAngle = 120f; // degrees
[SerializeField] float neutralAngle = 0f; // degrees
void Update()
{
angle = Mathf.Clamp(angle + Input.GetAxis(“Horizontal”) * rotateSpeed
* Time.deltaTime, minAngle, maxAngle);
if (Mathf.Approximately(0f, Input.GetAxis(“Horizontal”)))
{
angle = Mathf.MoveTowardsAngle(angle, neutralAngle,
rotateBackSpeed * Time.deltaTime);
}
transform.eulerAngles = angle * Vector3.forward;
}

Unity: Camera is jumpy/jerky when using touch grab to move rotation

Using Unity, I'm trying to move my turrets rotation using first person camera by using touch, I grab from side of screen to center from the direction i want to go to like in google maps, the touch grab will move my camera there smoothly.
Problem is the camera being very jerky, not smooth in position.
In Global
private float smoothTouchFactor = 5;
This is the code I am using in Update()
Quaternion rotation;
if (Input.touchCount > 0)
{
if (Input.GetTouch(0).phase == TouchPhase.Moved)
{
Touch touch = Input.GetTouch(0);
x += touch.deltaPosition.x * xSpeed * 0.02f;
y -= touch.deltaPosition.y * ySpeed * 0.02f;
rotation = Quaternion.Euler(y, x, 0f);
turretHeadToMove.rotation = Quaternion.Slerp(turretHeadToMove.rotation, rotation, Time.deltaTime * smoothTouchFactor);
}
}
rotation = Quaternion.Euler(y, x, 0f);
turretHeadToMove.rotation = Quaternion.Slerp(turretHeadToMove.rotation, rotation, Time.deltaTime * smoothTouchFactor);
If I dont add the last 2 lines after the if(touchCount) then the camera goes back to initial position after i lift my finger off the screen.
Here is the mouse axis Mouse X/Y code that works and moves the turret with no jerky motion, smoothly.
private float xSpeed = 40.0f;
private float ySpeed = 40.0f;
in Update()
x += Input.GetAxis("Mouse X") * xSpeed * 0.02f;
y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;
Quaternion rotation = Quaternion.Euler(y, x, 0f);
turretHeadToMove.rotation = rotation;
From your description of needing those "two lines" at the end, it sounds like you have code elsewhere that's constantly trying to reset their rotation. So, we add a variable to keep rotation consistent with the Quaternion set the last time the touch control was done, and just be sure to assign it to turretHeadToMove.rotation before handling the touch controls.
Then, you can use Euler to create a Quaternion of how much you want to rotate this frame. And then multiply the current rotation by that amount in order to get the new rotation
Altogether this might look like:
As a field:
private Quaternion savedRotation = Quaternion.identity;
In Update:
// Add it before the block so that modifications to `localRotation` are consistent with memory
turretHeadToMove.rotation = savedRotation;
if (Input.touchCount > 0)
{
if (Input.GetTouch(0).phase == TouchPhase.Moved)
{
Touch touch = Input.GetTouch(0);
Quaternion previousRotation = turretHeadToMove.rotation;
Quaternion rotationThisFrame = Quaternion.Euler(
touch.deltaPosition.y * ySpeed,
touch.deltaPosition.x * xSpeed, 0f);
turretHeadToMove.rotation = previousRotation * rotationThisFrame;
savedRotation = turretHeadToMove.rotation;
}
}

Unity3D - Third Person Player movement in diagonal direction is slower than vertical and horizontal movement?

I'm trying to create a character controller that allows the movement speed to be affected by how far the player is pressing the left stick. When I can get that much to work, the other issue I encounter is the players speed decreasing while moving diagonally.
Most of the information I've found online deal with the opposite issue where diagonal movement is faster (movement vector goes above 1). The solution there would be to clamp the magnitude so pressing the stick into a corner doesn't go above 1. I've tried clamping to see if that would work, however, I don't believe that's the solution to my problem.
The code here allows the player to move in relation to the camera and the speed is influenced by input vector.
Vector3 input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
Vector3 inputDir = input.normalized;
void Move(Vector3 inputDir, Vector3 input)
{
running = Input.GetKey(KeyCode.Joystick1Button1); // B button on xbox one controller
// Rotation stuff
if (inputDir != Vector3.zero) {
float targetRotation = Mathf.Atan2(inputDir.x, inputDir.z) * Mathf.Rad2Deg + cameraTransform.eulerAngles.y;
transform.eulerAngles = Vector3.up * Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation, ref turnSmoothVelocity, GetModifiedSmoothTime(speedSmoothTime));
}
// Pretty sure this is where things are breaking
float targetSpeed = ((running) ? runSpeed : walkSpeed) * inputDir.magnitude;
currentSpeed = Mathf.SmoothDamp(currentSpeed, targetSpeed, ref speedSmoothVelocity, GetModifiedSmoothTime(speedSmoothTime));
currentSpeed *= input.magnitude;
vel = transform.forward * currentSpeed + Vector3.up * velocityY;
currentSpeed = new Vector3(_controller.velocity.x, 0, _controller.velocity.z).magnitude;
}
void ExecuteMovement()
{
_controller.Move(vel * Time.deltaTime);
}

How rotate spaceship from y-axis to left in unity 3D

I am starting a new unity3d project and i want to know how to rotate moving forward spaceship from y-axis to left and right.
void Update(){
transform.position += transform.forward * Time.deltaTime * 10f;
if(input.Getkey(KeyCode.LeftArrow))
{
//code for rotate ;
}
}
I want that when I press arrow left key then spaceship rotates from y-axis to left until the arrow key released.
You can use Transform.Rotate(Vector3).
void Update()
{
if (Input.GetKey(KeyCode.LeftArrow))
transform.Rotate(transform.forward * 3);
if (Input.GetKey(KeyCode.RightArrow))
transform.Rotate(transform.forward * -3);
}
I dont really know if i understand the question, but I think this may help you.
void Update () {
if (Input.GetKeyDown (KeyCode.Space)){
transform.Rotate (new Vector3(Time.deltaTime * 0, 1, 0));
}
where the first 0 is for x-axis, number 1 is for y-axis and the other 0 is for z-axis
You'll want declare the speed and rotation speed as floats so you can easily change them later, but here you go. I did both left and right.
float speed = 10.0f;
float rotateSpeed = 2.0f;
void Update(){
transform.position += transform.forward * Time.deltaTime * speed;
if(Input.GetKeyDown(KeyCode.LeftArrow))
{
//code for rotate
transform.Rotate(vector3.left * Time.deltaTime * rotateSpeed);
}
else if(Input.GetKeyDown(KeyCode.RightArrow))
{
transform.Rotate(vector3.right * Time.deltaTime * rotateSpeed);
}
}
If you want to rotate the ship relative to the world then use:
transform.Rotate(vector3.left * Time.deltaTime * rotateSpeed, Space.World);

Camera rotation in maze game depending of accelerometer

I'm still newbie to Unity... I'm trying to develop simple maze game. I want to roll my ball through maze, but I have to rotate camera left or right, otherwise the player can't see what's behind while rolling the ball on the left or on the right.
void Start ()
{
offset = transform.position - player.transform.position;
}
void LateUpdate()
{
transform.position = player.transform.position + offset;
}
I'm using low pass filter for accelerometer values:
Vector3 lowpass()
{
float LowPassFilterFactor = AccelerometerUpdateInterval / LowPassKernelWidthInSeconds;
lowPassValue = Vector3.Lerp(lowPassValue, Input.acceleration, LowPassFilterFactor);
return lowPassValue;
}
Accelerometer values are from -1 to 1 for each coordinate. Because of that I check my lowPassValue.x value and limit it. If it's positive ( >0.3 ), then the camera should turn right and if it's negative ( <-0.3 ) then camera should turn left.
Quaternion rotation = Quaternion.AngleAxis(45, Vector3.up* Time.deltaTime) ; // right
transform.rotation = rotation;
Quaternion rotation = Quaternion.AngleAxis(-45, Vector3.up * Time.deltaTime) ; // left
transform.rotation = rotation;
But then my offset doesn't work anymore, I can't see ball anymore. And camera rotation doesn't work as it should.
Is there any better solution for this or I'm using the wrong function?
Any help will be very appreciated!
You have a problem with your AngleAxis() where you are multiplying the Axis to rotate on, by Time.deltaTime. this means that essentially you are changing the axis of rotation, and it is unpredictable. Most likely you wont get your "Right" or "Left" rotation. Multiply deltaTime by the angle itself instead.
Also, Quaternions are to be multiplied if you want to apply an angle with them:
Quaternion rotation = Quaternion.AngleAxis(45 * Time.deltaTime, Vector3.up) ; // right
transform.rotation *= rotation;
Quaternion rotation = Quaternion.AngleAxis(-45 * Time.deltaTime, Vector3.up) ; // left
transform.rotation *= rotation;
or this could just be achieved as:
transform.Rotate(45 * Vector3.right * Time.deltaTime); // right
transform.Rotate(45 * -Vector3.right * Time.deltaTime); // left

Categories

Resources