AI question - increasing alertness by player movement

Hello again all,

I’m having a bit of trouble puzzling out how I want to handle some AI for target that a player is hunting. The target has a child GameObject that has a SphereCollider as a trigger attached. If the player enters the trigger, the target will flee from the player to a random waypoint. That part is working fine.

What I want to have happen though is this-- the target will have a caution radius in addition to the flee collider. When the player enters the caution radius, the AI will start firing a function off that will check if the player has moved at all. If the player has moved, the radius of the flee collider will be increased. Essentially, if the player moves around too much while the target is in a caution state, it will eventually flee from him. The caution state will have a timeout on it so that if the player sits still long enough, the target will drop back into a normal movement state. There will be a slight cooldown on the caution state so that the player doesn’t trigger it immediately again.

I am having some trouble figuring out how to measure the player’s movement once the caution state is entered. It’s slightly complicated by the fact that the player can be in either first person or 3rd person mode when approaching the target, so there’s two different objects that could potentially approach the target. I thought I could maybe pull the speed value out of the ThirdPersonController script attached to the model, but the first person mode uses a different controller.

Am I just going to have to check what model is approaching the character and dig out the appropriate speed variable? Or is there a better way to be going about this?

Thanks,
Alex

Just have the caution script have a variable called “moving” that is turned true when input is received for player movement and turned false on frames that no input is found.

That plus another sphere collider to detect the player in the caution radius and you’ll be all set!

Alex:
Your solution sounds quite reasonable, however I don’t see why the amount of movement should cause the character to fear the player more.

AngryAnt, it’s not that the target will fear the player more the more he moves, I’m trying to simulate the target having a higher chance to “see” the player the more he moves around. The caution radius represents the target noticing that something is in its vicinity but not actually seeing it yet (it hears/smells the player), so it’s alert and trying to determine if there’s a threat in the area. As soon as it actually sees the player it will get spooked and run off.

Gargerath, that could work, but if possible I’d like there to be some variance besides just on/off. At first I started having the target do a check every update against a random value that was scaled based on how far away the player was, so the closer the player got the higher the chance the target would detect him. Unfortunately, the checks were occurring so fast it was pretty much guaranteed to trigger a flee as soon as the player entered the caution radius.

I guess I could set up an accumulator that gets incremented every frame that the movement flag gets set, and decays when there’s no movement, and have the flee radius increased when it reaches a certain threshold… though I think I’m going to run into the problem with the rate of checking again.

CONUNDRUM. T_T

If you don’t like the rate of checking, run a function that checks every quarter of a second or something.

Like this:

function IsHe()
{
   while (Vector3.Distance(transform.position, player.position) < *outer edge of sphere*) {
      if (Random.value < *chance to get caught*) {
         Flee();
      }
      yield WaitForSeconds(.25);
   }
}

Had a question about this… when determining if one object has come within a certain distance of another, is it more efficient to use a collider, or to use a Vector3.Distance check?

Hi Alex,

Re speed, I’d go with measuring player position in consistent increments, a few times per second. You could then calculate speed by the delta of position change during that time.

I like collider triggers, but they pose issues if you need to raycast from the object’s position (the ray may hit your trigger). If you only care about proximity of a specific object (your player), simply Vector3.Distance would likely be better, since a collider would be compared against other objects in the world as well.

You can use layers to avoid that. Vector3.Distance involves a square root, which is a bit slow. If you’re only doing a few checks occasionally, it’s not going to have any effect, but if you’re doing a bunch of distance checks every frame, then triggers would be more efficient. The physics system is quite well optimized and you can have hundreds of triggers without much effect on the framerate.

–Eric

I was wondering about this. How do you go about doing this? The only information I found regarding layers is telling them to ignore raycasts.

Right, I should have mentioned that. In the case of the example I ran into the trigger was attached to main object - I did want the ray to hit the object, just not the trigger. I later thought that I could have implemented it by having the trigger on a sub-object, which was on the to-be-ignored layer.

Something like:

var ignoreMe : LayerMask;

function Start () {
	if (Physics.Raycast(transform.position, transform.forward, 100, ~ignoreMe.value)) {
		print ("woot!");
	}
}

If you wanted to check to see if you hit certain layers (and ignore the others) instead of ignoring the specified layers (and hitting everything else), then you wouldn’t invert the value, of course.

–Eric

Does this only work with raycasts, or can you have two colliders only check OnTrigger events for each others’ layers and not the rest of the environment?

Only raycasts…for OnTrigger stuff, you’d use Physics.IgnoreCollision, which unfortunately doesn’t use layers. But you can always check the collider’s name or tag in OnTriggerEnter or whatever, and early out if it’s not the one you want.

–Eric