Calculate float values for animator on a AI agent

Version:
Unity: 2022.2.5f
Burst: 1.8.2
Entities: 1.0.0-pre.15

Hello

I am trying to make my ai use my current animations but the way have have made it is more based on the input from a player. But that would work a bit wierd for my ai character, especially since i want my ai to be able to strafe.

So what i need to calculate the floats that drives my animations based on the movement of the ai.

I have an naive solutions for this, just testing positions in 360 and check which values are closest to the movement direction.

Another solution that i am thinking about is just to calculate verticalMovement and horizontalMovement based on movement direction, speed and also take the look direction into consideration. But i not sure how i would calculate that with vectors and possible quaternion.

            translateData.position = translateData.position + (characterControllerData.verticalMovement * math.forward(translateData.rotation)) * 4f * deltaTime;
            translateData.position = translateData.position + (characterControllerData.horizontalMovement * vectorRight) * 4f * deltaTime;


Red is movement, orange is looking direction

Player input

        float sensitivity = 0.05f;


        //Inputs
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        float mouseX = Input.GetAxis("Mouse X");
        float mouseY = Input.GetAxis("Mouse Y");

        bool mouseLeftClick = Input.GetMouseButtonDown(0);

        bool leftAlt = Input.GetKey(KeyCode.LeftAlt);

        bool jumpButton = Input.GetKey(KeyCode.Space);

        bool crouchButton = Input.GetKey(KeyCode.C);

        bool phroneButton = Input.GetKey(KeyCode.Z);

        //Take input
        Entities.ForEach((ref PlayerInputData playerInputData) =>
        {

            playerInputData.inputHorizontal = horizontal;
            playerInputData.inputVertical = vertical;

            playerInputData.inputRotationMouseX = mouseX;
            playerInputData.inputRotationMouseY = mouseY;

            playerInputData.inputMouseLeft = mouseLeftClick;

            playerInputData.inputLeftAlt = leftAlt;

            playerInputData.inputJump = jumpButton;

            playerInputData.inputCrouch = crouchButton;

            playerInputData.inputPhrone = phroneButton;

            //Debug.Log(" Player Input " + "  horizontal " + horizontal + "  vertical " + vertical);

        }).WithBurst().Schedule();


        //From player input to character controller
        Entities.ForEach((ref CharacterControllerData characterControllerData, in PlayerInputData playerInputData) =>
        {
            characterControllerData.horizontalMovement = playerInputData.inputHorizontal;
            characterControllerData.verticalMovement = playerInputData.inputVertical;

            characterControllerData.rotationDirectionX = characterControllerData.rotationDirectionX + (playerInputData.inputRotationMouseX * sensitivity);
            characterControllerData.rotationDirectionY = characterControllerData.rotationDirectionY + (playerInputData.inputRotationMouseY * sensitivity);

            characterControllerData.useWeapon = playerInputData.inputMouseLeft;

            characterControllerData.jump = playerInputData.inputJump;
            characterControllerData.crouch = playerInputData.inputCrouch;
            characterControllerData.phrone = playerInputData.inputPhrone;

            //Debug.Log(" Character Input " + " horizontalMovement " + characterControllerData.horizontalMovement + " verticalMovement " + characterControllerData.verticalMovement);


        }).WithBurst().Schedule();

Translation system

public partial class TranslationSystem : SystemBase
{
    protected override void OnUpdate()
    {
        float deltaTime = SystemAPI.Time.DeltaTime;

        //Character controller tranfer translate data
        Entities.ForEach
        ((ref TranslateData translateData, in CharacterControllerData characterControllerData) =>
        {
            //Rotation
            translateData.rotation = quaternion.RotateY(characterControllerData.rotationDirectionX);


            //Movement
            float3 vectorRight = math.mul(translateData.rotation, math.right());
            vectorRight.y = 0;
            vectorRight = math.normalizesafe(vectorRight);

            translateData.position = translateData.position + (characterControllerData.verticalMovement * math.forward(translateData.rotation)) * 4f * deltaTime;
            translateData.position = translateData.position + (characterControllerData.horizontalMovement * vectorRight) * 4f * deltaTime;

            //Debug.Log(" characterController " + "  translateData.position " + translateData.position);
               
        }
        ).WithBurst().ScheduleParallel();

        //Moving and rotating objects
        Entities.ForEach
        ((ref LocalTransform localTransform, in TranslateData translateData) =>
        {

            //Set TransformAspect
            localTransform.Position = translateData.position;
            localTransform.Rotation = translateData.rotation;

            //Debug.Log(" Moving objects " + "  localTransform.Position " + localTransform.Position);
        }
        ).WithBurst().ScheduleParallel();



    }

}

