Field of view visualisation (E02)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

Great tutorial. Very recently I discovered a very similar project which I added to the resources section of my website:
http://twiik.net/resources/procedural-mesh-vision-cone

But your approach is a bit less "brute forcy" so I'll probably replace it with your tutorial when I have some time. I already have a lot of your tutorials featured there. :p

One quick question though. Did you experiment with the optimizations described here before making the tutorial?
http://www.redblobgames.com/articles/visibility/

I'm wondering how hard they are to implement in praxis in Unity. Everything on Red Blob Games is so expertly written and described, but he makes everything sound so trivial when someone like me feels they are practically impossible to implement. :p

👍︎︎ 3 👤︎︎ u/-TwiiK- 📅︎︎ Dec 28 2015 🗫︎ replies

I saw the first part and loved it. Can't wait to see this second part.

I stumble around to trigonometry parts but I see that you've been break it down to help. As a long time subscriber to your YouTube channel thank you for another awesome tutorial! You really know how to explain yourself.

👍︎︎ 2 👤︎︎ u/I2oy 📅︎︎ Dec 27 2015 🗫︎ replies

Awesome :)

Maybe have you idea how to apply field of view to a spherical world? :)

👍︎︎ 1 👤︎︎ u/szagis 📅︎︎ Dec 28 2015 🗫︎ replies

Awesome.

👍︎︎ 1 👤︎︎ u/bagomints 📅︎︎ Dec 27 2015 🗫︎ replies

That was quite nice, certainly saved the code on git :)

Hope you do some more tutorials on other fancy stuff like this!

