Animation blending logic

Hello. I’m making a 3D isometric game. Movement and look direction were determined by WASD keys, so I only needed a standard forward running animation so far. However, recently I added an ability where the player’s look direction faces the mouse position on the screen when RMB is held, in order to aim in that direction. This requires 3 additional running animations, backwards, left strafe and right strafe.

So, I made a blend tree to include all 4 animations. It is clear as day to me how this is supposed to function, but I can’t for the life of me figure out what exactly I’m doing wrong. Here’s the relevant code for blending between the 4 animations:

private void Move()
    {
        if (Input.GetMouseButton(1))
        {
            anim.SetBool("Running", false);
            anim.SetBool("Blending", true);
            // Get the mouse position on the screen
            Vector2 mousePos = Input.mousePosition;

            // Get the screen width and height
            float screenWidth = Screen.width;
            float screenHeight = Screen.height;

            // Determine which part the mouse is in
            int part;
            if (mousePos.x + mousePos.y < screenWidth && mousePos.x - mousePos.y > 0) part = 1; // Top
            else if (mousePos.x + mousePos.y > screenWidth && mousePos.x - mousePos.y > 0) part = 2; // Right
            else if (mousePos.x + mousePos.y > screenWidth && mousePos.x - mousePos.y < 0) part = 3; // Bottom
            else part = 4; // Left

            // Set the moveX and moveY parameters based on the part and the input direction
            switch (part)
            {
                case 1: // Top
                    anim.SetFloat("moveX", _input.x); // Left -> Left strafe, Right -> Right strafe
                    anim.SetFloat("moveY", _input.z); // Forward -> Forward, Backward -> Backward
                    break;
                case 2: // Right
                    anim.SetFloat("moveX", _input.x); // Left -> Backward, Right -> Forward
                    anim.SetFloat("moveY", _input.z); // Forward -> Left strafe, Backward -> Right strafe
                    break;
                case 3: // Bottom
                    anim.SetFloat("moveX", -_input.x); // Left -> Right strafe, Right -> Left strafe
                    anim.SetFloat("moveY", -_input.z); // Forward -> Backward, Backward -> Forward
                    break;
               case 4: // Left
                    anim.SetFloat("moveX", -_input.x); // Left -> Forward, Right -> Backward
                    anim.SetFloat("moveY", _input.z); // Forward -> Right strafe, Backward -> Left strafe
                    break;
            }
        }
        else
        {
            anim.SetBool("Blending", false);
            anim.SetBool("Running", true);
        }
        Vector3 movementDirection = new Vector3(_input.x, 0f, _input.z).normalized.ToIso();
        _rb.MovePosition(transform.position + movementDirection * _speed * Time.deltaTime);
    }

So, the screen is split into 4 parts, and the mouse position in the game is supposed to alter which animation plays when one of the movement keys is held(WASD). The code works when the mouse is in the top part of the screen, but anywhere else and it all turns to mush. I cannot figure out what I’m doing wrong in the switch statement so that the moveX/moveY parameters aren’t changing correctly.

Did you check the actual blend tree as good first?

To debug the blend tree, disable the script and run, the wiggle the input in the editor to make sure that’s set up properly.

Once you get the blend tree absolutely positively correct, then it is…

Time to start debugging! Here is how you can begin your exciting new debugging adventures:

You must find a way to get the information you need in order to reason about what the problem is.

Once you understand what the problem is, you may begin to reason about a solution to the problem.

What is often happening in these cases is one of the following:

  • the code you think is executing is not actually executing at all
  • the code is executing far EARLIER or LATER than you think
  • the code is executing far LESS OFTEN than you think
  • the code is executing far MORE OFTEN than you think
  • the code is executing on another GameObject than you think it is
  • you’re getting an error or warning and you haven’t noticed it in the console window

To help gain more insight into your problem, I recommend liberally sprinkling Debug.Log() statements through your code to display information in realtime.

Doing this should help you answer these types of questions:

  • is this code even running? which parts are running? how often does it run? what order does it run in?
  • what are the names of the GameObjects or Components involved?
  • what are the values of the variables involved? Are they initialized? Are the values reasonable?
  • are you meeting ALL the requirements to receive callbacks such as triggers / colliders (review the documentation)

