Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.

[RELEASED] Easy Character Movement 2

Discussion in 'Assets and Asset Store' started by Krull, May 5, 2021.

  1. dyadicgames-ericzou

    dyadicgames-ericzou

    Joined:
    Aug 30, 2022
    Posts:
    1
    Hello Krull,

    My character can't detect the collision between the ground and character by unity build-in collision enter and exit function, looks like there is a little offset from the ground make the character can't touch the ground.

    But there is only a OnCollided in Character class. But there is no something Like OnCollisionExit that I can use in collision detection mechanics.

    How do I detect the bottom collision enter or exit?
     
    Last edited: Feb 24, 2023
  2. Minzie

    Minzie

    Joined:
    Jul 26, 2019
    Posts:
    69
    Hey @Krull
    The swimming example doesn't seem to work, both in ECM2_Demo & example 5.8. Is this also the case at your side?
     
  3. Krull

    Krull

    Joined:
    Oct 31, 2014
    Posts:
    641
    Hi @jiajialin1975

    Thank you for the clarification.
    I have been performing several tests and honestly speaking I have not been able to reproduce the issue.

    Having said that, one possible culprit I can think of, could be the MathLib.Clamp0360 function (AddCameraYawInput method) causing the side to side camera issue.

    I attached a modified version for you to look at, this uses a different (more robust) method to keep the yaw value in the [-180, 180] range.

    Let me know if I can be of any further assistance.
     

    Attached Files:

    jiajialin1975 likes this.
  4. Krull

    Krull

    Joined:
    Oct 31, 2014
    Posts:
    641
    Hi @dyadicgames-ericzou

    Yes, that's correct, it maintains an offset around the character and the ground in order to smoothly resolve collisions.

    The OnCollided method / event works like Unity's built-in CC OnControllerColliderHit, which is called when the controller hits a collider while performing a Move.

    The CharacterMovement component offers a vast amount of ground related data, like wasGrounded, isGrounded, groundDistance, groundPoint, etc. or even more through the CharacterMovement.currentGround property.

    Additionally, you can use the CharacterMovements ComputeGroundDistance and FindGround methods, to sample any given position.

    The OnCollidedEnter / Exit, will only be called if the 'other' contains a non-kinematic rigidbody.
     
  5. Krull

    Krull

    Joined:
    Oct 31, 2014
    Posts:
    641
    Hi @Minzie,

    I just tested it on both the old input and new input and both ECM2 versions works fine, however worth noting the demo and the 5.8 example works a little differently.

    By default, when a character enters a 'water volume' it will float and change its movement mode to swimming, but will not cause the character to jump out of water.

    This is where the example enters, it shows how to use the water related functions, ie, isSwimming, ImmersionDepth, etc. to implement a jump out of water mechanics.

    For swimming, a character uses the OnTriggerEnter / OnTriggerExit methods, so please makesure if your character override those methods, to call the base method implementation (ie. base.OnTriggerEnter...) otherwise it won't work.

    Additionally, please make sure your character can collide with the triggers and has its canEverSwim property enabled.
     
    Minzie likes this.
  6. jiajialin1975

    jiajialin1975

    Joined:
    Jan 20, 2021
    Posts:
    5
    Hi @Krull ,

    Thank you for testing.

    I replace the file with same name, but still camera change position between two position.

    I test vanilla (un-modified) Cinemachine example in an brand new project. It works perfectly fine.

    Could it be some settings in my old existing project cause this problem?

    But the porblem still happened after I re-install "Cinemachine" and "Input system" in my old existing project.
     
  7. Krull

    Krull

    Joined:
    Oct 31, 2014
    Posts:
    641

    Hi @jiajialin1975,

    Yes, there could be some settings causing the strange issue. You could try resetting your project settings:

    PLEASE BACKUP BEFORE TRYING THIS!

    Close the unity editor, and from your project's folder delete the ProjectSettings folder, additionally you can also delete the Library folder so your assets will be re-imported. This should reset your current project.

    Let me know if I can be of further assistance.
     
  8. wjf644045860

    wjf644045860

    Joined:
    Oct 14, 2019
    Posts:
    1
    in ladder Climbing Eample I grab but Icant input W when I release ladder then I can Move . i find it output (0,0); but release ladder i can move
     

    Attached Files:

  9. Krull

    Krull

    Joined:
    Oct 31, 2014
    Posts:
    641
    Hi @wjf644045860,

    Mmm, I think this could be some kind of binding issue on your side, please make sure the input actions are correctly assigned, or reset input settings if using the old input system.
     
  10. One_Learning_Man

    One_Learning_Man

    Joined:
    Sep 30, 2021
    Posts:
    76
    @Krull Does ECM2 support swinging/hanging on ropes?
     
  11. Asarge

    Asarge

    Joined:
    Jun 2, 2018
    Posts:
    16
    Hi Krull, if my grapple logic is called OnUpdate() the grapple runs quite smoothly but will not sync up with the hand bone. OnFixedUpdate() has the hand bone sync but the grapple becomes jittery in some situations. Is there a way I can get these synchronized?
     
  12. Krull

    Krull

    Joined:
    Oct 31, 2014
    Posts:
    641

    Hi @One_Learning_Man ,

    Not by default, but I think you can use the general approach using a joint, where your end point is a regular non-kinematic rigidbody (jointed) and your character will be 'attached' to this endpoint rigidbody using the CharacterMovement SetPlatform method, so the character is moved along with this rigidbody.

    Hope this makes sense
     
  13. Krull

    Krull

    Joined:
    Oct 31, 2014
    Posts:
    641
    Hi @Asarge,

    Mmm, honestly not quite sure if I correctly understand your current issue, however if desired or needed, you can modify the default execution order (late fixed update by default), and perform the character movement in a regular Update method.

    For this, first disable its late fixed update coroutine, setting the Character enableLateFixedUpdate property to false and call the Character Simulate method in a regular Monobehaviour Update method.

    This basically modifies the execution order, so instead of being updated in a late fixed update (a WaitForFixedUpdate coroutine) you decide where to update your character (Simulate it) in your case an update method.
     
    Asarge likes this.
  14. my_little_kafka

    my_little_kafka

    Joined:
    Feb 6, 2014
    Posts:
    64
    Hi, @Krull !

    I started to implement behaviours for AI that are more complex than just finding and going towards their target. I'm using animations with root motion, and I implemented an animation controller from the examples provided for ECM2.

    Code (CSharp):
    1. float forwardAmount = _character.useRootMotion && _character.GetRootMotionController()
    2.                 ? move.z
    3.                 : Mathf.InverseLerp(0.0f, _character.GetMaxSpeed(), _character.GetSpeed());
    4.  
    5.             animator.SetFloat(a_forward, forwardAmount, a_walkingTransitionDumpTime, deltaTime);
    6.             animator.SetFloat(a_turn, Mathf.Atan2(move.x, move.z), a_rotatingTransitionDumpTime, deltaTime);
    This code works fine when I just tell an enemy character to move towards their target — the enemy stops and plays «turn in place» animation and rotates where needed (I don't use root rotation, as it is not as precise because of animation blending).
    I'm also using a blend state with idle, running and turning animations, driven by the «move forward» and «turn» parameters which I set in the code mentioned above.

    The problem is, I can't seem to turn the enemy when they're not moving anywhere. For example, in the pursuing state, when the enemy is close enough to their target, I want the enemy to just turn towards the target.

    I'm using
    Code (CSharp):
    1. actorCharacterController.RotateTowards
    2. (actorCharacterController.playerCharacterGO.transform.position - actorCharacterController.GetPosition());
    to rotate enemy, and the enemy does rotate towards their target (player character's game object), but the thing is, the turn parameter of the animation is not affected by this rotation, the enemy just stands still (it's not moving forward, so it plays an idle animation) and rotates.

    Using ECM's SetRotation command does affect the turn parameter, but I can't seem to find a correct quaternion to pass to this function so the enemy character turns steadily towards their target.

    Are there any specific ECM2 commands to sync character physics with animators?
     
  15. MichelVictor

    MichelVictor

    Joined:
    Apr 15, 2018
    Posts:
    6
    Hello again. Any news about new abilities like wall jump and ledge grabbing?
    I'm not a programmer but in my spare time I like to read the code and try to learn using the examples, I modified a lot of the examples to test if I'm learning something, and it seems that I think is working, but I feel like I need more knowledge to be able to fully comprehend some ideas. So it will be very useful if it has more examples on other types of movements.
     
  16. jiajialin1975

    jiajialin1975

    Joined:
    Jan 20, 2021
    Posts:
    5
    Hi @Krull ,
    I am trying to create a custom input action. I copy 'MyCharacter.cs' and create 'MyOwnCharacter.cs'. In this script I create an new input action "High Jump" copy from the "Jump" in 'Character.cs'. The only different between "High Jump" and "Jump" is 'Jump Impulse'. I make "High Jump" jump double higher than "Jump", but they still use the same jump animation.

    How to use two different animations for "High Jump" and "Jump"?

    I add "High Jump" animation in the 'Airborne Blend Tree' and setup "High Jump" animation play in value of Jump equal to 10 , but it always transform back to "Jump" animation because of "Jump" animation play in value of Jump equal to 5. It seems impossible to identify the different between "High Jump" and "Jump" in one value of blend tree.
     
  17. Krull

    Krull

    Joined:
    Oct 31, 2014
    Posts:
    641
    Hi @my_little_kafka,

    When using root motion (in general not only ECM2) basically you are controlling your animation with your given input, in case of a player controlled character its input vector, and in case of an AI a movement direction, in the end it is typically mapped to 0-1 range in the Animator.

    By default a Character (an an AgentCharacter too!) will rotate towards its given movement direction unless you modify its rotation mode or modify it by code (ie: custom rotation mode). In your case this is what you want, a custom rotation mode based on your character's logical state, as I think your rotation is being overriden someway (hard to tell without the whole code).

    So here you can simply ignore rotation modes and directly extend the UpdateRotation method and handle your character's rotation based in your logical state, for example:

    Code (csharp):
    1.  
    2. protected override void UpdateRotation()
    3. {
    4.     if (isMoving)
    5.     {
    6.         // Rotate towards movement direction
    7.  
    8.         Vector3 movementDirection = GetMovementDirection();
    9.  
    10.         RotateTowards(movementDirection);
    11.     }
    12.     else if (isCloseEnough)
    13.     {
    14.         // Rotate towards enemy
    15.     }
    16.     else ...                
    17. }
    18.  
    If not using a custom Character (ie: not extending a Character based class), makesure your character do not have input action assigned (so it wont process any input related code) and its rotation mode is None so it do not modify its rotation.[/code]
     
    Last edited: Mar 18, 2023
    my_little_kafka likes this.
  18. Krull

    Krull

    Joined:
    Oct 31, 2014
    Posts:
    641
    Hi @MichelVictor

    Unfortunately not yet, as the in the following update I am focusing in adding network support to the Character class, and while I could try to fit at least a wall jump example, probably will need to wait to next update, where I plan to add support for the 'canWalkOffLedge' option and more examples.

    Sorry for the inconvenience.
     
  19. Krull

    Krull

    Joined:
    Oct 31, 2014
    Posts:
    641
    Hi @jiajialin1975 ,

    Yes, consider a jump, it is a parabolic motion, where the vertical velocity goes from its initial impulse (ie: 5, 10, etc) towards zero (slowly decelerated by gravity) and zero means jump apex is reached, to start falling (negative value), so I think this should work only for 'short jump, since it will never reach the > 5 to 10 range, but the high jump will go from 10 to 0 passing from the short jump range.

    Mmm, If your jump(s) are triggered by different inputs, the easiest way (I think) is to use separate flags, ie: isJumping (regular jump), and isHighJump (new jump). You set this to true depending on your input and false on key release, etc.

    Later you will use these 'flags' (boolean vars) to feed your animator to triggering the corresponding animation.

    Now for the animator, you could use two separate jump states, regular jump, and high jump, and trigger accordly (based on jump), as the included example jump method is 'tailored' for a regular jump.

    It's common (in a more advanced setup) to even split jumps in sections, like jump start, jump apex, jump_falling, etc.
     
    Last edited: Mar 18, 2023
  20. One_Learning_Man

    One_Learning_Man

    Joined:
    Sep 30, 2021
    Posts:
    76
    Nice! So how will it work? Do you plan on having both:

    1) NetworkCharacterMovement.cs + NetworkThirdPersonCharacter.cs? Or will NetworkThirdPersonCharacter take care of all networking?

    2) Do you use Network Transform components offered by FishNet/Mirror?

    3) How do you plan on adding rollback/prediction offered by FishNet?

    4) Will you update your docs talking about implementing networking? I examined the example in CharacterMovement FishNet integration and wished it had a bit more comments.

    Keep up the great work Krull!
     
  21. my_little_kafka

    my_little_kafka

    Joined:
    Feb 6, 2014
    Posts:
    64
    Oh, so this is how the rotations should be handled, dully noted (I felt weird rotating the character in a completelly separate state script, this looks like a better idea). I extended this method in my ai agent controller script, made it like that:

    Code (CSharp):
    1.         protected override void UpdateRotation()
    2.         {
    3.             //base.UpdateRotation();
    4.             if (IsDisabled())
    5.                 return;
    6.  
    7.             if (currentState is States.ACS_Pursuing)
    8.             {
    9.                 if (pursuingState.isPursuing == true)
    10.                 {
    11.                     Vector3 movementDirection = GetMovementDirection();
    12.                     RotateTowards(movementDirection);
    13.                 }
    14.  
    15.                 if (pursuingState.isCloseToTarget == true)
    16.                 {
    17.                     Vector3 targetDirection = playerCharacterGO.transform.position - transform.position;
    18.                     RotateTowards(targetDirection);
    19.                 }
    20.             }
    21.           }
    And my state machine affects the isPursuing and isCloseToTarget booleans. It works fine when the enemy character pursues their target, but when they get close, they just rotate without affecting the «Turning» variable in the animator. Could it be that the StoppingDistance is interferring with it? The enemy character plays turning animations just fine when they're away (for example, if the target is behind them, they stop and turn to look at the target while playing turning at place animation), but not when they reached this stopping distance.
    I haven't extended the AgentCharacter script that much, so nothing else affects the turning or moving.
     
  22. jiajialin1975

    jiajialin1975

    Joined:
    Jan 20, 2021
    Posts:
    5
    @Krull
    Thank you for your help.

    I use your JumpLeg float value to seperate "jump" "high jump" animations in blend tree.
    And it works perfectly.

    I have another question about collision. I wirte a script on a cube(with rigibody and collider). I try the
    OnCollisionEnter event on the cube is not working.

    How do I let the cube know the cube was collided by the easy character?
     
  23. Krull

    Krull

    Joined:
    Oct 31, 2014
    Posts:
    641
    Thanks @One_Learning_Man,

    About your questions:

    1) For the first iteration I would like to add support for character (in general) as this is the 'core' base and all other characters are just extending this, so the same approach could be followed for networking purposes.

    Additionally I am adding client-authority examples including Unity's Netcode for gameobjects, as this is a lot easier to implement (almost transparent) than server-auth client-side prediction, so beginners should appreciate it the most.


    2) Yes, this is already used in the CharacterMovement examples, as a CharacterMovement can continue simulating from the given transform position, in this case given by fishnet NetworkTransform or through CM network state.


    3) Similar to the one used with CM examples, however the Character class requires a lot more of data housekeeping, so possible adding a tick based histogram for relevant data (ie: SavedMove), this way when a rollback is issued, the histogram data is restored for the given tick and then apply the server received data (something like this) to complement it.

    4) Well for networking I assume higher programming skills, since networking in general definitely adds a difficult layer to game programming in general, however I could add a kind of tutorial / doc for the ECM2 networking part.

    Also, please keep in mind this will be added as examples, and while it definitely be production ready in movement related part, as ECM2 it is a general purpose solution, so it is expected to be modified adapted for each project
     
  24. Krull

    Krull

    Joined:
    Oct 31, 2014
    Posts:
    641
    Hi @my_little_kafka,

    Yes, that's the approach I suggested.

    Now for your rotation issue, you could add a kind of tolerance radius, similar to the Agent's stopping distance, so within this tolerance radius you can simply stop rotating or apply a falloff effect torotation rate.

    This is similar to the stopping distance used in order to prevent overshooting as the agent reaches its destination.
     
    my_little_kafka likes this.
  25. Krull

    Krull

    Joined:
    Oct 31, 2014
    Posts:
    641

    Hi @jiajialin1975

    Glad I could help :)

    About your question:

    For it to work, the cube's rigidbody should be non-kinematic, as you can see here.

    Also, please make sure your script is on the GameObject containing the rigidbody.
     
  26. my_little_kafka

    my_little_kafka

    Joined:
    Feb 6, 2014
    Posts:
    64
    Oh, the issue is in the fact that the turning animation doesn't play, the character is rotation towards their target in idle state.
    I figured out that the stopping distance wasn't an issue, the issue was my inability to get a suitable Vector3 variable so it would not only turn the character, but also would affect the Animator component so it would play the turning animation.

    The closest thing I got is:

    In my custom CharacterController script that I built upon AgentCharacter, I created this override for rotation (don't mind the sloppy usage of states, I will clean the code once I will understand the issue with the Animator):

    Code (CSharp):
    1.        
    2. public Vector3 targetDirection;
    3. protected override void UpdateRotation()
    4.         {
    5.         //    Debug.Log("Movement direction is:" + GetMovementDirection());
    6.             //base.UpdateRotation();
    7.             if (IsDisabled())
    8.                 return;
    9.  
    10.             if (currentState is States.ACS_Pursuing)
    11.             {
    12.                 if (pursuingState.isPursuing == true)
    13.                 {
    14.                     Vector3 movementDirection = GetMovementDirection();
    15.                     RotateTowards(movementDirection);
    16.                 }
    17.  
    18.                 if (pursuingState.isCloseToTarget == true)
    19.                 {
    20.             //        Debug.Log("Target direction is:" + targetDirection);
    21.                     RotateTowards(targetDirection);
    22.                     targetDirection = playerCharacterGO.transform.position - transform.position;
    23.                 }
    24.             }
    25.  
    26.         }
    If the state is pursuing, then it works the default way, by using GetMovementDirection(). But if the enemy character is close to their target, they're rotating towards it. I calculate the vector as Target Position minus Enemy Position, and this gives me the seemingly right vector.

    Then, in the script that handles the animation, I wrote this:

    Code (CSharp):
    1. private void AnimateCharacter()
    2.         {
    3.             float deltaTime = Time.deltaTime;
    4.  
    5.             if (pursuingState.isPursuing)
    6.             {
    7.  
    8.                 Vector3 move = transform.InverseTransformDirection(_character.GetMovementDirection());
    9.                 if (_character.useRootMotion && _character.GetRootMotionController())
    10.                 {
    11.                     forwardAmount = move.z;
    12.                 }
    13.                 else
    14.                 {
    15.                     forwardAmount = Mathf.InverseLerp(0.0f, _character.GetMaxSpeed(), _character.GetSpeed());
    16.                 }
    17.                 animator.SetFloat(a_forward, forwardAmount, a_walkingTransitionDumpTime, deltaTime);
    18.                 animator.SetFloat(a_turn, Mathf.Atan2(move.x, move.z), a_rotatingTransitionDumpTime, deltaTime);
    19.             }
    20.  
    21.             if (pursuingState.isCloseToTarget)
    22.             {
    23.  
    24.                 Vector3 move = transform.InverseTransformDirection(_character.targetDirection);      
    25.                 forwardAmount = 0;
    26.                 animator.SetFloat(a_forward, forwardAmount, a_walkingTransitionDumpTime, deltaTime);
    27.                 animator.SetFloat(a_turn, Mathf.Atan2(move.x, move.z), a_rotatingTransitionDumpTime, deltaTime);
    28.             }
    29.         }
    It is also a rework of a default third person character animator, where I did the same split based on the current state. Pursuing state — default animation behaviour. But in CloseToTarget state I give the system a different vector — the one I calculated in CharacterController script.
    So here's my problem, the vector I calculated doesn't give a large enough value to drive the animator's turn animations. There's this really wide angle, right in front of enemy character, where if the character will turn, it will not play the turning animation. But if the angle will be large enough, it will play the animation.
    I tried to manually multiply the results from Mathf.Atan2(move.x, move.z), but it results in some weird jerky changes of animation and generally it doesn't seem like it's the right way. Is there any way to naturally transform the value that I pass into the animator to be in the range of turning animations in my blend tree (it's -0.5 and 0.5 currently)?

    I'm not really good with vectors, and, God forbid, quaternions, so I'm not sure if it's a ECM2 issue, or it's unrelated. Here's the video so it would be easier to understand, I know it's all confusing, but I can't express all this in a more precise way.



    (The small blue sphere indicates the direction of enemy character)
     
  27. revanaii

    revanaii

    Joined:
    Jun 13, 2017
    Posts:
    11
    howdy @Krull
    a question: currently I can jump on top of enemies and stay there, which obviously breakes the combat. How to prevent that from happening?
    The enemy is not a CharacterController, but a capsule collider + kinematic rigidbody + navmesh agent.
    Thanks.
     
  28. revanaii

    revanaii

    Joined:
    Jun 13, 2017
    Posts:
    11
    Fixed by checking
    if(...GetCharacterMovement().groundRigidbody..) AddForce(...);