4 Hugues Ross - Blog: C
Hugues Ross
Showing posts with label C. Show all posts
Showing posts with label C. Show all posts

12/25/18

Halberd: That's my property!

To anyone reading this on the same day that I post, Merry Christmas! Here's a little present: This year's final Halberd-related blog post.

Of my current priorities on Halberd, one of the biggest is building and integrating a property grid. I think this is a very interesting endeavor, and hopefully you'll see it the same way!

So what's a Property Grid?

When I spoke to some (programmer) friends about this, I was surprised to find out that some of them didn't initially know what a property grid was. So, let's start with definitions.

As the name implies, a property grid is a ui widget that lets the user inspect and edit the properties of an object. It usually amounts to a list of named fields, and you can find one in just about any game engine. Here are a few examples:

UE4's 'details panel' is a form of property grid
As expected, Unity's inspector panel is another good example
The ActiPro Windows UI framework also features a property grid
The GTK UI builder, Glade, also features something in this vein

If you're a game developer, you've probably figured out by now that property grids are a really important game development tool. This is even more true in RPGs, due to the large amounts of 'database' assets which lend themselves well to this type of UI pattern.

Digging deeper: How does a property grid work?

Like any programming problem, there are many ways to implement property grid functionality. However, I'd put most methods into one of 3 groups: Inspected, Declared, and Fixed. These are just personal categories, not proper terms, but I think they showcase some real differences.

An Inspected property grid assembles itself by looking at real game data and converting it to ui. This is typically done through Reflection, either by looking at data in a scripting layer or with a language that natively supports this, such as C#. For languages that can't do this out of the box, a dynamic class/property system can be added on top of the normal code. Examples of such systems can be found in Unreal Engine 4 and the GObject framework.

A Declared property grid is also dynamic. However, instead of inspecting objects at runtime it queries their information from a predefined source, such as a schema or similar descriptive document. Then, it sends data back and forth via Serialization instead of reflection. The result is less flexible, but it's also simpler and the underlying game code may be more performant since dynamic properties aren't necessary.

Finally, there is the Fixed property grid. I call this a property grid, but that may be an overstatement--this grid is essentially just a hard-coded bit of ui for interfacing with an object. By its nature, it's quite simple and totally static. Of course, it's also difficult to maintain and requires constant updates as the underlying data changes.

Depending on your use-case, any of the above can be valid approaches. The fixed solution may seem bad, but sometimes it's the best option: You don't need a powerful and complex solution just to edit a couple of fields.

What have I got?

At the moment, Halberd is stuck with the fixed style. I went for this solution early on to avoid getting bogged down while developing my proof-of-concept, but the scaling problems of this approach are starting to appear.

The general process of adding a new property to an object right now is:
  1. Add the property
  2. Add serialization for saving/loading
  3. Add simple getters/setters for editing
  4. Add the property to a Vapi file for interop access
  5. Add the property's editing widget to the ui file
  6. Add the widget code to the editor's vala class, including the signals for changing the property
That's a lot of steps for one property. With a simple property grid, I could skip steps 3-6 entirely and greatly streamline my development process.

A new property grid

With all that in mind, I decided to try out something better. Since most of my code is C, the declared style is necessary. But how to prepare the meta-information about the properties? In my case, it was actually rather simple. Since I already serialize my data to XML, I decided to go with the obvious option: XSD.

For the uninitiated, XSD is an XML-based schema standard for describing other XML files. While intended for databases, with a few additions the result can work quite well for defining a property grid!

The result looks like this:
You might recognize this as DFGame's editor demo which I completed last year. The controls were hard-coded since it was just a viewport showcase at the time, but now the editable parts are totally data-driven!