Knowing this information will help you reason about the behavior you are seeing.

You can also supply a second argument to Debug.Log() and when you click the message, it will highlight the object in scene, such as Debug.Log("Problem!",this);

If your problem would benefit from in-scene or in-game visualization, Debug.DrawRay() or Debug.DrawLine() can help you visualize things like rays (used in raycasting) or distances.

You can also call Debug.Break() to pause the Editor when certain interesting pieces of code run, and then study the scene manually, looking for all the parts, where they are, what scripts are on them, etc.

You can also call GameObject.CreatePrimitive() to emplace debug-marker-ish objects in the scene at runtime.

You could also just display various important quantities in UI Text elements to watch them change as you play the game.

Visit Google for how to see console output from builds. If you are running a mobile device you can also view the console output. Google for how on your particular mobile target, such as this answer or iOS: How To - Capturing Device Logs on iOS or this answer for Android: How To - Capturing Device Logs on Android

If you are working in VR, it might be useful to make your on onscreen log output, or integrate one from the asset store, so you can see what is happening as you operate your software.

Another useful approach is to temporarily strip out everything besides what is necessary to prove your issue. This can simplify and isolate compounding effects of other items in your scene or prefab.

Here’s an example of putting in a laser-focused Debug.Log() and how that can save you a TON of time wallowing around speculating what might be going wrong:

“When in doubt, print it out!™” - Kurt Dekker (and many others)

Note: the print() function is an alias for Debug.Log() provided by the MonoBehaviour class.

I’m not gonna lie, I came here hoping someone would spot the error in my code, correct me, and I’d be on my merry way, so I was kinda let down when I saw the generic post…

BUT THEN… I decided to take what I can get and apply your comment to my problem, and lo and behold, all it took was adding a few Debug.Logs to figure out my case determining logic was flawed:

 int part;
            if (angle < 45) part = 1; // Top
            else if (angle > 135) part = 3; // Bottom
            else if (playerToMouse.x > 0) part = 2; // Right
            else part = 4; // Left

            // Set the moveX and moveY parameters based on the part and the input direction
            switch (part)
            {
                case 1: // Top
                    anim.SetFloat("moveX", _input.x); // Left -> Left strafe, Right -> Right strafe
                    anim.SetFloat("moveY", _input.z); // Forward -> Forward, Backward -> Backward
                    Debug.Log("Case 1");
                    break;
                case 2: // Right
                    anim.SetFloat("moveX", -_input.z); // Left -> Backward, Right -> Forward
                    anim.SetFloat("moveY", _input.x); // Forward -> Left strafe, Backward -> Right strafe
                    Debug.Log("Case 2");

                    break;
                case 3: // Bottom
                    anim.SetFloat("moveX", -_input.x); // Left -> Right strafe, Right -> Left strafe
                    anim.SetFloat("moveY", -_input.z); // Forward -> Backward, Backward -> Forward
                    Debug.Log("Case 3");

                    break;
                case 4: // Left
                    anim.SetFloat("moveX", _input.z); // Left -> Forward, Right -> Backward
                    anim.SetFloat("moveY", -_input.x); // Forward -> Right strafe, Backward -> Left strafe
                    Debug.Log("Case 4");

                    break;
            }

I dread the next time I benefit from you directly answering me (because that means I’ve been stuck on a problem far too long), but, I’d like to use this opportunity to thank you for doing the Lord’s work on these forums, by providing us newbies with all the necessary information to figure out and solve the problem by ourselves, because that feeling cannot compare to getting the answer served on a platter. Changing the flair to resolved.

THANK YOU!!!

1 Like

Wow, thanks.

You nailed it: I want to offer everybody the universal hints they need to find the problems themselves.

It’s the whole “give a man a fish” or “teach a man to fish” decision.

And most problems like this are all about just chopping the problem up into smaller and smaller pieces until you find the one tiny rotten chunk and fix it. The extra challenge comes when there are more than one rotten pieces and you only find one, not the other. :slight_smile:

2 Likes