Good day folks. So as the title suggests, I’m currently attempting to program my own Parkour System, following a tutorial. However, I currently ran into an issue, and I need some help
Part of scripting my animations is something Unity calls as ‘Target Matching’, which is basically Unity’s attempt to get parts of my player’s avatar to be somewhere specific at a specific moment. For this one, it’s my hands/legs being at specific positions for specific animations.
However, Unity is giving me this error:
Calling Animator.MatchTarget while in transition does not have any effect.
UnityEngine.Animator:MatchTarget (UnityEngine.Vector3,UnityEngine.Quaternion,UnityEngine.AvatarTarget,UnityEngine.MatchTargetWeightMask,single,single)
ParkourController:MatchTarget (ParkourAction) (at Assets/PolygonFantasyHeroCharacters/Scripts/Parkour System/ParkourController.cs:115)
ParkourController/<DoParkourAction>d__7:MoveNext () (at Assets/PolygonFantasyHeroCharacters/Scripts/Parkour System/ParkourController.cs:92)
UnityEngine.SetupCoroutine:InvokeMoveNext (System.Collections.IEnumerator,intptr)
and this is my current “ParkourController.cs” script, where the issue is coming from:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// NOTE: Make sure that different actions are alligned in the parkourActions list in order of their heights, regardless of property
// (otherwise it'll not work properly!)
public class ParkourController : MonoBehaviour
{
[SerializeField] List<ParkourAction> parkourActions;
private EnvironmentScanner environmentScanner;
private Animator animator;
private bool inAction;
private PlayerController playerController;
private void Awake()
{
environmentScanner = GetComponent<EnvironmentScanner>();
animator = GetComponent<Animator>();
playerController = GetComponent<PlayerController>();
}
private void Update()
{
// Potentially find a way to only seek the Shift + W button (running) to perform parkour. Just Shift is a bit... boring
// If you want to eliminate the pressing of the space button to perform parkour, comment the next line, and uncomment the line after
// if (Input.GetButton("Jump") && !inAction)
if (!inAction)
{
var hitData = environmentScanner.ObstacleCheck();
if (hitData.forwardHitFound)
{
// if there's an obstacle, determine its height, and then pick the proper parkour action based on that height:
foreach(var action in parkourActions)
{
if (action.CheckIfParkourPossible(hitData, transform))
{
// If the parkour is possible, based on the 'hitData' and the players' transform, then do it, and then break the foreach loop:
StartCoroutine(DoParkourAction(action));
break;
}
}
}
}
}
private IEnumerator DoParkourAction(ParkourAction action)
{
// boolean flag, to ensure this function does not keep repeating itself in the Update function:
inAction = true;
// when the animation is about to start, deactivate the Player's controller:
playerController.SetControl(false);
// set the animator mirror up (for actions, like the vault one, which require mirroring hands based on direction of approach of player):
animator.SetBool("mirrorAction", action.Mirror);
// Cross-fade between the locomotion and the 'Step Up' animation, for 0.2 seconds:
animator.CrossFade(action.AnimName, 0.2f);
// skip a frame, before cross-fading to a new animation:
yield return null;
// Get the state we are transitioning into (to access its time, using (animation Name).length):
var animState = animator.GetNextAnimatorStateInfo(0);
// if the designer made a naming mistake in the Parkour action, and we can't find it, throw out a Debug error stating a wrong name:
if (!animState.IsName(action.AnimName)) Debug.Log("The parkour animation is wrong");
// wait for the animation to end
// yield return new WaitForSeconds(animState.length);
// Timer:
float timer = 0f;
// if the timer is shorter than the animation state duration, update the timer, rotate the player towards the obstacle, and wait for 1 frame ('yield return null')
while (timer <= animState.length)
{
// Increment the timer:
timer += Time.deltaTime;
// Rotate the players' rotation towards the target rotation (from his 'transform.rotation', to the obstacles' 'TargetRotation', at the players' RotationSpeed, independent of frames (therefore multiply the speed with "Time.deltaTime")):
if (action.RotateToObstacle)
{
transform.rotation = Quaternion.RotateTowards(transform.rotation, action.TargetRotation, playerController.RotationSpeed * Time.deltaTime);
}
// if target matching is allowed, then match the target:
if (action.EnableTargetMatching)
{
MatchTarget(action);
}
// stop the loop when the animation is transitioning to another animation:
// if (animator.IsInTransition(0) && timer > 0.5f) break;
// Wait for a frame:
yield return null;
}
// wait for 'postActionDelay' seconds, when you're playing the "ClimbUp + Crouch-To-Stand" Animation combo (or any other animation with a post-Action Delay):
yield return new WaitForSeconds(action.PostActionDelay);
// when the animation is done, re-activate your Controller:
playerController.SetControl(true);
// Once we are done waiting for our animation to end, then we can resume our game:
inAction = false;
}
void MatchTarget(ParkourAction action)
{
// if the animator is already matching its target, don't do it again lah:
if (animator.isMatchingTarget) return;
// we only want to match the y-axis (for both step-up and jump-up animations), and z-axis (for 'ClimbUp', more advanced wall climbs) values, hence x in the Vector3 input of the weight mask will be zero:
animator.MatchTarget(action.MatchPos, transform.rotation, action.MatchBodyPart, new MatchTargetWeightMask(action.MatchPosWeight, 0), action.MatchStartTime, action.MatchTargetTime);
}
}
can any experienced developer kindly tell me how to fix my issue?