playMaker

Author Topic: Drag RigidBody  (Read 14145 times)

jeanfabre

  • Administrator
  • Hero Member
  • *****
  • Posts: 14014
  • Official Playmaker Support
    • View Profile
Drag RigidBody
« on: March 27, 2012, 08:19:54 AM »
Finally :)

 yes, finally, you can drag rigidBodies arounds with a single action, not complicated or manual setup :)

 I made it so that you can drag from a given fictive plane defined by a gameObject Up axis. This is very convenient if you want to drag objects on the ground instead of from the camera point of view.

 It of course against the moto of actions I feel, because it lacks of flexibility and it has too much responsability and not real implementation of how fsm should work, but there you go, it's a start :). For example it would be good to set up a mask variable to only allow for certain objects to be dragged. but that would be already for real scenarios. in that case, don't hesitate to come back to me for mods and more granular dragging properties.

usage: create a fsm, and on the start state, simply have this action. Play the scene and you'll be dragging any rigidbodies ( not set to kinematics of course).



Code: [Select]
// (c) copyright Hutong Games, LLC 2010-2012. All rights reserved.

using UnityEngine;

namespace HutongGames.PlayMaker.Actions
{
[ActionCategory(ActionCategory.Physics)]
[Tooltip("Drag a Rigid body with the mouse. If draggingPlaneTransform is defined, it will use the UP axis of this gameObject as the dragging plane normal \n" +
"That is select the ground Plane, if you want to drag object on the ground instead of from the camera point of view.")]
public class DragRigidBody : FsmStateAction
{

[Tooltip("the springness of the drag")]
public FsmFloat spring;

[Tooltip("the damping of the drag")]
public FsmFloat  damper;

[Tooltip("the drag during dragging")]
public FsmFloat drag;

[Tooltip("the angular drag during dragging")]
public FsmFloat angularDrag;

[Tooltip("The Max Distance between the dragging target and the RigidBody being dragged")]
public FsmFloat distance;

[Tooltip("If TRUE, dragging will have close to no effect on the Rigidbody rotation ( except if it hits other bodies as you drag it)")]
public FsmBool attachToCenterOfMass;

[Tooltip("If Defined. Use this transform Up axis as the dragging plane normal. Typically, set it to the ground plane if you want to drag objects around on the floor..")]
public FsmOwnerDefault draggingPlaneTransform;


private SpringJoint springJoint;

private bool isDragging;

private float oldDrag;
private float oldAngularDrag;

private Camera _cam;

private GameObject _goPlane;

private Vector3 _dragStartPos;

private float dragDistance;

public override void Reset()
{
spring = 50f;
damper = 5f;
drag = 10f;
angularDrag = 5f;
distance = 0.2f;
attachToCenterOfMass = false;
draggingPlaneTransform = null;

}

public override void OnEnter()
{
_cam = Camera.main;
_goPlane = Fsm.GetOwnerDefaultTarget(draggingPlaneTransform);


}
public override void OnUpdate()
{

if (!isDragging && Input.GetMouseButtonDown (0))
{


// We need to actually hit an object
RaycastHit hit;
if (!Physics.Raycast(_cam.ScreenPointToRay(Input.mousePosition), out hit, 100))
{
return;
}

// We need to hit a rigidbody that is not kinematic
if (!hit.rigidbody || hit.rigidbody.isKinematic)
{
return;
}

StartDragging(hit);

}

if (isDragging)
{
Drag();
}


}

private void StartDragging(RaycastHit hit)
{
isDragging = true;

if (!springJoint)
{
GameObject go = new GameObject("__Rigidbody dragger__");
Rigidbody body = go.AddComponent<Rigidbody>();
springJoint = go.AddComponent<SpringJoint>();
body.isKinematic = true;
}

springJoint.transform.position = hit.point;
if (attachToCenterOfMass.Value)
{
Vector3 anchor = _cam.transform.TransformDirection(hit.rigidbody.centerOfMass) + hit.rigidbody.transform.position;
anchor = springJoint.transform.InverseTransformPoint(anchor);
springJoint.anchor = anchor;
}
else
{
springJoint.anchor = Vector3.zero;
}

_dragStartPos = hit.point;


springJoint.spring = spring.Value;
springJoint.damper = damper.Value;
springJoint.maxDistance = distance.Value;
springJoint.connectedBody = hit.rigidbody;

oldDrag = springJoint.connectedBody.drag;
oldAngularDrag = springJoint.connectedBody.angularDrag;

springJoint.connectedBody.drag = drag.Value;
springJoint.connectedBody.angularDrag = angularDrag.Value;

dragDistance = hit.distance;

}

private void Drag()
{

if (!Input.GetMouseButton (0))
{
StopDragging();
return;
}

Ray ray = _cam.ScreenPointToRay (Input.mousePosition);
if (_goPlane!=null)
{
Plane _plane = new Plane(_goPlane.transform.up,_dragStartPos);
float enter;

if (_plane.Raycast(ray,out enter))
{
springJoint.transform.position =ray.GetPoint(enter);
}

}else{

springJoint.transform.position = ray.GetPoint(dragDistance);
}



}

private void StopDragging()
{
isDragging = false;
if (springJoint==null)
{
return;
}
if (springJoint.connectedBody)
{
springJoint.connectedBody.drag = oldDrag;
springJoint.connectedBody.angularDrag = oldAngularDrag;
springJoint.connectedBody = null;
}
}

public override void OnExit()
{
StopDragging();
}
}
}


 Bye,

 Jean

