ZIM - Code and Learn Coding with ZIM for JavaScript and HTML Canvas with CreateJS

BADGES




Follow these steps to get ZIM Badges!

ZIM BADGES link to Badge 1 section -
    			 for JavaScript HTML Canvas Coding ZIM BADGES link to Badge 2 section -
    			 for JavaScript HTML Canvas Coding ZIM BADGES link to Badge 3 section -
    			 for JavaScript HTML Canvas Coding ZIM BADGES link to Badge 4 section -
    			 for JavaScript HTML Canvas Coding ZIM BADGES link to Badge 5 section -
    			 for JavaScript HTML Canvas Coding ZIM BADGES link to Badge 6 section -
    			 for JavaScript HTML Canvas Coding
BADGE 5
BACK

Use hitTests and forces to make the balls pop!

Okay... Badge 5 is the hardest! Are you ready? We are going to make a Ticker that runs constantly to rotate the labels on the balls so they are level. The Ticker will also check if the balls are hitting the triggers in the slots. We will shoot the balls upwards and add snowflakes if they are not the right ball for the slot. We will add a string between the balls and perhaps the most difficult part is that we will droop the string a little too. We need to keep track of when all the balls are in the right slot and go to Badge 6 when that happens!
38. INSIDE at the END of the makeBalls function, use the add() method of a ZIM Ticker to add a function literal function(){}. This method is run by the Ticker class directly - it is called a static method. There is only one Ticker - we do not make new Ticker() objects. This is similar to the Math class in JavaScript. So put the class (Ticker) and dot (.) the method (add()) with the function literal passed in as a parameter. In the function zog() "ticking!". View this in the Browser and view the console (F12) to see the word "ticking!" repeat over and over very fast. HIGHLIGHT MORE ANSWER
39 a) INSIDE the Ticker function { } loop() through the balls and collect a ball and the loop number, i in the ( ) of the function literal function(){} that is the second parameter of the loop method.

39 b) INSIDE the loop function { }, make a variable label and assign the result of the ball's getChildAt() method with 1 as the parameter. Here we are getting the label inside the ZIM Circle that is the ball. The circle shape is the first (0) child of the Circle and the label was added as the second (1) child. Remember that the child indexes start at 0 so the second child has an index of 1.

39 c) Set the rotation property of the label to MINUS the rotation property of the ball. This will counter the ball's rotation (that is constantly changing due to its mapping to the physics ball) and keep the label level. Put the object (label) then a dot (.) and the property (rotation) then assign (=) the negative (-) of the object (ball) and its property (rotation). HIGHLIGHT MORE ANSWER
We are going to start to test for the balls hitting the triggers of the slots now. However, there is no reason to test if the ball is not down by the slots. So we will use a conditional to check the y position of the ball. The ball's position is relative to the balls container which is centerReg'd on the stage. So we will add the balls' y to the ball's y to get the global y position of the ball. We could use var point = ball.localToGlobal(0,0) as we did before, but let's see the hard coded way. Remember, the hard coded way is too hard when containers are scaled, rotated or nested multiple times.
40. INSIDE the loop, create a conditional if(){} and inside the ( ) test to see if balls.y + ball.y > stageH-wallLength-bottomThickness. If it is, then inside the { } set the alpha property of the ball to .2 just to make sure that our conditional is working. Put the object first (ball) then the dot (.) and the property (alpha) followed by the assignment operator (=) and the value .2. Test the app in a browser and see if the balls opacity is reduced as they approach the bottom of the stage. HIGHLIGHT MORE ANSWER
Now that we know we are near the slots, we can loop through the slots and do a hitTest to see if we should shoot the ball in the air. Later we will see if the ball is the correct ball for the slot - but for now, we will shoot them all!
41 a) loop() through the slots as the first parameter and a function(){} as the second parameter. Collect TWO parameter inside the ( ) of the function literal. The first we will call slot as we are given the slot each time we loop. The second we will call j for the loop number. Note that we did not call this i as we are already in a loop through the balls that uses i. So we are in a loop within a loop! Cool.
The loop gives us a ZIM ball. We are going to shoot the physics ball - not the ZIM ball - the ZIM ball will follow due to the mapping. We currently do not have a reference to the physics ball that matches our current ZIM ball. If we wanted to, we could have stored a ball property on the ZIM ball when we made it that points to its corresponding physics ball but we did not do this. We did, however, put the physics balls in an Array called ballBodies as we were making the balls and the index of the array will match the index of the balls in the balls container. We know the index of the ball is i - not j - which is for the slots. So we can just use i to access the corresponding physics ball in ballBodies.
41 b) We will need access to the physics ball that matches our ZIM ball. So, INSIDE the { } of the loop function, make a variable ballBody and assign the corresponding body from the ballBodies array. Use the Array access operator [ ] and inside this put i.

