Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Learn to Code, Part 2 - Looping Targets
Ryan Palo
Ryan Palo

Posted on • Edited on • Originally published atassertnotmagic.com

     

Learn to Code, Part 2 - Looping Targets

This is part two in my series on Learning to Code. Check outPart 1 if you missed it. Somehow, my wife is still with me after the first one, so let's keep at it! I'm going to drop this excerpt from the previous part here, because I think it's really important to repeat as often as possible.

If you're just starting to learn to program, let me tell you something. Programming is Hard. So please don't feel discouraged or stupid. This tutorial movesreally fast and covers alot, so feeling overwhelmed or not understanding everything on the first pass-through istotally normal. My goal here is to take you through the motions and have you experience the thrill programmers feel of creating something dynamic and awesome out of nothing. Once you're through, ask me every question you can think of until things make more sense. You can email me or send me a Twitter message. Links are in my bio on theabout page. I'll work through the homework with you (since the homework will really help get things solidified some). Just put your head down, get through it, don't give up, and don't feel weird asking for help. If something doesn't make sense, it's not because you're a doofus -- it's because I didn't write well enough. Now. For real this time. Let's get started

Lesson 2: Looping Targets

Congratulations, you survived Lesson 1! Hopefully you're hungry for more. In this lesson, we're going to take what we learned in the previous lesson and cement it in place. We'll takeif statements,assigning variables, andusing functions to the next level. I'll try to mix in a few new cool P5 functions as well. Lastly, we'll get to see our firstloop. This lesson will also work a little different than the previous one in that, instead of getting the final product at the beginning of the project and working through it backwards, we're going to do what programmers often do, which is build something small and slowly increment it. I will, however, give you a little taste of what is to come.

final product teaser

We're going to make it so that when you click on the canvas, a randomly sized target gets drawn, with a random number of rings. These rings should go from black on the outside to white in the center, and get darker linearly.

Step 1: Loops (andnoLoops)

Let's get the hard stuff out of the way up front so you have longer to get familiar with it. Here's our goal for Step 1:

step 1 goal

It might not look that different, but we're removing a lot of our requirements initially. We're taking a big problem, and we're breaking it into a few smaller, easier to solve problems. I (and many others) call this "eating an elephant".

As a programmer, you have to know how to eat elephants. How do you eat an elephant?

One bite at a time, man. One bite at a time.

So what are our requirements for Step 1? We must draw a static, unmoving set of 10 circles in the middle of the canvas. The overall diameter must be 400px. The rings must go from white to black, linearly.

Our First Try

Ok, let's get started with the easy stuff: the setup. We need a canvas, a grayish background, we don't need our drawing looping, and none of the circles really have outlines.

functionsetup(){createCanvas(600,600);background(125);noStroke();noLoop();}
Enter fullscreen modeExit fullscreen mode

The first two lines should look familiar. We wanted a medium gray background, and remember that our grayscale goes from 0 to 255 by default. 125 falls relatively into the middle of that range. The last two functions need only a little introduction.noStroke turns off the borders, andnoLoop makes it so our draw loop doesn't update. If none of our shapes are moving, we can save some CPU cycles if we tell the canvas not to run draw over and over. You're welcome, CPU! We love you!

Cool! So canvas: check. Gray background: check. Static, non-moving drawing: check. What's next? The requirements of the circles. Let's declare some variables to make our lives easier.