ar2511

  • Playmaker Newbie
  • *
  • Posts: 14
    • View Profile
Re: Drag RigidBody
« Reply #1 on: May 03, 2012, 08:18:39 AM »
THX! It is very useful, however, Can I restrict it don't move Y axis or Z axis?
My game is a 2d game only.

Best Regards

Alex Chouls

  • Administrator
  • Hero Member
  • *****
  • Posts: 3520
  • Official Playmaker Support
    • View Profile
    • LinkedIn
Re: Drag RigidBody
« Reply #2 on: May 03, 2012, 08:24:01 AM »
You could try the Constraints in RigidBody. Haven't tested to see if it works with this action, but it should...

ar2511

  • Playmaker Newbie
  • *
  • Posts: 14
    • View Profile
Re: Drag RigidBody
« Reply #3 on: May 05, 2012, 08:17:16 AM »
Yes It works

I have some question would like to ask.

How can I make it move smoothly? I have tried for 1 day.

I have done some control experiment

increase  the spring

decrease the damper

decrease the drag

Best Regards

PS: I am a new beginner, plz forgive my annoying

mylesb

  • Playmaker Newbie
  • *
  • Posts: 1
    • View Profile
Re: Drag RigidBody
« Reply #4 on: May 24, 2012, 05:26:36 AM »
Hi guys :)
I made one small change.
I added a bool variable to check if you want to drag the object up and down or forward and back.

Code: [Select]
// (c) copyright Hutong Games, LLC 2010-2012. All rights reserved.

using UnityEngine;

