Sunday, March 4, 2012

Graphics Elements 1: Inkscape Introduction


     Hey everyone, today I am starting my graphics elements series with a introductory tutorial on using Inkscape. I have been creating many plants lately for my game and decided to post a tutorial explaining the process I used for creating these graphics. I use the same general process for pretty much everything that I do in Inkscape. In the future I hope to post more videos explaining some of the more interesting techniques that I have learned.

     Inkscape is a vector based graphics program, rather than pixel based programs like gimp. The main advantage of vector graphics is that they do not lose quality when scaled. Of all of the graphics programs that I have used Inkscape is by far the most intuitive and I use it for everything from games to diagrams for reports. I highly recommend Inkscape as one of the best open source graphics programs available.

Inkscape Official Website: inkscape.org

Friday, February 24, 2012

Game Elements 6: Multi-Character Camera


 In my last couple of tutorials I talked about shaders. Today I am going to take a quick diversion from that and go back to talking about the camera. I will be starting with the code I created from game elements tutorial 1, so make sure you check that out so that you can follow along.

I have already explained how to create a camera that follows the players character. However, sometimes we want the camera to follow multiple characters, like if the game is multiplayer. So what I am going to explain today is how to create a camera automatically adjusts so that all of the characters are always in view. If you still do not understand what I mean, check out the video example at the top of the tutorial.

A multiplayer camera can be divided into two separate parts. First we want the camera to move to follow both characters and secondly we want the camera to zoom in and out so that the even if the characters move far apart, they will both always be in view.

To get the camera to move, we first need to know the locations of the objects that we want to follow. Since this is a simple example I have just added a list of positions to the camera class:

public static List<Vector2> ViewTargets = new List<Vector2>();

Now to follow the characters we want to define a rectangle that contains all of the positions and then focus the camera on the center point of this rectangle. Since we want to update the rectangle every time one of the objects moves, we are going to need to recalculate the rectangle each time. So what I have done was create this update function:

public static void Update(GraphicsDevice graphicsdevice)
{
    if (ViewTargets.Count > 0)
    {
        Vector2 min = ViewTargets[0];
        Vector2 max = ViewTargets[0];
        for (int i = 1; i < ViewTargets.Count; i++)
        {
            if (ViewTargets[i].X < min.X) min.X = ViewTargets[i].X;
            else if (ViewTargets[i].X > max.X) max.X = ViewTargets[i].X;
            if (ViewTargets[i].Y < min.Y) min.Y = ViewTargets[i].Y;
            else if (ViewTargets[i].Y > max.Y) max.Y = ViewTargets[i].Y;
        }
        Rectangle rect = new Rectangle((int)min.X, (int)min.Y,
            (int)(max.X - min.X), (int)(max.Y - min.Y));

        Position = new Vector2(rect.Center.X, rect.Center.Y);
    }
}

As you can see, I define the rectangle by finding the min and max values of the positions. Then I get the center point of the rectangle and use that as the focus of the target.

To get this all working, in the main class's update method where we had put in a line to update the camera's position to make it match the position of the object, we now want the position to be calculated in the camera's update method and we also want to pass the camera the positions of the objects. So replace that line with the following, where "position2" is the position of a second object that you can add yourself:

Camera.ViewTargets.Clear();
Camera.ViewTargets.Add(position);
Camera.ViewTargets.Add(position2);
Camera.Update(GraphicsDevice);

Now when you run the game the camera will move so that it will focus on the point halfway between the two objects. However, if you move too far apart the objects will move outside of the view of the camera. So what we want to do is to zoom in and out so that the objects are always on screen.

The steps to figure out how much to zoom are pretty simple. First we need to calculate the difference between the height and width of the screen size and that rectangle that just barely contains all of the objects. Then once we have that we set the zoom value to be equal to the smallest difference. So add this code to the end of the camera's update function:

float widthdiff = ((float)graphicsdevice.Viewport.Width) / ((float)rect.Width);
float heightdiff = ((float)graphicsdevice.Viewport.Height) / ((float)rect.Height);
Zoom = Math.Min(widthdiff, heightdiff);

It you run your program now, you should notice that this works but that one problem remains. Although the characters are always visible on screen, the are always right at the edges of the screen, perhaps even a bit cut off. We would rather have a space between the characters and the edge of the screen. Luckly this is easily to add by simply increase the size of the rectangle. XNA even has a function to do this, just add this line to right after where you first define the rectangle in the camera's update function:

