Currently I have the following two issues:
The first issue being that when I reset my camera position I have to also reset my camera rotation. This is due to the fact that my camera
offset is set to a position slightly off of my player on the z and y
axis and obviously those values should change depending on my cameras
rotation although I am unsure how to figure out what those values
should be.
My second issue is that my rotation uses a raycast to find the middle of
the screen and determine its rotation origin although it seems to be
slightly off the middle of the screen as when it rotates the rotation
origin moves as well, if it is indeed in the middle of the screen
shouldn't it be completely still? Also is there a better and less
expensive way of achieving my desired rotation?
Relevant piece of code:
void RotateCamera()
{
//Find midle of screen
Ray ray = Camera.main.ScreenPointToRay(new Vector3(Screen.width / 2, Screen.height / 2, 0));
RaycastHit hitInfo;
//Checks if ray hit something
if (Physics.Raycast(ray, out hitInfo))
{
//Rotate left and right
if (Input.GetKey(KeyCode.RightArrow))
{
transform.RotateAround(hitInfo.point, -Vector3.up, rotationSpeed * Time.deltaTime);
}
if (Input.GetKey(KeyCode.LeftArrow))
{
transform.RotateAround(hitInfo.point, Vector3.up, rotationSpeed * Time.deltaTime);
}
}
//Draws Raycast
Debug.DrawRay(ray.origin, ray.direction * 100, Color.yellow);
}
void ResetCameraPosition()
{
//Reset and lock camera position
transform.rotation = Quaternion.identity;
transform.position = player.transform.position + cameraOffset;
}
Image displaying what I mean
Use Camera.ScreenToWorldPoint to create a 'target' in the middle of the screen around which to pivot. Remove all the raycasting stuff as you don't need it and replace the relevant bits with:
float rotationSpeed = 45; // or whatever speed
float distance = 5f; // or whatever radius for the orbit
Vector3 target = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width / 2, Screen.height / 2, distance));
if (Input.GetKey(KeyCode.RightArrow))
{
transform.RotateAround(target , -Vector3.up, rotationSpeed * Time.deltaTime);
}
if (Input.GetKey(KeyCode.LeftArrow))
{
transform.RotateAround(target , Vector3.up, rotationSpeed * Time.deltaTime);
}
Related
I'm having trouble with my Camera script. The Camera rotates around the player by using a pivot that's assigned as a child of the camera. This works with 2 arrows (I'm developing this game for mobiles, so they're touch arrows) that allow the camera to rotate left and right.
The problem is when the Camera goes behind a wall or a huge object and can't see anything. I looked for a solution and I see that many developers used the RaycastHit or something similar.
Here's my code, the goal is to get the Camera to go closer to the pivot when near a wall in order to avoid blocking the Camera view. Can anyone help me?
public Transform target1;
public Transform pivot;
protected ButtonLeft buttonLeft;
protected ButtonRight buttonRight;
public Vector3 offset;
public bool useOffsetValues;
public float rotateSpeed;
private void Start()
{
buttonLeft = FindObjectOfType<ButtonLeft>();
buttonRight = FindObjectOfType<ButtonRight>();
if (!useOffsetValues)
{
offset = target1.position - transform.position;
}
pivot.transform.position = target1.transform.position;
//pivot.transform.parent = target.transform;
//USE IF U WANT TO DISAPPEAR THE CURSOR
//Cursor.lockState = CursorLockMode.Locked;
//pivot.transform.parent = target.transform;
pivot.transform.parent = null;
// usa questa dopo la costruzione del livello1
//pivot.transform.position = target.transform.position;
}
private void Update()
{
pivot.transform.position = target1.transform.position;
if (buttonLeft.Pressed)
{
pivot.Rotate(0, -90 * Time.deltaTime, 0);
Debug.Log("rotate left");
}
if (buttonRight.Pressed)
{
pivot.Rotate(0, 90 * Time.deltaTime, 0);
Debug.Log("rotate left");
}
Ray ray = new Ray(pivot.transform.position, pivot.transform.position - transform.position);
RaycastHit hit;
/*float horizontal = Input.GetAxis("Mouse X") * rotateSpeed;
float horizontal = Input.GetAxis("Mouse X") * rotateSpeed;
pivot.Rotate(0, horizontal, 0);
pivot.Rotate(0, horizontal, 0);
Use this to make the camera rotate on Mouse Y axes*/
/*float vertical = Input.GetAxis("Mouse Y") * rotateSpeed;
target.Rotate(vertical, 0, 0); */
//move camera based on the current rotation of the target and the original offset
float desiredYAngle = pivot.eulerAngles.y;
//Use this float to set the x angle of player
float desiredXAngle = pivot.eulerAngles.x;
//Use this rotation only if you want to rotate on Y
//Quaternion rotation = Quaternion.Euler(0, desiredYAngle, 0);
//Use this if u want to rotate up&down on x axes
Quaternion rotation = Quaternion.Euler(desiredXAngle, desiredYAngle, 0);
transform.position = target1.position - (rotation * offset);
//transform.position = target.position - offset;
transform.LookAt(target1);
}
Good approach is when you do raycast from pivot to camera, and, if some obstacle found, put camera on a ray, a bit closer to pivot, than hit point. Like this: (pseudocode, not tested):
Vector3 origin = pivot.transform.position;
Vector3 direction = transform.position - pivot.transform.position;
float maxCameraDistance = 10;
Ray ray = new Ray(origin, direction);
RaycastHit hit;
if (Physics.Raycast(ray, maxCameraDistance))
{
Vector3 offsetFromObstacle = -direction.normalized * 0.1f;
transform.position = hit.point + offsetFromObstacle;
}
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;
}
}
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);
}
I want to rotate my 3d model based on player touch on x and y direction.
So that I am detecting horizontal and vertical touch.
But in this I want to restrict z direction rotation. For this I tried multiple codes and ask suggestions in other forums too. At present no suggestion is working for me.
Basically, I don't want z direction rotation. Following image give you more idea, where things going wrong. Model rotated completely into z direction. I want to stop this.
Here is my multiple tries to achieve same thing.
void Update ()
{
// If there are two touches on the device...
if (Input.touchCount == 1 && GameManager.Instance.IsGameStart) {
// Store currnet touch.
Touch touch = Input.GetTouch (0);
// transform.RotateAround (transform.position, Vector3.up, - touch.deltaPosition.x Time.deltaTime 15f);
// transform.RotateAround (transform.position, Vector3.right, touch.deltaPosition.y Time.deltaTime 15f);
// transform.RotateAround (transform.position, Vector3.forward, 0f);
// transform.Rotate (Vector3.up, -touch.deltaPosition.x Time.deltaTime 10f, Space.World);
// transform.Rotate (Vector3.right, touch.deltaPosition.y Time.deltaTime 5f, Space.World);
// transform.Rotate (Vector3.forward, 0f, Space.World);
myRigidbody.MoveRotation (myRigidbody.rotation Quaternion.Euler (Vector3.right touch.deltaPosition.y Time.deltaTime 5f));
myRigidbody.MoveRotation (myRigidbody.rotation Quaternion.Euler (Vector3.up -touch.deltaPosition.x Time.deltaTime 10f));
}
}
In above each block represent unique effort to restrict z direction.
Now please give me some suggestion to achieve same thing.
As well my discussion running at Unity forum
Touch based rotation of 3d model
EDIT: I have tried with restricting rigidbody in z rotation. So my inspector look something like this.
EDIT : After discussion on game development chat room. I have following kind of code :
float prevZ = transform.eulerAngles.z;
transform.Rotate (Vector3.up, -touch.deltaPosition.x Time.deltaTime 10f, Space.World);
transform.Rotate (Vector3.right, touch.deltaPosition.y Time.deltaTime 5f, Space.World);
Vector3 modelVec = transform.eulerAngles;
modelVec.z = prevZ;
transform.eulerAngles = modelVec;
I am just near to solution but now my golf globe model can't able to move more that 180 degree from top or bottom side drag. I just reach around 180 degree and it gets just rotated towards any other direction.
Ok after your comments, it sounds like you may need to start fresh.
Based on what I understand, try this out:
Vector3 rotation = new Vector3();
rotation.y = -touch.deltaPosition.x * Time.deltaTime * yRotSpeed;
rotation.x = touch.deltaPosition.y * Time.deltaTime * xRotSpeed;
rotation.z = 0;
transform.Rotate(rotation);
and then I'd suggest adding:
public float yRotSpeed;
public float xRotSpeed;
as fields of your script so that you can adjust the rotating speed within the unity engine in the future.
Just do like this.
// private variables
private Vector3 inputRotation; // difference of input mouse pos and screen mid point
private Vector3 mouseinput;
// Update is called once per frame
void Update() {
FindPlayerInput();
}
void FindPlayerInput()
{
mouseinput = Input.mousePosition;
mouseinput.z = 0; // for no rotation in z direction
inputRotation = mouseinput - new Vector3(Screen.width * 0.5f, Screen.height * 0.5f,0);
ProcessMovement();
}
void ProcessMovement(){
transform.rotation = Quaternion.LookRotation(inputRotation);
}
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