Scratch For Second Life (S4SL) – Easy LSL Coding

I’d never heard of it before, but there’s a beginners programming tool called Scratch that’s designed to teach the basic programming concepts. It seems to be run under the auspices of MIT, under the amusingly named “The Lifelong Kindergarten Group”. This from their website…..

“Scratch is designed to help young people (ages 8 and up) develop 21st century learning skills. As they create Scratch projects, young people learn important mathematical and computational ideas, while also gaining a deeper understanding of the process of design. ”

And now it’s available for Second Lifecourtesy of Eric Rosenbaum, and you don’t need to be eight years old to use it. Screenshot then details…….

 Scratch Interface

 As you can see above, you program things by dragging and dropping blocks, and the blocks contain instructions. When you put the code that this generates into a SL script in a prim, the prim will follow the directions.

The interface is easy to use and pretty intuitive, as you would imagine from its origins.

There’s a limited set of instructions to choose from – you can move things around, change color,  play sounds and communicate, but much of the more complicated stuff that’s unique to SL isn’t there yet. It is still a work in development, though, and the creator is asking for for suggestions as to what to include on the blog.

So what did I do with it? And perhaps just as importantly, how easy was it and how long did it take?

Because Scratch makes it easy to move things around, I decided to create a little dancer. Glued a few spheres and flexis together and clicked together a little dance for it. It took about an hour in total, though I’d had a couple of hours playing with Scratch beforehand. If I’d tried to program it by hand it would have taken a lot longer.

Here’s a pic of the simple dancer…..

 Dancer Bot

 And here’s a video of her dancing.

And here’s the code – it’s a bit top heavy for the things that it does, and it’s got too many listens for it to be lag friendly, but it does what it says on the tin. If you want to use it, put it into the root prim of a dancer. Also put the code into a basic prim and click this to broadcast the message to start the dance.

You can also issue chat commands to the dancer – (note the capital letters, they’re important) – Shimmy, Sidestep & Twirl.

