If this is your first visit, be sure to
check out the FAQ by clicking the
link above. You may have to register
before you can post: click the register link above to proceed. To start viewing messages,
select the forum that you want to visit from the selection below.

Short list of examples (exclusively 2D) as an introduction to XNA

Short list of examples on how XNA can be utilized to create a range of interesting effects (with almost complete focus on 2D).

Disclaimer: This is not a guide for XNA. Nor it is an in-depth discussion about 2D-graphics and implementation thereof in XNA. It is simply meant as a taste of some of the graphics abilities of XNA, written solely to get a reader sufficiently curious to delve deeper into XNA himself/herself.
I expect any reader to be not only an experienced VB programmer but also well-versed in graphical terms such as anti-aliasing, alpha-blending etc.

Prelude: To use XNA in VB is a relative new feature. I had trouble getting it to work with my VB.Net 2010 Express, but was pointed to a satisfactory solution involving downloading VS 2010 Express for Windows Phone, which includes a VB template for XNA games. This template will be the base for all the examples. The fact that XNA is relative new to VB also means that the vast majority of all examples online are written in C#. And since HLSL (High-Level Shader Language used in some of the examples to code a graphics card directly) is very similar to ANSI C, it is adviceable that any reader has a firm grasp on C and C#.
All examples are provided with the entire code at the top of the post. Any template-generated comment is removed and comments are added to the core lines I have added in the example. This will allow a reader to quickly sift through the code and advance to the next example right away, in case the example is understood without further reading. This introduction to XNA is supposed to be an extremely quick study and take no more than 2-4 hours to read through - links are provide in some cases to elaborate on certain topics, should the reader be inclined to study further.

Start by creating a new Windows Game (4.0) project and call it Example1. Most notably the solution will include 2 projects - one being your actual project and one being a container for all project content. The content project is similar in many ways to using Resources in VB.Net. Data loaded from the content-project need never be disposed - the contentmanager will make sure all allocated resources are freed before the program terminates.
The main project includes a class called Game1 inheriting a Game-class. This is the class we will be editing and expanding in all of the examples.
Besides the constructor, a Game class has 5 core methods:
* Initialize.
* LoadContent.
* UnloadContent.
* Update.
* Draw.
The 3 first are typically only called once while the last two are called in succession over and over while program is running. Typical program flow is:
Initialize -> LoadContent -> Update -> Draw -> Update -> Draw -> ... -> Update -> Draw -> Update -> UnloadContent.
Commonly Initialize is used for any pre-loading initializations, LoadContent is used to load content from the Content project, Update is used to get input from the user, Draw is used to refresh the display and UnloadContent is used to unload any non-content allocated resources. This is not written in stone though - you can easily load content in the Initialize method or scan for user-input in the Draw method, but as a general rule sticking as much to the common practises makes for readable code.
You have probably noticed already, that XNA is not event-driven. There is a natural flow of Updates and Draws taking place while program is running, allowing you to process user-input etc. without using events. This seperates the Draw method from the traditional VB.Net Paint event and the Update method from any of the Mouse, Keyboard and similar events. Input from the user is handled very differently, which we will get back to in another example.
You might also have noticed, that the Game1 class has two global variables declared for your convenience. You are free to remove or rename them as you see fit, but through the course of these examples, keeping them is a good idea. The first variable is a GraphicsDeviceManager object, used mainly to draw information about the current display-device and events attached to it. It will not be used much in the examples. The second variable is a SpriteBatch. SpriteBatches will be used in almost all examples as it is the only way to draw 2D textures to the display. Sprite is a very old name for a small and moveable bitmap, but it has in XNA terminology been extended to included any size 2D graphical object that can be drawn to the screen.
At this point you should copy the code supplied at the top into the Game1 class (replacing all that was there already).
I have added another global variable of type Texture2D. This variable is used to hold an image, and will be initialized in LoadContent. I added the picture 'Koala.jpg' to the content project (right-click and choose 'Add->Existing Item...') and loaded it into <myfirsttexture> using the method Content.Load(Of Texture2D)("Koala"). You will notice that no extension is supplied. Extensions are never used when loading from content!!!
Besides these comments, I consider Initialize, the rest of LoadContent and Update to be self-explanatory (remember keyboard input will be handled in later examples, so the Update method can be overlooked for now). Thus we turn our attention to the Draw method:
SpriteBatches are initialized with a call to the Begin method specifying which order, blending etc. things drawn to it should use. In our example, no parameters are supplied meaning that default values are used (most notably FIFO sorting and Alpha blending). After the Begin method, there can be any number of Draw methods invoked on the spritebatch, and finally an End method, that effectively terminates the drawing sequence. In this example, only a single call to Draw is made causing the Koala picture to be drawn at Vector2.Zero (which is (0,0)) with Color.White as tint.
At this point feel free to load other images and add variables to hold them. Try drawing several images at the time, preferable some with varying degrees of transparency, on top of each other. Also try drawing at other locations (New Vector2(x, y)) and using other tints.