Character system for setting animation and moving the Mono GameObject

public partial class CharacterControllerGeneralSystem : SystemBase
{

    protected override void OnUpdate()
    {
        float deltaTime = SystemAPI.Time.DeltaTime;
        float animationAccelerationSpeed = 0.05f;

       

        //Set animation Data
        Entities.ForEach
        ((ref CharacterAnimationData characterAnimationData, in CharacterControllerData characterControllerData) =>
        {

            /*
            //HorizontalAxis
            if (characterControllerData.horizontalMovement > 0.05f)
            {
                characterAnimationData.velocityX = characterAnimationData.velocityX + animationAccelerationSpeed;
                if (characterAnimationData.velocityX > 1)
                {
                    characterAnimationData.velocityX = 1;
                }
            }
            if (characterControllerData.horizontalMovement < -0.05f)
            {
                characterAnimationData.velocityX = characterAnimationData.velocityX - animationAccelerationSpeed;
                if (characterAnimationData.velocityX < -1)
                {
                    characterAnimationData.velocityX = -1;
                }
            }

            //VerticalAxis
            if (characterControllerData.verticalMovement > 0.05f)
            {
                characterAnimationData.velocityY = characterAnimationData.velocityY + animationAccelerationSpeed;
                if (characterAnimationData.velocityY > 1)
                {
                    characterAnimationData.velocityY = 1;
                }
            }
            if (characterControllerData.verticalMovement < -0.05f)
            {
                characterAnimationData.velocityY = characterAnimationData.velocityY - animationAccelerationSpeed;
                if (characterAnimationData.velocityY < -1)
                {
                    characterAnimationData.velocityY = -1;
                }
            }
            */

            characterAnimationData.velocityX = characterControllerData.horizontalMovement;
            characterAnimationData.velocityY = characterControllerData.verticalMovement;

        }
        ).WithBurst().ScheduleParallel();



        //Reading from CharacterControllerData and setting values for GameObject and Animator
        Entities.ForEach
        ((CharacterControllerClass characterControllerClass, in CharacterAnimationData characterControllerData, in LocalTransform localTransform ) =>
        {

            characterControllerClass.animator.SetFloat("VelocityX", characterControllerData.velocityX);
            characterControllerClass.animator.SetFloat("VelocityY", characterControllerData.velocityY);

            characterControllerClass.characterControllerGO.transform.position = localTransform.Position;
            characterControllerClass.characterControllerGO.transform.rotation = localTransform.Rotation;


        }
        ).WithoutBurst().Run();



    }

}

Components

public struct CharacterAnimationData : IComponentData
{

    public float velocityX; //Sidways movement X
    public float velocityY; //Forward or backward Y

}

public struct CharacterControllerData: IComponentData
{

    public float horizontalMovement; //Sidways movement X
    public float verticalMovement;   //Forward or backward Y

    public float rotationDirectionX;
    public float rotationDirectionY;

    public bool useWeapon;

    public bool jump;
    public bool crouch;
    public bool phrone;

    public float maxSpeed;

}

public class CharacterControllerClass : IComponentData
{
    public GameObject characterControllerGO;
    public Animator animator;

}

Mono test

    void Update()
    {
        float sensitivity = 1f;

        float deltaTime = Time.deltaTime;

        if (isPlayer)
        {
            horizontalMovement = Input.GetAxis("Horizontal");
            verticalMovement = Input.GetAxis("Vertical");

            mouseX = Input.GetAxis("Mouse X");
            mouseY = Input.GetAxis("Mouse Y");

            rotationDirectionY = rotationDirectionY + (mouseX * sensitivity);
        }else
        {

            aiInput(gameObject, directionGO, targetLookGO, rotationDirectionY, verticalMovement, horizontalMovement); // Not working

        }
       




        updateMovement(gameObject, rotationDirectionY, verticalMovement, horizontalMovement);



    }


    void updateMovement(GameObject gameObject, float rotationDirectionY, float verticalMovement, float horizontalMovement)
    {
        float deltaTime = Time.deltaTime;

        Quaternion rotation = (Quaternion.AngleAxis(rotationDirectionY, Vector3.up));

        Vector3 rightMovement = (rotation * Vector3.right);
        rightMovement.y = 0;
        rightMovement = Vector3.Normalize(rightMovement);
        Vector3 forwardMovement = (rotation * Vector3.forward);

        gameObject.transform.rotation = rotation;
        gameObject.transform.position = gameObject.transform.position + (verticalMovement * forwardMovement) * 4f * deltaTime;
        gameObject.transform.position = gameObject.transform.position + (horizontalMovement * rightMovement) * 4f * deltaTime;

        Debug.Log("rightMovement " + rightMovement + "  forwardMovement  " + forwardMovement);
    }