41 c) Make a conditional if(){} and inside the ( ) test to see if the result of a hitTestBounds() method of the ball with a parameter of slot is true. This will tell us if the current ball in the balls loop is hitting the current slot in the slots loop!
hitTestBounds() is among the fastest of the ZIM hitTests which also include hitTestPoint(), hitTestRect(), hitTestCircle(), hitTestCircles(), hitTestReg(), and hitTestGrid(). Read about these in the docs to find out the difference between these and when to use them.
41 d) INSIDE the { } of the conditional - when the ball and slot are hitting - use the ApplyImpulse() method of the ballBody and pass it the following two parameter values:
  • new b2Vec2(rand(10,20,null,true), rand(-80,-100))
    • A vector gives us an x and y distance resulting in a line with a direction
    • The x distance here is randomly selected between 10 and 20
    • The true value means randomly negative or positive
    • The y distance is always pointing up between -80 and -100
    • So this forces up a lot and either to the left or right a little
  • ballBody.GetWorldCenter()
    • This is the location the force acts
    • Here we say at the center of the ball
    • GetWorldCenter() is a Box2D method that returns a point
41 e) Test in a Browser to see the balls pop up!
HIGHLIGHT MORE ANSWER
Now, we are going to see of the right ball is hitting the slot! We will also keep track of how many balls are in the right place. And we will not shoot the ball if the ball is in the right place.
42 a) ABOVE the Ticker, add a variable called placeCount and assign a value of 0 as we have not placed any balls yet.

42 b) Back INSIDE the hitTestBounds conditional, add an if/else conditional if(){}else{}. In the ( ) test to see if i == j.
This means that the current ball (i) is at the same index as the slot (j). This is what we want as both the balls and slots were made to match the order of the letters.
42 c) INSIDE the first { } increase the placeCount variable by using the ++ operator. Put the object first (placeCount) then the ++.
The ++ JavaScript operator increases the value by 1. It is a shortform for placeCount = placeCount + 1;. There are also --, +=, -=, *= and /= JavaScript operators.
42 d) INSIDE the second { } move the ApplyImpulse() method we made in Step 41d) as we only want to shoot the ball if the ball is the wrong ball for the slot.
HIGHLIGHT MORE ANSWER
We no longer need to test for balls that are in the right slot. So we will use a check variable - or in this case a check property. This is a common technique that does not specifically come with any language so do not look in documentation. It is pure logic.
43. INSDIDE the (i==j) conditional { } set a placed property of the ball to true. This is a property that we have just made. In JavaScript, objects are dynamic, so we can do this. Here, we are saying that the current ball has been placed. UP, INSIDE the balls loop BELOW the label rotation code, add a conditional if(){} and test to see if the ball's placed property is true. If it is true, then return - which goes to the next ball in the loop (like a continue in a traditional JavaScript for loop). Since this conditional is for one line of code, we can remove the { }.
We put this code here because we no longer need to do any of the position checking or the slot checking for this ball. Note that all that code comes after the return so it will not happen for this ball.
HIGHLIGHT MORE ANSWER
We are now going to start in on making the string that goes between the balls. We use a ZIM Shape() which has a graphics property. It is on the graphics property that we draw lines. We will make the Shape outside the Ticker (once) and just redraw the lines inside the Ticker each time as the balls are always moving.
44. ABOVE the Ticker make a variable string and assign a new Shape() with parameters of stageW,stageH. Chain on an addTo() with parameters of stage,1. We pass the 1 as the second parameter to add the string behind everything on the stage except for the walls container which is at index 0. On the next line, make a variable g and assign the string's graphics property. HIGHLIGHT MORE ANSWER
45 a) INSIDE at the TOP of the balls loop, make a variable called point and assign the results of the localToGlobal() method of ball passing in 0,0 as the parameters. This will convert the center of the ball to a global point. Remember, the ball is inside the balls container which is centerReg'd on the stage.