Before this example is ended, I will take a line or two explaining how vectors and matrices work in XNA. Many of the known operators, such as +, -, *, / and unary -, work on both vectors and matrices. It is perfectly legal to use <VectorX-variable> + <VectorX-variable> and the result will be the expected one. Also <any scalar> * <VectorX> or <VectorX> / <any scalar> will produce the expected results. Matrices however are allways 4x4 (whereas vectors can be Vector2, Vector3 and Vector4), but any matrix can be applied to a vector using the shared VectorX.Transform() using only the apropriate entries. Both vectors and matrices are structures.

In truth, a mature man who uses hair-oil, unless medicinally , that man has probably got a quoggy spot in him somewhere. As a general rule, he can't amount to much in his totality. (Melville: Moby Dick)

You need not focus on the class FournierFussellCarpenterNoise and how it works. If interested, the algorithm is discribed in detail here: link, but for the purpose of this example it is not neccessary to understand it. Suffice to say, I modified it somewhat to generate seamless textures (by using Mod operations on indices). It is used here to display how to initialize textures using SetData and properly dispose unmanaged data. If you look at the Game1 class, you will notice, that the texture holding the generated image is initialized through its constructor and 'drawn' by suppplying an array of colors fitting the dimensions of the texture (ie. length of array is width * height of texture). At program termination, the texture is disposed in the UnloadContent method.
Feel free at this point to experiment with the generation (ie. supply various colors and/or roughness) or skip ahead to example 2b, if you don't really care about the algorithm.

In truth, a mature man who uses hair-oil, unless medicinally , that man has probably got a quoggy spot in him somewhere. As a general rule, he can't amount to much in his totality. (Melville: Moby Dick)

In this example, the focus should be on the Update and Draw methods, since pretty much everything else is identical to example 2.Update method: User-input in XNA is retrieved in socalled states. For example keyboard input is stored in a KeyboardState and can be retrieved by using Keyboard.GetState. A state will tell you which keys/buttons were pressed and the location of wheels, joysticks etc. of the device you are examining. To get the location of the mouse-pointer for example, this example uses Mouse.GetState.X and Mouse.GetState.Y. I could also have declared a variable of class MouseState and used it to store Mouse.GetState. I could then later use this variable to get the location of the mouse-pointer, the position of the wheel and which buttons were pressed, at the instant the state was read. This differs from traditional VB.Net input-handling to some extent, since there is no immidiate way of telling when a button was pressed - only if it is pressed at this instant in time. Thus you will need to store 'old' states and compare them to new states looking for changes to respond to the equivalent of keydown, keyup, buttondown etc. events. It is important to note though, that states are cheap to retrieve and store (ie. fast to collect and low memory usage).Draw method: The draw method differs somewhat from the one in the previous example. Instead of just drawing our texture to a specified location, we cut out a piece of it and display it on screen. The piece cut out is determined by <sourcerect>, which is a rectangle the size of the screen with a location determined by the (inverse of the) mouse-pointer. This will lead to partially empty areas being cut out in most cases, but the spritebacth Begin method has been supplied with the argument SamplerState,LinearWrap which causes textures to be repeated indefinately in any direction. So basically we have an infinately huge cloud texture (consisting of repetitions of itself). Another way of having infinately large textures created from a base-texture that isn't seamless is to use mirror sampling. You can try it by adding a variable in the Draw method:

