I do not understand how this code works and I am seeking an explanation. This code is inside an update function, updating the Objects location constantly.
By "facing the mouse direction" I mean that the object is like the earth orbiting around the sun for example, but you choose where it is currently located on it's rotation line around the sun.
public GameObject player;
private Vector3 v3Pos;
private float angle;
private readonly float distance = 0.16f;
private void Update()
{
v3Pos = Input.mousePosition;
v3Pos.z = (player.transform.position.z -
Camera.main.transform.position.z);
v3Pos = Camera.main.ScreenToWorldPoint(v3Pos);
v3Pos -= player.transform.position;
angle = Mathf.Atan2(v3Pos.y, v3Pos.x) * Mathf.Rad2Deg;
if (angle < 0.0f) { angle += 360.0f; }
transform.localEulerAngles = new Vector3(0, 0, angle);
float xPos = Mathf.Cos(Mathf.Deg2Rad * angle) * distance;
float yPos = Mathf.Sin(Mathf.Deg2Rad * angle) * distance;
transform.localPosition = new Vector3(player.transform.position.x +
xPos * 4, player.transform.position.y + yPos * 4, 0);
}
I found this code in a video which makes an object (like a gun) rotate around the player and follow the mouse simultaneously but I don't understand how it works. How does it work? Also, I don't know where the video is at any more but I will find it if necessary.
It warps the attached gameobject so many units in the direction of the mouse cursor from the player (locally for some reason), and also turns to face its right side in that direction (locally for some reason).
Individual sections explained in the comments below:
public GameObject player;
private Vector3 v3Pos;
private float angle;
private readonly float distance = 0.16f;
private void Update()
{
// mouse position in screen space
// current value = (mouse x, mouse y, 0) )
Vector3 v3Pos = Input.mousePosition;
// sets the z coordinate to be the difference from the camera to the player
// along the forward world axis.
// current value = (mouse x, mouse y, camera->player distance along forward)
v3Pos.z = (player.transform.position.z - Camera.main.transform.position.z);
// v3Pos now means the position of the cursor, projected onto plane the player
// is on parallel to camera plane
// This means it's a world space positioning of the mouse
v3Pos = Camera.main.ScreenToWorldPoint(v3Pos);
// v3Pos now means the direction from the player to the mouse position
// in world space.
// Despite the name, now a direction, not a position!
v3Pos -= player.transform.position;
// finds the signed angle from right to the direction v3Pos represents.
// in the range (-180, 180]. positive = counterclockwise
angle = Mathf.Atan2(v3Pos.y, v3Pos.x) * Mathf.Rad2Deg;
// converts negative angle into an equivalent positive angle.
if (angle < 0.0f) { angle += 360.0f; }
// sets the forward axis angle of the transform this
// MonoBehaviour is attached to as the same angle.
// Since we measured from the right to the direction of the mouse,
// this turns the right side to face the mouse.
// This is done in local space for some reason, can't tell from code.
transform.localEulerAngles = new Vector3(0, 0, angle);
// finds x and y coordinates of a point in the same direction as
// v3Pos the mouse from the player but at distance
// Could use v3Pos but with a z=0, normalized then * distance but
// recalculating with trig works too.
// basic trig refresher
// cos of angle from right gives unit circle x coordinate
// sin of angle from right gives unit circle y coordinate
float xPos = Mathf.Cos(Mathf.Deg2Rad * angle) * distance;
float yPos = Mathf.Sin(Mathf.Deg2Rad * angle) * distance;
// sets the player's local position to be the position of the player
// adjusted by the direction and distance of the point.
// Also done in local space, can't tell why from code.
// Weird to use something else's world position as the local position
// for something else.
transform.localPosition = new Vector3(player.transform.position.x
+ xPos * 4, player.transform.position.y + yPos * 4, 0);
}
The alternate calculation mentioned above could be done like this:
Vector3 v3Pos = Input.mousePosition;
v3Pos.z = (player.transform.position.z - Camera.main.transform.position.z);
v3Pos = Camera.main.ScreenToWorldPoint(v3Pos);
v3Pos -= player.transform.position;
angle = Mathf.Atan2(v3Pos.y, v3Pos.x) * Mathf.Rad2Deg;
if (angle < 0.0f) { angle += 360.0f; }
transform.eulerAngles = new Vector3(0, 0, angle);
Vector3 v3DirFlat = v3Pos;
v3DirFlat.z = 0f;
// keeping both * distance and * 4
v3DirFlat = v3DirFlat.normalized * distance * 4;
transform.position = player.transform.position + v3DirFlat;
The steps are
// Get the mouse position on the screen
v3Pos = Input.mousePosition;
// Bring that point down until it is level with the player
v3Pos.z = (player.transform.position.z - Camera.main.transform.position.z);
// Find that point in world space coordinates
v3Pos = Camera.main.ScreenToWorldPoint(v3Pos);
// Find the vector from the player to that point
v3Pos -= player.transform.position;
// Calculate the angle between that vector and the X axis
angle = Mathf.Atan2(v3Pos.y, v3Pos.x) * Mathf.Rad2Deg;
// ensure the values are between 0 and 360
if (angle < 0.0f) { angle += 360.0f; }
// Set the item's rotation to that angle, so it faces the right direction
transform.localEulerAngles = new Vector3(0, 0, angle);
// Find the new position of the item on the orbit circle
float xPos = Mathf.Cos(Mathf.Deg2Rad * angle) * distance;
float yPos = Mathf.Sin(Mathf.Deg2Rad * angle) * distance;
// Set the item to it's new position
transform.localPosition = new Vector3(player.transform.position.x + xPos * 4, player.transform.position.y + yPos * 4, 0);
I'm not sure why the coordinates in that last step are multiplied by 4, they should already be positioned on a circle with radius distance
Related
I'm doing a crossing system for a soccer game, and i managed to cross a ball to a target.
The current code ads a curve to the ball, but it only works if the crossSpeed variable is 5, and if the player y axis is at a certain height, if i change the player y axis i need to adjust the drag and angular drag of the ball for it to work correctly.
I took this formula from this unity question: https://answers.unity.com/questions/384515/addforce-to-go-a-specific-distance-and-height.html?sort=votes
vWSpeed, are supposed to be the offset for the ball to go to the left, for it to then get curve from the AddCurveToBall function.
Can anyone help make this code work for multiple cross speeds?
private void FixedUpdate()
{
//if we are crossing the ball add curve to it
if(addCurveToBall)
AddCurveToBall();
}
//this will cross the ball
public void StartCrossBall()
{
//get the direction from the ball to the player
Vector3 dir = playerHead.transform.position - transform.position;
//normalize the direction
dir.Normalize();
//balistic trajectory
float g = Physics.gravity.magnitude; // get the gravity value
float vSpeed = Mathf.Sqrt(2 * g * playerHead.transform.position.y + 2f); // calculate the vertical speed
//balistic trajectory
float gW = Physics.gravity.magnitude; // i use the gravity value for the curve
float vWSpeed = Mathf.Sqrt(2 * g * playerHead.transform.position.y + 2f); // calculate the curve speed
float totalTime = 2 * crossSpeed / g; // calculate the total time
float hSpeed = Vector3.Distance(transform.position, playerHead.transform.position) / totalTime; // calculate the horizontal speed
rb.velocity = dir * hSpeed;
rb.velocity += new Vector3(0, vSpeed + yOffset, vWSpeed + xOffset); // launch the projectile!
//rb.velocity += new Vector3(hSpeed, vSpeed, dir.z + 0.5f); // launch the projectile!
addCurveToBall = true;
}
void AddCurveToBall()
{
//rb.AddForce(Physics.gravity * rb.mass);
rb.AddForce(new Vector3(0,0,Physics.gravity.y) * rb.mass);
}
private void OnCollisionEnter(Collision collision)
{
addCurveToBall = false;
ResetRigidBodyPhysics();
}
I'm trying to rotate the camera angle on my third person player game object on both X and Y axes by using the middle mouse button.
I have this code below.
public class CameraControl : MonoBehaviour
{
public Transform player;
public Vector3 offset;
public float pitch = 2f;
private float turnSpeed = 5f;
private float currentZoom = 10f;
void LateUpdate()
{
if (Input.GetMouseButton(2))
{
offset = Quaternion.AngleAxis(Input.GetAxis("Mouse X") * turnSpeed, Vector3.up) * offset;
offset = Quaternion.AngleAxis(Input.GetAxis("Mouse Y") * turnSpeed, Vector3.right) * offset;
}
transform.position = player.position - offset * currentZoom;
transform.LookAt(player.position + Vector3.up * pitch);
}
}
Rotating in single axes seem to work as expected, I can rotate along X axis or Y axis without any problems, however when they mix up (moving in both directions at the same time) then some things seem to break and the rotation seems to not work as well. It's as if it loses its ability to move along the Y axis along the way.
Another problem is that I'm not quite sure how to limit the Y axis so that the rotation doesn't go below ground or above and around the player's head. I thought about using Mathf.Clamp but setting the min and the max values seems too problematic in my code as it currently is.
For the Quaternion operator * the order matters! It is currentRotation * additionalRotation.
Also usually what you want to do in order to not get a tilted rotation
rotate around the global Y axis
rotate around the local X axis
I think it should rather be something like
if (Input.GetMouseButton(2))
{
offset *= Quaternion.AngleAxis(Input.GetAxis("Mouse X") * turnSpeed, Vector3.up);
offset *= Quaternion.AngleAxis(Input.GetAxis("Mouse Y") * turnSpeed, transform.right);
}
In general I would propose a different approach
I would give the camera a parent object and place that parent object at the position where to look at (your player).
Then youove the camera on the negative local Z axis (zoom).
For the rotation you rather simply rotate the parent object.
Hierarchy
- CameraAnchor
|--Camera (With CameraControl)
and then do something like
public class CameraControl : MonoBehaviour
{
public Transform player;
public Vector3 offset;
public float pitch = 2f;
private float turnSpeed = 5f;
private float currentZoom = 10f;
void LateUpdate()
{
// Center the parent on the player with an optional offset
transform.parent.position = player.position + offset;
if (Input.GetMouseButton(2))
{
// Simply rotate the parent accordingly
// Since the camera is a child of it it will automatically be moved in an orbit
transform.parent.rotation *= Quaternion.AngleAxis(Input.GetAxis("Mouse X") * turnSpeed, Vector3.up);
transform.parent.rotation *= Quaternion.AngleAxis(Input.GetAxis("Mouse Y") * turnSpeed, transform.parent.right);
}
// For the zoom simply move the camera in its local space forward and backward
// A higher value for currentZoom means further away
// so you might want to either change the name or use something like 1/currentZoom
transform.localPosition = Vector3.back * currentZoom;
}
}
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;
}
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;
}
}
So I've been trying to get this to work but no luck so far, hopefully you can help. The thing is I have a camera in my project that the user can freely move with mouse and buttons.
Currently like so:
move = new Vector3(0, 0, -1) * moveSpeed;
move = new Vector3(0, 0, 1) * moveSpeed;
...
And then I just add move vector to cameraPos vector: cameraPos += move
Then problem is if I rotate the camera and then try to move, for example down, it will not move straight down but in a certain angle. I am assuming this is due to moving on local axis. But what I want to do is to move on a world axis. Is something like that possible, or do I have to somehow calculate the angle and then move on more than one axis?
Best regards!
EDIT:
I am rotating the camera where cameraPos is the current position of the camera and rotation is the current rotation of the camera. And this is the code to rotate the camera:
void Update()
{
...
if(pressed)
{
int newY = currentY - oldY;
pitch -= rotSpeed * newY;
}
Rotate();
}
void Rotate()
{
rotation = Matrix.CreateRotationX(pitch);
Vector3 transformedReference = Vector3.Transform(cameraPos, rotation);
Vector3 lookAt = cameraPos + transformedReference;
view = Matrix.CreateLookAt(cameraPos, lookAt, Vector3.Up);
oldY = currentY;
}
Ihope this is more readable.
I was able to solve this problem by using:
Vector3 v;
if (state.IsKeyDown(Keys.Up))
v = new Vector3(0, 0, 1) * moveSpeed;
... //Other code for moving down,left,right
if (state.IsKeyDown(Keys.V))
view *= Matrix.CreateRotationX(MathHelper.ToRadians(-5f) * rotSpeed); //Multiplying view Matrix to create rotation
view *= Matrix.CreateTranslation(v); //Multiplying view Matrix to create movement by Vector3 v
I suppose you're already saving the direction you're looking at in a Vector3. Replace your method with this:
direction.Normalize();
var move = direction * moveSpeed;
cameraPos += move;