Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by Hiilo · Jan 13, 2014 at 09:40 PM · rigidbody2dmouse-dragaddrelativeforce

Move rigidbody2D with AddForceAtPosition relative to mouseclick (no snapping)

Hi. I need help on figuring out how to move a rigidbody2D with force from mouse position, not from it's centre of mass.

Here's a quick video how my script currently works [1]: https://docs.google.com/file/d/0B0ozmyp9_wwiMmN0bGg3XzhvMW8/edit?pli=1 [Link to video][1]

If you watch the video then you can see that if I click on the edges of the red cube then it want's to align it's centre with the mouse cursor. But I want to be able to drag the cube from the point that I clicked on it. I tried getting the distance from the mouse position to the cubes transform and using that offset, but I still can't get it working.

Here's my script

 using UnityEngine;
 using System.Collections;
 
 public class ForceDrag : MonoBehaviour
 {
     public float toVel = 2.5f;//toVel converts the distance remaining to the target velocity - if too low, the rigidbody slows down early and takes a long time to stop;
     public float maxVel = 15.0f;//max speed the rigidbody will reach when moving;
     public float maxForce = 100.0f;//maxForce limits the force applied to the rigidbody in order to avoid excessive acceleration (and instability);
     public float gain = 5f;// gain, sets the feedback amount: if too low, the rigidbody stops before the target point; if too high, it may overshoot and oscillate 
 
 
     private int layerMask = 1 << 8;
     private GameObject[]players;
     private Camera mainCamera;
     private RaycastHit2D hit;
     private Vector3 mousePos;
     private Vector3 mousePosOnMouseDown;
     private Vector3 clickOffset;
 
     private static GameObject movableObject = null;
 
     void Start(){
         mainCamera = Camera.main;
         players = GameObject.FindGameObjectsWithTag("Player");
     
     }
 
     void FixedUpdate()
     {
         if(Input.GetMouseButton(0)){
             moveObject();
         }
     }
 
     void Update ()
         
     {
         mousePos = mainCamera.ScreenToWorldPoint(Input.mousePosition);
         mousePos.z=0;
 
         if (Input.GetMouseButtonDown (0)){
 
             foreach(GameObject player in players){
                 player.rigidbody2D.isKinematic=false;//Allow all the chain links to move
             }
 
             assignMovableObject();
         
         }
 
         if (Input.GetMouseButtonUp(0)){
             movableObject=null;
             foreach(GameObject player in players){
                 player.rigidbody2D.velocity=Vector2.zero;//Zero out linear velocity
                 player.rigidbody2D.angularVelocity=0;//Zero out angular velocity
                 player.rigidbody2D.isKinematic=true;//Stop all the chain links from moving
             }
 
         }
         
         
     }
     void assignMovableObject(){
         
         hit = Physics2D.Raycast(mainCamera.ScreenToWorldPoint (Input.mousePosition), Vector2.zero, Mathf.Infinity, layerMask);
 
         if (hit.collider && hit.rigidbody.isKinematic == false) {
             movableObject = hit.collider.gameObject;//Assign the object to be moved/dragged
         }
     
     }
     void calculateClickOffset(){
         if(movableObject){
             mousePosOnMouseDown = new Vector3(mousePos.x,mousePos.y,-10);
             clickOffset = mousePosOnMouseDown-movableObject.transform.position;//Distance from movable object to mousePos
         }
         
     }
 
     void moveObject(){
         if(movableObject){
 
             Vector2 dist = mousePos-movableObject.transform.position;
             // calc a target vel proportional to distance (clamped to maxVel)
             Vector2 tgtVel = Vector2.ClampMagnitude(toVel * dist, maxVel);
             // calculate the velocity error
             Vector2 error = tgtVel - movableObject.rigidbody2D.velocity;
             // calc a force proportional to the error (clamped to maxForce)
             Vector2 force = Vector2.ClampMagnitude(gain * error, maxForce);
 
             movableObject.rigidbody2D.AddForceAtPosition(force,mousePos);
         }
     }
     
 
 }


I guess the Important part is in the moveObject function. My idea was to add the click offset to the calculation that gives me the dist variable, but I couldn't find a way to get it working. About the gameObject's, they are simple planes that have rigidbody2D , boxCollider2D, hingeJoint2D. White cube is kinematic.