// The code below was generated by
// SCRATCH FOR SECOND LIFE (S4SL)
// alpha release October 19, 2007
//
// by Eric Rosenbaum (<a href="mailto:ericr@media.mit.edu">ericr@media.mit.edu</a>)
// MIT Media Lab
// Lifelong Kindergarten group
//
// S4SL is a modified version of Scratch,
// a graphical programming language for kids
// see scratch.mit.edu
//
//
// USER VARIABLES
//
float name_uservar = 0;
//
//  INTERNAL VARIABLES
//
vector home;
integer penState;
float penColor;
float color;
float ghost;
vector originalScale;
float sizePercent;
integer numAvatarsNearby;
vector nearestAvPosition;
key ownerKey;
vector ownerPosition;
//
//   INTERNAL FUNCTIONS
//
// move(steps)
// move object a number of steps (meters) along its current heading
// forward is along the positive x axis
// if the pen is down, create a line segment along the path traveled
// the line is positioned by its center, which is placed halfway back along the path
move(float steps)
{
    vector fwd = llRot2Fwd(llGetRot()) * steps;
    llSetPos(llGetPos() + fwd);
    if (penState == TRUE) {
  if (llGetInventoryType("lineSegment") == INVENTORY_NONE) {
   llSay(0, "Oops! To draw a line, my inventory needs a lineSegment. You can get one from the Scratch Inventory Box.");
  } else {
   integer randomID = llRound(llFrand(99999999));
         llRezObject("lineSegment", llGetPos()-fwd/2, &lt;0,0,0>, llGetRot(), randomID);
         llSay(1, (string)randomID + ":set length:"+ (string)llFabs(steps));
         llSay(1, (string)randomID + ":set color:" + (string)penColor);
  }
 }
}
// turnRight(float angle)
// turn angle degrees clockwise around the local z axis
turnRight(float angle)
{
    angle *= -1;
    rotation newRot = llEuler2Rot(&lt;0,0,angle> * DEG_TO_RAD);
    llSetRot(newRot*llGetRot());   
}
// turnLeft(float angle)
// turn angle degrees counterclockwise around the local z axis
turnLeft(float angle)
{
    rotation newRot = llEuler2Rot(&lt;0,0,angle> * DEG_TO_RAD);
    llSetRot(newRot*llGetRot());   
}
//up(float steps)
//move up along the global z axis by steps meters
//does not leave a line segment
up(float steps)
{
    llSetPos(llGetPos()+&lt;0,0,steps>);  
}
//down(float steps)
//move down along the global z axis by steps meters
//does not leave a line segment
down(float steps)
{
    llSetPos(llGetPos()+&lt;0,0,-1*steps>);  
}
// turnPitch(float angle)
// turn angle degrees upward around the local y axis
turnPitch(float angle)
{
    angle *= -1;
    rotation newRot = llEuler2Rot(&lt;0,angle,0> * DEG_TO_RAD);
    llSetRot(newRot*llGetRot());   
}
// float getHeading()
// return the current heading in the xy plane in degrees
float getHeading() {
    return llRot2Angle(llGetRot())*RAD_TO_DEG;
}
// setHeading(float angle)
// set the heading in the xy plane in degrees
setHeading(float angle) {
    vector newVec = &lt;0, 0, angle*DEG_TO_RAD>;
    rotation newRot = llEuler2Rot(newVec);
    llSetRot(newRot);  
}
// turnRoll(float angle)
// turn angle degrees clockwise around the local x axis
turnRoll(float angle)
{
    rotation newRot = llEuler2Rot(<angle ,0,0> * DEG_TO_RAD);
    llSetRot(newRot*llGetRot());   
}
// changePenColorBy(float num)
// change the pen color by an amount
changePenColorBy(float num)
{
     penColor += num;
 setPenColorTo(penColor);
}
// setPenColorTo(float num)
// set the pen to a particular color
setPenColorTo(float num)
{
    penColor = (integer)num % 100;
}
// penDown()
// put the pen down, so that when the object moves it will draw
penDown() {
    penState = TRUE;
}
// penUp()
// put the pen up, so that the object will not draw when it moves
penUp() {
    penState = FALSE;
}
// clear()
// broadcast a message to nearby line segments that will cause them to
// delete themselves
clear() {
 llSay(1, "clearLineSegments");
}
// pointTowardNearestAv()
// turn to point toward the nearest avatar
pointTowardNearestAv()
{
    vector myPos = llGetPos();
    float xdiff = myPos.x - nearestAvPosition.x;
    float ydiff = myPos.y - nearestAvPosition.y;
    float angle = llAtan2(xdiff, ydiff) * RAD_TO_DEG;
    setHeading(270 - angle);
}
// pointTowardOwner()
// turn to point toward the owner
pointTowardOwner()
{
    vector myPos = llGetPos();
    float xdiff = myPos.x - ownerPosition.x;
    float ydiff = myPos.y - ownerPosition.y;
    float angle = llAtan2(xdiff, ydiff) * RAD_TO_DEG;
    setHeading(270 - angle);
}
// float distanceToNearestAv()
// returns the distance in meters to the nearest avatar
float distanceToNearestAv()
{
    return llVecDist(llGetPos(), nearestAvPosition);
}
// float distanceToOwner()
// returns the distance in meters to the owner
float distanceToOwner()
{
    return llVecDist(llGetPos(), ownerPosition);
}
// float randomMinToMax(float min, float max)
// returns a random number between min and max
integer randomMinToMax(float min, float max)
{
 return llRound(llFrand(max - min) + min);
}
// say(string text)
// say the text on the public chat channel 0 so all nearby avatars and objects will hear it
say(string text)
{
 llSay(0, text);
}
// broadcast(string message)
// say the message on channel 1.  No avatars will hear it.
// all nearby objects will hear it.
broadcast(string message)
{
 llSay(1, message);
}
// setText(string text)
// create opaque white floating text above the object
setText(string text)
{
 llSetText(text, &lt;1,1,1>, 1);
}
// vector hueToRGB(float h)
// take a color represented as a hue value between 1 and 100 and
// return an RGB vector representing the same color.
vector hueToRGB(float h)
{
    integer i;
    float f;
    float p;
    float q;
    float t;
    float r;
    float g;
    float b;
    float s = 1;
    float v = 1;
    h *= 5;            // sector 0 to 5
    i = llFloor(h);
    f = h - i;            // factorial part of h
    p = v * ( 1 - s );
    q = v * ( 1 - s * f );
    t = v * ( 1 - s * ( 1 - f ) );
    if (i == 0) {
            r = v;
            g = t;
            b = p;
    } else if (i == 1) {
            r = q;
            g = v;
            b = p;
     } else if (i == 2) {
            r = p;
            g = v;
            b = t;
    } else if (i == 3) {
            r = p;
            g = q;
            b = v;
    } else if (i == 4) {
            r = t;
            g = p;
            b = v;
    } else {
            r = v;
            g = p;
            b = q;
    }
    return <r ,g,b>;
}
// setColor(float num)
// set the color of the object using a number between 1 and 100 representing a hue
setColor(float num)
{
  color = (integer)num % 100;   
 llSetColor(hueToRGB(color / 100), ALL_SIDES);
 if (llGetObjectName() == "Scratch Bug") {
  llSetLinkColor(2, hueToRGB(color / 100), ALL_SIDES);
 } else {
  //llSetLinkColor(LINK_SET, hueToRGB(color / 100), ALL_SIDES);
 }
}
// changeColorBy(float num)
// change the hue of the object by a number
changeColorBy(float num)
{
    float newColor = color + num;
    if (newColor < 0) {
        newColor = 0;
    }
    if (newColor > 100) {
        newColor = 100;
    }
    setColor(newColor);
}
// setGhost(float num)
// set the ghost effect of the object to a value between 0 (opaque) and 100 (transparent)
setGhost(float num)
{
 ghost = (integer)num % 101;
 llSetAlpha(((100 - ghost) / 100), ALL_SIDES);
 llSetLinkAlpha(LINK_SET, ((100 - ghost) / 100), ALL_SIDES);
}
// changeGhostBy(float num)
// change the ghost effect on an object by a number
changeGhostBy(float num)
{
 setGhost(ghost + num);
}
// setSize(float newSize)
// set the size of the object to a percentage of its original size
setSize(float newSize)
{
 sizePercent = newSize;
 vector newScale = originalScale*(sizePercent/100);
 llSetScale(newScale);
}
// changeSizeBy(float change)
// change the size of an object by a percentage of its original size
changeSizeBy(float change)
{
 sizePercent += change;
 vector newScale = originalScale*(sizePercent/100);
 llSetScale(newScale);
}
// playSound(string snd)
// play a sound at full volume
// snd can be the name of a sound in the inventory of the object, or the
// UUID of a sound which exists somewhere else
playSound(string snd)
{
 llPlaySound(snd, 1);
}
// playSoundNamed(string snd)
// play a sound at full volume
// snd can be the name of a sound in the inventory of the object, or the
// UUID of a sound which exists somewhere else
// this is the version for text input of the name of a sound in a scratch block
// so it checks the inventory and gives an error if the sound is missing
playSoundNamed(string snd)
{
 if (llGetInventoryType(snd) == INVENTORY_NONE) {
  llSay(0, "Oops! My inventory does not contain the sound " + snd);
 } else {
  llPlaySound(snd, 1);
 }
}
// wait(float secs)
// pause all execution of this script for some number of seconds
wait(float secs)
{
 llSleep(secs);
}
// levelOut()
// remove the x and y rotation components, so that the object is
// level with respect to the ground
levelOut()
{
    vector myVec = llRot2Euler(llGetRot());
    vector newVec = &lt;0, 0, myVec.z>;
    rotation newRot = llEuler2Rot(newVec);
    llSetRot(newRot);
}
// goHome()
// move the object back to its home position
// home is set the the position of the object when it is created,
// and can be set to a new position using setHomeHere()
goHome()
{
 llSetPos(home);
 //levelOut();
}
// setHomeHere()
// set the home position to the current position
setHomeHere()
{
        home = llGetPos();
}
// startListening()
// listen for messages on both channel 0, the public channel,
// and channel 1, where broadcasts are sent  
startListening()
{
    llListen(0, "", "", "");
    llListen(1, "", "", "");
}  
// initInternal()
// do some setup for internal functions
// this includes setting various variables to their defaults
// clearing text on the object, and turning on
// the repeating sensor and timer events
initInternal()
{
    setHomeHere();
        penState = FALSE;
        penColor = 0;
    color = 0;
    ghost = 0;
        sizePercent = 100;
        originalScale = llGetScale();
    ownerKey = llGetOwner();
    llSetText("", &lt;1,1,1>, 0);
    llSensorRepeat("", "", AGENT, 96, PI, .1);
    llSetTimerEvent(.1);
    startListening();
}
initAll() {
    initInternal();
}
listen1(string msg){
    if (msg == "Home") {
      setHomeHere();
      playSound("989eef2f-8170-3acb-5706-8fc78f540dc6");
    }
}
listen2(string msg){
    if (msg == "Sidestep") {
      integer i1;
      for (i1=0; i1<randommintomax (2, 4);i1++) {
         pointTowardNearestAv();
         turnRight(90);
         move(1);
         wait(0.5);
         turnLeft(180);
         move(2);
         wait(0.5);
         turnRight(180);
         move(1);
         wait(0.5);
         turnLeft(90);
      }
    }
}
listen3(string msg){
    if (msg == "Shimmy") {
      integer i2;
      for (i2=0; i2<randomMinToMax(6, 10);i2++) {
         turnRoll(15);
         turnRoll(-15);
         turnRoll(-15);
         turnRoll(15);
      }
    }
}
listen4(string msg){
    if (msg == "Twirl") {
      integer i3;
      for (i3=0; i3<randomMinToMax(2, 5);i3++) {
         turnRight(90);
         turnRight(90);
         turnRight(90);
         turnRight(90);
      }
    }
}
touch1(){
   say("Home");
   integer i4;
   for (i4=0; i4<randomMinToMax(2, 3);i4++) {
      pointTowardNearestAv();
      broadcast("Sidestep");
      wait(1);
      broadcast("Shimmy");
      wait(1);
      broadcast("Twirl");
      wait(1);
      broadcast("Shimmy");
      wait(1);
      broadcast("Twirl");
      wait(1);
      broadcast("Shimmy");
      wait(1);
      broadcast("Sidestep");
      goHome();
   }
}
default
{
    state_entry()
    {
        initAll();
    }
    on_rez(integer start_param)
    {
        initAll();
    }
    sensor(integer n)
    {
        numAvatarsNearby = n;
        nearestAvPosition = llDetectedPos(0);
        integer i;
        for (i=0; i<n; i++) {
            if (llDetectedKey(i) == ownerKey) {
                ownerPosition = llDetectedPos(i);
            }
        }
    }
    touch_start(integer n) {
        touch1();
    }
    collision_start(integer n) {
    }
    listen(integer channel, string name, key id, string msg) {
        listen1(msg);
        listen2(msg);
        listen3(msg);
        listen4(msg);
    }
    timer() {
    }
  }
