Search Unity

Help with my character controller

Discussion in 'Scripting' started by Wadoren, Sep 6, 2016.

  1. Wadoren

    Wadoren

    Joined:
    Mar 13, 2016
    Posts:
    9
    Hi,
    I'm building a 2.5 D Platformer which is very reliant on the physics system (most obstacles AddForce to the character) that's why I decided to use the rigidbody instead of the character controller component for the movement of the character.

    I tried using the function rigidbody.velocity.x to move the character, but the problem is that there are some zones where a force is applied constantly to the objects surrounding it (like a force field) and with rigidbody.velocity I can pass through them like if there was no force pushing you in the other direction.

    I tried it with the rigidbody.AddForce function:

    Code (CSharp):
    1.  
    2.     public float moveForce = 10;
    3.  
    4.     void Start()
    5.     {
    6.         myRb = GetComponent<Rigidbody>();
    7.     }
    8.  
    9.     void FixedUpdate()
    10.     {
    11.         Move();
    12.     }
    13.  
    14.     public void Move()
    15.     {
    16.         myRb.AddForce(new Vector3 Input.GetAxis("Horizontal") * moveForce, 0, 0));
    17.     }
    And it works much better but i have 2 problems:

    1- The first problem is that the movement doesn't stop immediately when the player releases the button.
    To solve this I tried reseting the velocity of the player to zero whenever the A or D button are pressed, but that also stops the forces (for example if there is a trampoline that pushes you to the left and then you release the button the character stops moving as if there wasn't any force pushing it).

    2- The second problem is that if there is a force that keeps accelerating the rigidbody it reaches very high speed.
    To solve this I tried limiting the speed with:

    Code (CSharp):
    1.  
    2.     public float moveForce = 10;
    3.     public maxSpeed;
    4.  
    5.     void Start()
    6.     {
    7.         myRb = GetComponent<Rigidbody>();
    8.     }
    9.  
    10.     void FixedUpdate()
    11.     {
    12.         Move();
    13.     }
    14.  
    15.     public void Move()
    16.     {
    17.         if (h * myRb.velocity.x < maxSpeed)
    18.         {
    19.             myRb.AddForce(new Vector3 (Input.GetAxis("Horizontal") * moveForce, 0, 0));
    20.         }
    21.  
    22.         if (Mathf.Abs(myRb.velocity.x) > maxSpeed)
    23.         {
    24.             myRb.velocity = new Vector3(Mathf.Sign(myRb.velocity.x) * maxSpeed, myRb.velocity.y, 0);
    25.         }
    26.     }
    But that also limits the velocity in those areas where there is a force.

    Please help me :) my game, as many others I guess... is pretty reliant on that you can move. So I extremely appreciate any idea that you can have on how to solve this.
    Thanks in advance ;)

    PD: I tried to keep the code as simple as possible (I haven't even incorpored the raycasting for the jump...) but it's not because I'm afraid of writting more complex code, if you have any idea on how to solve this with more complex code show it too please.
     
    Last edited: Sep 6, 2016
  2. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    Since you have potentially many environmental factors to consider, you should use AddForce. In order to get your character's movement to stop on a dime, I would increase your rigidbody drag so that he won't drift once you let go of the key. That will likely mean you also have to increase your forces across the board to overcome that drag initially.

    You can keep a max speed by using "myRb.velocity = Vector3.ClampMagnitude(myRb.velocity, maxSpeed)"

    Also, use the second parameter of AddForce to use ForceMode.Impulse, so that the force is added all in one frame, and you won't get the build-up of speed.

    Depending on the requirements of your game this may not work. An alternative approach is to have your player total all the forces manually, and calculate the final velocity yourself. So external forces would not use AddForce but call a function in the player script with the force parameter, which the player would handle each frame. This is essentially programming your own physics which is more work, but not out of the realm of possibilities.
     
  3. Wadoren

    Wadoren

    Joined:
    Mar 13, 2016
    Posts:
    9
    Hi,
    Thanks for your reply.

    I've increased the rigidbody's drag, and it does what you say but it also affects the velocity in which the object falls (since drag is related to the air resistance of the rigidbody).
    I think that one way of solving this could be doing a little bit of raycasting to know when the object is grounded and increment the drag value only when the player is grounded, I'll have to search a little bit about it though.

    However, I don't think that I can use your solution for the second problem since I want to limit the velocity just for the player movement, not for external forces. In relation to the previous example, I want the player to move at 5 speed units but if he gets in the trampoline that faces left, he may get 10 speed units.
     
  4. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    To the first point, you could have a groundDrag and airDrag to set your drag with, based on however you implement your grounded check, or increase gravity by a factor of the drag.

    As for the second solution, you are right. Perhaps you do not need to have a max speed, and just allow your forces and drag determine the max speed. Use ForceMode.Impulse should prevent the speed build-up, so while moving your player should reach equilibrium at a steady speed.
     
  5. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    You can add a ForceMode option to your AddForce, try this and see if it gets the affect you want:
    Code (CSharp):
    1. myRb.AddForce(new Vector3 Input.GetAxis("Horizontal") * moveForce, 0, 0),ForceMode.Impulse);
     
  6. Wadoren

    Wadoren

    Joined:
    Mar 13, 2016
    Posts:
    9
    Thanks, it's true ForceMode.Impulse seems to limit the velocity (in my case at around 3.5 speed units).
    I've done some searches while I was playing around and I found this:
    http://forum.unity3d.com/threads/terminal-velocity.34667/
    I think they are trying to achive something like what I want, but there is no clear code, just pieces of it. Is there any way to realte this to what I am doing?
    Right now I'll use the ForceMode.Impulse, but I think that that post may have a different soultion which could help me avoid using the drag incorrectly.

    Thanks, I looked up in the manual for the force modes. ForceMode.Impulse works fine for things such as the trampolin, but the force field needs to have ForceMode.Force if I want to achive what I want (I don't want the player to be able to pass though it, but that when he walks towards it, feel like if somethig is pushing him to the other direction).