rect.Inflate(200, 200);

You should now have a multi-character camera that automatically moves and zooms to follow the objects you want to be in focus. There are a few obvious extensions to this algorithm, such as putting min and max zoom constraints on the camera or making the camera more gradually move and zoom to the ideal position. However I will leave these as exercises for your to try on your own. Also if you have any questions or create something interesting that you want to share please leave a message in the comments.

Monday, February 20, 2012

Game Elements 5: Vignette and Shader Parameters

In my previous tutorial I gave a quick introduction to shaders. Now you should have a basic understanding of how to write a simple shader, but now I am going to show how you can control the shader through code using parameters.

To demonstrate this we are going to write a new shader that will produce a vignette effect. A Vignette typically refers to a darkened border around an image that draws the viewers attention to the center. Adding a subtle vignette effect to any game is a good way to make it look more eye catching and less amateurish. A more pronounced vignette can also be used when you want to obscure the players view of the edge of the screen, such as when walking through a dark cave.

Subtle Vignette (Left), No Vignette (Center), Strong Vignette (Right)
The algorithm for producing this vignette is not much different than the grayscale effect shown in the last tutorial so I wont bother explaining it.

sampler TextureSampler : register(s0);
float Intensity= 1;

float4 VignettePS(float2 texCoord : TEXCOORD0) : COLOR0
{
    float4 tex = tex2D(TextureSampler, texCoord);
    float2 dist = texCoord - 0.5f;
    dist.x = 1 - dot(dist, dist);
    tex.rgb *= saturate(pow(dist.x, intensity));
    return tex;
}

technique Colored
{
    pass Pass1
    {
        PixelShader = compile ps_2_0 VignettePS();
    }
}

Notice the variable "intensity" is defined outside of the pixel shader function. This intensity value sets how strong the vignette effect appears. Variables defined outside of the function can be modified from your game as follows:

MyEffect.Parameters["Intensity"].SetValue(5.5f);

Now you can use this to pass input parameters to your shaders. For practice, set a couple of buttons to increment and decrement the intensity values. And as always, let me know in the comments if you used this to produce any interesting results.

Friday, February 17, 2012

Game Elements 4: Intro to Shaders

In this tutorial I will be talking about pixel shaders, with examples in C#/XNA4 and HLSL. Shaders are a powerful tool that you can use to make your games really stand out. Many game developers using XNA that I have talked do not know much about shaders and thus do not use them. However, writing simple shaders is actually really easy and can add eye catching effects to your games without a major preformance hit.

A pixel shader is basically a function that you want to run on each pixel of an image. If you try doing this in code you will find it really slow. To improve performance shaders are run on the graphics hardware which is specially designed for quick repetitive operations.

Writing simple shaders is not that difficult but it can get a bit tricky when you try doing more complicated operations, but in this tutorial I will stay pretty simple. Shaders are written in a shader language called HLSL (high level shader language). HLSL looks similar to C# but has some differences, most important are that HLSL has a much more limited selection of functions, mostly just ones for math. Also, because HLSL is trying to be fast it has a lot more in common with C, for example all arrays in fixed size. Since we are only writing simple functions in HLSL these should not be too restricting, but they are good to know and a good function reference can be found in the official documentation.

Example Shaders

The first shader that we are going to write will replace the colors with shades of gray. To start we need to create an effect file. XNA treats shaders like any other type of content, so to create an effect file right click on a folder in your content project in visual studio and click "Add->New Item" then select "Effect File". A new effect file will be generated with place holder functions, most of which is unnecessary for our simple shader. Replace everything in your effect file with the following:

sampler TextureSampler : register(s0);

float4 GrayScalePS(float2 texCoord : TEXCOORD0) : COLOR0 {
    float4 tex = tex2D(TextureSampler, texCoord);

 float intensity = tex.r*0.3 + tex.g*0.59 + tex.b*0.11;
 tex.rgb = float3(intensity, intensity, intensity);
    
    return tex;
}

technique Vignette {
    pass Pass1 {
        PixelShader = compile ps_2_0 GrayScalePS();
    }
}

