mirror of
https://github.com/nature-of-code/noc-book-2
synced 2024-11-16 07:47:48 +01:00
commit
773472c478
4 changed files with 21 additions and 21 deletions
|
@ -42,6 +42,7 @@
|
|||
<p>You draw a shape at position <code>x</code>. With each frame of animation, you increment the value of <code>x</code>, redraw the shape, and voilà—the illusion of motion! Maybe you took it a step or two further and included a <code>y</code> position, as well as variables for speed along the x- and y-axes:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">x = x + xspeed;
|
||||
y = y + yspeed;</pre>
|
||||
<p style="letter-spacing : 0.1">Part 1 of this story will take this idea even further. After exploring how to use different flavors of randomness to drive an object’s motion (<strong>Chapter 0</strong>), I’m going to take these <code>xspeed</code> and <code>yspeed</code> variables and demonstrate how together they form a vector (<strong>Chapter 1</strong>). You won’t get any new functionality out of this, but it will build a solid foundation for programming motion in the rest of the book.</p>
|
||||
<p>Once you know a little something about vectors, you’re going to quickly realize that a force (<strong>Chapter 2</strong>) is a vector. Kick a soccer ball and you’re applying a force. What does a force cause an object to do? According to Sir Isaac Newton, force equals mass times acceleration, so that force causes an object to accelerate. Modeling forces will allow you to create systems with dynamic motion, in which objects move according to a variety of rules.</p>
|
||||
<p>Now, that soccer ball to which you applied a force might have also been spinning. If an object moves according to its <em>linear</em> acceleration, it can spin according to its <em>angular</em> acceleration (<strong>Chapter 3</strong>). Understanding the basics of angles and trigonometry will allow you to model rotating objects as well as grasp the principles behind oscillating motion, like a pendulum swinging or a spring bouncing.</p>
|
||||
<p>Once you’ve tackled the basics of motion and forces for an individual inanimate object, I’ll show you how to make thousands upon thousands of those objects and manage them as a single unit called a <em>particle system</em> (<strong>Chapter 4</strong>). Particle systems are also a good excuse to look at some additional features of object-oriented programming—namely, inheritance and polymorphism.</p>
|
||||
|
|
|
@ -676,9 +676,9 @@ class Cat extends Animal {
|
|||
}
|
||||
|
||||
eat() {
|
||||
// Call <code>eat()</code> from <code>Animal</code>. A child can execute a function from the parent while adding its own code.
|
||||
// Call <code>eat()</code> from <code>Animal</code>. A child can execute a function from the parent.
|
||||
<strong>super.eat();</strong>
|
||||
// Add some additional code for a dog’s specific eating characteristics.
|
||||
// Additional code for a dog’s specific eating characteristics.
|
||||
<strong>print("Woof!!!");</strong>
|
||||
}
|
||||
|
||||
|
@ -1139,10 +1139,10 @@ class Repeller {
|
|||
}
|
||||
|
||||
repel(particle) {
|
||||
// {!1}
|
||||
// {!2}
|
||||
let force = p5.Vector.sub(this.position, particle.position);
|
||||
// {.continue.bottom-align} This is the same repel algorithm from Chapter 2: forces based on gravitational attraction.
|
||||
let distance = force.mag();
|
||||
// {.continue.bottom-align} This is the same repel algorithm from Chapter 2: forces based on gravitational attraction.
|
||||
distance = constrain(distance, 5, 50);
|
||||
let strength = -1 * this.power / (distance * distance);
|
||||
force.setMag(strength);
|
||||
|
@ -1187,11 +1187,13 @@ class Repeller {
|
|||
</div>
|
||||
<p>First, declare a variable to store the image:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">let img;</pre>
|
||||
<p>Then, load the image in <code>preload()</code>:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">function preload() {
|
||||
<div class="avoid-break">
|
||||
<p>Then, load the image in <code>preload()</code>:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">function preload() {
|
||||
// Load the PNG.
|
||||
img = loadImage("texture.png");
|
||||
}</pre>
|
||||
</div>
|
||||
<p>Next, when it comes time to draw the particle, use the <code>img</code> variable instead of drawing a circle or rectangle:</p>
|
||||
<pre class="codesplit" data-code-language="javascript"> show() {
|
||||
imageMode(CENTER);
|
||||
|
|
|
@ -286,7 +286,6 @@ desired.setMag(this.maxspeed);</pre>
|
|||
<div class="avoid-break">
|
||||
<pre class="codesplit" data-code-language="javascript"> arrive(target) {
|
||||
let desired = p5.Vector.sub(target, this.position);
|
||||
|
||||
//{!1} The distance is the magnitude of
|
||||
// the vector pointing from
|
||||
// the position to the target.
|
||||
|
@ -531,9 +530,7 @@ let row = floor(this.position.y / this.resolution);</pre>
|
|||
<figcaption></figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="snip-below">
|
||||
<pre class="codesplit" data-code-language="javascript">class Vehicle {
|
||||
follow(flow) {
|
||||
<pre class="codesplit" data-code-language="javascript"> follow(flow) {
|
||||
// What is the vector at that spot in the flow field?
|
||||
let desired = flow.lookup(this.position);
|
||||
desired.setMag(this.maxspeed);
|
||||
|
@ -542,7 +539,6 @@ let row = floor(this.position.y / this.resolution);</pre>
|
|||
steer.limit(this.maxforce);
|
||||
this.applyForce(steer);
|
||||
}</pre>
|
||||
</div>
|
||||
<p>Notice that <code>lookup()</code> is a method of the <code>FlowField</code> class, rather than of <code>Vehicle</code>. While you certainly could place <code>lookup()</code> within the <code>Vehicle</code> class instead, from my perspective, placing it in <code>FlowField</code> aligns best with the OOP principle of encapsulation. The lookup task, which retrieves a vector based on a position from the flow field, is inherently tied to the data of the <code>FlowField</code> object.</p>
|
||||
<p>You may also notice some familiar elements from <a href="/particles#section-particles">Chapter 4</a>, such as the use of an array of vehicles. Although the vehicles here operate independently, this is a great first step toward thinking about the group behaviors that I’ll introduce later in this chapter.</p>
|
||||
<div data-type="exercise">
|
||||
|
@ -948,7 +944,7 @@ for (let i = 0; i < path.points.length - 1; i++) {
|
|||
<li><strong>Simple units operate in parallel.</strong> For every cycle through the <code>draw()</code> loop, each unit will calculate its own steering forces. This will create the appearance of all the units working in parallel.</li>
|
||||
<li><strong>Systems as a whole exhibit emergent phenomena.</strong> Complex behaviors, patterns, and intelligence can emerge from the interactions among simple units. This phenomenon occurs in nature, such as in ant colonies, migration patterns, earthquakes, and snowflakes. The question is whether the same results can be achieved in a p5.js sketch.</li>
|
||||
</ul>
|
||||
<p>Beyond these core principles, three additional qualities of complex systems will help frame the discussion, as well as provide guidelines for features to include in a software simulation. It’s important to acknowledge that this is a fuzzy set of characteristics, and not all complex systems have all of them:</p>
|
||||
<p>Beyond these core principles, three additional qualities of complex systems will help frame the discussion, as well as provide guidelines for features to include in a software simulation. It’s important to acknowledge that this is a fuzzy set of characteristics, and not all complex systems have all<br>of them:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Nonlinearity:</strong> This aspect of complex systems is often casually referred to as the <em>butterfly effect</em>, coined by mathematician and meteorologist Edward Norton Lorenz, a pioneer in the study of chaos theory. In 1961, Lorenz was running a computer weather simulation for the second time and, perhaps to save a little time, typed in a starting value of 0.506 instead of 0.506127. The end result was completely different from the first result of the simulation.
|
||||
|
@ -989,8 +985,9 @@ function setup() {
|
|||
<p>That looks good but is not quite right. What’s missing? In the case of <code>seek()</code>, I said, “Seek <code>mouseX</code> and <code>mouseY</code>.” In the case of <code>separate()</code>, I’m saying, “Separate from <em>everyone else</em>.” Who is everyone else? It’s the list of all the other vehicles:</p>
|
||||
<pre class="codesplit" data-code-language="javascript"> vehicle.separate(vehicles);</pre>
|
||||
<p>This is the big leap beyond what you saw before with particle systems. Instead of each element (particle or vehicle) operating on its own, I’m now saying, “Hey you, that vehicle there! When it comes time for you to operate, you need to operate with an awareness of everyone else. So I’m going to go ahead and pass you the list of everyone else.”</p>
|
||||
<p>Putting together what I’ve done so far, here are the <code>setup()</code> and <code>draw()</code> functions for a sketch that exhibits group behavior:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">let vehicles;
|
||||
<div class="avoid-break">
|
||||
<p>Putting together what I’ve done so far, here are the <code>setup()</code> and <code>draw()</code> functions for a sketch that exhibits group behavior:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">let vehicles;
|
||||
|
||||
function setup() {
|
||||
createCanvas(640, 240);
|
||||
|
@ -1012,6 +1009,7 @@ function draw() {
|
|||
vehicle.show();
|
||||
}
|
||||
}</pre>
|
||||
</div>
|
||||
<div class="half-width-right">
|
||||
<figure>
|
||||
<img src="images/05_steering/05_steering_34.png" alt="Figure 5.32: The desired velocity for separation (equivalent to fleeing) is a vector that points in the opposite direction of a target.">
|
||||
|
@ -1033,12 +1031,9 @@ function draw() {
|
|||
<p>Inside this method, I’ll loop through all the vehicles and see if any are too close:</p>
|
||||
<pre class="codesplit" data-code-language="javascript"> // This variable specifies how close is too close.
|
||||
let desiredSeparation = 20;
|
||||
|
||||
for (let other of vehicles) {
|
||||
|
||||
//{!1 .offset} What is the distance between this vehicle and the other vehicle?
|
||||
let d = p5.Vector.dist(this.position, other.position);
|
||||
|
||||
if (this !== other && d < desiredSeparation) {
|
||||
//{!1} Any code here will be executed if the vehicle is within 20 pixels.
|
||||
|
||||
|
|
|
@ -784,7 +784,7 @@ Composite.add(engine.world, constraint);</pre>
|
|||
let body = Bodies.rectangle(x, y, w, h);
|
||||
Composite.add(engine.world, body);</pre>
|
||||
<p>Next, I can create the constraint. With a <code>length</code> of <code>0</code>, it needs a <code>stiffness</code> of <code>1</code>; otherwise, the constraint may not be stable enough to keep the body connected at the anchor point:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">// The constraint connects the body to a fixed (<em>x</em>, <em>y</em>) position with a length of 0 and stiffness of 1.
|
||||
<pre class="codesplit" data-code-language="javascript">// The constraint connects the body to a fixed (<code>x</code>, <code>y</code>) position with a length of 0 and stiffness of 1.
|
||||
let options = {
|
||||
bodyA: this.body,
|
||||
pointB: { x: x, y: y },
|
||||
|
@ -1620,7 +1620,7 @@ function draw() {
|
|||
<li><strong>Connections aren’t repeated in reverse.</strong> For example, if 0 is connected to 1, I don’t need to explicitly say that 1 is also connected to 0. I already know this, based on the definition of how a spring works!</li>
|
||||
</ul>
|
||||
<p>How do I write the code to make these connections for <em>N</em> particles? Look at the four columns illustrated in Figure 6.19. They iterate all the connections starting from particles 0 up to 3. This tells me that I need to access each particle in the list from 0 to <em>N</em> – 1:</p>
|
||||
<div class="snip-below">
|
||||
<div class="snip-below avoid-break">
|
||||
<pre class="codesplit" data-code-language="javascript"> for (let i = 0; i < this.particles.length - 1; i++) {
|
||||
// Use the variable <code>particle_i</code> to store the particle reference.
|
||||
let particle_i = this.particles[i];</pre>
|
||||
|
@ -1690,8 +1690,9 @@ let behavior = new AttractionBehavior(particle, distance, strength);</pre>
|
|||
<p>Finally, in order for the force to be activated, the behavior needs to be added to the physics world:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">physics.addBehavior(behavior);</pre>
|
||||
<p>Now everything that lives in the physics simulation will always be attracted to that particle, as long as it’s within the distance threshold.</p>
|
||||
<p>The <code>AttractionBehavior</code> class is a very powerful tool. For example, even though Toxiclibs.js doesn’t automatically handle collisions like Matter.js does, you can create a collision-like simulation by adding an <code>AttractionBehavior</code> with a negative strength—a repulsive behavior—to each and every particle. If the force is strong and activated only within a short range (scaled to the particle’s radius), the result is much like a rigid-body collision. Here’s how to modify the <code>Particle</code> class to do this:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">class Particle extends VerletParticle2D {
|
||||
<div class="avoid-break">
|
||||
<p>The <code>AttractionBehavior</code> class is a very powerful tool. For example, even though Toxiclibs.js doesn’t automatically handle collisions like Matter.js does, you can create a collision-like simulation by adding an <code>AttractionBehavior</code> with a negative strength—a repulsive behavior—to each and every particle. If the force is strong and activated only within a short range (scaled to the particle’s radius), the result is much like a rigid-body collision. Here’s how to modify the <code>Particle</code> class to do this:</p>
|
||||
<pre class="codesplit" data-code-language="javascript">class Particle extends VerletParticle2D {
|
||||
constructor(x, y, r) {
|
||||
super(x, y);
|
||||
this.r = r;
|
||||
|
@ -1708,6 +1709,7 @@ let behavior = new AttractionBehavior(particle, distance, strength);</pre>
|
|||
circle(this.x, this.y, this.r * 2);
|
||||
}
|
||||
}</pre>
|
||||
</div>
|
||||
<p>I can now remake the attraction example from <a href="/forces#section-forces">Chapter 2</a> with a single <code>Attractor</code> object that exerts an attraction behavior anywhere on the canvas. Even though the attractor is centered, I’m using a distance threshold of the full <code>width</code> to account for any movement of the attractor, and for particles located outside the canvas boundaries.</p>
|
||||
<div data-type="example">
|
||||
<h3 id="example-615-attraction-and-repulsion-behaviors">Example 6.15: Attraction (and Repulsion) Behaviors</h3>
|
||||
|
|
Loading…
Reference in a new issue