Wednesday, July 9, 2008

Second Life Physics Scripting - Pinball #9 - Scoreboard Extension

I next extended the score board to have more digits. This was pretty simple, just copying the numbers and then copying and pasting parts of the scripts to add the extra digits. I first did this as a standalone scoreboard, then moved it onto the pinball machine.

Tuesday, July 8, 2008

Second Life Physics Scripting - Pinball #8 - Script Management

I was starting to do some more development on the walls and bumpers and started finding that managing the scripts in only four walls and three bumpers was going to be a problem, especially when I was planning on adding a bunch more of each in the next few iterations.

I went back and added a LinkNanny system described in the Tic-Tac-Toe Tutorial on the LSLWiki.

This was a bit of work and it took some tweaking to get it right. I ended up creating a different LinkNanny for the bumpers and a separate one for the walls. This allowed me to have separate sets of scripts for each. I was thinking it might be better to keep all this information in the RootNanny and do the distribution based on the prim name, but it was already setup this way so I'm going to just go with it.

My biggest problem with this is when you want to distribute out the latest scripts with "/1 update" it takes a long time. I've been editing directly on one of the objects until I get it right, then copying the script to the root object and the using "/1 update" to distribute them out. It takes too long to be useful in very small changes and my stuff is always so buggy that it takes a million small changes to get even the simpliest stuff working.

Monday, July 7, 2008

Second Life Physics Scripting - Pinball #7 - Linked Object Collision Problems

Linked objects caused some interesting problems with collisions for me. On the physics test from the previous post, I had the back wall which pushed the ball unlinked from the rest of the test. When I joined them together it caused the ball to stop at the bottom like it missed a collision and then would not go again until the ball bumped itself.

This wiki on collisions helped me understand the problem.

http://www.lslwiki.net/lslwiki/wakka.php?wakka=collision_start

The problem being that the back wall became the root prim (the one selected last before the Tools/Link was selected). This means it was getting collision events for both the middle wall collision and the base of the system as the same event. Meaning that the collision never stopped so there was no need to send another collision event. I had to add a blank start_collision method to both the middle wall and the table prims. That fixed the problem.

Wednesday, July 2, 2008

Second Life Physics Scripting - Pinball #6 - Collision Tester


During this process I created a simple test of the ball bouncing through an object. It has a back wall script on the bottom wall which pushes the ball. I could move the middle wall closer and closer to test for the ball bouncing through at different speeds. I left it for a couple of days to test my theories and the speed change I made fixed the problems.

Here is the code for the back wall.


float maxspeed = 0.8;
default
{
collision_start(integer total_number)
{
//llOwnerSay("Collision start");
//llOwnerSay(llDetectedName(0) + " collided with me!");
if ((llDetectedName(0) == "ball") || (llDetectedName(0) == "ball2"))
{
// need to find the direction from the backwall to the ball
vector pos = llGetPos();
list a = llGetObjectDetails(llDetectedKey(0), ([OBJECT_POS]));
vector pos2 = llList2Vector(a,0);
pos.y = pos2.y; // so we don't get any side to side movement
vector pos3 = pos2-pos;
//llOwnerSay("pos = "+(string)pos);
//llOwnerSay("pos2="+(string)pos2);
pos3.x = pos3.x*1.5;
pos3.z = pos3.z*1.5;
float mag = llVecMag(pos3);
if (mag>maxspeed)
{
float magdev = maxspeed / mag;
pos3 = pos3 * magdev;

//mag = llVecMag(pos3);
//llOwnerSay("------adjusted mag down to "+(string)mag);
}
//llOwnerSay("pos3 = "+(string)pos3);
//llOwnerSay("mag3 = "+(string)llVecMag(pos3));
integer ra = (integer) llFrand(1.0)-1;
//pos3.y = pos3.y*ra;
//pos3.y = pos3.y*8;
//llPushObject(llDetectedKey(0), <8,0,1>, <0,0,0>, FALSE);
llPushObject(llDetectedKey(0), pos3, <0,0,0>, FALSE);
}
//llOwnerSay("Collision done");
}
}


The code in the ball was the same as the pinball I showed previously. It really just jiggled once in a while.

Tuesday, July 1, 2008

Second LIfe Physics Scripting - Pinball #5 - Fix Sticky Ball

