Problem with a RigidBody and IsKinematic... I think

I’m making a 2D endless runner type game, except there are bars the character can grab onto and swing from. The idea is that the player jumps, they get attached to the swing, they begin rotating, then when they release, they’re launched in the direction they were facing when they release. I’ve got it working perfectly when they jump from the ground, however if they jump from one bar to another, things get… funky. Instead of attaching to the bar, the character just drifts lazily during the attachment phase, then begins rotating around wherever they happen to end up.

The code is a bit unruly, but I can walk you through the process:

  1. The player character hits the collider of the swing.
  2. The OnTriggerEnter function on the swing calls a function on the player that calls a function on the player that makes the player’s RigidBody kinematic and sets its velocity to 0
  3. The swing then starts a coroutine that drags the player from wherever they hit the swing’s collider to the center of the swing, so everything animates properly. However this is where things go wrong
  4. What’s supposed to happen is the swing brings the player to the center using transform.translate over a fixed length of time, then starts a coroutine on the player to make them swing
  5. The coroutine rotates the player around the center point until the jump button is pressed
  6. Once it is, it sets the player’s RigidBody to no longer be kinematic and adds a force to them in the appropriate direction.

Again, when I do this from the ground, everything’s fine. The player is “tractor beamed” into position, swings around the center point, and launches when released. The problem occurs when you launch from one swing, and hit the collider of another. Even going frame by frame, it’s hard to tell exactly what happens. I know the character’s RigidBody is set to kinematic and stops moving when they hit the collider. I also know the angle they need to move in to be centered is calculated correctly. However, instead of moving to the center, the character just kind of… drifts for the length of time the “tractor beam” is supposed to be centering them, then begins rotating as normal, except they’re nowhere near the center.

Just from looking at it, it looks like some wonky physics in action. However, as soon as the character hits the swing collider, I set IsKinematic to true, and set its velocity to 0, so no forces should be acting on it except for the transform.translate that’s moving it into position. But not only is there this weird mystery force, the transform.translate that’s supposed to be working, isn’t. I can post the code if anyone wants to look, but seeing as it works OK the first time, I feel like the issue must be something with RigidBodies, Kinematics, and Transform.Translate that I just don’t understand. Can anyone help?

Edit: Ok, here’s the code, sorry if it’s a mess:

This is the OnTriggerEnter code on the swing itself that gets everything started:

	void OnTriggerEnter2D(Collider2D col){
		if (col.gameObject.tag == "Player") {
			col.gameObject.GetComponent<SimpleRunning> ().TractorMode (transform.position);
			Destroy(gameObject.GetComponent<BoxCollider2D>());
			StartCoroutine(TractorBeam(col.gameObject));
		}
	}

This is the TractorMode function that it calls on the player:

	public void TractorMode(Vector2 center){
		swingPoint = center;
		hasDived = false;
		m_Rigidbody2D.isKinematic = true;
		m_Rigidbody2D.velocity = new Vector2 (0f, 0f);
	}

and the TractorBeam coroutine it starts:

	IEnumerator TractorBeam(GameObject player){
		Transform head = player.transform.Find ("Head");
		Vector2 targetAngle = transform.position - head.position;
		Debug.DrawRay (head.position, targetAngle, Color.red, 10f);
		float timer = 0f;
		while (timer <= tractorTime){
			player.transform.Translate(targetAngle * Time.deltaTime / tractorTime);
			timer += Time.deltaTime;
			yield return null;
		}
		StartCoroutine (player.GetComponent<SimpleRunning> ().Swing ());
	}

and finally the Swing coroutine on the player:

public IEnumerator Swing(){
	while (!Input.GetKeyDown(KeyCode.Space)) {
		transform.RotateAround (swingPoint, Vector3.forward, rotateSpeed * Time.deltaTime);
		yield return null;
	}
	isSwinging = false;
	m_Rigidbody2D.isKinematic = false;
	Vector2 jumpAngle = new Vector2(Mathf.Cos(Mathf.Deg2Rad*transform.eulerAngles.z),Mathf.Sin(Mathf.Deg2Rad*transform.eulerAngles.z));
	m_Rigidbody2D.AddForce(launchMultiplier * Mathf.Sqrt(2) * m_JumpForce * jumpAngle.normalized);
}

How are you calculating Tractor time ? It seems that this would need to be dynamic based on how far the player is from the swing pole.

If it’s a static amount it will work on the first time (as you have measured the amount of time it takes to move from the ground position to the swing, but then it might over translate as is happening as the tractor time was used to calculate just the initial distance between the position on the ground and the pole.

That tractor time will need to be set based on the amount of travel that actually needs to take place.

A better way of doing it might be to use MoveTowards or Lerp.

(Untested but something like)

    while (player.transform.position != target.position){
        player.transform.position = Vector2.MoveTowards(transform.position, 
        target.position,  Time.deltaTime / tractorTime);
        yield return null;
    }

Obviously this wont work directly as you will need to calculate the offset position of the head from the body.