YouTube2Text

Line Of Sight or Shadow Casting in 2D — Transcript

by javidx9 · 9,250 words · 1,250 segments · language en · Watch on YouTube

Full transcript

  1. 0:00hello in this video I'm going to be
  2. 0:03looking at quite a classic algorithm
  3. 0:04line-of-sight also known as shadow
  4. 0:06casting and as usual I'm going to start
  5. 0:09by showing you what it is that I'm
  6. 0:11talking about so here I have an arena
  7. 0:13where there is a boundary defined in
  8. 0:15blue and if I hold down my right mouse
  9. 0:17button I can smell shine a light in this
  10. 0:20arena and as I move the mouse around I
  11. 0:23can see that the light is following the
  12. 0:25mouse cursor this is all done in the
  13. 0:27pixel game engine by the way with the
  14. 0:29left mouse button I can draw shapes so
  15. 0:32I'm going to draw a boundary here and
  16. 0:35you can see it's sort of a a tile map
  17. 0:37array and the nice thing is now when I
  18. 0:39hold down the right mouse button and
  19. 0:40turn the light on we can see that the
  20. 0:42boundaries cast shadows well the cache
  21. 0:45shadows but they also indicate the
  22. 0:47locations that can't be seen from where
  23. 0:49the mouse cursor is so this is also
  24. 0:51line-of-sight and it behaves quite
  25. 0:54nicely I'll add in a few more features
  26. 1:01and see how it behaves and all of the
  27. 1:05features cast their own shadows in fact
  28. 1:12we can add lots and lots and lots of
  29. 1:14features you see some of them are little
  30. 1:20boundaries and walls we can extend
  31. 1:21things out got lots of interesting
  32. 1:22shapes it doesn't matter
  33. 1:24the algorithm can quite happily deal
  34. 1:26with any layout of tiles
  35. 1:34I'll make here at the bottom a small
  36. 1:37enclosure and the algorithm behaves as
  37. 1:41you might reasonably expect it to as we
  38. 1:43enter the enclosure visibility of the
  39. 1:45rest of the field is restricted to the
  40. 1:47doorway and in fact if we close off the
  41. 1:50doorway of course we can't see outside
  42. 1:52the little room line of sight is very
  43. 1:54useful in strategy games
  44. 1:56so here I've developed a little corridor
  45. 1:58and I'm going to push the agent through
  46. 2:00the corridor and we can see he can only
  47. 2:02see what's inside the corridor as he's
  48. 2:04moving around now I think that's a
  49. 2:09really cool algorithm and my
  50. 2:10implementation of it is one of several
  51. 2:12methods you can use so let's take a look
  52. 2:14at how it's done but before we get
  53. 2:16started this is my very first pixel game
  54. 2:19engine exclusive video so I'd like to
  55. 2:21spend some time just showing you how to
  56. 2:23set up a pixel game engine project using
  57. 2:25visual studio now I know that for many
  58. 2:28of you this is all old hat and you know
  59. 2:30what you're doing but you'll be
  60. 2:31surprised I get quite a few comments
  61. 2:32people saying they don't really know how
  62. 2:34to set up visual studio so please
  63. 2:35forgive the next couple of minutes
  64. 2:37whilst I do this when you start with
  65. 2:39your studio you'll be presented with the
  66. 2:40start page I recommend going to file new
  67. 2:43and selecting project in the list of
  68. 2:46projects you'll see one is called a
  69. 2:47Windows console application give the
  70. 2:50solution a name I'm going to call this
  71. 2:51one shadow casting 2d and press ok now
  72. 2:54visual studio this is the very latest
  73. 2:56version of visual studio we'll go ahead
  74. 2:58and create some files for you and it
  75. 3:01even gives you some hints as to what
  76. 3:02these files are for for my videos though
  77. 3:04I don't really want the file structure
  78. 3:06it provides the first thing I want to do
  79. 3:09is get rid of the PC H dot H file and
  80. 3:11the PC H CPP file so I select them and
  81. 3:14press Delete I don't want them I'm not
  82. 3:17going to use precompiled headers this
  83. 3:19also then means I have to get rid of the
  84. 3:21include PC H H from the main whilst
  85. 3:25method I'm going to get rid of all of
  86. 3:27these comments - we need to tell it that
  87. 3:29we don't want to use precompiled headers
  88. 3:30in our project so go to project
  89. 3:32properties and make sure we've got debug
  90. 3:34selected up here and expand the C and
  91. 3:37C++ option go to precompiled headers and
  92. 3:42in the tabloid says use precompiled
  93. 3:45headers we're going to say not using
  94. 3:47compiled headers and click apply whilst
  95. 3:50we're here you might also want to do the
  96. 3:51same thing for release now I know it's
  97. 3:57very controversial but the next thing I
  98. 3:58want to do is actually include the STD
  99. 4:02namespace and the only reason I do this
  100. 4:05is because it's easier to make videos
  101. 4:07that appear clearer best practice is not
  102. 4:10to do this we're creating a pixel game
  103. 4:12engine application so at this point you
  104. 4:15need probably need to go to the one line
  105. 4:16code at github and download the header
  106. 4:18file and here it is OLC pixel game
  107. 4:20engine dot h grab this file and copy it
  108. 4:24into the folder that you created when
  109. 4:25you created a new solution so on my
  110. 4:27machine this is the shadow casting 2d
  111. 4:29folder I've pasted the header file into
  112. 4:32this location I then want to include
  113. 4:34that file in my source code and now
  114. 4:42there is one last little thing that we
  115. 4:44need to do which was different from the
  116. 4:46console game engine and this is
  117. 4:47currently experimental so you may not
  118. 4:49always need to do this but right now
  119. 4:51it's something I'm trying out and that
  120. 4:53is before we include the pixel game
  121. 4:56engine file we need to define our
  122. 4:58application as being one uses the pixel
  123. 5:01game engine and we do that by doing hash
  124. 5:03define OLC underscore PGE underscore
  125. 5:07application and we need to do that
  126. 5:09before we include the header file and
  127. 5:11the reason that I'm taking this approach
  128. 5:14is that instead of having a separate
  129. 5:16object orientated version of the pixel
  130. 5:18game engine I'm trying just using a
  131. 5:20single header file which means we need
  132. 5:22somewhere in all of our compiled code an
  133. 5:24actual implementation of the pixel game
  134. 5:27engine and that will be wherever we have
  135. 5:29defined this constant LLC PGE
  136. 5:32application now as most of my videos are
  137. 5:34a single file solution I will just want
  138. 5:37to define this constant at the top of my
  139. 5:39main file next I want to override the
  140. 5:42OLC pixel game engine base class this is
  141. 5:44just the same as the console game engine
  142. 5:46and was shown in the OLC pixel game
  143. 5:48engine video so thanks for putting up
  144. 5:50with that I just needed a little bit of
  145. 5:51a video reference for the people that
  146. 5:53asked those questions so let's carry on
  147. 5:56now we've got the basic structure in
  148. 5:58place I'm going to create an object of
  149. 6:00type shadow cast
  150. 6:01and call the construct function and I
  151. 6:05want to construct a pixel game engine
  152. 6:07with resolution pretty high now 640 by
  153. 6:10480 where each pixel is 2 by 2 screen
  154. 6:13pixels and if that's successful then
  155. 6:17start the engine at this point you may
  156. 6:20want to do a test compile and run just
  157. 6:22to make sure everything set up correctly
  158. 6:23and we should see a black screen of the
  159. 6:26dimensions we've just specified very
  160. 6:29nice now this is a bit of a 2 for 1
  161. 6:31video because to implement shadow
  162. 6:33casting or line of sight we really need
  163. 6:35two algorithms I like the convenience of
  164. 6:38being able to draw the worlds in tiles
  165. 6:41placing blocks is quite intuitive as we
  166. 6:43saw with the Jerry Oh Pat forming game
  167. 6:45you could have a selection of blocks and
  168. 6:47place them wherever you want but the
  169. 6:49shadow casting algorithm itself relies
  170. 6:51heavily on geometry and if we have to do
  171. 6:54geometry for all of the blocks every
  172. 6:56single frame well I feel that that's not
  173. 6:58very efficient so the first stage of the
  174. 7:00algorithm is to convert our tile map of
  175. 7:03blocks into a polygon map of edges here
  176. 7:06I've drawn out an arbitrary section of
  177. 7:09level or world based on a tile map so we
  178. 7:12can see where I've placed the blocks
  179. 7:13where I haven't placed the blocks in the
  180. 7:15background this is very convenient as it
  181. 7:17makes life easy for the level developer
  182. 7:19it's easy to draw particular sprites in
  183. 7:21set locations we've done collisions with
  184. 7:23tile maps before there's lots of
  185. 7:25advantages to using a tile map but we
  186. 7:28don't want to use the geometry of every
  187. 7:29single block edge to cache shadows
  188. 7:31because at any given moment there's a
  189. 7:33lot of edges instead what we prefer to
  190. 7:37see is the boundary of the clusters of
  191. 7:40blocks so we're going to come up with an
  192. 7:41algorithm that will turn a cluster of
  193. 7:43blocks into a polygon we're going to
  194. 7:45find the bounding edge of this block now
  195. 7:47I can look at this block and intuitively
  196. 7:49say well we've got a vertex here and a
  197. 7:52vertex here a vertex here one here one
  198. 7:57here here here and here
  199. 8:04we also have four more vertices here and
  200. 8:08in principle what I'd like to do is link
  201. 8:10these vertices with lines
  202. 8:18and I think it's quite easy to see now
  203. 8:22that there are far fewer boundary lines
  204. 8:24than there are at block edges once you
  205. 8:27move out of the blocky world of the tile
  206. 8:29map into the smooth world of the polygon
  207. 8:31there are lots of other advantages we
  208. 8:34get as well we could implement realistic
  209. 8:36physics and collision detection we can
  210. 8:38have edges which don't align with the
  211. 8:40natural tile boundaries in the world and
  212. 8:42of course we can have natural-looking
  213. 8:44gradients and slopes I think we'll see a
  214. 8:46lot more of this algorithm popping up in
  215. 8:48future videos I want to construct these
  216. 8:51polygon boundaries in the most efficient
  217. 8:53way possible so ideally given a cutout
  218. 8:56of the larger world I want to scan
  219. 8:58through it want to create the set of
  220. 9:00edges and so that's exactly what I'm
  221. 9:03going to do and where to start from the
  222. 9:04top left and I'm going to scan
  223. 9:06horizontally across the screen and when
  224. 9:08I get to one end I'm going to come back
  225. 9:10to the start and scan the next row in
  226. 9:14this simplified example the structure
  227. 9:16that I have for a particular cell in the
  228. 9:19tile map is quite simple it either
  229. 9:21exists or it doesn't so in some sort of
  230. 9:23cell structure I know I'm going to have
  231. 9:26a boolean for exists but in order to
  232. 9:31implement my algorithm I'm going to need
  233. 9:32some additional information I'm going to
  234. 9:34add four more boolean's which tell me
  235. 9:38did the edges exist and these relate to
  236. 9:41the north south east and west edges and
  237. 9:45as we saw at the start a cell may share
  238. 9:49an edge with other cells this implies
  239. 9:52that any given cell can't actually own
  240. 9:54an edge instead it must use one that
  241. 9:57stored else were so I'm going to also
  242. 10:00include four integers which represent
  243. 10:03the ID of a given edge on the north
  244. 10:07south east or west side from some edge
  245. 10:10pool that will create else were so let's
  246. 10:14start manually going through the
  247. 10:15algorithm firstly we want to check does
  248. 10:18the cell we're currently inspecting
  249. 10:20exist because this cell doesn't exist
  250. 10:22this cell doesn't exist etc etc etc all
  251. 10:24the way along in our world until we get
  252. 10:26to why
  253. 10:27that does exist we only care about cells
  254. 10:30that exist so we're going to apply tests
  255. 10:33to this cell and the first test that
  256. 10:35we'll try from the perspective of this
  257. 10:38cell is do I have a Western neighbor
  258. 10:41well in this situation clearly I don't
  259. 10:43have a Western neighbor so I definitely
  260. 10:46have a western edge but where do I get
  261. 10:49this edge from well the only other place
  262. 10:52an edge could come from is from a
  263. 10:54northern neighbor that also has a
  264. 10:56western edge and in effect we'll take
  265. 10:59that edge and grow it downwards in this
  266. 11:02situation we don't have a northern
  267. 11:04neighbor so we're going to create a new
  268. 11:07edge and so we'll add to our edge pool
  269. 11:10edge a and in our cell structure we'll
  270. 11:14link our edge ID to the location of the
  271. 11:16edge in the edge pool we'll now
  272. 11:19systematically check the other sides of
  273. 11:21the tile firstly we'll check the eastern
  274. 11:23edge so if I have an Eastern neighbor
  275. 11:25which in this case I do then clearly
  276. 11:28that isn't an edge necessary between us
  277. 11:31we don't need to do anything further now
  278. 11:33we need to check our northern neighbor
  279. 11:35well I don't have a northern neighbor in
  280. 11:38this case so I do need an edge but where
  281. 11:41can I get one from well I can either get
  282. 11:43one from my western neighbor or I need
  283. 11:46to create one of my own and since I
  284. 11:48don't have a western neighbor I'm going
  285. 11:49to have to create one of my own so we'll
  286. 11:51create a new edge called B and we'll
  287. 11:54give the ID of that edge to the cell
  288. 11:56also drawing the a edge there as you can
  289. 11:59probably tell already it's a similar
  290. 12:01situation for this southern edge - we're
  291. 12:03going to need to create one for this
  292. 12:05cell see now we're traversing from top
  293. 12:08left to bottom right so this is the next
  294. 12:10cell that we check and we run through
  295. 12:12exactly the same routine do I have a
  296. 12:14Western neighbor well I do so I don't
  297. 12:17need a western edge
  298. 12:18do I have an Eastern neighbor well again
  299. 12:21I do
  300. 12:21I don't need an Eastern edge do I have a
  301. 12:23northern neighbor no I don't so I do
  302. 12:26need an edge but this time rather than
  303. 12:28creating one for myself what I can see
  304. 12:31is that my Western neighbor currently
  305. 12:33already has a northern edge so I'm going
  306. 12:37to extend that edge to suit my needs
  307. 12:41set my edge ID as well the final check
  308. 12:43is do I have a southern neighbor well I
  309. 12:46don't so I do need an edge and you've
  310. 12:48probably already guessed I'm going to
  311. 12:49grow my Western neighbors edge as well
  312. 12:51and set my cells edge ID on the southern
  313. 12:55boundary to suits that particular edge
  314. 12:57exactly the same routine occurs for the
  315. 13:00next cell so let's run through it again
  316. 13:02for this corner cell well
  317. 13:05we check do I have a Western neighbor I
  318. 13:08do so I don't need an edge
  319. 13:11do I have an Eastern neighbor I don't in
  320. 13:14this case so I do need an edge now I
  321. 13:17can't borrow one from my northern
  322. 13:19neighbor so I'll have to create one D
  323. 13:22and I'll add that edge to the edge pool
  324. 13:25and set my ID now we check for northern
  325. 13:28neighbors and just as we've done with
  326. 13:29the previous cells I don't have a
  327. 13:30northern neighbor so I do need a
  328. 13:32northern edge but fortunately my Western
  329. 13:34neighbor has got one I can borrow so I
  330. 13:37will extend that edge and set my ID
  331. 13:39accordingly now for southern neighbors
  332. 13:41this is the first time we check for
  333. 13:42southern and I've got a southern
  334. 13:44neighbor so I don't need an edge between
  335. 13:46myself and my southern neighbor which
  336. 13:48means I no longer need to extend the
  337. 13:51edge C horizontally in fact it's very
  338. 13:54likely we'll never need to do anything
  339. 13:55with C again but we don't have to touch
  340. 13:57it it's in the edge pool it's got to
  341. 13:59start at an end point it's done so we'll
  342. 14:02move on the next cell it exists is this
  343. 14:04standalone cell here it doesn't have any
  344. 14:07neighbors so the consequence of this is
  345. 14:10is going to add four new edges to the
  346. 14:12edge pool a western edge an eastern edge
  347. 14:15a northern edge and a southern edge e F
  348. 14:20G and H I'll just set the edge IDs four
  349. 14:25five six and seven so the worst case
  350. 14:28scenario is a map made up of lots of
  351. 14:31individual blocks let's carry on testing
  352. 14:34but I'm not going to go in as much
  353. 14:35detail now the next block tested is this
  354. 14:37one that does exist well this one needs
  355. 14:39a new western edge which is I now that's
  356. 14:42where up to but it can borrow from its
  357. 14:44northern neighbor its eastern edge and
  358. 14:48I'll just carry the algorithm along now
  359. 14:49for the rest of the shape
  360. 15:15once we've gone through all of the tiles
  361. 15:16were interested in we'll have an edge
  362. 15:18pool that contains only the bounding
  363. 15:20edges of the shapes and the edges are
  364. 15:23defined by a start and an end coordinate
  365. 15:26it's very possible that edges will share
  366. 15:28a coordinate but needless to say we've
  367. 15:30converted our tile map into a set of
  368. 15:33edges not strictly a polygon because
  369. 15:36we've not defined what's the inside and
  370. 15:38the outside of the polygon but we can
  371. 15:40assume that things don't pass through
  372. 15:42edges in our application so I think our
  373. 15:45first step in code is to implement this
  374. 15:47algorithm now I've gone through it in
  375. 15:48quite some detail here I'm not going to
  376. 15:50go through this same detail in the code
  377. 15:52so we'll do some cut and pasting I will
  378. 15:54need some basic structures to assist us
  379. 15:56the first thing I'll need is an edge
  380. 15:57which stores the x and y components of
  381. 16:00its start and end points and my world is
  382. 16:03going to be made up of an array of cells
  383. 16:05so this is my cell and I've included in
  384. 16:08it boolean Flags where does the cell
  385. 16:09exist or not and does it have an edge
  386. 16:12that exists on all four sides and what
  387. 16:15is the edge ID into the pool of edges
  388. 16:18and just to make the algorithm a little
  389. 16:20clearer to follow I'm going to define
  390. 16:21some constants for north south east and
  391. 16:23west our world is going to be a 2d array
  392. 16:27of cells the world will be defined by
  393. 16:30two variables world width and will
  394. 16:32height in this case I've chosen 40 and
  395. 16:3430 because if I assume a block size of
  396. 16:3716 by 16 pixels that lines up very
  397. 16:40nicely with our 640 by 480 resolution so
  398. 16:43essentially the world is a single screen
  399. 16:45that we can see in on user create I'm
  400. 16:48going to allocate the memory that holds
  401. 16:49our world adding code to place blocks in
  402. 16:52the world is quite simple because we're
  403. 16:54using the pixel game engine first I'm
  404. 16:56going to define the width of the block
  405. 16:58just in case we do want to change things
  406. 17:00later on and I'm going to grab a quick
  407. 17:02snapshot of the mouse coordinates it's
  408. 17:04important to do this because the mouse
  409. 17:06coordinates can technically change
  410. 17:08whilst this function is executing so
  411. 17:10grabbing them right at the staff means
  412. 17:12that we're going to have consistency
  413. 17:14through the code to check if the user
  414. 17:16has pressed the left mouse button or
  415. 17:18clicked we can call the get Mouse
  416. 17:20function on item 0 that's the left mouse
  417. 17:22button and we're going to check to see
  418. 17:24has it been released this frame if it
  419. 17:26has been released we're going to get to
  420. 17:28the in debt
  421. 17:28of the cell that it has been clicked in
  422. 17:31now this looks horrendous but it's
  423. 17:33actually quite a simple bit of code we
  424. 17:34take our input mouse coordinates and we
  425. 17:37do an integer divide with the block
  426. 17:39width so this will tell us how many
  427. 17:41blocks down the screen is the mouse
  428. 17:43cursor we do the same thing for the
  429. 17:45x-axis we take the X mouse corners and
  430. 17:48we also divide that by block width and
  431. 17:49that tells us how far across the screen
  432. 17:51is the mouse cursor in tiles effectively
  433. 17:54and this is something we've seen many
  434. 17:56many times now I equals a y-coordinate
  435. 17:59times the width of the array plus X
  436. 18:02which converts our 2d X&Y coordinates
  437. 18:06into a 1d coordinate in the array once
  438. 18:09we know the index we can toggle the
  439. 18:10exist flag for the cell at that location
  440. 18:13staying in unusual update what I'm going
  441. 18:15to move it down a bit will do the
  442. 18:17drawing code and the first thing I want
  443. 18:19to do is clear the screen to all black
  444. 18:22and now I want to draw the blocks from
  445. 18:24the tile map and to do this I'm going to
  446. 18:26iterate through them all and simply
  447. 18:28check if they exist or not and if they
  448. 18:31do exist I'm going to draw a filled in
  449. 18:33rectangle at that location so this is
  450. 18:35code versing back from block space into
  451. 18:37screen space now with a width and height
  452. 18:39set to the block width and I'm going to
  453. 18:41draw the rectangle as blue let's take a
  454. 18:44look so here got the screen with the
  455. 18:51mouse cursor on and if I click wherever
  456. 18:53I click I place a blue block and if I
  457. 18:57select a blue block and click it it gets
  458. 18:59rid of it we're basically toggling does
  459. 19:01the block exist at that location or not
  460. 19:04now we can worry about turning the tile
  461. 19:06map into a pool of edges and the
  462. 19:09container I'm going to use for this is a
  463. 19:11vector called BEC edges and because I
  464. 19:14know that doing this type of conversion
  465. 19:17is going to be incredibly useful for
  466. 19:19future videos I'm going to create a
  467. 19:21function that explicitly does this it
  468. 19:23converts a binary tile map into what I'm
  469. 19:26calling the poly map ie it's going to
  470. 19:28populate this vector with all of the
  471. 19:30edge information given a particular
  472. 19:32region of a tile map so the parameters
  473. 19:35of this function are a starting x and
  474. 19:37y-coordinate the width and height of the
  475. 19:40rectangle
  476. 19:40of tiles of which I wish to inspect and
  477. 19:43turn into edges and also going to pass
  478. 19:45in the block width parameter so we know
  479. 19:47where our edges exist in actual space
  480. 19:49and then the cysts a strange parameter
  481. 19:51called pitch which is going to be set to
  482. 19:53end world width but I'm leaving it as
  483. 19:55pitch because we want to be able to use
  484. 19:57it on arbitrary size maps going forward
  485. 19:59so I'm using the phrase poly map it's a
  486. 20:02little bit made-up and the first thing I
  487. 20:03want this function to do is to clear all
  488. 20:06of the information associated with
  489. 20:09constructing this map naturally I want
  490. 20:11to just clear the vector of edges first
  491. 20:13but then during the construction of
  492. 20:16creating this vector we're populating
  493. 20:18the cells with additional information I
  494. 20:21need to reset that information
  495. 20:23effectively back to zero so I'm
  496. 20:25iterating through all of the cells in
  497. 20:26the region of interest that I'm
  498. 20:28converting and for each cell I'm setting
  499. 20:30that it has no edges at all and it's not
  500. 20:33got any edges in the pool again here we
  501. 20:35see Y times width plus X once we've
  502. 20:39cleared our map it's time to start
  503. 20:41applying our algorithm and I'll do this
  504. 20:43by creating two for loops x and y and
  505. 20:46iterating through the region of interest
  506. 20:48now you'll notice here I'm actually
  507. 20:50iterating from one - with - one of the
  508. 20:53region of interest and that's simply
  509. 20:55because I don't want to have any
  510. 20:56out-of-bounds memory errors when I'm
  511. 20:58checking along the edges of the 2d array
  512. 21:01I can get away with that for this
  513. 21:03demonstration but going forward I'll
  514. 21:05probably want to tighten that up a
  515. 21:06little bit to accommodate genuine array
  516. 21:09boundaries now to stop things getting
  517. 21:11out of control I'm going to create some
  518. 21:13indices which are more convenient to use
  519. 21:16so the eye index will be the current
  520. 21:19cell index in the array we can look at
  521. 21:22our northern neighbor is the current
  522. 21:23cell but up one in the Y direction and
  523. 21:26our Eastern neighbor is the current self
  524. 21:28plus one in the X direction first let's
  525. 21:31check if the cell exists or not if it
  526. 21:34does exist we'll check to see does it
  527. 21:36have a Western neighbor because if it
  528. 21:38doesn't have a western neighbor then it
  529. 21:39needs a western edge one of the places
  530. 21:42we can get a western edge from is our
  531. 21:44northern neighbor by extending it
  532. 21:45downwards so let's see if our northern
  533. 21:47neighbor does that have a western edge
  534. 21:50if it does we'll extend it and we can
  535. 21:53extend it by
  536. 21:54looking at our northern neighbors
  537. 21:56western edge ID and using that ID to
  538. 22:00index into our edge pool so we can find
  539. 22:02which edge is on the western side of our
  540. 22:05northern neighbor and we know that we're
  541. 22:07going to grow downwards so we want to
  542. 22:09extend the end coordinate of that edge
  543. 22:12by one block width down which is what
  544. 22:15this line does so in our pool of edges
  545. 22:18we're looking at our northern neighbors
  546. 22:21western edge ID to get to the edge that
  547. 22:24is relevant to us we're taking the end
  548. 22:27y-coordinate because we're going to
  549. 22:28extend in the y-axis downwards and we're
  550. 22:30increasing that coordinate by the height
  551. 22:32of one of our blocks just so happens
  552. 22:34that our blocks are square
  553. 22:35since we're borrowing an edge that
  554. 22:37already exists we'll set the edge ID of
  555. 22:40our current cells western edge to be the
  556. 22:43same as our northern neighbors western
  557. 22:45edge and since the edge exists we'll set
  558. 22:48that to true now if I didn't have a
  559. 22:50northern neighbor I still need an edge
  560. 22:52from somewhere so I'll need to create
  561. 22:54one who creates a new edge object and we
  562. 22:56can use the position we are in the array
  563. 22:58and the block width variable to work out
  564. 23:01where we are in real space so we can set
  565. 23:03the start coordinate of our edge X&Y
  566. 23:06here using the array coordinates
  567. 23:08multiplied by the block width and we
  568. 23:11know at the moment that our edge is
  569. 23:13going to be at least one block tall so
  570. 23:15we can specify that two will add to the
  571. 23:17edge to our edge pool simply by pushing
  572. 23:20it into the vector and since we've
  573. 23:21created a new edge as we did up here we
  574. 23:24need to now set the ID and whether the
  575. 23:26edge exists or not for this cell well
  576. 23:29it's a new edge ID and we've used the
  577. 23:31location of it in the vector to specify
  578. 23:33that ID and so that is all of the code
  579. 23:35required to work out whether we should
  580. 23:37create a new western edge or borrow from
  581. 23:40our northern neighbors we now need very
  582. 23:42similar code for the other four edges
  583. 23:44and I'm not going to talk through it all
  584. 23:46I'm going to cut and paste it in but
  585. 23:48it's very very similar we just need to
  586. 23:50be careful of what we're doing when
  587. 23:51we're constructing the new edge so even
  588. 23:54though I appreciate that seems like a
  589. 23:55really large amount of code to put it in
  590. 23:57one go I hope you can appreciate that
  591. 23:59it's actually very similar to the one
  592. 24:01that we've just done you can probably
  593. 24:03tell that I'm going a little bit faster
  594. 24:05than usual on this video
  595. 24:07because there are two parts of the
  596. 24:09algorithm that I want to include but I
  597. 24:10don't want the video to become an hour
  598. 24:12long now we can go back to our own user
  599. 24:14update function and actually call our
  600. 24:17new function to create the poly map and
  601. 24:19here I am calling it and I'm giving it
  602. 24:21the region of interest that I wish for
  603. 24:23it to convert which in this case is the
  604. 24:25whole array and I specify the block
  605. 24:27width coordinate so the edges can be
  606. 24:29done to the right scale and I specify
  607. 24:31the pitch to be the width of the world
  608. 24:33which is actually the same as the value
  609. 24:35I'm using here because coincidentally
  610. 24:37the whole world is represented by the
  611. 24:38single screen I can see I think it'll be
  612. 24:41quite nice to visualize the edges to
  613. 24:43help us with some debugging and to make
  614. 24:44sure that everything is working
  615. 24:45accordingly so after we've drawn the
  616. 24:48blocks and I'm going to create a little
  617. 24:49Auto for loop to iterate through all of
  618. 24:52the edges in our vector of edges and
  619. 24:54I'll use the draw line function to draw
  620. 24:57a white line from the starting
  621. 24:58coordinates the ending coordinate of the
  622. 25:00edge but to make the ends of the edge a
  623. 25:03little bit more visible I'm going to
  624. 25:04draw a red filled in circle at each end
  625. 25:07most of the center of the circle this is
  626. 25:09the radius and this is the color let's
  627. 25:11take a look so is my blank screen and
  628. 25:17I'm going to place a block very nicely
  629. 25:19we can see we've got at least four edges
  630. 25:21there if I place a block next to it we
  631. 25:25can see it's not drawing any more ends
  632. 25:26of edges it's just drawing the edge so
  633. 25:28it does look like in that direction the
  634. 25:30algorithm is working just fine
  635. 25:32if we branch off from here we can see
  636. 25:37that seems okay too
  637. 25:38let's go off the top very nice
  638. 25:46if I've inadvertently drawn some
  639. 25:49offensive simple here I do apologize but
  640. 25:51I'm just randomly clicking where I need
  641. 25:54to click I think what's probably also
  642. 25:57worth Jacky is solid object so it
  643. 25:59doesn't necessarily have to be a wall
  644. 26:01one tile thick it does seem to work for
  645. 26:06that to that we can punch a hole in the
  646. 26:10middle of this solid so I'm reasonably
  647. 26:12confident that we have taken the tile
  648. 26:14map and we have reduced it into some
  649. 26:16geometric primitives which might be more
  650. 26:18useful for doing more complicated
  651. 26:20geometric maths now that's very nice and
  652. 26:23the performance seems quite reasonable
  653. 26:24too especially given I'm doing this
  654. 26:27every single frame that I'm rendering
  655. 26:29and in most cases you probably wouldn't
  656. 26:31need to do that you'd only need to
  657. 26:32regenerate the poly map if the tile map
  658. 26:35changes and in some situations it could
  659. 26:37be a completely offline precompilation
  660. 26:39step of how your level data is stored so
  661. 26:42all in all I think this is a very useful
  662. 26:44routine to have right so let's start
  663. 26:47thinking about the shadow casting and
  664. 26:48the line of sight bit here I've got a
  665. 26:51location of the source of the light or
  666. 26:54where the player stands or whatever it
  667. 26:55is we're going to project rays from this
  668. 26:58source radially out into the scene now
  669. 27:01I've covered casting rays before in fact
  670. 27:03in one of my very first videos the
  671. 27:05first-person shooter the command line
  672. 27:06engine we can cast rays in all angular
  673. 27:09directions and see what they hit so here
  674. 27:13we go cast out some rays not very
  675. 27:18straight razor might have but when they
  676. 27:20intersect with something in the scene
  677. 27:22the Ray length is shorter and anything
  678. 27:25that followed that ray length past the
  679. 27:27intersection point is technically in
  680. 27:30shadow and finally this one is also in
  681. 27:33shadow and I'm sure you can envisage
  682. 27:35your scenario where we keep doing this
  683. 27:37all the way around the scene but this is
  684. 27:40really computationally inefficient and
  685. 27:43in fact we can look at this and derive
  686. 27:46from it that we're only interested in
  687. 27:48rays that intersect with our line
  688. 27:50segments even more so we're only
  689. 27:52interested in rays that seemed to
  690. 27:54intersect with the cornice so let's
  691. 27:56explore that for a moment for each edge
  692. 27:58in
  693. 27:58the edge cool I'm going to project array
  694. 28:01to the start and end point so in this
  695. 28:04case I've effectively got four rays when
  696. 28:07the Ray intersects with the line we know
  697. 28:10the Ray effectively starts turning into
  698. 28:12shadow well let's consider not looking
  699. 28:15at areas which are in shadow but instead
  700. 28:17which areas are in light the second ray
  701. 28:20down was defined by this coordinate but
  702. 28:23we can see that it had to intersect with
  703. 28:25another edge on its way there and so
  704. 28:28we'll record the intersection point that
  705. 28:31is closest to the source of the array
  706. 28:33and we'll do that for all four rays cast
  707. 28:36out and in fact let's do the same for
  708. 28:39all rays for the other object too so we
  709. 28:41cast rays to each of the vertices
  710. 28:43defined by the edges of the other object
  711. 28:52drawing the intersection points that are
  712. 28:55closest along that Ray if I add in one
  713. 28:59more point which is the source what I'm
  714. 29:03actually constructed is a fan of
  715. 29:05triangles there's one triangle next
  716. 29:10triangle and these triangles construct a
  717. 29:13polygon which covers the area that is
  718. 29:16definitely visible from the source the
  719. 29:20problem is it's a bit of a strange
  720. 29:21polygon because if I shaded in this
  721. 29:24polygon with light we can see there's
  722. 29:32some big gaps which are definitely
  723. 29:35visible but they've been ignored by the
  724. 29:37algorithm what can we do about these big
  725. 29:40gaps then well there's a little hack
  726. 29:42that we can apply instead of casting one
  727. 29:45ray from the source to a vertex we cast
  728. 29:49three rays one goes directly to it and
  729. 29:52we have one either side displaced by a
  730. 29:56tiny fraction an angle so it'll miss the
  731. 29:58vertex in this case on that side and on
  732. 30:02this side well it'll just hit the line
  733. 30:04as it normally does so three rays one is
  734. 30:08it theta and one is it theta plus or
  735. 30:11minus some
  736. 30:12tiny little amount these additional ways
  737. 30:15will affect our image here in the
  738. 30:17following way so we'll have one of the
  739. 30:20rays will continue going until it hits
  740. 30:22something else or it hits the boundary
  741. 30:24of the world as will this one and this
  742. 30:27one and this one our intersection points
  743. 30:30are also recorded and now when we draw
  744. 30:33the triangle fan formed by these points
  745. 30:36there's a lot more area to fill in in
  746. 30:40fact it turns out that it's all of the
  747. 30:42visible area from the source
  748. 30:52and this is why these two algorithms are
  749. 30:54interchangeable because the lit up area
  750. 30:58is the line of sight it's all the areas
  751. 30:59that can be seen and the areas that
  752. 31:02can't be seen must be in shadow so we
  753. 31:04have cast shadows I'll start researching
  754. 31:06into this topic I found two brilliant
  755. 31:09websites you must go and have a look at
  756. 31:11the make outline these algorithms in
  757. 31:13quite some detail have lots of
  758. 31:14interactive demos you can play with too
  759. 31:16I will of course link to these websites
  760. 31:18in the text below the first one allows
  761. 31:20you to place a light source which like
  762. 31:22the demo that we're building and you can
  763. 31:24see it has a degree of complexity to it
  764. 31:26but it's quite nice because it talks to
  765. 31:28all of the different stages that you
  766. 31:29might go through when developing this
  767. 31:31algorithm this is the second one and
  768. 31:33again a really nice site full of nice
  769. 31:35toys to play with and some code too and
  770. 31:39you can see again you can interact just
  771. 31:42as we're doing with edges and shapes and
  772. 31:43seeing where the Rays get cast in fact
  773. 31:45it was this website that gave me the
  774. 31:47idea of casting out additional rays for
  775. 31:50the corners please take the time to
  776. 31:52check out these sites I think it's a
  777. 31:54wonderful thing that experienced
  778. 31:55developers are taking the time and
  779. 31:57putting the effort into actually doing
  780. 31:58tutorials like this with interactive
  781. 32:00elements it's a lot of work and yet
  782. 32:02yield so much so check them out the
  783. 32:04links are in the description below so
  784. 32:06going back to our application we know
  785. 32:08that for each edge end we're going to
  786. 32:10send out three rays and we're going to
  787. 32:12form some triangles from the
  788. 32:13intersection points of those rays with
  789. 32:15the edges I'm going to start by creating
  790. 32:19another function to encapsulate the
  791. 32:21calculation of our visibility polygon
  792. 32:23there's going to be a set of points that
  793. 32:25represents the visible space from a
  794. 32:28source location defined by Oh X and Oh Y
  795. 32:30over a given radius but I'm going to use
  796. 32:33a little bit of modern C to help me out
  797. 32:35here I'm going to store the points of
  798. 32:38this polygon as a vector of tuples of
  799. 32:41floats and you'll notice there are three
  800. 32:44floats the first float is going to
  801. 32:47represent the angle from the source of
  802. 32:50the vertex that were targeting with the
  803. 32:52Ray and the second two floats are the
  804. 32:55X&Y location of that vertex we need to
  805. 32:59store the angle because without it we
  806. 33:00won't be able to draw sensible triangles
  807. 33:03in the fan later on since we're going to
  808. 33:05iterate through
  809. 33:06our vector of edges to work out the
  810. 33:08coordinates of to where we should cast
  811. 33:10rays this could theoretically happen in
  812. 33:13any sort of order which means when I
  813. 33:15come to draw these triangles later on I
  814. 33:17can't do it sensibly I effectively need
  815. 33:20to draw these points in some rotational
  816. 33:23order and so the easiest way to do that
  817. 33:25is not just to store the point on its
  818. 33:27own but take an arbitrary axis from our
  819. 33:31source point and record the theta values
  820. 33:34of each of the points so for each point
  821. 33:40I'll have a theta and an x and a y I can
  822. 33:43then sort these points based on the
  823. 33:46theta value to make sure that they're in
  824. 33:48this clockwise order if you've not seen
  825. 33:51a tuple before it's just a simple way to
  826. 33:53group things together it's a bit like a
  827. 33:56struct but it's very compatible with the
  828. 33:59standard library so as we did before
  829. 34:00with the edges the first thing I want to
  830. 34:02do with my visibility polygon is to
  831. 34:04clear it and then we know that we need
  832. 34:06to do something for each edge in our
  833. 34:08vector of edges so little also for loop
  834. 34:11to iterate through all of the edges now
  835. 34:13an edge consists of two points the start
  836. 34:16and the end so for each edge we're going
  837. 34:18to have to cast at least two Ray's
  838. 34:20directly to hit the start and end points
  839. 34:22since I need the angle of the Ray I'm
  840. 34:25going to start by first calculating the
  841. 34:26gradient of the Ray they're depending on
  842. 34:28whether we're at the start or the end so
  843. 34:30I've used the ternary operator here to
  844. 34:32select between the start point or the
  845. 34:34end point and I'm subtracting from that
  846. 34:35the source location
  847. 34:37Oh XR know why giving me two variables
  848. 34:39are DX and our dy which represents the
  849. 34:41Ray vector now I'm not bothered what our
  850. 34:44angle is relative to as long as it's
  851. 34:46consistent so I'm just going to create
  852. 34:48another variable base angle and use the
  853. 34:50atan2 function using the RDX in our dy
  854. 34:54values so this gives us an angle of our
  855. 34:57ray vector and we know that for each ray
  856. 34:59actually we're going to cast three
  857. 35:01additional Ray's with a slight deviation
  858. 35:03between the two so I'll create a
  859. 35:05temporary variable called angle which is
  860. 35:07going to be the angle we will shoot the
  861. 35:08ray at so I'll add another for loop to
  862. 35:11generate the three additional race we're
  863. 35:13going to need and depending on the value
  864. 35:15J in the for loop we're going to choose
  865. 35:17an appropriate angle to
  866. 35:19the Rae out - so in effect we've gone
  867. 35:22back on ourselves a little bit here
  868. 35:23we've calculated the gradient of the Ray
  869. 35:25so we can work out what the angle is and
  870. 35:27then we're using the angle plus or minus
  871. 35:30a little bit of deviance to generate the
  872. 35:33vector of the Ray again which we can
  873. 35:35easily do taking the cosine and sine of
  874. 35:37the angle will only cast the Ray as long
  875. 35:39as we've specified the radius value to
  876. 35:42be as you can probably see the number of
  877. 35:43rays were actually projecting into the
  878. 35:45scene is growing and the more
  879. 35:47complicated our scene is with tiles the
  880. 35:49more graves were going to cast out
  881. 35:51however the one thing we're guaranteed
  882. 35:53is that our strategy is more optimal
  883. 35:54than casting rays out in all directions
  884. 35:56and seeing what happens at least our
  885. 35:58rays are guided intelligently to at
  886. 36:01least some features within the world and
  887. 36:03this is where the really chewy part of
  888. 36:05the algorithm comes because for every
  889. 36:07single ray we now need to check for
  890. 36:09intersection between the Ray and all of
  891. 36:12the edges in the scene the algorithm has
  892. 36:14now come down to a simple line segment
  893. 36:16intersection test so we can take two
  894. 36:18line segments and we want to work out
  895. 36:20the point at which they intersect and
  896. 36:22one way to think about this is to take a
  897. 36:25known point on each line represent the
  898. 36:28line segment as a vector therefore the
  899. 36:31opposing point becomes the existing
  900. 36:33point Plus that vector and we can add in
  901. 36:36here a parameter T 1 and T 2 because if
  902. 36:42T 1 is equal to 1 where at this end of
  903. 36:45the line and if T 1 is equal to 0 where
  904. 36:48at this end of the line and so we'll
  905. 36:50find where our intersection point is by
  906. 36:52looking at the T values and since we're
  907. 36:55talking about line segment intersections
  908. 36:57go and find the line segment
  909. 36:58intersection algorithm of your choice I
  910. 37:01personally like this one which I
  911. 37:03discovered on Stack Overflow the answer
  912. 37:05here is actually a code implementation
  913. 37:06of the answer above I'll put a link in
  914. 37:10the description below before we can
  915. 37:12calculate the intersection we need some
  916. 37:13additional information the first of
  917. 37:15which is we need the vector that
  918. 37:16represents the edge which is easy to
  919. 37:18calculate because it's just one end of
  920. 37:20the edge subtracted from the other we'll
  921. 37:22also need to make sure that the Ray
  922. 37:24isn't going to be collinear with the
  923. 37:26edge ie it's not going to lie on top of
  924. 37:28it so I'll check for that by just
  925. 37:29looking at the values of the vector
  926. 37:32component
  927. 37:33and making sure that they're
  928. 37:34sufficiently different for example if
  929. 37:36both had a dy of zero then both the Ray
  930. 37:39and the edge could be horizontal there's
  931. 37:41a chance they could overlap and there
  932. 37:42isn't a single solution point that
  933. 37:44represents the point of intersection now
  934. 37:46we can calculate the t1 and t2 values
  935. 37:49using the line segment intersection
  936. 37:51point algorithm and in this instance t1
  937. 37:54is the distance along the Ray and t2 is
  938. 37:57the distance along the edge and so if t1
  939. 37:59is positive that means we're traveling
  940. 38:01along the Ray in the correct direction
  941. 38:02and if t2 lies between 0 & 1
  942. 38:05then we've definitely hit a line segment
  943. 38:08now that we know we've got an
  944. 38:09intersection point what do we do well
  945. 38:12we're only interested in the
  946. 38:13intersection point which is closest to
  947. 38:15the source point even though we've
  948. 38:18detected intersections along many edges
  949. 38:20we want to reject most of them and only
  950. 38:22retain the one that is closest to the
  951. 38:24source and our t1 value represents that
  952. 38:27information we don't need to do
  953. 38:28Pythagoras theorem here to calculate the
  954. 38:30distance because t1 indicates the length
  955. 38:32along the Ray and so for each Ray that I
  956. 38:35cast I'm going to store some temporary
  957. 38:37variables that represents the minimum t1
  958. 38:39distance and I'm also going to store the
  959. 38:41X&Y of the intersection point at that
  960. 38:44distance I'll set it to infinity for
  961. 38:46each way to start but if the t1 that
  962. 38:48represents the intersection point of the
  963. 38:50Ray of just tested is less than our
  964. 38:52current closest intersection point I'm
  965. 38:55going to replace it and I'm also going
  966. 38:57to calculate the new intersection point
  967. 38:59and angle of course now for all of my
  968. 39:02rays
  969. 39:03once I've calculated where the Ray ends
  970. 39:05I'm going to add that location to my
  971. 39:07vector of visible polygon points our
  972. 39:10calculate visibility polygon has not
  973. 39:12quite finished yet because right now
  974. 39:14we've got a vector full of points in any
  975. 39:17particular order and so I need to sort
  976. 39:19them based upon the angle fortunately
  977. 39:22the standard library can provide a nice
  978. 39:23simple routine to do this for us it
  979. 39:25provides a sort as part of algorithm and
  980. 39:28so I'll call the sort routine giving it
  981. 39:31the start location of my vector and the
  982. 39:33end location of the vector but I'll pass
  983. 39:35in as the sorting criteria a small
  984. 39:38lambda function and it's going to sort
  985. 39:39based upon the angle so the two
  986. 39:41arguments going into the lambda function
  987. 39:43are references to the two tuples and
  988. 39:45we're only interested in
  989. 39:47the first element of the topple which is
  990. 39:49the angle so we're going to return true
  991. 39:51if the angle of the topple on the left
  992. 39:53is less than the angle of the topple on
  993. 39:54the right and this will sort it in
  994. 39:56ascending order every frame I wanted to
  995. 39:58convert our tile map to a poly map but I
  996. 40:00don't want it to calculate the
  997. 40:02visibility polygon every single frame
  998. 40:04I'm only going to do that when I'm
  999. 40:05holding down the right mouse button I
  1000. 40:07also only want to draw anything relating
  1001. 40:10to visibility under the same conditions
  1002. 40:12so we were to check that the mouse
  1003. 40:13button is held but I'm also going to
  1004. 40:15check that the vector of points that
  1005. 40:16we're going to draw at least has
  1006. 40:18something in it and so now I want to
  1007. 40:20draw my triangle fan this is quite easy
  1008. 40:23now we've got a vector of sorted
  1009. 40:25visibility points I'm going to go from
  1010. 40:27the start of the vector to almost the
  1011. 40:30end of it and I'll use the fill triangle
  1012. 40:32routine to draw a triangle from the
  1013. 40:34origin point the source where the mouse
  1014. 40:36is to the X&Y points of two successive
  1015. 40:40visible points in our polygon vector so
  1016. 40:42this will form a triangle what I mustn't
  1017. 40:45forget to do is also close the triangle
  1018. 40:47at the end so we want to draw from the
  1019. 40:50final point to the starting point this
  1020. 40:53will have to sit outside the loop let's
  1021. 40:55take a look well let's try with nothing
  1022. 40:59in I'm right clicking I don't see
  1023. 41:00anything let's add some obstacles oh
  1024. 41:04well it looks like a complete or not a
  1025. 41:07mess well it's a bitter kind of working
  1026. 41:10but something's not right and the
  1027. 41:15problem here is work with information
  1028. 41:17starved were only casting out Ray's
  1029. 41:19wherever we've got information in the
  1030. 41:21scene so if I put in lots and lots and
  1031. 41:24lots of points for it to reference from
  1032. 41:27it starts to look a bit more sensible
  1033. 41:31there's still some horrible glitches
  1034. 41:34though particularly what is this
  1035. 41:36triangle going up towards zero in the
  1036. 41:39top left
  1037. 41:40well this spurious triangle is because
  1038. 41:42we've assumed that all of our rays will
  1039. 41:45effectively intersect with something and
  1040. 41:47this might not necessarily be the case
  1041. 41:50particularly when we have raised which
  1042. 41:52we've adjusted by a slight angle so we
  1043. 41:54can limit the Rays that are added to the
  1044. 41:56vector of polygon points by assuming
  1045. 41:58they're only valid if they have in fact
  1046. 42:00hit something so lonely at them if this
  1047. 42:03be valid variable is set to true and
  1048. 42:05that's only set to true if we have had
  1049. 42:07some sort of collision along the length
  1050. 42:09of the Ray so let's take a look again at
  1051. 42:14some points in and I'll draw at least
  1052. 42:17now we've not got a problem going up
  1053. 42:18towards zero but things are still
  1054. 42:20looking a bit information starved in
  1055. 42:23fact if I now put in lots and lots of
  1056. 42:25points as a boundary
  1057. 42:26I see how it looks then and now it's
  1058. 42:31starting to look a lot more realistic so
  1059. 42:34what this is telling me is that our rays
  1060. 42:36that don't intersect with anything need
  1061. 42:38to intersect with something we're going
  1062. 42:39to have to put in an artificial boundary
  1063. 42:41now I could do that in our ray code what
  1064. 42:44I'm going to do instead is just hard
  1065. 42:46code into some boundaries into our tile
  1066. 42:49map array so in on user create where
  1067. 42:52I've just created the world I'm going to
  1068. 42:54add in a couple of for loops which draw
  1069. 42:56some parallel horizontal and vertical
  1070. 42:58cells into the world let's take a look
  1071. 43:02now we can see the boundary has been put
  1072. 43:07in I'm now illuminating everything is
  1073. 43:10lit up put in some obstacles and very
  1074. 43:14nice our Rays have somewhere to stop now
  1075. 43:17and that was what was necessary in order
  1076. 43:18to make this look smooth and work if we
  1077. 43:24just quickly change our fill triangles
  1078. 43:27to draw triangles now we can see the
  1079. 43:30individual polygons making up the
  1080. 43:32polygon fan around the point from which
  1081. 43:34we're checking for visibility I'm
  1082. 43:38curious about the number of rays being
  1083. 43:40cast so I'm going to capture that
  1084. 43:41information and display
  1085. 43:43I'll just quickly use the draw string
  1086. 43:45function to display this information to
  1087. 43:47the user so no rays being cast and right
  1088. 43:54now a hundred and sixty eight Ray's
  1089. 43:55being cast four am fully minimal number
  1090. 43:58of objects that we take out a bunch and
  1091. 44:02now cast 72 rays even though we've got 1
  1092. 44:052 3 4 5 6 7 8 9 10 11 12 known vertices
  1093. 44:09in the scene this is because we've got
  1094. 44:12duplication of vs.
  1095. 44:14fortunately the standard library can
  1096. 44:16help us out here - since our vector of
  1097. 44:19visibility polygons points is sorted we
  1098. 44:22can use the unique operator to remove
  1099. 44:26any duplicates why do we need to draw to
  1100. 44:29the same point multiple times and so
  1101. 44:31we'll do a before and after comparison
  1102. 44:34here is the unique function being called
  1103. 44:37again it's part of algorithm and I've
  1104. 44:39passed in a lambda function again which
  1105. 44:41compares the tuples but this time it's
  1106. 44:44saying please reject this particular
  1107. 44:46topple if the x and y coordinates are
  1108. 44:49similar in this case I've said less than
  1109. 44:51naught point 1 so I'll create a second
  1110. 44:54variable ray cast 2 and compare them so
  1111. 44:59just a little recap we've used the
  1112. 45:01unique function to remove all of the
  1113. 45:03duplicates in our sorted vector it
  1114. 45:06doesn't actually remove anything the
  1115. 45:08unique function it just reorganizes the
  1116. 45:10vector so that all of the unique stuff
  1117. 45:11is at the start so anything after the
  1118. 45:14point returned by the unique function we
  1119. 45:16want to remove and therefore I'm
  1120. 45:18resizing the vector based upon the
  1121. 45:20distance from the start of the vector to
  1122. 45:22that unique point I'll now draw how many
  1123. 45:25rays were actually cast versus how many
  1124. 45:27rays were actually drawing on the screen
  1125. 45:29let's take a look so in a blank
  1126. 45:35environment we're casting 48 rays for
  1127. 45:38the number of rays that were drawing is
  1128. 45:39changing between well 10 and 13 a little
  1129. 45:43bit of fluctuation and that's because it
  1130. 45:44depends if we look at the top right
  1131. 45:46corner here we can see a bunch of rays
  1132. 45:48but at certain angles the Rays overlap
  1133. 45:51each other there's absolutely no point
  1134. 45:53in drawing triangles that we can't see
  1135. 45:55so let's add in a few obstacles and of
  1136. 45:58course we'll expect the number of rays
  1137. 45:59to increase or casting 192 rays but now
  1138. 46:02we're only drawing about 50 rays I think
  1139. 46:05this is quite a significant optimization
  1140. 46:07to make lots of features 840 rays cast
  1141. 46:11and we're drawing well about 25% of them
  1142. 46:16I'm going to fill in the triangles again
  1143. 46:17now and so we can see we've got quite a
  1144. 46:20robust line of sight or shadow casting
  1145. 46:22algorithm implement it now at the start
  1146. 46:25of this video I made it look a little
  1147. 46:26bit
  1148. 46:27more special because I used a sprite of
  1149. 46:29a light sauce and modulated where it is
  1150. 46:32white here with that sprite so this is a
  1151. 46:35bit of pixel game engine trickery now in
  1152. 46:38Photoshop I have created a sprite that
  1153. 46:40represents a light source emanating out
  1154. 46:43from a central point it's a PNG file I'm
  1155. 46:46going to load the sprite into the pixel
  1156. 46:48game engine and I'll do that in on user
  1157. 46:50create I'm now going to do something a
  1158. 46:52little bit novel for this channel I'm
  1159. 46:54going to do some off-screen rendering
  1160. 46:56I'm going to create two additional
  1161. 46:57sprites called buffer light ray and
  1162. 47:01buffer light Tex and these are going to
  1163. 47:03be surfaces to which I'm going to do
  1164. 47:05some post-processing but I need to
  1165. 47:07create these in on user create you'll
  1166. 47:09see I'm not loading a file I'm just
  1167. 47:11giving them a dimension they're
  1168. 47:12basically going to be an image buffer to
  1169. 47:14which I'll draw things and then modify
  1170. 47:16the pixel game engine supports rendering
  1171. 47:19to different targets by default it
  1172. 47:21renders to what you can see on the
  1173. 47:23screen and we can specify the draw
  1174. 47:25target by calling the set draw target
  1175. 47:27function and by specifying null pointer
  1176. 47:30is the argument that means there isn't
  1177. 47:31any off-screen draw targets draw to the
  1178. 47:34main target which is the screen so we
  1179. 47:36want to do that before we clear
  1180. 47:37everything to black and any subsequent
  1181. 47:40drawing calls so in this case drawing
  1182. 47:42the string those calls will be
  1183. 47:44redirected to whatever the draw target
  1184. 47:46is I'm going to use the off-screen
  1185. 47:48buffers to implement a nice lighting
  1186. 47:50effect the first thing I want to do is
  1187. 47:53draw my radial light sprite into the
  1188. 47:56correct location in the buffer and so
  1189. 47:58I've specified the target buffer I've
  1190. 48:00erased it to black and now I'm going to
  1191. 48:03draw the sprite into the correct
  1192. 48:05location which happens to be my mouse
  1193. 48:06cursor now the center of my sprite
  1194. 48:08because the sprite is 512 by 512 needs
  1195. 48:11to be offset accordingly I'm then going
  1196. 48:13to render to the second off-screen
  1197. 48:15buffer the light rays so I'll clear the
  1198. 48:18buffer to blank first and all of the
  1199. 48:20fill triangle routines will now be
  1200. 48:22targeted at that buffer I want to
  1201. 48:24combine these two buffers to draw to the
  1202. 48:27screen and because we're drawing to the
  1203. 48:29screen now I set the draw target to null
  1204. 48:31and I don't have any additive or special
  1205. 48:33blending mode yet in the pixel game
  1206. 48:35engine so I'm going to iterate through
  1207. 48:37each pixel in my off-screen buffer and
  1208. 48:40I'm going to check
  1209. 48:41if each pixel has been set remember we
  1210. 48:43cleared it all to blank to begin with so
  1211. 48:45if it's been set by one of the light
  1212. 48:47rays it'll no longer be blank and if
  1213. 48:50that's the case I want to draw in the
  1214. 48:53current screen location X&Y the pixel of
  1215. 48:57my light cast sprite in the
  1216. 48:59corresponding location let's take a look
  1217. 49:02so I can see my sprite has now been
  1218. 49:04drawn wherever my mouse cursor is put in
  1219. 49:09some blocks and the light looks a little
  1220. 49:13bit more realistic well notice that the
  1221. 49:15performance has taken a little bit of a
  1222. 49:17hit here we've gone down to about 18
  1223. 49:19frames per second and this is something
  1224. 49:21to do with how the pixel game engine
  1225. 49:23validates all of the pixels in debug
  1226. 49:26mode if we now change from debug mode to
  1227. 49:29release mode and run the same code we
  1228. 49:34can see the frame rate is a far more
  1229. 49:37acceptable a hundred 200 frames per
  1230. 49:39second on my machine let's put in some
  1231. 49:41obstacles very nice
  1232. 49:46the use of off-screen buffers is a
  1233. 49:49little bit eglee into the things we've
  1234. 49:50been doing on this channel but it's a
  1235. 49:52very common practice in all manner of
  1236. 49:54computer graphics and the fact that we
  1237. 49:56now work with pixels instead of
  1238. 49:57characters allows us to do some higher
  1239. 49:59fidelity effects and I quite like it if
  1240. 50:02you've enjoyed this video firstly please
  1241. 50:04check out some of the links to the
  1242. 50:05resources I've used in this video
  1243. 50:07they're in the description below the
  1244. 50:09source code for this algorithm and the
  1245. 50:11pixel game engine is also linked and
  1246. 50:12that's available on the github come and
  1247. 50:14have a chat on the disk guard give you a
  1248. 50:16big thumbs up and don't have a think
  1249. 50:18about subscribing I'll see you next time
  1250. 50:19take care

About this transcript

This page contains the full transcript of Line Of Sight or Shadow Casting in 2D by javidx9, generated from the public captions YouTube serves with the video. The transcript has 9,250 words across 1,250 segments, with the original timestamps preserved so you can click any line to jump to that moment in the embedded player.

What you can do with it

Use the transcript to take notes, quote the speaker, build a study guide, generate a summary with ChatGPT or Claude via the YouTube Summary tool, or export it as a timed subtitle file with YouTube to SRT. You can also re-open it in the transcriber to translate the transcript into 100+ languages.

Free YouTube transcript tool

YouTube2Text is a free YouTube transcript generator — no signup, no daily limit. Paste any YouTube link and get the full transcript instantly, with timestamps, click-to-jump, translation to 100+ languages, AI prompts for ChatGPT, Claude, and Gemini, and exports to TXT, SRT, VTT, or Markdown.