Steam Greenlight Turns Yellow: Slow Down, Pay $100

Steam Greenlight launched late last week and was quickly flooded with a myriad of bogus submissions. The signal to noise ratio was far too low, and wading through the garbage to find the gems was proving to be just as arduous of a task for users as it had been for Valve (which prompted this whole community-driven selection business).

In response, Valve instituted a one-time $100 fee for submitting games to Greenlight. The funds collected (minus taxes) will be donated to Child’s Play. The fee grants your account submission access to Greenlight, so one fee will allow you to post and update any number of games to the service.

This new rule has proven surprisingly divisive. Many like the notion of eliminating spam while supporting charity, while others balk at the idea of a fee or its amount.

The Problem:

Greenlight has an identity problem. It has replaced the old direct-submission webform that Valve used to use, and thus is the only way for new developers to establish a relationship with Valve, short of meeting them in person or being contacted by them. (Yeah, you could leverage other contacts too, but it’s the official way to submit games for consideration on Steam).

So Greenlight is a replacement for the submission form. This sounds great: get users to publicly demonstrate their support for a product in order to gauge its popularity. Developers create a page on Greenlight for their project and then send their fans to it directly. The traffic they can generate is a good sign of the game’s popularity. Valve can monitor the projects with a lot of traffic and skim the cream of the crop. Problem solved.

But then Valve added a project listing and the ability to browse through submitted titles. This put *all* submissions into the public eye. This was never necessary to solve their problem of gauging the popularity of games; developers could have just directly linked to their projects. If there was no directory, then spam entries would be unseen and harmless.

Instead, Valve’s approach, paired with a lacklustre interface, thrust every joke, spam, malicious and simply naive project to the forefront. Every user saw when Half-Life 3 was posted for the fifth time. Discoverability was sabotaged by the huge amount of cruft and the fact that the system made no attempt to hide it.

The $100 Solution:

The quick answer of a $100 fee will likely solve the spam problem. And it probably won’t stop even the most mildly serious of game developers. But it’s still a knee-jerk reaction that a lot of people are opposed to.

To me, the amount is a non-issue. It should be enough to discourage invalid submissions and its low enough that any indie developer can scrounge for it.

What matters is that Greenlight is still evolving and has yet to prove itself. No game has been promoted to Steam proper yet. No game has even earned an audience with Valve. Heck, there’s no guarantee that reaching 100% “calculated positive ratings” will earn you anything.

And thus, you’re paying for… a web page really. A chance at Steam – sort of, maybe. It’s all a bit nebulous, and that’s the frustrating part. The old submission form was free, and that usually garnered an eventual response from a human at Valve. It was unsustainable, but why not just cut out Greenlight altogether and add the fee to the submission form?

Comparisons with the App Store, XBLIG, WP7, etc:

Many people are quick to point out that other App Stores charge a similar amount for no promises. However, in each of those cases developers are given clear guidelines for publication. Yes, there are rules, but if you adhere to them then your projects are accepted for sale on the store. There are no such guarantees with Greenlight. Some might argue further that acceptance on an App Store doesn’t guarantee you will have any sales, but that’s really beside the point (and would apply to Steam anyway). Getting on the store is what’s at stake. Your performance once there is up to you.

What Could Valve Have Done:

Close Greenlight submissions. It’s not even that radical a suggestion – Steam submissions were closed for all of August leading up to Greenlight’s launch. Then take the time to reevaluate whether or not Greenlight should be a “discoverability platform”. If so, look around for how other systems handle spam. Reddit’s “new” queue would be a good way of keeping invalid submissions away from anyone who didn’t want to play moderator. You could even just limit the number of brand new submissions on the front page when a user visits.

The Bottom Line:

The fee solution is probably going to be effective at weeding out spam. It probably won’t stop many legitimate developers (and some indies have even pledged to help out those in need). And all the money goes to a good cause. There’s not *that* much you can really be upset about. And if Valve continues to develop Greenlight as a browsable directory where Steam users can discover new games, then just being on Greenlight has value thanks to the sheer number of users.

I’m a bit annoyed that Valve jumped to this solution. I’m anxious that Greenlight keeps changing and has yet to prove itself. I would have preferred if Valve had solidified how Greenlight worked and proven it out before adding on a fee. But at the end of the day, I’ll be paying it without hesitation.

Say, did you know DLC Quest is on Greenlight right now?

The Great Porting Adventure: Day 8

After a few days off, The Great Porting Adventure resumes!

The TODO list has reached equilibrium – new items are discovered at about the rate that I knock them off. I figure this is a good sign, since I’m getting a handle on the remaining work and the issues that do pop up are pretty minor. Porting is a bit of a grind though. I’d much rather be working on something new and exciting at this point. The novelty of seeing my game running a Mac has all but worn off and thus I didn’t spend much time on it this week.

I started by going through the list of things that I compiled out just to get the game running in the first place. The big one was the intro “studio logo” splash which was a screen with a bit of transition flair that shows up when the game starts and loads a bunch of assets behind the scenes. I had to basically rip this out at first because it was causing a crash somewhere in OpenGL, which was about two levels of framework lower than anything I understood.

Is my code broken? Or XNA? MonoGame? MonoMac? maccore? OpenGL?

I read into it a bit and it turns out that OpenGL does not like it when you use an OpenGLContext from a different thread. This was a straightforward fix (a MakeCurrentContext() in the right place in the worker thread) because my use case was pretty simple: main thread loads assets, worker thread updates and draws the logo animation. This fixed the crash, but rendering had some serious issues. The screen fade was functional, but debug text was popping in and out over top of it. The logo itself wasn’t showing up at all, even though it was drawn in a very similar way to the screen fade.

It would eventually be revealed that none of these issues were related to OpenGL nor my multi-threaded use of it.

I spent a while trying to figure this out and lamenting the lack of PIX. At one point in my debugging, I noticed that I was multiplying some Color values by an alpha that was outside [0,1]. This turns out to be a no-go in MonoGame. I didn’t look into it much (and I don’t have the source in front of me), but I’m guessing it has something to do with the packed value representation used for Color. Not sure – needs more investigation. Certainly not the source of problems I was expecting.

The inconsistent rendering order that was causing sporadic popping of components was due to the draw order not being respected in the Game class. Or rather, the implicit draw order based on the order you added components was not always being honored. I fixed this one by explicitly assigning a DrawOrder to my components, which fixed the sorting.

After a while wrestling with this, I realized that my loading scheme was really just overly complicated and kind of hacky. I had pulled it from a previous game that loaded a lot of things up front, but the use case for DLC Quest wasn’t the same. There was no need for a second thread, and there certainly was no reason that it should be calling Present() on the GraphicsDevice. I’m pretty sure it came from the NetRumble sample, but it still felt wrong to be circumventing the Game loop like that. At any rate, I ripped it out and switched to a much simpler “show logo animation, then press start screen, then load stuff” flow. Much better.

The fiddling continued as I worked to get rid of some “dirty frame” transitions. It seems like there are some cases where I need to clear the framebuffer that don’t crop up in the XNA version of the game. I’m not sure where the discrepancy stems from, but it was easy, cheap and pretty logical to add in a Clear in one or two places. One transition problem stemmed from my use of ResetElapsedTime() to avoid a hitch after spending a long frame loading content. This method isn’t implemented in MonoGame at the moment, but I didn’t dig deep enough to find out if MonoGame would try to “catch up” on long frames anyway. It was easier just to add a few more frames to smooth out the transition.

Oh, and SoundEffectInstance.Resume() was broken. But I fixed it.

I’ve also noticed that my game throws a bunch of errors on startup (possibly when loading Mono-related libs) that look a little like this:


DLCGame.Mac(3296,0xb0183000) malloc: *** error for object 0x253f1010: pointer being freed was not allocated

*** set a breakpoint in malloc_error_break to debug

I don’t think there’s a <sarcasm> tag powerful enough to say I’m looking forward to figuring that one out.

That’s about it for Day 8. It’s getting to the nitty-gritty and game-specific stuff now. Still haven’t looked at the Mac store requirements… one of these days…

Running total: 8 days, $112.87

