Im using this code on an enemy to check when he spots the player in the game.
var int_HitRadiusDistance = 20;
function Update ()
{
var ray1 = new Ray( transform.position, transform.forward );
var hit : RaycastHit;
if( Physics.Raycast( ray1, hit, int_HitRadiusDistance ))
{
if( (hit.collider.name == "FPS") || (hit.collider.name == "Player") )
{
// TODO: Attack-code
}
}
}
The problem is that it’s just shooting a single ray, which means the enemy has no peripheral vision what so ever. So a cone of rays would be preferable.
Now, I could probably create a few additional rays with the same origin but with different directions, and check for each of them in the if-statement.
var int_HitRadiusDistance = 20;
function Update ()
{
var ray1 = new Ray( transform.position, transform.forward );
var ray2 = new Ray( transform.position, transform.left ); // ERROR
var ray3 = new Ray( transform.position, transform.right );
var hit : RaycastHit;
if( ( Physics.Raycast( ray1, hit, int_HitRadiusDistance )) || ( Physics.Raycast( ray2, hit, int_HitRadiusDistance )) || ( Physics.Raycast( ray3, hit, int_HitRadiusDistance )) )
{
if( (hit.collider.name == "FPS") || (hit.collider.name == "Player") )
{
// TODO: Attack-code
}
}
}
But perhaps there is a smoother inbuilt function that I havent been able to find in the docs? Besides, Im not quite sure about the syntax for the second parameter of the Ray-object (transform.[direction]) and how to create a cone. I mean, how do I express the direction in numbers? In degrees?
You could have an invisible cone shaped object and do collision testing on that. Then if any collisions come up positive do a ray from the enemy to the player to make sure its an actual line-of-sight.
That sounds like a better idea, actually. And simply unchecking the MeshRenderer of the object makes it invisible (did not know that until now :lol: ). I think Ill try that instead - Thanks
But just for educational value, is there a method such as the one I described with raycast?
Actually, using an invisible cone-object wont work in my case because of the layout of the map. It would mean that parts of the cone would penetrate walls and other objects that raycast wont, and the effect would be that of an x-ray. So back to the raycasting, any suggestions there?
That’s why Nick said to use a linecast between the enemy and the player when the trigger picks up the player in range. So you still use raycasting, but only when necessary. That’s going to be a lot more efficient than continuously emitting a spread of raycasts every frame.
Sorry, I must have read the first part of Nicks post and just assumed the rest I think, or my brain freezed up or something. That definitely sounds like the way to go, so thanks again
And I dont have to continue to figure out how to set a diagonal direction with ray, which has been a pain the last hour. ?
But out of curiosity, how do you set the direction? Lets say I wanted a ray to shoot horisontally but 45 degrees to the right, what would I put instead of [direction]?
var ray = new Ray (transform.position, [direction]));
I’ve tried that before and it doesn’t work. Problem is if you don’t mark it as a trigger things actually collide with it and if you mark it as a trigger it breaks the collision detection in one scenario.
From what I’ve seen if you make a trigger and attach it to your player and the player move the OnTriggerEnter will never fire for what it collides with. Triggers only seem to fire when something else moves into a trigger, not the other way around.
The way I solved a similar problem, not the same but close, was to put NPC’s on their own layer and then use:
There’s an easy way to get a cone using the dot product. Get a vector pointing from the player to a potential target using their positions, and normalise that vector:-
var heading: Vector3 = (target.transform.position - transform.position).normalized;
Then, get the dot product of this heading vector with the vector representing the player’s direction of sight (this should also be normalised):-
var dot: float = Vector3.Dot(sightVec, heading);
The dot product of two normalised vectors is equal to the cosine of the angle between them (ie, it will be one when the directions are the same, zero when they are at 90º and 0.5 when they are at 60º). You can convert this to an angle if you want particular accuracy, but for most purposes, you can just play with a value between 0 and 1 until you get good results. Basically, the check is whether the dot is greater than your threshold value. Since the angle could be in any direction, up/down, left/right, you effectively get a cone of sight this way.
Ive been knocking my head against the wall with trying to get the cone-object method to work, but without any luck.
Let me see if I understand whats going on here:
// 1) Get the direction between player and enemy (normalized value, between 0 and 1)
var heading : Vector3 = (target_player.transform.position - transform.position).normalized;
// 2) Get the direction the enemy is watching (normalized)
var sightView : Vector3 = ( transform.forward );
// 3) Compare heading with sightView to get the difference (normalized, which corresponds to an angle: 0(=0)/0,5(=60)/1(=90)
var dot : float = Vector3.Dot( sightView, heading );
Q: But how can I use this value (dot) with Raycast? I mean, dot is a single value, not a direction any more, while Raycast uses directions (Vector3) as parameters.
Alright, I got tired of this and constructed a couple of child objects for the enemy with different angles (forming sort of a cone), and attached this code to each one.
var int_HitRadiusDistance = 20;
function Update () {
var ray1 = new Ray( transform.position, transform.forward );
var hit : RaycastHit;
if( Physics.Raycast( ray1, hit, int_HitRadiusDistance ) ) {
if( (hit.collider.name == "FPS") || (hit.collider.name == "Player") ) {
// TODO: Attack-code
}
}
}
We’ll see later on if this puts a too heavy load on the game, but since I only have one enemy I dont think so in my case.
Im wondering if creating the objects (ray1 and hit) outside the Update-function will go less heavy on the engine than doing it each time the Update-function is called, or if its all the same?
Sorry - reading that again, I see I didn’t make it very clear what I meant. You can use the dot product thing as a quick test to see if the other object is roughly within the cone. Once you’ve established that it is, you can use a raycast for a line of sight check.
Incidentally, you can also use the result of the dot product as a probability of spotting the player (ie, 1 when straight ahead, 0 when at the side).
Since this is my first post ever let me say hello:
Hello,
I was trying to do something very similar with a cone of view, or a circular perception area. I have been unable to find a way to graphically represent it. I just want to draw two lines represting the cone and maybe a transparent layer in between them, or just a simple circle around the player but so far I could not find such a functionality except for a drawline function that is only available to pro version. Am I missing something or can it be done with creating a cone shaped object with transparent material and texture (which is what I am currently trying to do)?
@Danneman: It would be great if you could tell me how you tackled this problem.
Also there is the problem of the aforementioned perception circle/cone object being visible to the player (assuming all enemies have one) when it is not really convenient for the enemy’s location being given away while there is an actual obstruction between the player and the enemy. Or in another case I think it would be nice for the player to know that he can hide behind an object which casts a perception circle/cone shadow.
So I specifically want the rest of the circle/cone to be culled according to the objects that intersect it.
I am thinking about raycasting to find the objects that the circle intersects, get their dimensions, and maybe modify the circle/cone/transparent area so that they seem to cast a shadow but I have no idea how to do it apart from the raycasting part. Any ideas?
Much thanks in advance for any help to be followed by more thanks.
edit: the following link is a good example of the perception cone that I am trying to get.
are you sure you want a cone or a simple triangle will do, as it is in the video?
If a triangle is enough, then you can build a mesh dynamically using the mesh class. Cutting it where it intersects other objects though is not going to be easy.
you can check this thread as an example of how to create dynamic 2d shadows with meshes: http://forum.unity3d.com/viewtopic.php?t=26679
which is not quite what you want, but you might get some ideas from it.
I have also done something closer to what you ask (modelling a 2d spot light with a mesh) but it’s not too general and I can’t give you the code for it (since it was done per somebody’s request). I can give you an overview of the algorithm though, if you are interested.
//from [url]http://answers.unity3d.com/questions/8453/field-of-view-using-raycasting[/url]
function CanSeePlayer() : boolean{
var hit : RaycastHit;
var rayDirection = ObjectToSee.transform.position - transform.position;
// If the ObjectToSee is close to this object and is in front of it, then return true
if((Vector3.Angle(rayDirection, transform.forward)) < 90 (Vector3.Distance(transform.position, ObjectToSee.transform.position)<=CloseDistanceRange)){
//Debug.Log("close");
return true;
}
//Debug.Log("rayDirection - transform.forward =" + Vector3.Angle(rayDirection, transform.forward));
if((Vector3.Angle(rayDirection, transform.forward)) < fieldOfViewRangeInHalf){ // Detect if player is within the field of view
//Debug.Log("within field of view");
if (Physics.Raycast (transform.position, rayDirection, hit, HowFarCanISee)) {
if (hit.collider.gameObject == ObjectToSee) {
//Debug.Log("Can see player");
return true;
}else{
//Debug.Log("Can not see player");
return false;
}
}
}
}
On a related note, I’ve finally create a visual for the line of sight in the game. Nothing fancy, just attaching a Line Renderer to the game object.
“In the end, I decided to use a simple solution to create a Line Renderer object and tweak the starting and ending sizes to make it look somewhat like a cone. I created an image of a dotted line for the Line Renderer material. And I attached the Line Renderer object to the NPC object and have it drawn in the Z-axis(which is the direction the NPC is facing) by 2 units. See the image below for how I set up the Line Renderer object in the inspector. (Please ignore the transform as it’s related to how my NPCs are constructed.)”
I have an array of non-player characters accessible to a main game script. On every update I calculate the distance between NPC and the character, only NPCs that are close enough to see the character are processed.
Then I fire a raycast from the NPC to the player, this is to determine if anything is in the way.
If the distance is low and the line of sight is open, I can then (depending on the NPC) evaluate the rotation of the NPC and deal with “he is behind you” scenarios seperately.
If all of the tests return “True” I set a “spotted” boolean on the NPC script and his behaviour changes accordingly.
Slightly modified version in C#, uses player height to take into account uneven terrain.
//from http://forum.unity3d.com/threads/raycasting-a-cone-instead-of-single-ray.39426/
bool CanSeeMonster(GameObject target)
{
float heightOfPlayer = 1.5f;
Vector3 startVec = transform.position;
startVec.y += heightOfPlayer;
Vector3 startVecFwd = transform.forward;
startVecFwd.y += heightOfPlayer;
RaycastHit hit;
Vector3 rayDirection = target.transform.position - startVec;
// If the ObjectToSee is close to this object and is in front of it, then return true
if ((Vector3.Angle(rayDirection, startVecFwd)) < 110 &&
(Vector3.Distance(startVec, target.transform.position) <= 20f))
{
//Debug.Log("close");
return true;
}
if ((Vector3.Angle(rayDirection, startVecFwd)) < 90 &&
Physics.Raycast(startVec, rayDirection, out hit, 100f))
{ // Detect if player is within the field of view
if (hit.collider.gameObject == target)
{
//Debug.Log("Can see player");
return true;
}
else
{
//Debug.Log("Can not see player");
return false;
}
}
return false;
}