and replacing SamplerState.LinearWrap with s in the spritebatch Begin method - NOTE: XNA only allows mirror sampling of images with width by height on the form 2^n x 2^m for n and m positive integers.
Other sampling states aren't treated in these examples, but more information can be found here: link.

In truth, a mature man who uses hair-oil, unless medicinally , that man has probably got a quoggy spot in him somewhere. As a general rule, he can't amount to much in his totality. (Melville: Moby Dick)

In the same way that a Form in VB.Net has a collection of Controls bound to it, a Game in XNA has a collection of Components. In this example, we add a new such component in the class GradientBall (in that it inherits DrawableGameComponent). The class GradientBall contains the same 5 core methods as the game class, so it should look somewhat familiar by now. A spritebacth is declared to draw the texture of the ball, and the texture itself and the location of it on screen are declared in the same old way. A bit of additional information about a ball is also stored (radius, edge-color, center-color and direction of movement). These additional variables are initialized in the constructor, and the texture of the ball is drawn in the Initialize method (it is drawn by using the previously used SetData method on a color array using linear interpolation, lerp, between colors based on the distance from the center). The location of the ball is constantly updated in the Update method, and if it goess off screen, the direction is changed.
What remains is just to add it to the components in the game class, which is done in the Initialize method (before the call to MyBase.Initialize to ensure that the GradientBall.Initialize is actually called). The ball will bounce around in the window.

In truth, a mature man who uses hair-oil, unless medicinally , that man has probably got a quoggy spot in him somewhere. As a general rule, he can't amount to much in his totality. (Melville: Moby Dick)

If Keyboard.GetState.IsKeyUp(Keys.P)ThenMyBase.Update(gameTime)'Will allow you to pause

EndSub

EndClass

Example is for illustration purposes only and is considered self-explanatory.

In truth, a mature man who uses hair-oil, unless medicinally , that man has probably got a quoggy spot in him somewhere. As a general rule, he can't amount to much in his totality. (Melville: Moby Dick)

If Keyboard.GetState.IsKeyUp(Keys.P)ThenMyBase.Update(gameTime)'Will allow you to pause

EndSub

Protected Overrides Sub Draw(ByVal gameTime As GameTime)

'Draw the balls to the rtarget RenderTarget2D

GraphicsDevice.SetRenderTarget(rtarget)

GraphicsDevice.Clear(Color.Transparent)

MyBase.Draw(gameTime)

GraphicsDevice.SetRenderTarget(Nothing)

'Blend with the background.

If Mouse.GetState.LeftButton = ButtonState.PressedThen

GraphicsDevice.Clear(Color.Black)

spriteBatch.Begin(Nothing, blendsub)

spriteBatch.Draw(background, Vector2.Zero, Color.White)

spriteBatch.Draw(rtarget, Vector2.Zero, Color.White)

spriteBatch.End()

ElseIf Mouse.GetState.RightButton = ButtonState.PressedThen

GraphicsDevice.Clear(Color.White)

spriteBatch.Begin(Nothing, blendrevsub)

spriteBatch.Draw(background, Vector2.Zero, Color.White)

spriteBatch.Draw(rtarget, Vector2.Zero, Color.White)

spriteBatch.End()

Else

GraphicsDevice.Clear(Color.Black)

