12/31/15

Well, it has been quite a strange year!
In fact, strange might be an understatement.

I spent the first half of 2015 rewriting the entirety of my personal game engine from scratch, and the second half of 2015 deciding that maybe it's not worth keeping to begin with.

I announced a whole bunch of stuff, and while I made no real attempt to follow up on some of those things, I actually succeeded at some of them. Akradion has been done all semester, but I keep forgetting to test it, and Halberd's going strong. Speaking of which...

I went back to my roots! Halberd was this blog's first project, started 3 1/2 years ago. It feels like an eternity now, though.
Also, somewhere along the way, my small relaxed "Get into the college mindset" project turned into an epic quest to create the world's most absurdly powerful retro tile-based RPG engine, so that's something.
For those curious about how it's doing, have a courtesy link.

I spent a quarter of last year making a big update to Singularity, and then neglected to touch it at all this year. Just kidding, I started yesterday. That counts, right?

Speaking of not doing things, I wrote the first part of a tutorial last year, and wrote anotherpart 5 days ago. My goal is to release a part every other Monday, and so far things are looking ok.

I also got a job! (Two, in fact) They were only part-time, but it's nice finally seeing a return on the skills I've been carefully honing for nearly a decade. Will any luck, I'll be able to find work after college. Either way, I'm not going anywhere; this blog has grown on me.

2015 is also the year that I started posting code on my programming blog, three years later...

I quietly released a piece of software that's been in development for a year or two. I'll finish the the post for it eventually.

So...

What's Next?

I've talked a lot about last year, but you might be wondering what I'm up to, besides Halberd. You know about my plans for the tutorial, and for Singularity, but what else do I have in store?

My next step will probably be ditching DFEngine. It's a really hard decision to make, honestly, because DFEngine has been around since before Sophomore year. Nonetheless, I don't see myself using it like I see myself using Halberd. I think this is the case for a few reasons.

Why Drop DFEngine?

DFEngine is primarily operated by writing xml files and lua scripts, which define the game's assets. Halberd was designed with an actual human user in mind from the start, so it's built entirely on top of an interactive GUI-based editor. The thing is, Halberd is still mostly xml-based. It just lets you fill forms and click buttons instead of painstakingly entering information with a text editor. It even has a play button, for instant feedback.

I can't really fix this problem. Not well, at least. DFEngine is C++, and I write my UI code in Vala, which is C-based. The two don't mesh well. I could write my UI code using gtkmm, the gtk C++ bindings, but that just leads to the next problem.

I don't really believe in Object-Oriented programming the way I did coming into college. I used to be a huge fan of C++, until this year when I finally started using C. Don't get me wrong, OO is perfect for solving a few specific problems, such as UI code. Of course, that last bit applies more to simpler OO languages, such as Vala and C#.

More often than not, I feel like OO tends to put you in a mindset where you have to make the perfect hierarchy, and you lose sight of your real purpose. It feels like a trap that wastes resources for code that is ostensibly easier to extend. From my experience, it's the opposite: I find it much easier to extend C than C++, and my code usually feels more concise and easier to read. I've been looking at Go recently, and I think they might have the right idea. I'll have to play with it in the future.

The Solution

Ultimately, DFEngine lacks the ease of use it needs to be good. I started the project because I wanted a framework for quickly getting started during game jams. I'm ending the project because I no longer do game jams, and that's just a little bit sad. I've rewritten DFEngine twice, but each time it has only marginally improved. It's time for a different approach.

Of course, making my game jam entries from scratch in C isn't all that doable, so what will I do instead?

When I refactored Halberd, I split the engine into 5 separate modules. 3 of them are compiled into static libraries, for the sake of flexibility. That's what really got me thinking: What if I just took all of this great, simple, useful code, and just made utility libraries with it? How convenient would that be?

It's easy. It's convenient. Best of all, it's a standard. If I use a common set of libraries for my projects, most of my work disappears and I can focus on the details of each project. Better yet, I basically get a free head start on making editing tools.

