Search Unity

  1. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice
  2. Ever participated in one our Game Jams? Want pointers on your project? Our Evangelists will be available on Friday to give feedback. Come share your games with us!
    Dismiss Notice

Motion Controller

Discussion in 'Assets and Asset Store' started by Tryz, Feb 21, 2014.

  1. Censureret

    Censureret

    Joined:
    Jan 3, 2017
    Posts:
    243
    Hey guys. Is there a way to regenerate the body shapes (colliders) doing runtime? ive tried the following:

    Code (CSharp):
    1.  Collider[] lColliders = mMotionController.ActorCore.gameObject.GetComponents<Collider>();
    2.         for (int i = 0; i < lColliders.Length; i++)
    3.         {
    4.             lColliders[i].enabled = true;
    5.         }
    However this doesnt regenerate the body shapes and the lClliders count is 0.
     
  2. TeagansDad

    TeagansDad

    Joined:
    Nov 17, 2012
    Posts:
    880
    @Censureret There isn't actually a one-method call to do this, oddly enough. Just curious, why do you need to regenerate the body shapes?

    The body shapes are managed by the ActorController; in its Start() method, it initializes the body shapes with:

    Code (CSharp):
    1. // Create the body shapes from the definitions
    2.             DeserializeBodyShapes();
    3.  
    4.             // Create the unity colliders as needed
    5.             for (int i = 0; i < BodyShapes.Count; i++)
    6.             {
    7.                 if (BodyShapes[i]._UseUnityColliders)
    8.                 {
    9.                     BodyShapes[i].CreateUnityColliders();
    10.                 }
    11.             }
    Most of the work is done in DeserializeBodyShapes(). This method removes all existing BodyShape instances and then parses the serialized JSON data which represents the body shapes and creates new BodyShape instances.

    If you have called ActorController.RemoveBodyShapes(), then this won't work, as that method clears the List<string> which contains all of the serialized JSON data. The Death and BasicDeath motions call this method, as does the BasicKilledReactor.

    I'm not sure why RemoveBodyShapes() needs to clear the serialized data. I think that I'll give the method a bool parameter which defaults to false which will be checked before clearing it. I can also refactor the above code from Start() into a public method to recreate the body shapes.
     
    hopeful likes this.
  3. Bucee

    Bucee

    Joined:
    Apr 20, 2016
    Posts:
    12
    Hey I wanted to say thanks again for the help a few months ago, great stuff. I started up some more editing of the BasicRangedAttack code and I've run into another pickle with that file. I switched the whole firing process to operate under a new set of triggers that I added in, but every time the player shoots an arrow he then tries to shoot a second shot without player input. My debugging tells me that he's actually ignoring the new shooting requirements I've placed throughout the BasicRangedAttack.cs functions/update loop, and is instead reacting to "Animation Events" and the "Activate" function. Sometimes he doesn't just reach for an arrow, he actually manages to go through the whole firing loop without player input.

    Is there a place I can find information on manipulating these "Animation Events"? The ReadMe doesn't go into detail enough for me to find it in the program myself, and restricting these parts of code at all just causes the entire shooting process to stop.
     
  4. Censureret

    Censureret

    Joined:
    Jan 3, 2017
    Posts:
    243
    Hey thank you for your response. I need this to resurrect a character that has been killed using the sword and shield motion controller pack any idea on how i can do that?
     
  5. p_hergott

    p_hergott

    Joined:
    May 7, 2018
    Posts:
    349
    I thought i had this, but its not working, could someone give a example of stats modifying damage
     
  6. TeagansDad

    TeagansDad

    Joined:
    Nov 17, 2012
    Posts:
    880
    After digging into the code a bit, it seems that the serialized JSON data for the body shapes is modified every time you add or remove one or more Body Shapes. Perhaps the best approach, at least for now, is to simply have the component save a copy of the original serialized data when it starts up.

    Add a new field, might as well put it directly beneath mBodyShapeDefinitions:

    Code (CSharp):
    1. protected List<string> mOriginalBodyShapeDefinitions = new List<string>();
    Add two new methods (might as well place them with the other Add/Remove Body Shape methods around line 4420):

    Code (CSharp):
    1.         public void CreateBodyShapes()
    2.         {
    3.             DeserializeBodyShapes();
    4.  
    5.             for (int i = 0; i < BodyShapes.Count; i++)
    6.             {
    7.                 if (BodyShapes[i]._UseUnityColliders)
    8.                 {
    9.                     BodyShapes[i].CreateUnityColliders();
    10.                 }
    11.             }
    12.         }
    13.  
    14.         public void RestoreBodyShapes()
    15.         {
    16.             mBodyShapeDefinitions.Clear();
    17.             mBodyShapeDefinitions.AddRange(mOriginalBodyShapeDefinitions);
    18.             CreateBodyShapes();
    19.         }
    In the Start() method (around line 11105), replace:

    Code (CSharp):
    1.            // Create the body shapes from the definitions
    2.             DeserializeBodyShapes();
    3.  
    4.             // Create the unity colliders as needed
    5.             for (int i = 0; i < BodyShapes.Count; i++)
    6.             {
    7.                 if (BodyShapes[i]._UseUnityColliders)
    8.                 {
    9.                     BodyShapes[i].CreateUnityColliders();
    10.                 }
    11.             }
    With:

    Code (CSharp):
    1.             // Store a copy of the body shape definitions so that they can be restored later
    2.             mOriginalBodyShapeDefinitions.Clear();
    3.             mOriginalBodyShapeDefinitions.AddRange(mBodyShapeDefinitions);
    4.  
    5.             CreateBodyShapes();
    To summarize: when ActorController starts up, it stores a copy of the Body Shape definition data before it creates the Body Shape instances. When Body Shape instances are added or removed, the definition data is modified to keep it in sync with the instances. When you want to recreate the original Body Shapes, call RestoreBodyShapes() and it will clear the modified definitions and replace them with the originals before creating the Body Shape instances.

    Let me know if this works for you; if it does I'll test it further and then include it in the next Actor Controller update.
     
    Subliminum likes this.
  7. TeagansDad

    TeagansDad

    Joined:
    Nov 17, 2012
    Posts:
    880
    You'll need to create a class that inherits from WeaponCore and override the GetAttackDamage() function:

    Code (CSharp):
    1.        public virtual float GetAttackDamage(float rPercent = 1f, float rMultiplier = 1f)
    2.         {
    3.             return (_MinDamage + ((_MaxDamage - _MinDamage) * rPercent)) * rMultiplier;
    4.         }
    If it's a melee weapon, you can inherit from SwordCore instead (it inherits from WeaponCore and adds some sword sound effects, etc); SwordCore doesn't make any changes to the damage calculation.

    In WeaponCore's OnImpact() method, it builds the CombatMessage that includes the damage value:

    Code (CSharp):
    1.             // Put together the combat info. This will will be modified over time
    2.             CombatMessage lMessage = CombatMessage.Allocate();
    3.             lMessage.Attacker = mOwner;
    4.             lMessage.Defender = rHitInfo.Collider.gameObject;
    5.             lMessage.Weapon = this;
    6.             lMessage.Damage = GetAttackDamage(1f, (rAttackStyle != null ? rAttackStyle.DamageModifier : 1f));
    7.             lMessage.ImpactPower = GetAttackImpactPower();
    8.             lMessage.HitPoint = rHitInfo.Point;
    9.             lMessage.HitDirection = lHitDirection;
    10.             lMessage.HitVector = rHitInfo.Vector;
    11.             lMessage.HitTransform = lHitTransform;
    12.             lMessage.AttackIndex = mAttackStyleIndex;
    13.             lMessage.CombatStyle = rAttackStyle;
    So we can override GetAttackDamage() to use the attacker's attributes to calculate the damage. For example:

    Code (CSharp):
    1.         public override float GetAttackDamage(float rPercent = 1f, float rMultiplier = 1f)
    2.         {
    3.             var lAttributes = mOwner.GetComponent<BasicAttributes>();
    4.             if (lAttributes == null) { return 0; }
    5.  
    6.             float lStrength = lAttributes.GetAttributeValue<float>("Strength");
    7.             float lSkill = lAttributes.GetAttributeValue<float>("MeleeSkill");
    8.  
    9.             float lBaseDamage = Random.Range(_MinDamage, _MaxDamage) + (lStrength * 0.33f) + (lSkill * 0.5f);
    10.             return (lBaseDamage * rPercent) * rMultiplier;
    11.         }

    The values in the CombatMessage can and will be modified as the message is passed around. When using the default setup, the BasicDamagedReactor will apply the damage in its Activate() method before triggering the Damaged or Death animation:

    Code (CSharp):
    1.          float lRemainingHealth = 0f;
    2.             if (mActorCore.AttributeSource != null)
    3.             {
    4.                 lRemainingHealth = mActorCore.AttributeSource.GetAttributeValue<float>(HealthID) - ((DamageMessage)mMessage).Damage;
    5.                 mActorCore.AttributeSource.SetAttributeValue(HealthID, lRemainingHealth);
    6.             }

    You could create your own custom reactor to replace BasicDamagedReactor where you decrease the damage based on the target's "Armor" attribute:

    Code (CSharp):
    1.            if (mActorCore.AttributeSource != null)
    2.             {
    3.                 var lArmor = mActorCore.AttributeSource.GetAttributeValue<float>("Armor");
    4.                 lRemainingHealth = mActorCore.AttributeSource.GetAttributeValue<float>(HealthID) - (((DamageMessage)mMessage).Damage - lArmor);
    5.                 mActorCore.AttributeSource.SetAttributeValue(HealthID, lRemainingHealth);
    6.             }
    As I wrote this up, it occurred to me that I could write up an "IDamageCalculation" interface with a default "DamageCalculation" implementation that WeaponCore can pull from in GetAttackDamage(), and that would save the hassle of having to subclass from WeaponCore, SwordCore, etc just to change how damage is calculated.
     
    Subliminum likes this.
  8. p_hergott

    p_hergott

    Joined:
    May 7, 2018
    Posts:
    349
    Wow awesome and fast response tho.
     
    Subliminum likes this.
  9. TeagansDad

    TeagansDad

    Joined:
    Nov 17, 2012
    Posts:
    880
    Sorry, I missed seeing your post earlier.

    The first place to start is the Unity manual page on Using Animation Events.

    All of the Animation Events used by Motion Controller follow the same pattern. The function is named OnAnimationEvent and a string parameter is specified.

    When an Animation Event is triggered by a character's animation, it is received by the Motion Controller's OnAnimationEvent() method. This in turn calls OnAnimationEvent() on all of the motions that it manages.

    The motions which have an OnAnimationEvent() method defined will all have a static or const string event parameter name near the top of the file. When the Animation Event with the matching string parameter triggers, the motion will handle the event. I don't have the code in front of me to look at, so I can't tell you which event parameters BasicRangedAttack handles.

    Hopefully that answered your question. Let me know if I need to explain further/differently. :)
     
    Subliminum likes this.
  10. Bucee

    Bucee

    Joined:
    Apr 20, 2016
    Posts:
    12
    Thanks a ton, I understand everything now! I decided to put a return false on the Activate function to block the animation from starting up again if the player doesn't give input and that's working out.
     
  11. Twoonebe

    Twoonebe

    Joined:
    Mar 30, 2013
    Posts:
    174
    Hi all,
    when i change my scene with my scenemanager, how can i handle that the tpmc goes with the model to the next scene ?
     
  12. TeagansDad

    TeagansDad

    Joined:
    Nov 17, 2012
    Posts:
    880
    @Twoonebe - You can try adding a simple component to the player that calls Object.DontDestriyOnLoad().

    Alternatively, you could also have the player in a "startup" scene that stays loaded and your environment scenes get loaded additively. If your scene manager supports that, of course.
     
  13. Twoonebe

    Twoonebe

    Joined:
    Mar 30, 2013
    Posts:
    174
    @TeagansDad thank you that works but i have a little problem i want spawn the player on a fix point after scene switch ?
    Thank you for help
     
  14. TeagansDad

    TeagansDad

    Joined:
    Nov 17, 2012
    Posts:
    880
    You'll basically need to teleport the player there when the scene loads. I.e set the player's transform to the coordinates of the spawn point.
     
  15. p_hergott

    p_hergott

    Joined:
    May 7, 2018
    Posts:
    349
    Just off the top of your head. All the packs run a different int in the animator, eg. 100, or 200. And the attacks correspond with those. 201. 202 ect. To add a completely new animation set. Aslong as i dont use the same (obviously). I could make essentially a pile of weapon layers, eg 800s, 900s. Which i have done, and everything tends to work, but once in a while character gets stuck on idle. For this situation have any tips or key thoughts on it?
     
  16. Subliminum

    Subliminum

    Joined:
    Nov 9, 2017
    Posts:
    94
    could be that an attack animation doesn't have a animation event or is missing a part of the sequence your motion is using. Is the character in the MC's Idle Motion or just animating/visibly idle whilst in a different motion? Could also be that your custom Motion ID's are in too low a range and are conflicting with some MC motions. 10,000+ is a pretty safe bet
     
    p_hergott likes this.
  17. luekio

    luekio

    Joined:
    Jan 23, 2018
    Posts:
    22
    Hey all, I was wondering if an advanced climbing system was ever integrated into the controller?
     
  18. Subliminum

    Subliminum

    Joined:
    Nov 9, 2017
    Posts:
    94
    TeagansDad likes this.
  19. Censureret

    Censureret

    Joined:
    Jan 3, 2017
    Posts:
    243
    Hey, guys, this is a performance question for the Archery motion pack.

    So currently, when an arrow is fired, it is instanciated on the go, then fired, and then it checks for collision using raycast.

    Now, this is fine if you have a single (user / player / npc) firing arrows.

    However, this doesn't scale well! As soon as you add just five archers who fire at a rate one every thrid seconds, you are going to be spawning so many objects that it is visible in the profiler as a performance issue.

    In my game, we are going to have atleast 10 - 15 + Archers firing arrows constantly, so I am bound by law to use object pooling and, therefore, my question.

    How / where would I need to edit the controller to accommodate an object pool?

    Any help will be much appreciated.
     
  20. TeagansDad

    TeagansDad

    Joined:
    Nov 17, 2012
    Posts:
    880
    @Censureret - I initially figured that you could do this with just a couple of changes to BowCore.cs, but when I actually tried it out, I had to make a few more changes. Check your PM.
     
  21. Yerkisk

    Yerkisk

    Joined:
    Jul 28, 2015
    Posts:
    32
    Hi guys. I have modified ootii's framework to fit our needs here, which was for a client/server game with the server being authoritative (as in, the server sends the client specific events to start motions).

    Everything works fine except for one specific case :

    - First, when we receive the order to play an animation from the server, we check if we're in a motion that has to be cancelled. If so, we deactivate the motion and start the new one :

    Code (CSharp):
    1. if (mMotionController.ActiveMotion != null && mMotionController.ActiveMotion.Category != EnumMotionCategories.IDLE && mMotionController.ActiveMotion.Category != EnumMotionCategories.DEATH)
    2.             {
    3.                 mMotionController.ActiveMotion.Deactivate();
    4.             }
    5.  
    6.             LoopedAction motion = mMotionController.GetMotion<LoopedAction>();
    7.             if (motion != null)
    8.             {
    9.                 mMotionController.ActivateMotion(motion, parameter);
    10.             }
    The problem is, for some reasons sometimes the TestUpdate method of the LoopedAction motion is interrupted right at the start of the new motion by this :

    Code (CSharp):
    1. // If we've reached the exit state, leave. The delay is to ensure that we're not in an old motion's exit state
    2.             if (mAge > 0.2f && mMotionController.State.AnimatorStates[mMotionLayer._AnimatorLayerIndex].StateInfo.IsTag("Exit"))
    3.             {
    4.                 Debug.Log("LoopedAction : mAge is " + mAge.ToString() + " and Exit state reached for LoopedAction motion");
    5.                 return false;
    6.             }
    Yet, the first animation we play in the LoopedAction motion is not set with an Exit tag, which makes me believe that the animator state of the motion controller is actually looking at the old motion we previously deactivated. Might be due to lag or something, not sure. Please note that this is in the editor, haven't had time to test it out on an android device yet as I was trying to fix this prior to running the build process.

    All 'exit' animations are using a 0.1 frame long 'Pose' animation, so I can't figure out why that condition would force the new motion to exit early. I could increase the mAge condition to higher than 0.2f, but that doesn't seem like the way to go...

    Any idea what could be causing this?

    Thanks
     
  22. Censureret

    Censureret

    Joined:
    Jan 3, 2017
    Posts:
    243
    has anyone had this issue:

    The collision on the free-look camera is really acting up with the terrain throwing me hundred of feet away from where the player was. making it completely unusable.

    has anyone created their own free flight motor that works in a tightly packed terrain?
     

    Attached Files:

  23. TeagansDad

    TeagansDad

    Joined:
    Nov 17, 2012
    Posts:
    880
    I had never used the free flight camera until I checked this out, but I was able to reproduce this behaviour in my standard development scene (the Quick Start Demo Template in _Demos).

    From a quick perusal of the code, it appears that the Vector3 lNewFocusPosition that is obtained from within CameraController.UpdateCollisions() is always being determined as (0, 0, 0) when there's no Anchor assigned to the CameraController. When the camera collides, its transform gets set to lNewFocusPosition plus a calculated distance and direction. So for me, I was always getting bounced back to the origin in the scene.

    I suspect that you'd want to use the Camera Controller's position or an offset from it as the focus position. I'll try that out when I have the chance, but if you want to try it before I get to it, please let me know if it works. :cool:
     
  24. TeagansDad

    TeagansDad

    Joined:
    Nov 17, 2012
    Posts:
    880
    Your theory sounds very plausible. To be honest, I don't have any experience using these assets for client/server applications.

    What if you make your "exit" tags motion-specific? Say you use "LoopedActionExit" in the animator and when you check in the LoopedAction motion. If it's due to a lag, then the StateInfo tag should still be "Exit" and TestUpdate() won't return false.
     
  25. Yerkisk

    Yerkisk

    Joined:
    Jul 28, 2015
    Posts:
    32
    That's an idea, I'll test it out. For the records, increasing the mAge condition to 0.3f instead of 0.2f seems to also have fixed that particular issue, so yeah I do believe that the animationStates are lagging behind for some motions. The editor is often slower than the real devices.

    And you don't need a client/server for what I'm explaining, I was just giving the context. Every piece of code I mentioned is run on the client only. Anyone that would call deactivate and activate on motions rather than using actions would probably face the same issues.

    Thanks for your help!
     
  26. Yerkisk

    Yerkisk

    Joined:
    Jul 28, 2015
    Posts:
    32
    Thinking of it, that probably won't work, as two actions with the same motions could be called one after the other, and they both will check the same exitLoopedAction tag...

    Is there a unique ID for a motion being activated? Best bet would probably to be able to check the active motions by their unique ID rather than test each frame if we've reached some random exit statement after an arbitrary (0.2f) amount of time.
     
  27. Censureret

    Censureret

    Joined:
    Jan 3, 2017
    Posts:
    243
    I am not exactly sure where to do this and what to set it to? can you perhabs give me a snippet?
     
  28. FargleBargle

    FargleBargle

    Joined:
    Oct 15, 2011
    Posts:
    683
    I've finally taken another look at climbing non-vertical ladders, as originally discussed in posts #6242 and #6265. Following Tim's advice, I've written a script that tilts the player while in a trigger. It works great as long as the ladder is lined up with the X or Z axis, but gets trickier if the ladder has some Y rotation as well. And if the ladder rotation changes during the game, the preset values no longer work. It seems that the ActorController Tilt value uses world space coordinates. Is there another way of doing this that uses the player's local space instead? Then I'd just need to line him up with the ladder and apply a simple X rotation instead of some combination of X and Z. I'm sure there's a simpler, more foolproof way to accomplish this, but it's eluding me. Here's my existing script, which you're all free to use and modify if you like. Any suggestions for improvements are welcome.

    Code (CSharp):
    1. using UnityEngine;
    2. using com.ootii.Actors;
    3.  
    4. public class TriggeredPlayerTilt : MonoBehaviour
    5. {
    6.  
    7.     private GameObject myPlayer; // Player GameObject - must have "Player" tag
    8.     private ActorController lActorController; // Actor Controller
    9.     public float tiltX = 0f; // X rotation amount
    10.     public float tiltZ = 0f; // Z rotation amount
    11.     private Vector3 rotationVector; // Vector3 rotation values
    12.     private Quaternion newAngle;  // Calculated new rotation that will be applied to player
    13.     public float startDelay = 0.0f; // Optional delay before rotating player
    14.  
    15.     void Start()
    16.     {
    17.         myPlayer = GameObject.FindWithTag("Player"); // Find Player
    18.         lActorController = myPlayer.GetComponent<ActorController>(); // Get ActorController
    19.     }
    20.  
    21.     void OnTriggerStay(Collider other)
    22.     {
    23.         if (other.tag == "Player")
    24.         {
    25.             rotationVector = new Vector3(tiltX, lActorController.Rotation.y, tiltZ); // Set rotation vector
    26.             newAngle = Quaternion.Euler(rotationVector); // Calculate new rotation
    27.             lActorController.Tilt = newAngle; // Set new rotation
    28.         }
    29.     }
    30.  
    31.     void OnTriggerExit(Collider other)
    32.     {
    33.         if (other.tag == "Player")
    34.         {
    35.             lActorController.Tilt = Quaternion.Euler(0, lActorController.Rotation.y, 0); // Return player to normal rotation
    36.  
    37.         }
    38.     }
    39. }

    Invaders_Ladder1.png
     
    Last edited: Mar 9, 2020
    TeagansDad likes this.
  29. JammyGitGames

    JammyGitGames

    Joined:
    Jan 25, 2020
    Posts:
    15
    Hi, hopefully this is an easy question. I have the Ootii motion controller and the Ootii Camera controller. I am writing my own controller for a vehicle, I was wondering how I access ‘Input Source’ to get input from their in my own script.
     
  30. TeagansDad

    TeagansDad

    Joined:
    Nov 17, 2012
    Posts:
    880
    That depends on how you've designed things, of course. ;)

    The easiest way would probably be to access directly it from the MotionController on the player when the player enters the vehicle. As you'll want to stop the MC from controlling the character once entering the vehicle, you can set MotionController.IsEnabled = false when the character enters the vehicle and then get a reference to the existing InputSource via MotionController.InputSource.

    If you're using a different InputSource for the vehicle, or if the above method doesn't work for you, there is a helper class called InputSourceLocator (in Assets/ootii/Assets/Framework_v1/Input) with two static methods:

    Code (CSharp):
    1. public static IInputSource GetInputSource(GameObject rOwner)
    2.  
    3. public static GameObject GetInputSourceOwner(GameObject rGameObject, out IInputSource rInputSource)
    The first method is just a simple attempt to obtain a component that implements IInputSource from the GameObject passed in.

    The second method first attempts to obtain the IInputSource reference from the specified GameObject; if that fails, then it finds all objects in the scene which contain a component implementing IInputSource and returns the the first one that is enabled (both the GameObject and the IInputSource component). A few of the classes (such as MotionController) use this exact same logic to find an InputSource; I just extracted the logic into an external helper method.
     
  31. TeagansDad

    TeagansDad

    Joined:
    Nov 17, 2012
    Posts:
    880
    Unfortunately, I can't really give you much more than what I wrote previously in terms of a solution, but I can point you to where you probably need to look.

    In CameraMotor.cs:

    Code (CSharp):
    1.         public virtual Vector3 GetFocusPosition(Quaternion rCameraRotation)
    2.         {
    3.             Transform lAnchorTransform = Anchor;
    4.  
    5.             Vector3 lAnchorOffset = AnchorOffset;
    6.             Vector3 lAnchorPosition = AnchorPosition;
    7.             Vector3 lNewFocusPosition = (rCameraRotation.Right() * _Offset.x) + (rCameraRotation.Forward() * _Offset.z);
    8.  
    9.             if (lAnchorTransform != null)
    10.             {
    11.                ...
    12.             }
    Most of the logic for determining the focus position is within that if (lAnchorTransform != null) block. I tried adding an else block that set lNewFocusPosition to the position of the CameraController when lAnchorTransform is null, but it didn't work. As in, it didn't change anything. I'm not 100% certain that this is where to make changes, but it's the best starting point I could find. If @Tryz is listening, maybe he can pinpoint what needs to be changed.

    I have added this to my to-do list for the next significant update to CameraController, but I have a few other pieces that I'm currently working on that I want to finish first.
     
    Subliminum likes this.
  32. JammyGitGames

    JammyGitGames

    Joined:
    Jan 25, 2020
    Posts:
    15
    Many thanks for the speedy response. I think that's exactly what I needed.
     
    TeagansDad likes this.
  33. TeagansDad

    TeagansDad

    Joined:
    Nov 17, 2012
    Posts:
    880
    Not really, but there is a read-only Key property (string) on MotionControllerMotion that wraps the public _Key field. There are a handful of references to this _Key field in the code, but it doesn't appear to actually be used for anything right now. It's editable in the inspector if you Show Details and then click the blue gear:

    upload_2020-3-10_10-14-44.png

    So you could try using that _Key field to identify motions.
     
  34. TeagansDad

    TeagansDad

    Joined:
    Nov 17, 2012
    Posts:
    880
    Sorry for the delay -- I missed seeing this when you posted it.

    If the character is getting stuck, check that all of the Idle pose states in your BasicMeleeAttack-SM state machine have the "Exit" tag set, as that's what the motion uses to check when it's time to deactivate:

    upload_2020-3-10_10-20-44.png

    It's also worth double-checking to make sure that all of the layer-specific parameter names match the index of the animator layer on which the state machine exists. So on the base layer, you should be using L0MotionPhase, L0MotionForm, and L0MotionParameter.
     
  35. Yerkisk

    Yerkisk

    Joined:
    Jul 28, 2015
    Posts:
    32
    To my understanding, that would be motion-specific, and not instance of a motion, meaning that it would result in the same behavior. Continuing with my example a few posts above, calling a LoopedAction after another LoopedAction would both look for the same _Key field as it's set on the LoopedAction motion.

    What we need is some identifier that would change when a motion is activated, and kept in the animationState. That would allow a motion to verify in the TestUpdate call if the identifier is for the current motion and if the exit state has been reached for that particular motion.

    Otherwise the framework is just prone to errors as the animation states are not synced with the activations of a new motion. I just had a motion exit because mAge was over 0.4f and the exit tag was reached when calling two times the same motion one after the other, which made the second call be cancelled right at the start of the animation.
     
  36. Yerkisk

    Yerkisk

    Joined:
    Jul 28, 2015
    Posts:
    32
    For now, I've temporarily fixed it by implementing an activation timer on Activate() of each motions and checking for that rather than mAge in each TestUpdate(). So far it seems to work.
     
  37. TeagansDad

    TeagansDad

    Joined:
    Nov 17, 2012
    Posts:
    880
    Glad to hear that!

    I've been giving this some thought... if each activation "instance" needs to be tracked, then the we could generate an ActivationID upon the activation of each Motion and store it in the AnimatorLayerState for the corresponding Motion Layer. A System.Guid seems to be the appropriate data type to use, but I don't know how that would affect memory allocations and Garbage Collection if we're generating them frequently.

    At the very least, it would probably be best to have that disabled by default and have a toggle field in the inspector to enable generating and tracking Activation IDs that motion.
     
  38. Yerkisk

    Yerkisk

    Joined:
    Jul 28, 2015
    Posts:
    32
    Yeah I was thinking the same, and figured that it's something you probably wouldn't want on very short animations looping, poses, and such. But really just checking for the age of the current motion rather than the age set by the motion controller has fixed the issue. I didn't check where mAge is set/reset in the framework, but there is really something wrong with using it if you're calling motions directly as it's not reset to 0 when you activate a new motion.

    By setting an age parameter in the motions directly and assigning it on the Activate, and then testing that rather than mAge in TestUpdate seems way less error prone as I'm sure it gets the exact age of the current motion I'm on. For the records, before doing this, mAge often gave me false age of over 0.4f on newly activated motions.
     
  39. TeagansDad

    TeagansDad

    Joined:
    Nov 17, 2012
    Posts:
    880
    mAge is reset to 0 in the base virtual MotionControllerMotion.Activate() method. Motions which inherit from MotionControllerMotion should all be returning base.Activate(rPrevMotion) at the end of their override Activate() methods. Generally, this is immediately after setting the animator motion phase:

    Code (CSharp):
    1.         public override bool Activate(MotionControllerMotion rPrevMotion)
    2.         {
    3.             mActiveForm = (_Form > 0 ? _Form : mMotionController.CurrentForm);
    4.             mMotionController.SetAnimatorMotionPhase(mMotionLayer.AnimatorLayerIndex, PHASE_START, mActiveForm, mParameter, true);
    5.  
    6.             // Finalize the activation
    7.             return base.Activate(rPrevMotion);
    8.         }
    Are you running into this issue with the include motions, or only with custom motion scripts?
     
  40. bobAZ2019

    bobAZ2019

    Joined:
    Mar 28, 2019
    Posts:
    13
    hi,

    if i use a character that already got animations but i just want to use MC to control the character, is there a tutorial for that? i read the doc & video but it doesn't have anything related to what i wanted to do.

    thanks in advance.
     
  41. TeagansDad

    TeagansDad

    Joined:
    Nov 17, 2012
    Posts:
    880
    If you want to be able to use an existing animator controller without making too many changes to it, you would need to write your own motion classes for everything, rather than using the included motions. See the Motion Builder's Guide for details on how to do that.

    If you don't want to write custom motion scripts, then you would need to re-create your animator controller with the structure expected by Motion Controller. You would likely use the "Basic" set of motions (BasicIdle, BasicWalkRunPivot, etc) and you would extend those animator state machines by using a different Motion Form parameter for each of the animation sets that you are using.

    For example, the included Idle and Walk/Run animations use Motion Form 0. Each of the Motion Packs that extend the functionality of Motion Controller use free Mixamo animations by default, and each set is assigned a different Motion Form:

    • 100 - Sword and Shield Motion Pack
    • 200 - Archery Motion Pack
    • 300 - Spellcasting Motion pack
    • 500 - Shooter Motion Pack (Rifle)
    • 550 - Shooter Motion Pack (Pistol)
    If you're just looking for a character controller that doesn't have any baked-in assumptions as to how you have your animator controller structured, then the Actor Controller might be a better option for you (Actor Controller is included with Motion Controller).
     
  42. Yerkisk

    Yerkisk

    Joined:
    Jul 28, 2015
    Posts:
    32
    You are right, and after more testing, adding a parameter for Age in the activate didn't change anything. Sometimes it still exit the animation right after starting it due to the age parameter being over 0.2f and the exit state of the old animation being reached.

    I use a mix of included motions and custom motions from maximo. Unfortunately there's no 'looped' actions the way I want them in the included motions so I had to construct my own. They are all working fine when run separately, but often my LoopedAction get interrupted when it's following a basic melee attack (included motion). I've tried with and without the auto-generated code in the class (as one issue I had last year was due to not having the auto-generated code).

    The Looped motion is split into 3 different motions (start-loop-end) and only the End motion has a transition to an Exit-tagged state. The motion stays in loop until it receive an order to finish the casting, so there's no way it reaches an Exit state at the start (validated that by looking at the animator at runtime). It activate the Start motion, but then TestUpdate deactivate the motion due to the age and exit state. Only reason for that to happen is, imo, due to the animation states not being in sync with the new activated motion.

    Is there a way to force a refresh of the animation states when calling Activate on a motion? Or as I said a few post back, another way to detect when the testupdate should deactivate a motion?

    Thanks
     
  43. Frimus

    Frimus

    Joined:
    May 28, 2014
    Posts:
    2
    Hi, can anyone tell me if Motion Controller works with generic animations or not?
     
  44. TeagansDad

    TeagansDad

    Joined:
    Nov 17, 2012
    Posts:
    880
    Motion Controller will work with generic animations. As long as you're using an animator controller, it will work.
     
  45. TeagansDad

    TeagansDad

    Joined:
    Nov 17, 2012
    Posts:
    880
    If you can zip up and send me your custom motion scripts and animator controller, I'll take a look at them and hopefully provide you with a solution.
     
  46. Yerkisk

    Yerkisk

    Joined:
    Jul 28, 2015
    Posts:
    32
    O.M.G. I think I finally figured it when copying the classes and animations, preparing them for you. The problem isn't actually in the LoopedAction, but in the basic melee attack animation prior to that. Look at the transition here :

    animatorTransition.png

    When I changed the exit motion to a 0.1 frame long 'Pose' animation, the pose stayed at 0.165 there rather than at the end of the animation. Moving it back to the end seems to have fixed the exit state issue. Likely, the animation state were on the Pose (which is tagged as exit) until the transition was over which is for quite some frame if we're looking at the image above.

    Thanks for your help! I'll let you know if it goes all wrong again ;)
     
    Subliminum likes this.
  47. mnsingh367

    mnsingh367

    Joined:
    Oct 6, 2017
    Posts:
    1
    Hi, I tried using generic animations with motion controller, but for my player to move and jump and basically do anything that my humanoid player was able to do, I need to write my own logic which seems like a very big task. Because when looking at the motion scripts I cannot understand what are the things I need to take care of before my player can act as I want it to act.
     
  48. JammyGitGames

    JammyGitGames

    Joined:
    Jan 25, 2020
    Posts:
    15
    Hi, I've got a couple of questions: 1) Using the Motion Controller - 'Basic Interaction' script, if I untick the 'Use Raycast' property, all of the lines below it in the inspector disappear (Layers, Distance Root, Distance, Interactible Core Required, Walk Motion, Walk Speed, Rotation Speed). I would have thought that only the first 3 of these would be hidden and the remaining 4 (Interactible Core Required etc) would remain visible?
    2) I am using the 'Adventure Style' motion in the Motion controller for my character so the camera stays at a fixed distance from the player and doesn't rotate as the player rotates. I have added the swimming pack, when the character starts swimming, the camera behaves differently, so it rotates around as the player rotates. Is there any way to make the camera behaviour the same for the swimming motion as it is when the player is on dry land? Many thanks.
     
  49. JammyGitGames

    JammyGitGames

    Joined:
    Jan 25, 2020
    Posts:
    15
    Another question, I can't currently get the character to do a jump while he is running. It can only cope with 2 key presses, so as running needs forward and shift, it can't also cope with the space bar 'jump'. I want my character to run and dive into the sea, what is the best way to get this to work? Many thanks.
     
  50. TeagansDad

    TeagansDad

    Joined:
    Nov 17, 2012
    Posts:
    880
    This works fine for me -- I haven't encountered any instances where the input can only handle two simultaneous key presses. As I understand it, the hardware controllers on many keyboards can only handle 2 to 3 simultaneous key presses.

    Double check that you have all of the appropriate input entries set up.

    The Walk Motion, Walk Speed, and Rotation Speed should remain visible. I'll fix that. Thanks!

    The swimming motion included in the pack is a strafing motion; there is no adventure-style swimming motion included in the pack. I'll look into adding support for that.
     
    Subliminum likes this.
unityunity