Placeholder Image

Subtitles section Play video

  • - Hello, and welcome to a coding challenge:

  • 2D raycasting.

  • (dinging)

  • So, what you're about to watch

  • is an edited version of what I did

  • on last week's live stream.

  • This is the result of the example that I made,

  • but before I get started with the coding,

  • let me talk to you a little bit

  • about where I got this idea.

  • So, a lot of people shared with me

  • a recent YouTube video from Sebastian Lague

  • about the 2D raymarhing algorithm.

  • This is part of his coding adventure series.

  • The video's really wonderful,

  • and goes through the basic 2D algorithm

  • and gets into also some crazy 3D rendering stuff.

  • So, I want to explore that algorithm in a later video,

  • but I wanted to start with something simpler,

  • and I looked back and Brenda Dowd, back in 2016,

  • posted a suggestion for me to do 2D raycasting.

  • So, about 3 years later, I'm getting to it.

  • So, I've been looking for inspiration

  • and I found inspiration

  • in one of my favorite internet artists,

  • indie game creator, Nicky Case.

  • So, Nicky Case made a game called Nothing To Hide,

  • and also an accompanying explorable explantation

  • that describes the process of creating

  • this really beautiful 2D shadow effect in the game.

  • So, I encourage you to check out Nicky Case's work,

  • all of their explorable explanations,

  • but in particular, down the middle of this page

  • is an example of exactly, basically, what I tried to make

  • in the coding challenge.

  • There's also, from Red Blob Games, Amit Patel.

  • A wonderful interactive essay,

  • explorable explanation, again, if you will

  • of a 2D visibility, and there's a source code

  • and a variety of different languages

  • and a whole bunch of interactive demos.

  • So, this is what I wanted to make.

  • I kind of got there.

  • There's so much further it could be taken

  • by you, the viewer, and I hope you make

  • your own creative version of this

  • and share it with me at TheCodingTrain.com

  • So, I hope you enjoy this coding challenge.

  • (train whistling)

  • Let's get started with, actually, the coding of this.

  • In JavaScript, using the p5.js library.

  • So, let me map out what I think I need, first.

  • So, what I want to have,

  • is I want to have two classes

  • where I can make two kinds of objects in my world.

  • I want to have a boundary.

  • This idea of a boundary,

  • and the boundary really just is line segments

  • between two points, A and B, or start and end,

  • however I want to consider that.

  • Then, I also want to have an idea of a ray,

  • and a ray has a position, and then maybe a direction.

  • So, presumably, if this was the ray

  • at this position, with this direction,

  • I need a function to tell me,

  • where does this ray hit the boundary.

  • So, two questions:

  • One, does it hit the boundary?

  • Yes

  • or no.

  • If it does hit the boundary,

  • give me that point.

  • If no, then just say no.

  • So, this is what I need to do.

  • If I can create this basic ray class, a boundary class,

  • I can have every ray find its point on the boundary.

  • Then all of those things that I just showed you

  • I should be able to create.

  • I want to mention one other reason why I'm doing this,

  • and it might take me a while to get to this,

  • but in a future video, what I intend to use this for,

  • is also if I have a vehicle,

  • I want to use this in an A.I. application.

  • If I have a vehicle

  • that's trying to drive along some type of path,

  • if I can give it sensors, which are like rays

  • that allow it to see out from in front of it,

  • the readings of those sensors,

  • where those sensors, how far they are

  • from the path's boundary could be inputs

  • into a neural network that would then determine

  • which direction the vehicle should turn.

  • So, this is where I'm also going with this, eventually.

  • But right now, in this video, I just want this ray

  • and this segment.

  • Here I am with the code.

  • So, I have three JavaScript files.

  • I have sketch.js with my, sort of, meme stuff in it

  • set up in Draw.

  • I have ray.js, where I want to create a ray class,

  • and boundary.js, where I want to create my boundary class.

  • So, let's do the boundary first.

  • Class boundary,

  • and then I need a constructor.

  • And basically, I want two points.

  • A

  • equals createVector

  • and

  • B equals createVector.

  • So, this is the idea of the boundary.

  • I have two points.

  • I could have them be separate variables.

  • This.x1,

  • this.y1,

  • this.x2,

  • this.y2,

  • but I'm going to use A and B.

  • Oh, but wait!

  • Yeah, as vectors.

  • So, A and B each get their own X and Y.

  • Ha, okay.

  • But maybe what I'll do is I'll initialize it

  • with four arguments, which are the raw coordinate values.

  • I might regret this later, but let's see what happens here.

  • Then, let me write a function just to draw it.

  • So, if were to draw it,

  • all I want to do is say draw line

  • from a.x, a.y, to b.x, b.y,

  • and say stroke 255, okay.

  • This is good.

  • So, this is when things just work out so specifically.

  • So now, in sketch, I'm going to say,

  • let's start with just one boundary.

  • I'm going to call it B,

  • and I'm going to say createCanvas

  • 400,

  • 400,

  • and B is a new boundary

  • between, I don't know,

  • 300,

  • 300,

  • 300.

  • No, 300, 100,

  • 300,

  • 300,

  • and then

  • background

  • zero

  • and b.show.

  • Okay.

  • Alrighty.

  • Alrighty, alrighty, alrighty.

  • (comical music)

  • You'd think by now I wouldn't have this problem.

  • This comes from 15 years of programming in Java

  • before JavaScript, wasn't that many years, maybe 10?

  • There we go, look!

  • Yay, I have a boundary!

  • (trumpets blaring)

  • Now, let's make the ray class.

  • So, I'm going to say class ray,

  • and, oh, there's so many ways I could do this.

  • I definitely need a constructor,

  • and I want a position,

  • which is going to be a createVector,

  • and this a little bit.

  • This is a little bit silly, because ultimately,

  • what is a ray

  • but a vector?

  • What I'm saying, though, is that for my purposes,

  • I want the ray to be a vector that emminates

  • from a particular location.

  • So, instead of just using P5 vector directly,

  • I'm going to make my own ray class, which wraps a P5 vector

  • with a P5 vector, that's position.

  • So, the ray will be initialized at an X, Y location,

  • and then its direction,

  • I'm just going to hard code that right now.

  • CreateVector

  • zero comma one, and then what I'm going to do

  • is write a draw function

  • and I will say

  • same thing, stroke 255.

  • Let's translate to the position, that'll make things easier.

  • Translate to this.pos, I didn't forget this time.

  • This.pos.y, and then draw a line from zero, zero

  • to direction.x, direction.y,

  • and

  • pop.

  • Maybe what I'll also do.

  • So.

  • If I make the vector that size, it's going to be

  • one pixel of length, which means draw,

  • which is fine for just knowing the direction.

  • It's a unit vector, but for drawing it, it's kind of tiny.

  • So, I don't know, this is very silly,

  • but I'm just going to multiply it by 10

  • just so I can sort of see it

  • right here.

  • There's other more thoughtful ways I could do this,

  • but this will work.

  • All right, let's go here.

  • Oh, now I need to make a ray.

  • So, now I'm going to say let R.

  • Let's call this bound, let's call this ray.

  • Let's call this wall, let's call that ray.

  • So, wall is a new boundary, wall.show.

  • Ray is a new ray, and I will put this at,

  • you're going to be surprised about this location.

  • (laughing)

  • 100,

  • 200,

  • 100, 200, and then I'm going to say ray.show.

  • Okay,

  • ah!

  • Dir is not defined.

  • This dot, this dot, I was so close

  • to not forgetting my this dots.

  • Oh, what?

  • No!

  • (coughing)

  • Oh yeah, I want to point it to the right.

  • So, there we go.

  • X is the first component.

  • Yes, okay, look at this!

  • Oh, have I set myself up for an exciting situation or what?

  • So, if all goes according to plan,

  • all I need to do now is write a function

  • that says

  • ray

  • dot check

  • wall.

  • Ray.checkWall, that function should either return maybe null

  • or undefined if it doesn't intersect,

  • or the point at which it intersects if it does intersect.

  • In other words, I want to say let pt for point

  • equal ray.intersect

  • or cast.

  • Let's call it cast against the wall, I know it's weird,

  • and then if point exists, let's draw a point

  • at point.x, point.y.

  • We'll make this white and maybe we'll make it,

  • actually, an ellipse.

  • So, this is the idea.

  • I have a wall, I have a ray,

  • and I'm going to cast the ray towards the wall,

  • and if I get a point back, I'm going to draw it.

  • So, in the ray class,

  • I'm going to write a function called cast,

  • and it's going to receive a boundary, or I can call it wall.

  • In order to do this, I need math.

  • Right, I need to learn about how do I determine if a line

  • and another line are intersecting,

  • and what's that point of intersection?

  • There is a library, I'm being told from the chat,

  • called p5.collide2D

  • that offers a lot of functionality

  • for these types of geometry operations.

  • Line-line intersection.

  • You know, line-circle intersection.

  • Circle-rectangle intersection.

  • So, I could go and use that library

  • and maybe it might be useful to do that

  • and I'll come back to that at the end of this video.

  • I am, however, going to enjoy looking at a Wikipedia page

  • called line-line intersection and finding the formula

  • that I can use to apply to do this math myself in the code.

  • So, one thing I want to emphasize

  • is that I mentioned there's actually two steps.

  • The first step is just to determine

  • if it's even intersecting at all.

  • Is it intersecting the line segment or no?

  • And only if it is, then find that point.

  • And so, in order to do this,

  • and I'll go to the Wikipedia page itself,

  • there's actually two values that I need to calculate.

  • One called T, and one called U.

  • If T

  • is between

  • zero and one,

  • and U is greater than zero,

  • then the answer is yes.

  • Both of these have to be true.

  • Once the answer to that is yes,

  • I can use T and U to then calculate the actual point itself.

  • I'm going to let you enjoy this.

  • You can pause and read this whole Wikipedia page yourself,

  • but I'm going to scroll down to the important part,

  • and that is right here.

  • The intersection point falls within the first line segment

  • if T is between zero and one, and falls within

  • the second line segment if U is between zero and one.

  • So, this is different than what I said, right?

  • Because, the thing is, that Wikipedia page

  • is describing the point of intersection

  • between two line segments.

  • This boundary is an actual line segment.

  • It has a start and an end.

  • It's only this segment.

  • The ray, however, is not really a line segment

  • the way I'm thinking of it.

  • It is just an infinite line

  • that goes off in both directions.

  • So, this is why

  • if T

  • is for this

  • boundary,

  • then I know if this point is really within this boundary

  • if T is between zero and one,

  • but for U, I just want to know that the intersection point

  • is on the positive side of the ray

  • and not if the ray was pointing in this direction

  • on the negative side.

  • So, this is my modification of that math

  • on the Wikipedia page.

  • So, let's calculate T and U.

  • So, one thing you're looking in this formula here,

  • is this is the formula for T,

  • and this is the formula for U.

  • You'll notice that the denominator

  • of both of these fractions is exactly the same.

  • So, let me first, in my code.

  • I'm going to, just to reduce confusion,

  • I'm going to say

  • x1

  • is

  • wall.a.x,

  • just so I can use the same notation

  • that's on that Wikipedia page,

  • and I'll skip through this more quickly.

  • (calming music)

  • So, the wall's start and end points

  • are x1, y1,

  • x2, y2.

  • (calming music)

  • And now, for the next.

  • So, this one line segment between x1, y1, x2, y2.

  • Between the other line segment x3, y3, x4, y4,

  • I have the position of the ray

  • and then the endpoint of

  • the endpoint of that line segment,

  • which is the position of the ray

  • plus the direction of the ray.

  • So, I'm making a line segment

  • by saying here's the position of the ray,

  • this is the direction vector.

  • So, I have x3, y3, and then this point right here

  • is x4,

  • y4.

  • So, now that I've kind of renamed all of this,

  • and I could do the actual math with vector operations

  • and just keep these variable names,

  • but this going to let me make sure

  • that the Wikipedia formula matches

  • exactly what I'm doing here.

  • So, the first thing is to calculate the denominator.

  • Can I remember this?

  • X1, x2,

  • y3, y4,

  • y1, y2,

  • x3, x4.

  • (calming music)

  • Now, this is the formula implemented as the denominator.

  • Now, an interesting tidbit that you might've noticed here is

  • wow, the denominator is the thing you're dividing by.

  • What if it's zero?

  • This could be zero.

  • Well, guess what?

  • If the value of that is zero,

  • that means that the boundary and the ray

  • are perfectly parallel, meaning they would never intersect,

  • even if you stretch them out infinitely in both directions.

  • So, that's something that I need to look at right here,

  • which is just to say

  • if denominator equals zero,

  • return null.

  • So, this is, or just return, I'll just say return.

  • Get out of here if that's a zero.

  • I'm done!

  • I'm done with this, check.

  • Now, I want to calculate T.

  • So, T is going to be this

  • x1, x3,

  • y3, y4,

  • y1, y3,

  • x3, x4.

  • (calming music)

  • All right, now I've got T, the numerator

  • divided by that denominator I calculated above.

  • Now, it's time for me to get U.

  • X1, x2,

  • y1, y3,

  • y1, y2,

  • x1, x3.

  • This is actually the same, it's just a two.

  • (calming music)

  • This is close, but if you're not looking carefully,

  • there is a nice little negative there,

  • so I've got to also add that in for U.

  • People in the chat are asking

  • about these pipe symbols, what they mean.

  • That means its the determinate of a matrix.

  • So, I've got a lot of different videos

  • about matrix math beyond the scope

  • of what I'm doing here right now,

  • but that's certainly something you could look into,

  • and I'll try to include some resources

  • in the video's description.

  • But, what am I doing right now?

  • Remember, I said.

  • Right, what did I say?

  • I said I was checking now if T is between zero and one,

  • and if U is greater than zero.

  • So, let's try that.

  • Let's just say

  • if

  • T is greater than zero, and T is less than one,

  • and U is greater than zero, than return.

  • I'll just say return true.

  • Otherwise,

  • return.

  • So now, in theory, if the lines are parallel,

  • I'll return there.

  • If there is an intersection point,

  • I'll return true.

  • If there isn't, then I'll just return nothing again.

  • So, I haven't calculated the point of intersection.

  • Right now, this is just testing

  • if there's a point of intersection.

  • Let's see if that even works.

  • So, back to sketch.js,

  • and then if point.

  • Actually, let's just comment this out.

  • Console.log point.

  • Let's see what we get.

  • True!

  • There is a point of intersection, yay!

  • So, this is working.

  • Now, will I get false?

  • (dinging)

  • So, the chat is pointing out that I can just return

  • the bullion result of that,

  • and I would do that if this was all that I want to do,

  • but ultimately, if that's true,

  • then I want to go and calculate the actual point itself.

  • So, that's why I'm breaking this out

  • into a large if statement.

  • Now.

  • I need to do something where I actually

  • set the direction of the ray.

  • So, let me add a function called set.

  • I'm just going to call it update, I don't know.

  • Or set, I don't know, set direction,

  • and I'm going to give it

  • an X and a Y,

  • and

  • I'm going to say this.dir equals,

  • this.dir.x

  • equals

  • X minus

  • this.pos.x.

  • This.dir.y equals Y, minus this.pos.y,

  • and then this.dir normalize.

  • So, I'm just creating a vector

  • that's pointing towards.

  • This is kind of like,

  • so let's actually make this function called lookAt.

  • I want to look at this particular point,

  • and then in draw, what I can do is

  • I can say ray

  • look

  • at

  • mouseX,

  • mouseY.

  • So, now, let's see what happens.

  • So, true, true, true, but if I point up, undefined.

  • Right here, it starts being true, true, true.

  • Undefined, undefined, undefined.

  • So, this looks to me like it's working.

  • I'm getting true when I'm pointing at the wall

  • and getting undefined when I'm pointing away.

  • That's a good sign.

  • So, now, all I need to do is get that point.

  • So, right here, I want to create a vector.

  • I want to create a vector and eventually,

  • I want to say the X value of that vector is,

  • back to my Wikipedia page.

  • Here, I can use either T or U to find that point.

  • I don't know that it really matters.

  • Let's use T.

  • X1

  • plus T

  • times x2 minus x1.

  • X1 plus T

  • times x2

  • minus x1.

  • And then

  • the Y

  • is

  • Y1

  • plus T times y2 minus y1.

  • Y1

  • plus T times

  • y2

  • minus y1,

  • and then I can say return that point.

  • So now, I should be able to go back here,

  • and I should be able to add this.

  • And here we go.

  • Look, I'm casting my ray all the way up to the edge.

  • So, you might rightfully be wondering

  • is this really working?

  • I mean, I kind of set this up in a really simple scenario

  • where the line is perfectly vertical

  • and the ray is right here positioned

  • so that it's kind of pointed directly at it

  • from an obvious location.

  • So, to know if this is really working,

  • I might want to move the line around, skew it,

  • move the ray around.

  • But ultimately, what I want to do,

  • is I want to create something like what Nicky Case

  • has on their page,

  • and I want to create a world of many boundaries

  • and an object that I can move around and shoot rays out

  • in all directions.

  • So, I could approach that by first setting rays out

  • in all directions.

  • Let's do that.

  • Let's send rays out in all directions.

  • This might be a little bit overkill,

  • but I think I'm going to make a particle class

  • just to be the thing that's moving around the screen.

  • I don't really need that right now,

  • but it's going to set me up better for the future

  • if I want to do raycasting

  • from multiple vehicles driving around.

  • So, let's make a particle class.

  • I'm just going to give it.

  • I'm just going to give it a position.

  • This dot position equals createVector,

  • and I'm going to put it squarely in the center

  • of the canvas, and then I'm going to

  • create an array of rays.

  • So, I'm going to say for

  • let I equal zero, I is less than 360,

  • I plus equal 10.

  • So, I'm just going to think about the rays

  • as every 10 degrees from zero to 360,

  • and I am going to say

  • this.rays

  • equals a new

  • ray

  • position.x,

  • position.y,

  • and then I'm going to give it an angle.

  • So, I am now going to, when I create this.rays, index I.

  • I'm going to make it so when I create a ray,

  • I give it, in addition to its position,

  • I give it its angle.

  • Actually, if all of the rays are going to move

  • as the particle moves, then, to be honest,

  • it would make much more sense for me

  • to actually just pass in

  • the vector reference itself,

  • and have the rays point to it,

  • I don't even need to make a copy of that vector.

  • So, I could change the angle mode to degrees, by the way,

  • but I kind of like using radian,

  • so I'm going to just use the radians function to convert it,

  • but I wanted to think in degrees when doing the loop.

  • So, if I go back to ray, I need to change the constructor.

  • So, the constructor now expects a position and an angle.

  • So, I can say this.position just equals that position,

  • and the angle, now, equals p5.Vector.fromAngle

  • angle.

  • So, this is a function in the P vector class.

  • A static function that allows me to create a vector

  • pointing in the direction of an angle.

  • So, now that I have that,

  • I have my particle.

  • I'm going to say let particle.

  • Particle equals a new particle.

  • There's no individual ray, anymore.

  • I'm going to say particle.show,

  • and in particle.show,

  • I am going to comment all this out.

  • In particle.show,

  • I am going to say first fill 255, let's draw the particle,

  • which is an ellipse at pos.x, pos.y with, I don't know, 16,

  • and then, I'm going to say for let I equal zero,

  • I is less than rays.length, I plus plus, ray.show.

  • So, let's look at this, now.

  • Particle is not defined because I forgot to include it as

  • a file reference in my HTML.

  • Pos is not defined because I almost definitely forgot

  • this dot

  • everywhere.

  • And then, actually, I don't know what I'm doing

  • with this loop here.

  • I can just say for let ray of this.rays.

  • Pos is not defined.

  • Where was this.pos?

  • There we go.

  • Cannot read property show of undefined at particle.show.

  • Oh, this is terrible!

  • This.rays, I didn't mean to do that.

  • This is why I shouldn't have called that I.

  • This is really the angle,

  • and what I want to do is just add a ray.

  • So, I don't need to keep track of the number of rays.

  • So, there we go.

  • I just want to use push because I is not an index here.

  • There we go, so you can kind of see it there.

  • Those are all the rays shooting out.

  • Let's draw this smaller.

  • So, you can see there they are.

  • That's a little dot with all the rays shooting out.

  • Next step is to actually find if any of those rays

  • intersect

  • the boundary.

  • So, I have this cast function.

  • So, I should be particle

  • look.

  • Look, I'll call it particle.look,

  • and I need to give it the wall.

  • Not sure if this is the smartest

  • object-oriented structure, but this will work.

  • So, look at a wall, and then I'm going to say,

  • once again, I'm going to look at all the rays,

  • and I'm going to have every ray.

  • When I say check,

  • what is it called?

  • In

  • cast.

  • Cast

  • wall.

  • Okay, let's try this and see what happens.

  • So, what happens in cast?

  • It does this.

  • Oh, it returns the point.

  • Okay.

  • So now.

  • Okay, so it returns the point.

  • So, let

  • const

  • point equals ray.cast

  • if point exists.

  • Now, I want to draw a line from this.pos.x, this.pos.y

  • to point.x,

  • point.y.

  • There we go, you can see those are the rays

  • that intersect the wall.

  • Now, if I, oh, this is the exciting part!

  • If I say particle.update

  • mouseX,

  • mouseY,

  • and in the particle, I add a function called update

  • with an X,Y, and I can just say this.pos.set X,Y.

  • Now, I can move this around

  • and you can see that it's showing me the rays

  • that hit this boundary.

  • Let's make sure this works with the boundary

  • being in a completely different location.

  • So, now,

  • the boundary could be

  • 100, 100

  • to 200, 300.

  • Right, still works.

  • I could move over here.

  • So, these rays are hitting that boundary

  • from wherever they are.

  • I could draw the non-hitting rays with a alpha.

  • That could be kind of interesting.

  • Just all the way.

  • I could also add boundaries to the edge

  • of the window, but what happens now.

  • The real thing is to have multiple boundaries.

  • So, let's make multiple boundaries.

  • I'm going to make it called walls,

  • which is an array,

  • then I'm going to say for

  • let I equal zero, I is less than five, I plus plus,

  • and I'm going to say walls index I is a new boundary,

  • and let's, I don't know.

  • This is probably a terrible idea,

  • but let's make some random boundaries.

  • Y1, Y2,

  • height.

  • Height.

  • X1,

  • Y1,

  • X2,

  • Y2.

  • Then

  • I need to show

  • for

  • let wall of walls

  • show all the walls, and let's comment out this look, now.

  • All right, so this is what we get

  • if I make five random boundaries.

  • I probably should be more thoughtful

  • about how I'm setting them up,

  • but this should work, anyway.

  • So, let's let this.

  • Let's let this go.

  • So, right now, if I were to just say

  • look walls index zero,

  • this should work with just one of the walls,

  • but what I want is for now this ray

  • to stop here and not go to the next one.

  • So, I need to check the ray against every single wall

  • and find the one that's closest.

  • In other words, if I have this wall and this wall,

  • and I have a ray here, extending this ray out,

  • I get these two intersection points,

  • but all I need to do is figure out which one was closer,

  • and that's the one that I choose to draw the ray to,

  • and then I sort of ignore this part.

  • If this were a light source,

  • the shadow would be cast there,

  • but the light would arrive over here.

  • So, you can see how this idea of raycasting

  • is related to how light is sent out into a scene

  • and shapes and shadows are rendered.

  • So, if I change the particle look function

  • and give it walls.

  • Now, I can go in here

  • and I can say,

  • let ray

  • of

  • this.rays,

  • and I can then say for let, and this is walls,

  • let

  • wall of walls,

  • and I want to find the record distance.

  • So, if I start with infinity,

  • and I look for that point,

  • and then I say

  • let's get the distance.

  • The distance between

  • this.pos

  • and point.

  • Right, this the p5.Vector function,

  • and then I say D equals the minimum,

  • whichever's smaller between the distance and the record.

  • Sorry, the record is that.

  • The record is whatever's smaller between

  • the new distance I've found and the record.

  • All of this I can only do if point, if there is a point,

  • then

  • I need to keep track of the record point.

  • So,

  • I'll call this closest equals null,

  • and if point,

  • and if point.

  • Oh, I really should check if its a new record.

  • So, I might as well just do here.

  • There might be a better way to do this.

  • If distance is less than the record,

  • then the record equals distance,

  • and the closest is that point.

  • Oops, I typed closet instead of closest.

  • So, now,

  • if closest is a thing,

  • then now, I'm going to draw that line

  • from

  • this.pos.x,

  • this.pos.y,

  • to closest.x,

  • closest.y.

  • So, this is now the same exact algorithm,

  • but for each ray instead of seeing if there is a point.

  • For each ray, I'm finding is there a point,

  • and is it the closest one?

  • So, let's see if that works.

  • Yeah this, oh no.

  • So, why do I have a bug here?

  • Ah, here we go, this is the problem,

  • thank you to the chat. (dinging)

  • The record is a thing that I need to keep track of

  • while I'm looking at every wall.

  • So, I'm re-setting the record to infinity.

  • That's not going to work.

  • So, the record needs to be set to infinity,

  • and now I start checking all the walls.

  • So, that should do the trick.

  • There we go.

  • So, we can see, now, my beautiful raycasting is working.

  • I'm going to do one more thing to this.

  • This is really done.

  • There's so much more that can be done with this,

  • and I'm really

  • having a hard time stopping myself,

  • but I'm going to do one thing.

  • I want to let it just move without the mouse,

  • and the way that I'm going to do that

  • is I am going to just use Perlin noise.

  • So, let's say X offset and Y offset,

  • and I know, I know there was a whole long discussion

  • about what's Perlin noise

  • versus Simplex versus OpenSimplex.

  • Let me just say Perlin noise.

  • Please, please, let me just say it.

  • You can find those videos if you want,

  • and then I am going to say particle.update,

  • noise X offset times width,

  • noise

  • Y offset times height,

  • and then I will increase X offset by some small amount,

  • and also Y offset by some small amount.

  • That's going to move on the diagonal if I don't start Y offset

  • in a different place, and there we go.

  • So, I just want it to move automatically,

  • which I think is a little bit more interesting to watch.

  • Also, oh, there we go.

  • Here's a nice configuration of boundaries.

  • So, there we go!

  • Now we have raycasting from a particle

  • moving with Perlin noise throughout a space

  • in all 360 directions,

  • only drawing the rays where it actually hits a boundary.

  • (dinging)

  • (train whistling)

  • There is so much you can do with this.

  • I am going to return to do a video

  • where I take this idea and then render the view.

  • So, a couple things I want to do.

  • One is, I want these to be sensors to a neural network.

  • So, I want this object to learn how to move about a space

  • based on its ability to see the boundaries around it.

  • Another thing that I want to do

  • is see if I can take this and then render the view

  • of a 3D scene.

  • This is the kind of classic DOOM or Wolfenstine

  • Wolfenstine 3D, right, one of those things.

  • (chuckling)

  • Effects.

  • Stop me for a second, here.

  • People are asking me to put boundaries along the edges.

  • So, why not?

  • I'll fast-forward this for you.

  • (calming music)

  • Okay, so, here are four boundaries, if I did this correctly.

  • The top wall, the right wall, the bottom wall,

  • and the left wall.

  • Let's see what that looks like.

  • So, now you can see the rays are always going to go out

  • in every single direction.

  • Alternatively, I kind of like it better

  • without it casting everything out to the walls.

  • So, here is that again,

  • and the chat is saying, "More rays, please".

  • More rays?

  • So, let's add that.

  • So, particle right now, I made a very arbitrary decision

  • to have the angle increments at 10 degrees.

  • If I change this to one.

  • There we go, this feels much more like light, right?

  • You can almost, sort of, get the sense.

  • Again, the boundaries are in a sort of weird place here,

  • but you can get the sense of this casting of shadows.

  • Oh, now I should put the walls back in,

  • and maybe, just maybe,

  • I might like to draw

  • the ray.

  • Just maybe I'd like to draw these lines

  • with a stroke

  • weight

  • of,

  • let's actually just give it an alpha.

  • A little bit of transparency?

  • And let's see what happens.

  • There we go, ooh, I like that!

  • So, yeah, so now you really see it's like a light source

  • that's just casting its light everywhere,

  • and you can see where the shadows are.

  • Thank you for watching this raycasting coding challenge.

  • This one was a lot of fun to do,

  • and I think there are a lot of creative possibilities

  • that you could explore building on top of this

  • very basic example that I've made.

  • I haven't added color to this,

  • the boundaries are just in random locations.

  • They could be drawn by a user,

  • they could be part of some generative pattern.

  • How the actual light source moves about the space

  • doesn't have to be by a user interaction or Perlin noise.

  • There could be multiple light sources,

  • there could be different kinds of boundaries.

  • There are so many things you could do.

  • I'm sure there are things you could do

  • that I'm not even thinking of.

  • There are a whole bunch of follow-up videos

  • I could also do exploring this topic further.

  • So, the first thing that I want to try to tackle

  • is just exploring this idea of raytracing.

  • Could I render, sort of like a 3D view of the scene

  • from this little point of light that's moving about a space?

  • There's also another technique for doing essentially

  • the same exact thing, called raymarching.

  • There's a wonderful coding adventure video

  • by Sebastian Lague that I referenced

  • at the beginning of this video

  • that you should check out,

  • and I'm going to come back and do my own version

  • of that raymarching in 2D algorithm as well.

  • So, thanks for watching,

  • and see you in a future coding challenge.

  • (train whistling)

  • (bright pop music)

- Hello, and welcome to a coding challenge:

Subtitles and vocabulary

Click the word to look it up Click the word to find further inforamtion about it