One of my first goals this year will be to split some of the Halberd codebase into a separate collection of libraries. After that, I can see about getting back to my other game projects with a new set of tricks up my sleeve. For now though, Halberd and Singularity are my main concerns.

Oh, and here's a little gift for surviving my +3 Wall of Text:

A mockup of Singularity's new UI. It hasn't changed immensely, but there are a few cool new bits in there.

12/29/15

Now that we've discussed some of the prep work we need to actually set up and create our project. In this tutorial, we'll be setting up a title screen and starting menus. This project assumes that you've set up your work environment how you want to and made a fresh project, so if you haven't done that yet, then go do that now.

Drawing Text

Now, we're going to get into some actual code. We start by setting up NCurses:

If you try running this now, you won't see any output because all we're doing is setting up and shutting down. Let's fix that next.

A Centered Title

To start things off nice and easy, we're going to make a simple title screen. All it'll have for now is a line with the title and a line prompting you to press any key. Next week, we'll begin making things interactive, but for now let's stick to baby steps while we start out.

Terminal Size

Terminals come in all shapes and sizes nowadays. As a result, you'll want to be able to handle situations when you encounter a terminal with odd dimensions. While it's up to you how you handle different sizes, they can be broken into two main categories, resizable views and fixed-size views. Either way, it's recommended that you account for situations when the terminal is too small.
First, you need to determine the minimum size of your game, in characters. While there's no official standard, few if any terminals default to a size below 24 rows by 80 columns. This size is that of the VT100 terminal, which many emulators either default to or follow.

Note

Be aware that in this tutorial series, I will be referring to all coordinates/sizes in a rowxcolumn format. This means that the vertical/y-coordinate will be listed first. This notation is used by curses, and I'll be using it to try and stay consistent with the code.

It's up to you whether or not to support larger or smaller sizes, but I recommend keeping the minimum at or below 24x80.

Now, let's update the program to warn the user if their terminal is too small to use. The curses function getmaxyx() will let us do just that. This function takes in a handle to a curses window, as well as two integers for storage.
We haven't touched on windows yet, but you can use the stdscr variable as a replacement. stdscr represents the main window of the curses environment, which is created at the start and fills the entire terminal. getmaxyx(), as the name implies, gets the number of rows and columns in a curses window. Now, we can use these functions to test our terminal.

With this, we can now rest easy knowing that the user won't be launching our game in too small of a terminal. We can handle changing our game based on the size later. For now, this will do.

Note

If you're planning to use a graphical curses implementation, like pdcurses with SDL, then changing resolutions is probably not a huge problem for you. Regardless, it may be good to implement the above in case you want a fallback version that runs in the terminal.

Printing Text

Let's finally get some text on the screen. I'm going to start by showing the code, then we'll go through it line-by-line to examine how it works.

On the first two lines, we call the function mvaddstr(). This function is actually a sort of shorthand. In curses, the move() function changes the cursor's position, and the addstr() function places a string at the cursor's current position.

As you work with curses, you'll find that a lot of functions have common prefixes that can be added to simplify your code. move() takes the row and column positions to move to as basic integers, and the mv prefix on a function adds these arguments to the start, allowing you to move the cursor then do something, all in one function call. Again, keep in mind that curses starts with the row number, or the y. Here, I use the rows/cols variables that we used to check our terminal above to center our text. It's always helpful to have the current (or expected, for static views) screen size in a variable for the purpose of placing elements, especially since it'll result in less breakage if you change your mind later.

So, what does getch() do here? getch() is one of the most basic tools at your disposal when working with curses. With getch(), you can check for a single keypress at any time, getting back an integer with the character code (if it was a character), or a const value representing some other key. By default, the function waits indefinitely for a keypress, effectively pausing until the user decides to enter a command, but there's a way to prevent this that I'll cover in a later tutorial. For now, we're using the getch() call to pause the application before we exit (This is also why I added a "press any key to continue" message).

