Trial 20XX Notes


-ยง-

I made a game called Trial 20XX, using Three.js and no engine, just rawdogging JavaScript. I challenged myself to write a reasonably complex game with some rudimentary physics that could execute the game logic and rendering in less than a frame, that would be about 4 milliseconds on a modern 240 Hz display. It works well on modern hardware, and not too terrible on my 2018 MacBook Pro.

I had some amateur game dev experience as a kid, making Flash games in ActionScript. I didn't really set out to make a game, I was making interactive art, and thought a game might be the next step. I was also more interested in the art and message, rather than the gameplay, so I copied some mechanics from a game that I really liked as a kid, Infantry Online.

Fiziks

The physics is really basic, and also horribly broken if running at a low frame rate, because it does not do any sort of continuous collision detection at all. Even frame skips will cause things to no-clip through barriers. There is also no spatial indexing, so everything must run against everything else in the scene, which makes it O(n^2) time complexity. Modern physics engines will use bounding volume hierarchies (BVH) to group objects into virtual buckets so that only local objects can interact with each other. To avoid introducing the complication of BVH, I intentionally kept the scene as simple as possible.

The physics only operates in 2D space, which was an intended game design decision anyways. There are only simple shapes: rectangles and circles, to make the math easier. Detecting rectangle-rectangle and circle-circle collisions is really simple, the only somewhat complicated case to consider is rectangle-circle collision. For this, I basically followed the steps listed in this article. The trick is that it reduces the rectangle to 4 edges, and only compares the circle to the closest edge. Another thing I avoided was having to compute reciprocal force. If there is a collision, I simply clamp the position of one of the objects, and/or return a boolean value if it collided or not.

The only redeeming value of the physics is that it is reasonably fast and simple, given the constraints of the scene. For anything more complex, use a physics engine.

ArtiFicIaL iNteLligEnce

The bots most closely resemble an expert system, hard-coded in advance by a human that knows what it should do. The bots will maneuver forward or backward to maintain an optimal distance, and randomly strafe left or right.

The most complicated part is targeting, each bot will target the closest enemy to them. The velocity vector of the enemy will intersect with the velocity vector of a projectile at a certain point, which can be precisely calculated. To improve aim, the bots will randomly reduce the lead time compensation, given that the enemy can move erratically.

What would have been more interesting is if there were coordinated group behavior, each bot could try to maximize their distance from friendly bots to flank the enemy and avoid getting hit by splash damage.

Art direction

Mario 64 with guns. I knew up front that I wasn't going to make something with very polished graphics, so it has to look good with low poly counts and rough or no textures. I was going to focus on lighting, so there's three main light sources: the point light above the player, hemisphere light, and special lighting effects (muzzle flashes and explosions). The main point light also shows which enemies are in line of sight, and there's also a circular object ("vae victis") which casts an ominous shadow on enemies.

I was initially hesitant to add post-processing, but it became necessary because the view from the player to the camera can be obstructed by the scene, so I had to add an outline effect at least. Low poly stuff looks better with dithering in my opinion, or at least sets the tone of stylized retro, there's also a lookup table (LUT) pass to set the tone using color.

Entity (component?) system

I'm not sure if what I made would be considered an Entity Component System; if it does, then it is very rudimentary. In each tick, it will iterate over all entities and run some update logic. I did some clever monkey-patching so that when a game entity is added, it will also add the object to the scene, and same thing for removal, so that I don't have to manually manage the scene.

Each entity is given a singular type, and then different methods are run on update depending on the type. I did not even bother giving IDs to entities, since directly referencing entities worked in my case. I actually do not know what I'm missing out on from more complicated systems, I just wanted to make the simplest thing that worked.

Performance optimization

This was always something that plagued this game from the start, because I didn't write the initial code with optimizations in mind. There were quite a few offenders:

Game design

Breaking the fourth wall here. In the real world, we are subject to arbitrary rules and unjust laws which are always selectively enforced. Most of us know people who have violated traffic rules, consumed illicit substances, avoided or evaded paying taxes, some of us did it ourselves. The exact nature of the charge doesn't matter, as in Franz Kafka's The Trial. Rather than paying fines or serving time, wouldn't it be more fair to fight for your right? After all, the laws can only be enforced by men with guns, if these men could be defeated then the law is truly null && void.


The combat mechanics are very similar to Infantry, with some elements from Call of Duty zombie mode, like buying weapons and health from vending machines. It plays somewhat like a first-person shooter, but from a top-down perspective. Most Infantry players used keyboard only, while I used a mouse for more precise aiming. It's possible to play Trial 20XX with keyboard only, though it's disadvantageous to not be to aim quickly and precisely.

One of the quirky gameplay mechanics is the energy system, damage reduction scales based on energy percentage. Taking multiple hits in a row rapidly drops energy, so its better to take hits that are spaced out so that energy has time to recover. Another thing I added is a dash movement, which grants i-frames during which all projectiles will pass through the player. Adding another movement ability which allows the player to dodge incoming fire raises the skill ceiling.