The important part is in the GrayScalePS function, which takes as texture coordinate as an input. We next use a texture sampler to get the color at that texture coordinate. Then take the weighted sum of the three color components of the texture to get the intensity. The weights determine the luminosity of the image and by adjusting these values you can change how each color component contributes to the brightness. These values though are the most commonly used since they provide a good contrast between different colors. For those that have a better understanding on math you might notice that what we are doing here is actually a dot product, so we can simplify these two lines into one and get the same result:

tex.rgb = dot(tex.rgb, float3(0.3, 0.59, 0.11));

Now to apply this shader to your image go to your game code to the area where you are loading your content. Put in this line to load your effect file, the same way you would load a texture:

Effect MyEffect = Game.Content.Load("Effects/MyEffect");

Next, go to where you are drawing your image. In the sprite batch begin method there is an argument for imputing a shader.

spritebatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullCounterClockwise, MyEffect, Matrix.Identity);

Now everything that is drawn between the sprite batch begin and end calls will have this shader applied to it. If you run the game now you will see that everything is gray and colorless.


Congratulations, you have written your first shader. Its not a very complicated one but with slight modifications you could reuse it to tint the screen a certain color, invert colors, or make a sepia tone effect.

Friday, February 3, 2012

Game Elements 3: Drawing to a Texture

Introduction

In this tutorial I am going to explain how in XNA we take what would normally be displayed on the screen and instead draw it to a texture. This will be very useful in future tutorials when we want to add complicated effects. Some things that this can be used for is whole screen shaders, mini maps, split screen, etc. I will also show you how to save this image as a file, so you can make screen shots.

For simplicity I will be using the code from the first game elements tutorial as a starting point so that you can see what parts are specifically related to this example.

Creating a Blank Texture
The first step is creating our blank texture, known in XNA as a render target. Add this local variable to the top of your main class:
private RenderTarget2D cameraViewRenderTarget;

Now before we can start drawing to it we need to initialize it to the correct size. We will initialize it to the size of the screen which we can get from the viewport. Add this somewhere in your load content method:
Viewport viewport = GraphicsDevice.Viewport;
cameraViewRenderTarget = new RenderTarget2D(GraphicsDevice, viewport.Width, viewport.Height);
 
Drawing to our Texture
Since we are now drawing to a render target it probably would make more sense to divide up our draw method. I will create a new method for drawing to the render target. Simply take everything from your draw method and put it in this function. Then, at the start of the method set the graphics device's render target to the one we just created, and at the end of the method set the render target to null.
private void DrawTileMapScene()
{
 GraphicsDevice.SetRenderTarget(cameraViewRenderTarget);
 GraphicsDevice.Clear(Color.CornflowerBlue);

 spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend,
  SamplerState.LinearClamp, DepthStencilState.Default,
  RasterizerState.CullCounterClockwise,
  null, Camera.GetTransformation(GraphicsDevice));

 for (int x = 0; x < 10; x++)
 {
  for (int y = 0; y < 10; y++)
  {
   spriteBatch.Draw(sandtile,
    new Vector2(x * sandtile.Width, y * sandtile.Height), null, Color.White, 0,
    Vector2.Zero, 1, SpriteEffects.None, 0);
  }
 }

 spriteBatch.Draw(character, position, null, Color.White, 0,
  new Vector2(character.Width / 2, character.Height / 2),
  1, SpriteEffects.None, 0);

 spriteBatch.End();

 GraphicsDevice.SetRenderTarget(null);
}
 
Drawing Texture to the Screen
Now we go back to our original draw method and add this new method to the beginning. After you run this function we need to clear the graphics device since it still contains what we just had drawn. Once this is done the screen will be blank but the render target will have what used to be drawn to the screen. To prove it, we will treat the render target like a texture and draw it to the screen as we would any other texture.

protected override void Draw(GameTime gameTime)
{
 DrawTileMapScene();

 GraphicsDevice.Clear(Color.Transparent);

 Texture2D tex = (Texture2D)cameraViewRenderTarget;
 spriteBatch.Begin();
 spriteBatch.Draw(tex, new Vector2(0,0), null, Color.White, 0,
  Vector2.Zero, 1, SpriteEffects.None, 0);
 spriteBatch.End();

 base.Draw(gameTime);
}

The result is that you can now manipulate everything that would have been drawn to the screen as a single texture rather than manipulating each individual part.

