Scripting with Players -- The "User-Scripting" Style
Last updated at 3:35 am UTC on 29 August 2018
(content moved from page Programming in Morphic)
This has now (I'm writing this as of May 2004) evolved into the eToy scripting system. Go to Etoys to learn more about it. The following material still contains good pointers for getting started with eToy scripting.
The second style of programming is rather more informal, more like "scratch programming", somewhat comparable to what we Smalltalkers do when we use a Workspace to construct and evaluate various lines of code for some exploration or calculation in Smalltalk, and also comparable to the kind of scripting done by users of systems like HyperCard and HyperStudio, etc.
In the User-Scripting style, you construct surface graphics by directly assembling standard Morphic parts – e.g. Rectangles, Images, Joysticks, etc., by dragging them from a Parts Bin and arranging them as desired, and then you add user-defined state and behavior by adding instance variables and writing methods for "Players" who represent the individual morphs you wish to script.
The user thus does not directly subclass any particular kind of Morph, but rather she assembles Morphs and gives them special state and behavior by associating them with "Players", which are the fundamental user-scriptable object types for User Scripting.
(The basic hookup is that every Morph can have, optionally, a Player as its "player", and every Player has an associated Morph that it "wears" as its "costume". Player itself is a class with lots of capability but very little instance state; user-defined Players are all implemented as compact, single-instance subclasses of Player.)
Blanket Disclaimer and Warning
In Squeak 2.2 we offer only a very early take on User Scripting. It has many rough edges, consumes lots of memory, can leave garbage around that will bloat your image,
It is tempting to dwell at length on "Disclaimers" about User Scripting, but we shall resist, beyond saying: this is early work, preliminary, much in flux, with bugs, inconsistencies, significant gaps in design, and much that definitely will be changing before long.
If you wish to play with this stuff anyway, please do so in a spirit of high adventure, and be sure to protect yourself by doing your exploring in a "throw-away" copy of your image, not in something you intend to save and build on. These are very early days!
Live Examples Coming Later
In due course, we will be placing on the Squeak web site some "live examples" of use of "Player Scripting".
Eventually, we expect that full, interactive, mutlimedia-based Squeak tutorials, constructed entirely within the User Scripting domain, will be downloadable from the Web and will be immediately active and usable.
Technical limitations have kept us from doing that now, and space considerations have kept us from implanting live examples in this release image.
Good advice for the moment (except for the most adventurous) is to wait until those live examples are available on the net. They should make it much easier to absorb the basics of user-scripting, without having to wade through so much prose.
The Three Basic Elements of User Scripting are:
(1) The "Halo" – lets you interact with any object you can touch (i.e., any morph).
Alt-Click on a morph to bring up its halo. Successive alt-clicks will transfer the halo to the next morph in the hierarchy – experiment with this. Mouse over the various halo handles and read the help-balloons that will pop up to explain what each handle does. The name of the object that currently bears a halo will be seen at the base of its halo (and a click on the name will let you edit it.) Note: on a Mac use Cmd-Click.
(2) The "Viewer" – a dynamic graphical Inspector and Browser.
To get a Viewer for an object, drag from the cyan handle of its halo, and a Viewer will stick to the mouse; place it wherever you wish.
Use the top part of the viewer to see and change properties of the object; use the bottom part of the viewer to see and invoke scripts for the object. And use the buttons provided to add your own instance variables and your own methods to the object.
(3) The "Scriptor" – a place to define, edit, test, save, and schedule actual scripts.
In order to succeed with user scripting, you will need to be able quickly to get a Halo on any object, open a Viewer on any object, and get a Scriptor for any object.
Here is a suggested path to get started with scripting:
Choose "project (construction)" or "morphic construction window" from the "open new" menu.
Drag a Playfield (light green rectangle) from the parts bin and drop it somewhere.
Drag a Star from parts bin, and drop it in the Playfield.
Alt-click on the star to bring up its halo.
Tear off a "viewer" for the star by dragging from its cyan halo handle.
Drop that viewer somewhere nearby (but preferably NOT in the Playfield)
Balloon help over the various crude controls in the Viewer will give you a general orientation.
Exercise 1 – Relationship between an object and its viewer.
a. Play with the star via its halo. Drag it around, rotate it, resize it, change other things via its red-dot halo menu, and watch the changes in the viewer. (You may need to click to another "bank" of parts to find the group of information you're interested in; use the little left and right arrows )
b. Play with the star via its viewer – set its heading, its scale factor, its border width, its color, etc., and notice the effects on the star itself.
Anything you can manipulate in the viewer can also be manipulated via scripts, and the viewer gives you access to scripting, as will be seen below.
Exercise 2 – Sending requests directly to an object from its viewer.
In the lower part of the viewer are some sample lines of script. (Look at the other banks to see the full vocabulary.)
You can click on any of the yellow "run it!" (exclamation point) buttons to send that line of command to the star. You can change the values of parameters there in the viewer.
Make the star turn by hitting ! for "star turn by 5".
Hold down that ! button to keep the star turning.
Change the "5" to larger and smaller values, and to negative values (by clicking on the little carets that control these things) and notice the effect
We're not "scripting" yet because we're still manually issuing one command at a time. Scripting is simply the process of assembling a sequence of such commands in a "Scriptor", so they will all run, in sequence, when asked.
Exercise 3 – Making a simple script
Drag from the "star turn by 5" line of the viewer, and drop onto the "desktop" of the world. A Scriptor will result – your own script, with the "star turn by 5" command already in there as its first line.
Drag from the "star forward by 5" line of the viewer, and drop that down inside the same Scriptor. You have now created a two-line script, that tells the star to turn and move forward.
Let the cursor drift over each of the five buttons at the top of the Scriptor, and read the balloon help of each to get an idea of what they do.
Click on the "!" and the script runs. Hold down, it keeps running.
Change the amount-to-turn-by and the amount-to-move-forward-by and run again.
Now try out various alternatives for when the script should run. Initially, when it says "Normal" in the Scriptor, it's just cold code waiting to be called by someone, such as by the yellow "!" button.
So now make it run continuously – choose "ticking" from the script-status menu that pops up when you click on "Normal". The star should now start animating continuously, until you change its status or until you hit the "Stop" button.
While it's animating, change the parameters to "forward" and "turn" in the Scriptor, to achieve different curvature and speed as it putts along.
Now for fun you might find the "Pen" area in the star's Viewer, and change the star's "PenDown" to true. It will start laying down ink as it moves, and now by adjusting parameters to move and turn in the Scriptor, you can do a kind of scripted drawing.
Once you've started this, resist the temptation to change the pen's color and size in the viewer, because you might get distracted from the real business of scripting.
Exercise 4 – Hooking up a script to the user interface
Above, you made the star's script "run all the time". Now go back to that same menu in its scriptor, and tell it to run on "mouseDown". Then mouse down on the star and watch the script run once.
Experiment with all the different choices – mouseDown, mouseStillDown, mouseUp, mouseEnter, mouseLeave. For "countinuous controls" such as scroll bars and sliders, "mouseStillDown" is a good choice. For traditional "buttons", "mouseUp" is a traditional choice, though some controls feel better when they react on mouseDown.
You can have one script do something on mouseDown, and another on mouseUp, etc. One easy thing to try is to have the button change color (to something darker, for example) upon mouseDown, and then change back to its original color upon mouseUp.
Exercise 5 – Naming and saving a script.
Thus far, we've been working with a "temporary script". If you want to make it a permanent part of the object, choose "name and save this script", and give it a name, such as "wander".
As soon as you've done this, you'll see "wander" show up in the star's Viewer, alongside "forward by", "turn by", "make sound", etc.
Your "wander" script has now become a formal part of the object – you've added a method to your object. It can now be deployed in viewers and scriptors just like the system-defined scripts such as "forward by". And it can be called by any other script.
Exercise 6 – Substituting your own (textual) Smalltalk code
Here we will make a button that, when pressed, files out your current change set to the directory where the image file is in. This is to illustrate the fact that you can always "escape" from the tile-scripting framework to write your own arbitrary Smalltalk code.
Get a parts bin.
Drag out an ellipse.
Drag out a "Text for Editing", and edit its text to say "File Out Changes".
Place the text over the ellipse, nicely centered (resize the ellipse as needed), then bring up the halo for the text and choose "embed" from its menu, and embed it in the oval
From the Text's halo menu, choose "lock", so that the label won't go into text-editing mode when the mouse comes down on it – that's not what we want here.
Next, bring up the ellipse's halo and from the halo tear off a Viewer.
Name the ellipse "TestButton" (you can edit the name in the halo or at the top of the viewer).
From the viewer, tear off a "TestButton make sound croak" phrase, and drop it on the world's desktop. A scriptor opens up around it.
From the scriptor's menu, choose "name and save this script", and give it a name like "doFileout".
From the scriptor's status menu, change "normal" to "mouseUp". This will allow it to behave like a traditional button.
Test the button – when you click on it, you should hear a croak sound.
Now chose "edit script textually" in the scriptor. This will give you a message-editing window in which you can type arbitrary Smalltalk code, and it will be invoked whenever the script is triggered, be it via ticking, mouse actions, or being called from another script.
So now we type in whatever script we want, in plain Smalltalk, and submit it.
For the moment, you might edit the method to look like this:
Transcript cr; nextPutAll: 'Testing One Two Three...'.
self beep: 'croak'.
Now make sure a Transcript is open, and then try the script out. When you click on the button, your message should show up in the Transcript and you should hear the croak.
This shows that the hookup is working. Now all that remains is to edit the method to do something useful; in this case, it will look like this:
Smalltalk changes fileOut.
self beep: 'croak'
Now, whenever you click on this button, the current change set will be filed out and you will get a frogly confirmation.
Exercise 7 Adding instance variables to an object.
Hit the "add inst var" button in a Viewer to add an instance variable. The new instance variable starts out bearing a number, but you can change its type at any time. The type affects what form its readout (in the Viewer) will take, and also where you can drop a tile representing the value in a scriptor (numbers can be dropped on numbers, etc.)
If the type is a number, for example, you'll get a textual readout with arrows to change the values manually. But if the type is "player" (i.e. object-reference, or "alias", if you will), then the readout is a graphical thumbnail of the current referent.
Exercise 8 Scripting one morph to chase another
Get a morph (which you've named, say, "rabbit") busily animating with an ever-ticking script involving "forward by" and either "turn by" or "bounce".
Construct another Morph, name it "chaser" and script it to pursue the animating morph by giving it an ever-ticking script of the form:
chaser move toward dot
dot here refers to a little, faint object near the left edge of the window – the one object in the world that starts out life with "identity", this serves as the sample parameter for any Player-valued scripting element, just like the "5" in "forward by 5" serves as a sample parameter for "forward by".
To make the object move not toward dot but rather toward the "rabbit" you've earlier animated, you'll want to tear off a tile to represent the rabbit. There is a handle on the left edge of the Halo which, when you mouse down on it, will yield you just such a tile. Get it, and drop it over the "dot" tile, and it will replace that tile, so that your script will now read:
chaser move toward rabbit
[For extra credit, you can adjust the speed at which pursuit takes place by adding a numeric slot to chaser and calling it "speed".]
Exercise 9 Controlling an object with a Joystick
Generally you create scripts by dragging tiles from the viewer; this gives you simple scripts (which already are syntactically correct, and are fully functional) to start out with, and then you proceed to modify them.
One essential operation is dropping a tile into another tile bearing the same type of information; this results in the old tile being replaced by whatever you dropped.
Thus, for example,
Paint an airplane using the simple painting tools:
Drag from the crude green arrow in a Parts Bin (which indicates "paint a new object") and drop somewhere on a Playfield. Paint a rough airplane-like figure facing upward, then hit the "Keep" button. This is your airplane. Open up its Viewer, and name it "airplane".
Make a script for the airplane that starts out:
airplane forward by 5
airplane turn by 5
Get a fresh Joystick from the Parts Bin, and bring up a Viewer for it.
Go to the parts bank that shows the joystick-specific parts, "leftRight", "upDown", "angle", and "amount".
Drag from the tile showing the word "leftRight"; tiles for "joystick's leftRight" will stick to the mouse.
DROP that "joystick's leftRight" directly onto the airplane turn by "5", to make the airplane's script read:
airplane turn by joystick's leftRight
Now make this script run all the time ("ticking"), and you can spin the airplane by moving the joystick's handle to the left or to the right.
A simple extension (left to the reader) is to add a "forward by" command to the same script, and have the amount by which it is to go forward be obtained from some other number that a Joystick is able to report. It's fun to explore various possibilities for mapping the joystick parameters into the parameters for the "turn" and "forward" commands. Since the Joystick delivers four different numbers, you could hook them up to other things about your airplane (such as its color or its scale factor) and end up with some rather unusual controls!
The top part of the viewer consists of rows of the form:
airplane's heading <- 0.
If you drag from the green-and-purple left-arrow, you'll get an "assignment phrase", which is a line of code that lets you "assign a new value" for the heading. Plop it down in a script just like any other command.
Actually, there are four kinds of assignment: simple assignment, incrementing, decrementing, and multiplying; you move among them by hitting the carets on the assignment tile in the Scriptor. A simple exploration will reveal how they work, but be careful with the muliplying assignment – your numbers can quickly get big.
Advice, Bugs, Things To Avoid, etc.
(1) You will by now have noticed that when you drop a line of script on the Morphic "desktop", it sprouts a "scriptor" around itself, ready for editing and running. This is often what you want, but when it gets annoying, which is to say, when you find you'd like to be able to drop naked lines of script and have them stay as naked lines of script, for later use, you can drop them into any "Playfield" (PasteUpMorph) rather than onto the World desktop, and you can avoid this sprouting. (Whether or not a Playfield automatically expands a dropped phrase into a complete Scriptor is governed by the "automatic phrase expansion" option, which will be found in the its "playfield options" menu.)
(2) A nice standard kind of script for lots of experimentation is:
star forward by 10 "move forward ten units"
star bounce silence "if hit wall of container, bounce silently"
(3) Scripts that are "ticking" provide a "live" feel, making experimentation quite easy.
(4) Be sure to try out use of the "pen" for laying down color trails. The "colorUnder" and "colorSees" tiles provide ways that objects can easily interact with their surroundings.
(5) The "conditional" in the tile-scripting system is the "Test/Yes/No" complex (your basic if/then/else); in the test pane go boolean-valued things; into the Yes and No panes go any sequence of commands.
(6) When an object is scripted to handle the mouse, you won't be able to drag it with the mouse. To drag such an object, bring up its halo, and drag it from its black-dot or brown-dot handle.
(7) At present, there is no protection against script cycles, so that you can get yourself in trouble with two scripts or more scripts that end up calling each other.
(8) Before trying to duplicate a scripted object, make sure that all its scripts are "saved and named", so that the duplicate you make will have the same scripts.
This User-Scripting regime is still in its infancy, as is the documentation about how to use it. We put it out at this time only in hope that some users will find something of interest in spite of the rough state of the design and the code (and in spite of the numerous temporary perturbations it has caused to an otherwise elegant Morphic graphics system.)
Coming in the foreseeable future are extensions of the user-scripting design to cover "aliases", "collections", the "stack/card" dichotomy, file-based factoring of content, Finder-like analogies for content, integration with projects, navigation morphs, integration with the more generic Morphc inspector and browser tools, and more.
Much of this design space has been explored in various earlier prototypes we've worked on, so we're not starting from ground zero, but there is plenty of architectural work still to be done before we emerge with a clean and minimal design that will extend user scripting to span the space of the "multimedia, hypermedia, and simulation tool" that we believe it may become.
Many changes are on the way. We warmly welcome participation by the Squeak community as we pursue this work, which is quite at right angles to the traditional course of Smalltalk. Just, please, don't at this stage count on user-scripting to be compact, reliable, consistent, transportable, or stable. If you can thrive on such slippery ice, please join us now! Otherwise, give it a chance to mature over the coming months.
When I add the Joystick, the "turn-by" is too slow. How do add either a multiplier to the joystick output or the turnby's input ? MikeStramba