Search Unity

Fixing my Garbage Movement?

Discussion in 'Scripting' started by AnimusRex, May 15, 2019.

  1. AnimusRex

    AnimusRex

    Joined:
    Apr 26, 2019
    Posts:
    35
    So I got my movement working just the way I'd like; only problem is that I now realize that transform.Translate completely ignores collisions, so the Rididbody and Capsule Colider I have on my player character don't help much and he can basically push himself into walls.

    How would I make this code use velocity instead?

    Code (CSharp):
    1.     void Movement()
    2.     {
    3.         float moveHorizontal = Input.GetAxis("Horizontal");
    4.         float moveVertical = Input.GetAxis("Vertical");
    5.  
    6.         Vector3 jump = new Vector3(0, jumpForce, 0);
    7.         Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
    8.  
    9.         if (moveHorizontal + moveVertical != 0)
    10.         {
    11.             transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(movement), 0.2F);
    12.         }
    13.  
    14.        transform.Translate((movement * moveSpeed) * Time.deltaTime, Space.World);
    15.  
    16.         //Animator bits
    17.         if (Math.Abs(Input.GetAxis("Horizontal")) >= Math.Abs(Input.GetAxis("Vertical")))
    18.         {
    19.             mAnim.SetFloat("Speed", Math.Abs(Input.GetAxis("Horizontal")));
    20.         }
    21.         else
    22.         {
    23.             mAnim.SetFloat("Speed", Math.Abs(Input.GetAxis("Vertical")));
    24.         }
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    3,679
    Fortunately it's easy! When you do transform.Translate you are effectively "teleporting" as far as the physics system, so that is why your colliders don't work.

    Instead you want to use the reference you have to the Rigidbody and call the .MovePosition() method with your new position.

    That tells the physics system "hey, simulate me moving from where I am to this new position" and all the colliders will operate as they would during physics-induced movement.
     
  3. AnimusRex

    AnimusRex

    Joined:
    Apr 26, 2019
    Posts:
    35
    Totally broke it, now movement doesn't work at all :D

    Code (CSharp):
    1.     void Movement()
    2.     {
    3.         float moveHorizontal = Input.GetAxis("Horizontal");
    4.         float moveVertical = Input.GetAxis("Vertical");
    5.  
    6.         Vector3 jump = new Vector3(0, jumpForce, 0);
    7.         Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
    8.  
    9.         if (moveHorizontal + moveVertical != 0)
    10.         {
    11.             rigid.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(movement), 0.2F);
    12.         }
    13.  
    14.         rigid.MovePosition((movement * moveSpeed) * Time.deltaTime);
    15.  
    16.         //Animator bits
    17.         if (Math.Abs(Input.GetAxis("Horizontal")) >= Math.Abs(Input.GetAxis("Vertical")))
    18.         {
    19.             mAnim.SetFloat("Speed", Math.Abs(Input.GetAxis("Horizontal")));
    20.         }
    21.         else
    22.         {
    23.             mAnim.SetFloat("Speed", Math.Abs(Input.GetAxis("Vertical")));
    24.         }
    25.     }
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    3,679
    You want to give MovePosition the new position. You are giving it the little tiny chunk you want to move this frame.

    so like:

    Code (csharp):
    1. Vector3 tempPos = transform.position;
    2.  
    3. tempPos += movement * moveSpeed * Time.deltaTime;
    4.  
    5. rigid.MovePosition( tempPos);
    It's not like Translate, which is relative. .MovePosition instead means "GO HERE"

    Apologies I didn't make this clearer in my first post. The docs are here:

    https://docs.unity3d.com/ScriptReference/Rigidbody.MovePosition.html
     
  5. AnimusRex

    AnimusRex

    Joined:
    Apr 26, 2019
    Posts:
    35
    Hmm.. Still able to push himself into the floor. Thanks for the input though. I'm calling the movement from Update(), should I be using FixedUpdate()?

    Code (CSharp):
    1.         float moveHorizontal = Input.GetAxis("Horizontal");
    2.         float moveVertical = Input.GetAxis("Vertical");
    3.  
    4.         Vector3 jump = new Vector3(0, jumpForce, 0);
    5.         Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
    6.  
    7.         if (moveHorizontal + moveVertical != 0)
    8.         {
    9.             rigid.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(movement), 0.2F);
    10.         }
    11.  
    12.         Vector3 nextpos = transform.position;
    13.  
    14.         nextpos += movement * moveSpeed * Time.deltaTime;
    15.  
    16.         rigid.MovePosition(nextpos);
    EDIT: Calling movement from fixedupdate didn't fix it
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    3,679
    Hm... try also using the rigid.MoveRotation() method to set your new rotation... I didn't even notice you were rotating...

    Is there any possibility you're slightly "in" the floor already on start? That can cause collisions to allow further penetration.
     
  7. AnimusRex

    AnimusRex

    Joined:
    Apr 26, 2019
    Posts:
    35
    Definitely not in the floor; I start up on a block. Gravity is working normally, but walking under the side of a ramp I'm able to force the character into the floor. Will update the rotate and report back.
     
  8. AnimusRex

    AnimusRex

    Joined:
    Apr 26, 2019
    Posts:
    35
    Struggling here on the rotation script; even removing rotation though I'm still able to phase into the floor.

    I'm not sure what I should use in place of Quaternion.LookRotation(movement) to get the facing of my character..

    Code (CSharp):
    1.     void Movement()
    2.     {
    3.         float moveHorizontal = Input.GetAxis("Horizontal");
    4.         float moveVertical = Input.GetAxis("Vertical");
    5.  
    6.         Vector3 jump = new Vector3(0, jumpForce, 0);
    7.         Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
    8.  
    9.         if (moveHorizontal + moveVertical != 0)
    10.         {
    11.             Direction = Quaternion.Euler(LookRotation(movement));
    12.             Quaternion deltaRotation = Quaternion.Euler(Direction * Time.deltaTime);
    13.             rigid.MoveRotation(rigid.rotation * deltaRotation);
    14.         }
    15.  
    16.         Vector3 nextpos = transform.position;
    17.  
    18.         nextpos += movement * moveSpeed * Time.deltaTime;
    19.  
    20.         rigid.MovePosition(nextpos);
    21.  
    22.         //Animator bits
    23.         if (Math.Abs(Input.GetAxis("Horizontal")) >= Math.Abs(Input.GetAxis("Vertical")))
    24.         {
    25.             mAnim.SetFloat("Speed", Math.Abs(Input.GetAxis("Horizontal")));
    26.         }
    27.         else
    28.         {
    29.             mAnim.SetFloat("Speed", Math.Abs(Input.GetAxis("Vertical")));
    30.         }
    31.     }
     
  9. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    3,679
    That script sure looks like it would respect physics boundaries... but it is possible for physics to get inside each other in various combinations of angles and colliders. For humorous examples look up the GTA IV swingset of death.

    If you can make a simple project that just has this player agent and some simple geometry that shows off your problem, make a video and/or a .unitypackage and ask about it in the physics forum next door. From a scripting angle what you're doing seems correct.

    Also, just to check... is it maybe your animation that's driving the object into the wall?
     
  10. AnimusRex

    AnimusRex

    Joined:
    Apr 26, 2019
    Posts:
    35
    Nope, just turned off the animations and he's still able to go into the floor. Guess I'll create a thread over in the physics forum..
     
  11. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    11,786
    Yes. Most things that interact with a Rigidbody's movement or forces should be done in FixedUpdate().
     
  12. dontdiedevelop

    dontdiedevelop

    Joined:
    Sep 18, 2018
    Posts:
    66
    Replace it
    Code (CSharp):
    1. transform.Translate((movement * moveSpeed) * Time.deltaTime, Space.World);
    With;
    Code (CSharp):
    1. transform.position = Vector3.Lerp(transform.position, transform.position + movement,Time.fixedDeltaTime * moveSpeed);
    and do it in FixedUpdate
     
    Last edited: May 15, 2019
  13. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    1,228
    What is your playerss rigidbody's "interpolate" and "collision detection" modes set to? You want "collision detection" to be continuous at least.

    Is the rigidbody set to kinematic or not? moveposition behaves differently for each case.
     
  14. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    3,863
    All of the MovePosition methods are... less than ideal. You're still teleporting the object, just in a way that's more friendly to the physics engine.

    If you want snappy movement that collides properly, the easiest thing is generally to set the velocity of the rigidbody directly. In that case, you'd replace:

    Code (csharp):
    1.  
    2. transform.Translate((movement * moveSpeed) * Time.deltaTime, Space.World);
    3.  
    With:
    Code (csharp):
    1.  
    2. rigid.velocity = movement * moveSpeed;
    3.  
     
  15. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    11,786
    That can work really well. It can also get complicated if you've got things like falling, gravity, or being pushed by other forces or objects, because each time you set the velocity anything that was previously added for those things gets overwritten.

    That said, if you don't need any of that stuff then that simple approach can work really well.
     
  16. AnimusRex

    AnimusRex

    Joined:
    Apr 26, 2019
    Posts:
    35
    Doesn't resolve that the character is able to walk into the ground. The rigidbody is set to true for Use Gravity and false for Is Kinematic, collision detection is supposed to be continuous.
     
  17. AnimusRex

    AnimusRex

    Joined:
    Apr 26, 2019
    Posts:
    35
    Bump, anyone have any idea why this script would allow a character to push into the ground?

    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. public class CharacterController : MonoBehaviour
    7. {
    8.  
    9.     //Movement VAR's
    10.     public float moveSpeed = 5f;
    11.     public float jumpForce = 5f;
    12.     public Rigidbody rigid;
    13.     public Vector3 Direction;
    14.  
    15.     Animator mAnim;
    16.  
    17.     // Start is called before the first frame update
    18.     void Start()
    19.     {
    20.         rigid = GetComponent<Rigidbody>();
    21.     }
    22.  
    23.     void Update()
    24.     {
    25.  
    26.     }
    27.     // Update is called once per frame
    28.     void FixedUpdate()
    29.     {
    30.         Animation();
    31.         Movement();
    32.     }
    33.  
    34.     void Animation()
    35.     {
    36.         mAnim = GameObject.FindWithTag("Player").GetComponent<Animator>();
    37.     }
    38.  
    39.     void Movement()
    40.     {
    41.         float moveHorizontal = Input.GetAxis("Horizontal");
    42.         float moveVertical = Input.GetAxis("Vertical");
    43.  
    44.         Vector3 jump = new Vector3(0, jumpForce, 0);
    45.         Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
    46.  
    47.         if (Input.GetButtonDown("Jump"))
    48.         {
    49.             rigid.AddForce(jump, ForceMode.Impulse);
    50.         }
    51.  
    52.         if (moveHorizontal + moveVertical != 0)
    53.         {
    54.             rigid.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(movement), 0.2F);
    55.         }
    56.  
    57.         Vector3 nextpos = transform.position;
    58.  
    59.         nextpos += movement * moveSpeed * Time.deltaTime;
    60.  
    61.         rigid.MovePosition(nextpos);
    62.  
    63.         //Animator bits
    64.         if (Math.Abs(Input.GetAxis("Horizontal")) >= Math.Abs(Input.GetAxis("Vertical")))
    65.         {
    66.             mAnim.SetFloat("Speed", Math.Abs(Input.GetAxis("Horizontal")));
    67.         }
    68.         else
    69.         {
    70.             mAnim.SetFloat("Speed", Math.Abs(Input.GetAxis("Vertical")));
    71.         }
    72.     }
    73. }
    74.  
    75.  
    I've started an entirely blank project and the same issue is apparent, and I do want gravity and jumping/falling, so strictly setting the velocity will probably cause more headache down the line.
     
  18. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    3,863
    You can have gravity while setting the velocity; just don't touch the y-value:

    Code (csharp):
    1. newVelocity.y = rb.velocity.y;
    2. rb.velocity = newVelocity
    Calling MovePosition teleports your character. Either set velocity, or use only AddForce.
     
  19. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    3,679
    Or use The Force... These are not the Rigidbodies you're looking for.
     
  20. AnimusRex

    AnimusRex

    Joined:
    Apr 26, 2019
    Posts:
    35
    So the collision issue was solved through the following code;

    Code (CSharp):
    1.     void Movement()
    2.     {
    3.         float moveHorizontal = Input.GetAxis("Horizontal");
    4.         float moveVertical = Input.GetAxis("Vertical");
    5.  
    6.         Vector3 jump = new Vector3(0, jumpForce, 0);
    7.         Vector3 tempVel = rigid.velocity;
    8.  
    9.         tempVel.x = moveHorizontal * moveSpeed;
    10.         tempVel.z = moveVertical * moveSpeed;
    11.         tempVel.y = 0.0f;
    12.  
    13.         if (Input.GetButtonDown("Jump"))
    14.         {
    15.             rigid.AddForce(jump, ForceMode.Impulse);
    16.         }
    17.  
    18.         if (moveHorizontal + moveVertical != 0)
    19.         {
    20.             rigid.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(new Vector3(tempVel.x, 0, tempVel.z)), 0.2F);
    21.         }
    22.  
    23.         rigid.velocity += tempVel;
    24.  
    25.         rigid.velocity = Vector3.Scale(new Vector3(rigid.velocity.x, rigid.velocity.y, rigid.velocity.z), new Vector3(movefrictionMultiplier, 1.0f, movefrictionMultiplier));
    26.     }
    I still don't have anything to check if the rigidbody is grounded pre-jump, and I want a way to move in the direction the camera is facing. So no matter which way the camera is pointing, up on the left analog stick should move me towards it. I imagine it's something I'd add to these lines;
    Code (CSharp):
    1.         tempVel.x = moveHorizontal * moveSpeed;
    2.         tempVel.z = moveVertical * moveSpeed;
    But I'm not sure how specifically I should go about tackling this.