At this point, if you've been following along, you'll have some text showing up on screen. Next time, we'll start looking at drawing more interesting things in our terminal, and play with color.

Next Steps

Our centered text is slightly off-center. Can you figure out why? I'll provide an explanation (and solution) at the beginning of the next part.

Take the text that you fixed in part 1, and create a function that draws any text that you pass in centered on your screen. Bonus points for proper error checking (What happens when your text is too long?) and the ability to specify the curses window.

Delete the clear() call at the start. Do you notice something odd? Take a look at the documentation and try to figure out what happened.

Important Terms and Functions

Note

These sections will contain an overview of the new things that we covered in any given part. My hope is that it'll be a useful reference for following the next steps, although you may still need to refer to the API for more details.

Windows

initscr() Initializes curses.

endwin() Resets the terminal. It should be noted that endwin() is not necessarily final--you can continue using curses as long as you refresh the screen when you're done, making endwin() useful for temporary trips to the command line.

getmaxyx(WINDOW *win, int y, int x) Gets the size of a given window. Note that y and x shouldn't be passed by reference, because getmaxyx() is a macro.

stdscr The default curses window

Input

getch() Returns the next character of keyboard input. If echo is enabled, characters are printed. By default, getch() waits until input is given.

noecho() Disables echo, which is enabled by default. A corresponding call to echo() will re-enable echo.

cbreak() Enables cbreak, which prevents the terminal from buffering keyboard input. If cbreak is off, input won't be received by curses until a newline(\n) is added, probably from the enter key. A corresponding call to nocbreak() will disable cbreak.

Output

clear() Clears the current window.

addstr(const char *str) Draws a string at the cursor. The cursor is advanced to the end of the string.

Other

move(int y, int x) Moves the cursor to [y, x] in the current window.

mv- Prefix for some functions, mostly output functions. Moves the cursor to [y, x] before the function is called.

Final Code

Note

I probably won't always include this part, but I think it's helpful to include the full code that I wrote in a tutorial when I can. Some tutorials might not lend themselves as well to this, but I'll be providing Github links to the relevant commits otherwise, like this one.

12/28/15

Note

This is a reworked and updated version of a post from about a year ago. Since I've decided to try my hand at this again, and this time in a more organized manner, I felt is would be best to repost this with the necessary edits to catch any newer readers up.

In this new tutorial series, I'm going to be walking you through the process of making a roguelike game from start to finish, with text-based graphics. This tutorial will be written alongside the development of my first non-7drl roguelike project, and all code will be available on Github around the time of posting, with an appropriately-named commit.
This series expects at least basic knowledge of the C programming language, but you can probably follow along in C++ as well, though you may need to adapt some things later on to take advantage of its object-oriented capabilities. If you're new to programming, don't fret--there are plenty of good learning resources online for C, and I'll try to keep my explanations fairly simple.
Lastly, while I'm hoping to teach you something, if you think I've made
a mistake or need to be informed of something, then by all means email me or leave a comment below.

Warning

The following tutorial segment is entirely preparatory. There is no code here, only reading. I think this information is important, but if you don't want to read it then just get through the final section. The next installment will be a bit more exciting.

When you start any coding project, it's especially important to know the limitations of your platform. If you don't, you're liable to cause yourself all kinds of problems later down the line. This can be especially true when you work with something as 'different' as your average terminal. Terminals can be quite limiting environments compared to your average graphical medium. Let's examine some of these limits:

Lines

The first of limiting factor relates to how things are displayed in a terminal interface. Generally, text is added line-by-line to the window, and there's no apparent way to alter this. It is possible to do so, however. Most (all?) terminals provide a set of special escape sequences that allow you to perform various functions, like moving the cursor, changing text color, and so on. You can actually use these yourself if you know what they are, but I wouldn't recommend it. Instead, the best solution is to use a library.
I'm going to be using ncurses, an implementation of the curses spec. You should be fine with most curses implementations, however. if you're on Windows, or you want your game to be playable outside of a terminal, I would recommend pdcurses. It does a few odd things, but it's common and reliable.