The schema that the editor is based on is a bit wordy, but pretty straightforward:
    1 <xs:schema
    2     xmlns:df="/org/df458/dfgame"
    3     xmlns:xs="http://www.w3.org/2001/XMLSchema"
    4     elementFormDefault="qualified">
    5     <!-- Types -->
    6     <xs:simpleType name="fill_type">
    7         <xs:restriction base="xs:string">
    8             <xs:enumeration value="true" df:displayName="Filled"/>
    9             <xs:enumeration value="false" df:displayName="Wireframe"/>
   10         </xs:restriction>
   11     </xs:simpleType>
   12     <xs:simpleType name="size_type">
   13         <xs:restriction base="xs:float">
   14             <xs:minInclusive value="0"/>
   15             <xs:totalDigits value="6"/>
   16         </xs:restriction>
   17         <xs:annotation>
   18             <xs:documentation>A decimal value above 0</xs:documentation>
   19         </xs:annotation>
   20     </xs:simpleType>
   21     <xs:simpleType name="degree_type">
   22         <xs:restriction base="xs:float">
   23             <xs:minInclusive value="0"/>
   24             <xs:maxExclusive value="360"/>
   25         </xs:restriction>
   26         <xs:annotation>
   27             <xs:documentation>An angle in degrees</xs:documentation>
   28         </xs:annotation>
   29     </xs:simpleType>
   30 
   31     <!-- Triangle struct -->
   32     <xs:complexType name="triangle">
   33         <xs:attribute name="size" type="size_type">
   34             <xs:annotation>
   35                 <xs:documentation>The size of the triangle</xs:documentation>
   36             </xs:annotation>
   37         </xs:attribute>
   38         <xs:attribute name="angle" type="degree_type">
   39             <xs:annotation>
   40                 <xs:documentation>The angle of rotation</xs:documentation>
   41             </xs:annotation>
   42         </xs:attribute>
   43         <xs:attribute name="color" type="df:color4">
   44             <xs:annotation>
   45                 <xs:documentation>The color of the triangle</xs:documentation>
   46             </xs:annotation>
   47         </xs:attribute>
   48         <xs:attribute name="is_filled" type="fill_type" df:displayName="Style">
   49             <xs:annotation>
   50                 <xs:documentation>Toggles filled/wireframe rendering</xs:documentation>
   51             </xs:annotation>
   52         </xs:attribute>
   53     </xs:complexType>
   54 </xs:schema>
One way to reduce the workload even further would be to tie this into a code generation system, but that strikes me as overkill for the time being...

What now?

I've been working on this feature for a few weeks now, but I finally merged it into DFGame's master branch yesterday. The next step will be to try and integrate it into Halberd, I'm sure there'll be more to do but I think this feature is a good investment for future work! Once the property grid's in place, the work will hopefully start moving a bit quicker.

5/29/18

Halberd: Let's Talk Architecture

I knew this day would come...

...a dark, fateful day...

...when I actually have to care about code architecture


Halberd's development is chugging along, and now that I've gotten a few systems together my codebase is starting to get a little bit cluttered. That means it's time to tidy it up.

Why the Wait?

Personally, I believe that a lot of independent devs overthink game architecture. There's a certain school of thought that dictates that your code should be a perfect, well-oiled machine that's designed to handle anything you throw at it. That's all well and good in theory, but I've always had my doubts that most indies starting a "from scratch" project actually need more than a quickly-assembled Rube Goldberg machine, held together with twine and masking tape.

There are two primary reasons behind this notion of mine:
  1. Your customer does not care about code quality - They really don't. You could have the most beautiful, well-structured code, and a terrible game that no-one wants. On the other hand, your code could be a tire fire and no-one would notice if you managed to keep the game bug-free. As such, polish and quality should be put where it matters the most.
  2. Good architecture is an investment - And of course, investments have an upfront cost. Designing and building solid game architecture takes time, and implementing new code to work with it can also take longer in some circumstances. In return, you make your code more readable and maintainable in the long-term. However, games are often short-term projects and smaller titles are unlikely to fully reap the benefits, while still paying the cost.
Ultimately, I think a lot of novice devs see various patterns being touted as "best practices", and don't consider whether they'll actually benefit from them in the end. Nowadays, my usual approach to architecture is "as little as possible, until it is necessary".

Why now?

Now that I've written 4 paragraphs about why I usually avoid over-engineering like the plague, let's talk about why I've chosen to begin restructuring my code.

Unlike an internal engine built to develop one or two games, Halberd is intended to be an all-in-one tool used by others. That alone isn't necessarily an argument for needing architecture, but it does highlight one important point: This project will require long-term development and support. Along with large teams, long-term projects are among the truly valid use-cases for sound architectural design in my opinion.

If this were the only factor, I wouldn't be too worried yet.The other issue is that my code is starting to get rather complex, another good sign that some straightening out is necessary. More specifically, I'm dealing with:
  • Language interop
  • Data that must be edited and tested non-destructively at runtime
  • Modal input/display (due to switching between PIE and editing)
  • An engine that links into two different front-ends, and must be able to immediately start in any state (menus, combat, map, etc)
Technically, I have all of that already. Problem is, the level of complexity is turning it into a mess. So, it's time to refactor.

What's the Plan?

When you have a clear need for architecture, the next step is to plan it all out. I've done that already, and I'm going to step you through my process in hopes that it'll be helpful.

First of all, we have to consider our requirements. To begin with, we know that the editor and game are going to be Vala and C, respectively. We also know that our exported game shouldn't include any Vala code, giving us the following high-level relationships:
That has already existed since the beginning of the project, so the next step is to look a bit deeper. Specifically, let's look at how the asset editors are laid out, since my impression is that this is the messiest part. Right now, your average editor looks a little like this:
First off, let's get rid of the doubled-up viewport. As a GTK widget, the viewport shouldn't be owned by anything on the C side, but is so that they can check for things like size changes. If we add an event to DFGame for handling window size changes (which is a good idea to begin with), we can at least remove one of the references.

The other obvious problem here is that the C side is clearly doing two things, editing and testing. As a result, there are multiple copies of each child. The simple solution is to split off any test-related data into its own struct, ensuring a proper separation of responsibilities:
So, what about this new "Game Context" that just popped up? Right now, I'm in a position where I haven't had to think too much about how to put the different game elements together. Since I'm rapidly approaching the point where I will need to do that, let's talk about structuring the game state. Right now things are split:
So, what can we do about this? Clearly, the data here needs to be known to the two "states", but we need a way to manage them and make some data (such as the player) available across all of them. We also need to be able to handle things one at a time (We shouldn't be able to run around the map during a fight, after all) without losing information about other parts of the game. To me, this demands a certain structure:
I think a stack makes a lot of sense for this sort of interaction, because there's a lot of "layering" that can happen in an RPG.

Imagine you're walking through the woods, when a bunch of bandits appear to tell the party that they have a bounty on their heads. The bandits attack, then halfway through the fight some allies join in to help even the odds.

This feels pretty typical by the standards of scripted battles, and it fits this structure perfectly: Map, with a Cutscene within it, which initiates an Encounter, during which another Cutscene plays. And of course, most of these states clearly return to the previous state when they conclude. That's perfect territory for a stack-based solution. Since a stack needs something to manage it anyway, we can also make the manager handle our 'global' information, such as the player's stats and inventory.

Back to the editor, this means that we can use our test context to take the edited data in memory, convert it into a game state, and pass it to the game context to push it onto the stack. If we make an event for when the stack empties, we can also return to the edit mode automatically when something like an encounter in test mode finishes. I can also reuse this feature later to know when the final game needs to exit.

Ultimately, we get something that looks like this at a higher level:

Pretty close! There's a little bit more to figure out, but I think I've covered the important parts here.

So that's all?

Nope, not by a long shot. Iteration is key in software development, so I'm sure to return to the drawing board sooner or later. In the meantime though, this design will help make my code easier to maintain. This little diversion has slowed me down for a bit, but I think it'll be worth it regardless. Since I'm still making pretty good time right now, I can also afford to spend time on these sorts of things. As the Roadmap page shows, I'm getting pretty close to my first milestone!

4/15/18

Halberd: Questions and Answers

If I had to pin a core theme on the first two weeks of Halberd's production, it would probably be "Raising Questions". As I expected, the nature of the project has forced me to think a lot about how to use DFGame properly. Because this project is both very different and much larger than previous any work I've made using the framework, many unknowns in DFGame are being brought to light for the first time. Let's discuss a couple of them!

Modules

With DFGame, it was always my goal to add less 'core' features as optional modules. I had a pretty good general idea of how this was going to work, but I'd never actually tried doing it until recently. I needed a new DFGame module for handling tilesets and tilemaps in Halberd, so I finally gave it a shot.

I'm happy to report that it worked pretty smoothly! Everything mostly worked out of the box, even the OpenGL shader embedding Just Worked™ how I wanted it to with basically no modifications. The only thing that I had to figure out was how to properly use relative filepaths in Meson subprojects, and that only took about 5 minutes.

This good first impression was very reassuring, and has me excited to expand DFGame even more in the future.

Lost & Found

In less positive news, working on Halberd has also exposed a massive flaw in DFGame's design: None of the assets have any idea where they came from. In normal circumstances, this doesn't really matter. Who cares if that sprite doesn't know where it loaded its textures from? As long as they're correct, the game will work fine regardless. Even in a level editor, it doesn't matter too much (since you ought to be using the same backend as the game anyway).

Unfortunately, this information is vital when building something like Halberd. After all, how could you possibly update a tileset, when you have no idea where to save it? Even if you track the path you passed to the load function, that won't help much when you realize that you also need to write the texture's filepath in the same file.

Clearly, this is a pretty major roadblock. There are a few ways to solve this problem, but I'm still considering my options. I want to be sure that the choice I make here won't screw me over later.

Conclusion

It may seem negative to see new problems crop up, but I see it as a positive. The sooner I'm faced with unknowns in DFGame, the sooner I can test them (and fix them if needed). I don't expect anyone else to ever find and use DFGame, but on the off-chance it happens someday I'd rather have all the big questions ironed out ahead of time. In the more immediate future, having the answers to questions like these will also simplify any future projects with similar needs.

Besides the occasional trip-up, progress on Halberd's MVP has been going smoothly so far. Since I'm writing this update, I may as well post a progress screenshot while I'm at it. Here's the current state of the tileset editor:
Click the image to view a larger version
It's not the prettiest, but I think it's a pretty solid MVP-level feature. Now, I just need to keep going and finish the rest!

3/10/18

Cloudy Climb -- The Announcement and Postmortem Double-Feature!

Hello again, readers. It's been quiet around here lately, but that changes today. For the first time in quite a while, I'm releasing a brand-new game! The game is called Cloudy Climb, and it's--wait a second, we've done this before!


Not Quite a Remake

Yes, that's right. I've made a second Cloudy Climb. Like most of my projects, I was never really happy with the original. More importantly, I was in the mood to make a game. I've been working on this version for about 2 months, and I think it's finally reached a point where I can call it 'done' and not feel bad about it.

You can get the game via the widget below, or take a look at the git repo here.



A Postmortem for Our Dear Prince

While I still have your attention, I'm going to do a write a quick postmortem for Cloudy Climb. Postmortems are a very useful tool that I don't use nearly often enough. So, let's get to it!

The Story

I started Cloudy Climb during the last week of December, mostly because I was bored. After DFGame and Singularity taking most of last year, I was in the mood for some simple game development. I also wanted to see how well DFGame would fare under Game Jam-esque constraints, so I set myself a time limit of 1 week.

As we're all well aware, it took a little bit longer than a week. I got the basic mechanic and some of the graphics done in a couple days, but I got sick around the 4 day mark. Since I wanted to make something decent (and this wasn't really a game jam anyway), I decided to spend a few more weeks taking the game to a more finished state. Then, I just decided to take "as long as I need" to get the game into a relatively solid finished state.

In late January, when the game was really starting to come together I finally revealed its existence to some of my friends. Besides being friendly, I primarily did this to gather feedback. I started with this video:

...And slowly polished things over the course of a few more:


With the feedback gathered from my work, I completed the remaining assets and now we're here!

What Went Right?

  • Feedback! I'll be gathering more of that in the future. My friends caught a lot of little things that I was able to fix ahead of release. For a good example of that, listen to the stage end jingle in the last feedback video. One of my good buddies pointed out that it was too drawn-out, and I ended up cutting the final note out. I think it sounds a fair bit better as a result.

    There's also the really nice spike animation, which involved a bunch of critique passes in a pixel art community I found. I think the difference of quality there is very clear compared to the rest of the game's art.
  • Field Testing! One thing that I got out of this that I find really valuable is the experience of using DFGame on a non-trivial project. It's been really enlightening, and I'll be making (and in some cases, have already made) several changes to the API as a result.
  • Requirements! Because of this project, I was finally forced to get off my butt and learn audio. I'm still very bad at music, but I still think much of the game's audio is a good step up from previous games. I've taken the first step, now I just need to keep practicing.

What Went Wrong?

  • Rushing! A number of the assets could be much better, but after spending the better part of a weekend just making that spike ball I decided to try and make things "good enough" and move on. Frankly, it shows! Most of the art is pretty ho-hum, and it could've been much better if I'd taken the time to flesh it out and get critique. This was supposed to be a quick filler project, so I'm not too upset at the quality, but it's still a clear weakness.
  • Procrastination! I spent a long time finding tasks that didn't involve audio to do. This resulted in my last few weeks being almost nothing but music and sound, which got old pretty fast. It also means that the part that was my weakest skill also got the least amount of dev time! If my old drafts are any indication, the music would probably have been significantly better had I been iterating on it from early in the production.
  • The Core Design of Your Game, You Numbskull! So this is kind of a big one: Cloudy Climb's concept is pretty awful. The game works pretty well as an "infinite runner"-style game like the original, but the value doesn't translate well to a level-based platformer (in my opinion) for a few reasons:
    1. There's very little room for 'soft' failure states. For obvious reasons, messing up your movement always means death.
    2. Your character's only means of interaction with the world are 'bounce' and 'bounce, but moreso'. One could envision a game with one or two additional player actions, but the base concept pretty much requires one of the primary actions in the game to also be the basic movement.
    3. The game is extremely vertical, but the platform (PC) uses a horizontal aspect ratio. What's more, you move up and down pretty quickly thanks to gravity. A metroid-style camera would've helped, but I don't think it could've magically fixed the problem.

Takeaways

So the game didn't come out great, but at least most of my sins are pretty fixable, and a quick gander at this ancient postmortem indicates to me that I'm capable of learning from my mistakes. I put off much less of the content this time around, my code was perfectly fine, and I had a tight scope that grew organically to fit my needs and capabilities. Much like the game, the development process was distinctly not terrible! So here's my big lesson:

Iterate and get feedback on your big mechanic from the start, and make sure that your game is always presentable. That means taking the time to make assets (or at least decent placeholders), to ensure that you're always engaging with every aspect of the work and not leaving anything for the end.

There's More!

Even though this project is finished, I'm not moving on just yet. During the project, I found a few bits of technology that I want to explore, and this seems like a good opportunity to do just that. I'll be using this game as a testbed to get a feel for them, then I'll move on to something new. I also got an idea for a short tutorial/informative article based on some of my work on Cloudy Climb, so I'll be working on that as well.

9/10/17

Let's Make a Roguelike - Stats, Items and, Messages

Well, here's a new tutorial segment!

This one was pretty awful to write, mostly because I chose too broad of a topic. As a result, it's also quite a bit longer than most of the others (~14 pages, with a previous record of ~11). On the upside, I'm very nearly done here. Currently, I'm planning the remaining 3 segments to cover:
  1. AI and combat
  2. Generating artificial spaces (e.g. mazes, dungeons, buildings, etc.)
  3. Various remaining loose ends, like visibility and overall game structure
I didn't quite do item systems justice here, unfortunately. I seem to have missed dropping/equipping items in particular, although these actions should be fairly trivial to implement with the knowledge from the tutorial.
Since it looks like things are mostly on schedule despite last month's delays, I'm currently pegging the completion date as December 17th, 2017. This will be one full year after I restarted the series, minus a couple days to make it land on a Sunday.

Speaking of dates, this series will be 3 years old in a couple weeks. All I can say at this point is that I'm glad it's coming to a close. It's going to be a while before my next series, I have a few ideas but I want to put a lot of consideration into any future endeavors. However, I do have a one-off informative...thing that I'll be putting up on the tutorials page not too long after this series.

7/30/17

Roundup, July 2017 - Rock & Roll

Another month has passed, and it's time for a new update video:

Additional Comments

In addition to these demos, I also made a few nice additions to DFGame's API. Since it's mostly a grab-bag of small additions, here's a list:
  • Predefined colors to use for rendering
  • Simplified main loop function
  • mesh_render can also bind mesh attributes, which had to be defined separately before
  • hashmaps can now be told to delete their contents when being destroyed
  • sprites can be told to delete their source when being destroyed
  • Window init now enables transparency and depth testing by default
  • Function to create a new 2D camera based on a window's dimensions
  • Added transformation macros for cameras, to simplify transform_FOO(camera_get_transform(c)) syntax to camera_FOO(c)
  • Added a simple version of "copyadd" to data structures, which measures and copies non-pointer values
  • Attempting to lerp int literals now uses float lerp, since that's what most reasonable people would expect
  • Added an "almost zero" macro for testing floats and doubles
Writing them all out as a list looks pretty impressive, but most of these additions are quick 5-minute changes. Still, it's good to add them in before release.

Goals

  • Time Left: 3-4 weeks
  • Logo Demo: Done
  • Comets Demo: 90%
  • Heightmap Demo: 60%
  • ??? Demo: Not Started
  • ??? Demo 2: Not Started
I have 2 more demos planned, and a little more work for the existing demos. This puts me at about halfway through, with a month or so remaining. This means I'll have to stay focused if I want to get all of that done, and possibly a game jam as well.

Some Code

As I mentioned in the video above, the Logo Demo was designed to present a simplified example of the init, cleanup, and looping code. To demonstrate that, here's the code:
    1 #include "camera.h"
    2 #include "color.h"
    3 #include "interpolate.h"
    4 #include "mainloop.h"
    5 #include "mesh.h"
    6 #include "paths.h"
    7 #include "shader_init.h"
    8 #include "texture_loader.h"
    9 #include "vector.h"
   10 #include "window.h"
   11 
   12 GLFWwindow* win;
   13 camera c_main;
   14 shader s_default;
   15 float  timer;
   16 gltex  t_logo;
   17 vec4   color;
   18 
   19 bool loop(mainloop l, float dt) {
   20     timer += dt;
   21     color.a = lerp(0.0f, 1.0f, timer) + lerp(0.0f, -1.0f, timer - 2.5);
   22 
   23     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   24 
   25     glUseProgram(s_default.id);
   26     shader_bind_uniform_name(s_default, "u_transform", mat4_mul(camera_get_vp(c_main), mat4_scale(mat4_ident, 400)));
   27     shader_bind_uniform_name(s_default, "u_color", color);
   28     shader_bind_uniform_texture_name(s_default, "u_texture", t_logo, GL_TEXTURE0);
   29     mesh_render(s_default, mesh_quad(), GL_TRIANGLES, "i_pos", VT_POSITION, "i_uv", VT_TEXTURE);
   30 
   31     glfwSwapBuffers(win);
   32     return !glfwWindowShouldClose(win) && timer < 4.0f;
   33 }
   34 
   35 int main() {
   36     win = window_new_default(1280, 720, "Logo Demo");
   37     init_base_resource_path(NULL);
   38     shaders_init();
   39 
   40     s_default  = shader_basic_tex_get();
   41     color      = color_white;
   42     c_main     = window_create_2d_camera(win);
   43     char* path = assets_path("logo.png", NULL);
   44     t_logo     = load_texture_gl(path);
   45     sfree(path);
   46 
   47     mainloop_create_run(loop);
   48 
   49     glDeleteTextures(1, &t_logo.handle);
   50     camera_free(c_main);
   51     shaders_cleanup();
   52     resource_path_free();
   53     window_free_final(win);
   54 
   55     return 0;
   56 }
There are still a few things that could be simplified here, but this is still much less code than would be required without DFGame. That's the primary goal of the framework, so I'm pretty happy so far!
With DFGame's release now imminent, and the upcoming 5-year anniversary of this blog, next month ought to be pretty exciting. Until then, keep on reading!

7/2/17

Roundup, June 2017 - Lost in another dimension

It's that time of the month again, and I've made great progress!

Additional Comments

In addition to what was mentioned in the video, I also went back and re-added joystick emulation to the input code, as well as adding a few minor features and fixes all around. Besides the lackluster demo, this has been one of the best months for development this year.

Goals

  • Time left: about 1 1/2 months
  • Application Module: Done 
  • Audio Module: Done
  • Core Module: Done
  • Gameplay Module: Done
  • Graphics Module: Done
  • Math Module: Done
  • Resource Module: Done
As I said in the video, and as is apparent from the list above, I'm just about finished with dfgame for the time being. If you've been following along, you've probably noticed that I quietly removed particles from the goal list. This is because particles are a bit of an unknown for me.

"But wait!" I hear you cry. "Didn't DFEngine have particles?" This is correct, but DFEngine's implementation was super basic and not very good. My standards for features, while not amazing, are definitely higher than they used to be. If I were to make particles now, I'd want to use the GPU or at least some form of SIMD to get good performance. I played around with GPU particles last summer, but I ultimately ran into a few issues that halted progress and moved on to other things. A decent particle implementation will take some research and effort, but I don't know how much so I'm putting it off for the sake of polish.

Some Bad News

The main goal of my "End of August" timeline was to use dfgame for an actual game jam entry. Unfortunately, I just found out that for the first time in 15 years, Ludum Dare is being held at the end of July. Why? I have absolutely no idea. I could theoretically still enter, since dfgame's release is now feature-complete. However, I would be sacrificing about 50% of my polish time to do so.

Instead, I'll just keep an eye on the itch.io game jams page and select one in the right general time period.

As for next month's posts...beats me, I forgot to plan anything. Whoops!

6/4/17

Roundup, May 2017 - Oh man I sure do hope the mosquitoes don't--

It took great courage and perseverance, but I finally drove back the mosquitoes that definitely ate all of last week's update. So, here it is:


Additional Comments

I had some difficulty capturing/rendering this video. Last time, I used glc for the video capture. However, glc's audio recorder is really buggy and doesn't work on my machine. It's also quite old, so I'm worried about relying on it too much. This time I made a recording with FFmpeg, but it's just a normal desktop capture so it's a bit slower than I'd like. Next time, I'll probably try to capture the video and audio separately. It might be annoying to line the two up in editing, but it will probably result in a higher-quality video.

The big gain from rewriting the audio system is that multiple audio players should now be able to play audio from the same source at the same time. This means that I don't have to reload a sound effect every time I want to use it, and instead I can easily load once and play hundreds of times. As you can guess, this is really useful for games where you could easily have a couple dozen objects that can make the same sound.

Goals

  • Time left: about 2 1/2 months
  • Application Module: Done 
  • Audio Module: 95%
  • Core Module: Done
  • Editor Module: Not Started
  • Gameplay Module: 80%
    • Missing joystick emulation for input
  • Graphics Module: 65%
    • Missing sprites
    • Missing text
    • Missing framebuffers
    • Missing particles
  • Math Module: Done
  • Resource Module: 65%
    • Missing sprite loading
    • Missing font loading
This month went a little slower than I'd hoped, and some of the code that I wrote feels a bit rushed. As a result, I'm going to need to do another quick pass on the audio and resource modules at some point, just to tighten them up a bit. That's not what I'm doing for June, however. I want to get the last couple essential features, sprites and text, done. This will happen as part of a greater attempt to finish off the graphics module as a whole. I don't expect to finish all of it this month, but it wouldn't surprise me too much if I could sneak framebuffer support in.

If all goes to plan as it has so far, I'll have over a month left over to clean up any loose ends and improve my workflow in preparation.

Next month's posts will feature lots of Linux shell scripting. Much like in May, I'll be kicking off next week with some more organization work.

2/27/17

Let's Make a Roguelike - Generation

In a shocking turn of events, I actually finished this week's tutorial segment last night! This means that I was done a full day early, and I've opted to post the new segment this morning before I head to work.

The trick this time was starting early. I finished the followup last weekend, then did half of the work on Saturday and the rest on Sunday. I'm pretty sure that if I keep to this formula, then I should be able to keep up with the schedule without causing myself too much stress.