Any suggestions on how to proceed with this problem is appreciated

Usually for blend tree animations you’d do something like:

  • Grab movement delta (XYZ) in world space; [E.g. store it during movement]
  • Convert into local space movement delta; [so its relative to the CC, logic is equal to transform.InverseTransformDirection];
  • Normalize it, but keep magnitude for the speed calculation; [so that values are in -1…1 range]
  • Apply XZ values as XY for the blend tree [Y can be used for speed computation e.g. for the jump]
  • Apply magnitude as a speed parameter

Issue right now is how you perform movement. You need to abstract away from inputs & convert them into directions before performing movement.

If you want local direction based on input to (skip conversion from world to local space) it looks like this:

Vector3 desiredDirection = _transform.forward * _input.y - _transform.right * _input.x;

This will create normalized local space movement direction vector (if inputs are normalized), which you can then use to perform movement.

Space transformations can be performed on the new transform system as well. Math is similar, it all boils down to vector algebra and matrix multiplications (e.g. by using LocalToWorld).

Otherwise you’d need to be more specific at which point you’re stuck.

1 Like

Could you show an simple example in code? It would really help, it would speed up my learning for vector math. I can get a bit confused with trying to apply math theory to code.

Use Debug.DrawRay for the visualization purposes.
Its not actually code you need, but the understanding which space is which. And how to transform between them.

A bit of theory

TransformDirection (if simplified) is a matrix multiplication.
T[ranslation] R[otation] S[cale] is stored as a matrix in some data structure. Either its internal transform system, or new transform system - does not matter - logic remains the same.

World space is position, rotation or lossyScale == combined total value relative to the origin (0,0,0).
Local space is localPosition, localRotation, or localScale relative to the parent object.

TransformDirection - takes a vector and converts it from local space into world space by multiplying against TRS matrix [of some other transform, or parent, or point, or anything really].

InverseTransformDirection - takes a vector and converts it from world to local space.

Initial TRS matrix for both cases is the same. (also known as localToWorld)
However, in case of Inverse - matrix gets inversed first, hence the name.

Knowing this:

  • Figure out movement as single direction value (float3 / Vector3) first.
    Remove per axis manipulation (horizontal / vertical) and merge it into single float3;
    Value you’re applying to the position in TranslationSystem OnUpdate is your world space movement delta.

So instead of:

translateData.position = translateData.position + (characterControllerData.verticalMovement * math.forward(translateData.rotation)) * 4f * deltaTime;
translateData.position = translateData.position + (characterControllerData.horizontalMovement * vectorRight) * 4f * deltaTime;

Do:

// Note that this is already in local space
float3 moveDelta = localTransform.Forward() * verticalMovement - localTransform.Right() * horizontalMovement;

// Convert it to the world space, because position is in world space coordinates
moveDelta = localTransform.TransformDirection(moveDelta);

// Speed is your 4f, and deltaTime is Time.deltaTime;
moveDelta = moveDelta * speed * deltaTime;

translateData.position += moveDelta;
  • Store moveDelta in the component;

  • AI should move using movement vector as well. This way you’ll have both player & AI moving via same logic. And animator would be using same logic.

  • Use LocalTransform.InverseTransformDirection(storedMovementDelta) to get movement direction vector in local space as a direction relative to the entity;

// If you need to convert world space direction to local, do it like so:
float3 localMoveDelta = localTransform.InverseTransformDirection(moveDelta);

// Then this value should be fed to the Animator as a speed,
// potentially multiplied by some speed multiplier
// & clamped in case if entity goes too fast.
float magnitude = math.length(localMoveDelta);

localMoveDelta = math.normalizeSafe(localMoveDelta);

