The Spiderman issue

Hi guys.

I’m the developer of a small team currently working on a 2D gameplay platformer using unity. I’m currently testing a physic based alternative to the transform.position based code I’ve been using up until now. I’ve obtained some satisfying results but a few things still compromise the usability of the method. So I came here searching for people with more experience than me in the use of physics.

I’m using a simple capsule collider as character and I move it by using AddForce() until the maximum velocity is reached. I make it jump by adding a large vertical positive force when the Jump button is presed. The Spiderman problem is that if I jump near a wall while moving toward it, the wall slow down the jump and we fail to reach the top of the wall, worst, we stay glued to the wall if we keep the key presed.

I’ve tried the following solutions:

  • Using physics materials with no friction: no difference.
  • Adding Bounciness: no difference on the side and awful result on the top (kangaroo style).
  • Forbidding the addforce if a sweeptest detect an obstacle (ignoring collision with the floor): partial result.
private var Jump = false;
private var Grounded = false;
private var hit : RaycastHit;
private var VitesseMax : float = 0;

Physics.gravity = Vector3(0, -30.0, 0);

function FixedUpdate () 
{	
	//Running
	if(Input.GetButton("Fire1") == true){VitesseMax = 10;}else{VitesseMax = 5;}
	
	//Moving forward if there is no obstacle.
	if (!rigidbody.SweepTest (Vector3(Mathf.Sign(Input.GetAxis("Horizontal"))*1,0,0), hit, 0.1))
	{
		if(rigidbody.velocity.x < VitesseMax  rigidbody.velocity.x > -VitesseMax)
		{
			rigidbody.AddForce (Input.GetAxis("Horizontal")*30,0,0);
		}
	}
	else
	{
		if(hit.point.y < transform.position.y - ((collider.height/2)*0.9)  Grounded == true)
		{
			if(rigidbody.velocity.x < VitesseMax  rigidbody.velocity.x > -VitesseMax)
			{
				rigidbody.AddForce (Input.GetAxis("Horizontal")*30,0,0);
			}
		}
		else
		{
			rigidbody.velocity.x = 0;
		}
	}
	
	//Jumping
	if(Input.GetButton("Jump") == true  Grounded == true){Jump = true;}
	if(Input.GetButton("Jump") == false  Jump == true)
	{
		rigidbody.AddForce (0,400,0);
		Grounded = false;
		Jump = false;
	}
	
	transform.position.z = 0;
}

function OnCollisionEnter(collision : Collision) 
{
	if(collision.contacts[0].point.x <= transform.position.x + collider.radius  collision.contacts[0].point.x >= transform.position.x - collider.radius  collision.contacts[0].normal.y > 0)
	{
		Grounded = true;
	}
}

This last solution is efficient with floating nonmoving platform, we can’t Spiderman on them. We can’t Spiderman on wall either but the jump reduction is still here. Finally, we can still Spiderman on moving platform while they move toward us.

If you guys have ideas on the way I can modify my tests or methods to achieve the desired result it would help me greatly.

Any ideas guys ? I wouldn’t want to abandon this solution just because I fail to resolve this one matter.

After thinking about it some. You could have your character’s “gravity” turned off, then add downward force in all normal situations. Then, when it hits something, change the gravity to the direction. As it stay connected to that object, keep pressing the gravity in that direction. If you jump off of it, or for some reason become disconnected, gravity should immediately change back to normal.

Once you figure out the order of the Update, OnCollision… and LateUpdate, you will get a good grasp on how to keep accurate track of your gravity.

The big problem I can see is that you would have to know that you are facing that normal in order for it to look right. So in the method below, as soon as you touch the side, you are walking on it. This doesn’t mean that you are facing any specific direction, but are “stuck” to it. If you had bounce on your object, as soon as you bounce back up, gravity would resume as normal.

Also, on your Jump… use rigidbody.velocity=Vector3(0,30,0) or whatever should be your current direction of travel… ( rigidbody.velocity=gravityNormal * -gravity; )

private var gravity=-30.0;
private var gravityNormal=Vector3.up;
function Update(){
rigidbody.AddForce (gravityNormal * gravity);
}

// note that you shouldnt be using the collision info but a normal based on what is directly down.
function OnCollisionEnter(collision : Collision) {
var down= transform.TransformDirection (-Vector3.up);
var hit : RaycastHit;
if (Physics.Raycast (transform.position, down, hit)) {
gravityNormal=hit.normal * gravity;
}
}

function OnCollisionExit(collisionInfo : Collision) {
gravityNormal=Vector3.up * gravity;
}

Hi BigMisterB

Thank you for your reply. I’m curently trying your solution.

So, after quite a lot of tests, I came to the conclusion that I have to somehow forbid the character from touching walls. I’m now testing the surrounding of the character with sweeptests and raycast depending of the situation and I modify it velocity if there is an obstacle. This should do it for the time being.

Thanks to the people that thought about it and to you BigMisterB again for your solution.