As for the contents, I'm continuing my policy of "give the reader some fun stuff early on" by sharing a couple simple random generation algorithms. In my old plan for the tutorial, this probably wouldn't have happened until part 6 or 7 (we're on part 4), so I think this was a good move. I'm also trying to get through things quickly because (as I said before) I'm still not 100% happy with this tutorial and would like to get it out of the way so that I can focus on making better-planned tutorials in the future.

Lastly, I wanted to say that I'm going to try and revive the old one post a week policy from my college days. I've actually been working pretty hard on a couple of interesting projects lately, so this will give me an opportunity to share them on non-tutorial weeks. I'm painfully aware of the fact that the tutorial is the only thing I've written about this year and it's nearly March.

Anyways, you can click here to read the latest part.

2/14/17

Let's Make a Roguelike: Fashionably Late

Had you worried, didn't I?

I almost had this weeks segment done last night, but I was quite tired and decided to put it off. That was apparently a good decision, since I put several hours into it today before wrapping up! This was mostly poor time management on my part, but I'm happy that the delay was small this time.

As for the content, I'm trying to accelerate the early bits a bit more than in my previous iterations. Even for a long-running series, it's important to keep the readers engaged, and the shape drawing from last time didn't seem up to snuff. I'm taking a bit of a write once, refactor later approach this time: I present the technique in a fairly simple and "bad" way first, then show how the implementation can be refined and improved later. This allows me to frontload much of the exciting stuff, while still providing some basic design lessons down the line. We'll see how that goes.

This series has really been a bit of an interesting experiment for me. There are so many things wrong with my approach to writing these that I almost want to start fresh again, but for the sake of actually getting things done I'm resisting that urge. I think this series is helping me learn a lot about tutorials though, and future seasons will benefit from those lessons.

Here's the link to the new part.

7/13/16

Listening to Your Javascript

It's been a while, but I haven't been idle. I've made a great amount of progress on Singularity, and I've finally discovered a secret that has eluded me for years!

The Problem

These stories always have to start with a problem. In this case, the problem was a seemingly simple one:

"When the user interacts with Singularity's WebKitGTK viewport, how do I make the app react to it?"

This used to be really easy, back in the days of Webkit 1. However, when Webkit2 changed the API and moved some functionality to it's web extension API, Javascript support followed suit. For a long time, the only solution was to simply create an 'extension' that used some form of IPC to communicate with the main app, but I didn't want to do that in Singularity. Instead, I used a crude hack by encoding messages in URL requests.

A Tentative Solution

Needless to say, this solution is slow and terrible, but I've found a better answer. At some point since my work on Singularity started, WebKit introduced the UserContentManager, which can apply custom CSS and Javascript to web pages. This is a really awesome feature that I plan on using in future projects, but it also provides me with a new tool, a signal for handling certain Javascript calls. According to valadoc.org:

public signal void script_message_received (JavascriptResult js_result)
"This signal is emitted when JavaScript in a web view calls
window.webkit.messageHandlers.name.postMessage()
after registering name using register_script_message_handler"

In other words, it lets you set up a callback that is called whenever a script calls a certain function, sending arbitrary data from the web page to the application displaying it. This is a really powerful feature.

So powerful, in fact, that you can't actually use it in Vala.

What Gives?

As it turns out, Vala's JavascriptResult class has no useful functions or properties. I'm not entirely certain why, given the solution that I'm about to provide.

While WebKitGTK's JavascriptResult does contain the necessary functions, no Vala bindings exist for them. Instead, you need to access them from C and return whatever result your app is expecting. This seems really odd, and I hope I don't have to find out the hard way later on, but for now I'm feeling pretty good about this.

I'll be posting an update with a more thorough rundown of my work on Singularity soon.

5/6/16

Console Programming 8-Week Project: Bringing DFGame to the web

This semester, I posted twice about console programming projects. They were small, carefully scoped projects to help me get a jump-start with DFGame. Now, it's time for the big reveal: My final, 2-month long console programming project. That's right, I

PORTED DFGAME TO HTML5/WEBGL

Yes, all of it.

One of the requirements for Console Programming is to develop at least one project on an unfamiliar platform or with unfamiliar tools. I decided to try my hand at developing a web-based game framework that was fully compatible with DFGame. The project was a big success, and I actually have a playable demo up on my site (based on the demo I produced for my previous project).
You can check it out here. (Note: you need a relatively recent version of a modern web browser such as Firefox or Chrome. IE won't work and Edge/Opera/Safari are untested)

Now, let's talk a little bit more in-depth about the project.

History

When I originally kicked off this project in class, I had much bigger aspirations. My original goal was to port both DFGame and Halberd, then add an HTML5 export option to Halberd's editor. Obviously, that was way out of scope. After a week or two, I decided to scope down to only porting DFGame, and that's what I stuck with throughout the project.

I wasn't sure how much of a demo I'd be able to put together, but I managed to complete the DFGame port quicker than expected and ended up with a couple of spare weeks to work on the demo.

The current demo doesn't have a title screen or audio in it, but is otherwise mostly indistinguishable from the desktop version.



Now, let's talk about the platform in general, and weigh the pros and cons of the platform.

Pros

  • This setup is mostly cross-platform. I don't have to worry as much about weird edge-cases, at least compared to developing native Windows and Linux versions of my games.
  • The games that I make with this are more accessible in general. There's no need to download an executable, just load up a link and play.
  • Since the library draws to an HTML5 canvas, I can pretty much embed DFGame into any web page that I want.
Those are some pretty good benefits. However, there are some pretty big problems with it as well.

Cons

  •  Unlike the C version of DFGame, all IO is required to be asynchronous. This isn't necessarily a bad thing, but it means that I need extra boilerplate to ensure that the right assets are loaded before the game begins.
  • The actual performance of DFGame is way lower when using the HTML5 version. This is to be expected, of course--You can't really compare raw C to embedded JS, especially not in web browsers with single-threaded renderers, such as Firefox's current release. Chrome currently gets way higher FPS than Firefox, but it still lags if enough is happening on-screen.
  • The WebGL API is currently outdated, compared to the current OpenGL and OpenGL ES standards. As far as I can tell, WebGL is still locked to the GLSL 1.0 standard, compared to 4.3 in GL and 3.2 in GLES. As a result, I need to rewrite all of my shaders whenever I move something over.

Further Work

Despite the problems with developing HTML5-based games, I'm actually pretty happy with the result. I think it's worth my time to continue developing and using the DFGame port, especially for game jams and other simple prototypes.

The port is currently behind, because I've been recently developing DFGame some more. I'll be posting about some of the new improvements soon. I also want to look into optimizing for WebGL better. I can't get a comparable framerate to the C version, but maybe I can eke out some more performance out of the web.

Lastly, I want to polish the demo some more. If I get it into a good enough shape, I think it could make a good portfolio piece. For now, I'm going to let it sit in a dark and dusty corner of my site until it's properly finished. Hopefully I'll have more time to do that now that my finals are over.