The Great Porting Adventure: Day 7

In this episode, our hero does battle with the beasts known as music and vsync… and wins.

Last time, I wrote about how I had successfully transitioned to a SoundEffect-based audio system. This was true, but it was also not the end goal. I was playing my music as a SoundEffect as well, which had the unfortunate requirement of all the source files being in .wav format. This meant that the music in my game was taking up about 170MB of space. Yikes.

There are two ways you deal with this in XNA land: switch to XACT to get fancy xWMA compression (not an option) or use the Song API to load in MP3 or WMA files (which both end up as built WMA files). Simple, right?

So I switched up my audio system to use Songs whenever a music cue was played (and updated the pipeline and content loader accordingly to use Song objects instead of SoundEffects for music cues). Then I remembered some of the drawbacks to the Song API.

You can only have one Song playing at a time, so there’s no way to do cross-fading. What’s more, the Song is controlled through the MediaPlayer class which has ridiculous overhead doing simple things like changing Volume. That means it’s impractical to do fading out manually by adjusting Volume. It’s possible, but you take a big performance hit for something simple (on Xbox anyway).

So I reverted all of those changes and went back to using SoundEffect for music. I realized that you can actually use waves processed with a bit of compression (ADPCM) and still load them as SoundEffect objects. This was great, because it saved something like 70% filesize for “Medium” quality (which sounds fine for the chiptune-style music I have).

Next I had to solve the pesky problem of not being able to play multiple SoundEffectInstances of the same SoundEffect (a limitation of the current build of MonoGame). Fortunately, there is an outstanding pull request on the MonoGame github project that integrates OpenAL support along with a bunch of missing SoundEffect functionality. I had to download and build maccore and monomac from source first, but that was a simple case of cloning those repos and running “make” from inside the monomac folder.

This is a picture of the OpenAL logo, because otherwise this post would be mostly text.

After all that – it didn’t work! Turns out that OpenAL doesn’t seem to support ADPCM wave files, so I’m back to square one as far as music goes.

Or am I? OpenAL can read MP3 files. And the SoundEffect object doesn’t care what’s in its buffer. So I can actually load MP3 files as SoundEffects directly using MonoGame! Vanilla XNA can’t even do that!

I had one more bug where I wasn’t disposing of SoundEffectInstance objects properly (my code), but once that was fixed, everything seemed to be working perfectly. Yay!

Next on the docket was vsync. The screen tearing I was experiencing was unacceptable, so vsync was definitely required. Thankfully, @SoftSavage (the current MonoGame coordinator) pointed me in the right direction with some docs from Apple on the subject. It was a simple case of finding out where to get the OpenGLContext and then set up the SwapInterval. Since I had just built monomac from source, this was relatively easy to track down and then add it to Game.applyChanges() thusly (line 19):

internal void applyChanges()
{
    if (GraphicsDevice.PresentationParameters.IsFullScreen)
        _platform.EnterFullScreen();
    else
        _platform.ExitFullScreen();

    // FIXME: Is this the correct/best way to set the viewport? There
    // are/were several snippets like this through the project.
    var viewport = new Viewport();

    viewport.X = 0;
    viewport.Y = 0;
    viewport.Width = GraphicsDevice.PresentationParameters.BackBufferWidth;
    viewport.Height = GraphicsDevice.PresentationParameters.BackBufferHeight;

    GraphicsDevice.Viewport = viewport;

    _platform.Window.OpenGLContext.SwapInterval = graphicsDeviceManager.SynchronizeWithVerticalRetrace;
}

There was a bit of other housekeeping to get the above code to work, so I include that only to show you the general idea. There is one other odd and important thing thing: in monomac, the MonoMacGameView class (parent of the GameWindow your game runs in) looks like this:

public void Run (double updatesPerSecond)
{
    AssertValid ();
    if (updatesPerSecond == 0.0) {
        Run ();
        return;
    }

    OnLoad (EventArgs.Empty);

    // Here we set these to false for now and let the main logic continue
    // in the future we may open up these properties to the public
    SwapInterval = false;
    DisplaylinkSupported = false;

    // Synchronize buffer swaps with vertical refresh rate
    openGLContext.SwapInterval = SwapInterval;

    if (displayLinkSupported)
        SetupDisplayLink ();

        StartAnimation (updatesPerSecond);
    }