namespace HutongGames.PlayMaker.Actions
{
[ActionCategory(ActionCategory.Physics)]
[Tooltip("Drag a Rigid body with the mouse. If draggingPlaneTransform is defined, it will use the UP axis of this gameObject as the dragging plane normal \n" +
"That is select the ground Plane, if you want to drag object on the ground instead of from the camera point of view.")]
public class DragRigidBody : FsmStateAction
{

[Tooltip("the springness of the drag")]
public FsmFloat spring;

[Tooltip("the damping of the drag")]
public FsmFloat  damper;

[Tooltip("the drag during dragging")]
public FsmFloat drag;

[Tooltip("the angular drag during dragging")]
public FsmFloat angularDrag;

[Tooltip("The Max Distance between the dragging target and the RigidBody being dragged")]
public FsmFloat distance;

[Tooltip("If TRUE, dragging will have close to no effect on the Rigidbody rotation ( except if it hits other bodies as you drag it)")]
public FsmBool attachToCenterOfMass;

[Tooltip("Move th object forward and back or up and down")]
public FsmBool moveUp;

[Tooltip("If Defined. Use this transform Up axis as the dragging plane normal. Typically, set it to the ground plane if you want to drag objects around on the floor..")]
public FsmOwnerDefault draggingPlaneTransform;

private SpringJoint springJoint;

private bool isDragging;

private float oldDrag;
private float oldAngularDrag;

private Camera _cam;

private GameObject _goPlane;

private Vector3 _dragStartPos;

private float dragDistance;

public override void Reset()
{
spring = 50f;
damper = 5f;
drag = 10f;
angularDrag = 5f;
distance = 0.2f;
attachToCenterOfMass = false;
draggingPlaneTransform = null;
moveUp = true;
}

public override void OnEnter()
{
_cam = Camera.main;
_goPlane = Fsm.GetOwnerDefaultTarget(draggingPlaneTransform);


}
public override void OnUpdate()
{

if (!isDragging && Input.GetMouseButtonDown (0))
{


// We need to actually hit an object
RaycastHit hit;
if (!Physics.Raycast(_cam.ScreenPointToRay(Input.mousePosition), out hit, 100))
{
return;
}

// We need to hit a rigidbody that is not kinematic
if (!hit.rigidbody || hit.rigidbody.isKinematic)
{
return;
}

StartDragging(hit);

}

if (isDragging)
{
Drag();
}


}

private void StartDragging(RaycastHit hit)
{
isDragging = true;

if (!springJoint)
{
GameObject go = new GameObject("__Rigidbody dragger__");
Rigidbody body = go.AddComponent<Rigidbody>();
springJoint = go.AddComponent<SpringJoint>();
body.isKinematic = true;
}

springJoint.transform.position = hit.point;
if (attachToCenterOfMass.Value)
{
Vector3 anchor = _cam.transform.TransformDirection(hit.rigidbody.centerOfMass) + hit.rigidbody.transform.position;
anchor = springJoint.transform.InverseTransformPoint(anchor);
springJoint.anchor = anchor;
}
else
{
springJoint.anchor = Vector3.zero;
}

_dragStartPos = hit.point;


springJoint.spring = spring.Value;
springJoint.damper = damper.Value;
springJoint.maxDistance = distance.Value;
springJoint.connectedBody = hit.rigidbody;

oldDrag = springJoint.connectedBody.drag;
oldAngularDrag = springJoint.connectedBody.angularDrag;

springJoint.connectedBody.drag = drag.Value;
springJoint.connectedBody.angularDrag = angularDrag.Value;

dragDistance = hit.distance;

}

private void Drag()
{

if (!Input.GetMouseButton (0))
{
StopDragging();
return;
}

Ray ray = _cam.ScreenPointToRay (Input.mousePosition);
if (_goPlane!=null)
{
Plane _plane = new Plane();

if ( moveUp.Value )
{
_plane = new Plane(_goPlane.transform.forward,_dragStartPos);
}
else
{
_plane = new Plane(_goPlane.transform.up,_dragStartPos);
}

float enter;

if (_plane.Raycast(ray,out enter))
{
springJoint.transform.position =ray.GetPoint(enter);
}

}else{

springJoint.transform.position = ray.GetPoint(dragDistance);
}



}

private void StopDragging()
{
isDragging = false;
if (springJoint==null)
{
return;
}
if (springJoint.connectedBody)
{
springJoint.connectedBody.drag = oldDrag;
springJoint.connectedBody.angularDrag = oldAngularDrag;
springJoint.connectedBody = null;
}
}

public override void OnExit()
{
StopDragging();
}
}
}

Hope this is useful!

