News Forums RAIN General Discussion and Troubleshooting Custom RAINMotor

Tagged: 

This topic contains 13 replies, has 2 voices, and was last updated by  Other-Jeff 1 month, 2 weeks ago.

Viewing 14 posts - 1 through 14 (of 14 total)
  • Author
    Posts
  • #39528

    Other-Jeff
    Participant

    Hi,

    I’m writign a custom motor to attach RAIn to our movement system, which is based around setting a direction to move in.

    What i am getting is kind of odd. It appears to teleport to the target or at least close to it on the first move. After that
    movement is fine.

    Is there any condition under which the RAIN code would be setting the position of the actor directly in its transform?

    Thanks. Code below in case you can see something else wrong…

    using RAIN.Motion;
    using RAIN.Serialization;
    using UnityEngine;
    [RAINSerializableClass]
    public class UAMotor : RAINMotor
    {
        public AICharacterController controller;
        public override void ApplyMotionTransforms()
        {
            //NOP
        }
        /// <summary>
        /// Turn towards FaceTarget
        /// </summary>
        /// <returns></returns>
        public override bool Face()
        {
            if (IsFacing(FaceTarget.Orientation))
            {
                return true;
            }
            else
            {
                controller.targetRotation = Quaternion.LookRotation(FaceTarget.Position - controller.transform.position, new Vector3(0, 1, 0));
                return false;
            }
        }
        public override bool IsAt(Vector3 aPosition)
        {
            if ((controller.transform.position - aPosition).magnitude <= 0.1)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        public override bool IsAt(MoveLookTarget aTarget)
        {
            return IsAt(aTarget.Position);
        }
        public override bool IsFacing(Vector3 aPosition)
        {
            Vector3 facingVector = aPosition - controller.transform.position;
            float angle = Vector3.Angle(facingVector, controller.transform.forward);
            if (Mathf.Abs(angle) <= 0.1f)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        public override bool IsFacing(MoveLookTarget aTarget)
        {
            return IsFacing(aTarget.Position);
        }
        public override bool Move()
        {
            if (IsAtMoveTarget)
            {
                controller.inputMoveDirectionLocal = Vector3.zero;
                return true;
            }
            else
            {
                controller.inputMoveDirectionLocal = (MoveTarget.Position - controller.transform.position);
                return false;
            }
        }
        public override void Stop()
        {
            controller.inputMoveDirectionLocal = Vector3.zero;
            controller.targetRotation = controller.transform.rotation;
        }
        public override void UpdateMotionTransforms()
        {
            //throw new NotImplementedException();
        }
    }
    #39529

    Other-Jeff
    Participant

    Additional note.

    It appears to be a frame stutter just after I start moving? Our logic keeps a constant speed of motion ergo the jump.

    But why would RAIN cause a stutter just afetr starting a move? This is my behavior code.

    behavior

    #39530

    Other-Jeff
    Participant

    Okay, new behavior. Its not hitching and its getting where its supposed to… but its leaping over obstacles!

    Thats cool, but not what I intended.

    How do I tell it to keep its feet on the ground?

    #39535

    Sigil
    Keymaster

    I don’t see anything in your code that indicates it would add any jumping or similar, but a lot of your work is being done by that AICharacterController class that might be doing the jumping for you.

    On another note, you probably want to add this to your custom motor:

    public override void UpdateMotionTransforms()
    {
        AI.Kinematic.ParentTransform = Matrix4x4.identity;
        AI.Kinematic.Position = AI.Body.transform.position;
        AI.Kinematic.Orientation = AI.Body.transform.rotation.eulerAngles;
    }

    You don’t have to apply the details from our Kinematic (what normally goes in ApplyMotionTransforms) but likely you want RAIN to know about your position so that if you do something like move or face with your behavior tree it knows where you are coming from (in case you want to use the navigation mesh for instance).

    #39558

    Other-Jeff
    Participant

    Thanks, I figured that one out.

    I’m having an odd effect now however where its trying to go directly to the end point of the path. In your architecture, does the Navigator call the motor, or does the motor have to explicitly call the navigator to get a path?

    #39563

    Sigil
    Keymaster

    The motor needs to call the Navigator to get a path. You can also work directly with the RAIN Navigation Mesh (GetPathFinder or CreatePathFinder is the call I think) to get a path finder, and subsequently a path. You can also work with a Unity Nav Mesh Agent, or call their static functions to generate paths, if you are using a Unity navigation mesh.

    Here’s what the BasicMotor does in it’s move call:

    public override bool Move()
    {
        // If we are at our target, we are done
        if (IsAt(MoveTarget))
            return true;
        TurnAngle = 0f;
        AngleToTarget = 0f;
        // The cached move target keeps us from using up extra allocations
        _cachedTarget = AI.Navigator.GetNextPathWaypoint(MoveTarget, _cachedTarget);
        // After allowing the navigator to run, if we are done pathfinding
        // and we don't allow off graph movement, and we are at the last point in the path
        // then we are done
        if (!AllowOffGraphMovement && !AI.Navigator.IsPathfinding)
        {
            if (AI.Navigator.CurrentPath == null || !AI.Navigator.CurrentPath.IsValid)
                return true;
            if (!AI.Navigator.CurrentPath.IsPartial && IsAt(AI.Navigator.CurrentPath.GetWaypointPosition(AI.Navigator.CurrentPath.WaypointCount - 1)))
                return true;
        }
        // If we have a valid move target from our navigator, attempt to head towards it
        if (_cachedTarget.IsValid)
        {
            // Copy the position over so we can keep track of our last known good position
            GoodTarget.VectorTarget = _cachedTarget.Position;
            // Update our angle to target
            Vector3 tTurn = RAIN.Utility.MathUtils.GetLookAtAngles(AI.Kinematic.Position, _cachedTarget.Position, AI.Kinematic.Orientation);
            AngleToTarget = RAIN.Utility.MathUtils.WrapAngle(tTurn.y - AI.Kinematic.Orientation.y);
            SimpleSteering.DoDirectMovement(AI, _cachedTarget.Position, CloseEnoughDistance, CloseEnoughAngle, FaceBeforeMoveAngle, Allow3DMovement, Allow3DRotation);
        }
        // We don't have a valid move target from our navigator, and we don't allow off graph movement, so head to our last known good target
        else if (!AllowOffGraphMovement)
        {
            // Update our angle to target
            Vector3 tTurn = RAIN.Utility.MathUtils.GetLookAtAngles(AI.Kinematic.Position, GoodTarget.Position, AI.Kinematic.Orientation);
            AngleToTarget = RAIN.Utility.MathUtils.WrapAngle(tTurn.y - AI.Kinematic.Orientation.y);
            SimpleSteering.DoDirectMovement(AI, GoodTarget.Position, Mathf.Min(0.001f, CloseEnoughDistance), CloseEnoughAngle, FaceBeforeMoveAngle, Allow3DMovement, Allow3DRotation);
        }
        // We don't have a valid move target from our navigator, but we allow off graph movement, so head straight to our origina move target
        else
        {
            // Update our angle to target
            Vector3 tTurn = RAIN.Utility.MathUtils.GetLookAtAngles(AI.Kinematic.Position, MoveTarget.Position, AI.Kinematic.Orientation);
            AngleToTarget = RAIN.Utility.MathUtils.WrapAngle(tTurn.y - AI.Kinematic.Orientation.y);
            SimpleSteering.DoDirectMovement(AI, MoveTarget.Position, Mathf.Min(0.001f, CloseEnoughDistance), CloseEnoughAngle, FaceBeforeMoveAngle, Allow3DMovement, Allow3DRotation);
        }
        // And update our turn angle if we have angular velocity
        if (AI.Kinematic.Rotation.sqrMagnitude > 0f)
            TurnAngle = AngleToTarget;
        // We return false because we are still moving towards our target
        return false;
    }

    Ours is a little more complex as we’re trying to handle all the possibilities, but perhaps that can point you at the right calls to make.

    #39564

    Sigil
    Keymaster

    Sorry that was the Mecanim Motor and not the Basic Motor, although they will soon be one in the same.

    #39586

    Other-Jeff
    Participant

    Thanks Sigil, just wanted to make sure I was understanding the structure. All good for now!

    #39622

    Other-Jeff
    Participant

    I’m very close now. I see the path and the actor moves to the first border between the mesh node its in and the next mesh node, but then it sticks there, like itsg etting set back there after every move from the on.

    Do I need to do something to tell it to advance to the next target?

    Code below:

    using System;
    using RAIN.Core;
    using RAIN.Motion;
    using RAIN.Navigation.Pathfinding;
    using RAIN.Serialization;
    using UnityEngine;
    [RAINSerializableClass]
    public class UAMotor : RAINMotor
    {
        public AICharacterController controller;
        private RAINPath path;
        private float TurnAngle;
        private float AngleToTarget;
        private MoveLookTarget _cachedTarget;
        private MoveLookTarget GoodTarget = new MoveLookTarget();
        public override void AIInit()
        {
            base.AIInit();
            Allow3DMovement = false;
        }
        public override bool Allow3DMovement
        {
            get
            {
                return false;
            }
            set
            {
                //nop
            }
        }
        public override void ApplyMotionTransforms()
        {
            //NOP
        }
        /// <summary>
        /// Turn towards FaceTarget
        /// </summary>
        /// <returns></returns>
        public override bool Face()
        {
            if (IsFacing(FaceTarget.Orientation))
            {
                return true;
            }
            else
            {
                controller.targetRotation = Quaternion.LookRotation(FaceTarget.Position - controller.transform.position, new Vector3(0, 1, 0));
                return false;
            }
        }
        public override bool IsAt(Vector3 aPosition)
        {
            if ((controller.transform.position - aPosition).magnitude <= 0.1)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        public override bool IsAt(MoveLookTarget aTarget)
        {
            return IsAt(aTarget.Position);
        }
        public override bool IsFacing(Vector3 aPosition)
        {
            Vector3 facingVector = aPosition - controller.transform.position;
            float angle = Vector3.Angle(facingVector, controller.transform.forward);
            if (Mathf.Abs(angle) <= 0.1f)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        public override bool IsFacing(MoveLookTarget aTarget)
        {
            return IsFacing(aTarget.Position);
        }
        public override bool Move()
        {
            // If we are at our target, we are done
            if (IsAt(MoveTarget))
                return true;
            TurnAngle = 0f;
            AngleToTarget = 0f;
            // The cached move target keeps us from using up extra allocations
            _cachedTarget = AI.Navigator.GetNextPathWaypoint(MoveTarget, Allow3DMovement, AllowOffGraphMovement, _cachedTarget);
            // After allowing the navigator to run, if we are done pathfinding
            // and we don't allow off graph movement, and we are at the last point in the path
            // then we are done
            if (!AllowOffGraphMovement && !AI.Navigator.IsPathfinding)
            {
                if (AI.Navigator.CurrentPath == null || !AI.Navigator.CurrentPath.IsValid)
                    return true;
                if (!AI.Navigator.CurrentPath.IsPartial && IsAt(AI.Navigator.CurrentPath.GetWaypointPosition(AI.Navigator.CurrentPath.WaypointCount - 1)))
                    return true;
            }
            // If we have a valid move target from our navigator, attempt to head towards it
            if (_cachedTarget.IsValid)
            {
                // Copy the position over so we can keep track of our last known good position
                GoodTarget.VectorTarget = _cachedTarget.Position;
                // Update our angle to target
                Vector3 tTurn = RAIN.Utility.MathUtils.GetLookAtAngles(AI.Kinematic.Position, _cachedTarget.Position, AI.Kinematic.Orientation);
                AngleToTarget = RAIN.Utility.MathUtils.WrapAngle(tTurn.y - AI.Kinematic.Orientation.y);
                DoDirectMovement(AI, _cachedTarget.Position, CloseEnoughDistance, CloseEnoughAngle, FaceBeforeMoveAngle, Allow3DMovement, Allow3DRotation);
            }
            // We don't have a valid move target from our navigator, and we don't allow off graph movement, so head to our last known good target
            else if (!AllowOffGraphMovement)
            {
                // Update our angle to target
                Vector3 tTurn = RAIN.Utility.MathUtils.GetLookAtAngles(AI.Kinematic.Position, GoodTarget.Position, AI.Kinematic.Orientation);
                AngleToTarget = RAIN.Utility.MathUtils.WrapAngle(tTurn.y - AI.Kinematic.Orientation.y);
                DoDirectMovement(AI, GoodTarget.Position, Mathf.Min(0.001f, CloseEnoughDistance), CloseEnoughAngle, FaceBeforeMoveAngle, Allow3DMovement, Allow3DRotation);
            }
            // We don't have a valid move target from our navigator, but we allow off graph movement, so head straight to our origina move target
            else
            {
                // Update our angle to target
                Vector3 tTurn = RAIN.Utility.MathUtils.GetLookAtAngles(AI.Kinematic.Position, MoveTarget.Position, AI.Kinematic.Orientation);
                AngleToTarget = RAIN.Utility.MathUtils.WrapAngle(tTurn.y - AI.Kinematic.Orientation.y);
                DoDirectMovement(AI, MoveTarget.Position, Mathf.Min(0.001f, CloseEnoughDistance), CloseEnoughAngle, FaceBeforeMoveAngle, Allow3DMovement, Allow3DRotation);
            }
            // And update our turn angle if we have angular velocity
            if (AI.Kinematic.Rotation.sqrMagnitude > 0f)
                TurnAngle = AngleToTarget;
            // We return false because we are still moving towards our target
            return false;
        }
        private void DoDirectMovement(AI aI, Vector3 position, float closeEnoughDistance, float closeEnoughAngle, float faceBeforeMoveAngle, bool allow3DMovement, bool allow3DRotation)
        {
            Vector3 direction = (position - AI.Body.transform.position).normalized;
            controller.inputMoveDirectionLocal = AI.Body.transform.InverseTransformDirection(direction);
        }
        public override void Stop()
        {
            controller.inputMoveDirectionLocal = Vector3.zero;
            controller.targetRotation = controller.transform.rotation;
        }
        public override void UpdateMotionTransforms()
        {
            AI.Kinematic.ParentTransform = Matrix4x4.identity;
            AI.Kinematic.Position = AI.Body.transform.position;
            AI.Kinematic.Orientation = AI.Body.transform.rotation.eulerAngles;
        }
    }
    #39637

    Sigil
    Keymaster

    The AI.Navigator.GetNextPathWaypoint(MoveTarget, Allow3DMovement, AllowOffGraphMovement, _cachedTarget) call should automatically move on to the next path point once it thinks it got there. See if doing this improves your results (I think the defaults aren’t set right now):

    ...
        public override void AIInit()
        {
            base.AIInit();
            Allow3DMovement = false;
            Allow3DRotation = false;
            AllowOffGraphMovement = true;
            CloseEnoughDistance = 0.1f;
            CloseEnoughAngle = 0.1f;
        }
    ...

    If CloseEnoughDistance was zero it’d explain why the navigator never moved past the first point, as the chances of getting *exactly* on the path point is fairly slim.

    #39640

    Other-Jeff
    Participant

    Closer… he gets to the first waypoint and stops, but does not proceed to next waypoint?

    #39645

    Other-Jeff
    Participant

    Got it all working, thanks!

    #39647

    Sigil
    Keymaster

    What ended up being the cause?

    #39658

    Other-Jeff
    Participant

    Two things. The first was that someone put a second large collider on my monster for no apparent reason and it was hanging up.

    Secondarily, I had some issues with getting the angle of facing right.

    But its all working now

    New questions though on actions… but Ill post on a nwe thread.

Viewing 14 posts - 1 through 14 (of 14 total)

You must be logged in to reply to this topic.