Note

There seems to be an even better version of pdcurses for Windows, available here. I would give it a proper recommendation, but I haven't tried using it myself.

Draw Speed

ncurses, wonderful as it is, is a bit slow compared to modern hardware-accelerated graphics libraries. It doesn't take long to display things, but if you expect to have a gorgeous 1080p 60fps action game, then you are in for a major letdown. This is probably why most command-line games are either designed to refresh less than once per second, or don't update unless they absolutely must. In fact, if you're making a turn-based game you may be better off forgetting regular draws altogether.
You'll probably want output to be more reactive, only redrawing when things actually change. If you do this, the game will never feel sluggish, and it will use fewer system resources too. This means forgoing most animations, but it also makes things much simpler to work with. Not only that, it may also let you get away with complex calculations, given that you don't have to worry about frame time.

Color

Next, we have a tougher issue. Depending on what sort of terminal or terminal emulator is being run, you're likely to run into a number of color configurations. Most terminals fall under one of these categories regarding color:

Black and White Monochrome terminals aren't as common as they used to be, but they still exist here and there.

16 colors As far as I'm aware, most color-supporting terminals have the same general default 16 colors. They are split into two sets, 8 normal and 8 bright. The colors are black, red, green, yellow, blue, magenta, cyan, and white. Generally, this makes the first 16 colors set for a terminal easy to guess.

256 colors I believe this trend started with xterm, but nowadays this is fairly common, albeit less so than the basic 16.

