![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() |
Squeak How-ToHere are some Squeak Intro Links to smalltalk, not the etoy scripting. Some deal with both. Help decide which if any are good for etoy learning deal with both. Help decide which if any are good for etoy learning, and put links to the BEST tutorials on the following page:Tutorials The "how-to"s on our website are in various levels of detail. Some are just a couple of words to jog my memory so I'll know how I did something. They may be of no use to people who are not deeply invested until they are expanded. Other "how-to"s are at a level which should be enough for an accomplished programmer to understand and implement. Others are parts of tutorials, or pointers to walk-throughs for somebody learning squeak and perhaps programming for the first time. NEW Comprehensibility Ranking. Next to each topic covered is a number in red which indicates how much sense and use a beginning squeak (adult) will make of it. Basic scale: [3] should be quite useful and intelligible for the reader [2] rather sketchy, but should give main ideas [1] not very helpful, needs major expansion Since some expositions are aimed at people who have programmed already and may have advanced knowledge, the letter X is appended to indicate that it is aimed at an "expert" (anybody in this class). Thus, "[3X]" should be quite useful for the experienced programmer, be complete enough in description to get across the ideas, but these may or may not make sense to the beginning programmer. Finally, there may be TWO numbers, e.g., [1][3X] would indicate that the exposition is complete for an expert, but needs a lot more detail for a beginner. Please help assign rankings. AND, more importantly, please help move things UP in the comprehensibility scale. Note also, don't be afraid to use "3". It doesn't mean the perfect exposition. Ultimately, all of this needs to be done nicely, with illustrations, perhaps within squeak, or nicely printable document. Our Goals
Kathleen's suggested priorities, based on her experiences starting (a lot) of people on squeak. These are moved from the pedagogy page and slightly edited. As these get done, lets turn the items into links to where they appear.
How-To DirectoryLinks to stuff below, but probably incomplete.Technical ProblemsTechnical IssuesImporting Images CS Concepts in SqueakVariables (separate page) [3]Control (separate page) [3(X?)] Sequentail vs concurrent scheduling confusion [3] is related Tail Recursion Arrays [2X] Squeak SuppliesAn Introduction to SqueakUsing the Paint tool Using a joystick [3] Using the Button object [1] Using the Button button [1] Using a Holder (for animation) [3] Using a ListBox Other TechniquesSimple Collision Detection [3]Setting up Scorekeeping [2] Returning objects to a home position[3] Making things happen randomly once in a while[2][3X] Programing a delay Shooting things and Shooting things alternative explanation (these need to be merged into one. The second link immediately follows the first). Multi-object Interaction Finding the Mouse's location [1][3X] A "fire" button Balls bouncing correctly Using keyboard controls Distributing a project Bugs and other Weird ThingsObjects not moving straightI lost an object [3] Squeak Technical Issues...Publishing and Saving
FlapsIf after publishing, the Navigation flap loses all of itsicons, you can destroy all flaps, and then reinstall them. If you had created any flaps though, you'll lose them. To do this, get a desktop menu (available from red halo menu for "World", which is the basic background object of your project) and click on "flaps". TilesYou can modify a compound tile by dragging/dropping individual tiles to replace parts of the compount tile. For example, if you had objects redCar and greenCar, each with a variable "speed", and a tile "redCar's speed", then you can drag a tile "greenCar" (available from the halo of the greenCar, or from the menu next to its name at the top of the viewer: "tile representing me") and drop it inp lace of "redCar's" to obtain "greenCar's speed". This kind of substitutionwill usually work as long as the type of tile you're dropping is the same as the type of thing it is replacing. A special trick is the "double-possessive", which is explained in Scott Wallace's tutorial "multi-instance simulations".Scripts, panes, variablesSometimes when you change the name of a script, its name does not appear in the list of available scripts in the menus "object start scriptname", object pause scriptname", etc. Suggestions:
Importing ImagesTo import an image, go to the Navigator tab and hold down the Find button. A menu will appear. Click "Find any File". From here, you can browse for a picture to load. It will act as any other object would in Squeak. For more help, see: http://www.dmu.com/squeak/sq23.html CS Concepts in SqueakCreating an Array
Squeak SuppliesAn Introduction to SqueakThese links are a good place to start. Using the Paint toolThe Paint tool is located on the Navigator tab. It can be used to draw your own objects. See the tutorial at http://www.dmu.com/squeak/sq18.html Using the joystickDrag a joystick out of the widgets or supplies bin. Once you set it down it will resist moving (unless you use the black "tongs" icon in the halo). Bring up a viewer for it, and select the category "joystick" in one of the panes. You'll notice "joystick's leftRight", and "joystick's upDown" variables. Watch how these values change as you move the joystick's red "knob" in various directions by dragging it with the mouse. You can use these to get the value that the joystick is moved, and tie that to the motion of your object. For example, if you want the up/down action of the joystick to control forward/back motion of your car, in a script for your car you'd include the statement:
Similarly, you might want car to turn by joystick's leftRight. Of course, you need to build these statements with tiles. Drag out the phrase "car forward by 5" from the car's viewer (foward is found in the "basic" or "motion" pane). Either drop it into an already existing script, or drop the tile on the background and a script will sprout around it. Then drag the "joystick's upDown" tile, and place it over the "5". Do the same for "car turn by 5" and "joystick's leftRight". Now set the script ticking, and move the joystick in different directions to see the effect. You can make the joystick more or less sensitive by multiplying or dividing the amount the car moves. For example, click on the triangle to the right of "joystick's upDown" in your script, and the expression "+1" will appear at the end. Change the plus to times (an asterisk), and change the 1 to 5, so that the tile reads "car forward by joystick's upDown x 5". This has the effect of moving the car 5 times as far as the joystick is pushed - so it will be faster. Notice that there is a difference between absolute direction, and relative direction. If I give you directions to my house, "turn right" is different from "go east". Similarly, you must decide how the joystick will influence the motion of your object.
For example, if you have "car turn by joystick's leftRight", and the
car is doing down the screen, then moving the joystick to the
right will cause the car to turn right - which means head to the left
on your screen. This will be fine when the user is comfortable
projecting themselves into the game, imagining being the car.
This will have the effect of always moving the car to the right on the screen, regardless of which direction it is facing. This kind of motion is more appropriate for a pac-man style game. Notice also that for such games, you may need to set up a script that tests the direction the object is facing, so that it is consistent with the motion from the joystick. If the object is moving to the left, you don't want it pointing to the right. A way to correct this is to have the heading be 90 when the joystick is pushed to the right, and be -90 when it is pushed to the left:
Using a ButtonWrite a script that you want to be executed by pressing a button. If you only want to run the script once, skip to the next paragraph. If you want the script to run indefinitely,
In the viewer for the object that holds the script, open a "scripts" category pane. To the left of the script you wish to fire once, there is a white menu icon. Click it to get a menu, and select "button to fire this script". This attaches a button to your mouse, and you can click anywhere in the world to place it. Using a Button buttonTo use the button button from the supplies flap, you can drag the button off of the supplies flap onto the workspace. Then, bring up the halo. You will see an extra light green button next to the resize button in the lower right hand corner. If you click this, it will bring up the script for the button. You'll then probably want to click the black square to hide the code and return the button's script back to the normal tile scripting view.Using a HolderTo animate something, see the wonderful tutorial at A slightly more advanced example can be found at This, by the way, is what these lessons should strive to look like. Using a ListBoxTo create a scrollbox with a list inside it, see the tutorial at http://www.dmu.com/squeak/sq7.html Other TechniquesSimple Collision DetectionSuppose we have a cannon ball that shoots across the screen. Our job is to manueveur a ship from the bottom of the screen to the top. If the cannon ball hits the ship, we must start the game over and make a sound. We have two objects: Cannon Ball and Ship. One way to determine if we have a collision is to use the "overlaps function found in an objects "Tests" catalog.Cannon Ball Shoot Test Cannon ball overlaps Ship YES Ship start script StartGameOver Cannon Ball make sound Splash No Cannon ball forward by 5If the cannon ball overlaps (is on top) of the ship, the script "StartGameOver" runs. THere is also a splash sound that is made. If the cannon ball does not overlap the ship, the cannon ball moves forward by 5. Another way to determine if an object collides into another object is to use the "color sees" in an objects "Test" catalog. The "color sees" can be handy if one wishes to do different things if the cannon ball hits different parts of the boat. Suppose that the left side of the boat is resistant to cannon balls but the right side is not. All one must do is paint the left side of the boat a different color then the right side. (in the script below, XCOLOR would be the color of the cannon ball and YCOLOR would be the color of the boat) Cannon Ball Shoot Test Cannon Ball XCOLOR sees YCOLOR YES Ship start script StartGameOver Cannon Ball make sound Splash No Cannon ball forward by 5For a more indepth example, suppose the left side of the ship is colored RED and the right side is colored BLUE. The left side is resistant to cannons, that is, if the left side of the boat is hit by a cannon ball, a motor sound is made but the game does not start over. If the right side of the boat is hit by a cannon ball, we must start the game over and make a splash sound. (assume the cannon ball is colored black) Cannon Ball Shoot Test Cannon ball [black] sees [blue] YES Ship start script StartGameOver Cannon Ball make sound Splash NO Cannon Ball forward by 5 Test Cannon ball [black] sees [red] YES Cannon Ball make sound Motor Cannon Ball forward by 5 No Cannon Ball forward by 5Both "overlap" and "color sees" can be used to determine if a collision occurs. Overlap should be used if the same action will happen regardless of how the collision occurs. Color sees should be used if different actions occur if the collision occurs on different areas of an object. Also see the tutorials at Implement scoring(Read *Variables* first). Create a variable called "score", or whatever. From the menu next to the variable name, you can select the number of decimal places (probably you want 0). From this menu you can drag out a "detailed watcher", which will give a rectangle showing the name of the variable and the value. You can use this as a scorebox - any changes to the score variable will be reflected here.If you don't like the way that looks, you can instead select a "simple watcher", which gives you just the value of the score. You can increase the font size, style, etc., and embed it into any object or graphic that you want.
Read about *Tests* and assigning values of variables to figure out
how to change the score when you want to.
Get things to reset to a home position.Let's suppose your object is a car.
Make something happen "once in a while"This is achieved in a manner similar to the traffic light delay that built. As an example, suppose we'd like apples to fall from a tree once in a while. We might have many apples, and we'd like them all to fall at different times, randomly. Assume that they are siblings, so that they can all execute the same scripts. We'll use the following for each apple:
apple decideDelay apple's delay <-- random 50
apple's timer <-- 0
apple start script wasteTime
apple wasteTime
TEST apple's timer < apple's delay
YES apple's timer increase by 1
NO apple start script fall
apple stop script wasteTime
The fall script moves the apple down the screen (either by apple's y decrease by 1, or apple forward by 1, aassuming the heading was 180)). It may also test for when it hits the ground (using a color test, or testing it's y-value.) At that point, it should stop script fall. If you want to recycle the apple for another round, then you'll need to send the apple back to its home position, and then run decideDelay again. Notice that when all siblings execute the script, they each will choose a random number and set it to their own variable "delay", so they will each count up to a different amount before starting to fall. You can control the rate by choosing a smaller or larger range for the random number. Shoot something Suppose we've drawn a bubble-gun that we'd like to shoot bubbles.
Consider:
There are three different types of tests you might use. "isover", "overlaps", and "colorsees". (There is actually a fourth, "touchesA", but we will not address this test at this time). These are available on the tests pane in the viewer for an object. As an example, let us suppose we have a game where a dart is thrown at balloons. We will consider several variations, from simplest to more complicated,to illustrate when we'd use the different tests, and the difference between them. Here are the variations:
1. Suppose the balloon is red, and that dart's script "balloonHit" increases the score, and sends the dart back to its home location. Then when the dart was thrown, we could start any of the following scripts ticking. OPTION 1:
dart's checkHit
TEST dart isover [red] (you'd put the actual color here)
YES dart balloonHit
dart stop script checkHit
NO
OPTION 2: Same as option 1, but the test is
TEST dart's [yellow] sees [red]
OPTION 3: Same as option 1, but the test is TEST dart overlaps balloon We might like option 2, because we could test whether the tip of the dart (we've assumed it is yellow) is over the balloon, instead of whether, say, the tail of the dart (not yellow) grazed the balloon. 2. Here we have one dart, and many red balloons. It is not hard to see that either of the first two cases will work regardless of how many ballons we have, assuming they are all the same color red. However, Option 3 will not work, because we'd have to test each balloon individually... as written, the balloonHit script would not fire unless the specific balloon mentioned in the test was overlapped by the dart. 3. Now we have a challenge. We'd like the balloon to pop. Suppose we have written a script pop, which animates a popping balloon, and then hides the balloon. A simple version would just be to hide the balloon:
balloon's pop balloon hide Now, if we created a single balloon, wrote this script, and then created sibling balloons, then they all would have this script. So, which test should we use? Let's add a call to the "balloon pop" script when the dart is over red, as in the first option above:
dart's checkHit
TEST dart isover [red] (you'd put the actual color here)
YES dart balloonHit
balloon pop
dart stop script checkHit
NO
What will happen? Which balloon will pop? The balloon that will pop is the specific one that you mentioned in "balloon pop" above. If you wanted balloon3 to pop, you'd have had to have had the tile "balloon3 pop". Having balloon pop will be fine, if the dart was actually over that particular balloon. But the way it is written, regardless of which balloon has been hit, a specific balloon will execute its "pop" script. One solution to the problem, is to use the "overlaps" test, and write 37 different tests, one for each of our 37 balloons:
TEST dart overlaps balloon1...
YES balloon1 pop
..
TEST dart overlaps balloon37...
YES balloon37 pop
YUCCH. There must be a better way. There is. Whenever you find yourself having to type the same thing over and over, it is a good sign that you have not abstracted or generalized enough, and captured the same behavior in a single script that varies depending on only a variable, or who is executing it. In this case, suppose we script a balloon to do the checkHit script instead of scripting the dart:
balloon's checkHit
TEST dart's [yellow] sees [red]
YES dart balloonHit
balloon pop
balloon stop script checkHit
NO
Now, assuming that all of the balloons were siblings, they all should have this script, and when they execute it, "balloon" refers to the particular balloon running the script. To make all this work, when the dart was launched, we'd want to execute the statement "balloon start all checkHit" so that all balloons would start to check for overlap with the dart. OOOPS - when any of the balloons was hit, we'd want all of the balloons scripts to stop checking... so actually, we'd replace "balloon stop script checkHit" with "balloon stop all checkHit". While the above works, it is kind of ugly - it doesn't make sense to have to run so many scripts simultaneously. This will slow the whole program down. There should be a way to have the dart check which balloon it hit, and then to tell that balloon to pop. In object-oriented programming, this is usually no problem. There should be a way to specify that when a dart hits any particular balloon, that particular balloon should execute its "pop" script. However, there appears not to be a way to do this at present using the Etoys tiles. A clever work-around is to have the dart run the checkHit script, and when it is over red, send a message to all balloons to run their checkhit script exactly once. So, we'd have the following:
dart's checkHit
TEST dart's [yellow] sees [red]
YES dart balloonHit
balloon sendtoall checkHit
dart stop script checkIfHit
NO
balloon's checkIfHit
TEST dart overlaps balloon
YES pop
NO
Notice that we don't start the "checkIfHit" scripts ticking. We just run them once, just when the dart detects it is over something red. 4. Now we'd like to do the same thing, but with many different colored balloons. Assuming that the only thing that the dart can hit is a balloon (i.e., there are not other objects), we can simply test whether the dart is over the background color. If so, do nothing, but if not, then do as above.
dart's checkHit
TEST dart's [yellow] sees [white] (the background color)
YES
NO dart balloonHit
balloon sendtoall checkHit
dart stop script checkIfHit
Probably Irrelevant Comment, given current version: While "eToys friendly" seems to be desirable, the unavailability of "touchesA" can be a significant limitation if you are scripting with multiple copies of the same object. One work-around is to temporarily change to not being eToys friendly, go and get a "touchesA" tile, and store it for later duplication and use. (You will be able to replace the parts of the tile for who is touching, and who is being touched, by dragging tiles with the names of the relevant objects.) To change eToys friendly option, drag out an object catalog from the supplies or widgets flap, click on "tools", then drag out the "Preferences" window. Within that window, click on "scripting", and then you can select/deselect the eToys friendly option.
This is for another day.
To make a delay
Finding the Mouse's locationYou can get the mouse's current X and Y position to any object that has a Playfield tab in the viewer window. Some objects that contain this tab include the world, playfields, holders, and the pages of books.The lower left hand corner of the playfield is where X and Y are both 0. If you move the mouse up on the screen, the Y value will increase, similarly by moving the mouse right, the X value will increase. If you move the mouse below or to the left of the playfield, you will get negative numbers. These variables can be used for many things. One basic example of what you can do with the location of the mouse is to control the location of an object, such as a paddle in a game like pong or breakout. The following tiles would be used to set the X position of the mouse. Extension:Using the above tiles, how could you make an object move towards the mouse? Solutions (mouse)Making a ball bounce correctly off of a flat bar that is at an angle.
Using keyboard controlsTo allow input through the keyboard, go to the world's viewer and under the input tab you will find lastKeystroke. Then, in a script, put lastKeystroke in a Test statement.TEST lastKeystroke=w However, if you do not press another key after the w, it will continue testing true and the object will continually go forward. To get around this, manually change lastKeystroke within the Yes area. TEST lastKeystroke=w Sequential vs Concurrent in squeak.
Question 1: If we execute A, will this infinite loop, or stop?
Now suppose A is the same, but we replace B with
Question 2: will this infinite loop, or stop?
Explanation
FIRST CASE: tick 1: B is schedule to start next tick tick 2: B starts B is scheduled to stop next tick A called, so B is schedule to start next tick. tick 3: B stops B starts (in order of queued schedule above SECOND CASE:
Tail RecursionWhen you want to program a multi-step algorithm in squeak it can be difficult getting deterministic behavior using features like ticking or (shudder) tellAllSiblings, sendToAll. One option is to call scripts using normal function calls, but that may be undesireable if you want to visually see the algorithm's progress. Another option is to use the "do" feature from the Scripting pane. This tile does exactly what the bubble help says it does. It tells squeak to run a particular script on a particular object once, during the next tick.This is useful simply for the fact that you know what is going to happen when you call it, but it doesn't really get interesting until you consider what happens when multiple objects/scripts call "do" during a particular tick. The requested scripts will be called on the requested objects, but not until the current tick is done. In terms of recursive functions this means that if you branch into several subcases each case in the current level of recursion completes before the next level starts. Of course, you should also note that since you're not using function calls when you use "do" there really isn't any implicit recursion going on. When you call "do" nothing happens until the next tick, so you can't use the results of the called script in the currently executing script. What you do get is tail recursion, which means that you do something and then tell another object/script to take over where you left off. This can be used to program explicit loops. To program recursion with it you need to explicitly return control by having the script calling "do" save itself in a variable of the object being called and then having the script on the called object call a script on the object that called it. I know, I need to explain this better, but I wanted to get the idea out there since using this programming style has helped me a lot to get things working like Game of Life and Merge Sort. BugsObject not moving straightIf your object is gradually drifting from where it is supposed to go, find its Heading variable under the Basic tab. The value displayed is an integer rounded from the actual heading. For instance, if the object's heading is 89.7 degrees, it will display 90.To fix this, type in the value you want directly into the field. I lost an objectIf one of your objects has become hidden and you do not have the viewer available for it, go to the World menu -> playfield options -> show all players, and you will be able to see it.Another way to find a lost object is: On the right side of the Squeak screen, thumbnails of all the objects (except the World) should exist. If you lose an object or cannot find it, click on the desired objects thumbnail. At the top of the object viewer, there is rectangular box that displays the objects menu. Click on the Menu button. There are now two options. 1) Reveal me - click this option and your object will start flashing on the screen 2) Grab me - this option will move the object to wherever your mouse is Links to this Page
|