45 b) Create a conditional if(){}else{} and in the ( ) test to see if i==0. This will mean we have the first ball in the loop.
When we draw in the graphics property of a Shape() we chain on graphics methods - often using tiny methods (short-form methods). The methods we will use are:
  • c() - for clear() to restart the drawing.
  • s() - for beginStroke() to set the color of the line.
  • ss() - for setStrokeStyle() to set the width of the line.
  • mt() - for moveTo() to lift the pen and position.
  • lt() - for lineTo() to draw to a position.
  • qt() - for quadraticCurveTo() to curve to a position.
  • f() - for beginFill() is also common but not used here.
45 c) INSIDE the first { } we will start our graphics (g) and then using the dot (.) chain the c() method and then chain the s() method with parameter frame.light for a light grey. Then chain on ss() with a parameter of 2 for the line thickness. Then chain mt() with parameters of the center of the ball - point.x,point.y. This gets our line started at the center of the first ball.

45 d) INSIDE the second { } (for every ball except the first ball) we draw a line to the center of the current ball. Start with the graphics object (g) followed by a dot (.) and the lt() method with point.x,point.y as the parameters.
HIGHLIGHT MORE ANSWER
Now we will modify our code to curve the string to our next ball as long as the x distance is more than 100 pixels. This requires us to remember where the last ball's location is so we can set up our curve.
46 a) ABOVE the loop for the balls but INSIDE the Ticker, declare a variable lastPoint. Just use the var keyword and then the name (lastPoint). Do not assign anything to lastPoint.
This will allow us to store information across multiple loop functions. So it is an issue of Scope. If we declared the variable inside the loop function, it would only be available inside that function. It would be a different variable the next loop function. Here we store the variable outside the loop functions so they can all share the same variable.
46 b) AFTER the conditional if(){}else{} assign point to lastPoint. This makes sure that the next time we loop, lastPoint will refer to the point from the last loop.

46 c) INSIDE the else (the second { }) create another conditional if(){}else{}. In the ( ) test to see if the difference between the current point's x and the lastPoint's x is greater than 100. Since that difference could be negative or positive, use the abs() static method of the JavaScript Math class. Put the class first (Math) then the dot (.) and the method (abs()) and pass in point.x-lastPoint.x as the parameter. Then use the greater than operatator > followed by 100.

46 d) MOVE the code from 45d) to INSIDE the second { } as we will just draw a straight line if we are within 100 pixels. Note, this is the second { } when the x difference of the balls is not greater than 100.

