content top

What are we up to?

It’s been a while since I last posted on here, so I wanted to give a short update on what’s been going on since we released Baseborn.

Chris and I are both participating in OneGameAMonth, and have each released a few games this year already; Scattered Song, Humphrey’s Tiny Adventure: Remastered, and Must’ve been Rats, a game jam project with our friend Paul Ouellette. I also participated in December’s Experimental Gameplay challenge, and my entry was reviewed by Indie Impressions. and IndieStatik. Having one of my games reviewed was quite a milestone for me, and I’m quite pleased with the reception it recieved.

We’re deep in preproduction for our next game, and I’m hard at work on our new engine, which is coming along really well so far. I’ve found that working on small side projects every now and then is a great way to keep motivated on a larger one, so my monthly game jams have actually increased my productivity while working on the engine. We’ll be officially announcing our next game as soon as we have something to show off.

 

Read More

Baseborn released!

Baseborn released!

I’m very pleased to be able to announce that Baseborn (formerly “codename Ifrit”) is finally out of beta and has hit its first release. Thanks to all of you who tested the game back in January, we were able to squash a ton of bugs and add some new features, including a complete overhaul of the AI, a remastered soundtrack, new physics and effects for weapons, and the ability to restart the game at any time.

Perhaps the most important change is that it’s no longer necessary to download a specific version of the game if you’re a Mac or Linux user; thanks to the most recent version of FLAKit we can bundle the whole game into a single file and run it on any computer through a web page. This should also allow the game to run on computers that aren’t supported by Adobe’s flash projector, like PowerPC Macs.

If you missed it, we ran a series of posts during the last week that went into detail on various aspects of Baseborn’s development process.  You can find them here:

You can download the most recent version of the game here, and the source code is available on the project repository. We hope you enjoy the game!

Read More

Baseborn: Technical difficulties

Working on Baseborn was one of the best times of my life. I’ve never been so productive and learned so many new things in such a short time. Working with Chris was awesome, and I’m glad we decided to go on working together after the class was over. However, we encountered a number of technical problems during development, and I wanted to share some of them.

 

One major hurdle early on was that both Chris and I were using Flashdevelop to do our programming, but our professor required all work to be submitted in the form of a Flash Professional .FLA project file. Due to some incompatibilities between the Flex and Flash Professional compilers, all the code we were using to embed art and media assets would have to be rewritten, which, in a project of Baseborn’s scale, would have required an absurd amount of refactoring. Neither Chris nor I had access to Flash Professional except for one day per week (when our class took place), I knew we had to find a way to make our project work in both environments with a minimal amount of changes.

From this necessity FLAKit was born. Instead of embedding assets with Flex metatags or importing them as FLA project symbols, FLAKit loads images and sounds asynchronously from the project directory. This actually helped speed up our iteration time; embedding assets slows down compile time dramatically, while loading them at runtime is almost instant. In a future version of FLAKit I hope to add support for live updating to further increase productivity.

In the end we were able to take our finished project and open it in Flash Professional with no changes whatsoever. I’ll never forget the rush of excitement I as I checked and double-checked that I had opened the correct file. It was almost too good to be true.

 

Before Chris and I were working together, he asked me how one would go about creating and using animations when the tools built into Flash Professional were unavailable. I briefly explained the concept of a sprite sheet and demonstrated my usage of it in my own project. I was using my own custom bitmap canvas rendering framework at that point with sprite sheet rendering built in, but it would have required far too many changes in order to work in Chris’ project. I wrote a quick class that showed off the basics of a sprite sheet system, then polished up the interface afterward. As you can see from his article on Baseborn’s art process, Chris took to sprite sheets pretty immediately. This class ended up serving us through the entirety of Baseborn’s development, and it’s now included in FLAKit.

 

Another challenge was learning to use version control as a team. I’d been using Mercurial and Bitbucket on my own for some time before the class, but I’d never been in a situation where other people were contributing to the repository as well. The first few revisions were full of overwritten changes and merge errors, and more than once my commit message contained an apology for messing up Chris’s work or reintroducing a bug we had previously fixed.

Fortunately, it didn’t take too long for us to get familiar with the tools. TortoiseHG and Kdiff3 make using Mercurial a breeze, and Bitbucket’s hosting and issue tracker helped us to stay on track with our goals and tasks. I can’t recommend them highly enough.

 

One of the biggest mistakes I feel we made was in designing the structure of the code. The update method of our main class is over 450 lines of code and contains a lot of logic that should have been encapsulated into the player class. Towards the end it was difficult to make even minor changes. The enemy class is an example of good entity design; all of its logic is hidden away and it never directly modifies global data. If we had followed this pattern for the player class, our code would have been much more manageable. Unfortunately, the AI logic has received the most work and is the most recent code in the game, so the lessons I learned from writing it came too late to be of any use elsewhere in the project. We’ll be sure to keep this kind of encapsulation in mind in our future projects.

 

We encountered a lot of technical challenges during Baseborn’s development, but overall we managed to pull through and ship a game I’m quite proud of. Working on a team to bring a game to completion was a great learning experience, and I’m excited to see where we go from here.

Read More

Populating the world: Baseborn’s AI

Probably the thing I’m most proud of in Baseborn is the AI. In the original design document, the enemies you faced were simply going to be static targets that the player had to eliminate in a set amount of time, as a “combat training exercise”. When Chris and I teamed up, we decided to greatly increase the scope of the project, and proper enemy behavior was one of the first things I started on.

Behavior

