Announcement

Collapse
No announcement yet.

[Video] How to Make Fully Physics Functional Resizeable Grappling Hook / Tether

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    [Video] How to Make Fully Physics Functional Resizeable Grappling Hook / Tether

    Dear Community,

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    For Physics Tether for Skeletal Mesh Pawns See This New Tutorial + Video

    Tether / Grapple Hook for Skeletal Mesh Pawns
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


    In this tutorial I give you the simple and complete code for making a grappling hook / tether that is:

    1. fully physics functional with Kactors

    2. can be dynamically resized during game time, longer or shorter, and will pull up the attached object or lower it further down

    3. doesnt require a weapon, but you could adapt this code for a weapon





    I explain a lot of the properties of RB_Constraints in this tutorial so you can get your own setup going that does what you want

    You do not need any extra art assests for this tutorial, just
    Code:
    ParticleSystem'WP_LinkGun.Effects.P_WP_Linkgun_Altbeam'
    ~~~

    The Basics, RB Constraint

    Review the properties here please, and then you will be able to recognize all the properties in my RB Constraint class:

    http://udn.epicgames.com/Three/Physi...Reference.html
    ~~~

    Pawns?

    To use my code with a pawn, you must change the phyicss of the pawn to PHYS_RigidBody and then change it back again when you are done with the tether.

    Code:
    //while tether is active on pawn:
    pawn.setphysics(PHYS_RigidBody);
    ~~~

    My RB Constraint Class

    This class is focused on making the grappling hook / tether effect, where linear motion is constrained to within a certain distance of the point of attachment of the grappling hook in all dimensions.

    I restrict the swing as well, but that is not that important, the most important code is the restriction of the linear movement

    Here is my entire class that you see in this video for your own use:

    Code:
    class joyRBCWithFriction extends RB_ConstraintActorSpawnable
    	notplaceable;
    
    defaultproperties
    {
    	bNoDelete=false
    	
    	//~~~ Constraint Instance ~~~
    	Begin Object Class=RB_ConstraintInstance Name=MyConstraintInstance
    		
    	//enable friction
    	  bLinearXVelocityDrive = true
    	  bLinearYVelocityDrive = true
    	  bLinearZVelocityDrive = true
    	
    	  //target velocity is 0
    	  LinearVelocityTarget = (X=0,Y=0,Z=0);
    	
    	  //force to apply to reach velocity of 0
    	  //the amount of friction basically
    	  LinearDriveForceLimit = 3
    		
    	End Object
    	ConstraintInstance=MyConstraintInstance
    	
    	Begin Object Class=RB_ConstraintSetup Name=MyConstraintSetup
    		
    	  //the tether can be stretched
    	  //beyond normal limits
    		
    	  //damping is how much to reduce
    	  //spring-back after stretching
    		
    	  bLinearLimitSoft = true
    	  LinearLimitStiffness = 90
    	  LinearLimitDamping = 0
    		
    		
    	  //limit the rotation of the object
    	  bSwingLimited = true
    	  Swing1LimitAngle = 120
    	  Swing2LimitAngle = 120
    		
    	  /*
    	  //soft limit on rotation
    	 bSwingLimitSoft = false;
    	 SwingLimitStiffness = 10;
    	 SwingLimitDamping = 10;
    	  */
    		
    	  //~~~ Twist
    	  //bTwistLimited = true;
    	  //TwistLimitAngle = 60;
    	
    	  /*
    	  //soft twist limit
    	  bTwistLimitSoft = false;
    	  TwistLimitStiffness = 10;
      	  TwistLimitDamping = 10;
    	  */
    		
    	End Object
    	ConstraintSetup=MyConstraintSetup
    }
    there are two main parts to this class

    The RB_instance
    You need to set up the instance via defaultproperties and your own class because all the value are const.

    The need for the instance is not as great as for the constraintsetup, it is only to create some friction so that the object attached to the tether does not swing freely forever.

    The exact values I am using in this class are tailored to my Kactor ball that I am using, you might need to adjust the values dramatically.

    Notice how the velocity drive is turned on in all dimensions, so that means friction will be created in all directions.

    The target velocity is 0, or no motion, and the allowed force is the amount of friction to generate while the object is swinging on the tether.

    You could also use the velocity drive to GENERATE motion, by not having a target velocity of 0.

    More info on RB_Instance here


    The ConstraintSetup

    This is the really important part.

    There are three types of motion that can be constrained, or limited, to produce a variety of connections between physics objects.

    By knowing what they do you can create your own unique behaviors / joints / hinges etc!


    Linear motion in X Y and Z = forces/constrains the attached object to stay within a certain distance of the spawn location of the RB constraint. You can remove axis of movement completely by setting the LimitSize to 0.

    From the player pawn/obj class:
    Code:
    curTether.ConstraintSetup.LinearYSetup.LimitSize  = tetherlength;
    curTether.ConstraintSetup.LinearZSetup.LimitSize  = tetherlength;
    curTether.ConstraintSetup.LinearXSetup.LimitSize  = tetherlength;
    You don't see me setting these values in the RB Constraint class because the length of the tether is variable.

    Swing A direction of rotation of the object. If only swing is allowed the object cannot move from its fixed position, but it can rotate freely.

    Twist Another direction of rotation.

    So an object that has Swing and Twist allowed, and all the linear movement locked to 0 is a ball-socket joint, which is the default setup for a new instance of the RB_ConstraintSetup class.

    An object that has linear movement in 1 or all axis but no swing or twist allowed, can physically move but not rotate.

    And if you choose to have some limits on movement and some limits on rotation, well that is how you can make a whole variety of different joints and fun physics connections!

    Allowing the Tether to be Stretched Beyond Normal Limits
    Code:
     bLinearLimitSoft = true
    LinearLimitStiffness = 90
     LinearLimitDamping = 0
    The stiffness variable is the spring action, meaning once the object goes beyond the allowed limitsize for the linear motion, the tether will stretch and the try to pull the object back into the proper range, like a spring.

    Lower stiffness levels mean the object can go further away from the normal Limitsize with less resistance.

    The Dampening variable controls how forcefully the spring pulls the object back into the proper range. I wanted to make the pull back quite obvious so I left this at 0.

    Restricting Rotation
    Code:
    bSwingLimited = true
    Swing1LimitAngle = 120
    Swing2LimitAngle = 120
    Notice I choose to limit the swing because otherwise the ball looks like it is just spinning frictionless in the air and doesnt seem like it is attached to anything.

    ~~~

    The Code To Create / Destroy Tether

    This is the entire code to make the beam and the RB Constraint, you don't need a weapon class.

    You can test this without a weapon class if you setup some keys to activate/deactiave your tether.

    You can do this in udkgame/Config/DefaultInput.ini

    I am using a Kactor as my player object, but if you are using a Pawn, just remember to use
    Code:
    SetPhysics(PHYS_RigidBody)
    for the duration of time you want the pawn to swing around like my Kactor does in the video.

    In your Pawn/Player Object Class
    Code:
    //global class vars, put with other class vars
    var actor curTargetWall;
    var vector wallHitLoc;
    var joyRBCWithFriction curTether;
    var ParticleSystemComponent tetherBeam;
    
    function detachTether() {
      if (curTether != none) {
    		
    	  //RB
    	  curTether.termConstraint();
    	  curTether.destroy();
    	  curTether = none;
    		
    	  curTargetWall = none;
    		
    	  //beam
    	  tetherBeam.SetHidden(true);
    	  tetherBeam.DeactivateSystem();
    	  tetherBeam = none;
       }
    }
    
    function createTether() {
    	local vector hitLoc;
    	local vector hitNormal;
    	local actor wall;
    	local vector startTraceLoc;
    	local float tetherlength;
    	
    	//~~~ Trace ~~~
    	//pawn location + 100 in direction of player camera
    	startTraceLoc = Location + Vector(PlayerController.Rotation) * 100;
    	
    	//trace only to tether's max length
    	wall = Self.trace(hitLoc, hitNormal, 
              startTraceLoc + tetherMaxLength * Vector(PlayerController.Rotation) ,
              startTraceLoc);
    	
    	//replace this line with your own choice
    	//of what actors can be attached to
    	//if (!Wall.isa('Victorywall')) return;
    	
    	//attach to anything
    	if (!Wall.isa('Actor')) return;
    	
    	//Clear any old tether and beam
    	detachTether();
    
    	//store global vars for
    	//tether resizing functions
    	curTargetWall = Wall;
    	wallHitLoc = hitLoc;
    
    	//get length of tether from starting
    	//position of object and wall
    	tetherlength = vsize(hitLoc - Location);
    	
    	//~~~ RB Tether ~~~
    	//must spawn RB constraint near where you want movement to be limited
    	//in this case, where the tether hits the target object (HitLoc)
    	curTether = Spawn(class'joyRBCWithFriction', , , HitLoc, rot(0, 0, 0));
    	
    	//variable length tether
    	curTether.ConstraintSetup.LinearYSetup.LimitSize  = tetherlength;
    	curTether.ConstraintSetup.LinearZSetup.LimitSize  = tetherlength;
    	curTether.ConstraintSetup.LinearXSetup.LimitSize  = tetherlength;
    	
    	//~~~ Initialize RB Constraint ~~~
    	//Self = the player pawn / kactor / object
    	//which is the class this code belongs in
    	//if not using weapon
    	curTether.InitConstraint( Self, wall );
    	
    	
    	//~~~ beam ~~~ 
    	tetherBeam = WorldInfo.MyEmitterPool.SpawnEmitter(
    	   ParticleSystem'WP_LinkGun.Effects.P_WP_Linkgun_Altbeam', 
    	   Location + Vect(0,0,1) * 64, rotator(HitNormal), Self);
    	
    	//make sure beam is active
    	tetherBeam.SetHidden(false);
    	tetherBeam.ActivateSystem(true);
    	
    	//beam end point, this is very important
    	tetherBeam.SetVectorParameter('LinkBeamEnd', hitLoc );
    }
    In my default properties tetherMaxLength was set to 1200

    Things to note:

    Code:
    tetherBeam = WorldInfo.MyEmitterPool.SpawnEmitter(
      ParticleSystem'WP_LinkGun.Effects.P_WP_Linkgun_Altbeam', 
      Location + Vect(0,0,1) * 64, rotator(HitNormal), Self);
    Notice that the spawn location for the emitter is 64 unreal units directly above the player pawn/ kactor, that was for my code, but if you want to use a weapon you can attach this beam to a weapon socket.

    Notice that Self is used as the Actor that this emitter is attached to (the last parameter).

    This is why the beam stays with my kactor ball at all times wherever it goes.

    You must make sure “use local space” is checked in the Required module of your beam effect for this to work

    Code:
    //beam end point, this is very important
    tetherBeam.SetVectorParameter('LinkBeamEnd', hitLoc );
    For the linkgun beam, this is the way to set the end point, since a vector parameter has been set up. You can make your own beam effect and parameter and use your own name of course. Or if you dont have any parameters you could try SetBeamEndPoint.


    ~~~

    How to Make the Tether Shrink and Grow

    Here's the rest of the core code you need to do exactly what I do in the video!

    Code:
    function increaseTether() {
    //increase tether length
    	if (curTether == none) return;
    	
    	if (curTether.ConstraintSetup.LinearYSetup.LimitSize >= tetherMaxLength) return;
    	
    	curTether.ConstraintSetup.LinearYSetup.LimitSize  += 40;
    	curTether.ConstraintSetup.LinearZSetup.LimitSize  += 40;
    	curTether.ConstraintSetup.LinearXSetup.LimitSize  += 40;
    	
    	//re-initialize
    	curTether.InitConstraint( Self, curTargetwall );
    	
    }
    function decreaseTether() {
    	//decrease tether length
    	if (curTether == none) return;
    	
    	if (curTether.ConstraintSetup.LinearYSetup.LimitSize <= 10 ) return;
    	
    	if (vsize(wallHitLoc - Location) > 
    	  curTether.ConstraintSetup.LinearYSetup.LimitSize - 42 ) 
               {
    	  //move ball to within range
    	  Self.StaticMeshComponent.AddImpulse(
                    1200  * Vector (Rotator(wallHitLoc - Location ))
                  );
    	  return;  //<----- do not proceed past until above 
                               //condition is met
    	}
    	
    	curTether.ConstraintSetup.LinearYSetup.LimitSize  -= 40;
    	curTether.ConstraintSetup.LinearZSetup.LimitSize  -= 40;
    	curTether.ConstraintSetup.LinearXSetup.LimitSize  -= 40;
    	
    	//re-initialize
    	curTether.InitConstraint( Self, curTargetwall );
    }
    Things to note:
    Code:
    //re-initialize
    curTether.InitConstraint( Self, curTargetwall );
    If you dont call this then the changes to the constraintsetup will not have any affect on the current isntance of the tether.

    I am so glad it was this easy!

    Notice I am not destroy and remaking the RB constraint instance, I am just re-initializing the current one.

    This is why we had to store the wall that the beam hit as a global variable.

    The most important lines for retractable tether/ grappling hook
    Code:
    if (vsize(wallHitLoc - Location) > 
    	  curTether.ConstraintSetup.LinearYSetup.LimitSize - 42 ) 
               {
    	  //move ball to within range
    	  Self.StaticMeshComponent.AddImpulse(
                    1200  * Vector (Rotator(wallHitLoc - Location ))
                  );
    	  return;  //<----- do not proceed past until above 
                               //condition is met
    	}
    Notice that if the player pawn / kactor is NOT within range of the proposed new and smaller size of the tether, then

    the RB Constraint is NOT changed to be smaller!


    If that return was not there, then the ball/pawn could be locked outside of the limitSize of the joint, and that would not be good. It would cause the ball to be stuck in space basically.

    So as you implement this code for your own project, keep this in mind!

    You can ONLY make the limitsize smaller if you know the player object is going to still be within range.

    There is no such issue with making the tether longer as you can see in above code.

    Lower the value 1200 for the addImpulse if your Player pawn goes flying about using this code, or increase it if you are not getting enough lift off

    ~~~

    Player Controller Class

    In my player controller class I have something like this in my PlayerTick

    Code:
    if(keyisdownT){
    	pawn/Kactor.decreaseTether();
    }
    else if(keyisdownG){
    	pawn/Kactor.increaseTether();
    }
    So the value of adding or subtracting 40 is based on the fact that I am using playertick. If you are going to use a timer or something else you will need to adjust how quickly the tether size is changed. If you are not getting the results you want, verify how frequently decrease/increaseTether() is being called with `log.

    ~~~

    How to Make the Link Beam Less Curvy Near The Weapon Socket / Kactor Ball

    Make a copy of the link beam asset, and right click to get the entire path to copy into your code, and then, in Cascade:

    Source+, its the same for every beam in the whole emitter

    Beam Strength
    Look for the value of 1250, this is what makes the beam curve so much near its source point.

    I used 250 to have a much less pronounced curve which looked odd when tether was short.

    You could also try doing this via code:

    SetBeamSourceStrength


    ~~~

    It's That Simple

    That's all the code you need right there!

    Once you have this up and running you can easily experiment with modifying the RB ConstraintSetup values to make your own kind of joint / unique phyiscs experience!

    Enjoy!



    Rama

    #2
    Rama i have to say im noticing alot of really useful tuts coming from yourself these past few weeks.

    Good stuff. Do me a favour though, dont get a girlfriend so you can keep these coming

    Comment


      #3
      hahhahaha I regret to inform you it might already be too late for that

      But I will try to keep doing the tutorials anyway



      Rama

      Comment


        #4
        bumping as community resource

        Rama

        Comment


          #5
          This is all I could think about when watching you trying to get the ball on the final platform: http://www.youtube.com/watch?v=mYo5xYiyRuk

          But these are really cool tutorials, not sure how you figure all this stuff out but I'm glad you do. Keep it up!

          Comment


            #6
            Hahhaha that's a funny vid

            Those kids make it look easy tho

            I was really struggling with FRAPS running and all



            Rama

            Comment


              #7
              Bumping for Community

              Rama

              Comment


                #8
                This tutorial gave me an idea for my game. Right now when you get another players ship low on health instead of blowing it up you just disable them for a short time, where the engines turn off and they can't fly, and then to board there ship you just run into them and the collision loads you onto the inside of two ships that are docked together. Sometimes though its hard to run into them in space as quick as you need to before their ship is up and running again. I'm wondering if I can add this to my vehicle (basically the Cicada) as a second weapon that you can switch to and have it fire off your tether that grabs them and pulls you into them to make the collision that much easier and it would be fun.

                I'm just not sure how hard it would be to add a second weapon to the cicada like that.

                What are your thoughts?

                Comment


                  #9
                  Well for starters, just have your tether launch with the press of a button

                  so let's say the middle mouse button or the H key

                  now all the code I've provided you with is already ready to go

                  except you'll want the tether to have a longer max range

                  Rama

                  info on binding middle mouse or other key to the createTether()

                  PS: in other words you dont need the weapon interface at all, and for testing purposes it would only get in the way.

                  you could always limit when you can fire tethers some other way or leave it for some fun multiplayer activity. I am not sure what the replication issues are for multiplayer games with rb constraints but you'll want to look intot hat

                  Comment


                    #10
                    Ya key binding I've done a lot of. Alright I will have a go at it and see what I can do, I guess I don't need the vehicle to detect it as a second weapon if I just give it its own key, I shouldn't have to modify the code too much.

                    Will tell you how it goes

                    Comment


                      #11
                      well one issue you will have I am addressing in a tutorial that I am creating RIGHT NOW

                      that is almost done

                      which is the fact that both of your ships will be MOVING while tethered

                      but hey

                      no worries

                      that's what my next tutorial is about



                      Rama

                      PS: ETA 30 min to 1 hour

                      Comment


                        #12
                        Well I'm pretty tired so take your time, I will dive into this tomorrow. I'm not in the mood for everything to not work tonight haha

                        It wouldn't really be that bad actually if both moved. It might even make more sense since it is space and everything, but I would probably try and not let that happen if possible so thanks for the future tutorial

                        Comment


                          #13
                          I'm just trying to test if createTether() is working and I'm getting this error when I build it.

                          Code:
                          startTraceLoc = Location + Vector(PlayerController.Rotation) * 100;
                          'Vector' conversion: Bad or missing expression.

                          Its all the same as your code so I'm not sure why that would be.

                          Comment


                            #14
                            you have to replace playercontroller with your exact reference to your playercontroller

                            the code is using the player's camera view rotation, so plug in your custom player controller var

                            Rama

                            Comment


                              #15
                              Right of course. I shouldn't post things late at night because I post simple questions that I'm to delerious to figure out...

                              Anyway everything pretty much works now but sometimes when I get to close to something it will rip my player out of the ship. (I have all the code in the ship)

                              I'm not sure whats causing this but I was thinking it might be better if I have the beam attach to a socket on the exterior of the ship, and see if that helps.

                              So I assume I change this line.

                              Code:
                              tetherBeam = WorldInfo.MyEmitterPool.SpawnEmitter(
                                ParticleSystem'WP_LinkGun.Effects.P_WP_Linkgun_Altbeam', 
                                Location + Vect(0,0,1) * 64, rotator(HitNormal), Self);
                              My question is, whats the right way to add the socketname or location to this code? I'm just not sure the syntax it wants to have it spawn from a socket.

                              Thanks!

                              Comment

                              Working...
                              X