The problem with the automatic ball moving script was the granularity of the timer event. Once I fixed that (dropped it to 0.01) it when haywire like I expected it would because I was never letting it slow down. I changed it to have a minimum speed and back off the timer to ever half second. This is what I ended up with.
float movebump = 0.1;
float maxspeed = 0.42;
float minspeed = 0.02;
float speeddiff = 0.02;
// http://lslwiki.net/lslwiki/wakka.php?wakka=llApplyImpulse
default
{
state_entry()
{
llSetTimerEvent(0.5); // generate a timer event every 1 second
}
timer()
{
vector ra = <llfrand(movebump),llfrand(movebump),llfrand(movebump)>;
//llOwnerSay("ball moving "+(string)ra);
//llOwnerSay("ball moving mag "+(string)llVecMag(ra));
vector vel = llGetVel();
float velmag = llVecMag(vel);
if ( (velmag>0) && (velmag>maxspeed) )
{
//llOwnerSay("oldvelmag = "+(string)llVecMag(vel));
// need to slow the ball
float magdev = maxspeed / velmag;
vel = vel * magdev;

llApplyImpulse(llGetMass()*vel,FALSE);
//llOwnerSay("newvelmag = "+(string)llVecMag(vel));
}
else if ((velmag==0) (velmag<minspeed)) ra="<llFrand(movebump),llFrand(movebump),llFrand(movebump)>;
//llOwnerSay("ball moving "+(string)ra);
//llOwnerSay("ball vel 0 move bump = "+(string)llVecMag(ra));
// give the ball a little wiggle
llPushObject(llGetKey(), ra, <0,0,0>, FALSE);
//llOwnerSay("push mag = "+(string)llVecMag(ra));
}
}
}

Monday, June 30, 2008

Need Second Life volunteers for a quick photo

The Second Life pinball machine is coming along nicely. I'm over two weeks ahead on blog posts so you should be seeing slow, but significant progress over the coming weeks.

I'm getting to the point where I'm deciding on the art theme of the pinball machine and I've decided I would like to make it a "Second Life pinball machine". That means that I need to get some good in world photos that might work as the score board and table background. If you would be interested in being notified (and possibly immortalized on a pinball machine) when I would get everyone together, please send me an e-mail (wood@side8.com, SL:Wood Wheels) and I'll pick a time and a date for the shoot. Also if you have ideas for a place that might work well I'm open to suggestions. I'm thinking something on a hillside or pyramid shape will allow more people to fit in the sort of vertical space I'm needing.

Friday, June 27, 2008

Second Life Script - Number Texture Display #2

Once I had a working number I had to place a few of them in sequence and allow a master prim to control them.



I had to first have a way for the root prim (the scoreboard) to send a message to the individual numbers telling them to update their display. I did this with a link message.


integer MSG_SET_NUM = 141; // arbitray message number
link_message(integer from, integer msg_id, string str, key id)
{
if (msg_id == MSG_SET_NUM)
{
curval = (integer)str;
llOwnerSay("setting num to "+(string)curval);
doOffset();
}
}


Then in the root prim I had to find the individual link numbers of the linked numbers. This is done like this. You first iterate through the list of links and find the ones named 1,10,100,1000 and save their link number. An avatar siting on the item will have a key and will have the highes link number so you have to skip them.

integer thousands = -1;
integer hundreds = -1;
...
state_entry()
{
integer current_link_nr = llGetNumberOfPrims();
// Check if it's more than one
if (1 <>0)
{
if (llGetLinkName(current_link_nr)=="1")
{
llOwnerSay("found 1: "+(string)current_link_nr);
one = current_link_nr;
}
else if (llGetLinkName(current_link_nr)=="10")
{
ten = current_link_nr;
llOwnerSay("found 10: "+(string)current_link_nr);
}
....
current_link_nr--;
}
}
}


Then when the number is changed, you send get the value of the thousands, then hundreds, then tens, then ones and send each on to the various number displays.



