Need help with ledge climbing script
Hello, I'm new to game development, but I'm an experienced coder. I'm trying to build a system to free climb ledges, jump from ledge to ledge, etc..
I have included my script here and at the bottom of my post: https://pastebin.com/R95G5hY8
The ledges aren't always horizontal, they can be slanted as well. So I managed to make the player's hands and feet snap to an object at the correct height.
As you can see in this picture, the player's hands will snap to the green line.
How I'm doing this is using eg for the left hand
anim.SetIKPosition(AvatarIKGoal.LeftHand, leftHandPos); anim.SetIKRotation(AvatarIKGoal.LeftHand, leftHandRot);
I know it is a very crude way, but it's the only way I found to make it work.
The object the player hangs on to, is a prefab. The green line is where the player's hands will go and the blue object is where the player's feet will go.
The script uses raycasts to determine where a player go grab onto.
Now my problems are:
How can I implement an animation from the idle/running state to jumping towards the ledge and have to player's hands and feet go to the position shown in the picture?
Once the player is in place, how can I play an 'idle' hanging animation. What happens now is after the player is snapped to the correct place, they drop down (gravity I assume?)
I want it to be possible to shimmy along the edge as well, how can I implement an animation to go left and right, whilst keeping the players hands on the ledge and feet at the correct position? So the player moves alogn the ledge nicely (with hands and feet at the correct place), even if the ledge is slanted (so one hand will be higher than the other).
I tried using MatchTarget, but it didn't work as you can only move one IK at a time. Not 4 IK's (hands and feet) as would be necessary in this case.
Thanks very much! I'm eager to learn more about Unity!
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class IKSnap : MonoBehaviour
{
public bool useIK;
public bool leftHandIK;
public bool rightHandIK;
public bool leftFootIK;
public bool rightFootIK;
public Vector3 leftHandPos;
public Vector3 rightHandPos;
public Vector3 leftFootPos;
public Vector3 rightFootPos;
public Quaternion rightHandRot;
public Quaternion leftHandRot;
public Quaternion rightFootRot;
public Quaternion leftFootRot;
public Vector3 playerRot;
private Animator anim;
private Rigidbody rigidbody;
private bool hasDoneMatch;
public float leftHandWeight = 1f;
public float rightHandWeight = 1f;
private Vector3 curLeftHandPos;
private Vector3 curRightHandPos;
private bool climbMode;
// Use this for initialization
void Start ()
{
anim = GetComponent<Animator>();
rigidbody = GetComponent<Rigidbody>();
useIK = true;
}
void FixedUpdate ()
{
// Create 45 degree angles for the raycasts
Vector3 right45 = (transform.forward + transform.up + (transform.right / 2)).normalized;
Vector3 left45 = (transform.forward + transform.up + (-transform.right / 2)).normalized;
Vector3 right45Foot = (transform.forward + (transform.right)).normalized;
Vector3 left45Foot = (transform.forward + (-transform.right)).normalized;
Debug.DrawRay(transform.position + new Vector3(0.0f, 1.0f, 0), left45, Color.cyan);
Debug.DrawRay(transform.position, right45Foot, Color.cyan);
// right mouse click to toggle climbing mode
if (Input.GetMouseButtonDown(1))
{
climbMode = !climbMode;
}
if (!climbMode)
{
leftHandIK = false;
rightHandIK = false;
leftFootIK = false;
rightFootIK = false;
anim.SetBool("ledge", false);
rigidbody.useGravity = true;
anim.SetFloat("HandWeight", 0);
hasDoneMatch = false;
return;
}
RaycastHit LHit;
RaycastHit RHit;
playerRot = transform.eulerAngles;
Debug.DrawRay(transform.position + new Vector3(0.0f, 1.0f, 0.0f).normalized, right45, Color.green);
Debug.DrawRay(transform.position + new Vector3(0.0f, 1.0f, 0.0f).normalized, left45, Color.red);
LayerMask layerMask = 1 << LayerMask.NameToLayer("Ledge"); // only check for collisions with layerX
//Raycast from the player to detect if it hits with the Ledge prefab
if (Physics.Raycast(transform.position + new Vector3(0.0f, 1.0f, 0), left45, out LHit, 1.5f, layerMask))
{
if (LHit.transform.tag == "Ledge")
{
leftHandIK = true;
leftHandRot = Quaternion.Euler(-10, transform.eulerAngles.y, 0);
// Get the location of the green bar of the prefab, so we know where to put the player's hand
if (Physics.Raycast(LHit.point, Vector3.up, out LHit, 1.5f))
{
leftHandPos = new Vector3(LHit.point.x, LHit.point.y, LHit.point.z - 0.05f);
}
}
}
else
{
leftHandIK = false;
}
// Same as left hand
if (Physics.Raycast(transform.position + new Vector3(0.0f, 1.0f, 0), right45, out RHit, 1.5f, layerMask))
{
if (RHit.transform.tag == "Ledge")
{
rightHandIK = true;
rightHandRot = Quaternion.Euler(-10, transform.eulerAngles.y, 0);
if (Physics.Raycast(RHit.point, Vector3.up, out RHit, 1.5f))
{
rightHandPos = new Vector3(RHit.point.x, RHit.point.y, RHit.point.z - 0.05f);
}
}
}
else
{
rightHandIK = false;
}
anim.SetBool("ledge", leftHandIK && rightHandIK);
// If there is a point for the left and right hand, look for a point for the feet
if (leftHandIK && rightHandIK)
{
rigidbody.useGravity = false;
RaycastHit LFootHit;
RaycastHit RFootHit;
if (Physics.Raycast(transform.position, left45Foot, out LFootHit, 1.5f))
{
if (LFootHit.transform.tag == "LedgeFootPoint")
{
leftFootPos = new Vector3(LFootHit.point.x, LFootHit.point.y + 0.5f, LFootHit.point.z - 0.15f);
leftFootIK = true;
}
else
{
leftFootIK = false;
}
}
else
{
leftFootIK = false;
}
if (Physics.Raycast(transform.position, right45Foot, out RFootHit, 1.5f))
{
if (RFootHit.transform.tag == "LedgeFootPoint")
{
rightFootPos = new Vector3(RFootHit.point.x, RFootHit.point.y + 0.5f, RFootHit.point.z - 0.15f);
rightFootIK = true;
}
else
{
rightFootIK = false;
}
}
else
{
rightFootIK = false;
}
}
else
{
leftFootIK = false;
rightFootIK = false;
rigidbody.useGravity = true;
}
}
void Update()
{
}
void OnAnimatorIK()
{
if (useIK)
{
//if (Math.Abs(anim.GetFloat("HandWeight") - 1.0f) < 0.05f)
//{
// return;
//}
//Move the hands to the location over time in code
anim.SetFloat("HandWeight", 1, 0.1f, Time.deltaTime * 0.12f);
anim.SetIKPositionWeight(AvatarIKGoal.LeftHand, anim.GetFloat("HandWeight"));
anim.SetIKPositionWeight(AvatarIKGoal.RightHand, anim.GetFloat("HandWeight"));
anim.SetIKPositionWeight(AvatarIKGoal.LeftFoot, anim.GetFloat("HandWeight"));
anim.SetIKPositionWeight(AvatarIKGoal.RightFoot, anim.GetFloat("HandWeight"));
if (leftHandIK)
{
anim.SetIKRotationWeight(AvatarIKGoal.LeftHand, 1f);
anim.SetIKPosition(AvatarIKGoal.LeftHand, leftHandPos);
anim.SetIKRotation(AvatarIKGoal.LeftHand, leftHandRot);
}
if (rightHandIK)
{
anim.SetIKRotationWeight(AvatarIKGoal.RightHand, 1f);
anim.SetIKPosition(AvatarIKGoal.RightHand, rightHandPos);
anim.SetIKRotation(AvatarIKGoal.RightHand, rightHandRot);
}
if (leftFootIK)
{
anim.SetIKRotationWeight(AvatarIKGoal.LeftFoot, 1f);
anim.SetIKPosition(AvatarIKGoal.LeftFoot, leftFootPos);
anim.SetIKRotation(AvatarIKGoal.LeftFoot, leftHandRot);
}
if (rightFootIK)
{
anim.SetIKRotationWeight(AvatarIKGoal.RightFoot, 1f);
anim.SetIKPosition(AvatarIKGoal.RightFoot, rightFootPos);
anim.SetIKRotation(AvatarIKGoal.RightFoot, rightHandRot);
}
}
}
public bool OnLedge
{
get
{
if (leftHandIK && rightHandIK)
{
return true;
}
return false;
}
}
}
Answer by Antao98 · Feb 19, 2019 at 11:32 PM
Hello, I know this post is kind of old but did you ever finish the project? I suggest you look into blend trees and setting parameters in the animator. I'm currently trying to make a climbing system from scratch and am runnng into some problems, so thas how I found this post...
Your answer
Follow this Question
Related Questions
Animation not consistent 0 Answers
Animation Legacy behavior -> Mecanim animation 1 Answer
No Loop Time Options in Animation Inspector 1 Answer
Can't seem to get my animations to work proper 1 Answer
Humanoid Animations 0 Answers