Cheers  :)
Myles Blasonato.

Scrib

  • Playmaker Newbie
  • *
  • Posts: 3
    • View Profile
Re: Drag RigidBody
« Reply #5 on: September 26, 2012, 07:02:51 AM »
Does this script still work?

Added it to an empty gameobject and nothing happens, how are you supposed to set up the FSM with it, right now i'm running Start>State - Add script (use owner, Script=DragRigidBody)

Thanks

jeanfabre

  • Administrator
  • Hero Member
  • *****
  • Posts: 14014
  • Official Playmaker Support
    • View Profile
Re: Drag RigidBody
« Reply #6 on: September 26, 2012, 10:13:14 PM »
Hi,

 you need to target a physics gameObject, that is not an empty gameObject. It has to have a mesh,a collider and a physics component to begin with.

bye,

 Jean

Scrib

  • Playmaker Newbie
  • *
  • Posts: 3
    • View Profile
Re: Drag RigidBody
« Reply #7 on: October 02, 2012, 06:45:18 AM »
I do apologize for not making my predicament clear enough.

I'm unsure of where i need to place the script that you've created, i do (obviously) have an object with a collider and a physics component.
I think i'm just too thick to get what you mean by "...simply have this action...".
Now by this, do you mean setting up an FSM on the object then using "send message" to the script in order to activate it?

If you don't mind guiding me through this i'd be grateful :)
Thanks

jeanfabre

  • Administrator
  • Hero Member
  • *****
  • Posts: 14014
  • Official Playmaker Support
    • View Profile
Re: Drag RigidBody
« Reply #8 on: October 02, 2012, 06:51:41 AM »
Hi,

 no worries :)

 Take this script and put it somewhere ion your assets folder, simply by drag and dropping it.

 this script complies with the action protocoles, so you will see it listed in playmaker Action browser just like any other actions you might find currently. At the top of the action's browser, you have a search field, type "drag" and you will get a short list of all actions which name contains "drag", you'll find that action "dragRigidBody", add that action to a state of a given Fsm, and this is how you will start working with it.

This action is totally embedded in Playmaker, so you don't need to add that script as a component or send messages, it's all done within playmaker for you, simply use it like any other actions.

 now, for the sake of organization, I recommend you create a folder "PlayMaker Custom Actions" and have that script inside this, it will prevent crowding your project folder with junk scripts that do not belong there.

Does that male sense?

bye,

 Jean

Scrib

  • Playmaker Newbie
  • *
  • Posts: 3
    • View Profile
Re: Drag RigidBody
« Reply #9 on: October 02, 2012, 11:56:35 PM »
Yes this makes perfectly sense, i'm stuck though on where to place the working fsm, do i add it to the object i want to move or do i add it to the camera?
I've tried adding it to either the camera and object, both give me a NullReferenceException

Again, thanks for your patience and all the help Jean!

EDIT: Never mind, its seems like i forgot to turn off some older fsm's - so it's working now, thank you!

Also i've noticed that there's a bug in the script where if the object has all but Freeze position X turned on you can still try and drag the object up in Y from a dragging plane resulting in some pretty wacky physics (object flies off the screen faster than the drag velocity).

Last thing i want to point out is, if i only want to trigger this script with a button press how would you achieve this? I noticed you can't put a 'Get button' state in before the script because it already exists in the script.
« Last Edit: October 03, 2012, 01:36:38 AM by Scrib »

Lane

  • Administrator
  • Hero Member
  • *****
  • Posts: 2488
  • Yup.
    • View Profile
    • Cleverous
Re: Drag RigidBody
« Reply #10 on: April 13, 2013, 12:53:08 PM »
I have this working but need some changes to the action and I'm having a hard time butchering the code myself. I just can't seem to get it yet...

I think it would be better to greatly simplify the action altogether and make it more modular. Separate the raycast and such so you can use other actions to do more specific tasks and then run this grabbing action as a result of those conditions instead of making this thing some mega monster action with reduntant things inside of it, if not, my suggestions are below:

