VintFalken.com

Scratch for Second Life so even Vint can script?

July 23, 2008 2:50 am

Released by MIT Media Lab’s Eric Rosenbaum Scratch 4 Second Life, or shorty put S4SL is a new easy way to add behaviours and interactivity to your objects in Second Life. S4SL is based on Scratch, a graphical programming language that lets you construct programs by snapping together graphical blocks. With S4SL, you can snap together a few blocks to make your Second Life pet interact with you using chat commands, make your sculpture change size and colour, or make your house respond to your presence.

At this moment in time - as S4SL is still in alpha - options are limited: I wanted this object to de-rezz itself after a while, which was not a possibility. This - look below - seems like a whole lot of code for something simple - could be I’m doing something wrong -, but Scratch 4 Second Life is definitely something kewl, that will make content creation more accessible to the scripting-impaired like me. Why did Linden never bother with something like this?

To download Scratch for Second Life, you go to Eric Rosenbaum’s MIT page, for update information, feature requests and discussion, there is the S4SL Blog at Blogspot. More fun Scratch projects, games and animations over at MIT’s official Scratch page.

PS. You know I’m far from genius as it’s about scripting in LSL, but err… now we’re getting closer to Mono coming to the grids near you, will an LSL scratch be useful for a long time still? As Tenebrou Aimee Trescothick answered to Mr Tenebrou on Plurk when I asked about Mono and LSL compatibility: ‘Aren’t they going to phase out the old LSL VM at some point though?’ ‘”We have no plans to eliminate the LSL2 VM.” So … about a week after Mono goes live?’

// The code below was generated by
// SCRATCH FOR SECOND LIFE (S4SL)
// alpha release October 19, 2007
//
// by Eric Rosenbaum (ericr@media.mit.edu)
// 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
//

//
// 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, <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(<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(<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()+<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()+<0,0,-1*steps>);
}
// turnPitch(float angle)
// turn angle degrees upward around the local y axis
turnPitch(float angle)
{
angle *= -1;
rotation newRot = llEuler2Rot(<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 = <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( * 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, <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 ;
}
// 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 = <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(”", <1,1,1>, 0);
llSensorRepeat(”", “”, AGENT, 96, PI, .1);
llSetTimerEvent(.1);
startListening();
}
initAll() {
initInternal();
}
touch1(){
say(”Vint rawkz!”);
wait(5);
say(”Really!!!”);
wait(5);
say(”What you mean you don’t agree?”);
wait(5);
say(”Well, then you cant haz my company!”);
}
default
{
state_entry()
{
initAll();
}
on_rez(integer start_param)
{
initAll();
}
sensor(integer n)
{
numAvatarsNearby = n;
nearestAvPosition = llDetectedPos(0);
integer i;
for (i=0; 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) {
}
timer() {
}

}
//Vint rawkz!5Really!!!5What you mean you don't agree?5Well, then you cant haz my company!

touch_start(integer n) {
touch1();
}
collision_start(integer n) {
}
listen(integer channel, string name, key id, string msg) {
}
timer() {
}

}

5 Responses to “Scratch for Second Life so even Vint can script?”

ArminasX wrote a comment on July 23, 2008
MyAvatars 0.2

This looks very interesting, as it will enable those interested in creating, but not able to script to get going. However, for us who can script, it likely will have limitations and I can see that there would be difficulty building more complex functions with this approach. But that’s OK, since it’s not intended for actual scripters. It’s for non-scripters.

Alex Lapointe wrote a comment on July 23, 2008
MyAvatars 0.2

Interesting. Reminds me of the old RCX Code GUI for the LEGO Mindstorms kits.

morpheus_1999 wrote a comment on July 23, 2008
MyAvatars 0.2

Hi, i don’t know about that tool you are chatting here in your blog, but as i can understand it is still in alpha.
As far as i am able to get reading the snipped code you publiched in your blog, the application seems to be not able handle the parsing of the programming objects blocks you choosed (during the design of your script) and then to create the script on it.. it instead puts all the possible basic functions (present in the tool) inside the script.. so that is the fact it may result difficult to read by a newbie.

Concerning with the script that let your object to de-rez by itself after a while.. here i am posting the script .. i didn’t tested it.. but due the simplicity i think there must be not errors.

float i_secs_to_disappear = 200;
default
{
state_entry()
{
}
on_rez(integer start_param)
{
llSetTimerEvent(i_secs_to_disappear);
}

timer()
{
llDie();
}
}

Gaynor Gritzi wrote a comment on July 23, 2008
MyAvatars 0.2

I had a play with this at the weekend. It doesn’t make the most efficient code, and it it’s not very lag friendly, but for those who don’t know the first thing about LSL coding it’s a useful starter. And I like the Lego-like snap together interface.

And it avoids all that wasted time when you forget to put the correct brackets in the right places (which must add up to days of my time over the course of a year.).

Vint Falken wrote a comment on July 25, 2008
MyAvatars 0.2

Alex, they should combine Scratch for Second Life with this playmobil experiment? ;)

Gaynor, I totally agree with the brackets, or even worse, spelling errors that bork code. You spend ages staring at something, wondering what you did wrong, and then you just, somewhere mistyped ONE STUPID F*CKING LETTER!!!! :d

And thank you, Morpheus. At least your piece of code I understand. =)))

Care to comment?