functiondraw(){varmaxSize=400;// pixelsvarsteps=10;varsizeStep=maxSize/steps;// We'll use this to define our ring sizesvarcolorStep=255/steps;varcenterX=width/2;// pixelsvarcenterY=height/2;// pixels}
Enter fullscreen modeExit fullscreen mode

We covered all of the above in Lesson 1, so no worries there. You might ask, "Ryan, if we're not using thedraw function to loop (because ofnoLoop), how come we don't just do everything in the setup function and forget the draw function altogether?" You're right! You totally could. From what I can tell, the two are functionally equivalent. I chose to do things this way because it makes more sense in my head to only do "setup-ish" things insetup and "drawing" things indraw. Whatever floats your boat. You're the artist here!

OK, it's finally time to create our circles. Luckily, P5 provides us with theellipse function, which takes anx, y, x width and y height. Since we are geometry geniuses, we know that if we draw an ellipse whose width and height are the same, it's the same as drawing a circle.

functiondraw(){varmaxSize=400;// pixelsvarsteps=10;varsizeStep=maxSize/steps;// We'll use this to define our ring sizesvarcolorStep=255/steps;varcenterX=width/2;// pixelsvarcenterY=height/2;// pixels// Draw the circles, start with the biggest, black one on bottomfill(0);ellipse(centerX,centerY,maxSize,maxSize);fill(1*colorStep);ellipse(centerX,centerY,maxSize-1*sizeStep,maxSize-1*sizeStep);fill(2*colorStep);ellipse(centerX,centerY,maxSize-2*sizeStep,maxSize-2*sizeStep);fill(3*colorStep);ellipse(centerX,centerY,maxSize-3*sizeStep,maxSize-3*sizeStep);// oy vey...  Getting tired of typing yet?// ...}
Enter fullscreen modeExit fullscreen mode

Nope. I refuse to let you copy paste all that code. We're learning about thefor loop.

For Loops

Try this in your console:

for(vari=0;i<10;i++){console.log(i);console.log('Yeeeeeeaaaaaahhhhh boooyyyyyyyy');}
Enter fullscreen modeExit fullscreen mode

Yeah boy loop output

Let's break it down now. This was afor loop. Now you can see why they're called that. We're saying "for each time through this loop, perform the following actions". There are three main parts of afor loop. Theinitialization step, thetest step, and theincrementing step.

Theinitialization step is the first section you come to:

vari=0;
Enter fullscreen modeExit fullscreen mode

Everything here gets run once at the start of the loop. Generally you're just setting the initial looping variable value, but you can do whatever you want here.

Next is thetest step. This runs before every loop. If the test step is true, the loop continues on.

i<10;
Enter fullscreen modeExit fullscreen mode

The first time the computer reads the code, we will have just seti = 0, so — 0 being less than 10 — the loop activates, running everything inside.

After that happens comes theincrementing step.

i++
Enter fullscreen modeExit fullscreen mode

We saw this in Lesson 1 when we were stepping our walker over one pixel. This simply increasesi by 1. Then, we loop back to thetest step. Now,i is equal to 1, which is still less than 10, so the loop happens again. And again and again until afteri equals 9. After that last loop,i is 10, which is notless than 10. Satisfied, the loop exits and we can continue on with our program. Are you beginning to see how we can use this in our program?

Our Second Try

functionsetup(){createCanvas(600,600);background(125);noStroke();noLoop();}functiondraw(){varmaxSize=400;// pixelsvarsteps=10;varsizeStep=maxSize/steps;// We'll use this to define our ring sizesvarcolorStep=255/steps;varx=width/2;// pixelsvary=height/2;// pixels// The new stuff!for(vari=0;i<steps;i++){fill(i*colorStep);// 0, 25.5, 50.0, 75.5 ...varthisSize=maxSize-(i*sizeStep);// 400, 360, 320 ...ellipse(x,y,thisSize,thisSize);}}
Enter fullscreen modeExit fullscreen mode

Make sense? First the loop goes through withi === 0. Thus, we callfill(0) and our fill is black.thisSize gets set to400 - 0 * 40, and our largest circle is 400. And we draw a circle. The next time through,i === 1, so we callfill(25.5) andthisSize === 400 - 1 * 40 === 360, and so forth!

Only one problem, a problem that has plagued programmers since the dawn of programs. Theoff-by-one error. Note that in the last iteration of the loop,i will be equal to 9. Thus, the fill color will be9 * 25.5 which is only229.5! Not255 like we want. Our center dot isoff-white! The humanity! The problem stems from the fact that we have 10 rings, but the first one starts at zero. We're only really steppingnine times. Thus, we need to fix ourcolorStep.

varcolorStep=255/(steps-1);// now our fill will be 0, 28.33, 56.66, 85 ... 255
Enter fullscreen modeExit fullscreen mode

The same issue affects oursizeStep, but in that case, we want that to happen. We want our last circle to have a diameter of 40px, not 0px. If we did want 0px to be our stopping point, we would have to make the last adjustment there as well.

Off-by-one errors are super confusing to get your head around and one of the most common causes of errors, so don't worry if this was a bit dense and confusing. If you have any questions, hit me up and I'll see what I can do to clarify. But for now, we're moving on! ONWARDS!

Step 2: Mouse Clicks and Functions

Now that we've achieved our first milestone goals, let's step up the complexity a bit to try to get closer to our final goal. For this step, we want to draw a target 400px large with 10 rings, — same as before — but we want to draw it only when the user clicks on the canvas, and we want to place itwhere the user clicks on the canvas. Two new goals.

The Mouse Functions/Variables

P5 provides us with a function calledmouseClicked function. This function gets called whenever your mouse clicks the canvas. P5 also provides us with two built-in variablesmouseY andmouseX. You get three guesses for what these contain. Let's take our previous sketch and tweak it a little to see what we're talking about.

// This stuff is all the same as beforefunctionsetup(){createCanvas(600,600);background(125);noStroke();noLoop();}functiondraw(){varmaxSize=400;// pixelsvarsteps=10;varsizeStep=maxSize/steps;// We'll use this to define our ring sizesvarcolorStep=255/(steps-1);varx=width/2;// pixelsvary=height/2;// pixelsfor(vari=0;i<steps;i++){fill(i*colorStep);varthisSize=maxSize-(i*sizeStep);ellipse(x,y,thisSize,thisSize);}}// Here is the new hotnessfunctionmouseClicked(){fill(255);ellipse(mouseX,mouseY,50,50);}
Enter fullscreen modeExit fullscreen mode

Try it out!

our first mouse clicked function

I think you can see where I'm going with this.

Review of Functions

But first, I'm going to do a little cleanup (also known as arefactor) that will make our life marginally easier and, more importantly, help us to reviewfunctions. Let's pull our target drawing out of the draw loop and put it in a function calledcreateTarget.

// ...functiondraw(){createTarget();}functioncreateTarget(){varmaxSize=400;// pixelsvarsteps=10;varsizeStep=maxSize/steps;// We'll use this to define our ring sizesvarcolorStep=255/(steps-1);varx=width/2;// pixelsvary=height/2;// pixelsfor(vari=0;i<steps;i++){fill(i*colorStep);varthisSize=maxSize-(i*sizeStep);ellipse(x,y,thisSize,thisSize);}}
Enter fullscreen modeExit fullscreen mode

See? Nothing too much different. But let's do one better. Let's make the target customizable viaparameters. Remember them? Those are the function inputs that you put inside the parenthesis. Luckily, we have our inputs all stacked at the top of the function so they are easy to spot.

// ...functiondraw(){createTarget(width/2,height/2,400,10);}functioncreateTarget(x,y,maxSize,steps){// calculate what we need from our inputsvarsizeStep=maxSize/steps;varcolorStep=255/(steps-1);for(vari=0;i<steps;i++){fill(i*colorStep);varthisSize=maxSize-(i*sizeStep);ellipse(x,y,thisSize,thisSize);}}
Enter fullscreen modeExit fullscreen mode

Refresh the page, and nothing changes! Great! That's when you know you've had a successfulrefactor. So why did we do this? Because it makes it really easy to do this:

functiondraw(){createTarget(width/2,height/2,400,10);createTarget(100,400,200,5);createTarget(400,400,300,6);}
Enter fullscreen modeExit fullscreen mode

the effects of using functions

Or even:

functiondraw(){for(vari=0;i<5;i++){createTarget(40+i*100,50+i*125,100+i*50,5+i);// Get it?// x = 40, 140, 240, 340, 440// y = 50, 175, 300, 425, 550// maxSize = 100, 150, 200, 250, 300// steps = 5, 6, 7, 8, 9}}
Enter fullscreen modeExit fullscreen mode

a loop inside a function!

To try to clarify what's happening in a function, I'll try to explain it another way, that helped me. When you first define a function, the things you put into parenthesis are like a recipe. You're saying what you expect. Similar to a recipe book saying: get some kind of meat and some kind of vegetable.

functioniAmTheBest(x,y,r){// ...}
Enter fullscreen modeExit fullscreen mode

Then, when you actuallycall the function, it's like you're actually making the recipe.

functiondraw(){iAmTheBest(25,30,50);}
Enter fullscreen modeExit fullscreen mode

The function looks at the things you've passed in and goes, "OK. I'm going to do this withx = 25, y = 30, and r = 50." To carry on with the strained recipe metaphor, it's like you read the recipe and you start cooking with beef and broccoli, but following the steps in the recipe with those options. If you use beef and broccoli, it'll be similar to but not exactly the same as if you were to use chicken and carrots. Have I beat this metaphor to death successfully?

Finishing Up Step 2

Great. Let's wrap up this Step by completing our objectives.

functionsetup(){createCanvas(600,600);background(125);noStroke();noLoop();}functiondraw(){// nothing in here anymore!}functionmouseClicked(){createTarget(mouseX,mouseY,400,10);}functioncreateTarget(x,y,maxSize,steps){varsizeStep=maxSize/steps;varcolorStep=255/(steps-1);for(vari=0;i<steps;i++){fill(i*colorStep);varthisSize=maxSize-(i*sizeStep);ellipse(x,y,thisSize,thisSize);}}
Enter fullscreen modeExit fullscreen mode

step two is complete

Step 3: Adding Randomness

If you remember our initial goal:

We're going to make it so that when you click on the canvas, a randomly sized target gets drawn, with a random number of rings. These rings should go from black on the outside to white in the center, and get darker linearly.

You'll notice that we're pretty much all the way there! We just need to add randomness. Let's make a new function to handle this extra complexity. Thanks to ourcreateTarget function, this new function should be relatively straightforward.

functioncreateRandomTarget(x,y){varmaxSize=floor(random(25,350));varsteps=floor(random(1,10));createTarget(x,y,maxSize,steps);}
Enter fullscreen modeExit fullscreen mode

Remember our old friendfloor(random()) from Lesson 1? Great. Finally, let's use that in ourmouseClicked function instead.

functionmouseClicked(){createRandomTarget(mouseX,mouseY);}
Enter fullscreen modeExit fullscreen mode

SHABLAM! CIRCLES GALORE.

final product

Not too shabby huh?

Homework

  1. Go back to your Random Walker. Make it so that when you click on the screen, it clears the screen and he resets in the center of the canvas.
  2. Do #1, but make it so he starts wherever you click the mouse.
  3. Make a random walker that is not apoint, but a target!
  4. Update your target function to use colors. BONUS: make it a rainbow.
  5. Figure out how to make your target rectangular/square instead of a circle.

As before, I'm super dooper available to answer any questions you might have. Let me know what went well, and let me know what I could have explained better. Additionally, let me know how this lesson compared to the last lesson! Was this progressive building approach better than seeing the project up front like last time? Did you like getting the hard stuff (for loops) at the beginning and having the difficulty relax a little after that? Maybe you thought loops were easier than the last sections! I'd love to hear from you.


Originally posted onassert_not magic?

Top comments(2)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
courier10pt profile image
Bob van Hoove
I'm in the industry since 2008. Currently doing application development and maintenance for the local University, mostly using C#/.NET
  • Location
    Nijmegen
  • Education
    Some university
  • Work
    Developer
  • Joined

I'm familiar with p5 so I skimmed the article, I just enjoyed watching the pictures :)

For those who like this stuff I would highly recommend this MOOCThe nature of code by Daniel Shiffman.

CollapseExpand
 
rpalo profile image
Ryan Palo
Ryan is an engineer in the Sacramento Area with a focus in Python, Ruby, and Rust. Bash/Python Exercism mentor. Coding, physics, calculus, music, woodworking. Looking for work!
  • Email
  • Location
    Elk Grove, CA
  • Education
    M.S.C.S. Lewis University, B.S.M.E. Cal Poly (SLO)
  • Work
    Currently Job Hunting
  • Joined

Seconded. I mentioned this in the last lesson, but everything by Daniel Shiffman is worth watching. Especially the Nature of Code stuff.

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Ryan is an engineer in the Sacramento Area with a focus in Python, Ruby, and Rust. Bash/Python Exercism mentor. Coding, physics, calculus, music, woodworking. Looking for work!
  • Location
    Elk Grove, CA
  • Education
    M.S.C.S. Lewis University, B.S.M.E. Cal Poly (SLO)
  • Work
    Currently Job Hunting
  • Joined

More fromRyan Palo

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp