I have a method that will rotate my object to the point my mouse just clicked on. However, my object only rotates as long as my mouse button is held down.
My main rotation method is this:
void RotateShip ()
{
Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
Debug.Log (Input.mousePosition.ToString ());
Plane playerPlane = new Plane (Vector3.up, transform.position);
float hitdist = 0.0f;
if (playerPlane.Raycast (ray, out hitdist))
{
Vector3 targetPoint = ray.GetPoint (hitdist);
Quaternion targetRotation = Quaternion.LookRotation (targetPoint - transform.position);
transform.rotation = Quaternion.Slerp (transform.rotation, targetRotation, speed * Time.deltaTime);
}
}
I call this method inside the FixedUpdate method and I've wrapped it inside the following if statement:
void FixedUpdate ()
{
// Generate a plane that intersects the transform's position with an upwards normal.
// Generate a ray from the cursor position
if (Input.GetMouseButton (0))
{
RotateShip ();
}
}
However, the object will still only rotate as long as my mouse button is held down. I want my object to continue to rotate to the point my mouse just clicked until it reaches that point.
How can I amend my code properly?
It's only rotating while your mouse is down because that's the only time you tell it to rotate. In your FixedUpdate (which Imtiaj rightfully pointed out should be Update), you're only calling RotateShip() while Input.GetMouseButton(0) is true. That means that you only rotate your ship while the button is pressed.
What you should do is take that mouse event and use it to set a target, then continuously rotate toward that target. For instance,
void Update() {
if (Input.GetMouseButtonDown (0)) //we only want to begin this process on the initial click, as Imtiaj noted
{
ChangeRotationTarget();
}
Quaternion targetRotation = Quaternion.LookRotation (this.targetPoint - transform.position);
transform.rotation = Quaternion.Slerp (transform.rotation, targetRotation, speed * Time.deltaTime);
}
void ChangeRotationTarget()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Plane playerPlane = new Plane (Vector3.up, transform.position);
float hitdist = 0.0f;
if (playerPlane.Raycast (ray, out hitdist))
{
this.targetPoint = ray.GetPoint (hitdist);
}
}
So now instead of only doing the rotation while MouseButton(0) is down, we do the rotation continuously in the update and instead only set the target point when we click the mouse.
Related
I have an object moving forward constantly, I want to make it move on the X axis by mouse dragging any part of the screen (swiping), so I tried this code but when first clicking on the screen the object move to the mouse X position (without dragging)! do you have any suggestions on how to make it move only when dragging?
The Script:
private bool dragging = false;
private float distance;
public Rigidbody Ball;
public float Speed = 100;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void FixedUpdate () {
Ball.velocity = transform.forward * Speed * Time.deltaTime;
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
distance = Vector3.Distance(transform.position, Camera.main.transform.position);
dragging = true;
}
if(Input.GetMouseButtonUp(0))
{
dragging = false;
}
if (dragging)
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Vector3 rayPoint = ray.GetPoint(distance);
transform.position = new Vector3(rayPoint.x, transform.position.y, transform.position.z);
}
}
For the continues movement you can use GetMouseButton which is true while the button stays pressed (in contrary to GetMouseButtonDown which is true only the first frame the button is pressed)
You said the script is attached to the same as the RigiBody component. In such a case you should never place an object using
transform.position = ...
but instead use RigidBody.MovePosition like
Ball.MovePosition(new Vector3( ... ));
Finally store the initial position on mouse down and than use it as a reference point:
Vector3 initialPosition;
void Update()
{
// called the first time mouse button is pressed
if (Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
initialPosition = transform.position;
Vector3 rayPoint = ray.GetPoint(0);
// Not sure but this might get you a slightly better value for distance
distance = Vector3.Distance(transform.position, rayPoint );
}
// called while button stays pressed
if (Input.GetMouseButton(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Vector3 rayPoint = ray.GetPoint(distance);
Ball.MovePosition(initialPosition + new Vector3(rayPoint.x, 0, 0));
}
}
Also note that I'm not sure that
// Update is called once per frame
void FixedUpdate () {
Ball.velocity = transform.forward * Speed * Time.deltaTime;
}
is what you want to do. The usage of Time.deltaTime makes not much sense here in my eyes. You either want to set the velocity to a certain speed like
Ball.velocity = transform.forward * Speed;
or you want to change a position using Time.deltaTime to have a smooth movement like
Ball.MovePosition(transform.position + transform.forward * Speed * Time.deltaTime);
My app (using Unity 2017.3.1f1) is set up like this:
The perspective camera is inside the "player" object (collisions are disabled) and also a child of it (to get a 1st person view)
The camera has a "MouseLook" script (I changed a few things in Unity's default one) that keeps the cursor locked and hidden in the middle of the screen (Cursor.lockState = CursorLockMode.Locked & Cursor.visible = false)
The "player" has a general "input" script and a movement script: If wasd are pressed, these key presses move the player object, which also makes the camera move
What I want to achieve:
Use the scroll wheel on the mouse to move towards the point the camera is looking at or away from it, independent if there's an object.
I do NOT want to change the FOV/scale
It's not necessary to use Lerp, I just want to move the player a step towards the target position every frame the mouse wheel is used
What I've tried:
1a. In the general input script:
if(Input.GetAxis("Mouse ScrollWheel") != 0) {
transform.Translate(0,0,Input.GetAxis("Mouse ScrollWheel") * 200);
}
1b: In the general input script:
if(Input.GetAxis("Mouse ScrollWheel") != 0) {
transform.position += transform.forward * Input.GetAxis("Mouse ScrollWheel") * 200;
}
Both work the same but only move the player/camera on the same y level, even when looking down/up. transform.up always outputs "(0.0, 1.0, 0.0)", so there's no point incorporating that.
2.A new script on the camera (source):
public class ScrollToZoom : MonoBehaviour {
public GameObject player;
void Update () {
if(Input.GetAxis("Mouse ScrollWheel") != 0) {
RaycastHit hit;
Ray ray = this.transform.GetComponent<Camera>().ScreenPointToRay(Input.mousePosition);
Vector3 desiredPosition;
if(Physics.Raycast(ray,out hit)) {
desiredPosition = hit.point;
} else {
desiredPosition = transform.position;
}
float distance = Vector3.Distance(desiredPosition,transform.position);
Vector3 direction = Vector3.Normalize(desiredPosition - transform.position) * (distance * Input.GetAxis("Mouse ScrollWheel"));
transform.position += direction;
}
}
}
This doesn't work at all because "direction" is always (0,0,0) because the mouse cursor is locked (which I can't/won't change).
How do you incorporate the rotation around the x-axis (I clamp it to +/- 90° in the "MouseLook" script) in this?
if(Input.GetAxis("Mouse ScrollWheel") != 0) {
transform.localPosition += Vector3.forward * Input.GetAxis("Mouse ScrollWheel") * 200;
}
Should work. transform.position will refer to the world position regardless of where the object is facing.
I have an object rotating to always face the mouse, but the rotation is instant. I would like to slow the rotation down so the user slowly turns to face the mouse pointer.
I am using the code from here:
https://unity3d.com/learn/tutorials/projects/survival-shooter
Here is the snippet of code I am having problems chaning:
// Create a ray from the mouse cursor on screen in the direction of the camera.
Ray camRay = Camera.main.ScreenPointToRay (Input.mousePosition);
// Create a RaycastHit variable to store information about what was hit by the ray.
RaycastHit floorHit;
// Perform the raycast and if it hits something on the floor layer...
if(Physics.Raycast (camRay, out floorHit, camRayLength, floorMask))
{
// Create a vector from the player to the point on the floor the raycast from the mouse hit.
Vector3 playerToMouse = floorHit.point - transform.position;
// Ensure the vector is entirely along the floor plane.
playerToMouse.y = 0f;
// Create a quaternion (rotation) based on looking down the vector from the player to the mouse.
Quaternion newRotation = Quaternion.LookRotation (playerToMouse);
// Set the player's rotation to this new rotation.
playerRigidbody.MoveRotation (newRotation);
}
The solution from this link should work, as it lerps gradually towards the sought rotation.
https://answers.unity.com/questions/1093355/rotate-object-smoothly-over-time-when-key-pressed.html
public float smooth = 1f;
private Quaternion targetRotation;
void Start(){
targetRotation = transform.rotation;
}
void Update () {
if (Input.GetKeyDown (KeyCode.Space)) {
targetRotation *= Quaternion.AngleAxis(60, Vector3.up);
}
transform.rotation= Quaternion.Lerp (transform.rotation, targetRotation , 10 * smooth * Time.deltaTime);
}
void Update()
{
MovePlayer();
}
Then the first original MovePlayer i did rotate the player but in the specific angle i set for example:
private void MovePlayer()
{
if (Input.GetMouseButtonDown(0))
{
targetAngles = transform.eulerAngles + 85.0f * Vector3.up;
StartCoroutine(TurnObject(transform, transform.eulerAngles, targetAngles, smooth));
}
}
But i want it to rotate to the angle of the mouse cursor position clicked not to walk there or move there only to rotate and face to the mouse cursor position clicked. So i tried to change the MovePlayer method to this but this is wrong it does nothing:
private void MovePlayer()
{
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
{
startPositon = hit.point;
targetAngles = transform.eulerAngles + 0 * Vector3.up;
StartCoroutine(TurnObject(transform, transform.eulerAngles, targetAngles, smooth));
}
}
}
And
IEnumerator TurnObject(Transform ship, Vector3 startAngle, Vector3 endAngle, float smooth)
{
float lerpSpeed = 0;
isSpinning = true;
while (lerpSpeed < 1)
{
ship.eulerAngles = Vector3.Lerp(startAngle, endAngle, lerpSpeed);
lerpSpeed += Time.deltaTime * smooth;
yield return null;
}
startPositon = ship.position;
isSpinning = false;
}
This is working but with some problems:
void Update()
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, 100000))
{
transform.LookAt(hit.point);
}
}
The first problem is when i point the mouse cursor to the sky since it's too far the player is not rotating and then when i point back to something in the distance range the player jump to the position. Not sure how to solve it. Maybe to calculate the sky(sky box in my case) distance so it will rotate also when pointing the sky ?
The second problem is when i point the mouse cursor on my self the player body it's making some fast rotations on place. How can i avoid it ? Or solve it ?
I tried to change the line:
if (Physics.Raycast(ray, out hit, 100000))
To
if (Physics.Raycast(ray, out hit))
But it didn't solve it. Maybe the problem is that my sky is a skybox i dragged to the Scene window so it's not an object.
My skybox is material i dragged to the Scene window so maybe this is the problem i can't rotate the player when the mouse cursor is pointing the sky. Any solution?
When i press LMB i am rotating my player towards the mouse position, what i also want to do is simulate shooting. So when clicking LMB i want to create a line from the player position to the mouse position. My big issue is that my player is moving and i cant seem to figure out how to get the start position of the ray.
As of now the ray renders from vector3(0,0,0) to the mouse position, which is not what i want. I want it to render from the PLAYER position to the mouse position.
Here is my code:
void Update () {
transform.Translate (Vector3.down * Time.deltaTime * movementSpeed, Space.World);
if (Input.GetButtonDown("Fire1")) {
Vector3 mousePos = new Vector3 (Input.mousePosition.x, Input.mousePosition.y, 10);
Vector3 lookPos = Camera.main.ScreenToWorldPoint (mousePos);
lookPos = lookPos - transform.position;
float angle = Mathf.Atan2 (lookPos.y, lookPos.x) * Mathf.Rad2Deg + 90;
transform.rotation = Quaternion.AngleAxis (angle, Vector3.forward);
if (Time.time > nextFire)
{
nextFire = Time.time + fireRate;
StartCoroutine (shotEffect());
laserLine.SetPosition (1,this.transform.position+lookPos);
}
}
}
If your code is attached to player and laserLine.SetPosition() sets beginning and ending for your line i think that:
laserLine.SetPosition (1,this.transform.position+lookPos);
should looks:
laserLine.SetPosition (this.transform.position,this.transform.position+lookPos);