Posts Tagged xna

Copying pixels from a pointer to an XNA Texture2D

In the XNA framework, the only way to load pixel data into a texture is to provide it in the form of a .NET Array. In most cases, this isn’t a problem – you can specify an offset into the array, and the array can be of any type. However, if you’re using a library like Awesomium or Berkelium, you will end up with large blocks of pixel data in the form of a pointer. The only way to turn this into an array is to manually allocate an array of the appropriate size and copy the pixels over every time they change. Not only does this waste memory, but it’s relatively expensive.

In this post I’ll show you a (fairly evil) way to copy pixel data directly from a pointer into an XNA texture using C#, PInvoke, and some knowledge of XNA internals.

The first step is to understand how XNA’s Texture2D.SetData method works normally. I opened it up in ILSpy, a free .NET decompiler.
Looking at the three overloads of SetData reveals that they all call into a private method called CopyData, with this signature:

private unsafe void CopyData<T>(int level, Rectangle? rect, T[] data, int startIndex, int elementCount, uint options, [MarshalAs(UnmanagedType.U1)] bool isSetting) where T : struct

The method is large and complicated, and parts of it don’t decompile into readable, valid C#. But we can understand what it does and how it does it with some research and careful examination of the decompiled code.

The first part of the function spends a bunch of time validating all the provided arguments. It ensures that the texture being modified isn’t active as a render target or attached to one of the device’s texture samplers, and validates other parameters like the size of the texture and the size of the data provided.

However, you start to see rather confusing code like this in the disassembly:

	_D3DSURFACE_DESC d3DSURFACE_DESC;
	initblk(ref d3DSURFACE_DESC, 0, 32);
	IDirect3DTexture9* ptr = this.pComPtr;
	int num3 = calli(System.Int32 modopt(System.Runtime.CompilerServices.IsLong) modopt(System.Runtime.CompilerServices.CallConvStdcall)(System.IntPtr,System.UInt32,_D3DSURFACE_DESC*), ptr, level, ref d3DSURFACE_DESC, *(*(int*)ptr + 68));
	if (num3 < 0)
	{
		throw GraphicsHelpers.GetExceptionFromResult(num3);
	}

If you know enough about .NET bytecode (CIL/MSIL), it may be easier to understand what's going on here. Bytecodes that couldn't be mapped to C# constructs were basically spit out directly as if those bytecodes were functions. This is happening because XNA is mostly written in C++/CLI, not C#.

First, the initblk opcode is used to zero-initialize the contents of a new D3DSURFACE_DESC structure. This is equivalent to doing a memset in native C/C++.

Next, the calli opcode is used. Essentially, this opcode allows you to invoke a native function directly, given a function pointer and knowledge of the function’s signature. All of the decompiled code here is essentially describing the signature of the function and then providing arguments for it. You’ll probably never see this generated by C# code, but it’s used often in C++/CLI to invoke native functions – and in this case, it’s being used to invoke a method of a COM interface.

How can you tell it’s being used to invoke a method of a COM interface? There are a few clues here:
First, we notice that ptr contains a pointer of type IDirect3DTexture9 – a COM interface. Second, we can look at the argument list to the calli instruction:

System.Int32 modopt(System.Runtime.CompilerServices.IsLong) modopt(System.Runtime.CompilerServices.CallConvStdcall)(System.IntPtr,System.UInt32,_D3DSURFACE_DESC*), ptr, level, ref d3DSURFACE_DESC, *(*(int*)ptr + 68));

First we have the return type of the function – System.Int32. Next, two modifiers that notify the compiler about the nature of the function – it’s a stdcall, and the return type is a C++ ‘long‘ with the semantics that implies. This doesn’t really matter much to us at the moment, but it’s good to understand the basics.
Next, the argument types for the function are provided: System.IntPtr, System.UInt32, and a D3DSURFACE_DESC *.

