Fixing my Garbage Movement?

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

1. 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. 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. Totally broke it, now movement doesn't work at all 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. 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. 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. 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. 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. 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. 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. 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

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. 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. 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. 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

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. 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. 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.         {
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. 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. Or use The Force... These are not the Rigidbodies you're looking for.

20. 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.         {
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.