Should i use rigidbody for platformer movement?
Hello! I want to create character controller2d for simple platformer, but i don't know how realize it. I found a lot of answers in internet. but they all contradict each other.
Should i use rigidbody2d or i need use just Transform.Translate function or i should use rigidbody with kinematic?
(sorry for my english)
Answer by Pathojen · Dec 05, 2021 at 12:26 AM
I'll try to break it down using two codes.
Kinematic ignores collisions and gravity, so it probably wouldn't be the best option for a platformer. Examples for times you would want to use this might be stuff like lasers, or perhaps bullets, if you just want it to go a straight line until it hits something, or objects that fly and can go through objects like a ghost- think stuff like planes or spacecrafts in top down shooters, or, as suggested earlier, ghosts that travel through walls. transform.Translate would be useful for such things, and the only impact they would receive is from triggers.
Now, assuming the game is 2.5 (using 3D objects for 2D rather than sprites, in which rigidbody2D is replaced with rigidbody), you could try my code, which uses rigidbody.AddForce only for jumping. It does work, with only one minor bug which I will get into in a minute. Note I set jump to an integer in Start. By doing this, you could add as many jumps to the character as you want, which is why I removed one jump after using the Jump function. If it only jumps once, you could use a bool instead. Now if the player touches something tagged "ground", only then will it collide.
void MovePlayer()
{
if (Input.GetButtonDown("Jump"))
{
Jump();
}
if (Input.GetAxis("Horizontal") < 0)
{
transform.eulerAngles = new Vector3(0, -90, 0);
speed = -5f;
}
if (Input.GetAxis("Horizontal") > 0)
{
transform.eulerAngles = new Vector3(0, 90, 0);
speed = 5f;
}
transform.Translate(0, 0, speed * Input.GetAxis("Horizontal") * Time.deltaTime);
if (Input.GetAxis("Horizontal") != 0)
{
Anim.SetBool("isMoving", true);
Anim.speed = Mathf.Abs(Input.GetAxis("Horizontal"));
}
if (Input.GetAxis("Horizontal") == 0)
{
Anim.SetBool("isMoving", false);
Anim.speed = 1;
}
}
public void Jump()
{
if (jumps > 0)
{
GetComponent<Rigidbody>().AddRelativeForce(0, 300, 0);
Anim.SetBool("grounded", false);
jumps --;
}
}
About the bug. Obviously, with OnCollisionEnter, it resets the max jumps. However, you also have to set OnCollisionExit to remove a jump as well. Here's the bug I face- Suppose I have two objects connecting eachother that are tagged ground, like a mesh for a floor and a mesh for the stairs. For some odd reason, when you exit one collision, it doesn't recognize it's still colliding with something else tagged the same way. This method, however, would work if it was a platformer with no two things marked ground are touching. I haven't tested it, but OnCollisionStay might be useful to create a workaround.
Now, rigidbodies. If it's rigidbodies you want to use, you can use rigidbody2D.AddForce(), or rigidbody.AddForce, depending, and direct the character using vector direction. Rigidbody2D.AddRelativeForce would also work, in which case you would add force in the direction you want the player to go relative to its position. Rigidbodies are directed by forces and not just mouse presses and releases. This is suited for objects like thrown objects, jumping, as mentioned earlier, or vehicles, IMO. If you add rigidbody.AddForce or relativeForce to Update, releasing a button stops the force from being added any further, and the character or object will slowly come to a stop. Here's a quick little code that, once applied to an object, demonstrates what rigidbody.addforce does.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Movement : MonoBehaviour
{
private Rigidbody RB;
public float forwardSpeed;
public float jumpSpeed;
// Start is called before the first frame update
void Start()
{
RB = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
RB.AddForce(transform.up * v * jumpSpeed);
RB.AddForce(transform.right * h * forwardSpeed);
}
}
To make the vertical motion a jump with limits, you could set an int for the amount of jumps, and on button down, replace RB.AddForce(transform.up v jumpSpeed); something like this.
RB.AddForce(0, jumpForce, 0, ForceMode.Impulse);
Hope this helps.
Thanks for answer Rigidbody2d is not comfortable for unusual types of movement, because rigidbody physics affect position and this must be taken into account in the code. the code can become spaghetti with rigidbody. For example sliding on the walls like in super meat boy is hard to realize it with rigidbody and clean code. I thought to use kinematic and calculate collisions with boxcast, but i don't sure there is no better solution
Your answer
Follow this Question
Related Questions
How to address a 2D animation flaw during a ledge climb? 0 Answers
Rigidbody2D falls slowly with MovePosition? 1 Answer
Rigidbody2D.velocity Interupts Rigidbody2D.AddForce in 2D Platformer 0 Answers
2D Physics movement system is creating unexplained random variations. 0 Answers
Unity 2d random Enemy and random direction movement on spawn. 0 Answers