GGJ 2012: Eat Sheep (and Die)

This weekend I participated in Global Game Jam for the third time (after taking a hiatus in 2011 to go speak at conference overseas). This year, I was at the UVA site and took part in a 3 person team with my brother in law and an awesome UVA student. The result was an interesting two player competitive game called Eat Sheep (and Die).

This jam was way less stressful than any other jam I've participated in. We essentially had something fun to play by middle of day 2, and just polished things throughout the end of day 2 and day 3. As a result, this is probably the most complete jam game I've ever worked on. Very little went wrong. It was great.

So what when right?

  1. AngelXNA – Power of Angel combined with the power of XNA! This was the third time I've used Angel in a jam and every time it's improved. We were able to get levels in games quickly, spawn actors, and do all sorts of things quickly and easily. It actually worked really well. Again.
  2. Abandoning Design – The original point of the game was to have a cyclic back and forth of helping and hurting the other player. On your turn, you would have your goal, and a directive to help or hurt your opponent. On the next turn, you would be given a bonus based on how well or poorly your opponent did. At some point, we realized there was never any reason to help your opponent. It just wasn't worth it. So we abandoned the helping, turn based component, and went with real time versus. This makes it questionable whether we obeyed the theme, but whatever. The game was much better as a result.

What went wrong?

  1. AngelXNA? – So, the one thing I don't like about Angel and XNA is that it does have a huge barrier to entry for people playing the game after the Jam. I know I don't bother playing a GGJ game that requires a download unless it comes VERY highly recommended. I will, however, look at pretty much every HTML5 or Flash game. That said, Angel is awesome, so maybe I have to figure out a way to fix this. Native Client maybe?

That's it. Not too many lessons this time out because everything went so well. It's a weird feeling. Really looking forward to next year!

Updates to AngelXNA

One of the tips I gave in my recent talk is to have a tool for GGJ. My tool of choice is AngelXNA, because it combines the power of XNA with the power of Angel. When I was demoing it to the other attendees, I realized I hadn't added a few demo screens that are really necessary.

To that end, I've added two demo screens showcasing two features that are in AngelXNA that aren't in Angel. The first is the Editor. While I had a problem with the Editor in the last GGJ, Fire Hose used it a lot for prototyping and I think we've worked out most of the kinks. It's not perfect, but it does make creating levels in Angel super easy. There are shortcuts for cloning, scaling, and rotating actors, which makes the level editing process super quick.

The second feature has several aspects, but comes down to the ability to use sprite sheets and the ability to create named, data driven animations. There are two parts to this. The first part is the Tiling system. By adding a .tileinfo file parallel to any given image file, it will automatically start rendering Actors that use that image as tiled. Here's an example .tileinfo:

IsAnim = true
FrameSize = Vector2(160, 200)
AnimDirection = TiledAnimDirection.Horizontal
StartFrame = Vector2(0, 0)
EndFrame = Vector2(0, 2)

It specifies that this file contains an animation, that each frame is 160x200, that the animation frames run left to right, starts at 0,0, and ends at 0,2. You can also use a non-anim and specify a specific tile to render.

Once you have these .tileinfos specified, you can specify animation files. Here's the example anim file:

BeginAnimation("Idle", TiledAnimRenderPath.Create())
    AnimDelay = 0.03
    File = "images/devo/devo_idle_strip"
    AnimType = AnimationType.Loop
EndAnimation()

BeginAnimation("Run", TiledAnimRenderPath.Create())
    AnimDelay = 0.03
    File = "images/devo/devo_run_strip"
    AnimType = AnimationType.Loop
EndAnimation()

BeginAnimation("Fall", TiledAnimRenderPath.Create())
    AnimDelay = 0.03
    File = "images/devo/devo_fall_strip"
    AnimType = AnimationType.OneShot
EndAnimation()

This allows you to call just Actor.PlayAnimation("Idle") or Actor.PlayAnimation("Run").

This is still a bit more complicated than I'd like, but does work really well. The only thing I'd like to improve is the specification of the render path. AngelXNA actually generally selects the correct render path automatically, but the data driving system, specifically the animation system, isn't detecting its render path. I can probably fix this, which I'll hopefully do after GGJ.

Simplicity in AngelXNA

