- Home /
How to make "enemy" have a frustum(cone) view and not a circle radius
So I have an enemy script, and it works perfectly, but the only thing I don't like about it is that it chases the player when it's in a circle radius that I can change. I want to make it a cone shaped view where the enemy can only see the player. Similar to the radius, but not a circle AROUND the enemy. but ahead of the enemy in a certain direction. I am also using an array to detect if there are walls in the way. Here is the code for the following part. (Of course the script has more code)
Vector3 difference = playerTransform.position - transform.position;
bool hit = Physics.Raycast(transform.position, difference.normalized, difference.magnitude, 9);
Debug.DrawRay(transform.position, difference, Color.red);
if (difference.sqrMagnitude < distanceUntilChase && !targetPlayer.isInBush && !hit) // Conditions to follow the player
{
// Chasing player
goneImage.SetActive(false);
StartCoroutine(AlertNotice());
coroutineRunAlert = true;
coroutineRunGone = false;
rb.velocity = difference.normalized * chaseSpeed;
rotZTarget = Mathf.Atan2(-difference.x, -difference.z) * Mathf.Rad2Deg; // Calculates the angle between angle and enemy (from difference)]
lastSeenTime = Time.time + timeUntilPatrol;
lastSeenPosition = playerTransform.position;
//timeUntilNextPoint = Mathf.Infinity;
}
This is what I have:
And this is what I want:
The blue square is the player, the red square is the enemy, and the black outlines represent the radius.
Calculate the angle between the enemy's forward vector and the vector from the enemy to the player. If it's bigger than e.g. 45 degrees, ignore the player.
https://docs.unity3d.com/540/Documentation/ScriptReference/Vector3.Angle.html
Answer by Koen-Matthijs · Jan 30, 2017 at 09:53 PM
Hi
You could use trigger-colliders for this. If you'd need multiple on the same GameObject you can create child objects on that object, and put one trigger collider on each of them and use any of the available mechanisms in Unity to call a method in a script on the parent object (eg. SendMessage or a delegate maybe, or just a plain public method).
To make a cone-shaped trigger collider you could then create a cone-shaped mesh and assign that mesh to the collider (cone shaped meshes can come with very little vertices to keep them simple)
With kind regards,
Koen Matthijs
I knew this was a way to do it, but I thought it would be inefficient, but it works, so I guess why not? Thanks.
Answer by Sparrowhawk81 · Jan 31, 2017 at 04:58 PM
I had to do something similar once. Don't have the exact file I used anymore, but I kinda remember what I did. If I were you I'd recommend breaking out some of your tasks into bool functions you can use together to check whatever conditions you want. This example includes a bool InView function, but you could also do a bool UnObstructed that casts a ray and sees if it can hit a target and return true or false. This will result in you being able to use clean code like if (InView(target) && UnObstructed(target)) for simpler if statements and such.
I typed this up again from memory but didn't test it. You may have to redo a decent amount of what you've done, but I think this will work pretty well. This code is for a SphereCollider with "is trigger" set on it, but you could also use it with a CircleCollider2D in a 2D game with some tweaking. If you think about it, a cone is just two lines going off in different directions with a defined "end" line between them. You can use the radius of the trigger to take care of that for you and also make sure it only bothers to check the FOV when something is actually close enough to matter (in the trigger and matches the right tag).
public class BlahBlah : MonoBehaviour
{
[SerializeField] private string _targetTag; // set in editor
[SerializeField] private float _fov; // this thing's field of view
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
}
void OnTriggerStay(Collider other)
{
// Something entered the radius trigger!
// is it tagged as the thing we are interested in catching?? if not, just quit
if (!other.gameObject.CompareTag(_targetTag))
return;
// ok now we know it's what we want and that it is in our "radius" trigger collider
// no need for distance checking...that's the point of the collider
// Just use the function like a bool variable to see if we can see it, if we can...
// then do whatever we want to do.
// you could also combine this with a bool function that casts a ray to check for
// stuff in the way
if (InView(other.transform))
{
// target has been verified as in range and in view
// do whatever you want
}
}
bool InView(Transform target)
{
// Find the direction to the target from this transform (enemy's transform)
// using Vector3 math and save it in a variable
Vector3 targetDir = target.position - transform.position;
// Find the angle from the "forward" direction (eyes) to the target
float angle = Vector3.Angle(transform.forward, targetDir);
// Have a variable on the script called _fov (or whatever) exposed to editor so
// you can set it and tweak it if the calculated angle is less than the FOV then
// the enemy this script is attached to can "see" the target
if (angle <= _fov)
return true;
// If not, then he can't see the target.
return false;
}
}