You can see that vsync will be disabled no matter what you do (line 13). Fortunately, this only occurs just before the first Update. It does mean that you have to setup vsync after your Game has called Initialize(). I just added the following to MacGamePlatform.StartRunLoop():


public override void StartRunLoop()
{
    _gameWindow.Run(1 / Game.TargetElapsedTime.TotalSeconds);

    //The first call to MonoMacGameView.Run disables vsync for some reason, so  give
    //the game a chance to re-enable it here
    this.Game.applyChanges();
}

And then everything was right as rain.

The game is really coming together now. I gave the app packager/installer a whirl too and that looks pretty straightforward, which is a breath of fresh air compared to making an installer on Windows. More work remains to be done key binds, controller support, my loading screens and some resolution/alt-tab support, but I’m getting close! Oh, and I suppose I’ll need to look into Mac Store requirements sooner rather than later too :)

Running total: 7 days, $112.87

The Great Porting Adventure: Day 6

Day 5 ended with me realizing that an XACT-based audio system was probably going to be a bit of a bust using MonoGame. Sure, I had gotten the very basic “play a sound” functionality to work, but all the features I make use of in XACT are not likely to show up in the near future.

Speaking of which, I was asked why I use XACT at all. My main reason for trying it was its handling of music. XACT gives you nice crossfade options, as well as Category support (which in turn grants you Custom Soundtrack support on Xbox for free). It also gives you a nice layer of abstraction between sounds and the waves they use, meaning I can tweak the underlying sound of the game without changing anything in code. These sounds can also have nifty features like distance attenuation, instance limiting and automatic volume/pitch variance for giving your audio a bit of randomness. All of this is managed in reasonable functional application that allows me to keep my audio content separate from the game. To add new sounds (cues), I just update the XACT project and then call the corresponding cue from within code. I don’t need to update my game’s content project or loading, since it’s all handled through the “.xap” asset (which builds the associated wavebanks and soundbanks).

So XACT is great and now I can’t use it. But you can’t use it on Windows Phone either, so really it’s just Windows and Xbox. Maybe moving away from XACT is a prudent move, especially if I can get the same functionality out of SoundEffect.

It was time for a musical interlude!

Disclaimer: This is basically unnecessary over-engineering that I did for the heck of it. Don’t think for a second that this is required for porting to MonoGame, though it is part of my approach to porting.

I already added a layer of indirection to my audio system, so now it looked a little like this:

Pseudo-UML. Thanks Word art!

Coming up with an equivalent SoundEffect-based implementation was relatively straightforward, given that the features I use in XACT aren’t that advanced. I had to do a bit of work to keep track of instances (in categories) as well as music cross-fading, but it was all pretty simple to do. And my game didn’t need to change at all (other than to say whether it wanted to use XACT or SoundEffect).

The big wrinkle was how I was going to tell my game what sounds to load and what sort of features they would have (Category, volume/pitch variance, cue names rather than file names). After all, I want to just be able to play a sound by its cue name from the game, regardless of what the audio implementation is underneath.

One of the overarching goals of this whole project is to avoid maintaining different versions of anything just for the sake of porting. “Write once, play everywhere” right? If I need to maintain a separate list of sound effects to load (or have to add them to my project manually), it’s going to get out of sync quickly and be a huge headache.

But I already have a description of all the waves, cues, categories and events: the .xap file.

Here’s a peek at a small part of the .xap file for DLC Quest:


Category
{
    Name = Music;
    Public = 1;
    Background Music = 1;
    Volume = -260;

    Category Entry
    {
        Name = Global;
    }

    Instance Limit
    {
        Max Instances = 1;
        Behavior = 2;

       Crossfade
       {
           Fade In = 2000;
           Fade Out = 2000;
           Crossfade Type = 1;
       }
    }
}

Aha! A plain-text description of all the meta-data I’d need to play my sounds! Not in an easily paresable format though. It looked simple enough that I was going to try writing a quick and dirty parser. Then I found XapParse. From the Codeplex page:

