Tuesday, December 11, 2012

Moving Forward

The semester has come to an end. It has been a challenging and rewarding few months. Attraction made it to alpha and is in a very good place as we attempt to relax and replenish our brain meats over the winter break.

As a team we will be ramping up heavily on the PR side of things. Our website, Facebook, and Twitter will be seeing more and more activity. We have also just started our XBox account, giving us the ability to play and review other indie games. This will be essential to our becoming a part of the community. The feedback and support of our peers will be invaluable to our game's success.

We hope to have a code-cleanup session where we will get rid of unnecessary files and code. We will also refactor here and there in order to be ready for next semester.

We've come a long way but have a long road ahead of us before release. We need to develop all of the levels, enemies, scripted events and triggers that will make up the overall gameplay experience. Fortunately, we have equipped ourselves with the tools necessary to make it there. Our level editor, particularly, will allow us to rapidly create new content for the game. Thanks Andrew!

That, along with debugging, optimizing and polishing for release will keep us busy next semester. But for now, time to kick back with a mug of hot cocoa and play some Steam games...oh and XBLIG games, of course.

Why do all the images I used have animals? Whatever.

EAE Opens its House

The EAE Masters Game Studio hosted their annual EAE open house last Thursday. It was a huge success and the house was packed. We were able to commandeer two iMacs to demo our fledgling game, Attraction, in its alpha phase of development. Typically, the senior capstone students don't demo their games at this event because they've only had mere 7 weeks of development time. We felt like we were  ready. The promise of real player feedback left us drooling and we couldn't pass up the opportunity.

The feedback was almost unanimously positive. People love the mechanic and are excited to see the full game this Spring. There was a plethora of helpful criticism and suggestions. A personal favorite comment: "Isn't this the game that was released a year or two ago?" No, Billy, it's a puny 7 weeks old. But it sure doesn't look like it. Great job artists!

A word to all game devs out there: Get your game play tested early and get it play tested often. The feedback is worth a school-bus-full-of-children's weight in gold.

The U of U's press release is here.

Checkout our gameplay trailer:


The Title Screen

A map overview



It's Official!

We are officially incorporated. Tripleslash LLC is alive and kicking. You can check out our website at http://www.tripleslashstudios.com/ and on Facebook at facebook.com/tripleslashstudios.

Keep checking for udpates on development progress and news!

AlphaTest + Stencil Masking in XNA

Our team decided we want a smooth and consistent look for the environment and terrain in Attraction. At the time we were developing our level editor which uses a tile-based system similar to many 2D games. We were looking at ways to avoid a really "repeated" look due to each tile being used over and over to generate a level. Becky suggested we try to "mask" the tiles with a larger, repeating texture. The following explains the process.

The basic idea is as follows, we want this effect:


Here, the purple part is at 50% opacity. The green circle texture is repeatable. The "grass" on the purple tiles still shows through in the final result. Here's how it's done.

First and foremost, set the graphics format like so:

graphics.PreferredDepthStencilFormat = DepthFormat.Depth24Stencil8;

We are going to utilize XNA's stencil masking capabilities. The following are the two stencil masks we need to use.


//This is a stencil. It keeps track of pixels. But only the ones we tell it to.
//In this case, every pixel we pass to this stencil will be written to the buffer because we are using CompareFunction.Always
public static DepthStencilState AlwaysStencilState = new DepthStencilState()
{

       StencilEnable = true,
       StencilFunction = CompareFunction.Always,
       StencilPass = StencilOperation.Replace,
       ReferenceStencil = 1,
       DepthBufferEnable = false,
};


//This one will check what stuff was written to the stencil buffer and will help us to know what pixels to mask.
public static DepthStencilState EqualStencilState = new DepthStencilState()
{
       StencilEnable = true,
       StencilFunction = CompareFunction.Equal,
       StencilPass = StencilOperation.Keep,
       ReferenceStencil = 1,
       DepthBufferEnable = false,
};

These masks will help us "slice out" the portions of a texture that we wish to mask. The AlwaysStencilState will pass every pixel of the texture-to-be-masked to the buffer we are using. The EqualStencilState will then check the pixels that were written and see if their alpha channel passes the alpha test; in this case, if they have an alpha of 127 out of 255 (50%). Great, we have some stencils set up. But how do we use them?

First, we make a buildMask method:


private void buildMask()
{
        _bounds = _camera.Viewport.Bounds;
        _projection = Matrix.CreateOrthographicOffCenter(
             0,
             _spriteBatch.GraphicsDevice.PresentationParameters.BackBufferWidth,
             _spriteBatch.GraphicsDevice.PresentationParameters.BackBufferHeight,
             0,
             0,
             1);
}


This defines a couple of member variables for us. _bounds is for the size of the buffer we will be writing to. Not used in this particular example. _projection sets up the correct view matrix for our masking needs.

Now set up a couple of alpha tests. One to check for 50% opacity and the other to check for more solid values.


       //Create Alpha Test Effect
        _alphaEffect = new AlphaTestEffect(_spriteBatch.GraphicsDevice);
        _alphaEffect.AlphaFunction = CompareFunction.Equal;
        //This value can be 127, 128 or 129 depending on the program used to create the tile. We're shooting for 50% opacity

        //Apparently GIMP gives us 127
        _alphaEffect.ReferenceAlpha = 127;

        //Create next Alpha Test Effect for grass
        _alphaEffect2 = new AlphaTestEffect(_spriteBatch.GraphicsDevice);
        _alphaEffect2.AlphaFunction = CompareFunction.Greater; //Use the "greater than" comparison, the alpha value of the pixel must be greater than the ReferenceAlpha
        _alphaEffect2.ReferenceAlpha = 130;              //The value to test against is 130 out of 255

        //Give the alpha test the correct projection matrix
        _alphaEffect.Projection = _projection;
        _alphaEffect2.Projection = _projection;


Once all that is set up, we can simply draw. Draw in this order 50% opacity parts, opaque parts, mask.



protected override void Draw(GameTime gameTime)
{
        GraphicsDevice.Clear(Color.Transparent);

        //Clear the stencil's buffer
        GraphicsDevice.Clear(ClearOptions.Stencil, Color.Black, 0, 0);

        //Paint the layer to the stencil buffer
        _spriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, AlwaysStencilState, null, _alphaEffect);

        //Draw the tile
        _spriteBatch.Draw(_tile, new Vector2(200,200), null, Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0);

        //End
        _spriteBatch.End();

        //Now we draw the "grass" part of each tile. It is the only part that gets drawn to the rendertarget because of alphaEffect2
        //which says, if you have over 130/255 opacity, get drawn son! We don't use a stencil here because we don't need to keep track
        //of these pixels because we aren't masking them later.

       _spriteBatch.Begin(SpriteSortMode.Immediate, null, SamplerState.PointClamp, null, null, _alphaEffect2);

        //Draw me some grass!
        _spriteBatch.Draw(_tile, new Vector2(200, 200), null, Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0);
        //Done with grass!
        _spriteBatch.End();

        //Now we paint the mask texture
       _spriteBatch.Begin(SpriteSortMode.Immediate, null, SamplerState.PointClamp, EqualStencilState, null, null);

        //Draw the mask
        _spriteBatch.Draw(_maskTex, new Vector2(200, 200), null, Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0);

        //End
        _spriteBatch.End();

        base.Draw(gameTime);
}

Take a look at the linked project to see a fully working example. Feel free to use the code however you please but please give me credit somewhere. Thanks!

Click here to download sample project.