Tuesday, April 27, 2010

XNA4: BasicEffect optimizations


The soon to be released XNA4 (RC already available) contains some very nice optimizations to the supplied BasicEffect class:


Given this and the added flexibility of custom shaders and custom rendering for the BasicEffect class, developing games in XNA4 will be alot more feasible without requiring a custom renderer. 

This of course means more time to get the game polished and gameplay as fun as possible!

Monday, April 26, 2010

C#: Generated Code


Another post just to point out a nice link:


This post is fairly old now (2003), but still provides lots of useful information. It provides a good overview of what things cost in C#, by looking at the generated assembly code.

Overall, there aren't any significant surpises. Things seem to be fairly similar to C++. Instance function calls, basic variable manipulation, and simple math code is cheap and generates pretty much one-to-one assembly much like you can imagine C++ code does. 

In the same way, virtual functions and delegates are slow to call and should be avoided in performance-critical code much like C++. Memory layout and class internals are also fairly unsuprising, and structured as you would imagine; vtables are where you expect them, etc.

If you are comfortable with writing C++ and being aware of what sort of assembly code is generated, then you should be able to write C# code that performs well and doesn't give any nasty suprises.

Given that 7 (!) years have passed since that post, the runtime and optimizations should be significantly improved, so everything will be much faster and your generated code should contain alot of the sort of optimizations you can get from modern C++ compilers.

Sunday, April 25, 2010

Caching


This blog post is mostly just to share a link pasted by a co-worker:


It pays to be aware of the cache, especially when developing games for consoles. In the low-level code avoiding cache misses will be where most of your optimization gains can be realized. 

Having your data well structured for systems such as collision and rendering is of utmost importance for keeping things running fast. Just make sure you spend time on the right areas of your code, at the end of the day alot of your code will simply not need to be optimized to this degree if it is coded sensibly.

Wednesday, April 21, 2010

C#: Fibers


In the previous post, we looked briefly at the uses for fibers in relation to game code. Let's look at what this looks like in C# using yield, IEnumerable, and lambda expressions. We will need a CFiberManager class which handles updating and launching our fibers.

First we will examine the user code interface  we want to attain before we look at the whole implementation. We will make a sound player Play() function which only allows us to play a sound once every N frames.


// sound system example
public void Play(string name)
{
  if (registry.HasKey(name))
    return;

  SoundEffect sound = Content.Load(name);
  registry[name] = sound;

  int WaitFrames = 4;


  Fibers.Fork(() => {
            while (WaitFrames-- > 0)
                yield return WaitFrames;
            registry.Remove(name);
  });
}




Here we play a sound, and then add it to a registry which will prevent playing the same sound until 4 frames later. It would be good to also add some counts for tracking concurrent playing sounds, as well as minimum spacing to ensure the sound does not get spammed in certain gameplay situations, and to just sound 'right' with regards to spacing, even when lots of entities attempt to play a sound in a short time.

Here we can see the implementation of the Fiber manager:


Maybe one day we can see some nicer language support for fibers in C# without resorting to this IEnumerable trick!

Monday, April 19, 2010

Forking your game


Having used a scripting language with native fiber support (GameMonkey) with some custom modifications to provide in-language syntax for this, I found myself wanting a similar feature when making games in C#.

As an example, somecode would start out something like this:


// another enemy-based example
self.Shoot()


until someone requests 'can you just make it wait 1 second and then turn and shoot again?', or perhaps 3 times, or 4 times and then wait 2 seconds and explode. This sort of change without fibers will add a big mess of new members, tracking state, timers and sleep values, as so forth. After which may just end up being thrown away since it didn't 'feel' right.

Having native language support for fibers lets us do something like this:


fork {
  sleep(1);
  self.Shoot();
  sleep(1);
  self.Rotate( PI );
  self.Shoot();
  sleep(1);
  self.Explode();
}


and we can test it right away. The benefit here is that the code change is very very local and doesn't really impact any other behaviour of our entity.

There is no native C# support for this sort of fork'ing code, but with the yield statement and IEnumerable, we can implement something clever to make this work, which we will cover in the next blog post! :)

Friday, April 16, 2010

C#: Collection Initializers


Developing games requires countless amounts of properties and settings that often straddle the line between 'code' and 'data' quite akwardly. C# Collection Initializers allow very nice inline placement of these sorts of values.

Here is a snippet of some weapon data for a 2D top-down shooter:



new List() {
    new SWeaponData() {
        ReloadTime = 0.15f,
        Speed = 15.0f,
        Damage = 0.3f,
        Offset = new Vector2(0.0f, -10.0f),
    },
    new SWeaponData() {
        ReloadTime = 0.15f,
        Speed = 15.0f,
        Damage = 0.3f,
        Offset = new Vector2(0.0f, 10.0f),
    },
},


This sort of code/data is easy to modify and understand, and really just is an excellent fundamental language feature that you will miss using C++.

Keep in mind that this code runs -after- the constructors, so you can have default values in your constructor, and then on a per-instance basis override any of the values. Very useful!


// Instant variation!
Turret t = new Turret() { Texture = "MagicTurret", SE = "MagicShoot", Health = 20 };


Sensible defaults with the occasional case-specific override gives your code an enormous amount of flexibility.

Saturday, April 3, 2010

Variations


Having alot of unique entities in a game will make a game stand out as interesting and fun. Unfortunately there is never quite enough time to develop as much as you would like, so it is worthwhile to find ways to vary existing entities sufficiently with as minimal a change as possible.


It doesn't take a significant change to make an entity feel different and provide a different play experience. Enemies for example can be varied with an artwork change, and some small behavioural changes. Changing a texture, adding a simple variation of firing pattern, and changing projectile graphics will effectively create a new enemy while keeping the majority of the code reusable. 

There are lots of clever ways to design and code your game entities to allow code re-use and provide variations, but at the end of the day a simple old 'if' placed here and there in the code is often the most effective and flexibile solution. It's not worth abstracting every piece of behaviour and functionality, just keep the code simple and clean, and branch in the right places to make your entities new and interesting.

Game objects should be kept standalone where possible, so that they may be used in various places. When creating new entities, it can be very useful to just reuse some existing entity as a placeholder to get a feel for behaviour. This can be particularly useful for bosses, and designers will often ask things like 'can you make the boss shoot the foo-enemy bombs at this point?'. Bits and pieces of your game entities will end up in places you do not expect, so make sure everything is usable as standalone as possible.

And if all else fails, copy and paste it all. It's all going to change anyway, right? :)