android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
Notes on all my "fun" programming projects.
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
setVolumeControlStream(AudioManager.STREAM_MUSIC);
default
{
state_entry()
{
//list bb = llGetBoundingBox(llGetKey()); // get my bounding box
vector sz = llList2Vector(llGetPrimitiveParams([PRIM_SIZE]),0);
llMessageLinked(LINK_ALL_OTHERS,1,(string)sz,NULL_KEY);
}
touch_start(integer total_number)
{
//llSay(0, "Touched.");
llSay(1024,"move");
}
}
vector mysize;
vector mysizediv2;
vector tablesize;
vector min;
vector max;
integer posMinOrMax;
default
{
state_entry()
{
//list bb = llGetBoundingBox(llGetLinkKey(1)); // get the bounding box of the table
//max = llList2Vector(bb, 1); // max corner
//min = llList2Vector(bb, 0); // min corner
//min = >min.x,min.y,max.z<;
//llWhisper(0,(string)min+" "+(string)max);
posMinOrMax = 0;
llListen(1024,"",llGetLinkKey(1),"move");
}
listen( integer channel, string name, key id, string message )
{
if (channel==1024)
{
if (posMinOrMax==0)
{
llSetPos(max);
//llWhisper( 0, "max" );
}
else
{
llSetPos(min);
//llWhisper( 0, "min" );
}
posMinOrMax = 1 - posMinOrMax;
}
}
link_message(integer sender_num, integer num, string str, key id)
{
if (num==1)
{
mysize = llList2Vector(llGetPrimitiveParams([PRIM_SIZE]),0);
mysizediv2 = mysize / 2;
mysizediv2.z = 0.0;
tablesize = (vector) str;
llWhisper(0,"got size = "+(string)tablesize);
vector sz = tablesize / 2;
min = >-sz.x,-sz.y,sz.z*2< + mysizediv2;
max = >sz.x,sz.y,sz.z*2< - mysizediv2;
}
}
}
default
{ state_entry()
{
llSetTextureAnim(ANIM_ON | LOOP | SMOOTH | ROTATE,ALL_SIDES, 1,1, 0, TWO_PI, TWO_PI/360);
}
}
sensor( integer number_detected )
{
integer i;
for( i = 0; i < detpos =" llDetectedPos(" k =" llGetLandOwnerAt(detpos);" k ="="" k = "+(string)k); // } //} if ( (llDetectedKey( i ) != llGetOwner()) && (k != ">
default
{
state_entry()
{
}
touch_start(integer total_number)
{
llGiveInventory(llDetectedKey(0), "Light Hand Sculpture - notecard");
}
}
effect.LightingEnabled = true;With, specPower and light0Pos set at the top of the class definition.
effect.DirectionalLight0.Direction = light0Pos;
effect.DirectionalLight0.DiffuseColor = Color.White.ToVector3();
effect.DirectionalLight0.Enabled = true;
effect.PreferPerPixelLighting = false;
effect.DirectionalLight0.SpecularColor = Color.White.ToVector3();
effect.SpecularPower = specPower;
Vector3 light0Pos = new Vector3(-0.5f, -1.0f, 0.0f);After weeks of procrastination and thinking I was going to have to create a custom shader it was nice to find out that adding a directional light was very simple. One of the games I was going to do requires a spotlight and I do think that will require a custom shader so it's off the list until I get some extra time.
float specPower = 64;
This was fine, except that the gem came out all smooth.
I know from experience that this is caused by the rendering system using vertex normals to draw the individual triangles of the gem as smoothly as possible. This is almost always the case. But for a gemstone, you want it to have facets. I spent a ton of time trying to figure out how to get blender to remove the vertex normals and only use surface normals.
This ended up being easy inside of blender, but took a while to figure out how to get this information into the direct X file. To do this in blender, you select the object in edit mode, then click the Set Smooth button to use vertex normals for rendering and Set Solid to use surface normals for rendering.Easy and you can see the two rendered versions here.
That’s all well and good in Blender rendering, but XNA kept displaying the gem as a smooth surface. After tons of fiddling, I noticed one of the options on the Blender DirectX exporter was “no smooth”. The tool tip says exactly what I wanted to hear “Every vertex has the face normal, no smoothing”.
Once I exported with that setting everything worked.
The game is called Balance Air. You have a hair dryer at the bottom of the screen and you need to balance the ball on air and force it into the goal. You move the blower left or right to change the direction of the ball. There are 7 levels which are basically just different locations for the goal. The score for each goal is dropping over time.
As I did last time you can download a free executable of the game. It is available here: http://www.woodsgoods.com/gawxna/02balanceair.
The complete source and Visual Studio project is available for $5 at http://store.payloadz.com/go?id=246205 .
Enjoy and please give me some feedback.
I also messed with the publish system in XNA Game Studio 3.0 and created an installer you can use to run the game. It is available here: http://www.woodsgoods.com/gawxna/01skeetslider.
I have also been considering offering the source code to some of these games to try and cover some of my time. It's a minimal $5 to purchase the C# source code to the Skeet Slider XNA Game. Please tell me if you think this is reasonable or not? I could really use some feedback.
This is the map image. Each pixel represents the location of one of the 3d spheres.
Here is the code that reads the map image and converts it to 3d objects.
protected void LoadMap(String mapname)
{
// the map is just an image file with pixels of various colors
// red = barrier
// black = open space
// cyan = closed space
// green = starting point
Texture2D map1 = content.Load<Texture2D>(mapname);
mapwid = map1.Width;
maphei = map1.Height;
int numpix = mapwid*maphei;
if ((map == null) (map.Length < numpix))
{
map = new byte[numpix];
}
// want this map to fit over the circle, so calculate the size of each ball
// scale of the ball
mapballsz = (int)(maxballdist*2) / mapwid;
mapxstart = -(mapwid / 2-1) * mapballsz;
mapystart = -(maphei / 2-1) * mapballsz;
/*
* the sample I used to write this method
* ms-help://MS.VSCC.v90/MS.VSIPCC.v90/MS.XNAGS30.1033/XNA/GetData``1_B8CC1053 */
Rectangle sourceRectangle = new Rectangle(0, 0, mapwid, maphei);
Color[] retrievedColor = new Color[numpix];
map1.GetData<Color>(
0,
sourceRectangle,
retrievedColor,
0,
numpix);
goalcount = 0;
int i;
for (i = 0; i < numpix; i++)
{
if (retrievedColor[i] == Color.Cyan)
{
map[i] = MAP_CLOSED;
}
else if (retrievedColor[i] == Color.Red)
{
map[i] = MAP_BAR;
}
else if (retrievedColor[i] == Color.Blue)
{
map[i] = MAP_GOAL;
goalcount++;
}
else
{
map[i] = MAP_OPEN;
}
}
}
private void checkCollision()
{
// check the position of the ball with that of the spinner
Vector3 vspin; // = Vector3.Backward; // pointed up
Matrix m = Matrix.CreateRotationY(spinnerRot);
vspin = Vector3.Transform(Vector3.Forward, m);
// I know the spinner is 7.2 units before scaling (looking in milkshape)
vspin = vspin * 7.42f * scaleXY;
// now check the position of the ball in z space
if ((ballPos.Length()>vspin.Length()+ballradius) && (distToOriginLineXZ(ballPos, vspin) < ballradius))
{
// collision
// wwh score or death?
score = score + 1;
}
// check if we are inside the center of the spinner
// spinner sphere radius = 2
if (ballPos.Length() < 2 * scaleXY + ballradius)
{
// wwh score or death?
mode = STATE_OVER;
if (score > highscore)
score = highscore;
//score = score + 1;
}
}
private float distToOriginLineXZ(Vector3 pt, Vector3 oline)
{
// used this reference
// http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/
// p1 = 0.0,0.0
// p2 = oline.x,oline.z
// p3 = pt.x,pt.z
// oline is a line from the origin
Vector3 vi = Vector3.Zero;
float u = ((pt.X - 0.0f) * (oline.X - 0.0f) + (pt.Z - 0.0f) * (oline.Z - 0.0f)) / (oline.Length()*oline.Length());
vi.X = u * (oline.X);
vi.Z = u * (oline.Z);
pt = pt - vi;
return pt.Length();
}
// move the paddle using the upVector (true) or the cross product of the origin and up (false)
// direction is a + or - 1 for up, down, left or right
private void movePaddle(bool usingUpVector, float dir)
{
if (!usingUpVector)
{
// need to take the cross between the up and the origin
Vector3 vp = paddlePos;
vp.Normalize();
// vector change
Vector3 vc = Vector3.Cross(vp, paddleUp) * dir;
vc.Normalize();
vc = vc * paddleSpeed;
// find the new location, will be outside the sphere
vp = paddlePos + vc;
// fix up the distance ot the sphere
vp.Normalize();
vp = vp * radius;
paddlePos = vp;
// the up vector should not have changed, right?
// move the camera behind the puck
fixupCamera();
}
else
{
// for this case, take the cross product
// move in the direction of the up vector (+ or -)
// position against the spehere
// use the cross product to calculate the new up vector
}
}
private int rowFromPos(int p)
{
return p / tilerows;
}
private int colFromPos(int p)
{
return p % tilecols;
}
private int posFromRowCol(int row, int col)
{
return (row * tilecols) + col;
}
private void positionXYFromIndex(int idx, ref Vector3 vSet)
{
vSet.X = fLeftX + colFromPos(idx)*gapX;
vSet.Y = fTopY + rowFromPos(idx)*gapY;
vSet.Z = 0.0f;
}
if ((newState.IsKeyDown(Keys.Right)) && (!oldState.IsKeyDown(Keys.Right)))
{
int row = rowFromPos(curSel);
int col = colFromPos(curSel);
col++;
if (col >= tilecols)
{
col = 0;
}
curSel = posFromRowCol(row, col);
positionXYFromIndex(curSel, ref selPosition);
moves++;
score--;
}
else if (oldState.IsKeyDown(Keys.Right))
{
}
if ((newState.IsKeyDown(Keys.Left)) && (!oldState.IsKeyDown(Keys.Left)))
{
int row = rowFromPos(curSel);
int col = colFromPos(curSel);
col--;
if (col < 0)
{
col = tilecols - 1;
}
curSel = posFromRowCol(row, col);
positionXYFromIndex(curSel, ref selPosition);
moves++;
score--;
}
else if (oldState.IsKeyDown(Keys.Left))
{
}
if ((newState.IsKeyDown(Keys.Up)) && (!oldState.IsKeyDown(Keys.Up)))
{
int row = rowFromPos(curSel);
int col = colFromPos(curSel);
row++;
if (row >= tilerows)
{
row = 0;
}
curSel = posFromRowCol(row, col);
positionXYFromIndex(curSel, ref selPosition);
moves++;
score--;
}
else if (oldState.IsKeyDown(Keys.Up))
{
}
if ((newState.IsKeyDown(Keys.Down)) && (!oldState.IsKeyDown(Keys.Down)))
{
int row = rowFromPos(curSel);
int col = colFromPos(curSel);
row--;
if (row < 0)
{
row = tilerows - 1;
}
curSel = posFromRowCol(row, col);
positionXYFromIndex(curSel, ref selPosition);
moves++;
score--;
}
else if (oldState.IsKeyDown(Keys.Down))
{
}
touch_start(integer total_number)
{
if (avatar_attached)
{
llSay(0, "Currently allows only one player at a time. Sorry.");
return;
}
timer()
{
....
// see if we still have control
if (avatar_attached)
{
if( !(llGetPermissions() & PERMISSION_TAKE_CONTROLS) )
{
auto_tick_count = 0;
avatar_attached = FALSE;
}
else
{
// check the distance to the avatar...
key curkey = llGetPermissionsKey();
list temp;
vector pos;
vector pos2;
temp = llGetObjectDetails(curkey,[OBJECT_POS]);
pos = llList2Vector(temp,0);
pos2 = llGetRootPosition();
float dist = llVecDist(pos,pos2);
llOwnerSay("dist = "+(string)dist);
if (dist>15)
{
llSay(0,"Player too far away - resetting");
auto_tick_count = 0;
avatar_attached = FALSE;
llRequestPermissions(avatar_key, 0);
avatar_key = NULL_KEY;
}
}
}
}
run_time_permissions(integer perm)
{
// permissions dialog answered
if (perm & PERMISSION_TAKE_CONTROLS)
{
// we got a yes
// take up and down controls
llTakeControls(CONTROL_ROT_LEFT | CONTROL_ROT_RIGHT | CONTROL_UP | CONTROL_DOWN, TRUE, FALSE);
// remove any balls on the table
llSay(1296,"byebye");
in_play = FALSE;
avatar_attached = TRUE;
avatar_key = llGetPermissionsKey();
RestartGame();
}
}
control(key id, integer held, integer change)
{
if (id!=avatar_key)
{
list temp = llGetObjectDetails(id,[OBJECT_NAME]);
string nm = llList2String(temp,0);
llSay(0,nm+", you have been disconected, press the Release Keys button and touch the pinball machine again.");
return;
}