Announcement

Collapse
No announcement yet.

[VIDEO][CODE] How to Attach RB Constraint / Tether / Hook to -Moving- Objects

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

    [VIDEO][CODE] How to Attach RB Constraint / Tether / Hook to -Moving- Objects

    Dear Community,

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

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


    This tutorial shows you how you can set up an RB Constraint like a tether or grappling hook, to be attached to a moving object.

    1. The object on the tether will pick up the momentum, including angular momentum, of the object the tether is attached to

    2. The object will be forced to move along with the main object, just like you'd expect a tether/grappling hook system to do

    3. The drawing of a beam particle system can be made to following the moving object as well, shown in the code below and the video




    ~~~

    The Basic Tether / Grappling Hook / RB Constraint

    Here's all the code you need to be able to generate RB Constraints, including the RB Constraint class I created and use in the video

    Grappling Hook / Tether System, Dymanically Create RB Constraints


    ~~~

    How to Make the Tether / RB Constraint Move and Convey Momentum

    Continuing from the code in the link above, here is how you make the RB Constraint that you create to simulate your tether/grappling system actually move with the object it attaches to, and convey the momentum of that object.

    Code:
    //In your player object class
    
    //this tether does not require a weapon
    //it fires from center of player camera view
    //could turn this into a weapon if you wanted though
    
    //global class vars
    var actor curTargetWall;
    var vector wallHitLoc;
    var joyRBCWithFriction curTether;
    var ParticleSystemComponent tetherBeam;
    var bool tetheredToMovingWall;
    
    
    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 ~~~
    	startTraceLoc = Location + Vector(joyfulController.Rotation) * 100;
    	
    	//shooting tether from center of player camera, playerController
    	//replace playerController with your exact reference
    	wall = Self.trace(hitLoc, hitNormal, 
    		   startTraceLoc + tetherMaxLength * Vector(Playercontroller.Rotation) ,
                       startTraceLoc
                      );
    	
    	//replace with your "tether-able" class
    	//or leave to tether to anything
    	if (!Wall.isa('Actor')) return;
    	
            //save global vars for later use in other funcs
    	curTargetWall = Wall;
    	wallHitLoc = hitLoc;
    	
    	//get length of tether from starting
    	//position of object and wall
    	tetherlength = vsize(hitLoc - Location);
    	//~~~
    	
    	//Clear any old tether
    	detachTether();
    	
    	//~~~ 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));
    	//see previous tutorial re: joyRBCWithFriction
    	
    	//~~~~~~~~ Attach to obj ~~~~~~~~~
    	curTether.SetHardAttach(True);
    	curTether.SetBase(curTargetWall);
    	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    	
    	//variable length tether
    	curTether.ConstraintSetup.LinearYSetup.LimitSize  = tetherlength;
    	curTether.ConstraintSetup.LinearZSetup.LimitSize  = tetherlength;
    	curTether.ConstraintSetup.LinearXSetup.LimitSize  = tetherlength;
    	
    	//~~~ Init RB Constraint ~~~
    	//Self = the player object whose class
    	//this code is in
    	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);
    	tetherBeam.SetHidden(false);
    	tetherBeam.ActivateSystem(true);
    	
    	//replace with a test relative to your game
    	//to determine if object tethered to can move
    	if (true) {
    		tetheredToMovingWall = true;
    	}
    	else {
    		tetheredToMovingWall = false;
    		//beam end point
    		tetherBeam.SetVectorParameter('LinkBeamEnd', hitLoc);
    	}
    }
    Simulated Function tick( Float DeltaTime ) {
    //=== TETHER ====
    	if (curTether != none && tetherBeam != none && tetheredToMovingWall) {
    	  //beam end point
    	  tetherBeam.SetVectorParameter('LinkBeamEnd', 
                       curTargetWall.Location);
    	}
    }
    
    defaultproperties
    {
    tetherMaxLength = 3000 //adjust to game-specific needs	
    }
    Note that if the object is known to be able to move, then the visual beam ending point needs to be updated in Tick.

    So the goal is to only use the tick, which runs every moment of game time, when we absolutely need to.

    So that's why I have a bool for whether or not the object that has been attached to can actually move

    You will have to determine whether an object can move for your own code

    And if the objects you tether to can ALWAYS move you don't need the if case

    Again review code in the previous tutorial on grappling beam / tether as this tutorial is really an extension of that one



    ~~~

    The Critical Code

    Code:
    //Attach to obj
    curTether.SetHardAttach(True);
    curTether.SetBase(curTargetWall);
    please please note

    you MUST call actor.SetHardAttach(true);

    BEFOOOOOOORE you call actor.setbase()


    seriously

    it's a must



    and

    calling setHardAttach is how you cause the transfer of momentum between the main moving object and the attached player object.

    ~~~

    Appearance

    Code:
    //~~~ beam ~~~ 
    	tetherBeam = WorldInfo.MyEmitterPool.SpawnEmitter(
    		ParticleSystem'WP_LinkGun.Effects.P_WP_Linkgun_Altbeam', 
    		Location + Vect(0,0,1) * 64, rotator(HitNormal), Self);
    	tetherBeam.SetHidden(false);
    	tetherBeam.ActivateSystem(true);
    Change the number 64 based on the player object you are using, or attach the beam to a weapon socket. 64 is the height above the center of the player obj where beam effect with attach to the player object.

    64 is just a value to get you started

    Note the last parameter of the spawnEmitter, which is SELF, indicating that the particle system should attach itself to the ball

    make sure "use local space" is set to true in your particle system's Required module in order for this to work



    ~~~

    Summary

    Using this code you can tether two moving UTVehicles together (space ships for example),
    or two pawns that are both in PHYS_RigidBody,
    or two kactors / the traditional physics objects,
    or any two objects that can enter PHYS_RigidBody really!

    (pssst, you could also tether MULTIPLE moving objects together )

    In my video the kactor ball is tethering to a spawnable move-able static mesh

    So see you have all sorts of options of how you use this system to create dynamic physics interactions between the world objects in your game!

    Enjoy!

    Rama

    #2
    Originally posted by evernewjoy View Post
    Using this code you can tether two moving UTVehicles together (space ships for example),
    Indeed you can, in fact I recommend space ships

    I had gotten side tracked adding the last of the 'new stuff' to my game for now (wanting to get to a certain point before making a video displaying everything, like the gravity as I said I would and will in a couple weeks) and writing up my dissertation, and now it is time to go back through the last main bits that I got working but iron them out to where they are 100% working, and thats when I hit major problems, especially when I tested this on a server.

    But after 4 hours of constantly changing my code and referencing this page, it all works! Even online! (just added reliable server simulated function to the functions and made minimal changes to the functions themselves )

    So I have to say thank you so much Rama another one of your threads that has saved me

    The only very minor issue I am having is replicating the tether particle itself. The server gets accessed none for 'MyEmitterPool' so then tetherbeam gets accessed none and the server doesn't display the tether, which means the clients see nothing then.

    Is there any other way to do it or do you know how to replicate that tether properly?

    Other than that it works like a charm, really really pleased.

    Comment


      #3
      Ok I figured it out after a full day of working on it.

      Heres all the changes I made in case anyone wants to do something similar.

      Also I already realize this is not the most efficient way as I have other things I'm replicating that I've done differently but for what this is, this was just the only way I managed to get it to work how I want it to so here it is.

      A movable online tether that automatically pulls you into the ship you shoot, or into a static object.

      Code:
      var repnotify bool tetheredToMovingWall;
      var vector tetherhitNormal;
      var repnotify vector hitLoc;
      var repnotify actor curTargetWall;
      var vector wallHitLoc;
      
      replication
      {
      	if (bNetDirty)
      		tether,tetheredToMovingWall, hitLoc,curTargetWall;
      
      ....
      }
      
      
      simulated event ReplicatedEvent(name VarName)
      {
      	if (VarName == 'tether')
      	{
      		
      		if(tether)
      		{
      			tetherBeam = WorldInfo.MyEmitterPool.SpawnEmitter(ParticleSystem'Ship.Effects.Teather',Location , rotator(tetherhitNormal), Self);
      		
      			if(!tetheredToMovingWall)
      				tetherBeam.SetVectorParameter('LinkBeamEnd', hitLoc );
      
      			tetherBeam.SetHidden(false);
      			tetherBeam.ActivateSystem(true);
      		}
      		else if(!tether && !tetheredToMovingWall)
      		{
      			tetherBeam.SetHidden(true);
      			tetherBeam.DeactivateSystem();
      			tetherBeam = none;
      
      		}
      	}
      	else
      	{
      		Super.ReplicatedEvent(VarName);
      	}
      }
      
      reliable server simulated function detachTether() {
        if (curTether != none) {
      		tether=!tether;
      	  //RB
      		tetheredToMovingWall = false;
      	  curTether.termConstraint();
      	  curTether.destroy();
      	  curTether = none;
      		
      	  curTargetWall = none;
      	  //beam
      		if(tetherBeam != none)
      		{
      			  tetherBeam.SetHidden(true);
      			  tetherBeam.DeactivateSystem();
      			  tetherBeam = none;
      		}
         }
      }
      
      
      reliable server simulated function createTether() {
      	
      	
      	local actor wall;
      	local vector startTraceLoc;
      	local float tetherlength;
      	PC = HBController(Controller);
      	//~~~ Trace ~~~
      	//pawn location + 100 in direction of player camera
      	startTraceLoc = Location + vector(self.Rotation)  * 100;
      	
      	//trace only to tether's max length
      	wall = Self.trace(hitLoc, tetherhitNormal,startTraceLoc + tetherMaxLength * Vector(self.Rotation) ,startTraceLoc);
      
      	if(wall != none)
      	{
      	//attach to anything
      	if (!Wall.isa('Actor')) return;
      	if(Wall.IsA('Pawn'))
      	{
      		
      		//Clear any old tether
      	//detachTether();
      	
      	//~~~ RB Tether ~~~
      	curTargetWall = Wall;
      	wallHitLoc = hitLoc;
      
      	tetherlength = vsize(hitLoc - Location);
      	//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));
      	//see previous tutorial re: joyRBCWithFriction
      	
      	//~~~~~~~~ Attach to obj ~~~~~~~~~
      	curTether.SetHardAttach(True);
      	curTether.SetBase(curTargetWall);
      	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      	
      	//variable length tether
      	curTether.ConstraintSetup.LinearYSetup.LimitSize  = tetherlength;
      	curTether.ConstraintSetup.LinearZSetup.LimitSize  = tetherlength;
      	curTether.ConstraintSetup.LinearXSetup.LimitSize  = tetherlength;
      	
      	//~~~ Init RB Constraint ~~~
      	//Self = the player object whose class
      	//this code is in
      	curTether.InitConstraint( Self, wall );
      	tether=!tether;
      	
      	if(WorldInfo.MyEmitterPool.SpawnEmitter(ParticleSystem'Ship.Effects.Teather',Location , rotator(tetherhitNormal), Self) != none)
      	{
      		tetherBeam = WorldInfo.MyEmitterPool.SpawnEmitter(ParticleSystem'Ship.Effects.Teather',Location , rotator(tetherhitNormal), Self);
      		//tetherBeam = WorldInfo.MyEmitterPool.SpawnEmitterMeshAttachment(ParticleSystem'WP_LinkGun.Effects.P_WP_Linkgun_Altbeam',self,'Gun_Socket_01',,);
      		//make sure beam is active
      		tetherBeam.SetHidden(false);
      		tetherBeam.ActivateSystem(true);
      	}
      	
      		tetheredToMovingWall = true;
      	
      	}
      	else if(!Wall.IsA('Pawn'))
      	{
                    ..... this is for static wall, just a simpler version of what i have above, closely resembles original code...
              }
      }
      
      reliable server simulated function decreaseTether() {
      	//decrease tether length
      	if (curTether == none) return;
      	
      	if (curTether.ConstraintSetup.LinearYSetup.LimitSize <= 10 ) return;
      	//`log("Part1");
      	if (vsize(curTargetWall.Location - Location) > curTether.ConstraintSetup.LinearYSetup.LimitSize - 42 ) 
      	{
      		//`log("Part2");
      	  //move ball to within range
      	  Self.collisionComponent.AddImpulse(1200  * Vector (Rotator(curTargetWall.Location - 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 );
      }
      
      
      event Tick(DeltaTime)
      {
              ......
      
      
              if(tether)
      		decreaseTether();
      	if (tether && tetheredToMovingWall) {
      	  //beam end point
      		tetherBeam.SetVectorParameter('LinkBeamEnd', curTargetWall.Location);
      	}
      
           ......
      
      }
      Once you understand Rama's code understanding my changes is pretty easy but boy was it hard for me to figure out... oh well now I'm just that much better.

      Hope someone finds it useful

      Comment


        #4
        Thanks for sharing your code!



        Rama

        Comment


          #5
          No problem

          Have a slight problem with a related issue though with this tether and was wondering if you had any ideas.

          When you tether to another ship and it pulls you in, I have code that does a server travel for you and the ship you tethered to, in order to send everyone to the 'interior' of the ships.

          See when a ship gets all shot up instead of blowing up I just have it 'disabled' where the particles all turn off, and the controls all turn off so the player is kinda stuck floating for like 30 seconds.

          This is the time when the attacking ship should tether themselves to the other players ship to try and board them.

          So I need a way to check to see if I can tether to it or not. In psuedocode it would be something like

          function createtether()
          {

          is the ship i'm about to tether too have isdisabled set to false?
          then don't tether


          is isdisabled true on the other ship?
          then do all the normal tether stuff and pull yourself into it



          }

          I really need to access the variable inside the ship that it shoots but I have no idea how this could be possible...

          Does this make sense?

          Comment


            #6
            If I wanted to create a system that used this except at faster speeds will it break? Like fast enough speeds that it could launch the pawn high up into the air or could I create the illusion of going that fast so that a player will still have ease of control even if it isn't moving that fast?

            Comment


              #7
              If you make it go faster it will not break unless you specifically set:

              //in RB_Constraint, see first tutorial in this series
              bLinearBreakable
              LinearBreakThreshold




              Rama

              Comment


                #8
                Hey Rama how have you been?

                I'm trying to expand this code and make it even better but there are a few hurdles in my way that I was hoping you could help with. Or at least give me some ideas.

                I have a little custom projectile, very simple, that shoots out of my vehicle. I'm trying to take this code which I've already customized a little and attach it to the end of my projectile. Then the projectile will fire out and stick to the first thing it hits and the rest will work how it already works. You could even send like a sin wave or something through the particle beam to make it look like a rope while the projectile is flying away. But the particle beam needs to be running between them the whole time like a hook with a lead line trailing back to the vehicle.

                Right now I'm not sure how to:

                1. Get the the projectile to stick to another object, such as a moving object (enemy vehicle)

                2. Have this tether attach to the projectile, and then once its stuck, get it to act as it is now.

                Thoughts?

                Comment


                  #9
                  I dont know about sticking the projectile to a target, but

                  to get the beam to follow the projectile,

                  every tick, update the beam's target location to be the location of the projectile!


                  I'd probably just explode the projectile on contact with this target and then pass the info to the beam to now stay focus on the identified target that the projectile hit.

                  Nice to hear from you!

                  Comment

                  Working...
                  X