//<?xml version="1.0" encoding="UTF-8" ?><scratchscript><block event="Home"/><block selector="setHomeHere"/><block selector="playSound:"><argument>upward zing</argument></block></scratchscript>
//< ?xml version="1.0" encoding="UTF-8" ?><scratchscript><block event="Sidestep"/><block selector="doRepeat"><argument><block selector="randomFrom:to:"><argument>2</argument><argument>4</argument></block></argument><block selector="pointTowardNearestAv"/></block><block selector="turnRight:"><argument>90</argument></block><block selector="forward:"><argument>1</argument></block><block selector="wait:elapsed:from:"><argument>0.5</argument></block><block selector="turnLeft:"><argument>180</argument></block><block selector="forward:"><argument>2</argument></block><block selector="wait:elapsed:from:"><argument>0.5</argument></block><block selector="turnRight:"><argument>180</argument></block><block selector="forward:"><argument>1</argument></block><block selector="wait:elapsed:from:"><argument>0.5</argument></block><block selector="turnLeft:"><argument>90</argument></block></scratchscript>
//< ?xml version="1.0" encoding="UTF-8" ?><scratchscript><block event="Shimmy"/><block selector="doRepeat"><argument><block selector="randomFrom:to:"><argument>6</argument><argument>10</argument></block></argument></block><block selector="roll:"><argument>15</argument></block><block selector="roll:"><argument>-15</argument></block><block selector="roll:"><argument>-15</argument></block><block selector="roll:"><argument>15</argument></block></scratchscript>
//< ?xml version="1.0" encoding="UTF-8" ?><scratchscript><block event="Twirl"/><block selector="doRepeat"><argument><block selector="randomFrom:to:"><argument>2</argument><argument>5</argument></block></argument></block><block selector="turnRight:"><argument>90</argument></block><block selector="turnRight:"><argument>90</argument></block><block selector="turnRight:"><argument>90</argument></block><block selector="turnRight:"><argument>90</argument></block></scratchscript>
//< ?xml version="1.0" encoding="UTF-8" ?><scratchscript><block event="Scratch-MouseClickEvent"/><block selector="say:"><argument>Home</argument></block><block selector="doRepeat"><argument><block selector="randomFrom:to:"><argument>2</argument><argument>3</argument></block></argument><block selector="pointTowardNearestAv"/></block><block selector="broadcast:"><argument>Sidestep</argument></block><block selector="wait:elapsed:from:"><argument>1</argument></block><block selector="broadcast:"><argument>Shimmy</argument></block><block selector="wait:elapsed:from:"><argument>1</argument></block><block selector="broadcast:"><argument>Twirl</argument></block><block selector="wait:elapsed:from:"><argument>1</argument></block><block selector="broadcast:"><argument>Shimmy</argument></block><block selector="wait:elapsed:from:"><argument>1</argument></block><block selector="broadcast:"><argument>Twirl</argument></block><block selector="wait:elapsed:from:"><argument>1</argument></block><block selector="broadcast:"><argument>Shimmy</argument></block><block selector="wait:elapsed:from:"><argument>1</argument></block><block selector="broadcast:"><argument>Sidestep</argument></block><block selector="goHome"/></scratchscript>

Download Scratch from here (Windows & Mac)……..
http://web.mit.edu/~eric_r/Public/S4SL/

Advertisements

~ by slconceptual on July 26, 2008.

One Response to “Scratch For Second Life (S4SL) – Easy LSL Coding”

  1. uhhh… sounds very interesting 😀

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: