4 Hugues Ross - Blog: 2019
Hugues Ross

6/18/19

Back to AMAZE - 3 - Lookin' Good!

In just under a week, I've managed to produce a mockup for the remake. To keep things simple, I decided to redraw the screenshot on AMAZE's game page. I think the results speak for themselves:


Honestly, I'm really happy about how this one turned out. The environment feels like it came straight out of my nostalgia. But that's enough gushing, let's examine some of the changes.

What's New?

If you compare this mockup to the original, the first thing that you might notice is the difference in size. The new one is actually much smaller than the old one. This might seem odd, considering the difference in detail, but in reality the mockup just uses space more efficiently. The original was 640×480, but for the new one I decided on 480×270. This was for two reasons:
  1. It scales perfectly to 1080p (×4)
  2. It gives a similar aspect ratio to the original's viewing area, but with 24px tiles instead of 32px
32 was a really common number to see back when I started making games with GameMaker 6.1. However, I've since come to appreciate 24×24 as a tile resolution because it strikes the perfect balance of time investment and detail. 16 tends to be difficult to work with, and I often feel limited by it. On the other hand, 32 often feels like overkill, and the extra effort feels like a waste of time. 24 gives just enough space to add good detail without creating much more work.

Next up is the UI. The original game has this big tacky bar at the top of the screen with information on it. Most of the information on the bar is fluff, and I'm not convinced that your average player would even recognize what some of the bars meant.

By contrast, the new UI is simpler. The Nintendo-esque counters on the top-left are pretty clear, and the only other element is the active tool. You'll notice that I opted for numbers as opposed to the bar in the original. That bar's ticks didn't always line up with the actual number of uses, which is kind of an insane design decision in retrospect. This time, I wanted it to be perfectly clear.

The last big changes are, of course, the collectible and enemy sprites.
I mostly swapped those out to better fit the rest of the game. It seemed odd for a robot to be dodging fireballs in a cave for gold and jewels, and by changing the sprites I felt that I could get everything to fit a common theme.

The Next Stage

I think I'm happy enough with this mockup to move on. As I explained last time, my next goal is to get the old data working in a new codebase. That means putting this mockup away for the time being, but with any luck it'll be back soon!

Next Time: Good Old-Fashioned Programming Updates

6/11/19

Back to the Maze - 2 - Setting Our Sights

I had my fun in the last post, now it's time to get a little more serious. If I want this project to be successful, I need to plan a little in advance.

What Do I Want?

Before jumping deciding into this project, I think it's worth stepping back and deciding if I really want to do this. In this case, it's not actually a hard decision to back up. I was never really content with how the game wound up the first time around, that much is made clear by the release and postmortem. I've toyed with the idea of remaking the game for several years, but it never really felt like the right time. With Halberd shuttered, my slate is now pretty clean.

My drive here is pretty simple. I think AMAZE was a fun concept, but the execution was really lacking. With my vastly improved programming and art skills, I think I'm in a good position to do it justice. That informs my goal:

"Take the original concept of AMAZE, and turn it into a fun and polished result"

About the Project

Overall, AMAZE is actually on the smaller end. At almost 4kloc it's slightly bigger than Cloudy Climb, but dwarfed by Halberd and DFGame. Besides the antique rendering, it has a few odd quirks:
  • A custom scripting language, which looks like this:
    Component Physics yvel 0
    Component Physics xvel 2
    Wait 48
    Component Physics xvel 0
    Component Physics yvel 2
    Wait 48
    Component Physics yvel 0
    Component Physics xvel -2
    Wait 48
    Component Physics xvel 0
    Component Physics yvel -2
    Wait 48
    Repeat 
    • And yes, those times are measured in frames
  • A data-driven pseudo-ECS, but with (originally) no concept 'prefabs'. Besides some later parts of the project, every object in every level was made by hand.
    • And where prefabs were made, the files looked like this:
      Body
      0 4 0 0
      Collide
      ?P Kill . !c Info solid false = Destroy .
      Sprite
      Obstacle.spa:Cannonball
  • A sprite format that looks rather similar to DFGame's, but with separate images per-frame instead of spritesheets. This was the original prototype that eventually led to DFGame's (improved) sprite assets.
  • My first ever attempt at graphical development tools, in the form of a simple asset exporter. This was needed, because the asset format is compressed binary for some reason.

Baby Steps

Starting over completely from scratch doesn't seem like a great idea. Instead, my current plan is to handle this remake in stages:
  1. Mockups
  2. Recreate the engine, and get the original game's data working in it
  3. Upgrade the engine, adding features and replacing the godawful custom scripting language with something sane
  4. Build simple development tools, validate them by remastering the original game's content with new assets
  5. Build a better game with the result, and include the redone original levels as a fun little bit of side content
This is a fair bit of work, but I'm optimistic because it's broken up into concrete sub-projects with well-defined endpoints. If I ever get tired of the project, I can just wrap up the stage that I'm on and set it aside for a while.

Looking Deeper

Just making a list is all well and good, but I think it would be good practice to try and predict the future. My predictions are often wrong and I don't believe in ship dates, but it might be interesting to compare predictions with reality as I progress.

Mockups

As I mentioned in part 1, this all sprung from a desire for mockups. Since that was my original goal, it stands to reason that I should do that first. This is a simple art project, so there's no real risk here. I expect a couple weeks of work for a couple simple mockups, and judging by the original screenshots there's a solid guarantee of improvement regardless of what I do here.

Time estimate: 1-3 weeks

Recreate the Engine

This is an interesting one. Given that I'm changing language and underlying APIs, it's safe to say that I'm going to have to commit to a full rewrite. On the other hand, I don't have to worry about tools, exporters, or asset creation. In the old codebase, that's a modest 2.5kloc and I have a spec to build against.

On a third hand, there's one possible stumbling block: Asset loading. AMAZE had fully custom binary asset files (because of course it did), and I'll have to translate those into some DFGame equivalent when loading. This, combined with a custom scripting language and nonsense code, could create some problems. Just to be safe, I think it's worth padding this figure.

Time estimate: 3 1/2months

Beyond the initial engine rewrite, I'm not confident in any predictions. Stage 3 could last forever if I kept adding features, and beyond that requires more design and scope considerations. Still, what I've looked at here feels like enough to wrap up most of the year.

I haven't yet decided how I want to handle this project from a blogging standpoint. I know for certain that I'll be capping off each stage with a post, but I haven't yet worked out how much I want to do in-between. We'll see when we get there.

Next Time: Actual Real Progress

Back to the Maze - 1 - TERROR AND MADNESS

I think it's about time for another video game. In the spirit of last year's Cloudy Climb remake, why not take another of my old games out for a spin? Why not, say, AMAZE?

What could go wrong?

And So It Began

It all started innocently enough: A certain art community that I'm in announced an activity about re-drawing an old piece of work. Just for fun, I thought I'd grab a screenshot of AMAZE and make a mockup in my style. However, AMAZE doesn't really work anymore. The Linux build compiles, but the old renderer doesn't, err, render.

The Windows build fared a little better in Wine, but crashes frequently. I can't really blame Wine for this, I'm not entirely convinced that the game is crash-free under normal circumstances...

Foolishly, I began to think: "Man, I wonder what that drawing code looks like? Maybe it's a simple issue, I'll bump up the OpenGL version and we'll be all set."

As it turns out, the game is running OpenGL 2.1's fixed-function pipeline. For the unaware, OpenGL 2.1 is roughly 13 years old at this point, which is very interesting considering that AMAZE is only 5.

Delving Deeper

So, ok, maybe the rendering code is unsalvageable. You win some, you lose some. However, I thought it might be fun to poke around the rest of the codebase and see how I used to write code.

After all, why not take a fine trip down memory lane?
   23 class Game{
   24  public:
   25   Game();
   26   void loadspr(string s){Sprite spr = spriteLoader.load(s); sprites.insert(pair<string, Sprite>(spr.id, spr));}
   27   Sprite* get_sprite(string s){return &(sprites[s]);}
   28   void frameStep();
   29   void gameLoop();
   30   void end();
   31   bool get_keydown(byte key){return keys[key];}
   32   MapLoader* get_map(){return &maploader;}
   33   float camera_x = 0;
   34   bool camera_xlock = false;
   35   float camera_y = 0;
   36   bool camera_ylock = false;
   37   float camera_xscale = 1;
   38   float camera_yscale = 1;
   39   void drawText(float xpos, float ypos, SpriteFont f, string text);
   40   unsigned score = 0, level_score = 0, hub_score = 0;
   41   unsigned equipment = 0;
   42   float equipment_percentage = 0;
   43   unsigned lives = 3;
   44   unsigned max_lives = 3;
   45   unsigned hub_max_lives = 3;
   46   game_states gamestate = SPLASH;
   47   float splash_alpha = -1;
   48   float splash_timer = 30;
   49   string disptext = "";
   50   vector<c_CResponse*> active_scripts;
   51   vector<int> script_positions;
   52   vector<int> script_timers;
   53   vector<pair<int, int>> script_args;
   54   void updateScript(c_CResponse* script, int& position, int& timer, pair<int, int> arg);
   55   ScriptLoader scripts;
   56   unsigned disptxttimer = 0;
   57   bool keys[5] = {false, false, false, false, false};
   58   int menu_select = 0;
   59   string hub_level = "Z1-Beginning.rma";
   60   string hub_tileset = "Tutorial_tiles.tla";
   61   string hub_song = "/TitleMusic.ogg";
   62   string current_song = "/TitleMusic.ogg";
   63   int hub_progress = 0;
   64   map<string, ALLEGRO_SAMPLE*> sounds;
   65   bool scripts_run = true;
   66   bool game_running = false;
   67   MapLoader maploader;
   68   int triggers[10] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
   69   string var(string input);
   70   void alter(string key, string val);
   71   bool compare(string key, string val, char op);
   72   float sound_volume = 1.0f;
   73   bool godmode = false;
   74   void writeSave();
   75   bool dead = false;
   76   bool shield = true;
   77   int dcooldown = 60;
   78   int difficulty = 0; //0: easy, 1: med, 2: hard
   79  private:
   80   Sprite* s;
  ... 
  105 };
  106 #endif
oh no.

Some of you may be wondering to yourselves: "Strange, what's that 'Sprite* s' for?"
ABSOLUTELY NOTHING


Next Time: Busting out the hacksaw

5/19/19

Halberd - Violently and Thoroughly Changing Directions

Hello all,

Today, I wanted to talk about a change of approach. Changing approaches is something I do a lot, probably because I'm not very good at project management. I've been taking a break from Halberd for a few weeks, and I'm starting to have some reservations about my current plans. The crux of my worries is the following question:

Why am I doing this?

In the original announcement, I gave two goals for Halberd:
  • An open-ended and vague "make a cool and polished engine"
  • Making some games
Goal #1 feels like a bit of a cop-out. Saying "I'm going to make an engine because I want to make an engine" doesn't really mean anything. Instead, let's focus on goal #2.

It's been nearly 14 months since I started Halberd. My longest-running finished game project, AMAZE, took roughly 9 months to build from scratch. It's safe to say that had I been focused on the goal of making games that I'd have finished, or gotten very far along with, one or two of them by now. Instead, I've been busy with Halberd. Why?

I think this is a problem that I've had before. It's really easy to get distracted by dreams of being a cool dude with a big flashy product, and wind up on a wild goose chase. DFEngine suffered from a similar issue as well, and I think both have something in common: Rather than treating the game engine as a means to an end (the game), they exist as standalone projects.

There's also a question of audience, the way I'm designing Halberd's workflow isn't optimized for my needs. Despite that, I'm the only customer of the project that I know of. I really should be building tools for myself first and foremost, since I know myself best and don't have a focus-testing budget.

Thankfully, the solution is very easy! Like any good parent, I will cut down the disappointing child immediately, and without mercy. That's good parenting, right?

Wait, you're just--

Yup. Halberd's dead forever.

As for those games I wanted to make? I'll make them. I don't need a complete GUI-driven game engine to make games, and the remains of Halberd may provide a helpful initial boost. If I succeed in my projects, I'll obviously also have a viable RPG engine at the end.

Win-win, right?

4/26/19

Halberd - Actors, Part 2

At the end of last month, Halberd's current iteration had its first birthday. I think that makes this my longest and most successful attempt so far! As I mentioned in my original post about Halberd, this has been a recurring project, something that I wanted to do for years but never managed to complete. Seeing a milestone like this is pretty nice!

That's enough celebrating though, it's time for dialogue.

Dialogue

As you might guess by when this is coming out, I missed my mark by about a week. Between taxes, a birthday, and a pixel art collab, I put a few too many tasks on my plate and the date slipped. I finished the feature, but then some health problems kept me from actually posting for a while.

It's been done for a while though! Dialogue trees can be displayed via trigger, and can have test boxes, choices, and even call triggers mid-dialogue. This obviously helps a lot on the storytelling end of things, making a coherent plot that lacks dialogue isn't easy.

The Editor

Right now, the dialogue editor is pretty bare-bones. It's just a property grid wrapped around a dialogue tree, with no special checks or previewing. As always though, the important thing is that it just works. Like all of Halberd's editors, I'll be making multiple polish passes over time to improve usability.
She's a fixer-upper

I think this is a good moment to also remind everyone reading this: This is the gain I was expecting to get from the propertygrid. Being able to will functioning simple asset editors into existence with next to no effort is what made the months of work worth doing. From now on, these basic 'propertygrid editors' are probably going to show up a lot when adding new features due to how fast they are to produce.

Frame Job

Another thing that you probably noticed in that video is the UI. There are still a couple of bugs to work out, but the system's in place and it's now possible to make a much nicer-looking interface.

The main goal that I was working towards with the UI was full layout customization, but right now it's restricted to just skinning the frames around various elements. It's a pretty standard '9-slice' setup, without any fancy bells and whistles, but it works.

Next Up Is...

Something different. I'm going to be changing gears a little, so that will wait for its own post.

3/10/19

Halberd - Actors, Part 1

One of my goals for this month is to get myself back on the right track with Halberd's development. To recover from my little detour, I decided to implement one major new feature: NPCs. So far, I've had some good success!

Defining The Problem

Before starting, I decided to put together a to-do list of sorts. My overarching goal is to have placeable characters that the player can talk to, and in the following two issues I laid-out a general roadmap for the feature as lists of sub-tasks.

I broke my goal in two because I felt that I had a good strategy for separating dialogue from NPCs: By making dialogue part of Halberd's existing collection of triggers, I could design NPCs in a way that would make it pluggable later without any effort. The final design of NPCs (now called Actors) is very similar to Halberd's trigger volumes, but with different criteria for triggering actions.

The process of getting actors in place was uneventful, so much so that I apparently forgot to take any screenshots! Oh well, better luck next time.

Hitting A Snag

Once actors could be added to maps, the next problem was making them trigger actions when the player collided or interacted with them. This turned out to be much harder than anticipated, but not for the reason you might expect.

DFGame's property grid is still very young, and I've been trying to limit my work on it to features that I actually need. At the time that I started on actors, the property grid was completely static. That meant no triggers, because triggers have different fields depending on what they do. So, before I could finish my actors I had to revamp my property grid code accordingly.

In the end, I refactored the code and added support for the <choice> XSD element. This took a little over a week on its own, so I did take a few screenshots:

 Before

Click for full size

First Draft - Initial Support

Click for full size

Second Draft - Style Fixes

Click for full size

Third Draft - Changeable Element Types

Click for full size

Final Version - Slightly simplified UI

Click for full size

 

Wrapping Up

The new possibilities offered by <choice> gives me pretty much everything I need to convert the rest of Halberd's UI to use the property grid. With the new tech in hand, I finished adding actor triggers then merged the actor and trigger editors into a single 'Objects' tab. I also converted the encounter list while I was at it:
Click for full size

Let's Talk

The first step to adding dialogue is getting a trigger in place to do it:
Click for full size

That's the latest thing I've accomplished, so my next move will be to get that text on-screen. To that end, I'm looking into merging my UI work so I can get some pretty dialogue boxes in place.

Once that's done, I'll be making a proper editor for dialogue trees. In addition to letting devs add more text, I also want to add support for choices and triggers in dialogue. The latter in particular excites me: It'll let dialogue have a real effect on gameplay, while tying into future additions (such as items and plot flags) very well.

Watch this space for a follow-up!

2/24/19

Halberd Studio - Priorities and Time

Hello again! Despite 2 months of work, this is somehow my first post of the year. I've got an even deck of good and bad news, but nothing too major on either side.

Tilemap Upgrades

Let's start off with the good stuff. While the task splitting that I started late last year has been slowing down direct progress, it has also given me the opportunity to make some nice improvements elsewhere. During the past few months, I've put some effort into improving the tilemap editor. I'll go through the changes below, but let's see what you can spot first:

Click for a full-size version

Grids

The first thing I added to the tilemap editor this year was a tile grid. A grid overlay may seem like a humble addition, but it makes judging distances and lining up elements much easier. Pushing the feature even further, I also made it fully customizable:

Click for a full-size version
With several different line styles, and any color(s) you want, there's a good chance that you can find a grid that fits the maps it overlays. With that said, the grid can also be turned off if you prefer a clean-looking map.

New Tools

To complement the existing pencil and bucket tools, I added tools for placing lines, rectangles, and ovals. All of these tools can be modified with ctrl/shift as you'd expect coming from most art programs, so the result should be pretty straightforward when people pick it up.

I also took the opportunity to re-use the line code to add 'connected drawing' to the pencil tool, so quick strokes won't leave holes behind!

Previews

Of course, those tools wouldn't work well if you couldn't see what the results. To that end, I also made all of the tools preview their effects before placement. Before, you could only see the new tile directly under your cursor and it was deceptively easy to flood-fill a tile that you meant to place normally. Now, it's blindingly obvious what will happen so I expect fewer slip-ups.

Priority Issues

As I mentioned at the top, there is some bad news to go with the good. Progress on new features has been excruciatingly slow, mainly due to prioritizing the wrong tasks. I've been slowly realizing this for a few weeks, and I'm finally changing directions.

Right now, there are two main improvements that Halberd needs. First of all is NPCs/dialogue. Without characters and text, it's hard (though technically possible) to tell an engaging story. NPCs can also do a lot of other things in an RPG context, such as selling items or providing avoidable combat instead of random encounters. Second is adding depth to the available combat systems, through skills/items/etc. Such additions can give the player options beyond 'attack' and 'run', and allow for more interesting challenges.

What I think happened is that I looked at both of these possibilities, and saw a common thread: both of these require some UI work. So, instead making direct progress on these tasks, I started working on a fully customizable UI system. At the time, I figured that giving developers the tools to dictate how the UI looked and worked would allow for more possibilities with these features. However, this system turned into a time sink that blocked my progress for a few months.

Don't get me wrong--custom UI is a real feature that Halberd will need. It just isn't the right task for the moment. On the upside, what I did make can still be used even if it doesn't offer much yet.

Next Steps

Whether or not I learn my lesson remains to be seen. With that said, I think the next feature that I'll work on is getting NPC support in. With my tilemap editor upgrades done, it feels like the natural path to take. Ideally, I'd also like to work NPC interactions and dialog into the existing triggers. I think doing so will greatly expand the possibilities that these new features add.