Given this information, we now know the signature of the function being called, so we could write an equivalent delegate if we wanted. The decompilation doesn’t tell us the name of the function, but given the name of the interface (IDirect3DTexture9) and the argument list, we can check that interface’s documentation on MSDN and try to figure out which function it is.

Finally, let’s look at the actual arguments being passed when invoking the function:

ptr, level, ref d3DSURFACE_DESC, *(*(int*)ptr + 68)

First, we see the first three arguments are of the appropriate type for the function’s signature and their values make sense. There’s a fourth argument, though, and it looks funny – in fact, it looks like the decompiler didn’t quite make sense of it. What is that?

Well, we know that ptr is a pointer to an IDirect3DTexture9. First, the code is dereferencing the pointer. If you know enough about COM, you will realize that dereferencing a pointer to a COM interface will allow you to access that interface’s vtable. The vtable contains pointers to each function provided by the interface, which allows you to invoke those functions on a given instance.
Given that the code is dereferencing the interface pointer and then adding an offset to it, we can now infer that it’s pulling a specific function out of the interface’s VTable. Again, we can’t immediately tell which function, but we have a lot of information here that we could use to figure it out.

This makes it pretty clear that a COM function is being called on the interface, because we can see the function pointer being pulled out of the COM vtable and passed to the calli instruction, along with a function signature and argument types that seem like a perfect fit for a COM method (HRESULT return value, first argument is the interface pointer).

At this point, we could dig through the vtable and find our way to the necessary method calls to lock the texture and get a pointer to its pixels. Then we could do any necessary pixel format conversion, etc while copying from our buffer to the texture. However, there’s an easier solution!

If you read through the code for the method, in certain code paths, it makes use of a D3DX function called D3DXLoadSurfaceFromSurface. It’s a pretty useful function – it can convert pixel formats, resample textures, and even handle DXTC compression. And if you look through the documentation, there’s a version of this function that can take a pointer as a source instead of another surface – making it perfect for our needs. We just have to find a way to call that function and hand it our image data pointer and D3DX will handle any necessary pixel format conversion and copy our pixel data to the texture.

Now, to call the function. First, we need to get ourselves an interface pointer. If we’re willing to use reflection, this is quite simple – we can get ourselves a reference to the pComPtr field we see used in the disassembly and use that reference to get the interface pointer for a given texture, like so:

public static class TextureUtils {
  internal static FieldInfo pComPtr;

