Search Unity

Half Life 2 Object Grabber

Discussion in 'Scripting' started by Iron-Warrior, Feb 24, 2011.

  1. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    838
    Hullo,

    Been working on a new prototype, and I've been trying to add in a solid First Person object grabber like Half Life 2 (and probably other games) uses. For anyone who hasn't played Half Life 2, it's pretty simple: you can pick up objects in the world, which then are locked in front of your view. You can carry them around, but they're still part of the world in that they will hit the surrounding walls and such. If the object gets too far away from you, it's automatically dropped.

    Most of this is pretty darn easy, but I can't find a really good way of moving the object properly while having the world influence it (so it needs to be inside the physics system). SpringJoints are really...springy and kinda eratic. If I just use MoveRigidbody to move it directly in front of the camera every frame, it causes eighty billion problems. If I push it up against a wall it will fall through or push the character away (using a rigidbody FPS Walker). If it hits against any other object, it smacks them with a huge amount of force. Can't just use transform.position of course...running out of ideas. I don't want it to be floaty...I want it to basically lock in front of the player's vision, and not get pushed through walls.

    My entire idea is based off this, so I hope someone can come up with an idea...thanks for any help!
     
  2. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    838
    Bump for help...I guess I might just have to code everything manually if there isn't a built-in physics solution.
     
  3. PrimeDerektive

    PrimeDerektive

    Joined:
    Dec 13, 2009
    Posts:
    3,090
    Couldn't you just parent a gameobject to your fps player object, position it slightly in front of the player model, and use that as a "pickup anchor point", then parent whatever rigidbody is "picked up" to the anchor?

    Or you could even just parent it directly to the fps player object, assuming you only allow a "pick up" action to happen if the object is in the center of the screen and the player is within a certain distance. But you'd probably want to set IgnoreCollision between the player and the object once its picked up, as it might cause problems if the player is super close when he picks it up.
     
    Last edited: Feb 24, 2011
  4. Cameron_SM

    Cameron_SM

    Joined:
    Jun 1, 2009
    Posts:
    915
    You'd have to code this yourself. The behaviour of picking up objects in HL2 isn't going to work with physics joints because it's not realistic in the slightest and physics joints, for the most part, model real world behaviour. There's probably a few special cases you'll need to script in to make it feel as "simple and effortless" as HL2.
     
  5. Myx

    Myx

    Joined:
    Nov 29, 2010
    Posts:
    196
    Hello there!

    I coded something similar for a prototype on telekinesis, the trick is to use forces to move the object to your wanted position.
    First of all you're going to need a target-point, if it's in the middle of the camera and you're using a first person set up then this should give you the point:

    Code (csharp):
    1.  
    2. Vector3 targetPoint = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width / 2, Screen.height / 2, 0));
    3.  
    Then offset the point in your camera's forward direction a bit.
    This code will give you a position in worldspace which is located in front of your camera.
    Now that you have a point in worldspace applying the forces is fairly simple.
    In the next piece of code I presume the heldObject is the object you're holding.

    Code (csharp):
    1.  
    2. public float mCorrectionForce = 50.0f;
    3. public float mStabilizationFactor = 0.5f;
    4. public float mPointDistance = 3.0f;
    5.  
    6. void FixedUpdate()
    7. {
    8.     Vector3 targetPoint = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width / 2, Screen.height / 2, 0));
    9.     targetPoint += Camera.main.transform.forward * mPointDistance;
    10.     Vector3 force = targetPoint - heldObject.transform.position;
    11.    
    12.     // Applying the force directly would result in a very wobbly experience
    13.     // Hence we add this check to stabilize the object depending on it's distance from the targetPoint
    14.     if(force.magnitude < mStabilizationFactor)
    15.         force = force.normalized * Mathf.Sqrt(force.magnitude);
    16.  
    17.     heldObject.rigidbody.AddForce(force * mCorrectionForce);
    18. }
    19.  
    That will cause your object to move towards the specified point and stabilize once it reaches it. Since the physics engine is the one doing the work the object will not move through walls and will collide properly.

    The mCorrectionForce variable will depend a bit on the object's weight, you'll have to experiment to find a good value.
     
  6. PrimeDerektive

    PrimeDerektive

    Joined:
    Dec 13, 2009
    Posts:
    3,090
    Is there a reason parenting wouldn't work? I've never tried it and don't have Unity on hand, just curious.
     
  7. PrimeDerektive

    PrimeDerektive

    Joined:
    Dec 13, 2009
    Posts:
    3,090
    @Myx, you really like lowercase m's, don't you? :)
     
  8. Myx

    Myx

    Joined:
    Nov 29, 2010
    Posts:
    196
    Hehe, I blame my education!
    You see at my university the coding-standard most teachers used was to put a lower case m when declaring member-variables. I quite like it, I can quickly see if a variable I'm changing will stay changed for the next time I need it or if it's only a local variable and will dissapear the moment I leave it.

    Could've been worse. Some of my teachers followed a standard where you indicate the type of the variable in the name. By that standard a float would be declared mfCoolValue. That quickly got tedious. :p
     
  9. Ahonek

    Ahonek

    Joined:
    Jan 12, 2011
    Posts:
    140
    glRotated()

    For a while, once I started programming OpenGL, I actually thought they might have misnamed this function. I didn't know why it was "rotated" in the past tense. Then I saw glRotatef() and everything clicked, lol
     
  10. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    838
    Okay, so I got around to trying everyone's various solutions...

    Myx, yours works really well, but it's just far too floaty for my purposes--the player needs to be able to move everything fairly precisely. Since I can't just parent the rigidbody directly, I'm trying a trick right now. Basically when the player grabs an object, it is a deactivated and a dummy is put in it's the place. The dummy has the same mesh filter/collider, but no rigidbody, and is parented to the player. This makes it part of a compound collider with the player's rigidbody...but this causes alot of problems too.

    I might just see if I can turn the player solely with forces (right now I'm just using the MouseLook, which modifies the transform directly, then set the grabbed object's velocity/angular velocity to the player's. Worth a try!
     
  11. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    Lemme change it up a bit. Instead of using force, which when I tested it made the object fly in circles around me. Lets use Lerp. Just Lerp it to us from it's current position. I also tagged in the mouse into it. get it centered in the screen and click, it will draw the object to you. click again, it will throw it.

    For a better result, when you pick up an object I turned gravity off for that object. then back on when you throw it. ;)

    Code (csharp):
    1.  
    2. var pointDistance = 3.0;
    3. var pullSpeed = 8.0;
    4. var throwForce = 40.0;
    5.  
    6. private var mGrabbed : boolean = false;
    7. private var heldObject : Transform;
    8.  
    9. function Update()
    10. {
    11.     if(mGrabbed){
    12.         var targetPoint = Camera.main.transform.TransformPoint(Vector3.forward * pointDistance);
    13.        
    14.         heldObject.position=Vector3.Lerp(heldObject.position, targetPoint, Time.deltaTime * pullSpeed);
    15.     }
    16.     if(Input.GetButtonDown("Fire2")){
    17.         if(!mGrabbed){
    18.             var ray : Ray = camera.ViewportPointToRay (Vector3(0.5,0.5,0));
    19.             var hit : RaycastHit;
    20.             if (Physics.Raycast (ray, hit)){
    21.                 print(hit.transform.name);
    22.                 if(hit.rigidbody){
    23.                     heldObject = hit.transform;
    24.                     heldObject.rigidbody.useGravity=false;
    25.                     mGrabbed = true;
    26.                 }
    27.             }
    28.         }else{
    29.             heldObject.rigidbody.velocity=Camera.main.transform.forward * throwForce;
    30.             mGrabbed = false;
    31.             heldObject.rigidbody.useGravity=true;
    32.         }
    33.     }
    34. }
    35.  
     
  12. Myx

    Myx

    Joined:
    Nov 29, 2010
    Posts:
    196
    @BigMisterB: The problem with that solution is that it will ignore collision since you just set the position directly.
    Turning off gravity does indeed help and after a closer look at my Telekinesis that's what I do aswell.
    Something else I did miss from the initial script is the focusing-function which will probably be relevant in this case.
    It stops objects from orbiting and simply centers them towards the point. Add this piece of code to my earlier script right before the force is added.

    Code (csharp):
    1.  
    2. heldObject.rigidbody.velocity = force.normalized * heldObject.rigidbody.velocity.magnitude;
    3.  
    Since you're simulating picking an object up you might also want to have the script disable rotation for the rigidbody.
    Play around a bit with the correction force aswell, higher values should reduce "floatiness".

    On second thought, moving the dampening could also be a good idea. Yes. Lets do that. Hmm, might aswell adjust the whole script while I'm at it.

    Code (csharp):
    1.  
    2. public float mCorrectionForce = 50.0f;
    3. public float mPointDistance = 3.0f;
    4.  
    5. void FixedUpdate()
    6. {
    7.     if(heldObject.contraints != RigidbodyConstraints.FreezeRotation)
    8.         heldObject.contraints = RigidbodyConstraints.FreezeRotation;
    9.     if(heldObject.useGravity)
    10.         heldObject.useGravity = false;
    11.  
    12.     Vector3 targetPoint = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width / 2, Screen.height / 2, 0));
    13.     targetPoint += Camera.main.transform.forward * mPointDistance;
    14.     Vector3 force = targetPoint - heldObject.transform.position;
    15.  
    16.     heldObject.rigidbody.velocity = force.normalized * heldObject.rigidbody.velocity.magnitude;
    17.     heldObject.rigidbody.AddForce(force * mCorrectionForce);
    18.  
    19.     heldObject.rigidbody.velocity *= Mathf.Min(1.0f, force.magnitude / 2);
    20. }
    21.  
    There we go. I wouldn't normally place the checks for gravity and rotation in the update, but rather in the function which actually sets heldObject. Don't forget to turn them back on when you release the object!
     
    Sethtek_Dev likes this.
  13. Iron-Warrior

    Iron-Warrior

    Joined:
    Nov 3, 2009
    Posts:
    838
    Alright, after cranking the correction force up a ton, I was able to get that smooth movement needed for my game, this is really great! I also will probably modify it to temporarily reduce the mass of the object I'm grabbing (to normalize the value so ALL objects move the same speed) and so you can't send other objects flying with the one you're holding.

    Thanks very much for all the help guys!
     
    Sethtek_Dev likes this.
  14. Mauri

    Mauri

    Joined:
    Dec 9, 2010
    Posts:
    2,664
    Myx' Script gives me an Error:

    What means that?
     
  15. picklednerd

    picklednerd

    Joined:
    Jul 7, 2012
    Posts:
    1
    Hey I'm working on a similar project
    My setup is a first person controller with an invisible game object (child of the camera) about 3 meters ahead that acts as a "beacon" that the "grabbed" object will follow. When the right mouse button is pressed, an object appears, when the left is pressed, the object drops.

    Here is the code that is attached to the object that's being placed:

    Code (csharp):
    1. #pragma strict
    2.  
    3. function Start () {
    4.  
    5. }
    6.  
    7. var correctionforce : float = 50.0f;
    8. var grabbed : boolean = true;
    9.  
    10. function Update () {
    11.     var target = GameObject.Find("NAME OF BEACON GAME OBJECT HERE");
    12.     var force : Vector3 = target.transform.position - transform.position;
    13.    
    14.     if(Input.GetButton("Fire1"))
    15.     {
    16.     grabbed = false;
    17.     print("released");
    18.     }
    19.    
    20.     if(grabbed == true)
    21.     {
    22.     print("grabbed");
    23.     if(rigidbody.freezeRotation == false)
    24.     {
    25.     rigidbody.freezeRotation = true;
    26.     }
    27.    
    28.     if(rigidbody.useGravity == true)
    29.     {
    30.         rigidbody.useGravity = false;
    31.         print("it works");
    32.     }
    33.        
    34.     rigidbody.velocity = force.normalized * rigidbody.velocity.magnitude;
    35.  
    36.     rigidbody.AddForce(force * correctionforce);
    37.    
    38.     rigidbody.velocity *= Mathf.Min(1.0f, force.magnitude / 2);
    39.     }
    40.    
    41.     if(grabbed == false)
    42.     {
    43.     rigidbody.useGravity = true;
    44.     }
    45.  
    46. }
    47.  
    I basically re-wrote Myx's script in JavaScript with a few additions.

    Here's the script attached to the beacon:

    Code (csharp):
    1. var cube : Rigidbody;
    2.  
    3. function Update () {
    4.  
    5. if(Input.GetButtonDown("Fire2"))
    6. {
    7.     print("new");
    8.     Instantiate(cube,transform.position,transform.rotation);
    9. }
    10. }
    11.  
    Hope this helps!
     
  16. johndii1491

    johndii1491

    Joined:
    Oct 12, 2014
    Posts:
    1
    I still can't figure out how to not depend on raycasting upon camera view, but rather on empty gameobject line of sight.

    for example: You put the empty object on the tip of your gun