Comment
Add comment · Show 2
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image robertbu · Jan 13, 2014 at 11:55 PM 0
Share

While I'm sure what you are trying to do here can be figured out, consider an alternate solution of using a Joint ins$$anonymous$$d. For an example, exa$$anonymous$$e the standard DragRigidbody.js script provided by Unity. If you don't already have it:

 Assets > Import Package > Scripts
avatar image Hiilo · Jan 14, 2014 at 06:50 AM 0
Share

Thank's for the reply. I tried using Joints for dragging. Found even some scripts in the forum's that were made for 2D physics, but I didn't manage to get them to work properly. I guess the biggest problem with using joint's is still the fact that I would be moving my invisible joint through transform. That started to mess with my hinge joint's when I would drag the red cube(in my example) outside the limits of the "chain". Connected joints would come apart. Also collision detection was poor. Tried both spring joint and hinge joint. Here's a quick video of my HingeDrag in Action [https://drive.google.com/file/d/0B0ozmyp9_wwiYW1DX3hh$$anonymous$$EdhUDA/edit?usp=sharing][1] [1]: https://drive.google.com/file/d/0B0ozmyp9_wwiYW1DX3hh$$anonymous$$EdhUDA/edit?usp=sharing

And here's that script

 using UnityEngine;
 using System.Collections;
 
 public class HingeDrag : $$anonymous$$onoBehaviour
 {
     public float distance = 10.0f;
     public int layer$$anonymous$$ask = 1 << 8;
     private HingeJoint2D hingeJoint;
     private GameObject[]players;
     private Camera mainCamera;
     private GameObject go;
     RaycastHit2D hit;
     private Vector3 mousePos;
     private Vector3 mousePosOn$$anonymous$$ouseDown;
     private Vector3 clickOffset;
     void Start(){
         mainCamera = Camera.main;
         players = GameObject.FindGameObjectsWithTag("Player");
     }
     void FixedUpdate()
     {
         if(Input.Get$$anonymous$$ouseButton(0)){moveObject();}
     }
     void Update ()
     {
         mousePos = mainCamera.ScreenToWorldPoint(Input.mousePosition);
         if (Input.Get$$anonymous$$ouseButtonDown (0)){
             foreach(GameObject player in players){
                 player.rigidbody2D.is$$anonymous$$inematic=false;
             }
             getObject();
             calculateClickOffset();
         }
         if (Input.Get$$anonymous$$ouseButtonUp(0)){
             if (hingeJoint && hingeJoint.connectedBody) {
                 hingeJoint.connectedBody = null;
                 
             }
             if(go){
                 Destroy(go);
             }
             foreach(GameObject player in players){
                 player.rigidbody2D.velocity=Vector2.zero;
                 player.rigidbody2D.is$$anonymous$$inematic=true;
             }
         }    
         
     } 
     void getObject(){
         hit = Physics2D.Raycast(mainCamera.ScreenToWorldPoint (Input.mousePosition), Vector2.zero, $$anonymous$$athf.Infinity, layer$$anonymous$$ask);
         if (hit.collider != null && hit.rigidbody.is$$anonymous$$inematic == false) {
             if (!hingeJoint) {
                 go = new GameObject ("Rigidbody2D Dragger");
                 Rigidbody2D body = go.AddComponent ("Rigidbody2D") as Rigidbody2D;
                 hingeJoint = go.AddComponent ("HingeJoint2D") as HingeJoint2D;
                 body.is$$anonymous$$inematic = true;
             }
             hingeJoint.connectedBody = hit.rigidbody;    
         }
     
     }
     void calculateClickOffset(){
         if(hingeJoint){
             Vector3 goWorldPosition = hingeJoint.connectedBody.transform.position;
             mousePosOn$$anonymous$$ouseDown = new Vector3(mousePos.x,mousePos.y,-10);
             clickOffset = goWorldPosition - mousePosOn$$anonymous$$ouseDown;
         }
     }
     void moveObject(){
         if(hingeJoint){
             hingeJoint.transform.position = mousePos+clickOffset;
         }
     }
     
 }

1 Reply

· Add your reply
  • Sort: 
avatar image
0
Best Answer

Answer by Hiilo · Feb 10, 2014 at 08:12 PM

So after a month started working on this problem again and I think I got it working reasonably well. Ended up using a PID controller. Most of the credit goes to aldonaletto. He's answers were very useful.

Anyway here's how it work's with a single rigidbody2D https://drive.google.com/file/d/0B0ozmyp9_wwiVkEwRUVsaVpIdE0/edit?usp=sharing No snapping :)

And here's how it work's with three rigidbody2D's that are connected through hinge joint's https://drive.google.com/file/d/0B0ozmyp9_wwiSjZGMEx1Nk9VM1k/edit?usp=sharing There's still a little bit of snapping but this seems to be due to the connected hingejoints.

Here's the code

 using UnityEngine;
 using System.Collections;
 
 public class PIDAForceDrag : MonoBehaviour
 {
     Vector2 targetPos = Vector2.zero; // the desired position
     public float maxForce = 100f; // the max force available
     public float pGain = 20f; // the proportional gain
     public float iGain = 0.5f; // the integral gain
     public float dGain = 0.5f; // differential gain
     private Vector2 integrator = Vector2.zero; // error accumulator
     private Vector2 lastError = Vector2.zero; 
     private Vector2 curPos = Vector2.zero; // actual Pos
     private Vector2 force = Vector2.zero; // current force
 
     private int layerMask = 1 << 8;
     private GameObject[]players;
     private Camera mainCamera;
     private RaycastHit2D hit;
     private Vector3 mousePos;
     private Vector3 mousePosOnMouseDown;
     private Vector3 clickOffset;
 
     private static GameObject movableObject = null;
 
     void Start(){
         mainCamera = Camera.main;
         players = GameObject.FindGameObjectsWithTag("Player");
     
     }
 
     void FixedUpdate()
     {
         if(Input.GetMouseButton(0)){
             moveObject();
         }
     }
 
     void Update ()
         
     {
         mousePos = mainCamera.ScreenToWorldPoint(Input.mousePosition);
         mousePos.z=0;
 
         if (Input.GetMouseButtonDown (0)){
             
 
 
             foreach(GameObject player in players){
                 player.rigidbody2D.isKinematic=false;//Allow all the chain links to move
             }
 
             assignMovableObject();
             calculateClickOffset();
         
         }
 
         if (Input.GetMouseButtonUp(0)){
             movableObject=null;
             foreach(GameObject player in players){
                 player.rigidbody2D.velocity=Vector2.zero;//Zero out linear velocity
                 player.rigidbody2D.angularVelocity=0;//Zero out angular velocity
                 player.rigidbody2D.isKinematic=true;//Stop all the chain links from moving
             }
 
         }
         
         
     }
     void assignMovableObject(){
         
         hit = Physics2D.Raycast(mainCamera.ScreenToWorldPoint (Input.mousePosition), Vector2.zero, Mathf.Infinity, layerMask);
 
         if (hit.collider && hit.rigidbody.isKinematic == false) {
             movableObject = hit.collider.gameObject;//Assign the object to be moved/dragged
         }
     
     }
     void calculateClickOffset(){
 
         if(movableObject){
             mousePosOnMouseDown = new Vector3(mousePos.x,mousePos.y,-10);
             clickOffset = mousePosOnMouseDown-movableObject.transform.position;//Distance from movable object to mousePos
 
         }
         
     }
 
     void moveObject(){
         if(movableObject){
             targetPos=mousePos-clickOffset;
             curPos = movableObject.transform.position;
             Vector2 error = targetPos - curPos; // generate the error signal
             integrator += error * Time.deltaTime; // integrate error
             Vector2 diff = (error - lastError)/ Time.deltaTime; // differentiate error
             lastError = error;
             // calculate the force summing the 3 errors with respective gains:
             force = error * pGain + integrator * iGain + diff * dGain;
             // clamp the force to the max value available
             force = Vector3.ClampMagnitude(force, maxForce);
 
             // apply the force to accelerate the rigidbody:
             //movableObject.rigidbody2D.AddForce(force);
 
 
             movableObject.rigidbody2D.AddForce(force);
             //movableObject.rigidbody2D.AddForceAtPosition(force,mousePos);
         }
     }
     
 
 }

I ended up leaving IGain to 0 as it worked best for me.

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

18 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Moving Rigidbody2D relative to the mouse. 1 Answer

Rigidbody2D mouse follow 1 Answer

Adding speed relative to move vector (Rigidbody2D) 1 Answer

Right click drag and drop script 1 Answer

Tracking slow mouse movements 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges