Monday, December 17, 2007

Building A Second Life Checkers Game #4

I was making good progress on the checkers game, but something was nagging at me in the design and I decided to take a step back and see if there might be a better way. In the current design, I've been saving the initial placement of all the pieces (as a list of vectors) on initialization and using those locations when people move or choose the location to move a piece. I had been avoiding calculating the locations programatically for no other reason than it seemed like it might take more thought (ie. lazy). But then there was the nag in my head that said it needed to be investigated, so I took the board apart and made a new one just to test how difficult it would be to programatically calculate the locations on the board.

Here is a screenshot of my mockup environment.

The positions would be numbered 0-63. The starting location would be 0. Touch the green button and the piece would move to the next position.

The first problem is to find the size of the board. Since it is the root object it's pretty easy to find. To note, if it's not the root prim you are screwed and it is very difficult, and I'll talk about that in the next post. The size (PRIM_SIZE) is the actual size of the object in meters. When calculating the location on that prim with local coordinates, the origin is at the center of the prim.

list params = llGetPrimitiveParams([PRIM_SIZE]);
vector sz = llList2Vector(params,0);


Next is to calculate the x and y row and column. This is also very simple given there are 8 rows and 8 columns.

integer px = curPos / 8;
integer py = curPos % 8;


I then calculate the size of each square and save it.

float sqr_x_size = sz.x / 8;
float sqr_y_size = sz.y / 8;


Once you have all that it is fairly straight forward to calculate the location of that square, but look at all the comments and see how many tests I went through to get it right. Tons of experimentation here.


//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 >;

//vector p = < (sqr_x_size * px) - (sz.x/2),
// (sqr_y_size * py) - (sz.y/2),
// 0.1 >; // the position without centering on the square
//vector p = < sz.x/2, sz.y/2, 0.1 >; // the origin
//vector p = < -sz.x/2, -sz.y/2, 0.1 >; // the negative origin - this didn't work
//vector p = <0,0,0.1>; // the 0,0 location which is the center of the board
//vector p = <(sqr_x_size/2),(sqr_y_size/2),0.1>; // just a futile experiement to offset from the origin some amount
//llOwnerSay(" new pos = "+(string)p); // the debug message seen over and over and over again

Given all that, the actual calculation that I used can be summed up like this.
For x: The location given the x location minus the offset because the origin is the center + the offset of half a square to center on the square.
For y: Same as x
For z: Just a number which I twiddled to get right.

The last step is to set the actual location. This is pretty simple given that you know the link num for the piece you want to move. This number was retrieved on initialization. After that, change curPos and wait for the next touch.

llSetLinkPrimitiveParams(pieceLinkNum,[PRIM_POSITION,p]);
curPos++;


And yes, this was more work, but as I expected it will be a lot more efficient and easier to work with in the long run.

No comments: