Im currently trying to create a simple 2d platforming game, and im having a little trouble with my character when he collides with another object mid-fall/mid-jump. He is jumping, falling and walking sideways like i want him to. However, whenever he jumps into a wall or falls into a wall, if I hold down the ‘walk left’ or ‘walk right’ button (i.e. keep trying to get him to walk sideways into that wall), he freezes in mid air. Could anyone tell me how I might fix this?
Here is some of my script (Javascript):
public var horizontal_speed : float = 5.0f;
public var jump_Power : float = 15.0f;
private var directionFacing : int = 1;
private var isJumping : boolean = false;
public function Update () : void {
//Horizontal Movement
if (Input.GetButton("Horizontal")) {
// for directionFacing variable -----
// 1 = character is facing right
// -1 = character is facing left
directionFacing = Input.GetAxis("Horizontal") < 0 ? -1 : 1;
rigidbody.velocity = new Vector3((directionFacing * horizontal_speed), rigidbody.velocity.y, 0);
}
//If character isn’t jumping
if (!isJumping) {
//Jump
if (Input.GetButton("Jump")) {
isJumping = true;
rigidbody.velocity = new Vector3(rigidbody.velocity.x, jump_Power, 0);
}
}
}
Please note: I later on in this script have a section where “isJumping” is turned back to being false, but it involves a lot of raycast detection stuff that i dont think is relevant. If however you would like me to post that as well, Im more than happy to do so.
Currently the character has a box collider (not trigger) and a rigidbody. It is using gravity and ‘is kinematic’ is off. Constraints are set to freeze all rotation and z translation. Mass = 1. Walls and floor are also using box colliders (not trigger) - no rigidbodies on these.
I’m assuming when I hold down left or right the amount of velocity i am applying to the character is overpowering the effect of gravity, but i dont know how to stop this effect (plus it doesnt make sense to me that this should happen).
Can anyone please help? I dont mind if someone can suggest a C# solution as well, as it looks like it should be a fairly simple issue.
I do a lot of scripting around people from more C orientated backgrounds with slightly anal coding standards. Like you say, it’s not at all of value; but it makes things ‘friendlier’ for them when they come to read it … apparently. But yeh, it doesnt add anything.
In my experience the Unity physics seems to be reactive as opposed to proactive. Objects are allowed to end up inside eachother, and when they do forces are applied to make sure they’ll get out of eachother. Therefor when you directly modify the rigidbody’s velocity you counter-act the physics attempt of making your object leave the thing it’s collided with.
This appears to cause problems with walking into walls, as long as you’re inside the wall physics is bound to behave a bit odd. Hence as long as your keep applying the velocity towards the wall, you will stay inside the wall and have unwanted effects on your physics.
The way I’ve solved this in previous projects is by implementing a proactive physics check. That is to say, see if your wanted movement will result in a collision and if so prohibit that movement.
private Rigidbody mBody;
void Awake()
{
mBody = rigidbody;
}
void FixedUpdate()
{
// Get the velocity
Vector3 horizontalMove = mBody.velocity;
// Don't use the vertical velocity
horizontalMove.y = 0;
// Calculate the approximate distance that will be traversed
float distance = horizontalMove.magnitue * Time.fixedDeltaTime;
// Normalize horizontalMove since it should be used to indicate direction
horizontalMove.Normalize();
RaycastHit hit;
// Check if the body's current velocity will result in a collision
if(mBody.SweepTest(horizontalMove, out hit, distance))
{
// If so, stop the movement
mBody.velocity = new Vector3(0, mBody.velocity.y, 0);
}
}
That’s a rough implementation of the concept, hopefully enough to give you a general understanding of what I mean.
thanks for this solution, I may end up having to use it. I actually discovered another little extra thing on this topic. Turns out that Unity’s default physics material on all objects has friction settings of 0.4, if you create a new physics material and apply this to your walls then adjust the friction to be 0, then the problem will disappear. HOWEVER. Your floors need to still have some kind of friction material on them, otherwise your character will skid. The problem will consequently still exist if you fall off a floor platform then try to walk mid-air into its side. Another long winded workaround here might eb to divide up your floor into 3 separate meshes: the main floor with default physics material, and a separate mesh for either end of the platform with your new frictionless physics material applied.
The material solution you mentioned worked fine for me. I have no issues with the drag because I am stopping the moving object with rigidbody.velocity. I have no need for drag. I’m so happy I found this thread!
I see I’m not the only one who got “Stuck” on this issue, lol. I just changed the Interpolate variable on my characters rigidbody2d component to Extrapolate and that worked like a charm for me.
Two things: set the player collider physics material to have 0 dynamic and static friction coefficient. Also set it to minimum instead of average.
Next: do not set velocity manually. Use Rigidbody.AddForce(). Say your target velocity is a variable named targetSpeed. You’d do: rigidbody.AddForce(targetSpeed - rigidbody.velocity, ForceMode.Velocity). By subtracting target speed by the current velocity, this does the same thing as changing the velocity directly without the flaws of doing so. This also fixes your sliding problem, since if targetSpeed = 0,0,0 (the player isn’t touching the controls), the net result of the function sets rigidbody.velocity to 0.
For another 2D example, I created a few variables which would start the raycast at the boundary of the player on left and right, with a sufficiently small raycast max distance. Set velocity to 0 if you hit the wall. Additionally, I add a small amount to the boundary in order to give a bit of tolerance to the collision detection. I’m writing th
I’m sure i’ll eventually get something cleaner, but this works for now until i need something with a bit more elegance. Also note that my comments are from my code, so some of that may not be clear out of context, so apologies in advance.
//Create a box to start raycast outside of. Keeps hitting player instead if we do not do this.
Vector2 boxVectorStartRight = new Vector2(body2D.position.x + boxSize.size.x/2+ 0.05f, body2D.position.y);
Vector2 boxVectorStartLeft = new Vector2(body2D.position.x + -boxSize.size.x/2 - 0.05f, body2D.position.y);
if (inputState.moveRight) {
//Set velocity to zero if we hit a boundary. fixes speed thing. Might want to slow descent in the future. Allow to move normally otherwise.
if (Physics2D.Raycast (boxVectorStartRight, -Vector2.right, 0.001f))
playerVelocity = 0;
else
playerVelocity = 20f;
//playerVelocity = Acceleration (playerVelocity, accelerationBase);
}
Although this may be an old topic and I found the above posts to be very helpful; but they didn’t quite work for what I wanted in 2D.
This method should be used at the end of FixedUpdate. Then it will stop the rigid body from clinging clinging to walls as well as stopping the user from being unable to jump when on the floor.
private void FinalCollisionCheck()
{
// Get the velocity
Vector2 moveDirection = new Vector2(rigidBody.velocity.x * Time.fixedDeltaTime, 0.2f);
// Get bounds of Collider
var bottomRight = new Vector2(playerCollider.bounds.max.x, player.collider.bounds.max.y);
var topLeft = new Vector2(playerCollider.bounds.min.x, player.collider.bounds.min.y);
// Move collider in direction that we are moving
bottomRight += moveDirection;
topLeft += moveDirection;
// Check if the body's current velocity will result in a collision
if (Physics2D.OverlapArea(topLeft, bottomRight, EnvironmentLayer))
{
// If so, stop the movement
rigidBody.velocity = new Vector3(0, rigidBody.velocity.y, 0);
}
}
Note:
EnvironmentLayer is the layer of which we want to walk on. Floors, Walls… etc
The layer I used was 1 << 9 as the Environment is considered layer 9
If you do this and update the player velocity.x based on input, then the next frame will render the velocity.x above zero because the distance calculated will be 0. So you are going to ping pong between dropping and pushing against the wall.
If it’s a 2D game, then you’re better off not calculating the position of the character on the next frame and just running a SweepTest() based on the horizontal input and the (+/-)Vector.right. Then use an arbitrary distance value. For 3D I’m not sure how you would approach this issue.
// We are going to hit a wall next frame if we keep moving in this direction
if (rb.SweepTest((Mathf.Sign(HorizontalInput) * Vector3.right), out hit, 0.3f))
{
// Nuke horizontal velocity
rb.velocity = new Vector3(0, rb.velocity.y, 0);
}
In 2d game, i have a solution that is detect collision combine with on ground checking, if true set velocity horizontal to 0 or movement horizontal to 0.
private bool m_isCollided;
private void OnCollisionEnter2D(Collision2D other) {
m_isCollided = true;
}
void OnCollisionExit2D(Collision2D other) {
m_isCollided = false;
}
void Update() {
float horizontalMove = Input.GetAxis("Horizontal");
// Setting movement if was collided in the air or not
if (!controller.isGrounded && m_isCollided) {
horizontalMove = 0;
}
// my Move function is normal change rigidbody2d velocity;
controller.Move(horizontalMove);
}