RVOController; I am utterly confused regarding how to use this

I have added the RVOSimulator class to a gameobject in my scene, and I have added the RVOController to a single agent in my scene, but when I have several issues:

1.) The character is floating above the terrain (no rigidbody, no collider). My characters have their origin set at their feet. if I adjust the height manually during play I can it to a point where it turns around in circles, but it just makes zero sense what it is doing; it bounces all over the place when I do this:

2.) I can’t seem to get the character to actually GO anywhere. Pathfinding was working perfectly before switching to this controller (and still technically is, vectorPath is chock full of points), but now it simply will not move anywhere. Here is my relevant code:

public virtual IEnumerator Goto(Vector3 position) {           
        actorState = ActorState.Thinking;
        Path path = pathSeeker.StartPath(transform.position, position);    
        yield return StartCoroutine (path.WaitForPath());      

        actorState = ActorState.Moving;
        int currentWaypoint = 0;           

        while(currentWaypoint < path.vectorPath.Count) {
            // move us a bit                
            Vector3 direction = transform.position.NormalTo(path.vectorPath[currentWaypoint]);
            //transform.LookAtConstrained(path.vectorPath[currentWaypoint], Vector3.up);

            //Vector3 translation = direction * speed * Time.deltaTime;
            //transform.Translate(translation, Space.World);
            controller.Move(direction * speed);                

            // are we close to next waypoint?
            if (Vector3.Distance(transform.position, path.vectorPath[currentWaypoint]) < 0.1f) {
                currentWaypoint++;                               
            }

            yield return null;               
        }

        NextAction();
    }

The old code I used before using the controller is commented out. NormalTo() is an extension method I made to simply get the normal pointing to a target.

Sometimes it will just spin in circles, but mostly it will just stand there like an idiot.

What am I doing wrong?

EDIT: I got a little further. I figured out that I needed to set the center variable to something correct (for me (0,1,0)). the character now does not stand there like an idiot; it rotates around in circle like an idiot. It is almost as if it is trying to avoid itself…

I’ve been at this all day. I am starting to suspect it is bugged.

Unless speed is around 10000, the character hardly moves. Everywhere it does move to is not anywhere near the path generated. Sometimes it moves in a straight line forever, sometimes it moves in a square or triangle pattern. My code is not all that different from the getting started examples.

Please anyone?

@aron_granberg can you confirm or deny if something is wrong with the current code? Why do I need speeds in the 1000’s to see any movement for a 2 meter tall character? Why does it not go in the direction I pass to Move()?

Hi

Sorry for the late answer.
Correct, you need to adjust the center field.

Unfortunately I cannot see anything wrong with the code otherwise (assuming the implementation of NormalTo is something like (other - this).normalized).

Have you tried using the RVOExampleAgent script that is used in the example scenes and checked if you get the same result with that script? (you will need to call the SetTarget method once on it to make it move).

One thing that might be problematic is your code for picking the next waypoint. If that characters is 2 meters tall, a distance of 0.1 to the next waypoint is very small in comparison so it might be hard for it to actually get within 0.1 units of the waypoint without overshooting. Even worse, the y coordinate matters in that check, so even if the character is standing right on top of the waypoint, their y coordinates might still differ by 0.1 (depending on how you have set it up). I would suggest that you discard any y coordinate differences when you make the check.

I hope this helps.

Hey Aron, Thanks for getting back to me.

I got a teeny bit further, but it is still acting very wonky. I found I had a small issue with that NormalTo() method in instances where the distance is so small, that I woudl end up with an up facing vector. It worked fine when using it as starting point for finding the next position (no velocity or anything), but NOT as a velocity. I made an adjustment to it, but unfortunately it was not enough. It moves, but the path it takes is very unusual. Here is my latest version:

