Placeholder Image

字幕列表 影片播放

  • (train whistle blows)

  • - Hello and welcome to a coding challenge.

  • In this coding challenge,

  • I should probably go eat lunch but I'm going to just

  • try this coding challenge.

  • I am going to attempt to program this.

  • This is a flocking simulation.

  • What you're seeing right now is an example

  • that I made many years ago

  • of a flocking simulation, a Boids system

  • running in processing.

  • I'm going to try to make exactly this or close to it

  • in JavaScript with the p5 library

  • starting from no code at all.

  • Obviously besides the dependencies.

  • Okay, so this is the example running.

  • Let me just give you a little bit of background here.

  • If you want to learn more about the flocking

  • algorithm as invented by Craig Reynolds,

  • you can go to this link to read this explanation of it.

  • And find many other links and resources

  • down here, whoa, that's a lot of stuff.

  • Have fun, see you in a few weeks.

  • You can also check out this YouTube video

  • of the original 1986 flocking simulation.

  • Wow, that's amazing.

  • This is so cool, oh my God, I love that.

  • You can also find an explanation of it

  • as part of my Nature of Code book online

  • with some diagrams and code written in Processing

  • and an exercise that you can try which I'll refer to

  • back at the end.

  • But I'm going to start from this, a blank slate.

  • I just have the canvas, you have some amount

  • of pixels in it.

  • There is nothing happening in the console.

  • I've got this code set up and draw.

  • So I'm going to write this code without worrying about

  • being perfectly organized and being able to scale

  • it very easily.

  • I just want to get the algorithm working but of course,

  • where is the button?

  • (pop music)

  • If you want to see a refactored example of it,

  • you can probably go and look and actually,

  • at my finished version in the Nature of Code

  • which I'll also link to.

  • The first thing that I want to do though is

  • I want to add another JavaScript

  • file called particle.js.

  • Actually, I should rename this to boid.js.

  • What is this term Boid?

  • When Craig Reynolds invented this algorithm

  • for simulating a flocking system like a flock of birds

  • or a swarm of bees, I suppose he didn't want

  • to use the term bird, but it is kind of like bird,

  • so Boid, it's kind of like droid but Boid.

  • Go read the original paper about steering behaviors

  • and flocking systems and you'll find out more

  • about the history about this.

  • But I'm going to make a class called Boid

  • and I am going to use the p5 vector object.

  • I'm going to make Boid objects and each time

  • I made a Boid object, I need a constructor

  • and I am going to give each Boid a position.

  • I'm going to give each Boid a velocity.

  • I'm going to give each Boid an acceleration.

  • All of those are going to be vectors.

  • I'm doing this is 2D.

  • You should, after you watch this, if you watch this,

  • you should make your own 3D version of this.

  • All of the math will work exactly the same way

  • but you'll just need to rethink how you're visualizing this.

  • Just to give things a start, let's just put

  • the position in the middle of the window

  • and let's write a function called show

  • which will just draw them as a circle right now.

  • Actually, I'll just even use point, like strokeWeight 16.

  • Stroke 255, point this.position.x.

  • I think Visual Studio code will do this for me.

  • No, where does it?

  • It fixed that for me once, whatever.

  • This PointerEvent, I'm not a PointerEvent,

  • I'm a point.

  • So now, if I were to then here make a flock,

  • which is an array, and I should probably make

  • a flock class, that might be a thing to refactor later.

  • But I'm just going to say a flock.push new Boid

  • and then I just want to say for every Boid in the flock

  • and I'm using of, I'm using a for-of loop

  • but I said in because that's the way I feel right now.

  • A boid.show and then I need to make sure

  • I am also referencing boid.js in my index HTML

  • so I have the basic Boid class, which just creates

  • a position, velocity, acceleration and draws it

  • as a point and then I have my sketch where I make

  • one Boid and I draw it in the center of the screen.

  • There it is, there's my boid.

  • (bell rings)

  • Step one complete.

  • I probably should plan this out

  • but I have one Boid, that's good.

  • Now, let's have that Boid move.

  • Let's actually write in the Boid object

  • a function called update and this I probably covered

  • ad nauseum in a lot of other videos

  • in my whole Nature of Code series.

  • But the idea here is that position is updated

  • based on the Boid's velocity and its velocity

  • is updated based on the Boid's acceleration.

  • If I now actually create a p5.vector, a random 2D.

  • I believe this is a function that will give me

  • a random velocity vector and I now go in here

  • and say boid.update.

  • Oh I just spelled it wrong.

  • I thought I missed the this dots.

  • Here we go, look, it's moving.

  • It's moving with a random velocity each time.

  • Okay, now let's just make a bunch of these.

  • Now I have a system of 100 Boids but it's interesting,

  • look at this, it fans out in the perfect circle.

  • Do you know why that is?

  • Because I make a Boid and I give it a random velocity.

  • That random vector is always of unit length one.

  • The direction is something different so they're all

  • actually moving with the same speed.

  • If I wanted them to have, and this is not an important

  • detail to the flocking stuff, but just to sort of

  • get a sense of how this vector stuff works,

  • I could say this.velocity.setMag, which is set the

  • magnitude to a random value between 0.5 and 1.5.

  • Now, you can see now they're all moving with a

  • slightly different velocity.

  • This is by the way, a nice little almost like explosion

  • motif and I could have them with a real big burst

  • then they slow down.

  • There's all sorts of physics-y stuff I could do

  • with this but I just want to do flocking.

  • How, how, how do I do flocking?

  • Let's return to this paper here.

  • This is the key behind Craig Reynolds' flocking algorithm.

  • Three rules.

  • Separation: steer to avoid crowding local flockmates.

  • Alignment: steer towards the average heading

  • of local flockmates.

  • Cohesion: steer to move toward the average position

  • of local flockmates.

  • Something so crucial in that description is the

  • word local, local.

  • Local, what does that mean?

  • Let's just do, I think the easiest one to do

  • actually is alignment.

  • Let's say there are five Boids.

  • Actually, let's make a lot more of them.

  • These are all of my Boids.

  • This is the one that I'm currently operating around.

  • It has a velocity vector like this.

  • Let me just make up, let's just pretend

  • like the ones around it are kind of moving

  • all in the same direction.

  • Because that'll be easier to think about.

  • This is the current one that I'm looking at.

  • And maybe some of these others are also moving

  • in other directions also.

  • I could take the approach to say this particular Boid

  • needs to align itself to everything.

  • But this is not really how complex systems

  • in nature actually work.

  • The emergent phenomena comes from this Boid having

  • a limited perception of its environment.

  • So maybe, it's actually only able to see things

  • that are in front of it or behind it

  • or to the right or what's probably the most simplest

  • thing to implement is within some radius.

  • So I only want to look at the Boids that are

  • within this radius, meaning this one, this one,

  • this one, this one, this one.

  • What if I iterate over all these ones that are

  • within some distance, average all of their velocities

  • and shift this velocity in the direction

  • of all those velocities.

  • That's what we want to do.

  • I am going to write a function now.

  • I'm going to put it in the Boid and I'm going to

  • call it align and it's going to get presumably

  • an array of other Boids.

  • The idea is this function align this Boid

  • with all the other Boids.

  • So what do I need to do?

  • I'm going to make a variable called average

  • which is going to be a vector then I am going to

  • iterate over and I could do all of these with

  • reduce probably in some higher order array functions.

  • That's a great thing for you to do.

  • (pop music)

  • Refactor it later but I'm just going to do it

  • this way, I'm actually going to use i because that's

  • maybe going to help me.

  • I'm not really sure but I'm going to use i right now.

  • Actually, no, I'll still use a for-of loop.

  • Let other, I'm going to call it other of Boids

  • and I'm going to just say average.add other.velocity.

  • So I'm adding up all of the velocities.

  • This is how you do an average, right?

  • Vectors, again you might want to go watch my videos

  • about how vectors work, but a vector, a p5 vector

  • just holds an x and a y or a x and y and a z.

  • So I want to average the vector, represented as an arrow

  • with an x component and a y component.

  • If I want to average, just like I might average

  • an array of numbers, I add them all up and divide

  • by the total.

  • Then I would say average.divide by Boids.length.

  • This would be a way of getting the average velocity

  • of all the Boids but remember I only want

  • the ones within some distance.

  • So actually, let's implement that right now.

  • Let's have some sort of distance like max.

  • I'll call it perception equals let's just make it

  • arbitrarily like 100 and then I'm going to now

  • say if the distance between this.position.x.

  • You know what?

  • I think there's a p5.

  • This is fine.

  • It's just going to be, it's kind of long the way I'm writing this

  • but I don't mind.

  • Let's actually make it in a variable.

  • D = the distance between this Boid's position

  • and the other Boid's position, x and y.

  • If that distance is less than 100,

  • look at that.

  • Look at this fancy thing that it's doing.

  • I have some prettier stuff that doesn't want

  • to let me write a long line of code.

  • I guess I'll keep that in there for right now.

  • So I'm calculating the distance between this Boid's

  • position and the other Boid's position.

  • If that distance is less than 100, I'm adding

  • it up and I should also probably have a total number,

  • total = zero.

  • Add it up.

  • I also should probably ignore myself and if other

  • does not equal this, right?

  • So I basically, I might as well put that first.

  • As long as the other thing is not me

  • and the distance is less than 100, add it up

  • and then divide by the total.

  • But obviously, I only want to divide by the total

  • if total is greater than zero.

  • All right, so this is kind of a little bit of

  • a long-winded algorithm now.

  • And perception.

  • If d is less than perception.

  • So I'm starting with a perception, a radius of 100.

  • Maybe that would be better if I called it

  • perceptionRadius, that's really what it is.

  • Starting with a perception radius of 100,

  • an empty vector, adding up the velocities

  • of any vector, any Boid that's near me,

  • dividing by the total and as long as I've found one,

  • dividing by that total.

  • Now here's the thing.

  • I could just say, for example I could do something

  • weird, this will be really weird.

  • I could just say this.velocity equals average.

  • What if I did that?

  • Then I said Boid, like here I said boid.align flock.

  • I don't know what's going to happen here,

  • but let's refresh this.

  • They just basically don't go anywhere.

  • Because they're all instantly equal

  • to each other's velocity.

  • That's not really a good way.

  • What I need to do now is if I don't want to actually

  • assign its velocity to that average directly.

  • I want to steer towards it.

  • This is where Craig Reynolds' steering formula comes in.

  • A steering force equals some desired velocity

  • minus the current actual velocity.

  • It's kind of like the error.

  • If I am moving this way.

  • Sorry, if I'm moving this way but I want to move this way,

  • the way I want to move minus the way I am moving

  • is the steering force meaning push me in that direction.

  • Apply a force.

  • Maybe I'm going to turn the steering wheel.

  • You can think of these as bees or cars

  • or birds or whatever.

  • This formula desired minus velocity will give me

  • the steering force and what is the desired velocity here?

  • The desired velocity is actually that average.

  • I'm actually going to rename this to desired

  • and I'm going to add it up

  • and divide by total and then I'm going to say

  • this.velocity equals, no, I'm not going to say that.

  • I'm going to say steering equals.

  • I should actually call this steering because I could

  • do all of this.

  • I don't need to save anything as I'm going.

  • I'm going to call that the steering force.

  • I'm going to add all the velocities.

  • I'm going to divide by the total and then,

  • I'm going to say steering force is the desired

  • subtract this.velocity.

  • And I'm going to say return steering.

  • I want this function to basically,

  • I want this function to return that force.

  • So really what I should be doing is I should have,

  • I'm going to write a function called flock.

  • That's maybe more like, yeah.

  • Flock with some number of Boids and then I'm going to say

  • alignment equals align with those Boids.

  • So I'm going to get this force and I suppose

  • steering is here, so you know what?

  • I should always return steering.

  • I want to always return a vector but if it didn't find

  • anything, I'll just return the zero vector.

  • Then, what I'm going to do is I'm going to say

  • acceleration add alignment.

  • The idea, the way that a physics engine works,

  • and this has to do with,

  • this is a very simple crude physics engine.

  • Force equals mass times acceleration.

  • This is Newton's Law of Motion.

  • Or acceleration equals force divided by mass

  • or in a world where the mass of everything is one,

  • acceleration equals force.

  • So if I want to apply a force to this object,

  • I just need to set the acceleration to that force.

  • Actually, that's what I'm going to do just starting out here.

  • I'm going to say acceleration equals alignment.

  • So now, if I go back to sketch and I say

  • Boid.flock Boids, we should now have all of Boids

  • doing that with just implemented with that alignment rule.

  • Let's see.

  • Boids is not defined.

  • Sketch.js line 14.

  • Oh, it's called flock.

  • Oh, this is my variable naming is terrible.

  • But I'm going to leave it that way right way.

  • Align is not defined.

  • Boid.js align Boids.

  • Oh, this .align, this .align Boids.

  • Acceleration is not defined, oh my goodness.

  • (pop music)

  • Let me actually not have them all start

  • in the same place because that is just to see

  • this effect happen, let me actually start them

  • in random places on the screen.

  • Let me change that.

  • Let me make the perception radius a little bit smaller

  • and I don't know why this matters, but let me draw them

  • like a little bit smaller.

  • Let me actually have them start out going

  • quite a bit faster.

  • You can see as they get near each other,

  • they start to get each other's velocity.

  • They start to average each velocity with their neighbor's.

  • You start to see these clumps moving together.

  • So much more to do on this.

  • Oh my goodness, but first a couple things.

  • One is I have now basically allowed these Boids

  • to steer with infinite power in a way.

  • We should probably have a variable.

  • I'm going to call it maxForce.

  • I'm going to set it equal to one for a second

  • and then what I'm going to do is here,

  • I'm going to say a steering limit this.maxForce.

  • What this does is it limits the magnitude,

  • the length of that vector to some maximum force

  • so this should be exactly the same.

  • We're not seeing anything different but if

  • I were to make maximum force 0.01, like really small

  • right now, you're going to see they're not actually

  • changing their velocity.

  • They are, but very, very slowly.

  • The other thing we're really going to need to do

  • is I just have to give up and do this.

  • I'm going to add something for the edges here.

  • I'm going to say if this.position.x is greater than

  • the width, this.position.x should equal zero.

  • Else, if this.position.x is less than zero,

  • this.position.x equals width.

  • Then I'm just going to do that for the y,

  • with, with, x, x, y, y, y, x, x, x, x,

  • with, with.

  • All right, and now here, let's do boid.edges.

  • We should see them reappear wrapping around

  • and now, let's go back to the Boid and

  • let's make this 0.2 is kind of reasonable-ish.

  • Interestingly enough, they're kind of like

  • it slows them down which is interesting.

  • But I'm only doing alignment right now,

  • but you can see how this works.

  • You can see how they're all starting to align

  • with each other.

  • I could keep them going at some minimum velocity

  • which might make sense.

  • You can see how these are going back and forth.

  • But you get the idea.

  • Why are they all slowing down?

  • Is that just a coincidence?

  • One thing I could do is

  • this is actually going to be worth it

  • because I should also give them a maximum speed.

  • This is going to be a parameter of a variable

  • that's going to allow me to control the system

  • pretty well and I could consider their desired

  • velocity in the alignment to not actually be

  • the actual average velocity but just the average

  • direction because I could then say steering.setMag

  • to this.maximum speed.

  • So basically I'm saying I always want to go in the

  • direction of my neighbor but at maximum speed.

  • I don't know that that's really an important detail,

  • but if I add that in here, we might get the effect

  • that I was hoping to get.

  • Now, this is what I was expecting to see.

  • As they get near each other,

  • they're all starting to align together.

  • Let me refresh that one more time.

  • You can see that they're clumping and as they group,

  • they all start to align.

  • This is the alignment rule.

  • This is a very simple rule.

  • It's predictable, it's obvious, this is alignment.

  • Now, all we need to do is add cohesion and separation.

  • Separation is going to be the hardest one to do.

  • So let's do cohesion next.

  • Same thing, we're going to look at our neighbors.

  • We should go back to what the rule was actually

  • defined as, let's go back to Craig Reynolds' original page.

  • Cohesion: steer to move toward the average position

  • of local flockmates.

  • This is actually not going to be that hard

  • because we've already calculated the average velocity

  • of local flockmates.

  • Now, let's just duplicate that code.

  • I know, I know we could refactor it.

  • I won't play the song.

  • Because we could probably do this all in one fell swoop.

  • There's so many possibilities but now I just want

  • to get the average location of my local flockmates

  • and steer towards that.

  • So, the way I'm going to do that is I'm just going to

  • go nuts and copy paste this whole thing

  • and call it cohesion and I'm going to keep

  • the perception radius, I'm going to keep this idea

  • of steering, keep this idea of total.

  • Go through all the Boids, check the distance

  • between myself and the other ones,

  • as long as I'm not myself and within the perception radius,

  • what am I doing?

  • Steering not the others' velocity but the others' position.

  • This should clearly be refactored into its own function

  • but whatever.

  • Then as long as I have at least one, I'm going

  • to divide by the total.

  • Now I don't want to set the magnitude to the maximum speed

  • so what I have now in steering is the average location.

  • So if this is the average location,

  • let's say it's over here, then what I need

  • is a vector that,

  • that's clearly not the average location.

  • But let's just say it was.

  • The average location is kind of like

  • where this Boid is.

  • But I want to steer in that direction

  • so to get a vector in that direction,

  • I take the average location minus the current position

  • of me which is basically saying

  • now what I want to do is say steering subtract

  • this.position so subtract my position now,

  • I've got a vector that's pointing from me

  • to the average location, remember which is in

  • the steering variable then let's say I want to

  • go at maximum speed.

  • Then, this is now my desired velocity.

  • I'm going to subtract out my current velocity,

  • limit it to max force and there we go.

  • That's cohesion.

  • (bell rings)

  • Now, in flock, let cohesion equal this.cohesion

  • Boids and then oh, we have a big problem.

  • This.acceleration equals alignment.

  • This.acceleration equals cohesion.

  • So how can I set the acceleration to two different forces?

  • But before I even answer that, there's an easy answer

  • to that question.

  • Let's just comment out alignment and let's watch

  • cohesion happen.

  • We can see they start to group together.

  • This is cohesion happening.

  • And that neighbor radius, by the way,

  • is a super important value.

  • Right?

  • That neighbor radius, if I were to change that

  • and I have a different, that perception radius,

  • if I were to change that to 10,000

  • they're all going to come together as one.

  • Because they all see everybody.

  • If I were to change that to 10,

  • there's really little pairs.

  • You can see they kind of get in groups of two.

  • The force though isn't so strong.

  • So if I were to change the maximum force to one,

  • now you see them, almost like these little

  • electron thingies that start, magnetic things

  • that start to spin around each other and then fan off.

  • There's so many possibilities here.

  • I could make the maximum speed two.

  • I'm not actually limiting it, you can see.

  • There you go, that's what I was looking for.

  • They get into these little groups

  • but let me go back and say maximum speed is four.

  • Maximum force is 22 and I realize

  • this maximum speed is not actually a maximum speed

  • because in update if I really wanted it to be

  • a maximum speed, I would want to say

  • this.velocity limit this.maxSpeed.

  • Let's actually add that in and see what we get.

  • Oh, and let's put the perception radius back

  • at 100 and there we go.

  • Now we have cohesion.

  • We have cohesion, what happens if we have cohesion

  • and alignment?

  • How do we get cohesion and alignment?

  • Okay, this is fun.

  • This is really working.

  • This is going to be a really long video.

  • All right, cohesion and alignment.

  • The problem rests here.

  • This is actually a really easy thing to solve

  • because this is called force,

  • the answer here is force accumulation.

  • In physics, if two forces are acting on an object,

  • the resulting acceleration is the sum of those forces.

  • So all I need to do is say acceleration.add alignment

  • and acceleration.add cohesion because

  • why is this not falling?

  • Because the force of gravity is pointed this way

  • and the other force of my hand holding it up

  • is pointed in the other direction with an equal magnitude.

  • Therefore, it is at rest.

  • Those two forces added together have a net acceleration

  • of zero.

  • But obviously, all you do is add them together

  • but there is this, if I actually do this right now,

  • we're going to see some really crazy behavior.

  • Looks like it's kind of working but it really isn't

  • because what it should also have is this acceleration

  • shouldn't accumulate over time.

  • Every moment in time, I start with a net acceleration

  • of zero and add all the forces together.

  • So what I should do right before I flock is say

  • this.acceleration set zero, zero.

  • This is setting its values to zero and zero.

  • Another way I could do that is just multiplying

  • it by zero, because multiplying a vector by zero

  • takes everything out.

  • It could make a new vector, whatever.

  • But this will work nicely and technically,

  • this also might make more sense here

  • because it's kind of like after I finish updating

  • the velocity then I can reset the acceleration

  • in case there's other things at play.

  • Now we can see both cohesion and alignment

  • are at play, look at this.

  • Eventually, they're just all going to become this one clump.

  • Come on, you can catch up, you got it, go go!

  • Go, catch up!

  • Yeah! One clump.

  • One thing I would like to do.

  • I really want to attach sliders to those parameters

  • but I got to resist because we got to have separation.

  • We've got alignment.

  • (bell rings) And cohesion.

  • (bell rings)

  • Separation, this one is a little bit hard.

  • Steer to avoid crowding local flockmates.

  • Here's the me.

  • That's me, here's my local flockmate.

  • If this local flockmate is too close, it's within

  • some sort of distance threshold, I want to steer

  • to avoid that.

  • What would be my desired velocity?

  • My desired velocity would be to move

  • in the opposite direction.

  • So the idea is my desired velocity is in the observable.

  • What if there is one here?

  • My desired velocity would also be opposite direction

  • so then my next desired velocity would be

  • the average of these two.

  • It even would make sense for the magnitude of that

  • desired velocity to be proportional to the distance.

  • So if this one is here, I maybe want to avoid it

  • but I only really need to think about avoiding it

  • a little bit.

  • If this one's really close, I need to avoid it a lot.

  • So the magnitude of the vector is inversely proportional

  • to the distance of the local flockmate.

  • This might end up with kind of like a desired velocity

  • of something like this if I'm averaging those two together.

  • So let's see if we can implement that.

  • Not going to be super easy but let's do it.

  • The nice thing is I'm going to start with cohesion

  • because we've kind of,

  • cohesion is close to separation.

  • Again, boy.

  • (pop music)

  • ♪ I will refactor this later

  • You know I will refactor this later

  • ♪ I will refactor this later

  • - I should show you that I'm wearing my

  • I will factor this later t-shirt, by the way.

  • Okay, so separation.

  • I'm going to leave this at 100.

  • Steering, I think I might need to use,

  • I like the idea of just having one vector

  • that ends up as, it's sort of the average here

  • then it's the difference but I think I need

  • to think, one step at a time.

  • I'm creating this vector.

  • This is the same.

  • If it's not me and it's within the perception radius,

  • what do I need?

  • I need a vector, I'm going to call it difference

  • which is the other's.

  • I want a vector, sorry, that points from

  • the other to me.

  • Because I want that to go to the opposite direction.

  • So it's my position minus the other.

  • I want a vector, the new vector which is

  • the subtraction between my position

  • and the other's position.

  • This is a way of calling the subtract function

  • which doesn't operate on a vector

  • but subtracts two vectors and gives us

  • a new vector which is difference.

  • Then, I want the difference to be inversely

  • proportional to the distance

  • so I'm going to multiply it or set its magnitude

  • something like that.

  • Or divide by distance.

  • I could just say divide by distance, right?

  • It's inversely proportional, the farther away

  • it is, the lower the magnitude.

  • The closer, the higher the magnitude.

  • The distance, I guess technically that distance

  • could be zero and that would be problematic

  • but with floating point math rule,

  • it's never going to get a value of zero.

  • Then I want to add.

  • Then that is the thing I add up.

  • That's the thing I want to average.

  • All those vectors that are pointed away

  • from the things near me, I think we're actually

  • in pretty good shape here.

  • This isn't as hard as I thought.

  • This was the tricky part here.

  • Get a vector that's pointing away

  • from the local flockmate, make it inversely proportional

  • to the distance, add it up, then this is the same.

  • Because, oh no.

  • I don't need to subtract position.

  • I kind of don't know if I want to.

  • Let's leave this whole always go at maximum speed thing

  • and then the rest is the same.

  • Once I have that, just average it,

  • set it to max speed, subtract out the velocity

  • and limit it to maximum force and that's the end.

  • Okay, just to be sure this is right,

  • let's go into Boid.

  • Let's go to flock.

  • Let's add let separation equal this.separation

  • and then let's add in separation.

  • I want to not bother adding cohesion and alignment.

  • Are these separating?

  • Yeah, it looks like they are.

  • It looks like when they get close to each other,

  • they're kind of moving away from each other.

  • Weird, weird behavior.

  • This doesn't look like exactly what I expected.

  • Let's give the maximum force, like really let

  • them be super strong about separating.

  • There we go.

  • So that's more like what I expected.

  • That's way too strong.

  • Yeah.

  • Why do they feel like they're, something is weird here.

  • Let's give it a much smaller perception radius.

  • Oh yeah, this is what I expected to see

  • and I think it wasn't really a bug in my code.

  • It's sort of a bug in my conceptual sense

  • of how this works.

  • This is pretty good, this is what I want to see.

  • This is separation only.

  • Now, we should be able to see and let's

  • I'm going to do this really quickly.

  • I'm going to not be thoughtful at all about the interface

  • but just so I can debug this effectively,

  • I am going to go into sketch.js.

  • I am going to say let alignSlider, cohesionSlider

  • and separationSlider.

  • AlignSlider equals createSlider.

  • I'm going to give it a range between one and five.

  • Actually, let's give it a range between zero,

  • sorry, zero and five, with a starting value of one

  • and increment value of 0.1.

  • I want to do this for all of these sliders.

  • Then in Boid, I want to say where I call in flock,

  • I want to just scale them.

  • I'm going to say separation.multiply separationSlider.value.

  • I'm going to do this also for cohesion and for alignment.

  • Now, by the way.

  • (bell rings)

  • (train whistle blows)

  • Flocking.

  • This is now the flocking simulation.

  • I didn't draw them as triangles.

  • They're not rotating, but this is the flocking simulation.

  • But just for the sake of argument, this should be

  • what order did I make them in?

  • They're not labeled.

  • So let's just turn all the way up to separation.

  • Oh boy.

  • Let's turn off.

  • This is only separation.

  • Now, turn that off.

  • Let's turn on only alignment.

  • Whoa, that's oh they're just the alignment

  • is so strong.

  • When the alignment is so strong, it makes them

  • go in circles around each other.

  • I have to talk about why.

  • Why did that happen that they were twirling in circles?

  • Well, look at this.

  • I'm updating them one at a time.

  • The next one is flocking with the previous ones

  • updated value, right?

  • Instead of taking a snapshot of all their current

  • velocities and then each one updates based

  • on that snapshot, I start with a set of velocities.

  • I update the first one based on the set of velocities

  • and now its new velocity is there.

  • So when the next one, it's actually updating itself

  • based on the previous one that I updated

  • which is not how the world works.

  • It's not how time works.

  • That's causing them to ripple into each other

  • based on the order and go in circles.

  • That is something that you might want to consider fixing.

  • I'm not going to fix that but now I can really play

  • with these, I can get different sort of qualities

  • of flocking based on how strong I make these rules.

  • So no alignment, a lot of cohesion.

  • No separation, add a lot of separation.

  • Less cohesion.

  • Separation, I shouldn't make too strong

  • but it's important and then let's add some alignment

  • back in and there we go.

  • Flocking.

  • (bell rings)

  • Okay, this coding challenge is complete.

  • It is done.

  • I will be uploading the code, if you want you can

  • look at the video description to find the code

  • as a snapshot, that's exactly what it is.

  • Let's go make a list.

  • Let's make a list on the board of things

  • you might try to do.

  • Number one, snapshot of all velocities.

  • That's going to be my code for that.

  • Number two, an optimization you can do

  • is called spatial subdivision or a quadtree optimization.

  • One of the things that makes this really slow.

  • And I'll just show you here for a second,

  • is if I were to try to do this with a 1,000 Boids.

  • Look how slow this is.

  • But you have to realize, it's not a big deal

  • for p5 to draw 1,000 things moving.

  • Why it's fine drawing 1,000 things moving

  • but as soon as I put the flock function in,

  • it's super slow.

  • The reason that is is because it's got to do

  • and every Boid, check every Boid against every

  • other Boid check.

  • So there's a way of subdividing the space,

  • spatial subdivision into bins or buckets

  • and have the Boids only check ones that are near

  • each other in the same buckets.

  • That's called, I probably have a lot of distance

  • calculation, I could reduce the number of times

  • I call it.

  • But for this, quadtree or just simple spatial subdivision.

  • These are things you could try.

  • These are just code refactoring, really.

  • Actually, the other thing you could try

  • is really build a more sophisticated interface

  • and there's lots of other parameters that you

  • could control and try.

  • There's the perception radius, there's maximum force,

  • there's maximum speed.

  • Each rule could have a different perception radius.

  • So many possibilities.

  • This is the stuff that you could do to really

  • control and tweak all the values and play with it.

  • Another thing you could do is just design,

  • visual design, like are they triangles,

  • circles, are they flapping butterflies.

  • How do you, design of the Boids?

  • Make your own beautiful visualization of how you

  • design and draw them.

  • Tadpole-like, there's so many ideas there.

  • Another thing you could try to do is 3D.

  • Can you extrapolate this into 3D?

  • If you see the word quaternian anywhere you start

  • to research, you might want to turn back.

  • But you could extrapolate this into 3D.

  • Another suggestion which came from the chat,

  • thank you, is Boids with different parameters.

  • There's no reason why every Boid has to have

  • the same max speed or maximum force.

  • Or same perception radius.

  • You could implement this thing called the view rule.

  • There's two things about this one.

  • Each Boid can see everything around it.

  • But what if the Boid itself could actually only

  • see if it's moving in this direction,

  • things that are within its particular view in front of it.

  • Maybe it doesn't deal with anything that's behind it.

  • It doesn't try to do cohesion or alignment or separation.

  • Then the view rule which is posited by Flake

  • in the book A Computational Beauty of Nature,

  • I'll link to that book also.

  • It's a wonderful book with tons,

  • probably like 90% of the things on my channel

  • are all from the Computational Beauty of Nature book.

  • But Flake posits this additional rule

  • that you always want to keep your view empty.

  • So if there is a Boid in front of you,

  • you want to steer that way to keep your view empty,

  • clear and this might result, the theory is

  • that this will result in something that looks

  • more like that pattern that you'll see of

  • actual birds flocking where they kind of

  • appear almost in this triangular pattern.

  • What's interesting about this, you see a pattern

  • like this, you think, "Ah ha."

  • This is a top-down behavior.

  • There is a leader, this bird, who is saying

  • to all the other birds, "Fan out from me and follow me."

  • But yet, this type of intelligent behavior

  • emergent phenomena can come actually only from

  • simple, local rules of interaction.

  • This by the way, this is what this whole video

  • was about, if you didn't gather that already.

  • Other ideas might be adding obstacles,

  • other forces, there's thinking about these

  • living in a world where they're interacting

  • with other things, maybe there's a predator

  • that comes in and is chasing them.

  • All sorts of unique and interesting possibilities.

  • So thank you everyone for watching this coding challenge.

  • Put your other ideas for things people could do

  • in the comments and then if you make your own

  • version of this flocking code, please go to the

  • codingtrain.com challenge page and submit a link

  • to what you made.

  • I will also make a version of this that runs

  • in the web editor and link to all those resources

  • like the Craig Reynolds original paper

  • about flocking, the video on YouTube and the chapter

  • in my Nature of Code book, okay?

  • Thanks for watching this coding challenge

  • on flocking and I'll see you soon.

  • (train whistle blows)

  • (upbeat music)

(train whistle blows)

字幕與單字

單字即點即查 點擊單字可以查詢單字解釋

B1 中級

編碼挑戰#124:蜂群模擬。 (Coding Challenge #124: Flocking Simulation)

  • 3 0
    林宜悉 發佈於 2021 年 01 月 14 日
影片單字