You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
<p> The aim of this workshop is to recreate boids, which are a simulation of birds and other flocking entities (like fishes).
47
62
Hopefully, you'll be able to apply this in your own projects!
48
-
Along the way, we'll learn a little bit about p5.js, rendering, and physics.</p>
63
+
Along the way, we'll learn a little bit about p5.js, rendering, and simulating physics. </p>
49
64
50
65
<divid="BoidContainer"></div>
51
66
@@ -70,13 +85,19 @@ <h2> Introduction to p5.js </h2>
70
85
</ol>
71
86
72
87
<h3> What is a sketch? </h3>
73
-
<emph>Insert brief description (couple lines) on what a sketch is, and what frames are... </emph>
88
+
89
+
<p>A sketch is a small program that will work with p5.js to produce a simulation.
90
+
Just like a game, your simulation will have frames (an image of your simulation) and p5.js will aim to show 60 frames a second.
91
+
You'll be able to code what happens just before a frame is shown, and also code what happens just before the simulation starts!
92
+
</p>
93
+
94
+
<br>
74
95
75
96
<p> Your new sketch should have 2 functions: </p>
76
97
77
98
<ul>
78
99
<li><strong> setup() </strong> which is a function executed once at the start </li>
79
-
<li><strong> draw() </strong> which is a function executed every frame (by default, about 60 times a second) </li>
100
+
<li><strong> draw() </strong> which is a function executed every frame </li>
80
101
</ul>
81
102
82
103
We create the canvas (arguments being the width and length) in <strong> setup() </strong> since we only need to do this once, and <strong>background()</strong> (which you can call with RGB colour arguments as below) is called every frame in <strong> draw()</strong>!
@@ -179,7 +200,38 @@ <h2> Using Arrays and Objects </h2>
179
200
<li>An object representing a circle, which stores the position and size of the circle </li>
180
201
</ul>
181
202
182
-
<p> Let's create a class for that object first! </p>
203
+
<p> Let's get the growing circle part out of way; this part isn't important, but the rest of the code makes less sense without it: </p>
204
+
205
+
<pre>
206
+
<codeclass="language-js">
207
+
function setup() {
208
+
createCanvas(400, 400);
209
+
}
210
+
211
+
const maxCircleSize = 15;
212
+
const minCircleSize = 5;
213
+
let circleSize = 5;
214
+
215
+
function draw() {
216
+
background(222, 222, 222);
217
+
218
+
// update circleSize
219
+
circleSize++;
220
+
221
+
if(circleSize > maxCircleSize){
222
+
circleSize = minCircleSize;
223
+
}
224
+
225
+
// draw circle
226
+
circle(mouseX, mouseY, circleSize);
227
+
}
228
+
</code>
229
+
</pre>
230
+
<em>Could also use modulo to loop the circleSize, but the if statement is more readable here</em>
231
+
232
+
<p>Don't worry about how this is performed; it's more important that you understand how objects, classes, and arrays work in JavaScript. </p>
233
+
234
+
<p> Now let's create a class for the circle! </p>
183
235
184
236
<h3> Coding the Class </h3>
185
237
@@ -244,7 +296,6 @@ <h3> Storing the trail </h3>
244
296
245
297
<p>We can declare an array for the trail and a trail length pretty easily: </p>
246
298
247
-
248
299
<pre>
249
300
<codeclass="language-js">
250
301
const trails = [];
@@ -255,13 +306,17 @@ <h3> Storing the trail </h3>
255
306
<p> Note <strong>const</strong> here doesn't mean an uneditable array; you can insert and remove from the array, but you can't reassign the <em>trails</em> variable to something else.
256
307
It will forever be that array, but you can edit the array as you wish. </p>
257
308
258
-
<p> Now let's create a method updateTrail to add the current circle to the trail; we'll have a variable <em>circleSize</em>for the current size of the circle that we can reference. </p>
309
+
<p> Now let's create a method updateTrail to add the current circle to the trail; remember that we have <em>circleSize</em>from the growing circle part of the sketch. </p>
259
310
260
311
<br>
261
312
<p>Our method will need to:</p>
262
313
<ul>
263
-
<li></li>
314
+
<li>Create a new trail object, and add it to the array</li>
315
+
<li>If the array is full, then remove the oldest circle in the array</li>
264
316
</ul>
317
+
318
+
<p> I'll arbitrarily decide that the circles will be added to the end of the array, so therefore the oldest circles will be in the front of the array. Here's one implementation of the specification above:</p>
319
+
265
320
<pre>
266
321
<codeclass="language-js">
267
322
function updateTrail(){
@@ -276,17 +331,147 @@ <h3> Storing the trail </h3>
276
331
</code>
277
332
</pre>
278
333
279
-
<p>You can see JavaScript's array methods <ahref="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift">here</a></p>
334
+
<p>This might be a little confusing because of the use of <em>shift()</em> and <em>push()</em>; they're just JavaScript methods to make our lives easier.
335
+
336
+
You can see JavaScript's array methods <ahref="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift">here</a>!
337
+
</p>
338
+
339
+
<p> We do need to call updateTrail() in draw; let's call it just after we draw the current circle. </p>
340
+
341
+
<pre>
342
+
<codeclass="language-js">
343
+
function draw() {
344
+
background(222, 222, 222);
345
+
346
+
// update circleSize
347
+
circleSize++;
348
+
349
+
if(circleSize > maxCircleSize){
350
+
circleSize = minCircleSize;
351
+
}
352
+
353
+
// draw circle
354
+
circle(mouseX, mouseY, circleSize);
355
+
356
+
// update stuff
357
+
updateTrail();
358
+
}
359
+
</code>
360
+
</pre>
361
+
<em>Our updated draw method</em>
362
+
363
+
<br>
364
+
365
+
<p> Okay great, now we just need to display the trail! </p>
366
+
367
+
<h3> Displaying the trail</h3>
280
368
281
-
Useful: use framerate to debug
369
+
<p>All we have to do is loop through our array of trail circles and display each one. I want to draw the oldest first, so I'll iterate from the start to the end of the array. </p>
370
+
371
+
<pre>
372
+
<codeclass="language-js">
373
+
function drawTrail(){
374
+
for(let i = 0; i < trails.length; i++){
375
+
let trailObj = trails[i];
376
+
377
+
// draw the circle!
378
+
trailObj.draw();
379
+
}
380
+
}
381
+
</code>
382
+
</pre>
383
+
384
+
<p> And now we'll call <em>drawTrail()</em> before the current circle (so we don't draw the trail over the current circle!) </p>
385
+
386
+
<pre>
387
+
<codeclass="language-js">
388
+
function draw() {
389
+
background(222, 222, 222); // background drawn every frame!
390
+
391
+
// update circleSize
392
+
circleSize++;
393
+
394
+
if(circleSize > maxCircleSize){
395
+
circleSize = minCircleSize;
396
+
}
397
+
398
+
// draw stuff
399
+
drawTrail();
400
+
circle(mouseX, mouseY, circleSize);
401
+
402
+
// update stuff
403
+
updateTrail();
404
+
}
405
+
</code>
406
+
</pre>
407
+
<em>Our final version of draw()!</em>
408
+
409
+
<h3> Full Code for Growing Trails </h3>
410
+
411
+
<p> You can have a look at the full program <ahref="https://editor.p5js.org/RexMortem/sketches/hJOgrWgry">here</a></p>
282
412
283
413
<h3> Task: Rainbow Trails </h3>
284
414
415
+
<p> With the help of the Growing Trails code, modify your solution to the task Multicolour to create rainbow trails. </p>
416
+
417
+
<p> You should keep the colour changing code, and store the previous 10 or so circles! Your simulation should look something like this:</p>
418
+
285
419
<divid="RainbowTrailsContainer"></div>
286
420
421
+
<p><strong>Tip:</strong> Once you've finished your sketch, sometimes it's a little hard to see whether the trail circles have the colour and position that they should. You can verify this more easily by slowing down your simulation! Use <em>frameRate(10)</em> in setup to set your sketch to run at 10 frames per second. </p>
422
+
287
423
<h2> May the Force be with you </h2>
288
424
289
-
<p> To make our boids move, </p>
425
+
<p> To make our boids move, we're going to use forces. We know from physics that $F=ma$, but we're going to simplify things by ignoring mass and jump straight to changing acceleration! </p>
426
+
427
+
<p> Let's try modelling a simple force; a constant acceleration to the right. </p>
428
+
429
+
<h3> Accelerating Ball </h3>
430
+
431
+
<p> First, let's establish a class for this ball to make things clear to ourselves. </p>
432
+
433
+
<pre>
434
+
<codeclass="language-js">
435
+
class Ball {
436
+
constructor(){
437
+
this.position = createVector(0,0);
438
+
this.velocity = createVector(0,0);
439
+
this.acceleration = createVector(0,0);
440
+
}
441
+
442
+
draw(){
443
+
circle(this.position.x, this.position.y, 10);
444
+
}
445
+
}
446
+
</code>
447
+
</pre>
448
+
449
+
<p> This is all stuff we've basically done, but we also need a method to simulate all the physics.
450
+
From physics, we know that acceleration is the rate of change of velocity, and that velocity is the rate of change of position.
451
+
</p>
452
+
453
+
<br>
454
+
455
+
<p> So to simulate our ball's motion, we would: </p>
456
+
457
+
<ol>
458
+
<li> Update the velocity with acceleration </li>
459
+
<li> Update the position with velocity </li>
460
+
</ol>
461
+
462
+
<p> As we'll soon see, we have to do this in a <em>very careful</em> way. </p>
463
+
464
+
<h3>Enter deltaTime</h3>
465
+
466
+
<p>Until now, we've not really considered how long each frame takes.
467
+
It'd be reasonable to assume each frame takes about 1/60 seconds to process <em>however</em> each frame takes a slightly different amount of time and, when simulations get more complex, each frame might take a non-slight different amount of time!
468
+
469
+
</p>
470
+
471
+
<divid="WrongFPSBallsContainer"></div>
472
+
473
+
<divid="CorrectFPSBallsContainer"></div>
474
+
290
475
291
476
<ahref="https://p5js.org/reference/p5/deltaTime/"> delta time docs </a>
0 commit comments