One of my early goals was that the core of the AI system be versatile enough that creating a new type of enemy was as simple as changing a few variables. To facilitate this, I specified a number of behavior parameters that an enemy type could combine, such as “stand ground”, “afraid”, “no melee”, and “no ranged”. By designing the base AI procedure to work differently depending on these parameters, we were able to quickly design new enemy types with almost no class-specific code. Additionally, any bugs we found only needed to be fixed in one place, which was a lifesaver as the system started to grow in complexity.

Thinking

The base AI procedure (called the “think” method) is called each frame. Here’s the structure of the method:

For the sake of my own sanity, I designed each task to only modify a certain set of data. For example, the enemy’s heading is only ever set in the adjustHeading() method, and his position is only ever set in the move() method.

Tasks

When an enemy is killed, there is a chance that he’ll drop a pickup of health, mana or ammo for the player to collect. This method simply checks for collision with the player and applies the effect accordingly.

Some enemies have no default behavior. Whether this is simply because they aren’t supposed to do anything (the debris on the beach near the beginning of the game is actually an enemy type), or because their behavior is designed specifically (such as the Doppelganger enemies that appear towards the end of the game), this allows the think procedure to exit early.

Here we check if the enemy’s health has been reduced to zero, and stop the procedure if it has. From here on, all the tasks are related to moving and attacking, and we can’t have our defeated enemies doing that.

Line-of-sight is calculated as a simple rectangle check. The enemy looks to see if the player is within a rectangle offset from his position to see if the player is within its bounds. This step moves the rectangle to correspond to his location.

This is the most involved task by far, and deserves a more in-depth explanation.

There are a number of cases in which the enemy should reverse his heading. The most obvious is that he should turn to face the player, if the player is in his field of view…but not if a wall is between them. Additionally, he should reverse his movement direction if he reaches the edge of a platform (but not if the player is across a gap and within ranged weapon distance) or bumps into an obstacle (but not if the obstacle he’s bumped into is the player backed into a corner).

The first step is to find out whether the player is in view. This involves a rectangle check and a raycast, as shown here:

In this case, even though the player is obviously within the view rectangle, a wall is occluding the enemy’s view.

Not so in this case. Since the player is in view, the enemy should turn and walk towards him.

Each time an enemy moves, he remembers where he was last. If at any time his position hasn’t changed since last time, he knows he’s hit a wall and that the physics engine is preventing him from moving forward. Unless the player is in view, he should turn around and walk the other way.

Next, the enemy checks to see if his next move would cause him to fall off the platform he’s standing on. Most of the time this would cause him to turn so as not to fall, but if the player is in view he will wait on the edge and try to attack him or knock him off.

If the enemy can see the player, he goes into attack mode. Depending on his behavior parameters, he may try to shoot, get in closer for a melee attack, or run away.

If the enemy’s health is below 33%, he goes into flee mode. During this phase he’ll rush off of platform edges, and stop trying to chase the player. After a certain amount of time he’ll start to regain health, and eventually drop out of flee mode and resume his normal behavior.

Finally, the enemy’s position is updated based on his current speed.

 

This was my first time working with any kind of AI, and I’m very pleased with the way it came out. I look forward to taking what I’ve learned on this project and applying it to future implementations. If you’re interested in seeing the code behind the tasks, you can find it in the Enemy class on Baseborn’s BitBucket repository.

Read More

Game jam postmortem

This was my second completed game jam. I feel like I did a lot of things wrong this time around, as opposed to my Ludum Dare #23 entry where everything seemed to go smoothly. I’m still pretty happy with the way it came out, but I thought it would be worth mentioning the mistakes I made.

What went wrong

Too much time to plan

When I participated in the Ludum Dare, I had no idea what the theme was going to be until the timer started. The knowledge that any time I spent brainstorming would cut into development time was a great incentive to come up with a plan quickly. Chris and I both checked the polls constantly for the days leading up to the deadline, so we knew fairly early on which choices would be picked. This led to far too much planning on our parts, and we were deep in feature creep before the jam had even officially begun. As a result, a lot of our ideas didn’t make it into the final game; some of these, unfortunately, were cut after working on them for long hours.

Distribution of labor

Somehow Chris ended up doing most of the artwork and I did most of the programming. Chris is a competent programmer and I can hold my own when it comes to creating art assets, so I’m not sure why it ended up happening this way. It would have been better for both of us to have split the work evenly.

What went right:

Ogmo

We used Ogmo Editor to design the levels and entity placement. I’d never used any kind of level editor for my own projects before (though Chris had), and it was a huge productivity boost. Level design took minutes instead of hours. I’ll definitely be using Ogmo in my future projects.

Flashpunk

Flashpunk is a totally awesome framework. It does just enough for me without doing too much. Using Flashpunk (and AS3) also meant we got to use Flashdevelop, which is the best IDE I’ve ever used.

…and a little of both

AI

One of the things I focused on towards the end was enemy behavior. I was able to plug in a Dijkstra path solver I’d written fairly easily, and I’m happy with its performance. Unfortunately, by the time I had it finished we were running low on time, so I wasn’t able to tweak things and make them perfect. The enemies occasionally overlap walls, and they can cluster together onto the same square.

I would have also liked to have implemented actual attack procedures. In the end we had to settle for making the enemies damage the player when they got close, instead of attacking normally.

 

Overall, we had a lot of fun and learned a lot about crunch time as a team. You can play the game here, and here’s my timelapse video of my computer during the jam:

Read More
content top