spriteBatch.Begin(Nothing, BlendState.Additive)

spriteBatch.Draw(background, Vector2.Zero, Color.White)

spriteBatch.Draw(rtarget, Vector2.Zero, Color.White)

spriteBatch.End()

EndIf

EndSub

EndClass

In this example, we will begin in the Draw method. Instead of just drawing the 20 balls, that are initialized in the same way as the one in example 3 or the 100 in example 3b, we choose to draw the balls to a target other than the screen. A socalled RenderTarget, which I have added as a global variable named rtarget and initialized/disposed in LoadContent and UnloadContent respectively, is basically nothing more than a bitmap. The GraphicsDevice is instructed to draw into this bitmap with the SetRenderTarget method, and the call to MyBase.Draw will draw all the 20 gray balls into it. The current render-target is then set to Nothing indicating that we wish to draw directly to the screen again.
Now we encounter one of those gray areas, where I did not use Update for user-input, but rather chose to use the Draw method directly, as it makes for shorter code in this example. Apologies in advance for teaching you bad coding-practise. The Draw method is seperated into 3 parts:
Pressing the LMB: On a black background, the blendsub BlendState is used to draw first the gray balls and then the Penguin texture.
Pressing the RMB: On a white background, the blendrevsub BlendState is used to draw first the gray balls and then the Penguin texture.
No buttons pressed: On a black background, additive blending is used to draw first the gray balls and then the Penguin texture.
Basically all blending is done using the following formula:
ResultColor = SourceColor * SourceBlendColor <ColorBlendFunction> DestinationColor * DestinationBlendColor
ResultAlpha = SourceAlpha * SourceBlendAlpha <AlphaBlendFunction> DestinationAlpha * DestinationBlendAlpha
The blendsub BlendState uses Subtraction as function and One for both blends giving a simple subtraction of source-color and destination-color as result. The blendrevsub is identical except it uses reverse subtraction giving subtraction of destination-color and source-color as result. And finally BlendState.Additive has one for both blends and uses addition as function giving source-color plus destination-color as result.
In these 3 examples, the additive blend is the most interesting giving a glow-like effect, but subtraction, min, max etc. have their uses and can be combined to give a wide range of interesting results.
At this point, you could try reloading your example 3b (100 balls) and try drawing the GradientBalls with additive blending (as opposed to the default Alpha). It should give you some odd modern-art type images with a complete blur of bright colors.

In truth, a mature man who uses hair-oil, unless medicinally , that man has probably got a quoggy spot in him somewhere. As a general rule, he can't amount to much in his totality. (Melville: Moby Dick)

This may seem like a large example, but in fact it uses pretty much only things we have already gone through. A DrawableGameComponent is inherited to create a simple button, and three rendertargets are used to create textures for the button in normal, hover and pressed states. In order to try it out, you need to add a new sprite-font to the content project ('Add->New Item...'), choose sprite font and call it MyFont (will get extension .spritefont). Edit the XML of the font so the fontname is Tahoma and the size is 24. You can now try running the example.
In the Game class, three variables are used to control a message being displayed on the screen, and three simple buttons are being declared as withevents. The spritefont is used through another global variable called _myfont. Besides that the Game class is pretty simple and should be a quick study. The only new thing is the method DrawString on spritebatches, but that should hardly present any problems at all.
In the button class nothing much is new either. Since this is a 2D only study, I will refrain from explaining the 3D-stuff used to draw the buttons. Suffice to say - an orthographic projection is used to avoid perspective and the coordinate system is setup to have the same coordinates as the screen. Vertices of position and texture are used to describe the button, and a triangle-strip consisting of two triangles ({0,1,2,3} is interpreted as {0,1,2} and {1,2,3}) is used to draw it. No lighting has been used. The only thing noteworthy, is that the button class listens for events if the client-window changes size. This is done to ensure that the textures and coordinates are always viable. Thus changing the size of the client-window will recalculate the coordinate-system and dispose/redraw the textures.

