Tuesday, 25 May 2010

Gamma

Yesterday's post about sound reminded me of the problems I had with gamma correction. Getting on for a year ago I decided to create all the menus. Mainly so I didn't leave it to the end and rush them.

I am glad I did because there is a lot of fiddling about to create each screen. Nothing difficult but still time consuming.

I created a screen to adjust the volume levels of the music and effects separately. The levels are easy to impliment using XACT but the form took a while to get all the sliders drawn and working.

Anyone who has done any windows programming will be used to having a whole host of controls to just drag and drop on to a form. Then you write the code behind it. In game development targeted at anything other than Windows you need to create all the controls you want to use yourself.



In the above form you'll note a rather wordy description of how to adjust the contrast and brightness. This manual solution was the end result of spending ages trying to find a way to set the gamma levels on a graphics card.

I eventually failed. On all the PC's I tried changes to the gamma settings resulted in nothing at all happening. When I tried this same code on an Xbox I got a black screen.

My alternate to the manual solution would be code all my shaders to include an adjustment to the lighting value. I have decided that there are many other areas of the game far more important and as yet I have not seen an Indie game solve this, so I am at least no worse than others.

I will probably simply remove the note and the contrast bar from the finished game.

Monday, 24 May 2010

Sounds

I got side tracked again. I was bored play testing in silence so I have added gun shot sounds. I already had music for the menus and a click for menu choices, so I was not starting from scratch.

Basic sounds fitted in nicely. I already had enough code to display a decal when the round hits a structure. That same code path was perfect for putting in the play command for a sound and storing what sound each round made, both at the muzzle and upon impact of an object.

The problems came when making them 3D! Sounds further away need to sound quieter. There is an Apply3D method but to go with it I had to learn how to set this up in XACT. Not as difficult as I thought, but getting it right for my scale, 1 unit = 1m, took some tweeking.

The other problem I have is that for some reason the 3D sounds don't work on my main development PC but are clearly audible on the 360 and on my secondary PC. Odd but it just means I have to test more frequently on the Xbox.

The bit I don't like is that XACT still creates a tiny bit of garbage. I'd avoided the obvious areas but eventually after lots of searches on forums decided the last bit of garbage is unavoidable. Garbage is any object that uses memory on the heap and then needs disposing of during the game. It can interrupt game play. My aim is to avoid any garbage collection during a normal game. My target is 10 minutes between garbage collections.

At the moment I am well within target so I've added sound to my list of areas that create some garbage. It's a short list because the only other thing on it is particle emitters. I'm sure I'll come back to that when I decide I must reduce garbage further.

Back to animating my alien character.

Tuesday, 20 April 2010

Web Site

My wife created the logo yesterday and I got it on to the web site last night.

http://www.DiabolicalGame.co.uk

At the moment the only content is this blog. The background image and others in the game are from the Hubble Space Telescope thanks to NASA and STScI.

Thursday, 15 April 2010

Named the game

Over the weekend the game got a name. 'Diabolical: The Shooter'.

It's not easy to tell from the current screenshots that this will be a Science Fiction game, so I thought it worth describing the game concept.

It's set in a future where humans have travelled between stars and set up outposts and cities on remote worlds, where we have encountered a small number of other intelligent races.

The premise of this game is that human nature does not change. We still work and fight to get more of what we want. On the outer reaches there are few governments and corporations have a free hand to do business as they please. Humans, however, were not the first race to travel through space. There was a long dead ancient civilization spread through the galaxy. Anything to do with that ancient civilisation can easily be sold to the highest bidder and there is strong competition to get artifacts.

Back to what I've been doing...
I was intending to do the artwork myself but after spending far more time than I could spare to create one small object. I decided that if the game is ever to be finished to the quality I would like, then I need to get an artist to create the key assets.

I have found some off the shelf models for some characters and most of the scenery but the game needs to be unique so I commissioned a 3D model a week ago and the result is back. It is exactly what I was after. I need to do some more work before I will present it.

My main focus at the moment is map design, although I get side tracked when I need to add new features to the level editor. Things like a model to represent the spawn points or a grid to help position structures.


I've also improved the random noise and terrain smoothing functions and added a rectangular cursor which is useful for man made shapes like roads.


The circular one is best for hills and natural terrain.

First Images

Originally posted: 23 Feb.2010

These are the first images of the engine in development.





