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.