In truth, a mature man who uses hair-oil, unless medicinally , that man has probably got a quoggy spot in him somewhere. As a general rule, he can't amount to much in his totality. (Melville: Moby Dick)

I will not explain how Perlin noise is implemented or how it works. There are plenty of pages on that already - for interested readers, the article referred at the top of the PerlinNoise class is an excellent place to start. For others simply accept that for any given 2d-vector Perlin noise will give a floating point number, so that the function is continuous everywhere yet produces semi-random values. And Perlin spent a long time working on his version(s) trying to get it as fast as possible; using the additional shared generate method to construct entire textures of Perlin-noise is indeed doable. Even with multiple calls per pixel (as is costumary to generate the most spectacular of effects). In our little example however, we will just call the plain simplex function since this example is not about generating beautiful and accurate landscapes but rather illustrating a method. To run the example, you will need to supply a tiled texture of size 320 x 64 pixels consisting of 5 tiles of water, grass, forest, desert and mountains respectively. The one I used is linked here , which is merely colored squares representing the areas (I'm not much of an artist).
Aside from the fact that Perlin-noise is used, nothing much is new in this example. The tiled texture is loaded and 64 by 64 bits of it is displayed at every location on the screen based on the Perlin-simplex of the location (or rather a scaled down version of the location). The update method in the TiledMap class illustrates how an 'old' state of the mouse is saved to determine if the left button has just been pressed. Then the location of the mousepointer is used to generate a small offset of pixels and an offset into the map. Feel free to adjust the levels in TiledMap.Draw to accomodate more forest or whatever - Perlin-noise gives values in the range -1 to 1, and it should be easy to allow for more area-types or various other features.

In truth, a mature man who uses hair-oil, unless medicinally , that man has probably got a quoggy spot in him somewhere. As a general rule, he can't amount to much in his totality. (Melville: Moby Dick)

Also you will need to add that same old texture Penguins.jpg (or Koala or similar as long as you remember to change the name) to the content project.

Phew that was alot of work creating and copying all those effects. And if you're no fan of C, they might look a tad difficult to read.
The first line in either effect simply declares a global variable of type sampler2D. Nothing is ever supplied to it, so the sampler used is the default in all cases (linear wrap). float in HLSL is equivalent to Single in VB - similarly Vector2 is equivalent to float2, Vector3 to float3 and Vector4 to float4. Matrix is equivalent to float4x4, but HLSL also supports float3x3 etc. int is equivalent to Integer or Int32. HLSL like C is case-sensitive!
The same rules as in VB apply to vectors in HLSL, in that you can add and subtract them using +/- and scale them using * and /. To take the dot-product of two vectors use dot(u, v) and to transform a vector with a matrix use mul(m, u). The function tex2D(<sampler>, <float2>) will use the supplied sampler to sample the texture-color at the location supplied in the float2 (which are coordinates in the range 0 to 1). TEXCOORD and COLOR are semantics used to tell the compiler that we're dealing with texture-coordinates and a color-vector respectively. Any PixelShader should take as argument a uv coordinate set (but possibly more parameters in a struct) and return a color. Thus all our effects (which are pixel-shaders) use the TEXCOORD/COLOR semantics.
An effect consists of a range of techniques each containing a range of passes. This is what the bottom lines in each effect signify. All examples except the last uses one technique and one pass. The last example contains a single technique consisting of two passes (initial texture goes through first pass and the result is used as source for second pass - result of second pass is final result).

The three first examples are simple. We just extract the color at the specified uv-coordinate and multiply it by a constant vector (dot product) to get a grayscale value that is returned as a non-transparent gray color (colors in HLSL are 0 to 1 values of RGBA in a float4). The variation between the three is very subtle to the human eye, but supposedly they each have characteristics useful in various areas.
In the fourth example, we instead use a matrix to multiply the color from the texture to get a sepia-color effect.
The fifth example introduces a parameter to the effect in the form of a float called SatValue (declared as a global variable). The value of this variable can be set from outside HLSL, so we may control the level of saturation used. The function SaturateInput uses a well-described method for saturating a pixel in RGB color-space (you may note that if v is a variable of type floatX, you can use v.x or v.r to get the first element, v.y or v.g to get the second and so on. HLSL also supports using v.xy/v.rg to extract a float2 from a floatX or using v.rgb/v.xyz to extract a float3 from a float4).
The sixth example has four parameters for width by height of the source texture and the two colors used as return values. It is a Sobel operator taking nine samples from the source texture, converting them all to gray and using a simple formula between them to determine if a given point is on an edge between two colors. If the pixel is found to be on an edge, the ForeColor is returned - otherwise the backcolor is returned. I used the Luminance_ITU_R in the example, but feel free to try out the other two listed.
The seventh example is a two-pass version of Gaussian blur with a four pixel radius. The first pass blurs the current pixel with every pixel in a horizontal span of four pixels to each side. The second pass blurs the current pixel with every pixel in a vertical span of four pixels to the top and bottom. The values used have been found in various articles on Gaussian blur.

In truth, a mature man who uses hair-oil, unless medicinally , that man has probably got a quoggy spot in him somewhere. As a general rule, he can't amount to much in his totality. (Melville: Moby Dick)

Re: Short list of examples (exclusively 2D) as an introduction to XNA

Example 7: A complete game using just 100 lines of code and relying on nothing but what you've learned.

I thought that I would end this series of examples with a little funny game with rich potential for development. The game basically consists of an invisible background (make the window full-screen). Your mouse-pointer is a torch, that you can use to light up the image - find the ghost and make him scream! Once you have found him, he will move to a new location. There are plenty of oppotunities to add score to this game - and difficulty making the torch smaller or the ghost more invisible. Or adding bonus ghosts. This is a cash-cow handed to you .
To use it simply add my little ghost and my scream to the Content project - both are linked here {OK so there's no method of uploading sound - but find and download a very short scream (3-5 secs tops) and add it as Scream(.wav)}. As background the trusty old Penguins.jpg is used. Enjoy and thanks for reading.

Ok so the 'nothing but what you have learned'-comment was a bit rash. We actually hadn't covered sound yet, but since it is as simple as scratching you own arse (pardon my italian), I thought I'd leave it to you to determine how that <SoundEffect>.Play works.

Regards Tom

Last edited by ThomasJohnsen; Nov 18th, 2012 at 12:36 PM.

In truth, a mature man who uses hair-oil, unless medicinally , that man has probably got a quoggy spot in him somewhere. As a general rule, he can't amount to much in his totality. (Melville: Moby Dick)

Re: Short list of examples (exclusively 2D) as an introduction to XNA

Hi Thomas,

Did you mean to post this in the codebank? If so I will request the thread moved.

when you quote a post could you please do it via the "Reply With Quote" button or if it multiple post click the "''+" button then "Reply With Quote" button.
If this thread is finished with please mark it "Resolved" by selecting "Mark thread resolved" from the "Thread tools" drop-down menu.
Please consider giving me some rep points if I help you a lot.
Please rate my post if you find it helpful!
Technology is a dangerous thing in the hands of an idiot! I am that idiot.

Re: Short list of examples (exclusively 2D) as an introduction to XNA

Did you mean to post this in the codebank? If so I will request the thread moved.

Nope - I meant to post it right here .
It's mainly intended for one specific member.
The audience for VB specific XNA introduction examples strictly concerning 2D is too limited for a codebank submission IMO.

In truth, a mature man who uses hair-oil, unless medicinally , that man has probably got a quoggy spot in him somewhere. As a general rule, he can't amount to much in his totality. (Melville: Moby Dick)