Writing Texture to a File
On a bit of a side note, something we can do with this texture is save it as a file. This will allow us to take screenshots. 

Add this code somewhere in your update method. Now when you press 'p' it will save this texture as a file called "screenshots" in the programs execution folder "..\bin\x86\Debug", or you can specify an absolute path like "c:\screenshot.png" to save it to c.
if (Keyboard.GetState().IsKeyDown(Keys.P))
{
 FileStream filesteam = new FileStream("screenshot.png", FileMode.Create);
 cameraViewRenderTarget.SaveAsPng(filesteam,
  cameraViewRenderTarget.Width, cameraViewRenderTarget.Height);
 filesteam.Close();
}

Download Package
Source code can be found in the download package for this tutorial:
Download Package

Extensions
In my some of my future tutorials I will explain some uses of this, however if you find a particularly interesting use let me know in the comments.

Monday, January 30, 2012

Game Elements 2: Efficient Tile Map

Introduction

In my last tutorial we created a character that moves around on top of a map made of several images pieced together in a 10 by 10 grid. Well everything seems to work fine for a 10 by 10 grid, but if you try to make the grid larger than 100 by 100 your game might start to get choppy. In this tutorial I will show how to only draw the tiles that are visible, something that will help us make larger more complicated tile maps in future tutorials.

Determining the Camera's View

If we want our tile map to be efficient we should only be drawing tiles that the camera is looking at, and not draw the ones that are off of the screen.

To do this we first go to our camera class and add two new methods. First, to get the size of the camera's view. GraphicsDevice contains a viewport object that has the size of the screen, however the camera may be zoomed in or out so we need to divide the viewport size by the zoom amount. Then once we have the view size we need calculate the view rectangle.

public static Vector2 GetViewSize()
{
 Viewport viewport = Game.GraphicsDevice.Viewport;
 return new Vector2(viewport.Width / Zoom, viewport.Height / Zoom);
}

public static Rectangle GetViewRectangle()
{
 Vector2 size = GetViewSize();
 Vector2 topleft = Position - size / 2;
 return new Rectangle((int)topleft.X, (int)topleft.Y, (int)size.X, (int)size.Y);
}

Drawing Tiles

Now we need to calculate which tile index will be in the upper right corner and which will be in the lower left corner. To do this we first need a function that can take a point in our scene and return the index of the tile at that point:

public Vector2 PointToTileIndex(Vector2 pos)
{
 return new Vector2(pos.X / sandtile.Width, pos.Y / sandtile.Height);
}

Then add this to the beginning of our draw method to call these functions and to prevent the results from being smaller than 0,0 or greater than the map size:

Rectangle viewrect = Camera.GetViewRectangle(GraphicsDevice);
Vector2 tile1 = PointToTileIndex(new Vector2(viewrect.X, viewrect.Y));
Vector2 tile2 = PointToTileIndex(new Vector2(viewrect.X + viewrect.Width + 150, viewrect.Y + viewrect.Height + 150));
int topx = Math.Max(0, (int)tile1.X);
int topy = Math.Max(0, (int)tile1.Y);
int bottomx = Math.Min((int)mapSize.X, (int)tile2.X);
int bottomy = Math.Min((int)mapSize.Y, (int)tile2.Y);

Lastly, in our loop instead of drawing all of the tiles we start at the top left of the camera's view and end at the bottom right:

for (int x = topx; x < bottomx; x++)
{
 for (int y = topy; y < bottomy; y++)
 {
  spriteBatch.Draw(sandtile, 
   new Vector2(x*sandtile.Width, y*sandtile.Height), null, Color.White, 0, 
   Vector2.Zero, 1, SpriteEffects.None, 0);
 }
}
Now try increasing the size of your tile map, something like 10000 by 10000 and notice that it runs as smoothly as the 10 by 10 did initially. If you are still not sure if any thing is happening, adjust the size of the camera's view rectangle and you should see tiles appearing and disappearing around the edges of the screen as you move.  

Download Package
Source code can be found in the download package for this tutorial:
Download Source  

Extensions
I have covered the basics but there are a few more things you can do with this example. For example, this code does not handle camera rotations. Let me know in the comments below if you making something interesting with this or have any questions about this tutorial.

Friday, January 27, 2012

Game Elements 1: 2D Camera

Introduction