A C#-based XACT project (XAP) file parser, intended for use as part of the custom content pipeline in XNA.

Hot dog, just what I needed! Here was the plan:

Add a new SoundEffectMetaData class to my audio system. Whenever a cue needs to be played by the SoundEffect-based system, it looks up the associated meta data and uses that to play the appropriate wave with the correct category/volume variance/pitch variance, etc.

Create a XAP Pipeline Extension project to:

  1. Read in the .xap file.
  2. Parse the .xap file into objects using the xapparse project.
  3. Construct a list of SoundEffectMetaData objects from the in-memory representation of the XAP project in Step 2.
  4. Write out the list of meta data to an .xnb file (automatic .xnb serialization is awesome – no work here!)
  5. Also, call context.BuildAsset to find and build the corresponding .wav file.

Step 5 is important, because it means that I can just build the .xap file and it will build all the required wave assets without me needing to add them to my content project.

Lastly, in my game itself:

  1. Reference the XAP Pipeline Extension in the Content project.
  2. Switch the Importer/Processor on the .xap file to use my new extensions and build.
  3. Copy the built list of metadata and all the built sounds to the Mac project.
  4. Switch the Importer/Processor back to the default XACT project to continue using it with the Windows/Xbox builds.

Step 4 here is simply because all content builds are performed on Windows and I need to build things a different way for Mac. I still need to come up with a nicer way of syncing content to my Mac build, but for now I can just switch the processor and I get everything I need for the SoundEffect-based implementation.

So now I can continue to manage all my audio inside XACT but still end up with the metadata I need for using SoundEffect when I choose. No duplicating effort, no managing two lists of sounds and their properties. Pretty slick, if a bit convoluted. But that’s why we program, right? :)


This is running a bit long, so I’ll take a quick look at what remains to be done:

  • Handle music properly. Right now it’s just a normal .wav SoundEffect, so the filesize is huge. Should probably be using the Song API.
  • Fix multiple sound effect instances (currently bugged in MonoGame, but there’s a promising pull request for OpenAL support that I’ll try out)
  • Fix vsync (still no idea where to start)
  • Investigate key binds
  • Investigate controller support
  • Bring back my loading screen
  • Supported resolutions list
  • Alt-tabbing? Minimizing? Losing focus? Etc.
  • App bundling
  • Test test test

Not going to finish within 7 days, but the game is looking pretty good right now.

Running total: 6 days, $112.87

The Great Porting Adventure: Day 5

Shorter update today, as I didn’t spend as much time working on the port yesterday. Things are still coming along, but I’m not there yet.

First off, I did a bit of testing to see what was already working. It seems like everything around saving and loading is functional, making use of the user directory seamlessly. Switching to full-screen also seems to Just Work, though I bet the Alt-Tab equivalent on Mac will need some handling. The list of supported resolutions isn’t being populated, so I’ll need to address that eventually. My use of render targets to perform a screen swirl effect also works nicely. I was initially surprised, but then I remembered that I use render targets for drawing anything in my game (to handle resolution-scaling easily), so it was clearly working already.

I then decided to track down the “jitter” that was occurring in my physics. The player character would constantly be moving slightly above ground and then landing, causing a sound and a puff of smoke several times a second. The likely culprit was my newly added handling in the physics manager’s step function that was chewing through updates in increments of 1/60f. It turned out that some rounding was causing updates of 1/60f followed immediately by another update with a very tiny time delta to pick up the remaining time in the frame. Evidently the frame time being passed in was just slightly larger than 1/60f. This tiny extra update wasn’t enough for the player to “fall” all the way to the ground, so his flags were updated to say he was off the ground and hence he would “land” the next frame. Ugly! But also an easy fix (and my fault – nothing to do with MonoGame).

Next up was VSync. I use vsync, but it wasn’t taking effect on the port. It turns out that vsync is just not implemented in MonoGame yet. I’m not sure how that’s an option for anybody – the screen tearing is horrible without it in my game. I spent a little while searching through the code and reading up on vsync in OpenGL and OpenTK, but it’s a problem I don’t actually know how to solve yet. I’ll dig into the Update/Draw/Present loop today I think – my game is probably pegging the CPU at 100% now anyway.

