- Home /
Creating a retractable grappling hook
I'm having trouble making a retracting grapple hook similar to the Hookshot featured in most Legend of Zelda games, where the hook retracts back to the Hookshot either if the player hits a button before the hook gets to its intended destination or if the hook reaches a specified distance without hitting a grapple point.
Right now, the hook I've created shoots out to a specified distance away from the player, then immediately 'snaps' into place when it's greater than that distance. Below is the FixedUpdate function where most of the action takes place:
void FixedUpdate()
{
hookDist = Vector3.Distance(subItem.transform.position, hook.transform.position);
Ray ray = Camera.mainCamera.ScreenPointToRay(new Vector3(Screen.width / 2, Screen.height / 2, 0));
RaycastHit hit;
rotSpeed = Mathf.Clamp(rotSpeed, 0f, 1f);
step = speed * Time.deltaTime;
hook.transform.Rotate(Vector3.forward * (angle * rotSpeed), Space.Self);
Debug.Log(rotSpeed);
Debug.Log(hookDist);
if (Input.GetMouseButton(0))
{
if (Physics.Raycast(ray, out hit))
{
GrappleToGrapplePoint();
}
}
if (isSpinning) {
rotSpeed += .2f * Time.deltaTime;
}
else
rotSpeed -= .5f * Time.deltaTime;
if (Input.GetMouseButtonDown(0)) {
isSpinning = true;
}
if (Input.GetMouseButtonUp(0)) {
isSpinning = false;
if (rotSpeed >= 1f) {
hook.rigidbody.velocity = subItem.transform.TransformDirection(new Vector3(0, 0, hookSpeed));
hook.transform.parent = null;
}
}
if (hookDist >= 18)
{
hook.rigidbody.velocity = Vector3.zero;
hook.transform.position = Vector3.Lerp(hook.transform.position, subItem.transform.position, retractSpeed * Time.time);
hook.transform.parent = subItem.transform;
}
}
If you read the script, you can see that I use Vector3.Lerp to retract the hook to its initial position without snapping into place. However, as stated before, the hook 'snaps' back to the empty GameObject's position.
As per usual, I may be missing something very small; I just can't figure out what it is at the time. Any relevant input would be much appreciated.
Thank you for taking the time to read this.
Answer by Chronos-L · Jun 10, 2013 at 09:42 AM
Look at my answer in this question. The problem (at least what I can see right now) lies with the Vector3.Lerp
in these:
if (hookDist >= 18)
{
hook.rigidbody.velocity = Vector3.zero;
hook.transform.position = Vector3.Lerp(hook.transform.position, subItem.transform.position, retractSpeed * Time.time);
hook.transform.parent = subItem.transform;
}
You should not use Time.time
for any Lerp
function. For Lerp( a, b, factor)
(Vector3.Lerp reference), if factor
is smaller than 0, you will get a
, if it is larger than 1
, then you will always get b
.
When you use retractSpeed * Time.time
, chances are the result will be way larger than 1, so the hook snaps right back to subItem.transform.position.
To fix this, you can use Vector3.MoveTowards, something like this:
hook.transform.position = Vector3.MoveTowards(hook.transform.position, subItem.transform.position, retractSpeed * Time.deltaTime);
On a non-related but important issue, you should use a variable/state to store the current state of the hook, from what I can see:
void FixedUpdate()
{
hookDist = Vector3.Distance(subItem.transform.position, hook.transform.position);
...
if (Input.GetMouseButtonDown(0)) {
//...
}
if (Input.GetMouseButtonUp(0)) {
//Shooting the hook
}
if (hookDist >= 18)
{
//Retract hook
}
}
When your hook starts retracting ( no more snapping after you fixed it ), when the hook distance becomes shorter than 18, it will not fully retract. You need a state to record the current state of the hook: standby, shooting, stick(if applicable), retracting.
Thanks for the help. I had forgotten there was a $$anonymous$$oveTowards function existed.
I just noticed the distance check wasn't working when the hook was < 18. All I did to fix it was set a Boolean value which sets to true when the hookDist is >= 18f; as long as the bool value is true, the hook will retract back to the subItem's position.
Unfortunately, I have another issue with resetting the hook's rotation where it's facing forward rather than the last rotation before launching. The only thing I know is that it requires me to rotate it back into position in the world space.
I've tried setting the rotation back to 'Quaternion.identity' and to 'Vector3.zero,' but nothing seems to work.
Edit: I just made a quick fix by setting the hook's rotation to that of the subItem when the hook starts retracting and setting the re-parenting to the subItem object after it retracts back to that position.
Your answer
Follow this Question
Related Questions
Why does IsSleeping() keep returning false? (Billiard Logic) 0 Answers
game object falling through plane when adding rigid body component 1 Answer
Attach rigidbody to ground below it 1 Answer
Issue with an object rotating strangely 2 Answers
Rigidbody: NullReferenceException: Object reference not set to an instance of an object 1 Answer