integer MSG_SET_NUM = 141; // the same message id defined in the number
doSendNumbers()
{
integer itmp = curval;
if (itmp>10000)
itmp=9999;

integer i;
i = itmp / 1000;
llOwnerSay("thousands = "+(string)i);
if (thousand!=-1)
{
// send the value to the thousands number
llMessageLinked(thousand, MSG_SET_NUM, (string)i, NULL_KEY);
}
itmp = itmp - (i*1000);

i = itmp / 100;
llOwnerSay("hundreds = "+(string)i);
if (hundred!=-1)
{
llMessageLinked(hundred, MSG_SET_NUM, (string)i, NULL_KEY);
}
itmp = itmp - (i*100);
.....


I had one more method on the scoreboard. When an avatar touches the scoreboard I increment the value and send the value out to the various individual numbers.

touch_start(integer total_number)
{
curval++;
if (curval>9999)
curval = 0;

doSendNumbers();
}

Thursday, June 26, 2008

Second Life Script - Number Texture Display

I needed a number display system. I've seen these in other items, but as usual I'm willing to just figure it out and build my own.

I first created a simple 16x256 pixel image of all the numbers. I made 16 numbers because textures are all power of two stuff. I doubt I'll do any hexadecimal displays, but I had the extra room.


I then used hit and miss to figure out the offsets for the numbers. I found the change from one number to the next was about 0.6 then worked backwards and forwards until I had both edges and -0.47 and +0.47. To make this programticall, I used (0.47*2)/16 to get 0.05875 which I tried, then finally figured out that I needed to divide by 15 because one cell is not counted in the actual size. That gave me an offset of 0.62666666.

integer curval = 0;
doOffset()
{
float ftmp = -0.47+(curval*0.062666);
llOwnerSay("offset ="+(string)ftmp);
llOffsetTexture(ftmp,0.0,ALL_SIDES);
}

default
{
state_entry()
{
doOffset();
}

touch_start(integer total_number)
{
// u offsets
//0=-0.47
//1=-0.41
//2=-0.35
//3=-0.29 *
//4=-0.22
//5=-0.16
//6=-0.10
//7=-0.04 *
//8=0.03
//9=0.09
//A=0.15
//B=0.21
//C=0.28 *
//D=0.34
//E=0.41
//F=0.47
// 0.47*2/15 = 0.06266666
curval++;
if (curval>16)
curval = 0;
doOffset();
}
}

There is one glaring problem I noticed after I got the number image uploaded. I probably need one cell that is only transparent so I can turn of the number. I'll do this later with another image upload and use -1 to display the blank number, but then I will not be able to show a hexadecimal number! Oh well... I'll probably end up making the other columns numeric symbols like dollar, pound, comma, period and such.



Make sure you apply the number texture to only one face. The rest of the faces are completely transparent. A simple trick is to select the texture face on the build dialog, then click on the individual faces to apply the texture to only that face.

Wednesday, June 25, 2008

Second Life Physics Scripting - Pinball #4

I need to make the ball a little smarter when it got stuck on the lower slope. I got a little crazy and added something to try and keep it at a constant velocity of 0.42M (the size of the ball), since anything faster than this can cause it to move through a wall across a frame rate. This is the final script.

float movebump = 0.2;
float maxspeed = 0.42;
float speeddiff = 0.02;
// http://lslwiki.net/lslwiki/wakka.php?wakka=llApplyImpulse
default
{
state_entry()
{
llSetTimerEvent(1.0); // generate a timer event every 1 second
}

timer()
{
//vector ra = <llFrand(movebump),llFrand(movebump),llFrand(movebump)>;
//llOwnerSay("ball moving "+(string)ra);
//llOwnerSay("ball moving mag "+(string)llVecMag(ra));
vector vel = llGetVel();
float velmag = llVecMag(vel);

// check for anything above or below the maximum velocity
if ( (velmag>0) && ((velmag>maxspeed+speeddiff) || (velmag<maxspeed-speeddiff)))
{
//llOwnerSay("oldvelmag = "+(string)llVecMag(vel));
// need to slow or speed up the ball
float magdev = maxspeed / velmag;
vel = vel * magdev;

// take that new velocity and apply it to the ball
llApplyImpulse(llGetMass()*vel,FALSE);
//llOwnerSay("newvelmag = "+(string)llVecMag(vel));
}
else if (velmag==0)
{
// need to give it a random bump!
vector ra = <llFrand(movebump),llFrand(movebump),llFrand(movebump)>;
//llOwnerSay("ball moving "+(string)ra);
llOwnerSay("ball vel 0 move bump = "+(string)llVecMag(ra));
// give the ball a little wiggle
llPushObject(llGetKey(), ra, <0,0,0>, FALSE);
}
//llOwnerSay("ball vel "+(string)llGetVel());
// give the ball a little wiggle
//llPushObject(llGetKey(), ra, <0,0,0>, FALSE);
}
}

I think there are still problems with it, because it should have been a problem in that it would never slow down and move down the board in the way I "thought" I coded it, but it does work, but it also still gets stuck briefly. I had to add the check for 0 velocity at the end because it was getting divide by zero and that else (velmag==0) piece of code was all I really needed in the first place. I'll have to revisit it, but at least it doesn't get stuck indefinitely any longer, but it still gets stuck a lot more than I'm willing to live with.

Tuesday, June 24, 2008

Second Life Physics Scripting - Pinball #3

The ball kept getting stuck, and I was looking for clever ways to keep it from getting stuck. This is what I tried as a script within the ball itself.

float movemax = 0.002;

default
{
state_entry()
{
llSetTimerEvent(1.0); // generate a timer event every 1 second
}

timer()
{
vector ra = ;
//llOwnerSay("ball moving "+(string)ra);
//llOwnerSay("ball moving mag "+(string)llVecMag(ra));
// give the ball a little wiggle
llPushObject(llGetKey(), ra, <0,0,0>, FALSE);
}
}

A simple timer and an added small push no matter if it needs it or not. This worked well for the steeper grade, but it still gets stuck on the machine with the "curved" slope (two planes one steeper than the other). It keeps getting stuck in the angle between the two planes. I either need to check the velocity every second and give it a bigger push if it is lower than some amount, or place some sort of accelerator in the area of the change in angle. It'll probably need to be some sort of combo between the two because I think I'll need the accelerator to make the game more interesting since the ball seems to have lost a lot of momentum once it reaches the "curve".

Monday, June 23, 2008

Second Life Physics Scripting - Pinball #2

The problems I was seeing with missed collision detection were (I believe) mostly fixed. The velocity of an object cannot exceed it's size or it may travel farther than itself in a single frame tick.


float maxspeed = 0.42;
default
{
collision_start(integer total_number)
{
if (llDetectedName(0) == "ball")
{
// need to find the direction from the backwall to the ball
vector pos = llGetPos();
list a = llGetObjectDetails(llDetectedKey(0), ([OBJECT_POS]));
vector pos2 = llList2Vector(a,0);

// direction from wall to ball
vector pos3 = pos2-pos;

pos3.x = pos3.x*4;
integer ra = (integer) llFrand(1.0)-1;
pos3.y = pos3.y*ra;

/// check the magnitude of the speed versus the max speed
float mag = llVecMag(pos3);
if (mag>maxspeed)
{
// limit the speed to the max magnitude
float magdev = maxspeed / mag;
pos3 = pos3 * magdev;
}
llPushObject(llDetectedKey(0), pos3, <0,0,0>, FALSE);
}
}
}

The Lindens chose this method to increase performance. The alternative would be to check for collisions multiple times during each fram as something moves. It would be nice if there were one more flag where you could turn on a finer granularity on an object, say multiple checks based on it's size, but for now it will have to be a slower game if I'm going to continue making a pinball game.

I created this contraption to test my scripts for bounce through. The pink ball just bounces up into the first wall and goes through it if it is going too fast. It hits the top wall without losing the ball and I can rerun the test. I have been able to get away with a little larger value than the object size because there is some immedite friction before it reaches the wall, but that just means that it WILL still happen so I'll need to account for a ball off the table, but it should/better be infrequent.

Friday, June 20, 2008

Second Life Physics Scripting - Pinball

I have been continuing to learn Linden Scripting Langage (lsl) in Second Life. I was stuck for a while on a board game and 2d video game idea and decided to move on to something else. My latest round of work has been learning the physics engine. I've been doing this by building a pinball machine.



The basics of this are in the collision_start method in the backwall of the pinball machine. The ball is a simple sphere with physics turned on.

default
{
collision_start(integer total_number)
{
//llOwnerSay(llDetectedName(0) + " collided with me!");
if (llDetectedName(0) == "ball")
{
// need to find the direction from the backwall to the ball
vector pos = llGetPos();
list a = llGetObjectDetails(llDetectedKey(0), ([OBJECT_POS]));
vector pos2 = llList2Vector(a,0);

// subtract the two positions to find the direction from the wall to the ball
vector pos3 = pos2-pos;
//llOwnerSay("pos = "+(string)pos+" pos2="+(string)pos2);
//llOwnerSay("pos3 = "+(string)pos3);

// do some multiplication to get a big bounce
pos3.x = pos3.x*4;

// change the y direction in minute ways to create a random bounce
integer ra = (integer) llFrand(1.0)-1;
pos3.y = pos3.y*ra;

// push the ball in that direction
llPushObject(llDetectedKey(0), pos3, <0,0,0>, FALSE);
}
}
}

This failed miserably since the ball kept leaping off the table. After a lot of reading it turns out that the collision system in second life only does detection on each frame so if the ball is moving fast it will be across the wall between frames and you have to go hunting for the ball. I'll discuss solutions in upcoming posts.

Thursday, June 19, 2008

Second Life Land Grab - Bay City #2

I've been thinking more about the Bay City Land grab. It seems like the Lindens struck a nerve and people are really excited about this new land. To me, the difference is in the roads and always having an open side to your land. Up until now it seemed like you could get trapped very easily by a couple of bad neighbors unless you wanted to spend a lot on a big space and waste a lot of it.

Then I was wondering why the Lindens didn't think about this earlier? They've said their initial designs came directly from Snow Crash and as I remember, roads were an integral part of the VR system in the book, so why no roads until now? Seems odd to me, but at least they finally figured it out. Now all they need to do is replicate this land style a few hundred more times and get the prices out of the stratosphere!

For those starting new sims, know that open space is important to a community and greatly affects land prices.

Here is a screen shot of a map that shows how much of this brand new land is up for sale. All the prices are over $200,000L (about $100US) for 1024. Here is a link/slurl for Bay City Imaginario (http://slurl.com/secondlife/Bay%20City%20-%20Imaginario/28/70/45) so you can check out those prices for yourself.

Wednesday, June 18, 2008

Second Life Land Grab - Bay City

I finally decided to get a little piece of my own land in Second Life. I was searching around trying to find a good plot for some simple builds and a group I'm helping. I was watching auctions and searching around and noticed the new community of Bay City being auctioned off. People were paying more for 1024 meter ($190,000L = $700US) plots than they were paying for a whole island. I'm not sure that these people are completely sane, but the land they were buying was nice. It had a lot of open space and roads between plots so land was not sandwiched in between four different neighbors.

Try visiting that area now, it is mostly baren with most of those 1024M plots up for resale for around $280,000L=$1000US. Crazy.

I passed on Bay City then noticed that the Lindens are starting to build roads through some of the older sims. I found a spot that had the acquired land for roads next to it, but no road built yet. I bought a small 512M plot next to that. I paid about $11L/Sq meter. Land value is basically around $7L/Sq Meter unless it has some geography. Since it was going to be next to a road I figured it was worth a little more and it fit my needs. I don't like all the subdividing that is going on along the roads. People selling tiny plots next to the roads for pure advertising. I found one that does not have a lot of that going on.

Thursday, March 20, 2008

Still Learning Second Life Script

I'm still learning a lot about Second Life scripting even though I've been through a couple of rounds of what I would call significant digging. The latest learning as you might know from this blog was a Tic-Tac-Toe game I built. I've been working on another game, still in the rough draft phase trying to discern what might be possible and found a page of examples on the lsl wiki.

http://www.lslwiki.net/lslwiki/wakka.php?wakka=examples

I was digging through these and found a full example of building a Tic-Tac-Toe game. Had I known this existed, I probably wouldn't have read it before hand anyways because I sometimes like to figure stuff out then compare to how others solve the same problems.

http://www.lslwiki.net/lslwiki/wakka.php?wakka=ExampleTicTacToe

As I expected, we went about it in different ways, but I do think that both solutions are fairly equal. I did force people to sit at the table and after I was done thought it would be better to have people simply click to choose sides. He did the later and it is a lot easier to setup than the code I did to watch for people that sit down. Good learning experience though.

One of the best parts of this example is the discussion on Version Control. (http://www.lslwiki.net/lslwiki/wakka.php?wakka=ExampleTicTacToeVersionControl) This is something I struggled with early on and he has a very elegant solution to keep all the scripts in the root prim and copy them out to the child prims when you type "/1 listen" into chat. I spent a lot of time reading this section of the example and will probably be implementing something similar for a future game. A full example isn't given and it glosses over some of the aspects that make this a general use solution so this is definitely going to be home grown.

I haven't really gotten past that page yet and I'll have to spend some time to finish it. Very worthwhile reading.

Monday, March 3, 2008

My Web Comic Turned 50

A little over a year since I first turned over my daily Farside calendar and started drawing my own webcomic (in about 30 seconds as the drawing is very simple), I've reached 50 posts and decided to take a break. It was fun and I still have some more I could post, but it doesn't feel healthy to put my mind in such a negative light and keep producing these. I recently read C.S. Lewis' Screwtape Letters and his prologue mention his unwillingness to continue writing more letters (even given high reader/publisher demand) because of the negative attitude this produced within himself. This hit home. Not to compare myself to C.S. Lewis as he is a huge figure, it just reminded me of my own feelings on the subject of writing in a negative light. Hopefully I'll be inspired to write a more positive webcomic, but then I'll probably have an even smaller audience than I've garnered with this one. Of course the final webcomic is one that mocks myself so it seemed an appropriate place to take a break.

http://facebigelow.blogspot.com

Monday, February 4, 2008

Video Gaming Causes Decline In National Park Activity

http://news.yahoo.com/s/ap/20080204/ap_on_sc/communing_without_nature_8

I'm not sure if this is good news or bad news since I both like video games and I'm an avid backpacker. It means both that there will be more games to play, and less people and trash on the trails.

On the other hand it probably means less maintenance on trails and less protection in the long run, since people will be less likely to fight to save a resource they don't have any interest in. Let's just hope it means more games and less people and not the consequences for public lands and conservation.

Thursday, January 24, 2008

Torque Closer To Browser Based Games

Torque is a popular game engine in the indie 3d game development community. Lately there has been a lot of buzz about a new browser based plugin that will allow people to play fps (or walk-through virtual world) style Torque games directly from the browser. The site is still not live, but it did open recently for signups and looks to be much closer to a final launch.

http://www.instantaction.com/

There still isn't a lot of information on how this works, the business model or if independent developers will be able to launch titles on the site. At least I have not found much. It is looking more like it might be the equivalent to the next generation gaming console right in your browser so it looks like something interesting to watch. Stay tuned.

Tuesday, January 22, 2008

Second Life Wiki Of Choice

When learning Second Life scripting, there are two choices of wiki. One is owned by Linden Labs and the other is independent. I've found the independent one to be a much better resource, but your ideas may be different.

The Independent Wiki http://www.lslwiki.net/lslwiki/wakka.php?wakka=HomePage

The Linden Labs Wiki http://wiki.secondlife.com/wiki/LSL_Portal

When I have added content, I have tried to add it to both sites, but it seems more people use the lslwiki.

One issue with the independent Wiki is ownership. It isn't clear if people who have posted to the wiki have given up their copyrights while the Linden Labs Wiki puts all information posted to the wiki in the public domain. I wonder if there will be some kind of crazy court case concerning intellectual property and public wiki's in the future? It seems inherent to me that if you post something on a wiki you are giving up your copyright, but I do see that it needs to be clearly stated or some kind of legal settlement needs to happen.

Monday, January 21, 2008

My Next Second Life Creation

I've moved on from the Tic-Tac-Toe game. I took a break as I got really busy, but I finally got some extra time in and went back and did some more digging into SecondLife Script.

In this next game I'm thinking it will have a bunch of moving parts. So instead of creating all the objects at build time, I think they need to be generated programatically instead of creating them at build time. So, I found myself looking into Inventory functions and specifically llRezObject.

My initial attempts were flawed. I called my object "puck" and linked it to the object that had the script. This is the way I did it in Tic-Tac-Toe. Then I called


llRezObject("puck", llGetPos(), ZERO_VECTOR, ZERO_ROTATION, 1);

Thinking it would create the new puck object. No luck and no error and nothing at all happened.

Then I found that inventory page and read it more closely. "SL has two kinds of inventories, one for users and one for objects".

I finally read somewhere else, I can't find it now. That an object's inventory is actually under the "contents" tab when editing. So I unlinked the puck. Took it into my own inventory, then dragged the puck to my game's contents. Wala! It was there with the main script and sure enough the script created a new puck at the center of my other object with I touched a lever I had setup to test this. Overall, a good learning experience.

Thursday, January 10, 2008

Building A Second Life Checkers Game #9

I've finished the tic-tac-toe game. It works and I've played it a couple of times with people. Probably a few more bugs and I could make it look a little nicer as well, but overall the goal is complete, I built a two player table top game. I'm trying to figure out if I'm going to go back and finish the checkers game. I don't think I will because there doesn't seem to be a lot of value in it. I would like to be more innovative than that and make a new game that is only available virtually. Something completely new. I started on and I'll have a screen shot of that tomorrow, but it doesn't have any mechanics besides something in my head. When making video games I typically just get a mechanic working then tweak it until some sort of game forms. So, after 9 posts I feel like I may have mislead you into thinking we would have a checkers game and here we end up with a lot less, except for the knowledge that it is possible and I have most of the functional code required to do it. Anyway, I'll keep digging until I have something more. If you want a copy of the tic-tac-toe game, send me and e-mail or find me in SL: Wood Wheels.


One thing I don't think that shows here is that the green triangles point in the direction of the player who's move it is. When the player makes a move I change the rotation of all the green triangles like this.

RotateBtns(integer xoro)
{
integer itmp;
integer btnnum=0;
integer btnlinknum;
integer pos = 0;
rotation rot;
if (xoro==1)
{
rot = rot0;
}
else
{
rot = rot180;
}

for (;pos<9;pos++)
{
itmp = llList2Integer(btnpositions,pos);
if (itmp>=0)
{
llSetLinkPrimitiveParams(itmp,[PRIM_ROTATION,rot]);
}
}

}

This may look pretty simple, but it has a trick. I originally tried to just rotate 180 around the z axis, but as I should have known before I started this would not work as rotations are cumulative and coming up with a rotation axis is not trivial piece of math. Quaternians are simply a vector associated with a rotation angle around that vector. They can be hard to calculate programatically.

The solution? Find the two rotation angles at build time and use them as static values.

vector eul = <0,77,0>; //0 degrees around the z-axis, in Euler form
eul *= DEG_TO_RAD; //convert to radians
rot0 = llEuler2Rot(eul); //convert to quaternion
eul = <3.9,283.0,184.45>; //180 degrees around the z-axis, in Euler form
eul *= DEG_TO_RAD; //convert to radians
rot180 = llEuler2Rot(eul); //convert to quaternion

Cheating I know, but most programming is about getting something to work, even though it might not be the most elegant solution. At least that is how I work. Why not the elegant solution you ask? One takes hours and hours and one just a few minutes and schedule is usually the most important element of all. Also, if it really needs to be elegant, it is better to get it working and come back in later if it really needs it.

Wednesday, January 9, 2008

Building A Second Life Checkers Game #8


Success. I finally got to the point where I could do some testing. Finding someone to help you test is incredibly hard. I have about 30 people listed as friends and of course no one is online. I start asking around at various sand boxes and no one will even lift a finger. Finally one of my friends logs on and it is the person who was harassing me just a while back. I ask him and he is more than eager to help.


So it really pays to try and make friends even when someone is being rude.

There were a couple of small problems, but overall it is working. The next step is to redo the "programmer art" and make it look a little more professional. I've been watching HGTV to try and decide on a color scheme for the game that will fit in most residences. Yeah right.

Tuesday, January 8, 2008

Building A Second Life Checkers Game #7

The checkers game, currently the tic-tac-toe game is coming along nicely. I made really great progress last night.


The pieces are placed in order as the green traingles are touched. The peices are stored in a box below the table and the triangles are swapped to that location when the triangle is touched. Next I'm starting to work on the seating and makeing the game play depended on two avatars seated on the "chairs". The little green button on the left resets the entire board.

Here is some code to move a piece to a specific location on the board.

MoveLinkToPos(integer iLinkNum, integer posWhere)
{
key rootKey = llGetLinkKey(1);
list params = llGetPrimitiveParams([PRIM_SIZE]);
vector sz = llList2Vector(params,0);

//llOwnerSay("area size = "+(string)sz);
integer px = posWhere / 3;
integer py = posWhere % 3;
//llOwnerSay(" px="+(string)px+" py="+(string)py);

// set the position of the piece to 0,0
float sqr_x_size = sz.x / 3;
float sqr_y_size = sz.y / 3;
//llOwnerSay(" sqr_x_size="+(string)sqr_x_size+" sqr_y_size="+(string)sqr_y_size);
vector p = < (sqr_x_size * px) - (sz.x/2) + (sqr_x_size/2),
(sqr_y_size * py) - (sz.y/2) + (sqr_y_size/2),
0.051 >;
//llOwnerSay(" new pos = "+(string)p);
llSetLinkPrimitiveParams(iLinkNum,[PRIM_POSITION,p]);
}


Most of this code was outlined in post #4. I just consolidated it into a function.

And some code to move a piece into the storage box.

MoveLinkToStorage(integer iLinkNum)
{
llSetLinkPrimitiveParams(iLinkNum,[PRIM_POSITION,vStorage]);
}


When the game starts up I store the location of all the buttons.

 for (i=2;i<=iNumPrims+1;i++)
{
string stName = llGetLinkName(i);
key keyObj = llGetLinkKey(i);

list a = llGetObjectDetails(keyObj,([OBJECT_POS]));
vector p = llList2Vector(a,0) - regionPos;

if (stName=="movebtn")
{
btnlinks = llListInsertList(btnlinks,[i],0);
}
else if (stName=="xtile") ....
}
ShowAllBtns();


Here is the ShowAllBtns function.

ShowAllBtns()
{
integer itmp;
integer btnnum=0;
integer btnlinknum;
integer pos = 0;
vector v;
for (;pos<9;pos++)
{
itmp = llList2Integer(board,pos);
if (itmp==-1)
{
btnlinknum = llList2Integer(btnlinks,btnnum);
//llOwnerSay("move linkbtn="+(string)btnlinknum+" to pos="+(string)pos);
MoveLinkToPos(btnlinknum,pos);
btnnum++;
}
else
{
btnlinknum = -1;

}
//llOwnerSay(" adding btnlinknum="+(string)btnlinknum+" at pos="+(string)pos);
btnpositions = llListReplaceList(btnpositions,[btnlinknum],pos,pos);
}
//llOwnerSay("btnpositions="+(string)btnpositions);

// move the rest of the buttons to the storage
for (;btnnum<9;btnnum++)
{
btnlinknum = llList2Integer(btnlinks,btnnum);
MoveLineToStorage(btnlinknum);
}
}


I'm going through all the board positions looking for a -1 meaning that the location doesn't already have a piece. If it doesn't, I move the first (then next) button to that location and save which button is at that point in the btnpositions list. At the end I move the rest of the buttons that were not used to storage so they are not visible. Using this list, when a button is touched we can convert the link number given by the touch event into a position on the board by traversing the btnpositions list.

integer FindBtnPos(integer iLinkNum)
{
integer count = llGetListLength(btnpositions);
integer itmp;
integer i=0;
for (;i<9;i++)
{
itmp = llList2Integer(btnpositions,i);
//llOwnerSay("btn at pos "+(string)i+" = "+(string)itmp);
if (itmp==iLinkNum)
{
return i;
}
}
return -1;
}

Monday, January 7, 2008

Building A Second Life Checkers Game #6

Detour number 6 is what this post should really be called. After the experiment for positioning the pieces programatically instead of based on the initial layout of the pieces I decided to take a step back and finish something simpler first. I took that sample board and turned it into the first draft of a tic-tac-toe game.



The green triangles will be the location to click on to place the actual piece during play. The texture for the board is the same as the checker board, a 2x2 grid repeated the correct number of times. In the case of checkers it was repeated 4 times in the x and y direction. In the case of tic-tac-toe it is repeated 1.5 times in each direction then I had to offset the u and v direction by 0.75. I'm not smart, I just fiddled with it until it worked.

That said, I think this step back will actually be a step forward since I'll be able to do most of the work for the checkers game in a scaled down fashion, then take that knowledge to the larger game.

Wednesday, January 2, 2008

Building A Second Life Checkers Game #5

As I mentioned in the previous post, finding the size of a non-root prim from a Second Life Script in the root prim is a pain.

What really makes this a puzzler is that setting the size is pretty darn simple. So I'll look at that first.

I used this same call in the last post to set the position


llSetLinkPrimitiveParams(pieceLinkNum,[PRIM_POSITION, vectPos]);


It is the same call you would use to set the size.

llSetLinkPrimitiveParams(pieceLinkNum,[PRIM_SIZE, vectSize]);


So what's the big problem? Well, there is no llGetLinkPrimitiveParams. What!?! But what about this code to get the location using the object key.


key pieceKey = llGetLinkKey(pieceLinkNum);
list d = llGetObjectDetails(pieceKey,[OBJECT_POS]);
vector vPos = llList2Vector(d,0);


That seems simple enough, except when you look closer at llGetObjectDetails it is not llGetPrimitiveParams. Object details are only a few parameters like name, description and size. Then if you dig for an hour like I did, you find that there is no 'easy' way to get PrimitiveParams given a link number or an object key. You will find it on a Wish List of functions and can just give up until someone finally decides to implement this function that seems so useful, but seems to have been overlooked.

Or you could be like me and figure out a way to get it done, knowing the mantra "There is always a way. There is always a way. There is always a way." I can't seem to find it now, but there was some obscure reference on some wiki page that said it didn't exist and you have to use link messages. So here goes.

Link messages are a way to send messages from one prim to another in a link set (link sets are groups of prims, but group would have been too confusing the Linden folks say). Even though it is frowned upon to have scripts in all the linked objects, this is the only way to get this done.

When I traverse the list of pieces I look for the name of my piece to get the linknum of that piece.


integer i=2;
integer count = llGetNumberOfPrims();
for (;i>count;i++)
{
string stName = llGetLinkName();
if (stName == "thepiece")
{
// send the link message
pieceLinkNum = i;
llMessageLinked(pieceLinkNum,PRIM_SIZE,"43",NULL_KEY);
}
}


I'm sending PRIM_SIZE as the message number just because I don't have any other messages. In good practice I could probably send a number that is associated with some hair brain scheme I came up with on the fly, but since there is only one message to this prim, anything will do. Normally I would chose 42 since that is the most important number in the universe. Which seems strange in that I did use 43 for the message number that will be used when returning the size to this root prim from the linked prim.

Inside the linked prim, I need to setup a way to receive that message. It is the only thing that script does, which is a good thing. Avoid scripts in linked prims if at all possible.


default
{
link_message(integer sender_num, integer num, string ret_num, key id)
{
if (num==PRIM_SIZE)
{
list re_list = llGetPrimitiveParams([PRIM_SIZE]);
//llOwnerSay("re_list = "+(string)re_list);
llMessageLinked(LINK_ROOT,(integer)ret_num, re_list, NULL_KEY);
}
}

}


This gets the size using llGetPrimitiveParams([PRIM_SIZE}) which returns the correct value since this is in the prim we want the size from. I then return this size to the LINK_ROOT using the message number that was passed in the link message.

Back to the root prim, here is the code to receive that message.


link_message(integer sender_num, integer num, string list_argument, key id)
{
if ((sender_num==pieceLinkNum) && (num==43))
{
//llOwnerSay(" list_arg = "+(string) list_argument);
pieceSize = (vector) list_argument;
//llOwnerSay("received pieceSize="+(string)pieceSize);
}
}


I save that size into the global variable pieceSize which I can then use in other calculates.

Note, I went through this whole exercise thinking I needed the size of the piece to calculate it's location on the board because I would have to center the piece somewhere. After all that, and a ton of failed attempts I remembered that the origin is automatically the center and I didn't need a single line of this code. Gotta love programming for the amount of time I've wasted in my life. I'm sure I'll use this elsewhere though.