For my first tutorial I will explain how to create a 2D camera that follows a character using XNA. Since this is my first tutorial I will start a bit slow and introduce some basic concepts so that I do not have to spend as much time on them in future tutorials.

The reason I am making this and other tutorials is because I have been programming games for a few years and have made many beginner mistakes. In the case of Cameras I would often move keep the character stationary and then move the rest of the tiles, which worked fine initially but then I wanted to find the position of the player to see if they were standing on a button and I had to do a whole bunch of complex calculations. Then I learned this technique and my life became so much easier.

Displaying our Character and Background



Lets start with drawing our scene. I have included the two images I use above, you can also find them by following the link to the download package at the end of the tutorial.

First we need couple of local variables to the top of our Game1.cs file. Two for storing the images and two more for the characters position and speed:

// local variables used for this tutorial
Texture2D character, sandtile;
Vector2 position = new Vector2(100,100);
int speed = 5;


Next we load our images in the load content method as follows:

// load the two images
character = Content.Load("Images/character");
sandtile = Content.Load("Images/sand");

Then we draw our tiles using by repeatedly drawing the sand tiles in a loop, and then the character:

spriteBatch.Begin();

// Draw a 10 by 10 grid of tiles
for (int x = 0; x < 10; x++)
{
 for (int y = 0; y < 10; y++)
 {
  spriteBatch.Draw(sandtile, 
   new Vector2(x*sandtile.Width, y*sandtile.Height), null, Color.White, 0, 
   Vector2.Zero, 1, SpriteEffects.None, 0);
 }
}

// Draw the character using the position variable for its position
spriteBatch.Draw(character, position, null, Color.White, 0,
 new Vector2(character.Width / 2, character.Height / 2), 
 1, SpriteEffects.None, 0);

spriteBatch.End();
Moving the Character
Now that we have our scene lets get the character moving using the keyboard. Simply add this to the update method:
// Use arrow keys to move the player
if (Keyboard.GetState().IsKeyDown(Keys.Up))
{
 position.Y -= speed;
}
else if (Keyboard.GetState().IsKeyDown(Keys.Down))
{
 position.Y += speed;
}
if (Keyboard.GetState().IsKeyDown(Keys.Left))
{
 position.X -= speed;
}
else if (Keyboard.GetState().IsKeyDown(Keys.Right))
{
 position.X += speed;
}
If you run the game at this point, you will be able to move the character around. However, if you move to far in one direction you will disappear off the screen.
player can move, but camera does not follow
Creating the Camera
We will create the camera as a new class. It will have three variables for position, zoom and rotation and one function for obtaining the transformation matrix. We will make all of these static because generally in 2D games we will only use one camera. Here is the code for the camera class:
public class Camera
{
 public static Vector2 Position = Vector2.Zero;

 public static float Zoom = 1;

 public static float Rotation = 0;

 public static Matrix GetTransformation(GraphicsDevice graphicsdevice)
 {
  return Matrix.CreateTranslation(new Vector3(-Position.X, -Position.Y, 0)) *
   Matrix.CreateRotationZ(Rotation) *
   Matrix.CreateScale(new Vector3(Zoom, Zoom, 0)) *
   Matrix.CreateTranslation(new Vector3(
    graphicsdevice.Viewport.Width * 0.5f,
    graphicsdevice.Viewport.Height * 0.5f, 0));
 }
}
Once that is done we can apply the transformation to the spritebatch begin function in our games draw method:
// Notice that the last argument is the camera's transformation matrix.
// The other arguments are just the default values.
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, 
 SamplerState.LinearClamp, DepthStencilState.Default, 
 RasterizerState.CullCounterClockwise,
 null, Camera.GetTransformation(GraphicsDevice));
Lastly, add this to the end of the update method so that the camera position will be updated ever time the character position changes:
// when the player position changes, so should the camera
Camera.Position = position;
Now when you run the program you will notice that the character always stays at the center of the screen and the camera follows.
player stays at center of the screen and camera follows
Downloads
Source code and images can be found in the download package for this tutorial:
Extensions
I have covered the basics but there are a few more things you can do with this camera that you can try on your own. For example, try setting up keys for zooming and rotating the camera. You can also try to extend this so that the camera only moves when the character gets closer to the edge of the screen. Let me know in the comments below if you making something interesting or have any questions about this tutorial.