Physics based grappling hook not acting as it should

Hey all,

I’m working on a movement focused FPS game using Rapier.js for physics and I’m running into a very specific issue.

The games movement is very heavily inspired by Titanfall 2 and therefore I am trying to implement a grappling hook mechanic that works similarly to that game, here’s an example of how it looks in that game

I have gotten the basic mechanic of it down, shooting the ray to the point, getting the camera dir in relation to it, all of that fun stuff, but theres one massive issue im running into: the way i am pulling the character towards the point feels so slow and honestly just bad. The player can easily just negate the pull of the grappling hook, ive had to reduce the this.grappleSpeedInfluence and this.grappleHeightInfluence to be numbers just over or smaller than 1 as otherwise the player gains so much speed that its uncontrollable (see those variables in the code below).

Titanfall pulls the player in faster, lets them gain height faster
, but they gain less speed overall, seems like an almost impossible task to fix

Here’s the bit of code that’s important and an example video to go along side it

const playerPos = this.playerParent.translation()

const difference = this.grappleDifferenceVec3.set(
   this.grappleHit.x - playerPos.x,
   this.grappleHit.y - playerPos.y,
   this.grappleHit.z - playerPos.z
).normalize()

const pullDir = this.grapplePullDirVec3.set(
   (difference.x + this.cameraDir.x) / 2,
   (difference.y + this.cameraDir.y) / 2,
   (difference.z + this.cameraDir.z) / 2
).normalize()

const linvel = this.playerParent.linvel()
this.playerParent.setLinvel({
   x: linvel.x + (pullDir.x * this.grappleSpeedInfluence),
   y: linvel.y + (pullDir.y * this.grappleHeightInfluence),
   z: linvel.z + (pullDir.z * this.grappleSpeedInfluence)
})

Thanks in advance for any help :slight_smile:

1 Like

Edit:
I found out that they use a clamp on circular motion to give it that fluid swing, however it still isn’t acting perfectly, here’s the updated code

const playerPos = this.playerParent.translation()

const difference = this.grappleDifferenceVec3.set(
    this.grappleHit.x - playerPos.x,
    this.grappleHit.y - playerPos.y,
    this.grappleHit.z - playerPos.z
).normalize()

const pullDir = this.grapplePullDirVec3.set(
    (difference.x + this.cameraDir.x) / 2,
    (difference.y + this.cameraDir.y) / 2,
    (difference.z + this.cameraDir.z) / 2
).normalize()

const linvel = this.playerParent.linvel()
const pushDir = this.currentVelocity <= 0.1 ? 0.1 : this.currentVelocity / 50 

this.playerParent.setLinvel({
    x: linvel.x + (pullDir.x / pushDir),
    y: linvel.y + (pullDir.y / pushDir),
    z: linvel.z + (pullDir.z / pushDir)
})

this.currentGrappleLength -= this.currentVelocity / 50

difference.multiplyScalar(this.currentGrappleLength)
this.playerParent.setTranslation({
    x: this.grappleHit.x - difference.x,
    y: this.grappleHit.y - difference.y,
    z: this.grappleHit.z - difference.z
})