Please do not judge it yet. This is not even alpha code. I've been working on this in my spare time since March 2009. That's when I started with XNA and very shortly after had to learn a little bit of Blender to get some models in to a scene. The XNA dude shown in the screenshots is not the original from the skinning sample.
It is a duplicate transfered to Blender so I could add additional animations.



The first area I tackled was shadows however that took a lot longer than I expected. The shadows are still not finished.



The terrain editor is a very recent addition which only took a few days to complete.

Wednesday, 14 April 2010

New blog set up

Originally I was using a web site but that was slow to manage so I've resorted to a free blog.

Any posts dated prior to this one have been moved from the previous web site.


Wednesday, 10 February 2010

Another way to find your XNA Garbage

Like most people developing using XNA for the Xbox 360 eventually I encountered the frustrating situation where every minute or so my game froze for a few moments. With only the minimal of investigation the term Garbage Collection came to my attention.

What is garbage collection?

Garbage collection is the process used to remove unassociated items from the memory heap of the computer or console. This is necessary to make best use of the limited memory resources on the machine. What adds to the heap is outside the scope of this article but as a quick rule of thumb anything that needs a new statement infront of it will add to the heap.

The garbage collection process on a Windows PC is typically non-intrusive however the one on the Xbox 360 has a noticable impact on performance. We are therefore only talking about Xbox 360 garbage collection. It's worth noting that memory measurements on the PC are going to indicate the same areas of code using memory, so we can still debug garbage collection using the PC.

Garbage collection on the Xbox 360 happens when the heap has grown by 1M Bytes since the last garbage collection. If you can write your programme so the heap never grows that large during the game play, you will never notice garbage collection.

Memory usage is not a crime!

Before we go too far it is worth pointing out that all programmes NEED to use memory and the heap will always grow. It is usually many megabytes in size depending on the objects used in your programme. The heap is NOT garbage.

Many people also confuse memory usage with a memory leak. A memory leak is where your code continues to use more and more memory without releasing it for reuse when it is no longer needed.

Your programme will use memory and that memory usage may grow while the programme is running but that is not a memory leak. As we are talking about XNA which uses .NET which is managed code it is extremely unlikely that you could even accidentally cause a memory leak. That is one of the reasons for using a managed architecture like .NET because it deals with tidying up the memory you are using. It does that with the garbage collection process.

What is garbage then?

Garbage is made up of objects on the heap that we no longer need. More specifically items that are no longer linked to any active part of our code. Typically these are objects we create, use and discard. One common way to avoid garbage collection is to reuse objects rather than discard them. However this article is not how to get rid of the garbage but to show where the garbage is being created.

To find out about how to avoid garbage have a look at this article on the blog by Mike B. McLaughlin.

How can we tell where my code leaves stuff on the heap?

The typical way to find out what any programme is doing is to use a profiler. Two common ones for XNA are, the CLR Profiler and nProf. Both of these will provide loads of statistics to show you what your programme is doing while it is running. These should be your first port of call when you have a performance shortfall with your game.

Is there another way to measure the garbage?

There are times when for one reason or another a profiler does not give the information you need in the way you need it. As an example, and the reason I use an alternate solution, is because sometimes the CLR Profiler will not run with a particular programme. It just doesn't and no one can adequately explain why.

Rather than use an external tool to measure the memory usage we can build it in to our programme. That's the method I am demonstrating here.

The most significant part of the code simply measures the memory usage at a point in time and calculates the difference between that and the last measurement. The number recorded is the growth in memory during that update cycle.


// Call these before and after the method that we want to measure
public void Before(int pair)
{
 memBefore[pair] = System.GC.GetTotalMemory(false);
}

public void After(int pair)
{
 memAfter[pair] = System.GC.GetTotalMemory(false);
 CalcMemUsed(pair);
}

private void CalcMemUsed(int pair)
{
 long diff = memAfter[pair] - memBefore[pair];
 // Cumulative
 memUsedAllPasses[pair] += diff;
 if (diff > memUsedEachPass[pair])
 {
  memUsedEachPass[pair] = diff;
 }
 if (memUsedAllPasses[pair] < 0)
 {
  memUsedAllPasses[pair] = 0;
 }
 MeasureMemoryNow(pair);
}

private void MeasureMemoryNow(int pair)
{
 if (memAfter[pair] < memPrevious[pair])
 {
  // Set this level as the base to count from
  memLowest = memAfter[pair];
  // Clear other counters
  memUsedEachPass[pair] = 0;
  memUsedAllPasses[pair] = 0;
 }
 memPrevious[pair] = memAfter[pair];
 memGrowth = memAfter[pair] - memLowest;
}