I know I've promised tutorials on AngelXNA, and I actually have the first one written, but I've been distracted by crunch on my current project and don't feel comfortable just posting the first portion without more to follow. In addition, the first tutorial feels way too long, so I'm hoping to add some minor changes to Angel to make getting started even easier, specifically by setting up GameManager and default screens in ClientGame, rather than forcing you to make them yourself.

But enough about that, this post is about rendering.

Angel is really nice in that it makes a lot of things really simple and really transparent. With a few exceptions, everything does what you expect. There are very few pre-conditions for any given call in Angel, so you don't have to worry about whether you've set up X or Y before calling things, or worry that calls will fail because objects weren't in the right state. That just doesn't exist In Angel. With that mind, I'm looking for a way to add a piece of complexity and functionality to Angel without sacrificing the external simplicity.

In AngelXNA, all actors render as a rectangle by default (we didn't port over circles from Angel C++), so by just putting an actor in the scene, you get something rendering. If you want to make it a sprite, you call Actor.SetSprite or (simpler) Actor.Sprite = "ContentFile". Animations follow a similar pattern, loading a sequence of files using Actor.LoadSpriteFrames (or, again, you can use Actor.Sprite = "ContentFile_001" and it will automatically load the sequence). Then you can play the animation sequence using Actor.PlaySpriteAnimation.

The problem is, now, I want to add two additional render paths, one for rendering static and animated sprite sheets, and one that allows you to specify and load multiple named animations (so you could say Acor.PlaySpriteanimation("Jump")). This is easily done by either inheriting from Renderable or Actor, but inheriting from Renderable loses all of the functionality in Actor, and inheriting from Actor means I can't use these render paths in other sub classes of Actor (namely PhysicsActor). In addition, if these render paths were reusable *outside* of actor, it might make alleviate some problems we've had adding things as Actors that aren't just to get their rendering properties.

So my question is, how do I make this simple? One option is to just add more methods and properties. Actor.SetSpriteSheet(), Actor.SpriteSheet, Actor.LoadSpriteSheetAnim() etc. This does have the benefit of being consistent, but doesn't make those render paths reusable. I could create an IRenderPath interface and have one for each render path, using SetRenderPath to assign a specific render path to an Actor and remap the current functions to creating those render paths. In addition, it means the SpriteSheetRenderPath could make use of a reusable SpriteSheet for both tilled maps as well as character animations. This, however, I feel makes Angel overly complicated. Maybe some combination of the two makes more sense?

I feel like I've spent way too much time thinking about this, and would love to get it implemented, hopefully soon.

Immigration Jam Post Mortem

This past weekend was the Boston Immigration Game Jam, and like previous Jams, I'm going to do a quick post mortem about my experiences, not only because I think it will help others, but because it will help me as I prepare for my next game jam. For this Jam I was in a team of 5 on day one, which included 3 musicians, which later went down to 4 when two of the musicians decided to not show up for day 2 and one new programmer joined the team.

The game we ended up making was called "Cultural Exchange" and was supposed to be about managing a country, trying to attract the "right" sort of people while keeping out undesirables. To do this, you would enact legislation to make your country more appealing to some people, and erect fences and the like to keep out undesirables. However, people in the country wouldn't like things being too restrictive, since they value their freedom, so it was a constant balancing act.

Now I know normally I start with what went right at the jam, but this time I feel what went wrong is more important. So here we go:

What Went Wrong

  1. Not having a clear idea: While the description is fairly clear, neither me nor Kevin had any idea how we were going to get it working. We didn't actually get anything designed until very late in the night, and even that was just kind of border-line. Since we didn't have a clear idea about how the game would play, we had no idea what our restrictions were.
  2. Using Flixel: Now, no offense to Flixel, but I was not impressed with my first outing with it. This is partially linked to not having a clear idea of what we wanted to do, and not having a clear understanding of the limitations of Flixel. I spent 90% of the first day fighting with ActionScript and Flixel, not only because I didn't understand Flixel's limitations, but because Flixel has some issues with transparency, some of which it inherits from Flash. I spent at least 3 hours trying to move some buttons off screen, only to throw up my hands in frustration. In addition, we were using FlashDevelop, which does not have a debugger included for Flash, which made dealing with all the problems that much worse.

    I took 3 learning points out of my Flixel experience, which don't actually relate to Flixel at all:

    1. Always use something you're familiar with for a game jam, even if it's not the best tool for the job.
    2. Constrain your idea to what you know is possible in the tool you're working with.
    3. Do not take platform into account. If you really like your game after the game jam, you can always port it to a different platform.
  3. Too big a scope: Out game had way to big a scope for our time frame. There was just no way it was getting done.

What Went Right

  1. Switching to AngelXNA: At about 10 on Saturday, I switched all of our code from ActionScript to C#, and all of our Flixel code to AngelXNA. I was too pissed with Flixel's limitations to deal with learning how to get around them, whereas I knew the limitations of Angel really really well, and I could get around them very quickly (see point 3a). Even though this meant losing a programmer on the project, it still ended up well (see the next point).If nothing else, Angel, I've realized, is EXTREMELY transparent. I find pretty much everything does what you'd expect. That said, getting started up in Angel is hard, and I do need to do some tutorials for it in the near future.
  2. Having a dedicated designer: The second day, Kevin ended up doing only design. This was the ONLY way we actually were able to finish at all. Kevin ran through turns on paper, and looked into how legislations would affect the population on each turn. Looking back at other Jams, I think we could have done better if we'd had someone we dedicated as "designer" to look into these problems earlier, rather than playing it by ear the whole time.

Generally this was a short Jam, and I went into it already tired, so I wasn't super psyched about it. But I think I learned a little bit more about jamming, and some things I really want to add to Angel for the next jam, so keep an eye out for that.

One More GGJ Observation

Today I realized another thing that went wrong with our GGJ product, that I felt needed sharing.

  1. Starting At the End: This one is twofold. First, we spent a lot of time working on the end boss battle of the game, which, while awesome, had to be integrated with the rest of the game at the end of the jam, mostly in the last hour. The core mechanic of the game, the destruction and rebuilding of objects, suffered as a result. Second, when the game was pitched, I included a portion "at the end" where it turns out you're a horrible person for destroying the world. We never got to this because the game took precedence.

Basically, I've found that almost any game jam game that has the words "and at the end of the game" never gets to that point because developing the core mechanic and building gets in the way. And if you need the end of the game to keep with the theme of the jam, you're going to end up breaking theme.

I will say in defense of my team that it was mostly me talking about the "end of game" theme. Amanda pretty much spent the entire time telling me we wouldn't get to it, so this rest squarely on my shoulders.

In the end, I don't think Quest For Stick fit the main theme of the Jam (deception), but I think the core mechanic was awesome, and I'm happy we decided to make it instead of making something that would obviously fit with theme.

Jamming Post Mortem 2010 Edition

Last weekend, I took part in the Global Game Jam, like I did last year and let me say it was just as fun, if not MORE fun this year than last. Our game, Quest for Stick, was really, really awesome this year, and you can learn more about it from the GGJ page and from our Twitter account. We even have a video of a complete play through of the game. The game is super pretty, only a little bit buggy, and generally I think accomplished everything we wanted.

But this year I went in knowing what to expect. How'd I do this year? What did I learn?

What Went Right

  1. The Team. Last year, I said one of the things that went right was having a team. This year, that was even more so. We had a total of 7 people working full time on the game, which, initially, I thought was way too many. So much so that I actually asked people to leave the team and considered leaving the team myself to reduce the number of people. But, when it came down to it, I decided I wanted to work on the game idea and went with the other 6 people to create the game. Honestly, 7 people may still have been too many, as communication and tasking did get hard near the end of the project, but there's no way the game would have been anything near what it was if we didn't have at least that many people. Everyone was basically tasked the whole time, and the game came out great because of it.
  2. Getting Down To Business: We spent very little time talking design this time, which worked out to our advantage. Although we spent a lot of time later arguing about how exactly the game was going to play, it didn't take away from everyone working, which was good. We got down to making something playable quickly, and didn't try to design too much stuff up front.
  3. Tools choice: Last year, I was super happy with XNA. This year, the team used AngelXNA, even though I was the only one familiar at all with it, and I was the really the only one well versed in XNA. Even though I spent a lot of time helping people understand Angel / XNA, it was still, by far, better than attempting to use only XNA. It performed a lot of the heavy lifting for us in terms of doing animations, placing and managing actors, and, surprisingly, editing levels, though this is its own bullet point.

What Went Wrong

  1. Unclear tasking: Occasionally, we got duplicated work or weird moments of down time because, like most game jams, people just shouted out things they needed. Kate was really the only person keeping track of most of these tasks, and really only for herself. For the artists, no one was really in charge of knowing what art was still needed and who was doing what. For a team this large, what we needed to create and consult a list on a whiteboard or cork board that had any asset requests, who was potentially doing them, what was in progress and what was up for grabs. This would have avoided duplicated work and would have given us an idea of how much work was left.
  2. Late Playable: Despite my work to prevent this (more on this later), we still didn't end up with an actual playable game until mid day on the last day. Just having *SOMETHING* sometime on Saturday to hand to the artists and designers to make levels on would have helped. We did have lots of pieces that worked, essentially, but didn't get them integrated together fast enough.
  3. Encapsulation problems: We had three programmers working together on individual parts of the game, which helped not only keep them tasked without stomping on each other, but made it so people were in charge of very small systems. However, some of the systems were weirdly encapsulated, and required copying and pasting over when we actually got to the point of integrating. Though this actually ended up *helping* at the very end, I would have liked fewer instances where I had to copy and paste the code from one class to another in order to integrate a new system into the main game.

What Didn't Work

So, these things really didn't go wrong, but they were things I was hoping would help us during the jam, but didn't.

  1. The Simple CI: Before the Jam, I wrote a simple python script that would query a mercurial repository, pull down new code, build it, copy it up to a network location, then message everyone over gchat. This was awesome in theory, but not so much in practice for a few reasons. First, it didn't work so well. If anyone was signed out of gchat when it went to message them, the CI would get stuck in an endless loop. Second, the network drive would occasionally flake out and not be able to take the new build. Third, we didn't have anything the team could play until Sunday, so the CI ended up being useless until then.
  2. The Angel Editor: The editor in Angel was an awesome idea, but when we got to the Jam, it was buggy and untested. It didn't save out things correctly, crashed, spawned items in weird places, and didn't work at all with our custom actors. In addition, the editor saved all levels out to the build directory, which was great for everyone but the people who were using it. Besides fixing the other editor bugs, in the future, the editor will probably need to detect whether a debugger is attached, and figure out where to put the levels from there, or create a custom levels folder that can be easily moved back and forth and through to an integratable build.

All and all, an awesome Jam. Please play Quest for Stick, and let me know what you think. I'm super proud of it.

Angel, New Features, Documentation?

I just pushed a new feature to AngelXNA which integrates a very simple (and currently fairly dirty and incorrect) lexer / parser into the console. The nice thing about this lexer / parser is that it allows the Angel console to now track C# objects, instead of just strings. In addition, you can bind various properties and methods of those C# objects to be console callable, which has made actor and level definition pretty easy to do in the new versions.

Here's an example of the old Angel adf files:

  1. ActorFactoryInitializeActorClass PhysicsActor
  2.  
  3. ActorFactorySetColor 0.5 0.5 0.8 1.0
  4. ActorFactorySetSize 3 3
  5. ActorFactorySetDensity 0
  6. ActorFactoryAddTag maze_wall

Each of these was marked with the [ConsoleMethod] attribute bound to their singleton via reflection. It was cool, and it worked, but here's the new system:

  1. ActorFactory.InitializeActor(PhysicsActor.Create())
  2.  
  3. Color = Color(0.5, 0.5, 0.8, 1.0)
  4. Size = Vector2(3, 3)
  5. Density = 0
  6. Tag("maze_wall")

In the back end, ActorFactor.InitializeActor and PhysicsActor.Create are static methods on C# classes, marked with [ConsoleMethod], and the Console class automatically finds them. Initialize actor has an implicit "Using" which means that the object passed to it is the subject of any calls until an EndUsing (or EndActor). So, in reality, the new adfs are shortcuts for console scripts that would look like this:

  1. ewActor = PhysicsActor.Create()
  2.  
  3. newActor.Color = Color(0.5, 0.5, 0.8, 1.0)
  4. newActor.Size = Vector2(3, 3)
  5. newActor.Density = 0
  6. newActor.Tag("maze_wall")
  7.  
  8. World.Add(newActor)

Each of the calls to set actor properties (Color, Size, Density, etc.) are all just properties on the C# Actor object, marked with [ConsoleProperty].

What all of this means is that if you want a class that's accessible from the console and has properties and methods you can call here's what you do. First, create a class that has methods and properties that you want console accessible tagged appropriately, like so:

  1. public class MyConsoleClass
  2. {
  3. public MyConsoleClass()
  4. {
  5.  
  6. }
  7.  
  8. [ConsoleProperty]
  9. public string Name
  10. {
  11. get; set;
  12. }
  13.  
  14. [ConsoleProperty]
  15. public string Value
  16. {
  17. get; set;
  18. }
  19.  
  20. [ConsoleMethod]
  21. public string GetMyInfo()
  22. {
  23. return String.Format("{0}:{1}", Name, Value);
  24. }
  25.  
  26. [ConsoleMethod]
  27. public static MyConsoleClass Create()
  28. {
  29. return new MyConsoleClass();
  30. }
  31. }

Second, compile and run you AngelXNA application. It will automatically detect your new class and allow you to now issue the following commands in the console:

  1. myVar = MyConsoleClass.Create()
  2. myVar.Name = "My Name"
  3. myVar.Value = "My Value"
  4. Echo(myVar.GetMyInfo())

So, that's pretty cool! The whole system needs work, as it's fairly dirty. The execution / parse tree can't handle chaining right now, so commands like myVar.Actor.PerformAction() won't work. In addition, there's a lot of boxing / unboxing of values going on, and some wonkyness where the console works mostly in floats. I'm not sure how much of this I'll have a chance to correct, but hopefully much of it as we move forward.

In addition, this whole system needs documentation on our wiki. If you want to help out please let me know.

Lastly, Ian Bogost and Borut Pfeifer have pointed out (via twitter) that AngelXNA definitely needs to be more user friendly to start with. As Ian said, we need things that " will help people get started' and "help them get up and running" quickly. Borut pointed out we may need more screens for the Intro game to explore a lot of the features, but I'm thinking we need more than that. For people that have tried Angel / AngelXNA, what keeps you from starting work quickly? What's confusing? How can we improve the experience and get you working on prototypes quickly and efficiently?

AngelXNA v1.0

Thanks to a lot of help from Darren, today we're officially announcing the release of AngelXNA 1.0. For those that don't know, AngelXNA is a port of the Angel prototyping engine made by EALA and released open source not too long ago. The justification for making a C#/XNA version is that I, personally, like working with C# more than I like working with C++, at least when I'm trying to do prototypes. C# allows me to program faster and worry less about things like memory leaks, memory trashing, and weird side effects. By utilizing XNA, we get a lot of stuff for free, including input handling, sound, and music handling, along with the possibility (though it has not been tested) or running prototypes on the 360.

The interesting thing about creating / porting Angel was a question Darius posed to me while we were working on it, which is this: were there any decisions we'd made about the design of Angel that favored simplicity of creating games over speed / efficiency? Did we do anything that we wouldn't do in an actual game engine, just because it made programming games simpler? It\'s a pretty good question. Certainly, neither Angel nor AngelXNA take advantage of memory optimizations like object recycling or pooling, and there's no inline optimizations in AngelXNA. In addition, we didn't optimize any of the rendering or animation systems by doing things like pooling similar objects together, and AngelXNA ends up opening SpriteBatch blocks a bit more often than it should. But the question is could either version of Angel be optimized this way and still keep the same interface? It's a pretty good question, and right now I'm not sure. Regardless, I think AngelXNA is pretty easy to develop on and runs fast enough to make it a pretty nifty prototyping system, so at least that part is a success.

If you'd like you can grab the v1.0 tag from our bitbucket site (zip direct link), and Darren did a great job of creating some initial documentation on the wiki, so please check it out and tell us what you think! Darren and I (and anyone else who\'d like to contribute?) will be looking at moving on to version 1.1 soon, which should include some simple pathing using A* (something that\'s in the original Angel) and some other simple AI.