- Home /
How to use Mecanim's Target Matching?
Hi. To get straight to the point, I am attempting to use Mecanim to achieve a climbing system a la Assassin's Creed, and as far as I can tell Target Matching is the way to go. My Climb.js script is as follows.
private var animator : Animator;
var nearestObj : Transform = null;
private var dist = 1.5;
var furthestPoint : float;
var bodyPart : AvatarTarget;
private var state : AnimatorStateInfo;
var Player : Transform;
var grabPoint : Transform;
var weightPosition : float = 1;
var weightRotation : float = 1;
// the tag to search for (set this value in the inspector)
var searchTag = "Climbable";
// the frequency with which to re-scane for new nearest target in seconds
// (set in inspector)
var scanFrequency = 1.0;
// the current target
private var target : Transform;
function Start() {
// set up repeating scan for new targets:
InvokeRepeating("ScanForTarget", 0, scanFrequency );
animator = GetComponent ("Animator");
var Player = GameObject.FindGameObjectWithTag("Player");
}
function Update() {
// we rotate to look at the target every frame (if there is one)
if (target != null) {
if (animator) {
var distance = Vector3.Distance(Player.transform.position, nearestObj.transform.position);
if (distance < furthestPoint) {
animator.MatchTarget(grabPoint.position, grabPoint.rotation, AvatarTarget.bodyPart, new MatchTargetWeightMask(Vector3.one, 1f), animator.GetFloat("MatchStart"), animator.GetFloat("MatchEnd"));
}
animator.SetBool("Climbing", false);
}
}
}
function ScanForTarget() {
// this should be called less often, because it could be an expensive
// process if there are lots of objects to check against
target = GetNearestTaggedObject();
}
function GetNearestTaggedObject() : Transform {
// and finally the actual process for finding the nearest object:
var nearestDistanceSqr = Mathf.Infinity;
var taggedGameObjects = GameObject.FindGameObjectsWithTag(searchTag);
// loop through each tagged object, remembering nearest one found
for (var obj : GameObject in taggedGameObjects) {
var objectPos = obj.transform.position;
var distanceSqr = (objectPos - transform.position).sqrMagnitude;
if (distanceSqr < nearestDistanceSqr) {
nearestObj = obj.transform;
nearestDistanceSqr = distanceSqr;
}
}
return nearestObj;
}
As I am sure you will realize when you read the code, it is finding a GameObject tagged "Climbable" and, if that GameObject is close enough, setting TargetMatching's goal to that GameObject. If there is a problem with the script, let me know. I am a novice coder and the distance-finding code is cobbled together from other posts on Answers anyways.
Thanks for your assistance, chazman124
Whoops. I just realized that the code I posted was the script I was editing to see what the problem was. If you find some odd stuff, that's why.
And, to top it all off, I forgot to say what my issue was. Dur. $$anonymous$$y issue is that no matter how I edit the code, target matching doesn't seem to do anything. The animation plays normally, with no adjustment towards the GameObject that it's supposed to be moving the character's hands toward. I cannot for the life of me figure out why, so I figured I'd let you talented people figure it out.
What kind of objects are you using for the climb? I see that it's constantly scanning for climbable objects. $$anonymous$$aybe a video on youtube with that is going on might help.
Answer by coffiarts · Feb 26, 2013 at 09:07 PM
How are you assigning a value to bodyPart? I assume by manually dragging a part of your model into it in the properties inspector?
Furthermore, in line 37, you're calling bodyPart as if it were a property of AvatarTarget:
animator.MatchTarget(grabPoint.position, grabPoint.rotation, AvatarTarget.bodyPart, new MatchTargetWeightMask(Vector3.one, 1f), animator.GetFloat("MatchStart"), animator.GetFloat("MatchEnd"));
But AvatarTarget doesn't have that property, so I wonder why you don't get any compiler errors here. AvatarTarget is an Enumeration (see http://docs.unity3d.com/Documentation/ScriptReference/AvatarTarget.html), which in my understanding (I may be wrong!!) is used statically (e.g. as "AvatarTarget.Root", without assigning an arbitrary object to it beforehand).
I've tried to reproduce your logic in my code:
I could define a variable of type AvatarTarget, but it's just not assignable. Instead, I automatically get a dropdown selector for choosing from "Root", "Body", "Left Hand", "Right Hand" etc. This doesn't seem to make any harm, but it doesn't work to any positive effect either.
However, using "AvatarTarget.bodyPart" creates a compiler error in my scenario!
First of all, thank you for answering. I've changed the script quite a bit since I first posted this, and I've attached the changed version in a .txt file.link text Just in case this is important, my $$anonymous$$ecanim State $$anonymous$$achine is attached as well. And I have a new problem. $$anonymous$$y problem at this point is that Target $$anonymous$$atching works, but it doesn't work the way it's supposed to. It's hard to describe what happens, but basically the character jumps back from the wall about two feet and then rotates so that he's lying horizontally, all while the climbing animation is playing. All of that happens in about two seconds. If you or anyone else knows why this is happening, I'd be happy to know. Thanks, chazman124
[2]: /storage/temp/8327-state+machine.jpg
Two comments about your script, and one about your state machine:
your script:
function Update() {
if (target != null) {
if (animator) {
var distance = Vector3.Distance(bodyPart.transform.position, nearestObj.transform.position);
if (distance < dist) {
animator.SetBool("Climbing", true);
animator.$$anonymous$$atchTarget(target.position, target.rotation, AvatarTarget.RightHand, new $$anonymous$$atchTargetWeight$$anonymous$$ask(Vector3.one, 1f), start, end);
}
else {
animator.SetBool("Climbing", false);
}
}
}
}
comment 1:
You're applying the "Climbing" flag in every single frame:
animator.SetBool("Climbing", true);
Ist this really necessary? I don't think this is the cause of your described issue, but it's probably not good for performance. It should be fine just to assign this value only whenever it changes.
comment 2:
You're trying to match the model's hand to the target's exact rotation:
animator.$$anonymous$$atchTarget(target.position, target.rotation, AvatarTarget.RightHand, new $$anonymous$$atchTargetWeight$$anonymous$$ask(Vector3.one, 1f), start, end);
I wonder whether this is correct: Wouldn't this (in certain cases, depending on the target's rotation coords) try to turn/twist the hand in ways it shouldn't be? Have you already tried Quaternion.identity ins$$anonymous$$d (just to check whether the problem is still existing then)? But be aware that I am not really experienced in target matching myself, so this is only vague guess!
your state machine (jpg): - What are oyur transition conditions? - Have you already checked (inside the animator window during game mode), which animations are actually running while your character is thrown back?
I changed the target.rotation to Quaternion.identity, but the effect continues. In attempting to figure out which state was active during the effect, I noticed that the blue bar that normally denotes which animation is playing isn't showing for any of the states. I don't know what's going on there.
From my experience, to get the blue bar showing up during game mode, the game object to which the Animator component is attached needs to be selected in the editor. At least that was the reason in my case for the bar not being displayed (cost me days of confusion until I found out about that).
Thank you. I figured that out after a little while. Any other ideas as to why this is happening? The climb transition is playing at the correct times, and I've modified the script to heaven and back trying to fix it, but it's still happening.
Answer by jankrib · Jul 29, 2013 at 08:02 PM
I'm struggling a bit with target matching myself, but I think you can fix your rotation problem by setting the last parameter in the MatchTargetWeightMask to 0. This will mask out the rotation part (I think)
animator.MatchTarget(target.position, target.rotation, AvatarTarget.RightHand, new MatchTargetWeightMask(Vector3.one, 0), start, end);
Your answer
Follow this Question
Related Questions
MatchTarget unable to target and giving varried results 0 Answers
Mecanim hand/arm look at target (Aim), not pro? 0 Answers
Mecanim, external force match animation 0 Answers
Mecanim position specific bone 0 Answers
Mecanim is not playing facial animation. 6 Answers