Last week's progress is listed here https://www.josephcatrambone.com/?p=1110.

There are three things I really want to get done tonight.  (1) Characters, when they die, should go flying backwards after the last strike.  (2) I need to recursively swap out the crash-dummy yellow body parts with something that's more in line with the game's theme.  (3) I need to actually write something that ends the level.  I did make a phone booth.  (Whee!)

Let's review those P1's from the start:

  • Main menu with start/settings/quit.

Bam.  Done.  Sorta.  Quit and start work.  Definitely need to add the settings page and maybe put something other than "Title" in for the title.

  • The 'fighter body' should broadcast the "damage taken" event to child nodes so they can record whether or not they're dead.  Dead enemies should be despawned.

Also done!  Enemies indeed die and take damage.  That's handled by the controller.  I need to figure out what approach I want to use for the "die but then fly backwards on hit if enough damage is done."

  • End-of-level triggers need to fire.

The triggers fire when the player enters, but that's it.

I even had time for a little AI:

Last week's progress is listed here https://www.josephcatrambone.com/?p=1109.

I spent a while running through the game in its current state and think that it's fitting to add at least a few of the following features.

Priority One:

  • Main menu with start/settings/quit.
  • The 'fighter body' should broadcast the "damage taken" event to child nodes so they can record whether or not they're dead.  Dead enemies should be despawned.
  • End-of-level triggers need to fire.

Good to haves:

  • Motion feels very limited right now.  Jumping would go a good ways to helping that, as would having different attacked based on whether or not one was jumping or moving.
  • Perfect-time parrying.  Right now a user can cancel into a block at certain times, but there's no reward for having anything above 'okay' timing.
  • Rebindable keys would be very nice.

I know I should work on having a level completion and a main menu before adding any of the extras, and I expect I'll do as much, but I want to keep track of these items to I can refer back here and see how much I set out to accomplish and how much I actually accomplished.

It's the end of the first weekend. I managed to finish a few important pieces and numerous unimportant pieces. Here are some hiccups and what I did to work around them.

I started by making a walk cycle to get into the swing of things. Moving around is an important first step, pun intended.

Immediately after this I encountered a problem where moving left would cause the character to spaz out and flip left and right once per frame for as long as the left button was held. It turns out that Godot does not like having a KinematicBody flipped horizontally (scale.x = -1) and will override that. It was not, as I suspected, an issue with the animation resetting scale. The workaround here was to move the skeleton beneath a Node2D and flip that. It means the hitboxes won't be flipped correctly, but I'll have to keep them centered and balanced.

The next thing I worked on was analog stick input and variable speed walk cycles. I wanted to cool off a bit from that bug, so I spent a short while setting up key and controller inputs, then tweaking the scaling between the walk rate and the animation rate. I have a move speed and an animation multiplier speed. The move speed can vary by character, but the animation multiplier stays fixed. If a character moves 20 units per second, the animation plays at (20*animation_multiplier)x normal speed. I just have to play with the multiplier until the feet look right. One problem that comes from this is scaling. If the models change scale (or I change animation) I'll need to readjust the animation_multiplier. Could prove tricky.

The last issue I ran into was broadcasting events. I had originally set up the Fighter scene as a child of another Node2D, so a player node, for example, could have a script which handled input and called into Fighter to strike or block or move. This worked well enough, but when it came to detecting and broadcasting hit events, I found that selecting the overlapping areas with an area 2D (at the hit point) yielded the root nodes (i.e. player, npc, etc) and NOT the fighter nodes which could handle 'hit'. I struggled in part because Godot does not allow one to attach multiple scripts to the same node. I didn't want to put more logic into the fighting controller, but it didn't seem graceful to select all the nodes of a given type and then seek the first child with name "Fighter". The solution turned out to be flipping around the hierarchy. Instead of having PlayerNode -> FighterBody, I had FighterBody -> PlayerController. All on-screen fighters have this same root type which means I can simply have the animation call 'strike' and the event will propagate.


func strike():
for b in strike_area.get_overlapping_bodies():
if b.is_in_group(target_group) and b.has_method("hit"):
b.hit(self, strike_area, damage)