The next glaring issue I tackled is shown below:

I have around 99 problems and one of them is texture filtering.

Something was going a bit wrong with the way my textured tiles were being drawn, but only when the camera was in certain positions. I use Point (or NEAREST in OpenGL terms) texture filtering, so being “a little bit off” on texture coordinates causes the completely wrong pixel to be drawn. The pixels that are drawn are a consequence of the spritesheet I use – you’re just seeing the nearest adjacent pixel (which belongs to another tile type).

After ensuring that the correct sampling and filtering was being used, I used a debugger to verify that my vertices were being sent to the device with the correct texture coordinates. They all seemed correct, but they were also right on the boundary of the… texel (I think that’s the term I want) that I wanted to use. It seemed like occasionally the Nearest filter would pick the pixel just slightly to the left of the coordinate rather than the one just slightly to the right. It feels like I need a half-texel offset or something so that I would be sampling in the middle of the texel rather than the boundary. Regardless, it’s a bit beyond my understanding and the hacky fix was just to add a tiny constant offset to the texture coordinate, which did the trick. Sometimes the “right” fix is the one that works.

Now that the game looks more or less correct (minus vsync) and plays properly, I decided it was time to see just how broken XACT under MonoGame really was. I had seen this video showing an AppHub sample running with XACT sound on MonoGame, so I knew there was a least some potential. The first roadblock was that xWMA files are not supported. I use this for my music, as it saves me something like 95% filesize versus PCM. For giggles, I rebuilt my XACT project using all PCM format (for a nice 20x increase in filesize) and loaded it up. I spent another while figuring out that other things are not supported, such as using Volume/Pitch Variance and RPCs like DistanceAttenuation. Categories are also missing. Those are pretty much my main reasons for using XACT in the first place.

What’s more, the reading of XACT soundbanks/wavebanks is essentially black magic. None of the file format is documented, so the whole thing is a lot of guess work and tedious file analysis. It took a fair amount of work just to safely ignore the unsupported features I was using in order to have the basic sound effects work. I don’t see myself reverse engineering the binary format to get those features working.

To that end, I’ve decided to give the SoundEffect approach a shot. Taking a page out of computer science’s handbook, I added another layer of indirection to my audio system and can now swap out the XACT implementation for a SoundEffect-based implementation without my game being any wiser. But I still want to retain some of the nifty features, like volume/pitch variance. And I don’t want to have to maintain a separate list of .wavs to load. Fortunately, it looks like the .xap file for an XACT project contains a human-readable description of all the waves, sounds and cues in the project. If can process that at build time to generate some SoundEffect wrapper objects… hmmm… a new project for today.

That’s where things stand for now. I’m going to continue working at a replacement audio implementation. I’m also going to look into vsync – if anyone has any experience here, I’d love to hear from you.

Running total: 5 days, $112.87

The Great Porting Adventure: Day 4

Here be dragons

Back to work after a weekend off. I’ve started to get some good feedback from other devs who are curious about the porting process. It’s great to see some on Twitter deciding to give it a whirl too – it’s always easier when you have more eyes on something. It seems like most are content to follow along and see what happens first. Honestly, that’s a pretty smart approach since I have no idea if this is going to work in the end.

So, day four. This was easily the worst day so far.

At the end of day three, I had my game all converted and compiling, but anything beyond my first screen wasn’t drawing properly. In fact, it was initially crashing trying to apply a texture that was never set (and never needed to be applied anyway). I put a quick guard against using such a null texture to get things running, but it quickly became obvious that things were Not Right.

Specifically, everything except my menus and the background was not being drawn at all. It looked a bit like this:

You could technically play the game using the brown pixels under the "Y" in top left.

Upon (very) close inspection, I could see some tiny moving dots in the upper left corner of the screen. It seemed like anything I was drawing with just a vanilla “SpriteBatch.Draw()” call was working, but the rest of my game was tiny and offset. All of those draw calls used a more involved Draw override that included a SpriteSortMode as well as a BasicEffect argument. Now I had to figure out why those calls weren’t doing what I expected.