public virtual IEnumerator Goto(Vector3 position) {           
        actorState = ActorState.Thinking;
        Path path = pathSeeker.StartPath(transform.position, position);    
        yield return StartCoroutine (path.WaitForPath());            

        actorState = ActorState.Moving;            
        int currentWaypointIndex = -1;            

        Vector3 direction;
        Vector3 currentPosition;
        Vector3 currentWaypoint;
        do {
            currentWaypointIndex++;
            currentPosition = transform.position;
            currentWaypoint = path.vectorPath[currentWaypointIndex];
            currentPosition.y = 0;
            currentWaypoint.y = 0;
            direction = currentPosition.NormalTo(currentWaypoint);
            yield return null; 
        } while (direction == Vector3.zero && currentWaypointIndex < path.vectorPath.Count);

        if (direction != Vector3.zero) {  
            while(currentWaypointIndex < path.vectorPath.Count) {
                // move us a bit                
                //Vector3 direction = transform.position.NormalTo(path.vectorPath[currentWaypoint]);
                //transform.LookAtConstrained(path.vectorPath[currentWaypoint], Vector3.up);

                //Vector3 translation = direction * speed * Time.deltaTime;
                //transform.Translate(translation, Space.World);               
                DebugExtension.DebugArrow(transform.position + (Vector3.up * 0.5f), direction, Color.red);
                DebugExtension.DebugWireSphere(path.vectorPath[currentWaypointIndex], Color.blue, 0.0825f);
                controller.Move(direction * speed);                

                // are we close to next waypoint?
                //if (Vector3.Distance(transform.position, path.vectorPath[currentWaypointIndex]) < 0.1f) {     
                Vector3 directionToMe = currentWaypoint.NormalTo(transform.position);
                float dot = Vector3.Dot(direction, directionToMe);
                DebugExtension.DebugArrow(transform.position + (Vector3.up * 0.5f), directionToMe, Color.blue);
                if(dot >= 0) {
                    do {                           
                        currentWaypointIndex++; // INDEX OVERFLOW; why?
                        currentPosition = transform.position;
                        currentWaypoint = path.vectorPath[currentWaypointIndex];
                        currentPosition.y = 0;
                        currentWaypoint.y = 0;
                        direction = currentPosition.NormalTo(currentWaypoint);

                        yield return null; 
                    } while (direction == Vector3.zero && currentWaypointIndex < path.vectorPath.Count);                     
                }

                yield return null;               
            }

            // stop movement
            controller.Move(Vector3.zero);
        }

        NextAction();
    }

Funny enough, my fix is exactly what you suggested about Y (this was why I needed such high speeds, it was almost exactly up). It helps, but it still seems to overshoot paths a lot. The only reason it is not overshooting indefinitely is because I now use a dot product to test rather than distance (far fewer calculations as well). My grid nodes are 2 meters apart, but it still manages to overshoot them.

I did try to study RVOExampleAgent, but I was confused by it. I didn’t understand why you keep recalculating the path. I guess I could run a test to see if it helps. I wish I could make a gif to show you what happens, but every time I leave focus the agent stops moving.

@aron_granberg

So I tried RVOEXamplAgent, and that for the most part acts correctly.

I tweaked my own code and I finalyl have something that mostly works. It’s a little wonky at times, but it accidentally ads a naturalness to the movement for a human, so I am happy with it.

I have an important question though. I see that in RVOExampleAgent you recalculate the path fairly often. Is this something that has to be done in case local avoidance kicks in? Could you explain why you do that a bit?

Also, is there a way to query the RVOSimilation to see if an agent is avoiding something? I’d like to be able to support several thousand agents, so I would like to keep the pathfinding to a minimum (only recalculating the path when absolutely necessary)

Hi

Recalculating the path from time to time is good when using local avoidance since there is no guarantee that the agent actually follows the path (there might be a large number of agents moving in the other direction preventing it from doing that for example). You can either check for the deviation from the path that it is currently following, or you could for example check if the velocity of the agent is roughly the same velocity as you requested it to move with and use that to decide if the path should be recalculated or not. Also keep in mind that for several thousand agents, things like raycasts for ground placement and rendering can add up and become a bottleneck.

Yeah, I know there will be a bottleneck for sure. I plan to only render these agents up close and cull them when X distance from camera; probably disable local avoidance (if possible) for agents not currently being rendered.

Anyways, yes I would love to be able to compare velocity. Not just for recalculating the path, but also to help prevent overshoots at higher speeds. My math is a bit lackluster though. How could I do the following:

  1. Compare the requested velocity to the actual velocity used? not sure where the actual velocity is stored (if at all)
  2. Test to see if the actual velocity would overshoot a point?

Maybe the second question is getting out of scope, but kinda hopping there is a method hidden somewhere I could use.

You know what? I think I am beginning to understand RVOExampleAgent. It is constantly checking it’s velocity against the distance to the next point, and using that to find the next logical waypoint. Is that the jist of it?

Hi

Yes, that is essentially it.

This is trickier because RVO runs in a deferred loop, so the velocity is actually not applied unit the next frame. However a good way to solve overshooting is to clamp the length of the velocity vector to the distance between the current position and the target (if the target is currently the final waypoint in the path) multiplied by some constant (depending on how smoothly you want it to slow down).

You can get the velocity from rvoController.velocity. Note that this will not change immediately after you have called Move since to use local avoidance, the movements of all agents are calculated at the same time after you have called Move on all agents.