Tag mask/filter - only need one, more would be nice. For the times when you only want to affect objects with a certain tag. This way you can have physics on many objects but only be able to manipulate certain ones. Would also be nice to have a Raycast Tag Mask but that may be a bit much, it would be useful for grabbing objects behind other objects or semi-transparent props. It may be unnecessary if you can change the action to make it more modular and play well with other actions that already do these things.


Choice of mouse input - either mouse left or right. Its easy to change in the code, but should have the dropdown available for modularity.


Doesn't play well with other actions - if you use a Mouse Down in a state to jump to the Drag action state it will expect the mouse button to be pressed again before it will engage dragging, this makes it difficult to play with other actions. It should all be run immediately instead of working like a script you drag onto the player. Then I could use a Mouse Up/Down action to dictate when it is on or off.
« Last Edit: April 13, 2013, 01:00:58 PM by Lane »

jeanfabre

  • Administrator
  • Hero Member
  • *****
  • Posts: 14014
  • Official Playmaker Support
    • View Profile
Re: Drag RigidBody
« Reply #11 on: April 14, 2013, 10:55:14 PM »
Hi,

 Good points, it's odd cause I have a another version of this drag action using a gameObject as the source but I can't find any reference in the forum. So basically, it does then allow you to do it the way you want, any mouse or touch input, you simply control the gameObject and run a state with this DragRigidBodyWithGo action I attached to this thread. as soo as you exit the state the drag will stop, so you have total flexibility now :)


bye,

 Jean

Lane

  • Administrator
  • Hero Member
  • *****
  • Posts: 2488
  • Yup.
    • View Profile
    • Cleverous
Re: Drag RigidBody
« Reply #12 on: April 15, 2013, 03:37:04 AM »
Great, I'll look at it tonight.

Thanks Jean  :D

Lane

  • Administrator
  • Hero Member
  • *****
  • Posts: 2488
  • Yup.
    • View Profile
    • Cleverous
Re: Drag RigidBody
« Reply #13 on: April 17, 2013, 05:15:14 PM »
Alright well I tried to manipulate this over the past couple of days but have no success. I don't understand it well enough yet.

Basically what I tried to do was make it so that as soon as the action was started then it would drag a defined object that the user specified, I see that it builds the spring joint in the script so I exposed a gameobject variable and tried to assign that in the script so that it would run, add the spring joint and start dragging. Got rid of the mouse click conditions entirely. It needs to be modular and serve a specific task, that's why I tried taking the mouse click options out so a user could be more flexible in defining when and how it is used by setting up activation conditions states and then hopping into the drag state, then setting the conditions in there for when it exits, whether that is mouse or keyboard buttons.

Anyway I couldn't figure it out, I kept getting one annoying error on a class i didn't understand... I think it was using the Ray to define a hit point vector but I failed to replace that properly and couldn't seem to work around it so after a few glasses of scotch I rage-quit and deleted the modded script... It was pretty small though... I guess I'm happy that I managed to get down to only one error by removing code and replacing variables and exposing new ones.

A fix would be really awesome if you have a moment to do so. The other script was really useful in my hammering around but I'm mostly an bull in the china cabinet in the code so I couldn't pull it off.

jeanfabre

  • Administrator
  • Hero Member
  • *****
  • Posts: 14014
  • Official Playmaker Support
    • View Profile
Re: Drag RigidBody
« Reply #14 on: April 17, 2013, 10:52:37 PM »
Hi,

 you need to cut down the problem.

 Please find first a working scene where it shows how to use this drag with go action properly. then you do what ever you want with both the trigger events and the actual gameObject position in the world, the drag will work.

 now it seems you actually have an issue with the actual logicl behind the mouse events and positions, am I right?

 I don't think there is any fix to apply to the actions I provided, it only needs a proper fsm logic and it will be fine.

Bye,

 Jean