This is where I spent most of my day. Stepping through the MonoGame framework, trying to figure out what it was doing and then trying to figure out what it should be doing instead. This isn’t easy when you don’t know what “right” looks like.

Armed with Reflector and the StockEffects sample from AppHub, I dug through the internals of XNA’s SpriteBatch, BasicEffect and its associated shaders. MonoGame is not the line-for-line port of XNA that I expected. I’m guessing that’s for for legal reasons, but maybe it’s for technical reasons. I don’t know. I do know that the implementation MonoGame uses is incomplete though. This makes sense, since only 2D is supported at the moment whilst BasicEffect has a lot of 3D features.

Eventually, I found this in MonoGame’s implementation of one of BasicEffect’s vertex shaders:


gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;

This was the first “Aha!” moment. Setting World/View/Projection on the BasicEffect modifies shader parameters, not built-in GLSL uniforms (by the way, did I mention I had to read up on OpenGL and GLSL?). This is why setting my own values had no effect! I therefore changed it to this:


gl_Position = gl_Vertex * WorldViewProj;

In the above, “WorldViewProj” is a uniform that is set by BasicEffect. Note that the order of multiplication is switched because matrices in XNA are row-major, whereas OpenGL uses the column-major convention. I think.

Compile, run….

No change.

Since I don’t really know what I’m doing, I played around with various different matrix values and shaders for a while with no luck. I would receive runtime errors whenever the shaders failed to compile or link, so I foolishly assumed they were working. I began to suspect something was seriously wrong when I modified the pixel shader to just return the color red and yet the textures were still drawn. Something was definitely not behaving as I expected. Whenever problems like this come up in the AppHub forums, the answer is invariably, “Have you tried PIX?”. I should look into some sort of PIX alternative – it would have saved me a lot of time.

It took a lot of investigation, stepping through code, and reading up on OpenGL to determine that although the shader program was being constructed, compiled, linked, set, etc properly, it was also subsequently being unset just before the actual draw OpenGL call happened. Argh! I used the following call to inspect what shader program was active in order to figure that out:


GL.GetInteger(GetPName.CurrentProgram, out programNum)

Since “programNum” came back zero, I knew OpenGL was using the fixed function pipeline and not BasicEffect’s shaders. It was easy enough to track down where the erroneous call to GL.UseProgam(0) was and resolve that.

With the shaders now actually being used, I was able to work my way back from “all red pixel shader” to a properly functioning BasicEffect, complete with World/View/Projection matrices. Drumroll…

DLC Quest on a Mac! Now with actual gameplay!

Yay! In-game!

That’s actually skipping ahead a bit, but I figured you deserved a picture after all that rendering rambling.

I had to do a bit more work to enable SpriteBatch tinting, but that was rendering solved for today. There were a few more issues though.

First off, a lot of my data (awardments, DLC pack definitions, etc) was not being loaded at all. It turned out to be my use of the ContentManifest, which is a sample from the AppHub. In a nutshell, the ContentManifest is an asset that builds a list of all the assets in your Content project at build time. The result is a list of filenames that you can read at runtime to iterate through the built files. I use this to facilitate a data-driven approach to things like awardments – I just drop an awardment .xml file in my content project and it will be loaded by the AwardmentManager automagically.

However, since all content is built on Windows, those filenames use the Windows file separator (“\”). When I read them at runtime on a Mac, they still have the (now incorrect) Windows separator and thus parsing them using something like Path.GetDirectoryName doesn’t work. I added a little fixup routine for the slashes and that was that.

The last problem was that everything was falling through the world.

This came as a bit of a surprise. How could that be platform dependent? Maybe a floating point precision difference? I was perplexed, and certainly not looking forward to debugging my core collision library.

I disabled gravity and glided my character around the world, only to find that collision worked just fine against vertical surfaces. I then added a toggle to gravity so I could turn it back on at runtime easily, which let me run and jump properly, but only if gravity was initially disabled. That was a hint that something was going wrong on the first frame.