func hit(striker, damage_area, damage):
# TODO: Face correct direction.
hit_recovery_time_remaining = hit_recovery_time
animation_player.play("Hit_Front")
# TODO: Report the hit to child nodes.

All that comes together to make a nice striking system.

It's happening!

This year's theme: Sequels!

After spitballing in Discord for a while, I decided to make a sequel/spinoff to Enter the Matrix. The Matrix really needed sequels, right?

Gameplay: A side-scrolling 2D platformer/beat 'em up.

Minimum Viable Product:

  1. 2D character controls with jumping and collision (weekend 1)
  2. Punching and kicking with some visual indicator (weekend 1)
  3. Enemies that respond to punches (weekend 1)
  4. A start and end state with menus, spawning, death, and completion (weekend 2)
  5. An environment the player can traverse from start to end (weekend 2)

Stretch goals:

  1. JUICE for enemy impacts.
  2. Animated player moves.
  3. A combo system.
  4. Backstory and dialog systems.

Let's roll.

Godot is a reasonably light-weight, highly featured, open-source game engine that's been in development for more than a decade. I started playing with it not long after it was mentioned by TheHappieCat on YouTube and found myself enjoying it in most every respect. The editor was, as the tagline implied, small and lightweight while being very feature complete. Version 3.0 brought with it high-fidelity visuals (a la PBR), a new physics system, and a slew of other additional enchancements. Fun on most every front.

The one area where I found Godot most lacking was GDScript. Coming from languages like Python and Kotlin and Java and C, I found it to embody most of the pieces I didn't like about Python with none of the things I did like. It was just similar enough to cause problems and be unintuitive. As good as the documentation is, the language is fundamentally too limited for the kinds of things I like to do in games. (Has anyone written a matrix library for GDScript?)

Enter GDNative: what appears to be the ability to simply build a DLL and then call the methods from GDScript! We're saved!

Almost.

GDNative doesn't provide a way to call arbitrary methods in a DLL or .so and, from what I can gather in the IRC and Discord channels, there's no intent to include Dylib or another system for gathering those. Instead, there are key methods which are exported and need to be defined so that the DLL can register with Godot. The problem then is nontrivial, but it's not impossible.

What We Want:

There are plenty of paths that let us go from Kotlin to Godot and it's worth describing the desired output before we enumerate them.

We could want nothing more than to produce some exported methods that take standard data types and return standard data types. This was my desired use case. I had two methods I wanted for my game, Terminus. Those were compile(string code, byte[] memory) that accepted a string and compiled it into instructions for the virtual CPU. I didn't need to define any special node type or interface with other Godot types. I just wanted to put two methods into a DLL and call them.

Another option is interop with the other Godot node types. This means having the ability to subclass Godot's Node and interact with Variants. This provides, arguably, the most versatile of solutions at a small amount of added complexity.

Options:

The most obvious path forward is to have Kotlin Native produce a DLL that we can import as a native library. When I last tried this I ran into an issue where it's not possible to define the name of a method in the DLL with Kotlin Native**. I considered then building a static library and having a small C wrapper that defined the required nativescript_init method. This, too, turned out to not be an option. Kotlin Native can produce a KLIB library file, but not a lib. Finally, I turned to building bytecode with LLVM and then using that in place of a static library. The process for setting up LLVM in Windows is straightforward, but between fighting with MSVC, not being able to find link.exe, and a handful of other technical issues, I thought this would be too cumbersome a path for the average user.

The next path, then, is to use the cinterop tool along with the Godot header files to produce a klib file that can be used by Kotlin native. With a little fandangling, this can be wrapped up and shipped nicely as just the KLib and a build.gradle file, allowing for a quick and painless way of producing nodes with Kotlin. This is the approach I'm trying to implement right now, and it remains to be seen how trivial the process is.

** Note: As of Kotlin/Native 0.6.0, exporting function names with @CName is possible. One must simply figure out how to import kotlinx.cinterop. I'm currently working on that.

UPDATE: 2018/03/26: This issue is now tracked on YouTrack at https://youtrack.jetbrains.com/issue/KT-23455