👍︎︎ 1 👤︎︎ u/Divinux 📅︎︎ Dec 27 2015 🗫︎ replies
Captions
hi everyone welcome to the second installment of this field view miniseries so today we're going to be working on the actual field of view effect so essentially what we want to do is just cast out a bunch of rays between our two view angles here and find all these sort of contact points of those rays and just construct a mesh out of that let's go into our field of view script and let's make a public float here called something like mesh resolution so if we make a new method called say draw field of view that mesh resolution variable is going to determine how many rays we casted out so let's make an integer we'll call this the step count but we also think of it as the Ray account and this we can just set equal to the view angle multiplied by the mesh resolution so the mesh resolution is basically the number of rays will cast per degree we're going to want to convert this to an integer so let's just round it with math F dot round to inch then we're also going to want to figure out how many degrees are in each step so let's say float step angle we'll size is equal to the view angle divided by the number of steps so step count well then want to loop through each of the steps so let's say for int I equals note I less than or equal to the step count I plus plus we can say that our current angle that we're working with is equal to first of all our objects current rotations so transformed or Euler angles or Y well then want to sort of rotate back to the leftmost view angle so we can just subtract view angle over to and all just sort of rotate clockwise in increments of step angle size until we get to the rightmost view angle so you can just add step angle size x I which is our current step count so if we want to just quickly preview what we've actually got here we could say debug draw line from transform drop position to transform drop position plus the direction from the angle that we've just calculated surpass an angle and this is a global angles we'll say true since we've already included the transforms own rotation in it and we can multiply this direction by the view radius and let's perhaps make the line nice red color all right let's we have to actually call this method so let's make a little update method and from this we can call draw field of view okay let's try that out so just hit play currently the mesh resolution is zero but if we bump that up you can see that we're getting a bunch of rays being drawn between the two view angles so that's great let's go back into the script though and we're going to want to do a ray cast for each of these angles in case there's an obstacle in the way having of course work through all of this stuff in advance I know that a little bit later on we're going to encounter some problems and in order to resolve those we're going to have to do even more ray casting so it kind of makes sense now just to make a method to handle the ray casting for us so we don't have to keep writing it out each time so this method will actually need to return a bunch of information about the ray cast so let's first make a little public struct to store that information in I'll call this the view cast info all right so this will need to know first of all whether or not the raycast hit something we also want to know the end point of the Ray so just make a public vector three-point as well as the length of the rail just call that distance and finally the angle that the rail was fired at all right so let's just quickly set up a constructor public view cast info so want to pass in a value of which of these variables Seibu underscore hich vector3 underscore point float underscore distance and finally float underscore angle and then we just need to assign these real quick so it equals underscore hitch and well you get the idea okay so let's go ahead and create our view class method so this will return a view cast infrastructure we can call the method view cast just takes in a angle let's just specify that that's a global angle all right and then we can say vector 3 Direction is equal to direction from angle pass in the global angle say true okay and then we want to make a ray cast hit variable just call that hit and then we can say if physics dot ray cast from a transform drop position with a direction of Direction we can pass out our raycast hit variable and we want the length of the Ray to be the view radius and we only want to collide with obstacles such as pass in our obstacle mask so if we hit something we will return a new view cast info or easy to pass in the relevant information to the constructor so this is if we did hit an obstacle so we can say hit is true the point will be hit dot point the distance will be hit distance and the angle will be the global angle that was provided to us on the other hand if we did not collide with an obstacle we'll want to once again return a view cast info this time will say false we did not hit anything instead of hit a point we'll just say transform and opposition plus the direction multiplied by the view radius and the distance will of course just be the actual view radius since it goes all the way out unimpeded and we just pass in the global angle as well okay so with that set up let's go back to our draw field of view method and I guess we can just delete this draw line and let's create a new view cast info and call this our new view cast this will be set equal to view cast and pass in our angle okay so we're going to want to maintain a list of all the points that our view cast hits so that we can actually construct that mesh so let's have a list of vector threes I'll call us our view points say that's equal to a new list of vector threes and so for each of these we can say view points dot add new view cast dot point okay so in case you're unfamiliar with how we go about generating a mesh from a script like to give a quick rundown of how this will work so say this is our character's position and let's say we fired out for rays so these are our four vector threes in our view points list we now want to join these all up into triangles for our mesh so let's let's call these vertices vertex 0 1 2 3 & 4 so our first triangle and we want to form each of these in a clockwise fashion we'll go from 0 to 1 to 2 back to 0 then the next one will go from 0 to 2 to 3 back to 0 and then 0 to 3 to 4 back to 0 let's triangle a B and C so apart from this array of vertices unity also wants a array of integers where each set of 3 integers represents the index of the 3 vertices in the triangle so in this example our triangles array would be 0 1 2 for triangle a then zero two three four triangle B and finally zero three four four triangle C so now let's say that the number of vertices we have let's call that V is equal to the number of viewpoints that we have so these four over here Plus this one point that we need at the origin so in this case we've got five vertices we've got a triangle count of three so we can say that the number of triangles is equal to the number of vertices minus two that means that the length of this triangles array over here is going to be the number of vertices minus two x three since there are three vertices in each triangle all right so back in monodevelop we can say that the number of vertices that we're going to have a vertex count is equal to the number of viewpoints so view points count plus one for that sort of origin vertex that we need so then we can say vector three array call this vertices is equal to a new vector3 array with a size of vertex count and then we want to create our integer array for the triangles this is equal to a new integer array with a size as we discussed of vertex count minus 2 or x 3 now one important thing to be aware of is that our view mesh is going to be a child of the character object and that means that all of the positions of the vertices need to be in local space so relative to the character so if we want to set for example our first vertex so vertices 0 equal to the transforms position we don't say transform the position we rather say vector 3.0 all right so now we want to loop through the rest of the vertices and set them equal to the positions in our view points list so let's say for int I equals 0 I less than vertex count minus 1 the minus 1 is just because we've all we've already set one of the vertices so we we can miss out doing one of them this time and we just say I plus plus okay so we want to say vertices I plus 1 so that we don't overwrite the first vertex we set is equal to viewpoints I all right I also want to set that triangles array so we can say triangles with an index of I times 3 so that will be the first vertex of each triangle which triangle starts at the origin vertex so the next vertex in the triangle that is I times 3 plus 1 should be equal to I plus 1 and then the last vertex in the triangle I times 3 plus 2 should be equal to I plus 2 so in case that triangles things are all confusing all we're doing is when I is zero we're setting these three vertices when I is 1 we are setting these three and when I is 2 we're setting these 3 and so on for however many vertices we have and of course we don't want this to go out of bounds of our array so let's just say that we only do this while I is less than vertex count minus 2 all right so let's go up to the top of the script and just want to create a public mesh filter variable can call this our view mesh filter and then we also want to a private mesh variable called our view mesh and then in the slant method will save you mesh is equal to a new mesh and we can really just set its name so view mesh name is equal to just view mesh I guess and then we want to assign that to the view mesh filter so say view mesh filter mesh is equal to that view mesh we've just created I'll write 2 now if we go into unity to select our character let's say create empty child and we can call this our view visualization all right we want this to have a mesh filter component as well as a mesh renderer component you can maybe just set cast and receive shadows both to off let's go into our character and just assign that mesh filter to our mesh filter variable might as well just set match resolution up to 1 now all right so now we can go down to the draw field of view right at the bottom here we can say view mesh dot clear so we reset everything in the view mesh to start with and then we'll say view mesh dot vertices is equal to our vertices array we've created view mesh dot triangles is equal to the triangles array we've created and then finally we can just say view mesh dot recalculate normals all right so if we run this we should see something happening okay so things are pretty wacky here at the moment I said I suspect the problem is that remember when I was saying how everything must be relative to the player's position well these sum these viewpoints that we're setting the vertices to our global points so we want to convert those to local space points so we can just say transform dot inverse transform point and pass in the view point so now if we save that let's go back and run this again so this is looking much closer to what we want one thing that's very important though is that our draw field of view method be called only after the controller has finished rotating otherwise we get this very sort of jittery look so let's go back and just change this update method to a later update method so it only gets called after the controller is updated and this should already look much smoother so one problem we're having at the moment is with the edges of obstacles you can see as we rotate it's still the sort of jitter that's happening there so one way we could fix this is just by increasing the mesh mesh resolution so I set that to ten the the jitter should now be basically entirely gone and that's that's very nice but that does mean that we're we're casting about 870 rays per frame which is a tiny bit excessive so basically what we're going to be doing for the rest of this episode is finding a slightly smarter way to solve that edge problem while we're here though let's just quickly open to the materials folder and to duplicate this and call this the view visualization material okay so we can set that to say fade and maybe just make this a sort of transparent white color and you can just apply that to the material slosh of our mesh renderer so now instead of that ghastly pink we get a nice transparent white so here's a sort of close up of the edge jitter problem and as you can see what we really need is to always have a vertex edge or at least very close to the edge of the obstacle okay so here's a diagram with a character and an obstacle let's say we're firing out our rays and the first one hits the obstacle we fire off a second that one also hits the obstacle and then we fire off a third and that one misses the obstacle so now we know that the edge is somewhere in between those last two rays so we label those the minimum and the maximum and we fire after a new Ray halfway in between the two let's say that one also misses the obstacle now we can say that is our new maximum and the edge is located somewhere between min and Max so once again we fire array right in between the two and this time let's say it hits the obstacle so that now becomes the new minimum and once again we fire a ray out halfway between the min and the max and as you can see we're very quickly sort of narrowed down the space and find the edge okay so we're going to have a find edge method so once again I want a little struct for the information that it's going to return so call this this time edge info and this just has to public factor threes a point a and a point B so one of those will be the closest point to the edge on the obstacle and the other the closest point to the edge or for the obstacle so we can just quickly make a constructor public info takes in vector3 underscore point a vector3 underscore point b it assigns those point a goals underscore point a and point B equals underscore point B okay so when we're doing this loop to find all of the view points we're going to want to know whether or not the previous view cast hit an obstacle or not so let's outside of the loop create a view cast info we call this old view cast set that equal to a new view cast info all right and at the end of the loop will always just set the old view cast equal to the new view cast of course on the first iteration when I is equal to zero the old view cast won't have been set yet so we can't really do anything so let's just say if I is greater than zero then we want to check if either the old view cast hit an obstacle and the new one didn't or the old one didn't and the new one did because then we need to find the edge so we can say if old view cast dot hit is not equal to new view cast dot hit then we're going to want to find the edge so let's make that method returns a edge info variable I'll call the method find edge and it takes in to view casts a min view cast and a max view cast all right so at the beginning we can say float min angle is equal to min view cast or angle and float max angle is equal to max view cast angle also want to make a vector3 min point by default that can be equal to vector 3.0 and likewise vector three max point should be equal to vector 3.0 so we're now going to want to have a loop where as I described in each step of the loop we will cast out array in between the min and the max angle so the more times we run this loop the more accurate our edge detection is going to be so let's actually create a variable up at the top here we can call this a public inch let's call this the edge resolved iterations all right so we'll run this for int I equals 0 I less than 8 resolved iterations I plus plus so we'll want to find our new angle so let's call that angle it's equal to the minimum angle plus the maximum angle all divided by 2 we'll then cast out a new view cast let's have you cast info new view cast equals view cast and we'll give that the angle we can then say that if the result of the new view cast hitch is the same as the min view cast hit then we're going to want to set the min angle equal to the current angle and the midpoint equal to the new view cast dot point otherwise we'll want to do the same thing with max angle set that equal to angle and Max Point get set equal to new view cast dot point okay then at the end we'll just want to return a new edge info and we'll pass in the min point and the max point okay so up here we'll say edge info edge is equal to find edge we'll give it our old view cast is the and the new view cast as the maximum and I want to add both of those edge points to the viewpoints list provided that they've been set so in other words there they don't still have their default value of vector 3.0 so we can say if edge dot point a is not equal to vector 3.0 then we'll say viewpoints dot add edge dot point a and just copy this to do the same thing for point B so if that's not equal to zero then we add point B so here's the same example from before only this time we've got the find edge method implemented and it's running with a edge resolve iteration of six and as you can see it's handling those edge cases far better than it was previously there is still a problem however and that is if you consider this scenario well view mesh is being cast against two different obstacles it doesn't realize that it needs to search for an edge because the first ray hits something and the second ray also hits something underneath an entirely different obstacle so what we want to do is implement some sort of distance threshold so even if the two consecutive rays both hit something if they're hits are a certain distance apart we'll consider them two different objects and we'll run our find edge method anyway so let's go up to the top of the script and make a public float can call this edge distance threshold all right and then over here where we're trying to decide whether or not we should do our edge detection we can make a boo called edge distance threshold exceeded and this will be equal to we first get the distance between the two hits so the absolute value of old view cast distance minus new view cast dot distance and we want to see if that is greater than the edge distance threshold and if it is then our edge distance threshold exceeded boo will be crew so we can say we do education if the old view cast hit is not equal to the new view cast hit or if both the old view cast hit something and the new view cast hit something and we've exceeded the edge distance threshold okay we'll want to make a similar modification in our find edge method let's just copy this edge distance threshold exceeded boo and we can paste it in over here so we'll just change old view cast to the min view cast and then we'll say we set the min angle in min point if the new view cast hit is equal to the mini you cast it and we have not exceeded the the edge distance threshold all right so now if we go into unity let's just set our edge resolve iteration to something like four and I'll set the edge threshold distance to may be 0.5 so now if we try this out you can see that our edges nice and smooth over there on this side as well and even with two obstacles it still works very nicely alright so that concludes episode 2 and possibly the series there have been a couple of requests for some additional features which I'm currently experimenting with and yeah so I may or may not decide to release a third episode with some of those but regardless thanks for watching I hope you've enjoyed the mini series and learned something useful from it so until next time Cheers
Info
Channel: Sebastian Lague
Views: 117,775
Rating: undefined out of 5
Keywords: Unity (Software), line of sight, stealth game, c#, programming tutorial, game development
Id: 73Dc5JTCmKI
Channel Id: undefined
Length: 27min 22sec (1642 seconds)
Published: Sun Dec 27 2015
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.