Turns out that my physics step function took a “float deltaTime”, but made no checks that the delta was a reasonable duration. The first frame of my game was unusually long on Mac and thus all of the objects were simply tunneling through the ground on the first update. Whoopsie. I updated the function to chew through multiples of 1/60 sec rather than just whatever the deltaTime was and then things were back to normal.


This has been a super long update, but it was a long and frustrating day for the most part. Being able to run around my game and interact with things is a great reward though, so I’m thoroughly motivated to press on. There’s still a lot to do though. There seems to be some precision problem with collision after all, as entities are a bit jittery on the ground. Audio is still missing. I haven’t even tried the part of my game that uses render targets. Controller support is untested. Keybinding is an unknown. The list goes on.

And yet, I’m happy with my progress :)

Stepping outside your programmer comfort zone is how you become a better programmer. I’ve definitely got that first bit down.

Running total: 4 days, $112.87

The Great Porting Adventure: Day 3 Recap

The way I was numbering these posts felt a bit weird, since the “Day 3″ post was a recap of Day 2 plus my plan for that day. I’ll use this post to get back on track, and also because I probably won’t be doing any work today so calling it Day 4 would just be silly.

So my plan was to come up with an automated way of creating and setting up the MonoMac copies of solutions and projects, but given that I didn’t really know what was happening, I figured it would be prudent to try porting over a few more projects manually. Looking into the .sln and .csproj format, I decided it might just be faster to do it all manually this time around. I’d certainly like to take a stab at making a tool to do this at some point in the future though.

  1. Started porting over my “DebugUtil” library, which I figured was pretty straightforward and self-contained. I quickly realized that it relied on my “Core” library, which in turn relied on EasyStorage. So much for minimizing dependencies.
  2. Started porting over EasyStorage. To my surprise, this was relatively painless. There was a bit of a discrepancy in the implementation of IUpdateable and IDrawable in MonoGame (its eventhandlers were just EventHandler, not EventHandler<EventArgs>) but that was simple enough to fix. Not sure if that was the right way to fix it, but it compiled and the sample ran fine nonetheless. There also seems to be a difference in the way .resx strings are handled, but I just ignored all of that and kept only English support for the time being. None of my games are localized anyway, so I’ll fight that battle another day.
  3. Lunch!
  4. Then it was time to tackle some of my own libraries, including my debug utilities, my “core” library (with things like input, audio and storage) and my “tweaker menu” lib, for messing with tagged values at runtime. Audio support with XACT is kinda spotty/missing with MonoGame, so I just compiled out audio for now. That was a fight I knew I’d be making going into this, so I plan on dealing with it after the rest of the game is working.
  5. Next came TiledLib. This should have been straightforward, but the support for ExternalReference in the ContentReader implementation in MonoGame was a bit broken. Specifically, the path it pieced together for external assets was incorrect. I played around with it a bit and got it to construct the proper path.

    Why hello to you too, TiledLib sample.

  6. With all my dependencies now converted, and some of them tested, it was time to move on to the main event and start porting DLC Quest.
  7. Only a few compilation errors off the bat, including one for a missing GameTime constructor. Easy enough to implement myself, but a strange omission.
  8. I then came to realize how often I use “#if WINDOWS” in my code. A whole lot of “|| MAC” was added.
  9. I compiled out Audio, my keybind input hooks and my whole “draw from a different thread while loading content on the main thread” just to simplify things. I’ll tackle these soon, but for now audio has some missing XACT support, my input hooks rely on Windows functionality, and creating a SpriteBatch on a different thread seems to cause an OpenGL shader crash.
  10. Et voila!

    DLC Quest, on a Mac. But not really.

But wait! Why did I choose a screenshot of the intro screen and not gameplay? Well, there’s still a lot of work to be done. Something in my main game’s spritebatch calls, be it the SpriteSortMode.Deferred or the use of a custom BasicEffect, causes a crash in MonoGame. I haven’t looked into it enough yet to say what the cause is.

Still, mighty fine progress for two days worth of work in MonoGame (plus one day for investigating porting options)! Nothing more for today though – time to go play some games with friends.

Running total: 3 days, $112.87

Follow

Get every new post delivered to your Inbox.