46 e) INSIDE the first { } we will draw our curve.
The qt or quadraticCurveTo() uses a control point (the first two parameters) to guide the curve to the final point (the last two parameters). The curve starts at the pen's current position. So we do not pass that in as parameters. The current position will be the position of the last ball. The control point is the location of the Bezier handle for the last point. The curve does not actually pass through the control point, but rather it curves so that it is tangential (parallel) to the Bezier line at the last point. Bezier curves are used in Flash, Illustrator, PhotoShop, ZIM Blob() and ZIM Wiggle(), etc.
We want the controlX to be half way between the points and down a depth depending on the x distance between the points. The depth will start at half way between the points in the y direction. Here are the values:
  • Make a var controlX and assign lastPoint.x + (point.x - lastPoint.x)/2
  • This is the last point plus half the distance between the points.
  • Make a var depth and assign Math.abs(point.x - lastPoint.x)*.3
  • This is a percentage of the absolute x difference.
  • Make a var controlY and assign lastPoint.y + (point.y - lastPoint.y)/2 + depth
  • This moves the point down the depth from half the distance between the points.
On the graphics object (g) use the dot (.) to apply the qt() method and pass controlX,controlY,point.x,point.y as parameters. This curves the string to the next ball with a slight droop due to gravity.
HIGHLIGHT MORE ANSWER
We are now going to make snowflake for the ZIM Emitter() to "spurt" every time a ball hits the trigger. We only need one of these for the Emitter to clone so we make it outside the Ticker.
47 a) ABOVE the Ticker, ABOVE the placeCount variable, make a variable flake and assign a new Container().

47 b) Make a variable called arm and assign a new Rectangle() with parameters 30,5,frame.white. Chain on a centerReg() method with a parameter of flake (not stage).

47 c) Use the clone() method of arm to make THREE copies of the art. On the next three lines, put the object first (arm) followed by the dot (.) then the method (clone()). For each, chain on the addTo() method with a parameter of flake. For each, also chain on the rot() method with a different rotation for each. Pass a parameter of 90 for the first, 45 for the second and -45 for the third.
Clones get copies of the property values for x, y, scaleX, scaleY, regX, regY, alpha, etc. (but not for dynamic/custom properties added after the creation of the original object). So the clones will have center registration like the original arm Rectangle. When we rotate the arms, they will rotate around the center and look like a snowflake. Note, clones are not automatically added to the same container as the object being cloned - we must add them afterwards.
HIGHLIGHT MORE ANSWER
48. INSIDE the Ticker INSIDE the slots loop AFTER the ApplyImpulse(), make a variable emitter and assign a new Emitter() with a configuration object as a parameter {obj:flake, cache:true}. Chain the addTo() method with a parameter of stage. Chain the pos() method with parameters of ballBody.x,ballBody.y+50. This moves the emitter just a little lower than the ball bounce.
This will add a constantly running emitter which is not quite what we want. We will fix that in the next step. Note, the Emitter class has many more parameters and is very powerful. See the MORE link below for examples.
HIGHLIGHT MORE ANSWER
49. Add a startPaused property with a value of true to the configuration object of the Emitter. This pauses the emitter to start. Then after the positioning of the emitter (on the next line - not chained) use the spurt() method of the emitter to shoot only a few particles. Put the object first (emitter) followed by the dot (.) and the method (spurt()) and pass the parameters {min:2, max:5}, null.
The first parameter is a ZIM VEE value that says spurt between 2 and 5 particles. The second parameter is normally the time but we will use the default set in the emitter. We add the null here so that spurt() does not think we are passing a configuration object. There are only a couple of places in ZIM where the first parameter can be an object literal { }. When this is the case (such as with animate()) we always have to make sure to pass a second parameter. Or we can use the configuration object format. In this case: {num:{min:2, max:5}} would work as this single parameter is a configuration object.
HIGHLIGHT MORE ANSWER
50. The last step for the emitter is to dispose of the emitter when it is done spurting. Use the on() method of the emitter to capture a "spurtfizzed" event and call a function literal function(){}. Put the object first (emitter) followed by the dot (.) and the method (on()). Pass to the method two parameters: "spurtfizzed", function(){}. In the ( ) of the function capture the event object provided by the on() method by placing a parameter we will call e.
The event object gives us extra information about the event such as what object caused the event. This is called the target. We also are given a currentTarget property which is the object the event was placed on. In this case, they are the same. In the case of a container and a click event, they are often different as the target would be the object inside the container that was clicked on and the currentTarget would be the container.
INSIDE the { } use the dispose() method to dispose of the target of the event object. Put the object first (e.target) then the dot (.) then the method dispose(). In this case, the object we want to dispose is the target property of the event object. HIGHLIGHT MORE ANSWER
When the ball is in the right slot we want to animate the ball to the horizontal center of the slot just so things look tidy. We need to remove the physics mapping for the ball otherwise the mapping will conflict with the animation. We also need to get the center point of the slot relative to the ZIM ball's container (balls). We will use the globalToLocal() method for this conversion.
51 a) INSIDE the slots loop BELOW the placeCount++, make a variable point and assign the results of the globalToLocal() method of the parent property of the ball. Put the object first (ball.parent) followed by the dot (.) then the method (globalToLocal()). Pass the method the following parameters: slot.x, stageH-ball.height/2-bottomThickness.

