Search code examples
unity-game-engine

Rod bending by blend tree in Unity


I'm working on implementing rod bending by blend tree. Once, I've made a system to achieve that but then after I found out it doesn't work correctly in every angles and situations. I fills directionX (First parameter of blend tree) and directionY (Second parameter of blend tree) by using this code:

            Vector3 projectedLineXY = Vector3.ProjectOnPlane(_baitHandler.transform.position - _context.rodTip.position, _context.rod.transform.forward);
            float angleSignXY = Mathf.Sign(Vector3.Dot(Vector3.Cross(_context.rod.transform.up, projectedLineXY), _context.rod.transform.forward));
            float angleXY = Vector3.Angle(_context.rod.transform.up, projectedLineXY) * angleSignXY;
            Vector3 projectedLineZY = Vector3.ProjectOnPlane(_baitHandler.transform.position - _context.rodTip.position, _context.rod.transform.right);
            float angleSignZY = Mathf.Sign(Vector3.Dot(Vector3.Cross(_context.rod.transform.up, projectedLineZY), -_context.rod.transform.right));
            float angleZY = Vector3.Angle(_context.rod.transform.up, projectedLineZY) * angleSignZY;

_baitHandler.transform.position is bait position and _context.rodTip.position is tip of rod position. _context.rod is rod gameObject and other things related to that are its directions.

Please if you know how I can achieve the correct solution for that, explain what exactly I can fill these 2 parameters of blend tree.

Video from blend tree


Solution

  • I hope it is helpful for anyone who is looking for implementing rod bending by blend tree. Finally I got to this after so many iterations:

    private const float ROD_BEND_CORRECTION_ERROR = 0.01f;
    private readonly int DIRECTION_X_HASH = Animator.StringToHash("directionX");
    private readonly int DIRECTION_Y_HASH = Animator.StringToHash("directionY");
    
    public void UpdateRodBend(float deltaTime, float strength = 1f)
    {
        for (int i = 0; i < 10; i++)
        {
            Vector3 line = bait.position - rodTip.position;
            Vector3 projectedY = Vector3.ProjectOnPlane(line, rodTip.forward);
            float angleSignY = Mathf.Sign(Vector3.Dot(Vector3.Cross(rodTip.up, projectedY), rodTip.forward));
            float angleY = Vector3.Angle(-rodTip.up, projectedY) * angleSignY;
            if (Mathf.Abs(angleY / 90) > ROD_BEND_CORRECTION_ERROR)
            {
                animator.SetDirectionY((angleY / 90) * deltaTime * rodBendingSmoothSpeed, strength);
                animator.ForceUpdateRodAnimator();
            }
            line = bait.position - rodTip.position;
            Vector3 projectedX = Vector3.ProjectOnPlane(line, rodTip.right);
            float angleSignX = Mathf.Sign(Vector3.Dot(Vector3.Cross(_context.rodTip.up, projectedX), -rodTip.right));
            float angleX = Vector3.Angle(-rodTip.up, projectedX) * angleSignX;
            if (Mathf.Abs(angleX / 90) > ROD_BEND_CORRECTION_ERROR)
            {
                animator.SetDirectionX((angleX / 90) * deltaTime * rodBendingSmoothSpeed, strength);
                animator.ForceUpdateRodAnimator();
            }
        }
    }
    
    public void SetDirectionX(float value, float maxLength)
    {
        Vector2 clamped = Vector2.ClampMagnitude(new Vector2(animator.GetFloat(DIRECTION_X_HASH) + value, animator.GetFloat(DIRECTION_Y_HASH)), maxLength);
        animator.SetFloat(DIRECTION_X_HASH, clamped.x);
    }
    
    public void SetDirectionY(float value, float maxLength)
    {
        Vector2 clamped = Vector2.ClampMagnitude(new Vector2(animator.GetFloat(DIRECTION_X_HASH), animator.GetFloat(DIRECTION_Y_HASH) + value), maxLength);
        animator.SetFloat(DIRECTION_Y_HASH, clamped.y);
    }