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

Drag and throw in Isometric view [C# Code included]

Discussion in 'Scripting' started by nicmarxp, Jan 6, 2018.

  1. nicmarxp

    nicmarxp

    Joined:
    Dec 3, 2017
    Posts:
    404
    I'm trying to make a simple (codewise) way to pick up, drag and throw objects in an isometric view. I'm trying to get the speed of the mouse, then apply that speed to the object. I currently only read the last mouse position, but I'm not sure if I need to read a few frames back, maybe with an array of mousepositions?

    My camera is set up as Orthographic with rotation X: 30 and Y: 45, but ideally the script would adapt to whatever rotation I'm using and both in orthographic and perspective view.

    My code is quite simple, but it doesn't work very well. I have three issues:

    1) When dragging, the object goes below the ground level, so I think I messed up something with WorldToScreenPoint / ScreenToWorldPoint.

    2) When releasing with speed, up/right works well, up/left moves it down left. I want it to ignore Y values, and only convert between mouse movements to the isometric view, so it maps to the direction I'm moving the mouse.

    3) If the object is rotated when I release it, it moves in a very different position. I want the force vector to be the same whatever the rotation is.

    Here's my code:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class DragThrow : MonoBehaviour {
    4.     private Rigidbody rigidbody;
    5.     private Vector3 screenPoint;
    6.     private Vector3 offset;
    7.     private Vector3 oldMouse;
    8.     private Vector3 mouseSpeed;
    9.     public float speed = 5;
    10.  
    11.     void Start () {
    12.         rigidbody = GetComponent<Rigidbody>();
    13.     }
    14.  
    15.     void OnMouseDown() {
    16.         oldMouse = Input.mousePosition;
    17.         screenPoint = Camera.main.WorldToScreenPoint(gameObject.transform.position);
    18.         offset = gameObject.transform.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPoint.z));
    19.     }
    20.  
    21.  
    22.     void OnMouseDrag() {
    23.         var curScreenPoint = new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPoint.z);
    24.         Vector3 curPosition = Camera.main.ScreenToWorldPoint(curScreenPoint) + offset;
    25.         transform.position = curPosition;
    26.     }
    27.  
    28.     void OnMouseUp() {
    29.         mouseSpeed = oldMouse - Input.mousePosition;
    30.         rigidbody.AddForce(mouseSpeed * speed * -1, ForceMode.Force);
    31.     }
    32. }
    33.  
    Here's a GIF showing the issues. Thank you in advance! :)

     
    Gornian likes this.
  2. brownboot67

    brownboot67

    Joined:
    Jan 5, 2013
    Posts:
    375
    Your mouse up fling is based on the start and end positions of the mouse drag. That's not really what you care about. Try comparing the current frames position with last frames position.
     
  3. nicmarxp

    nicmarxp

    Joined:
    Dec 3, 2017
    Posts:
    404
    Thanks for the feedback. I rebuilt my code, and used a ray to get the mouse position to world, based on a ground collider. Also I'm now storing the last positions in a list. So I can get the vector between current and last X steps back.

    I'm very satisfied with how it works, and I don't have any spring joints and the code is quite short, so this works very good for my application. I'd love to have some feedback on if there's anything that can be made better. I suspect there might be some glitches in throwing speed depending on frame rate/drops.

    You can set the amount of frames to capture, the maxMagnitude, to limit to quick throws, and also a throwPower which multiples the magnitude, to give some power to smaller throws.

    Hope this helps someone that tries to make a simple drag and throw function, since I read probably 20 different threads on it, and finally wrote this from scratch instead. Feel free to ask if there's anything I can help with :)

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections.Generic;
    4.  
    5. public class DragThrow : MonoBehaviour {
    6.     private Rigidbody rigidbody;
    7.     public List<Vector3> listOfPositions = new List<Vector3>();
    8.     private Vector3 currentPosition;
    9.     private Collider groundCollider;
    10.     public GameObject ground;
    11.     public int captureFrames = 8;
    12.     public float throwPower = 5;
    13.     public float maxMagnitude = 10;
    14.  
    15.     void Start () {
    16.         rigidbody = GetComponent<Rigidbody>();
    17.         groundCollider = ground.GetComponent<Collider>();
    18.     }
    19.  
    20.     void OnMouseDown() {
    21.         readPosition();
    22.         listOfPositions.Add(currentPosition);
    23.         rigidbody.velocity = rigidbody.angularVelocity = Vector3.zero;
    24.     }
    25.  
    26.  
    27.     void OnMouseDrag() {
    28.         readPosition();
    29.         if (listOfPositions.Count>=captureFrames) {
    30.             listOfPositions.RemoveAt(0);
    31.             listOfPositions.TrimExcess();
    32.         }
    33.     }
    34.  
    35.     void OnMouseUp() {
    36.         var oldPosition = listOfPositions[0];
    37.         var forceMagnitude = (oldPosition-transform.position).magnitude;
    38.         var forceDirection = (oldPosition-transform.position).normalized;
    39.         float newMagnitude = Mathf.Min(forceMagnitude, maxMagnitude);
    40.         rigidbody.AddForce(forceDirection * -newMagnitude * throwPower, ForceMode.Impulse);
    41.         listOfPositions.Clear();
    42.     }
    43.  
    44.     void readPosition() {
    45.         RaycastHit hit;
    46.         if (groundCollider.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit, 200.0F)) {
    47.             currentPosition = new Vector3(hit.point.x, transform.position.y, hit.point.z) ;
    48.             transform.position = currentPosition;
    49.         }
    50.         listOfPositions.Add(currentPosition);
    51.     }
    52. }
    53.  
     
    Gornian, joergipoergi and hsnvrsln like this.
  4. nicmarxp

    nicmarxp

    Joined:
    Dec 3, 2017
    Posts:
    404
    Here's an example of what it looks like. Looks better in higher frame rate of course.. :)
     
    Gornian and joergipoergi like this.
  5. Gornian

    Gornian

    Joined:
    Jun 22, 2017
    Posts:
    7
    You save me time. Thanks man a lot :)
     
  6. astonb

    astonb

    Joined:
    Apr 27, 2014
    Posts:
    1
    Hey @nicmarxp, I saw initially that you had used an offset for your original implementation of the drag and throw system. Is there anyway you would know how to do this for your new adaptation as when dragging objects they are centered to the mouse's position. Im struggling to wrap my head around it any help would be much appreciated :)
     
  7. palex-nx

    palex-nx

    Joined:
    Jul 23, 2018
    Posts:
    1,745
    I suggest you use average speed for last 0.1 - 0.25 seconds because mouse and especially finger (if you ever plan touchscreen) movement is not very smooth. Smothing it based on few last frames gives better results. You can accumulate movement in Update and apply force in FixedUpdate to achieve this effect natively.