- Home /
Very peculiar aiming issue - OnAnimatorMove()
So I'm ripping my hair off atm (what's left of it)
Basically I have 3 aim animations that I blend together to create smooth straight/up/down aiming. And I have a 'gunTip' transform at the tip of the character's gun. The 'forward' of the tip is coming out of the gun's barrel. This is where I shoot from.
When I fire, I cast a ray from the tip with the tip's forward being the direction. The animations are handled from script (not root motion). For some reason, if I call the shoot method from OnAnimatorMove
, the raycast doesn't seem to want to follow the tip's forward when I aim up/down!
I made a video showing the problem, my code, and the two different aid-band fixes I made.
The first fix is calling TryShoot from the PlayerController's Update, and not OnAnimatorMove, it worked pretty well, but of course without an animation.
The second fix, just to prove that the animation has nothing to do with the problem, I created a
tipFwd
Vector3 inWeapon
, and assigned it to the gun's tip in Update, and used that variable when I do raycast.
The first fix actually produces more accurate results than the second. Notice in my OnDrawGizmos I draw a red sphere (which I get its position from the shooting hit point) and a yellow sphere which I draw from the hit point I get from raycasting in OnDrawGizmos. What drives me crazy is that I'm using the "SAME" raycast both when I shoot and OnDrawGizmos, yet the red sphere doesn't appear where it should, while the yellow one is placed appropriately....
Here's my PlayerController's Update and OnAnimatorMove:
private void Update()
{
if (animator)
{
animator.speed = animationSpeed;
animator.SetFloat("Speed", Input.GetAxis("Vertical"));
animator.SetBool("IsAiming", Input.GetMouseButton(1));
animator.SetBool("IsRunning", Input.GetKey(KeyCode.LeftShift));
}
transform.Rotate(0f, Input.GetAxis("Horizontal") * Time.deltaTime * rotationSpeed * 100f, 0f);
}
private void OnAnimatorMove()
{
var baseLayer = animator.GetCurrentAnimatorStateInfo(0);
var secondLayer = animator.GetCurrentAnimatorStateInfo(1);
Func<int, bool> baseIsInState = hash => baseLayer.nameHash == hash;
Func<int, bool> secondLayerIsInState = hash => secondLayer.nameHash == hash;
bool isAiming = Input.GetMouseButton(1) && secondLayerIsInState(aimState);
if (isAiming)
{
if (!resetAim)
{
resetAim = true;
animator.SetFloat("AimY", 0f);
}
animator.SetFloat("AimY", Mathf.Clamp(animator.GetFloat("AimY") + Input.GetAxis("Mouse Y"), -1f, 1f));
animator.SetBool("IsShooting", Input.GetMouseButton(0) && weapon.TryShoot());
}
else
{
resetAim = false;
}
// Movement...
}
And here's my Weapon's Shoot/TryShoot:
public bool TryShoot()
{
if (shooting conditions are not met)
{
return false;
}
Shoot();
return true;
}
private void Shoot()
{
// Sound, muzzle and ammo decreasing...
RaycastHit hit;
if (Physics.Raycast(gunTip.position, gunTip.forward, out hit, Distance, shootingMask))
{
dbgLastShot = hit.point; // this is how I determine the red sphere's position
// irrelevant code...
}
}
The problem doesn't make any sense to me... But I have a good amount of certainty that it's a gotcha related to Animator/Mecanim and/or OnAnimatorMove... If you have any idea I'd really appreciate it!
Thanks a lot!
Although shooting from Update ins$$anonymous$$d of OnAnimator$$anonymous$$ove is a sort of an acceptable band-aid fix, but I would love to know/understand what's happening, and why...
perhaps it is just to do with the animation starting before the raycast is sent. You are more knowledgable than me about this I expect but could you try yielding the result of Shoot() in TryShoot() before returning true or is that done automatically when you put them in that order:
//pseudo
yield Shoot();
return true;
Or perhaps passing the gunTip.forward to TryShoot() when you call it at line 30 in your first script
animator.SetBool("IsShooting", Input.Get$$anonymous$$ouseButton(0) && weapon.TryShoot(gunTip.position, gunTip.foward));
with the additional changes in the other scripts.
Hi @Scribe thanks for your input!
perhaps it is just to do with the animation starting before the raycast is sent
That's incorrect;
animator.SetBool("IsShooting", weapon.TryShoot());
The value of weapon.TryShoot()
is passed as an argument to SetBool
so it's first calculated. The value returned is used as the value for "IsShooting"
I've tried yielding, but no dice.
I'll try to pass the position and direction to TryShoot, although I'm not a fan of that syntax...
Yeah I realised that would probably be the case, after watching your video a few more times it looks like the gunTip (and probably other objects) are somehow set to default values, I thought when I had watched it before that the spot was always higher than the shot but I notice that's not the case it is simply always directly forward on the x/z axis, though the rotation around y seems to be correct. It is indeed very peculiar, sorry I can be of much help, I'd be interested to know what the issue is if you solve it, perhaps a bug report to unity would be worth a try!
Scribe
P.S. Do you have any links to tutorials or reading about the Func <,> syntax, I probably should know it but as I'm self taught there is no structure to my knowledge :P
P.P.S sorry for the grammar, wiring on my phone!
OnAnimator$$anonymous$$ove - ( OA$$anonymous$$() )
This callback will be invoked at each frame after the state machines and the animations have been evaluated
In OnAnimator$$anonymous$$ove you are actually setting parameters of the Animator. So, the Animator will be re-evaluated before the changes occur. This could be resetting your Late changes to Root $$anonymous$$otion.
There is a mismatch between what you expect and what you get as a result.
Try moving the bones transform in OA$$anonymous$$() ins$$anonymous$$d of moving via Animator from OA$$anonymous$$() and see if this still happens.
OnAnimator$$anonymous$$ove is for making direct changes to transforms, essentially; not for making loop-backs to the Animator.
Answer by meat5000 · Aug 16, 2014 at 03:02 PM
I refer you back to the InPlace page. Notice how simple they have kept it, modifying transforms only. It is perfectly fine to Get parameters from the Animator but Setting them here is not a good idea, as far as I know.
In OAM() you can directly modify the transform of a bone. For example rotating the spine, arms etc to re-position them as desired. I did this same thing to make custom IK in Free Edition, so I could swing a sword and always cut through a particular point in space (or target).
No problem. Lets keep this open a while; hopefully someone out there will have more information/experience on what IS possible in OnAnimator$$anonymous$$ove. Added the function name to the title, hope you don't $$anonymous$$d.
Sure. Do you want me to untick your answer for the time being?
Depends if you use it in solving your problem :) I'm indifferent; just glad to help.
Thanks! I think I'll leave it. I'm not setting params in OA$$anonymous$$ everything's fine so far :)
Your answer
Follow this Question
Related Questions
Best Way to script Mecanim Animation CrossFade? 0 Answers
16 identical Mecanim Animator errors....why? 2 Answers
Mecanim Animate Physics - When to use? 0 Answers
Macanim Mouse Buttons interaction c# 1 Answer
Animator MatchTarget Problem 0 Answers