// So now you'd pass
animator.SetFloat(*horizontalMovement*, localMoveDelta.x);
animator.SetFloat(*verticalMovement*, localMoveDelta.z);

Animator’s blend tree is based on XY plane. Hence why you’d want XZ → XY conversion.
Animations are in local space, that’s why you’d want to convert the direction which way your entity moves into local space.

1 Like

Thanks this very well explained, going to try it in my test environment when i get home.

I tried the code that you showed, and the animation matches on the player character. But the direction which i move is a bit weird, if i move along the z axis in editor world then all is fine. But if i start to rotate 90 degrees camera either direction then the movement starts to move in -z, when i press forward. The animation works its just my inputs that don’t work as intended. Might be the way i have applied the rotation.

Will have more time to test it tomorrow.

characterControllerData.rotationDirectionX takes direct input form the mouse

translateData.rotation = quaternion.RotateY(characterControllerData.rotationDirectionX);

Character controller

        //Character controller tranfer translate data
        Entities.ForEach
        ((ref TranslateData translateData, in LocalTransform localTransform, in CharacterControllerData characterControllerData) =>
        {
            //Rotation
            translateData.rotation = quaternion.RotateY(characterControllerData.rotationDirectionX);

            float speed = 4f;

            //Debug.Log(" characterController " + "  translateData.position " + translateData.position);

            translateData.moveDelta = localTransform.Forward() * characterControllerData.verticalMovement + localTransform.Right() * characterControllerData.horizontalMovement;

            translateData.moveDelta = localTransform.TransformDirection(translateData.moveDelta);

            translateData.moveDelta = translateData.moveDelta * speed * deltaTime;

            translateData.position = translateData.position + translateData.moveDelta;
        }
        ).WithBurst().ScheduleParallel();

        //Moving and rotating objects
        Entities.ForEach
        ((ref LocalTransform localTransform, in TranslateData translateData) =>
        {

            //Set TransformAspect
            localTransform.Position = translateData.position;
            localTransform.Rotation = translateData.rotation;

            //Debug.Log(" Moving objects " + "  localTransform.Position " + localTransform.Position);
        }
        ).WithBurst().ScheduleParallel();

Set animation

        //Set animation Data
        Entities.ForEach
        ((ref CharacterAnimationData characterAnimationData, ref LocalTransform localTransform, in TranslateData translateData, in CharacterControllerData characterControllerData) =>
        {

            float3 localMoveDelta = localTransform.InverseTransformDirection(translateData.moveDelta);


            float magnitude = math.length(localMoveDelta);

            localMoveDelta = math.normalizesafe(localMoveDelta);



            characterAnimationData.velocityX = localMoveDelta.x;
            characterAnimationData.velocityY = localMoveDelta.z;
            /*
            characterAnimationData.velocityX = characterControllerData.horizontalMovement;
            characterAnimationData.velocityY = characterControllerData.verticalMovement;
            */
        }
        ).WithBurst().ScheduleParallel();



        //Reading from CharacterControllerData and setting values for GameObject and Animator
        Entities.ForEach
        ((CharacterControllerClass characterControllerClass, in CharacterAnimationData characterControllerData, in LocalTransform localTransform ) =>
        {

            characterControllerClass.animator.SetFloat("VelocityX", characterControllerData.velocityX);
            characterControllerClass.animator.SetFloat("VelocityY", characterControllerData.velocityY);

            characterControllerClass.characterControllerGO.transform.position = localTransform.Position;
            characterControllerClass.characterControllerGO.transform.rotation = localTransform.Rotation;


        }
        ).WithoutBurst().Run();