  static TextureUtils () {
    pComPtr = typeof(Texture2D).GetField("pComPtr", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
  }

  public static unsafe void* GetIDirect3DTexture9 (this Texture2D texture) {
    return Pointer.Unbox(pComPtr.GetValue(texture));
  }

Next, we can set up the necessary P/Invoke skeleton to be able to invoke the D3DX function. We need two enumerations, a struct, and finally the function itself:

// Some enumeration members omitted for readability
public enum D3DFORMAT : uint {
    UNKNOWN              =  0,

    R8G8B8               = 20,
    A8R8G8B8             = 21,
    X8R8G8B8             = 22,
    A8B8G8R8             = 32,
    X8B8G8R8             = 33,
}

[Flags]
public enum D3DX_FILTER : uint {
    DEFAULT              = 0xFFFFFFFF,
    NONE                 = 0x00000001,
    POINT                = 0x00000002,
    LINEAR               = 0x00000003,
}

[StructLayout(LayoutKind.Sequential)]
public struct RECT {
    public int Left;
    public int Top;
    public int Right;
    public int Bottom;
}

// Note that we need to be careful to use the same exact version of D3DX that XNA uses.
[DllImport("d3dx9_41.dll")]
private static unsafe extern int D3DXLoadSurfaceFromMemory (
    void* pDestSurface, void* pDestPalette, RECT* pDestRect,
    void* pSrcMemory, D3DFORMAT srcFormat, uint srcPitch,
    void* pSrcPalette, RECT* pSrcRect,
    D3DX_FILTER filter, uint colorKey
);

At this point, however, you might have realized the problem: This function takes a pointer to an IDirect3DSurface9, not an IDirect3DTexture9. How do we go from a Texture to a Surface? Well, the MSDN documentation shows that there’s a convenient method that will give us the surface pointer for a given mip level of a texture: GetSurfaceLevel. But given that all we have is a void *, how can we call it? There are a few ways, of course – you could write a tiny C++ library to do the work, or you could add a reference to the Managed DirectX libraries, or something like that. But we’re going to do it the way they do it in C – with pointers!

To make sure we’re on the right track, we can first dig through the disassembly of CopyData to find the spot where it calls GetSurfaceLevel. Since we know the argument types and return type, it’s not too hard to find:

IDirect3DSurface9* ptr3;
IDirect3DTexture9* ptr4;
num5 = calli(System.Int32 modopt(System.Runtime.CompilerServices.IsLong) modopt(System.Runtime.CompilerServices.CallConvStdcall)(System.IntPtr,System.UInt32,IDirect3DSurface9**), ptr4, 0, ref ptr3, *(*(int*)ptr4 + 72));
if (num5 >= 0) {

With what we know about COM method invocations, we can tell this is invoking a method of IDirect3DTexture9 with an integer parameter (0) and a pointer-to-pointer parameter of type IDirect3DSurface9. This is definitely GetSurfaceLevel. This not only tells us what the right signature for the function looks like, but it also tells us where in the interface’s VTable we can find a pointer to the function in order to call it. So, armed with this knowledge, we can write some terrifying unsafe code to pull the function pointer out of the VTable:

// This could be written better, probably. I'm lazy.
public static unsafe void* AccessVTable (void* pInterface, uint offsetInBytes) {
    void* pVTable = (*(void**)pInterface);
    return *((void**)((ulong)pVTable + offsetInBytes));
}

And, using that function along with an appropriate delegate type, we can put these pieces together to get a surface pointer:

internal unsafe delegate int GetSurfaceLevelDelegate (void* pTexture, uint iLevel, void** pSurface);

public static class VTables {
    public static class IDirect3DTexture9 {
        public const uint GetSurfaceLevel = 72;
    }
}

public static unsafe void* GetSurfaceLevel (this Texture2D texture, int level) {
    void* pTexture = texture.GetIDirect3DTexture9();
    void* pGetSurfaceLevel = AccessVTable(pTexture, VTables.IDirect3DTexture9.GetSurfaceLevel);
    void* pSurface;

    var getSurfaceLevel = (GetSurfaceLevelDelegate)Marshal.GetDelegateForFunctionPointer(
        new IntPtr(pGetSurfaceLevel), typeof(GetSurfaceLevelDelegate)
    );

    var rv = getSurfaceLevel(pTexture, 0, &pSurface);
    if (rv == 0)
        return pSurface;
    else
        throw new COMException("GetSurfaceLevel failed", rv);
}

We use the marshalling API to turn the function pointer into a callable delegate with the appropriate signature, and then call it in order to get ourselves an interface pointer. Now, we’re finally ready to call D3DX! Let’s wrap this magic up with a helpful function:

public static unsafe void SetData (
    this Texture2D texture, int level, void* pData,
    int width, int height, uint pitch,
    D3DFORMAT pixelFormat
) {
    var rect = new RECT {
        Top = 0,
        Left = 0,
        Right = width,
        Bottom = height
    };

    void* pSurface = GetSurfaceLevel(texture, level);

    try {
        var rv = D3DXLoadSurfaceFromMemory(pSurface, null, &rect, pData, pixelFormat, pitch, null, &rect, D3DX_FILTER.NONE, 0);
        if (rv != 0)
            throw new COMException("D3DXLoadSurfaceFromMemory failed", rv);
    } finally {
        Release(pSurface);
    }
}

You’ll note that we’re also ensuring that we decrement the reference count on the surface after we’re done with it so it doesn’t leak. To do this, we wrote a quick helper function:

internal unsafe delegate uint ReleaseDelegate (void* pObj);

public static class VTables {
    public static class IUnknown {
        public const uint Release = 8;
    }
}

public static unsafe uint Release (void* pObj) {
    void* pRelease = AccessVTable(pObj, VTables.IUnknown.Release);
    var release = (ReleaseDelegate)Marshal.GetDelegateForFunctionPointer(new IntPtr(pRelease), typeof(ReleaseDelegate));

    return release(pObj);
}

And now, after all that hard work, copying pixel data from an Awesomium WebView into an XNA texture is as simple as this:

var rb = WebView.Render();
WebViewTexture = new Texture2D(GraphicsDevice, rb.Width, rb.Height, false, SurfaceFormat.Color);
WebViewTexture.SetData(0, rb.Buffer.ToPointer(), rb.Width, rb.Height, (uint)rb.Rowspan, D3DFORMAT.A8R8G8B8);

If you’d like to use this for yourself, you can download it in a ready-to-use form from my GitHub repository. Hope it’s helpful!

Tags: , , , , ,

A managed WebKit library: BerkeliumSharp

I spent the past day or so hacking together a managed wrapper for Sirikata’s Berkelium library. Berkelium allows you to easily embed a WebKit-based browser into games and other applications. It’s based on Chrome and it’s really easy to get up and running in a C++ application. Unfortunately, if you’re using C# or VB.net, you’re out of luck – no way to link directly against a C++ .lib file in those languages. The addition of my managed wrapper – BerkeliumSharp – means that you can use the library in managed applications, and integrate it with Windows Forms, XNA, or even WPF.

I’ve released the source under the BSD license (just like Berkelium) and included two simple examples (one for Windows Forms, one for XNA). It’s not a Chrome competitor but it’s surprisingly easy to get a lot of complicated things working – you can watch Hulu videos if you have Flash installed, and Flash games like Dino Run work in the Windows Forms example since it implements keyboard input.

You can download pre-built demos to test it out, or grab the source code over at Google Code.

Have fun!

P.S. If you try out the examples, be aware that content that opens pop-up windows doesn’t seem to work. I think this is a bug in Berkelium.
Edit: Figured out how to build Chromium and Berkelium myself and fixed the bug. Open source is awesome!

Tags: , , , , , , ,

Spite: An entry for Gamma 4

John and I spent a few weekends putting together a little game experiment for Kokoromi’s Gamma 4 competition. We didn’t get selected, which I think means we can look forward to some outstanding winners, based on some of the other entries I’ve seen. My favorite aspect of the competition is how severe the limitations were – it really forced me to get out of my comfort zone and experiment with storytelling and gameplay mechanisms I’d never put much thought to before. In the end I think we were both surprised by how much we managed to get done in such a short time span, and by how well the final product actually expressed the little bit of story and gameplay we put into it. It’s 5 minutes long and only uses a single button, so I encourage anyone to check it out. If you’re a fan of games like Zelda I think you will find it strangely familiar. :)

Spite Screenshot

Spite is a one-button game for the Kokoromi Gamma 4 competition built with C# and XNA. You’ll need Windows XP or better and a Direct3D 9 capable video card to play it. Also, a finger to press your spacebar. XBox 360 controllers are supported. Also, there’s no audio – sorry, contest limitation. :)

Download
Discuss on TIGSource

Tags: , , , , , , ,

Achievements and player data II

In my previous post I gave an introduction to achievements and player data collection. In this post, I’ll cover the remaining two significant pieces: Services and front-ends. Unfortunately neither of these will be quite as complete as the coverage in the previous post, since I haven’t completely finished implementing these parts of my achievement system. Whoops!

Services

Boy, that’s a generic heading. Anyway, so your game is collecting important data on gameplay events, and it’s reporting that data periodically to a web service on your server. Unfortunately, since you haven’t actually written that service yet, it doesn’t seem to be working. Odd how that goes, isn’t it?

The primary issue you have to deal with when building a service to recieve collected data from your game is storing the data. There are other incidental issues – security, performance, serialization, etc – but none of them are of any significance if you can’t find a way to reliably store and access the data. This isn’t a simple problem, but there are ways to handle it.

Essentially, the biggest issue you have to confront here is that you are going to have a lot of data. You may not have a lot of data now, but you will eventually. On one hand, you could spend weeks and weeks trying to design the perfect solution for storing all of this data – but doing that doesn’t actually get your game any closer to shipping, and doesn’t actually guarantee that you won’t have scalability issues down the road. On the other hand, if you completely ignore the problem, you risk losing some of the data you’ve already gathered by discovering significant design issues after you ship. In practice, you probably want to prioritize shipping over the longevity of your data, since (assuming you ship and your game isn’t terrible) you can always get more data.

As a starting point, I decided to build my services on Google App Engine, since it was relatively easy to get up and running and provides fairly generous quotas for free.

Read the rest of this entry »

Tags: , , , , ,

Achievements and player data I

One of the things I’ve been working on lately is a way to collect and store data on play sessions, so I can track achievements for players and track where players spend the most time in a level, or where they die the most. There are lots of details to get right for a system like this, but I’ve at least gotten a prototype working and started experimenting with ways to visualize the data.

For a system like this you have a few important pieces:

  • Your game needs to collect data during play sessions – in my case, I track important events like player/creature death, and periodically sample the player’s position to get a general idea of where players are in a given level during their session.
  • You need a way for your game to periodically report collected data to a remote server. You have to handle lots of edge cases here – for example, it’s likely some people will play without an active internet connection or suffer temporary loss of connection, so you need to batch up events to report later in this case – you definitely don’t want the game to fall over and choke without access to the internet, and if possible you want to avoid losing data too.
  • You need to build a set of services to collect data from the game. This typically means you also need services to handle things like uniquely identifying individual play sessions and computers, so that you can track achievements for individual players and perform analysis on individual sessions instead of only on a player’s entire play history.
  • You need to build an in-game frontend, to expose collected data to a player. This means turning your event data into user-friendly statistics – like a kill counter, or an achievement for killing a particular boss. One interesting challenge here is that you probably want to expose this information online, too, so that players can share their profiles and achievements.

In this post, I’m going to cover the first two.

Read the rest of this entry »

Tags: , , , , ,

Updating Onscreen Objects / Profiling

One of the problems I started to run into while polishing things up for my contest entry builds was that as my levels grew larger, the game’s CPU utilization on the 360 steadily grew with them. While PC builds of my game ran smooth on basically all the machines I had access to, on the 360 the cost of updating all the level’s objects and entities became quite significant – most likely due to the 360′s feeble floating-point performance and lack of out-of-order execution.

The solution to this was, at least to me, relatively obvious: My levels were much larger than the camera, so it didn’t really make sense to update the entire level every frame.

The first thing I tried to verify this theory was simply hacking it in: Do a check before updating each object to see if it was onscreen. Interestingly, this didn’t make the game any faster on the 360. Depending on your point of view, this either confirmed or denied my hypothesis: If the problem was simply the cost of all the floating point operations, the cost of doing the onscreen check for each object (since the camera and object bounds were both expressed in floating-point) could have been making the problem worse. Clearly, I didn’t have enough data to be sure about the right choice to make.

So, I spent a day or so rigging up the necessary infrastructure to be able to profile my game on the 360. Since you can’t use tools like CLR Profiler or NProf on the 360, I ended up building a very simple frame timing system, and adding an overlay to the game that would show timing data. This let me get a good idea of how much time each subsystem in the game was using, and then I could compare the costs of individual subsystems, and try making changes and seeing how the profile data changed.

Once I had the profiler up and running on the 360, a clear pattern emerged.

01

Updates were consuming a huge amount of CPU time on the 360. While on my desktop, updates basically accounted for no more than 1% of CPU time, on the 360 they actually accounted for more CPU time than rendering – this was actually a bit of a surprise to me since rendering was definitely the bottleneck at one point on the 360. It seems that at some point along the way, I solved my rendering performance issues on the 360, but didn’t notice because I had made updates so much more expensive – one mistake I plan not to repeat was that I went a week or two without testing the game on the 360, since my 360 was not hooked up at the time. During that span of time I made a lot of changes that drastically altered the game’s performance characteristics, so it was hard to tell what had caused things to degrade.

Read the rest of this entry »

Tags: , , , , ,

Supporting alternate keyboard layouts in XNA games

If you support keyboard input in your XNA game, one issue you need to be aware of is alternate keyboard layouts. Due to the way the XNA Framework handles keyboard input, if you create your game while using the QWERTY keyboard layout, and a customer runs your game while using the DVORAK keyboard layout, he will have to press different keys on his keyboard. At first, this might seem logical – but consider the typical case:

A customer downloads and installs your game. He’s running it on a Windows PC. He has his keyboard layout set to DVORAK, because he heard it helps reduce wrist strain. His keyboard still has QWERTY labels printed on it, so he presses ‘S’ to type an ‘O’.

He starts your game up, and let’s say the splash screen says ‘Press S to continue’. He presses S on his keyboard.

Nothing happens.

This is because the OS is remapping the S keystroke into an O. For your XNA game to see an S keystroke, he will have to press the semicolon key (;:) instead.

While users with alternate keyboard layouts are a comparative minority compared to people using the QWERTY keyboard layout, that’s no excuse for ignoring them. As it turns out, the solution to this problem is pretty straightforward.

What you need to do is find a way to map a keystroke in your keyboard layout – let’s say QWERTY – to whatever keyboard layout your customer is running at the time. The Win32 API actually makes this quite simple, so as long as you’re willing to include some P/Invoke code in windows builds of your game, you can accomplish this in a couple dozen lines of code.

To accomplish this, I use the following helper struct:

uusing System;
using Microsoft.Xna.Framework.Input;
using System.Runtime.InteropServices;

namespace Labyrinth.Framework {
    public struct LocalizedKeyboardState {
        internal enum MAPVK : uint {
            VK_TO_VSC = 0,
            VSC_TO_VK = 1,
            VK_TO_CHAR = 2
        }

        [DllImport("user32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto, SetLastError = true)]
        internal extern static uint MapVirtualKeyEx (uint key, MAPVK mappingType, IntPtr keyboardLayout);
        [DllImport("user32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto, SetLastError = true)]
        internal extern static IntPtr LoadKeyboardLayout (string keyboardLayoutID, uint flags);
        [DllImport("user32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto, SetLastError = true)]
        internal extern static bool UnloadKeyboardLayout (IntPtr handle);
        [DllImport("user32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto, SetLastError = true)]
        internal extern static IntPtr GetKeyboardLayout (IntPtr threadId);

        internal const uint KLF_NOTELLSHELL = 0x00000080;

        public struct KeyboardLayout : IDisposable {
            public readonly IntPtr Handle;

            public KeyboardLayout (IntPtr handle) : this() {
                Handle = handle;
            }

            public KeyboardLayout (string keyboardLayoutID)
                : this(LoadKeyboardLayout(keyboardLayoutID, KLF_NOTELLSHELL)) {
            }

            public bool IsDisposed {
                get;
                private set;
            }

            public void Dispose () {
                if (IsDisposed)
                    return;

                UnloadKeyboardLayout(Handle);
                IsDisposed = true;
            }

            public static KeyboardLayout US_English = new KeyboardLayout("00000409");

            public static KeyboardLayout Active {
                get {
                    return new KeyboardLayout(GetKeyboardLayout(IntPtr.Zero));
                }
            }
        }

        public readonly KeyboardState Native;

        public LocalizedKeyboardState (KeyboardState keyboardState) {
            Native = keyboardState;
        }

        public bool IsKeyDown (Keys key, bool isLocalKey) {
            if (!isLocalKey)
                key = USEnglishToLocal(key);

            return Native.IsKeyDown(key);
        }

        public bool IsKeyUp (Keys key, bool isLocalKey) {
            if (!isLocalKey)
                key = USEnglishToLocal(key);

            return Native.IsKeyDown(key);
        }

        public bool IsKeyDown (Keys key) {
            return IsKeyDown(key, false);
        }

        public bool IsKeyUp (Keys key) {
            return IsKeyDown(key, false);
        }

        // Maps a localized character like 'S' to the virtual scan code
        //  for that key on the user's keyboard ('O' in dvorak, for example)
        public static Keys USEnglishToLocal (Keys key) {
            var activeScanCode = MapVirtualKeyEx((uint)key, MAPVK.VK_TO_VSC, KeyboardLayout.US_English.Handle);
            var nativeVirtualCode = MapVirtualKeyEx(activeScanCode, MAPVK.VSC_TO_VK, KeyboardLayout.Active.Handle);

            return (Keys)nativeVirtualCode;
        }
    }
}

Here’s how it works: First, at startup, we ask the Win32 API to load up a specific keyboard layout; US English QWERTY. From then on, we can ask the Win32 API to convert a ‘virtual key’ from that keyboard layout into a scan code. Once we have a scan code, we can then ask the Win32 API to convert that scan code into the equivalent virtual key for the end-user’s keyboard layout. Since the XNA Framework uses virtual keys (the Keys enumeration contains virtual key values), this allows us to apply this technique to existing keyboard input code without any significant changes.

So, with this helper struct, you can add support for alternate keyboard layouts like DVORAK with only a couple changes:

  • First, add the helper struct to your game code somewhere so you have access to it.
  • Replace any uses of the XNA KeyboardState struct with the LocalizedKeyboardState helper struct. If you use some of the more obscure KeyboardState helper methods, you may need to do some work to add them to LocalizedKeyboardState; it only provides IsKeyUp and IsKeyDown.
  • Figure out whether any of the keys you’re using should not be remapped based on the current keyboard layout. For example, it’s common practice for some kinds of games to expose a ‘console’ that allows the user to view log messages and enter commands. In a lot of games, you press the tilde (~) key to open the console. This is a convenient key since it’s near the top left corner of a QWERTY keyboard. However, if you allow the keystroke to be remapped to the current layout, non-QWERTY typists will find they have to press some random key on their keyboard to open the console. In this case, you’ll want to pass a value of true for the second, optional parameter to the IsKeyDown/IsKeyUp helper methods:
if (ks.IsKeyDown(Keys.OemTilde, true))
    ShowConsole();

Tags: , , ,

One down

One to go.

Thanks to some especially hard work by Troupe and Ian, we got a relatively decent build of the game in for the Dream-Build-Play 2009 deadline. Next is the Intel Level Up 2009 competion, only a few days from now.

I’m too lazy to write a large blog post this time, since I’ve been up for about 48 hours. Instead, enjoy this conveniently placed link that allows you to download a build of the game and try it out. Feel free to mess around with the level editor, too.

Biggest things of note from the SVN logs this week:

  • Added a boss fight!
  • Overhauled my physics system to address some floating point accuracy issues.
  • Overhauled the combat system to try and make it more fun. Only slightly successful.
  • Finally implemented the player character’s companion, and added support for talking to her.
  • Significantly reduced garbage generation, which means less stuttering on the 360. Hooray!

Tags: , , , ,

Home stretch

In the next two weeks I have deadlines for two different contests coming up, so things are getting pretty hectic. Lots of things changed in the ~100 or so commits since the last blog post, so I’ll pick a few to describe.

Read the rest of this entry »

Tags: , , , ,

Event-driven audio

One of the older items on my to-do list was to give my sound designer a way to change the game’s audio without having to recompile the game in Visual Studio and start it up. Based on some of the improvements I made recently, I was finally able to knock that item off my to-do list.

Below, you can see a short annotated video walkthrough where I demonstrate the technique and show how it integrates with XACT.


There are a few key pieces necessary for this to work.

Read the rest of this entry »

Tags: , , , , , ,