I have tried looking at a lot of other questions that are similar but all of them have the same problem: that you can jump up the wall. Here is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Movement : MonoBehaviour {
public float movementSpeed = 20f;
public float jumpHeight = 1250f;
private Rigidbody2D rb2d;
private bool isJumping = true;
void Start ()
{
rb2d = GetComponent<Rigidbody2D>();
}
private void FixedUpdate()
{
float move = Input.GetAxis("Horizontal");
Vector2 movement = new Vector2(move * movementSpeed, 0f);
rb2d.AddForce(movement);
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Ground")
{
isJumping = false;
}
}
private void OnCollisionExit2D(Collision2D collision)
{
if (collision.gameObject.tag == "Ground")
{
isJumping = true;
}
}
void Update ()
{
if ((Input.GetKeyDown(KeyCode.W) || Input.GetKeyDown(KeyCode.UpArrow)) && !isJumping)
{
rb2d.AddForce(Vector2.up * jumpHeight);
}
}
}
Is there any other way I can use the OnCollisionEnter2D to specify what side it collides on?
You could use Collision2D.contacts, get the average Normal vector of all contact points, and compare that to vertical by using Vector3.Angle(). If the angle between the normal and Vector3.up is less than, say, 30 degrees, the player can jump.
EDIT
Instead of getting the average, this script tests each contact normal to see if at least one is within the minimum incline angle.
I think I may have gotten something wrong with my currentCollisions
set, though. This should work for now.
Movement.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Movement : MonoBehaviour
{
public float movementSpeed = 20f;
public float jumpImpulse = 20f;
public float maxGroundedAngle = 30f;
private Rigidbody2D rb2d;
private bool isJumping = true;
// set of colliders currently in contact.
private List<Collider2D> currentCollisions = new List<Collider2D>();
void Start()
{
rb2d = GetComponent<Rigidbody2D>();
}
private void FixedUpdate()
{
float move = Input.GetAxis("Horizontal");
Vector2 movement = new Vector2(move * movementSpeed, 0f);
rb2d.AddForce(movement);
}
void Update()
{
TryJump();
}
// attempts to make the object jump.
private void TryJump()
{
// if they can jump, and player presses jump button
if ((Input.GetKeyDown(KeyCode.W) || Input.GetKeyDown(KeyCode.UpArrow)) && !isJumping)
{
isJumping = true;
// apply an impulse at this moment of time (not a force over a long time).
rb2d.AddForce(Vector2.up * jumpImpulse, ForceMode2D.Impulse);
}
}
// every frame it is in contact with a collider...
private void OnCollisionStay2D(Collision2D collision)
{
if (collision.gameObject.tag == "Ground")
{
// add the collider to the set of current collisions
if (!currentCollisions.Contains(collision.collider))
currentCollisions.Add(collision.collider);
// Check if they can jump.
// default value for if no contacts fulfilled the condition.
isJumping = true;
Vector2 up = Vector2.up; // or you could use -Physics2D.gravity
// They can jump if any contact has a normal pointing up (within a maximum angle).
foreach (ContactPoint2D contact in collision.contacts)
{
// for visualization in scene view
Debug.DrawRay(contact.point, contact.normal, Color.red);
Debug.DrawRay(contact.point, up, Color.blue);
// if within maximum angle
if (Vector2.Angle(contact.normal, up) < maxGroundedAngle)
{
isJumping = false;
break; // exit for loop
}
}
}
}
private void OnCollisionExit2D(Collision2D collision)
{
if (collision.gameObject.tag == "Ground")
{
// remove the collider from the current collisions
currentCollisions.Remove(collision.collider);
// if that was the last collider to leave,
if (currentCollisions.Count == 0)
{
isJumping = true; // the object is in the air.
}
}
}
private void UpdateIsJumping()
{
// if there are no more colliders touching the object,
if (currentCollisions.Count == 0)
{
isJumping = true; // it is in the air.
}
else // if there is at least one collider,
{
isJumping = false; // it is still on the ground.
}
}
}
First create empty child for that object and on that child add Collider to top of the child and create this function in script and add script to child
public RigidBody2D object;//this is object which will jump, attach him to script in editor
private void OnCollisionEnter2D(Collision collider)
{
object.AddForce(Vector2.up*speed);
}