Player Input

    protected override void OnUpdate()
    {

        //Temp 
        float sensitivity = 0.05f;


        //Inputs
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        float mouseX = Input.GetAxis("Mouse X");
        float mouseY = Input.GetAxis("Mouse Y");

        bool mouseLeftClick = Input.GetMouseButtonDown(0);

        bool leftAlt = Input.GetKey(KeyCode.LeftAlt);

        bool jumpButton = Input.GetKey(KeyCode.Space);

        bool crouchButton = Input.GetKey(KeyCode.C);

        bool phroneButton = Input.GetKey(KeyCode.Z);

        //Update camera position
        Entities.ForEach
        ((PlayerCameraClass playerCameraClass, in CharacterControllerData characterControllerData) =>
        {
            playerCameraClass.rotation = quaternion.EulerXYZ(-characterControllerData.rotationDirectionY, characterControllerData.rotationDirectionX, 0);
            if (playerCameraClass.firstOrThirdPerson == true)
            {
                playerCameraClass.playerCamera.transform.position = playerCameraClass.firstPersonCameraGOPosition.transform.position;
                playerCameraClass.playerCamera.transform.rotation = playerCameraClass.rotation;
            }
            if (playerCameraClass.firstOrThirdPerson == false)
            {
                playerCameraClass.playerCamera.transform.position = playerCameraClass.thirdPersonCameraGOPosition.transform.position;
                playerCameraClass.playerCamera.transform.rotation = playerCameraClass.rotation;
            }

        }
        ).WithoutBurst().Run();


        //Take input
        Entities.ForEach((ref PlayerInputData playerInputData) =>
        {

            playerInputData.inputHorizontal = horizontal;
            playerInputData.inputVertical = vertical;

            playerInputData.inputRotationMouseX = mouseX;
            playerInputData.inputRotationMouseY = mouseY;

            playerInputData.inputMouseLeft = mouseLeftClick;

            playerInputData.inputLeftAlt = leftAlt;

            playerInputData.inputJump = jumpButton;

            playerInputData.inputCrouch = crouchButton;

            playerInputData.inputPhrone = phroneButton;

            //Debug.Log(" Player Input " + "  horizontal " + horizontal + "  vertical " + vertical);

        }).WithBurst().Schedule();


        //From player input to character controller
        Entities.ForEach((ref CharacterControllerData characterControllerData, in PlayerInputData playerInputData) =>
        {
            characterControllerData.horizontalMovement = playerInputData.inputHorizontal;
            characterControllerData.verticalMovement = playerInputData.inputVertical;

            characterControllerData.rotationDirectionX = characterControllerData.rotationDirectionX + (playerInputData.inputRotationMouseX * sensitivity);
            characterControllerData.rotationDirectionY = characterControllerData.rotationDirectionY + (playerInputData.inputRotationMouseY * sensitivity);

            characterControllerData.useWeapon = playerInputData.inputMouseLeft;

            characterControllerData.jump = playerInputData.inputJump;
            characterControllerData.crouch = playerInputData.inputCrouch;
            characterControllerData.phrone = playerInputData.inputPhrone;

            //Debug.Log(" Character Input " + " horizontalMovement " + characterControllerData.horizontalMovement + " verticalMovement " + characterControllerData.verticalMovement);


        }).WithBurst().Schedule();



    }

Try forward - right;
If its not it - try debugging visually where direction is messed up.

Like you posted earlier? I tried that but got invert strafing movement.
Thankful for the time you spend helping.

Going test some more when i get home.
If i dont solve it tomorrow i will create a mono test environment, so i am sure of the logic.

But it would be nice if i get this to work more vector functions that i can use is good.

I got it to work.
When i applied the localTransform.TransformDirection(translateData.moveDelta) to moveDelta it caused the movement behave weirdly, mabye dubble the rotation amount, not sure.

But the new way to modify animation values works.

float3 localMoveDelta = localTransform.InverseTransformDirection(translateData.moveDelta);

Going to do some more testing later today or tomorrow

Character controller tranfer translate data

        Entities.ForEach
        ((ref TranslateData translateData, in LocalTransform localTransform, in CharacterControllerData characterControllerData) =>
        {
            //Rotation
            translateData.rotation = quaternion.RotateY(characterControllerData.rotationDirectionX);


            float speed = 4f;


            translateData.moveDelta = localTransform.Forward() * characterControllerData.verticalMovement + localTransform.Right() * characterControllerData.horizontalMovement;

            //translateData.moveDelta = localTransform.TransformDirection(translateData.moveDelta);

            translateData.moveDelta = translateData.moveDelta * speed * deltaTime;

            translateData.position = translateData.position + translateData.moveDelta;

            //translateData.moveDelta = localTransform.TransformDirection(translateData.moveDelta);
        }
        ).WithBurst().ScheduleParallel();

Set animation

        Entities.ForEach
        ((ref CharacterAnimationData characterAnimationData, ref LocalTransform localTransform, in TranslateData translateData, in CharacterControllerData characterControllerData) =>
        {


            float3 localMoveDelta = localTransform.InverseTransformDirection(translateData.moveDelta);


            float magnitude = math.length(localMoveDelta);

            localMoveDelta = math.normalizesafe(localMoveDelta);



            characterAnimationData.velocityX = localMoveDelta.x;
            characterAnimationData.velocityY = localMoveDelta.z;
            /*
            characterAnimationData.velocityX = characterControllerData.horizontalMovement;
            characterAnimationData.velocityY = characterControllerData.verticalMovement;
            */
        }
        ).WithBurst().ScheduleParallel();

