Turn AI off when colliding and keep physics simulation

Desired behavior: when AI colliding with a moving rock, turn AI off for a while (dizzy) to follow the physics (bounce).

In OnCollisionEnter2D, I set AIPath.canMove to false. But what I got is the AI just move a little bit, then stops moving. If it been hit by a second rock during the period, the AI starts follow physics to bounce.

I checked AIBase, that it uses rigidbody.MovePosition in FixedUpdate to position the game object. Based on Unity ExecutionOrder, the execution order is FixedUpdate => Internal physics simulation => OnCollisionXXX. Thus during that frame, rigidbody.MovePosition run first, then the physics simulation, then canMove set to false.

I wonder is this desired behavior achievable?

Update: the collided rock is destroyed in OnCollisionEnter2D.

Try this when a rock hits your AI:

richAI.updatePosition = false;
richAI.updateRotation = false;

and when you want to turn it back on:

richAI.updatePosition = true;
richAI.updateRotation = true;
richAI.Teleport(current_agent_position_here);

also you need to enable/disable RVOController if you have one

Thanks for help. But the approach didn’t work for me. The AI (AIPath) stops when I set updatePosition/Rotation to false, no physics simulation was “applied” to the agent itself.

Example

  • Red dot is the moving agent
  • Brown dot is the rock
  • When agent hit the rock, the rock is bouncing, but the agent just stops (expected to bounce back).

My workaround is set canMove=false, then wait for 1 frame to apply a force manually.

I guess that the AIPath is using rigidbody.MovePosition to move agent, which changed velocity of that frame, basically canceled the bounce (internal physics simulation). Because the OnCollision call is always late, there seems no way to achieve what I want, unless applying a force manually short after.

I can not upload an example project to this forum. All game objects has dynamic rigidbody 2d, with mass 1. My agent script here,

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Pathfinding;

public class MyAgent : MonoBehaviour
{

    private void OnCollisionEnter2D(Collision2D collision)
    {
        Debug.Log("OnCollisionEnter2D");
        var agent = GetComponent<AIPath>();

        agent.canMove = false;
        // or
        // agent.updatePosition = false;
        // agent.updateRotation = false;

        // Toggle below line to simulate a force one frame delayed.
        // StartCoroutine(CoMove(-transform.up, 0.5f));
    }

    IEnumerator CoMove(Vector2 dir, float power)
    {
        yield return null;
        GetComponent<Rigidbody2D>().AddForce(dir * power, ForceMode2D.Impulse);
    }

}

.

Ah, your agent have rigid body. Then you are probably right. I am using collider only and create RigidBody on CollisionEnter and it works fine. I can sujest you to try it if you dont need RigidBody all the time. Also this approach will have better performance.

It feels a bit weird to create a RigidBody in the CollisionEnter (and perhaps remove it later when AI is on, then back and forth) to trigger the physics simulation. I guess when you do that on two overlapped objects, the new created rigidbody (with AIPath off) will trigger the physics simulation to solve the overlap and results in a little bounce?

In my example with below code, it doesn’t work well. The agent don’t have a rigidbody until the CollisionEnter. It prints OnCollisionEnter2D twice. But perhaps the object overlap is not that much, it won’t bounce at all.

    private void OnCollisionEnter2D(Collision2D collision)
    {
        Debug.Log("OnCollisionEnter2D");
        var agent = GetComponent<AIPath>();

        agent.canMove = false;
        var rigidbody2D = GetComponent<Rigidbody2D>();
        if (!rigidbody2D)
            rigidbody2D = gameObject.AddComponent<Rigidbody2D>();
        rigidbody2D.gravityScale = 0;
        // or
        // agent.updatePosition = false;
        // agent.updateRotation = false;

        // Toggle below line to simulate a force one frame delayed.
        // StartCoroutine(CoMove(-transform.up, 0.5f));
    }

Well it depends on your project needs. In my case it fits well. I create Rigidbody for some time and give it a force. I am using it to push agents away from a point. But if you need to navigate agents that are continuously colliding all the time maybe you need another approach.

And give a force, perhaps that’s the key. Then it is same with my workaround - give a force in one frame delayed.

Anyway I agree with you that this approach works for a few cases. It just I’m looking for an easy way to turn off the AI and keep the physics simulation instantly. I can of course give a force, but I don’t want to repeat what physics did already.

Sorry to revive an old thread @favoyang, did you ever get this working? I’m trying to get enemy AIs to bounce off each other as well and your code above almost works for me but not quite.

The enemies were initially launching each other into the stratosphere but I changed the power float to 0.02(wth?!) and now it’s better.
But now the bounce starts off as non-existent and seems to build up over time, sending them further and further away from each other as time goes on. At the moment I’m resetting the velocity back to 0 after the bounce ends and they get back on their aipath but it doesn’t seem to be stopping my problem.

Hi @scoot I’m not sure what the problem you’re facing.

The post just clarifies that if you want a moving agent to bounce with another agent, you need to simulate that by adding force to both agents when they collide.

  • AIPath.canMove = false;
  • AddForce
  • Wait a proper time
  • AIPath.canMove = true;

When you turn on canMove (the last step), your agent will recalculate the velocity, so don’t need to set the velocity to zero manually.