24-bit color support These are rare, but they definitely exist. 24-bit color (To avoid any confusion, I'm talking about 3 8-bit color channels, rather than 3 24-bit channels) is equivalent to what you can normally draw to a screen (ignoring alpha). It looks like most curses implementations don't currently support this feature, so I won't be covering it.

As you can see, that's an incredibly wide range. Terminals will support fewer colors than their maximum, so most games and applications generally seem to target 2 or 16-color terminals.
Another potential feature is palette swapping. While the default set of colors is fairly predictable, many terminals allow you to change them, giving you more choices to work with if you're careful about managing your colors. However, just like every other feature it isn't guaranteed. Palette swapping can get very messy, but can have a great payoff if you
have a solid understanding of it. If you don't believe me, look at these demos.
Depending on how many optional graphical features you want to add, the complexity can ramp up significantly. I've decided to try and cover whatever a given terminal throws at me, but I'd recommend that a less experienced coder try to stick to simpler configurations. A good easy setup would be to try 16 color support, and maybe add support for other color setups later down the line.
One upside is that these all have one thing in common: Colors are defined and used in foreground/background pairs, giving you the opportunity to draw 2 different colors per space.

Text

The final, most obvious limitation is the medium: In the terminal, everything is text! In fact, it's usually just ASCII. Sometimes other fonts and character sets can be supported, but that isn't necessarily guaranteed. Depending on what is being represented there are a number of methods for displaying things. If you want to draw something large and potentially elaborate, then ASCII art may be the right choice. However, if you want to draw a lot of smaller things, then you may want to only give them one character each, based on what they are. Players are often represented with an @ sign, a dragon could be an uppercase D, and so on. You aren't restricted to using one form of depiction in a game, but you need to keep them consistent.

Now that we've examined our target platform, we can really start. That will have to wait until the tomorrow's installment, though. For now, let me thank you for reading through all of this. I promise we'll start coding soon!

Next Steps:

Note

Since there will be a short wait between segments, I plan on ending each one with 'homework'. These tasks are designed to build off of whatever I discussed in the tutorial, and if they involve coding I'll usually address them at the start of the next post.

Come up with an idea for a game. If you need inspiration, take a look at Roguetemple for some ideas.

Decide what sort of terminal features you'll be targeting.

Get set up. Make sure you have a decent environment for C programming, and a curses implementation. Ensure that a test program will compile and run properly. While you're at it, make sure that the terminal or platform that you use matches your target(s). If you have multiple targets, you need to make sure that you can test for all of them.

Look over an ASCII table, and think about how to best depict the elements of your game. If you plan on using Unicode, browse the charts a bit.

If you don't already know how, learn to use a version control system. I recommend and will be using Git, but plenty of options exist. This isn't required, but it will really help.

12/21/15

If you're reading this, you may have noticed already--I've given the blog a domain of its own, huguesross.net. If we're being perfectly honest here, I should have done this a long time ago, it was quite cheap.

Anyway, I've begun working on a small personal site that will connect to this blog, but I need to finish it before hooking it up to the domain. I also need to figure out how I want the blog to look, which may result in me switching back from dynamic theming again.

I'm still figuring out how I want all of my things to be organized, so please bear with me for the moment!

12/6/15

It has been a long time since the last update post, and I've been quite busy!
I've been wanting to write this post for a while, but kept thinking "one more feature", and now 3 months have passed and I still don't have a post, so I'm going to just do a general writeup of what I've been up to. Hopefully I can get back into the posting rhythm by getting this out of the way.

Refactoring

I realize that not everyone is as excited by the prospect of refactoring as I am, but it has really done this project a lot of good. The main focus of my latest batch of refactoring was to make Halberd's codebase more modular. Before, I had a single set of code that was sort-of broken up, but not well. Now, I've broken things up into 5 discrete segments, each with a very specific purpose. They can be arranged in a hierarchy as follows:

Engine

Editor backend (The base editor functionality, with no user interface)

Game backend (The base game functionality, with specifics like context creation and hardware input handling missing)

Common library (A library containing all the code that's shared between both the editor and the game)

Interface

Editor interface (The actual editor front-end)

Game interface (The executable that the user runs)

The code is still all in C, with the exception of the editor interface which is written in Vala. There's a good reason, of course...

UI Overhaul

...And that reason is the UI.
I tried writing a GTK-based UI in C, and although it worked it was annoying and clunky to write. I decided that since Vala translates to GObject C anyway, it would be easier just to write bindings for the backends than to continue working on this specific part in C.

The startup dialog and project creation workflow are probably the most finished parts, so far.

So far, things have been going well. I've made GTK UIs with Vala a few times now, but this time I'm trying to pay more attention to the details and make things look just a tiny bit nicer.

At this point, the majority of the UI elements above function as intended.

The editor interface is starting to look more like an actual game engine, which is great! This is my first time making an editor with this sort of scope, but I'm pretty happy with my progress so far.

Projects

Apart from the UI, the other main improvement to the game is the addition of projects. The engine now has actual projects that separate different games being made. This is useful for a lot of obvious reasons, so I won't go too far into it. Overall, though, this will make things much easier for me once I actually start making games with it.

Up Ahead

Even with all that I've done, there's always plenty more. My next goals are as follows, in order:

Finish project support, at least as far as file handling goes. This means creating, importing, renaming, moving, opening, and deleting assets from the bottom pane. You can currently open and import these files, but that's about it. I'd also like to get the search feature working.

Re-add support for tilesets. Right now, the editor is using garbage data left behind by the graphics card as a temporary stopgap, but I want to be able to use actual tilesets again, as well as tie tilesets to specific maps.

Start adding more game/engine features. The only thing you can really do right now is walk around (You can interact with NPCs, but they can't be spawned in yet), and I have quite a bit that needs adding before I can call this a real RPG engine.

12/1/15

It has been 2 weeks since my last update post, and the verdict is in: Dungeon Restocker isn't going forward to next semester. This is being posted later than expected, because it has taken a long time to write and consider. To be perfectly honest, I'm a bit relieved. Despite the sunny disposition of most of my posts, this project has been fraught with issues, and I'm not sure I could take a second semester of this.

The Project

I think I'm going to kick things off with our meeting schedule. I've always been a supporter of short, infrequent meetings that serve to make sure that the team is still on track. Sadly, this was not the case here.
Our meeting schedule consisted of in-person meetings on Tuesdays, Wednesdays, Fridays, and Sundays every week. Each individual meeting lasted between 1 1/2 and 2 hours, resulting in the team spending about 6 hours a week on meetings. Note that while we did meet on Wednesdays in class, this was not the Official Wednesday Meeting. That one was about 5 hours later. By the way, I live about 15-45 minutes from campus, so I spent an additional 3 hours a week in transit because of these meetings, tallying up to a personal total of 9 hours a week spent on these things (although, only 6 were logged).
On the day that our team was cut, we spent almost 2 hours in an online meeting, in which we made the decision to have a meeting the next day in order to make the actual decisions we wanted to. This meeting lasted 3 1/2 hours. That's right, we spent 5 1/2 hours over the course of 2 days making decisions that didn't actually matter. I think this little anecdote best describes our team's decision-making process.

Next I should probably address the game itself. Dungeon Restocker's design underwent numerous changes as we worked on it, and I don't think all of them were good. I think the main problem with the design can be summed up simply: There are two games in this concept that oppose each other. My view of how the game ought to function differed from the rest of the team, and I had difficulty seeing the value in their concept. To some degree, this is my own problem, but I'll try to address each evenhandedly so that you can decide for yourself which direction is more engaging.

Concept 1

The team's concept is that of a slow, methodical game. The player acts as a sort of hidden caretaker for the Hero, taking time to prepare the 'perfect experience', and then watching and waiting as the hero goes through it. An infinite number of heroes pass through the dungeon, but there's a long break between the exit of one and the arrival of the next. To end the stage, the player must complete certain objectives, usually making the heroes do a cumulative X of Y (e.g. loot 20 chests, kill 10 monsters, etc). Hero emotion is treated more vaguely, instead relaying various needs to the player (This hero wants loot! This hero wants to fight things!). Making heroes happy is a secondary objective, which rewards the player with resources.

Concept 2

My concept is a fast-paced, frantic juggling act. The player must constantly sneak around behind the hero's back, resetting traps, closing doors, and refilling chests without being noticed. A limited number of heroes enter the dungeon at timed intervals, such that in a perfect game the previous hero exits just as the next enters. To be successful, the player must prepare while a hero is exploring, while making sure that the current hero doesn't start messing with the old preparations. Hero emotions are given freely, along with the addition of a suspicion/confusion meter that fills if things are out of place. Screw up a hero too much, and they'll start moving and acting more erratically, trashing your dungeon, and ultimately leaving. On the way out, they'll confuse the new arrivals, resulting in a entertaining spiral of destruction where a loss is its' own reward (This is admittedly inspired by the classic Dwarf Fortess tantrum spiral). Making the heroes happy is a secondary objective, but is necessary to keep the show running smoothly.

Who's right? Beats me, but our current concept (closer to 1) was unable to properly convey the hero's needs and emotions, which is what ultimately resulted in our failure.
As a gamer, I find concepts like #2 to be more fun and engaging (Hence the chaos that was Sports Game), but it's entirely possible that the rest of my team was correct here. The main thing that bothers me is that I felt like I was left out of the decision-making process. I could speak, but since the rest of my team had all silently agreed on #1, my input would never be used or even acknowledged.

The Team

I'm just going to go down the list when talking about my team. Note that I refuse to name any names here--My teachers will know them already, and I see no reason to call anyone out publicly for anything they did or didn't do.

I should also stress that while I may come off as being rather negative, I really do like these people. Many of them had to deal with external issues, which affected their work, and I don't think they were able to give their all as a result.

Producer

My producer is the easiest to criticize here. On a 4-person team with 504 hours logged, he has logged 50, and never reached the weekly minimum hours on even a single week. He was absent from the majority of our meetings, and pretty much never did anything apart from the occasional piece of documentation. I understand that he was having issues with other classes and his job, but logging 10% of a 4-person team's hours is rather unacceptable.
I think a team can live or die by it's producer, and this one certainly made things difficult for us.

Programmer

If I had to pick phrase to describe my counterpart on this project, it would have to be "too little, too late". He did good work, but it was always a couple of days later than I expected to see it, and less complete than I'd hoped. If that was all, I wouldn't mind, but he did a rather poor job of communicating in general.
He was prone to disappearing for days at a time, without any warning, and failing to respond to any communications. One time he lost his phone, and inexplicably wouldn't just check the team Slack on his computer like I did for the entire semester. Another time, he was having family issues right before the day of a major presentation, and failed to even send a text (he had his phone this time) informing us of his absence. Instead, we were forced to present with him effectively MIA, only to find out where he'd been that evening.

Designer

I'm somewhat torn about our designer, because most of the issues I've had with him relate more to the way he acts, and it's hard for me to tell if what he's doing is intentional or just a simple miscommunication. If we had a more active producer, they probably would have had plenty of opportunities to mediate and 'translate' for us, but as it stands I've just put up with his behavior and tried to ignore anything that feels odd or out of place.
Another reading of his actions, the one that I've been considering more lately, is that he simply takes input and criticism poorly. Every time I try to suggest something or point out a potential stumbling block, he immediately gets to work ripping apart what I just said or otherwise attacking my proposals. I initially figured it might have to do with my point above about there being 2 opposing designs, but then I realized that he'd been doing this since long before we decided to go with Dungeon Restocker in the first place. I might even be as bold as to suggest that he's been doing this from the very beginning.
I don't think it's appropriate to discuss any specific instances of this, given that this is such a personal thing and I'm not even sure if he was doing this on purpose, but I'm prepared to give specific examples if asked.

The other issue, which I will go into more detail with, pertains to this old post, or at least the aftermath of it. After superseding me as the team's de facto artist, he proceeded to achieve...nothing. No assets, no concepts, and no documentation. Over the course of almost a month, he modeled and rigged the character base. He finished it a day or two before our final presentation (with no animation), and it never made it into the game. I would be more understanding, but the last time I checked he'd put in close to 50 hours on it. Those 50 hours could've been much better spent, given the state of our game, not to mention that I found out later that he'd never done this before. What this means is that, having made fully rigged and animated humanoid models before, I would've been a significantly better choice for team artist, at least in this instance. I'm over it at this point, but I wasn't happy finding that out at the time. Worse yet, he also proceeded to completely ignore the standards that I set in our art bible when designing our proper level, but also failed to alter any of our art documentation in the process. Rather than talk to the team and make changes to the art direction, he simply chose to ignore it.
At the time this all bothered me quite a bit, given how abruptly and confidently he took on the position.

Me

I'm certainly far from blameless. For instance, most of the issues that I've had with our designer and other programmer are caused by my own perception, and it's quite possible that the others disagree. I also held on to my beliefs about what was best for the game, despite the fact that my team felt otherwise. All of these factors made it difficult for me to get excited about the project. I put in exactly the amount of effort I needed for a decent grade, and no more, because I any passion I had was suppressed by the situation I was in.

Even now, I feel like I'm betraying my team by writing this, but I need to get these feelings off of my chest once and for all. I can only hope that the others felt the same, because I'm not sure I could face them if they read this.

Conclusion

After reading this post over, I really don't know what to think. This is certainly the worst, most negative and venom-filled postmortem that I've ever written, and I don't know if it should be. A few times this semester, I considered the possibility that I might be the problem with this team. Either way, I hope this little insight into a failed project is useful; I think I've learned quite a bit from this myself. Hopefully, next semester will be better. I've found a team that wants me on board, and with any luck that'll work out nicely.