Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question ScreenToWorldPoint when camera is moving.

Discussion in 'Scripting' started by esiorbmA, Dec 1, 2020.

  1. esiorbmA

    esiorbmA

    Joined:
    Dec 1, 2020
    Posts:
    9
    Hello everyone,

    Sorry in advance for my english.

    In my game made for android devices, the character is ball that moves around as in angry birds. The player drags his finger on the screen to create the ball trajectory. When released, the ball jumps following the trajectory.

    (like this :

    it is a great tutorial by the way,thanks to Hamza Herbou)

    The trajectory is made between two points (startPoint and endPoint).

    Everything works fine, until my ball jumps on a moving platform. The camera follows the ball so the camera moves with the platform.
    If the players drags his finger when the camera is moving, the endPoint moves by himself which makes it impossible to play.


    How to compensate the camera movement on the endPoint without creating an offset?



    Code (CSharp):
    1. Camera cam;
    2.  
    3.  
    4.     public Ball ball;
    5.  
    6.     public Trajectory trajectory;
    7.  
    8.     [SerializeField] float pushForce = 3f;
    9.  
    10.     public bool isDragging = false;
    11.  
    12.  
    13.     public Vector3 startPoint;
    14.     public Vector3 endPoint;
    15.  
    16.  
    17.  
    18.     Vector2 direction;
    19.     Vector2 force;
    20.     [Range(0, 5)] public float distance;
    21.  
    22.  
    23.  
    24.     void OnDragStart()
    25.     {
    26.         ball.DesactivateRb();
    27.         startPoint = cam.ScreenToWorldPoint(Input.GetTouch(0).position);
    28.         trajectory.Show();
    29.     }
    30.  
    31.     void OnDrag()
    32.     {
    33.  
    34.         endPoint = cam.ScreenToWorldPoint(Input.GetTouch(0).position);
    35.  
    36.  
    37.         direction = (startPoint - endPoint).normalized;
    38.         force = (direction * distance * pushForce);
    39.  
    40.  
    41.         //just for Debug
    42.         Debug.DrawLine(startPoint, endPoint);
    43.  
    44.         trajectory.UpdateDots(ball.pos, force);
    45.  
    46.     }
    47.     void OnDragEnd()
    48.     {
    49.         distance = 0;
    50.         //push the ball
    51.         ball.ActivateRb();
    52.  
    53.         ball.Push(force);
    54.  
    55.         trajectory.Hide();
    56.     }



    I tried to do something like:

    Code (CSharp):
    1. endPoint = cam.ScreenToWorldPoint(Input.GetTouch(0).position) -  cam.transform.position ;
    it kind of works, the trajectory stays still, but with a large offset :(



    Any help will be appreciated, thanks !
     
    Last edited: Dec 1, 2020
  2. Ray_Sovranti

    Ray_Sovranti

    Joined:
    Oct 28, 2020
    Posts:
    172
    This doesn't actually have anything to do with ScreenToWorldPoint (which doesn't care at all whether the camera is moving), but just has to do with the fact that OnDrag is only going to be called when your player moves their finger. If the camera moves but the finger stays still, you won't get that message, and the state of the game will become "outdated".

    The simple solution here would be to switch to an Update() based system. Make a bool that is set to true in OnDragStart and to false in OnDragEnd. Then you should be able to move your OnDragCode into Update:
    Code (csharp):
    1. void Update() {
    2. if (isDragging) {
    3. // your existing OnDrag code
    4. }
    5. }
    This will run every frame in between when the player has started and stopped dragging, whether or not the player is actually dragging on that frame.
     
  3. esiorbmA

    esiorbmA

    Joined:
    Dec 1, 2020
    Posts:
    9


    Hello Ray,
    Thanks for your answer.
    I didn't submit the full script, but OnDragStart and OnDrag are already called in update.

    Here's the full script, this might help you to understand the problem.


    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class GameManagerTouch : MonoBehaviour
    6. {
    7.  
    8.     #region Singleton class: GameManagerTouch
    9.  
    10.     public static GameManagerTouch Instance;
    11.  
    12.     void Awake()
    13.     {
    14.         if (Instance == null)
    15.         {
    16.             Instance = this;
    17.         }
    18.     }
    19.  
    20.     #endregion
    21.  
    22.     Camera cam;
    23.  
    24.  
    25.     public Ball ball;
    26.     public PlatformMove platformMove;
    27.     public Trajectory trajectory;
    28.  
    29.     [SerializeField] float pushForce = 4f;
    30.  
    31.     public bool isDragging = false;
    32.  
    33.  
    34.     Vector2 startPoint;
    35.     Vector2 endPoint;
    36.     Vector2 direction;
    37.     Vector2 force;
    38.     [Range(0, 5)] public float distance;
    39.  
    40.  
    41.     public bool isStopped;
    42.  
    43.  
    44.  
    45.     void Start()
    46.     {
    47.         cam = Camera.main;
    48.         ball.DesactivateRb();
    49.         isStopped = false;
    50.     }
    51.  
    52.     void Update()
    53.     {
    54.         ball.CheckIsMoving();
    55.  
    56.         ball.CheckIsGrounded();
    57.  
    58.  
    59.  
    60.         if (Input.touchCount > 1)
    61.         {
    62.             Debug.Log("y'a deux doigts");
    63.             //StopDrag();
    64.             isDragging = false;
    65.             isStopped = true;
    66.             trajectory.Hide();
    67.  
    68.         }
    69.  
    70.         if (Input.touchCount > 0)
    71.         {
    72.             if ((Input.GetTouch(0).phase == TouchPhase.Began) && (ball.isMoving == false) && (ball.isGrounded == true))
    73.             {
    74.                 isDragging = true;
    75.                 OnDragStart();
    76.             }
    77.         }
    78.  
    79.  
    80.         if (Input.touchCount > 0)
    81.         {
    82.             if ((Input.GetTouch(0).phase == TouchPhase.Ended) && (isDragging == true))
    83.             {
    84.                 isDragging = false;
    85.                 ball.rb.constraints = RigidbodyConstraints2D.None;
    86.  
    87.                 OnDragEnd();
    88.          
    89.             }
    90.         }
    91.  
    92.  
    93.  
    94.         if (isDragging)
    95.         {
    96.             OnDrag();
    97.         }
    98.     }
    99.  
    100.     private void FixedUpdate()
    101.     {
    102.         if (ball.isSticked == true)
    103.         {
    104.             ball.DesactivateRb();
    105.             //ball.rb.constraints = RigidbodyConstraints2D.FreezePositionY | RigidbodyConstraints2D.FreezePositionX | RigidbodyConstraints2D.FreezeRotation;
    106.      
    107.             ball.isSticked = false;
    108.         }
    109.     }
    110.  
    111.  
    112.  
    113.     //---------------------------- Drag
    114.  
    115.     void OnDragStart()
    116.     {
    117.         ball.DesactivateRb();
    118.         startPoint = cam.ScreenToWorldPoint(Input.GetTouch(0).position);
    119.  
    120.         trajectory.Show();
    121.         isStopped = false;
    122.     }
    123.  
    124.     void OnDrag()
    125.     {
    126.         endPoint = cam.ScreenToWorldPoint(Input.GetTouch(0).position);
    127.  
    128.         //pour atténuer la force, on peut soit diviser la ligner en dessus pour 0.7f ou bien directement modifier la valeur de force dans le gamemanagerTouc
    129.         if ((Vector2.Distance(startPoint, endPoint)) < 6.5f)
    130.         {
    131.             distance = (Vector2.Distance(startPoint, endPoint));
    132.         }
    133.  
    134.  
    135.         direction = (startPoint - endPoint).normalized;
    136.         force = (direction * distance * pushForce);
    137.  
    138.  
    139.  
    140.         //just for Debug
    141.         Debug.DrawLine(startPoint, endPoint);
    142.  
    143.         trajectory.UpdateDots(ball.pos, force);
    144.  
    145.  
    146.  
    147.     }
    148.     void OnDragEnd()
    149.     {
    150.         distance = 0;
    151.         //push the ball
    152.         ball.ActivateRb();
    153.  
    154.         ball.Push(force);
    155.  
    156.         trajectory.Hide();
    157.     }
    158.  
    159.  
    160. }
    161.  


    Here a video showing what the problem is,if it can help :) it works fine until the ball is on the moving platform




    When I do
    Code (CSharp):
    1. endPoint = cam.ScreenToWorldPoint(Input.GetTouch(0).position) - cam.transform.position;
    it works, but with a terrible offset making it really hard to play
     
    Last edited: Dec 3, 2020
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,756
    When you do this, the Vector3 you get back from touch has the Z component as zero.

    From the documentation:

    https://docs.unity3d.com/ScriptReference/Camera.ScreenToWorldPoint.html

    "The z position is in world units from the camera."

    This means the distance into the scene is zero, so it will always be at the camera.

    This is not generally useful.

    Instead, copy out the position, set the Z for how far you want to be from the camera.

    OR... you can use the ray helpers on the camera and then raycast into the scene.
     
  5. esiorbmA

    esiorbmA

    Joined:
    Dec 1, 2020
    Posts:
    9
    Hello, Thanks for your answer but I don't understand how it is helpfull. My camera is orthographic. I don't see what the Z has to do with my problem. Also I Can make startPoint and endPoint Vector 2 instead of Vector 3 but it doesn't change anything
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,756
    Looking over your problem more, pretty sure it has to do with this:

    - when the drag starts, you calculate
    startPoint
    and save it.

    - while you are dragging you calculate
    endPoint


    - you produce your delta from these; I agree that if you are in ortho, the Z depth does not matter, as I said above. Ignore my above post.

    - you also indicate that your camera moves when you're on a platform

    This makes me think you need to track where the camera was in the drag start, and then every frame of drag, you need to adjust (or rather anti-adjust) the startPoint by the amount which the camera has moved.

    Otherwise your startPoint just becomes stale, which I think is what you're seeing.
     
    esiorbmA likes this.
  7. esiorbmA

    esiorbmA

    Joined:
    Dec 1, 2020
    Posts:
    9
    Exactly: if the mouse isn't moving, the distance between startPoint and endPoint shouldn't change, but it does when the camera moves.

    so I came to the same conclusion, it is what I try to do now.

    But now my question is that: how to apply to add to startPoint the camera movements? or how to substract it from endPoint?

    I asked the question on this thread because it might hep other people
    https://forum.unity.com/threads/how-to-apply-to-two-vectors-the-same-variation.1016728/
     
  8. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,756
    If your camera is ortho, you only care about motion in X, Y, and this also means that as much as you moved the camera this frame, that's how much to anti-move the startPoint.

    I would just put an Update() method in your script above that keeps the notion of the previous frame camera position, takes the difference and subtracts it from startPoint.

    It's also useful to spawn disposable objects in game to see where points are, like spawn a sphere and every frame make it go to startPoint, so when you're doing the above calculations you can verify it moved. Then delete that code when you have the math right.
     
  9. dhumildholiya97

    dhumildholiya97

    Joined:
    Sep 1, 2020
    Posts:
    2
    • I got this same problem recently and above discussion helped understand my problem very well. And Now I got solution.
    • Main problem is that `Start Point` and 'End Point' are in world position. So When camera moves and it Recalculates `End Point` and changes the World position which affects the projectile motion.
    • Solution is to convert both `Start Point` and `End Point` to local position of camera so when `End Point` get recalculates its still in the reference of Camera's position and does not affect the projectile motion.
    • Code (CSharp):
      1. if (Input.GetMouseButtonDown(0))
      2.                     {
      3.                         _startMousePosition = _cam.ScreenToWorldPoint(Input.mousePosition);
      4.                         _startMousePosition = _cam.transform.InverseTransformPoint(_startMousePosition);
      5.                         _projectileIndicator.PointsSetActive(true, transform.position);
      6.                     }
      7.  
      8.                     if (Input.GetMouseButton(0))
      9.                     {
      10.                         Vector2 position = _cam.ScreenToWorldPoint(Input.mousePosition);
      11.                         position = _cam.transform.InverseTransformPoint(position);
      12.                         _initialVelocity = (_startMousePosition - position) * speed;
      13.                         _projectileIndicator.DrawProjectileTrajectory(transform.position, _initialVelocity,
      14.                             Physics2D.gravity * _rb.gravityScale, timeInterval);
      15.                     }
    • I hope you understand solution and it helps you to solve your problem. :)
     
  10. unity_35906D23077B33E16848

    unity_35906D23077B33E16848

    Joined:
    Apr 8, 2021
    Posts:
    2
    For those who still have problems with this, all they have to do is create another camera that doesn’t follow the Player and that’s it, that’s it. It could be any camera.
     
  11. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    That's a needlessly expensive solution for a simple problem. If you don't truly need another camera, this should not be a solution for merely computing a point.
     
    Anthiese likes this.