51 b) Use the animate() method of ball with a configuration object with the following properties:
  • wait:1000
  • waitedCall:function(){}
  • obj:{x:point.x,y:point.y}
  • time:500
Open up the waitedCall function to multiple lines between the { } Use the removeMap() method of physics to remove the ballBody mapping. Put the object first (physics) followed by the dot (.) and then the method (removeMap) passing in a parameter of ballBody.
HIGHLIGHT MORE ANSWER
We will set the physics ball to a static body now that it is no longer moving. We still might need the ball to be there if other balls come and bounce on it. Perhaps the other balls will not hit it due to the hitTest on the slot, but just in case, we will leave it. We will do this when the animation stops.
52 a) Add a call property to the animate() configuration object with the value of a function literal function(){}. Open up the { } to multiple lines and inside.

52 b) Use the SetType() method of the ballBody to set the ballBody to static. Put the object first (ballBody) followed by the dot (.) then the method (SetType()) and pass in a parameter of b2Body.b2_staticBody. Then curse Box2D for their lengthy abstraction and capital letter methods.

52 c) Set the x property of ballBody to slot.x and the y property of ballBody to stageH-ball.height/2-bottomThickness.
Note that the x and y properties do not exist in Box2D. They are much more convoluted as we use vectors and have to translate to meters. But the ZIM Physics module does this for us.
HIGHLIGHT MORE ANSWER
ZIM Learn References
Now we check to see if all balls have been placed and if so, call the function done. We will make the done function in the next step and we will fill the done section in the next section for Badge 6.
53. Make a conditional if(){} and in the ( ) check to see if placeCount == letters.length. If we have placed all the letters then do the following in the { } use the clear() method of the scoreInterval to stop the timer score from running. Put the object first (scoreInterval) then the dot (.) and the method (clear()). Then set a ZIM timeout() passing parameters of 300,done. This will call the done function with a little pause for effect. HIGHLIGHT MORE ANSWER
54 a) DOWN at the bottom of our code UNDER the var = bottom, create a done() function with function done(){}.

54 b) ABOVE the function make a variable doneCheck and assign the Boolean false.

54 c) INSIDE the function { } make a one-line conditional to return if doneCheck is true. Put the conditional (if(){}) with doneCheck inside the ( ). You can put doneCheck == true in there but doneCheck is the same thing as this evaluates to either true or false. Then optionally, delete the {} for a single line conditional and put the return keyword.

54 d) AFTER the conditional but still in the done() function, set doneCheck to true and zog() "done" to see if this works. It should zog only once.
If you did not use the check variable, then the Ticker would keep calling the done() function. We need the Ticker to still run so that the string is placed during the final phase otherwise we could have removed the Ticker function.
This is the end of the hardest part! Congratulation. Claim your badge and on we go to the last section.
HIGHLIGHT MORE ANSWER
CLAIM YOUR BADGE
(SHOW YOUR TEACHER)