Moving and rotating objects

        //Moving and rotating objects
        Entities.ForEach
        ((ref LocalTransform localTransform, in TranslateData translateData, in CharacterControllerData characterControllerData) =>
        {

            //Set TransformAspect
            localTransform.Position = translateData.position;
            localTransform.Rotation = translateData.rotation;


            //Debug.Log(" Moving objects " + "  localTransform.Position " + localTransform.Position);
        }
        ).WithBurst().ScheduleParallel();

        //Debug
        Entities.ForEach
        ((ref LocalTransform localTransform, in TranslateData translateData, in CharacterControllerData characterControllerData) =>
        {

            float3 forwardRay = localTransform.Position + localTransform.Forward();
            float3 rightRay = localTransform.Position + localTransform.Right();

            Debug.DrawLine(localTransform.Position, forwardRay, Color.red);
            Debug.DrawLine(localTransform.Position, rightRay, Color.green);


            //Debug.Log(" Moving objects " + "  localTransform.Position " + localTransform.Position);
        }
        ).WithoutBurst().Run();

I made some progress that work in my mono test environment. Going to try and implement it in my ECS systems.

But this should be good enough at the moment.

Mono test

public class MonoControllerTest : MonoBehaviour
{
    public GameObject gameObject;
    public GameObject directionGO;
    public GameObject targetLookGO;

    public bool isPlayer = true;

    Animator animator;
    float rotationDirectionY = 0;

    [Range(-1,1)] float horizontalMovement = 0;
    [Range(-1, 1)] float verticalMovement = 0;

    float mouseX = 0;
    float mouseY = 0;
    CustomTools.CustomAITools customAITools = new CustomTools.CustomAITools();

    // Start is called before the first frame update
    void Start()
    {
        animator = gameObject.GetComponent<Animator>();
    }

    // Update is called once per frame
    void Update()
    {
        float sensitivity = 1f;

        float deltaTime = Time.deltaTime;

        if (isPlayer)
        {
            horizontalMovement = Input.GetAxis("Horizontal");
            verticalMovement = Input.GetAxis("Vertical");

            mouseX = Input.GetAxis("Mouse X");
            mouseY = Input.GetAxis("Mouse Y");

            rotationDirectionY = rotationDirectionY + (mouseX * sensitivity);
        }else
        {
            float3 currentPosition = new float3(gameObject.transform.position.x, gameObject.transform.position.y, gameObject.transform.position.z);
            float3 movementTarget = new float3(directionGO.transform.position.x, directionGO.transform.position.y, directionGO.transform.position.z);
            float3 targetLookPosition = new float3(targetLookGO.transform.position.x, targetLookGO.transform.position.y, targetLookGO.transform.position.z);

           
            rotationDirectionY = customAITools.aiLookDirection(currentPosition, targetLookPosition);
            float2 floatVertHortValues = customAITools.aiMovementInput(currentPosition, movementTarget, rotationDirectionY);
            verticalMovement = floatVertHortValues.x;
            horizontalMovement = floatVertHortValues.y;
            Debug.DrawLine(currentPosition, movementTarget, Color.red);
            Debug.Log("verticalMovement " + verticalMovement + "  horizontalMovement  " + horizontalMovement);

            /*
            rotationDirectionY = aiLookDirection(gameObject, targetLookGO, rotationDirectionY);
            Vector2 floatVertHortValues = aiMovementInput(gameObject, directionGO, targetLookGO, rotationDirectionY, verticalMovement, horizontalMovement);
            verticalMovement = floatVertHortValues.x;
            horizontalMovement = floatVertHortValues.y;
            */
            //Debug.Log("verticalMovement " + verticalMovement + "  horizontalMovement  " + horizontalMovement);
        }

        updateMovement(gameObject, rotationDirectionY, verticalMovement, horizontalMovement);


    }