If the total memory used has gone down there must have been a garbage collection process between the last update and this update. Therefore I set all the counters back to zero.

As mentioned above there is nothing wrong with memory usage, that is normal. All classes will use memory when they are created but what we are looking for is continuous growth.

I have created a class with the above methods in and just add that in to my code between compiler directives.


#if DEBUG
 // fixed location at the top left
 Garbage = new GarbageTools(ShorterClassName(this.GetType().ToString()), 
  new Vector2(5, 5));
#endif



How do we see what is going on?

Finally I needed a way to output the information without creating garbage! This is trickier than it sounds because any string manipulation will create garbage. Even adding a number to the end of a line of text or just changing that number will add a significant amount to the heap!

The solution is to do everything graphically. I use the name of the class followed by a tiny line graph showing the growth of the memory used since the last garbage collection. There are three lines because I have allowed for three separate measurements within any class. I deliberately put the graphs outside the title safe area because I don't want them to get in the way of play testing the game.


private const float lineThickness = 2f;
public const float maxScale = 550f;
public const float maxMainValue = 2600000f;
private const long warnByteLevel = 100;
private const long warnEachLarge = 2000;

// This must be inside an existing SpriteBatch.Begin() End() pair
public void Draw(SpriteBatch spriteBatch, SpriteFont spriteFont, Texture2D imagePixel)
{
 if (memUsedAllPasses[0] > warnByteLevel || memUsedAllPasses[1] > warnByteLevel)
 {
  Color colour = Color.Green;
  if (memUsedEachPass[0] > warnEachLarge || memUsedEachPass[1] > warnEachLarge)
  {
   colour = Color.Red;
  }
  spriteBatch.DrawString(spriteFont, className, positionText, colour);
  // Graph
  positionGraph.X = positionText.X + spriteFont.MeasureString(className).X;
  positionGraph.Y = positionText.Y + ((spriteFont.MeasureString(className).Y) * 0.5f) - lineThickness - lineThickness;
  // Same scale as the DebugMessages
  colour = Color.Orange;
  float lineLength = (float)memUsedAllPasses[0] / maxMainValue * maxScale;
  spriteBatch.Draw(imagePixel, positionGraph, null, colour, 0, Vector2.Zero,
    new Vector2(lineLength, lineThickness), SpriteEffects.None, 0);
  positionGraph.Y += lineThickness + 1;
  colour = Color.Violet;
  lineLength = (float)memUsedAllPasses[1] / maxMainValue * maxScale;
  spriteBatch.Draw(imagePixel, positionGraph, null, colour, 0, Vector2.Zero,
    new Vector2(lineLength, lineThickness), SpriteEffects.None, 0);
  positionGraph.Y += lineThickness + 1;
  colour = Color.Purple;
  lineLength = (float)memUsedAllPasses[2] / maxMainValue * maxScale;
  spriteBatch.Draw(imagePixel, positionGraph, null, colour, 0, Vector2.Zero,
    new Vector2(lineLength, lineThickness), SpriteEffects.None, 0);
 }
}



Somewhere in your game, probably in your central controlling class, the Draw(...) call for the garbage tool needs to be added to include all the classes you have added the measurements to. I have created a base class for most of my classes which includes the various garbage bits. I can then simply add a block of code to the end of my classes which call any sub classes as part of their DrawGarbage(...) method.


#region GARBAGE
#if DEBUG
// This is the same in each class
// Override and call the base then add other classes where necessary
public override void DrawGarbage(SpriteBatch spriteBatch, SpriteFont spriteFont, Texture2D imagePixel)
{
 // Draw our own first
 base.DrawGarbage(spriteBatch, spriteFont, imagePixel);
 // Add any other classes here
 // ...
 for (int i = 0; i < controllers.Count; i++)
 {
  controllers[i].DrawGarbage(spriteBatch, spriteFont, imagePixel);
 }

}
#endif
#endregion



That's it. Once the GarbageTool has been added to a class just use the Before(0) and After(0) calls round any code you want to measure.


public override void Update(GameTime gameTime)
{
 base.Update(gameTime);
#if DEBUG
 Garbage.Before(0);
#endif

 // Do normal game stuff here and the memory usage will be measured
 // ...
 
#if DEBUG
 Garbage.After(0);
#endif
}



I use this to reduce my garbage creation to very nearly nothing and avoid garbage collections during game play.

Download Source Code

XNA Garbage helper classes (4kb) Feb. 2010