    void updateMovement(GameObject gameObject, float rotationDirectionY, float verticalMovement, float horizontalMovement)
    {
        float deltaTime = Time.deltaTime;
        float speedModifer = 2f;


        quaternion rotation = (quaternion.AxisAngle(math.up(), rotationDirectionY));

        Vector3 rightMovement = math.mul(rotation, math.right());
        rightMovement.y = 0;
        rightMovement = Vector3.Normalize(rightMovement);
        Vector3 forwardMovement = math.normalizesafe(math.forward(rotation));

        gameObject.transform.rotation = rotation;
        gameObject.transform.position = gameObject.transform.position + (verticalMovement * forwardMovement) * speedModifer * deltaTime;
        gameObject.transform.position = gameObject.transform.position + (horizontalMovement * rightMovement) * speedModifer * deltaTime;

        animator.SetFloat("VelocityX", horizontalMovement);
        animator.SetFloat("VelocityY", verticalMovement);
        // Debug.Log("rightMovement " + rightMovement + "  forwardMovement  " + forwardMovement);
    }


}

aiMovementInput

        public float2 aiMovementInput(float3 currentPosition, float3 movementTarget, float rotationDirectionY)
        {

            quaternion rotation = (quaternion.AxisAngle(math.up(),rotationDirectionY));

            float3 testPosition = movementTarget;

            float3 rightMovement = math.mul(rotation, math.right());
            rightMovement.y = 0;
            rightMovement = math.normalizesafe(rightMovement);
            float3 forwardMovement = math.normalizesafe(math.forward(rotation));



            float verticalMovement = 0;
            float horizontalMovement = 0;
            float3 verticalVector = (verticalMovement * forwardMovement);
            float3 horizontalVector = (horizontalMovement * rightMovement);


            //Check which way to turn
            int testCount = 20;
            float testValue = (1.0f / testCount);
            //Vertical test
            float closestValueVertical = float.MaxValue;
            float closestValueHorizontal = float.MaxValue;
            for (int iV = -testCount; iV < testCount; iV++)
            {
                float verticalTestValue = testValue * iV;
                verticalVector = currentPosition + (verticalTestValue * forwardMovement);
                float distanceVerticalCheck = math.distance(verticalVector, testPosition);
                if (distanceVerticalCheck <= closestValueVertical)
                {
                    closestValueVertical = distanceVerticalCheck;
                    verticalMovement = verticalTestValue;
                }
                //debugGeneralGizmo.PositionDebugCross(verticalVector, 0.1f,Color.red,0.05f);

            }
            //Horizontal test
            for (int iH = -testCount; iH < testCount; iH++)
            {
                float horizontalTestValue = testValue * iH;
                horizontalVector = currentPosition + (horizontalTestValue * rightMovement);
                float distanceHorizontalCheck = math.distance(horizontalVector, testPosition);
                if (distanceHorizontalCheck <= closestValueHorizontal)
                {
                    closestValueHorizontal = distanceHorizontalCheck;
                    horizontalMovement = horizontalTestValue;
                }
                //debugGeneralGizmo.PositionDebugCross(horizontalVector, 0.1f, Color.blue, 0.05f);
            }

            horizontalVector = currentPosition + (horizontalMovement * rightMovement);
            verticalVector = currentPosition + (verticalMovement * forwardMovement);
            debugGeneralGizmo.PositionDebugCross(horizontalVector, 0.1f, Color.blue, 0.05f);
            debugGeneralGizmo.PositionDebugCross(verticalVector, 0.1f, Color.red, 0.05f);

            return new float2(verticalMovement, horizontalMovement);

        }

aiLookDirection

        public float aiLookDirection(float3 currentPosition, float3 targetLookPosition)
        {


            float rotationDirectionY = 0;
            float closestValue = float.MaxValue;
            float degreesToRad = (math.PI / 180f);
            for (int i = 0; i < 360; i++)
            {
                float testRotation = i * degreesToRad;
                quaternion rotation = (quaternion.AxisAngle(math.up(), testRotation));
                float3 testPosition = currentPosition + math.forward(rotation);
                float distanceCheck = math.distance(testPosition, targetLookPosition);
                if (distanceCheck <= closestValue)
                {
                    rotationDirectionY = testRotation;
                    closestValue = distanceCheck;
                }
               
            }
            quaternion rotationDebug = (quaternion.AxisAngle(math.up(), rotationDirectionY));
            float3 testPositionDebug = currentPosition + math.forward(rotationDebug);
            debugGeneralGizmo.PositionDebugCross(testPositionDebug, 0.1f, Color.black, 0.05f);
            return rotationDirectionY;
        }