Skip to main content

Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines

Strupf's jam log

A topic by Strupf created Jul 29, 2022 Views: 439 Replies: 8
Viewing posts 1 to 7
(1 edit)

This is going to be my small jam diary. My game is going to be written in C, and hopefully I can manage to compile it to WebAssembly. The game is going to be available open source afterwards. I'll start off with a custom code base  which is incomplete and very much hacked together for my personal use and is going to change a lot, but I just want to be transparent with that:

  • Built on top of Raylib
  • Indexed palette-based pixel art renderer (lines, rectangles, circles, affine sprites - rotation, scaling, shearing)
  • Generic dynamic array
  • A-star pathfinder
  •  .json parser.
  • Some basic utility

For now I'm playing around with the resolution in Aseprite to get a feel for our beloved tiny 64 square. Didn't get to work with such a small amount of screen space, yet. My tools of choice:

  • VS Code - Language: C
  • Aseprite (Art)
  • Bfxr (Sound)
  • Bosca Ceoil (Music)

So, yeah, two more days to go until we're off to the races! 

(4 edits)

13 days left

Today I spent some time thinking about the themes. I'll go for a game involving trains or steamy locomotives. Never made a train game before although I'm a pretty big train lover. You Are The Last Hope & Kill It With Fire are looking like nice secondary themes for my game but I'll need to figure out what kind of gameplay I want: Action? Strategy? Management? Survival? ... I'll figure that out soon. So I went ahead and got working.

There are four core elements in my implementation:

  • Rail nodes form the connections between rails. They have a position (x, y) and a list of rails connecting to them.
  • Rails are the edges between nodes. They have a "from node", a "to node", a length and an identifier for the type: straight or curved rail (quadrant 1-4).
  • Waggons are standing on one rail and have a position on that rail: the distance from the "from-node" on that rail. They also have a facing value (1, -1) which will used for the graphical representation (facing one of the two nodes).  Of course real waggons have two axis connected by the waggon on top of them but I figured to simplify that to a single point instead.
  • Trains are a collection of waggons belonging together.

When I want to move a train I always pull the train. For that I'll figure out the first waggon which needs to move. Whenever a moved waggon crossed either the "from" or the "to" node its rail and position have to get updated. Only the first pulling waggon decides the route to go, all other waggons behind are following the one in front of them. Important was to only look at valid rails when choosing the next rail of the leading waggon at a node: Basically only tangent rails which are on the opposite side of the node must be considered. It got a little bit tricky figuring out how to go about the movement direction because of different facings and orientations of rails e.g. two "from" nodes of rails ending at the same node which resulted  in sign switching. There also could be two instead of one rails between two waggons, depending on minimum rail and maximum waggon length so I also couldn't simply just use the previous waggon's rail.

With some hardcoding of rail placements I already got a train with some waggons moving in both directions with direction switching without going crazy. Pretty good for a day 1 imo.

Next up:

  • Train sprites with proper rotation
  • Basic tile sprites
  • Calculate waggon position on curves (instead of lerping diagonals)
  • Camera movement


(4 edits)

11 days left


Since last post I did a lot of mockups and worked more on the graphical side and integration of Tiled. After going for the top left style of the right image collection (didn't do the other five smaller ones yet) I realised that the sprites were too big for that kind of gameplay. I had to redo the sprites and scale everything from 8x8 to 6x6 which gained 33% more tiles in each X and Y, or 77% more viewed "playable area" on screen. The rails look better now, though, because I stumbled upon perspective rails while doing the rework and pixeling around. I also decided to place a black outline on the gameplay elements to help with readability as I struggled to maintain waggon contrast on both the background and rails. However, the outline gets drawn at runtime. For that I coded a quick outline method to in my engine which works using an additional temporary buffer image. That way the outline is automated and only placed around the outside of combined elements instead of outlining every individual waggon of a train separately - if I had outlined every waggon beforehand then the combined train would have black lines inbetween all waggons which I didn't want.

for every transparent pixel (x, y) of source image
    if at least one of its direct four neighbours is an opaque pixel
        put outline color on buffer image (x, y)
draw buffer image on top of the input image.

To avoid overlapping of sprites I had to implement Y-sorted sprite drawing. That really wasn't that much of a hassle as it only involved an additional queuing of sprite calls and sorting those for Y when flushing. For sorting I used the C builtin qsort().

Furthermore I worked on loading my maps from Tiled. For that I already had my .json parser on hand but I still needed to convert the placed graphical rails into my internal rail network using nodes and edges. That involved lots of hardcoding and also another smaller graphical rework because I accidentally found out that I made "edge" tiles at first: tiles connecting the midpoint of the tile edges instead of the tiles' center points which meant that my curves had to shrink from an effective 1.5 tile radius to a 1 tile radius. The difference is subtle but can really throw off things. On a sidenote: While the graphical tiles might be 6x6 I actually draw all my rails except the pure straights using 12x12 tiles offset by a half 6x6 tile, so to speak.


End of the day:

  • Edit and load Tiled maps
  • Drive a train bug free on the created rail network
  • Waggons are tracing the curves correctly
  • Sprites for waggons depending on rail: front, back, side, 45 degree back, 45 degree front, + all mirrored at runtime in X
  • Automated outlining of sprites
  • Camera functionality
  • Y-sorting of sprites to prevent overlapping

Next up:

  • User controlled rail choosing at switches
  • Locomotive sprites & steam particle effects
  • Docking of waggons
  • Camera which looks ahead instead of being centered on a waggon

9 days left

The last development work went into the train movement. I implemented user controlled choosing of rails at switches regardless of going forwards or backwards. The way I implemented it was pretty straightforward: I look ahead the pulling waggon (remember, always pull the train) as long as I don't encounter a node which I has more than 1 valid rail for the current train movement direction. That node will be the next upcoming switch unless the train changes its direction.

Trains can now collide correctly with other trains, and depending on docking mode waggons can be docked with each other on collisions. Also slightly modified my train movement algorithm: I work with discrete integer positions of waggons on rails so I don't have some weird decimal stuff going on, but now I move every pixel position one pixel at a time instead doing the movement in one go. The basic idea can be found in the blog post from Maddy Thorson (Celeste and Towerfall Physics). I determine how many "pixels" a train needs to move. After that, move every single pixel of the total movement one by one until an obstacle (other waggon) is collided with. If we can dock it: Continue movement together with the newly added waggon, otherwise: Reset the pulling waggon back to the previous position and stop moving. I can probably throw in some physics inertia stuff but I need to focus my time on other areas.

The locomotive received particle effects for smoke. Interestingly I don't draw opaque smoke but I also don't alpha blend it. I go through a palette of colors to replace the current screen color at the smoke pixel with so all colors covered by smoke are replaced by corresponding darker ones. The smoke particles also have a z position and cast a small shadow on the ground which works the same way.

Next up:

  • Gameplay: Decided for train siege combat / action gameplay. Will get interesting.
  • Computer controlled trains (dumb random movement)
  • Train docking/undocking via GUI?
  • Camera looking ahead

Would this approach to movement mean that your speed is capped by the framerate? Since you're moving one pixel at a time. It's a very interesting way to do collision detection though! Solves the overshooting issue.

The total movement is still the same. Instead of moving 5 pixels per frame in one go I split those 5 pixels up into 5 separate 1 pixel movements. That simplifies some things: Instead of moving 5 pixels and seeing "oh, now I'm overlapping something, where do I go now" I can just go "can you go to the next pixel? Yes? Move." Pixel is actually the wrong word here because a screen pixel is made up multiple subpixels internally but the idea is the same. You should really take a look at the linked blog post because the idea is very interesting but only works for integer based positions I guess so it lends itself to pixel art.

7 days left

Started working on gameplay and polishing things up. Time is getting short because I'll upload on Friday so only a few days left. Train siege combat will be some kind of "stretch goal" depending on how things work out. But most importantly: The camera is finally done. It centers on the leading waggon and then offsets to look ahead along the tangent of the waggon's rail. Pretty basic but I think it's working great that way.

I implemented a basic quest system where you pick up waggons which need to be delivered to specific locations.  For that I still need to make dedicated waggon sprites and some kind of hint where that waggon needs to go to. Some NPC waggons are roaming around which don't have any functionality, yet (besides blocking your way), so I'll need to figure out what to do with those if I can't make a combat system.

Changed the rails' colors to reduce their contrast. Some more tiles got pixeled which can overlap the waggons so those had to be Y-sorted, too. But because my train outlining worked on that specific texture where those sorted tiles were rendered to I had to resort to a bit more basic mechanism so all the big tile props don't get outlined, too. On the start of the game (loading textures) I copy the texture data of all the waggons and outline those instead. When drawing the Y-sorted elements I draw those black waggon silhouettes before the actual train sprites (so a lower Y coordinate), but after the corresponding tiles so the outline doesn't disappear behind these tiles. For that I needed to play around with some offset magic constants but got it working just the way I wanted to.

The game already has a few sound effects to go with specific events. However, the trains should play that kind of "locomotive puffing sound" (you know what I mean) but it's really hard to find or make something which isn't too aggressive and also doesn't get annyoing after a while. That still needs some work. I already spent a bit of time playing around with BFXR and moved the parameter sliders around when I got a sound I liked and reset them when it started to sound worse. I 100% don't know what any of the parameters do so that's a magic black box to me.

Next up:

  • Background music (hopefully self made if I have time left)
  • Quest system fleshed out (sprites, visual cues, ...)
  • Stretch goal: train combat
  • More and better sound effects
  • Making the big final map
  • Particle effects for different things

For the sound, the best way I can think to make it not annoying is to make it higher pitch when going fast, and lower pitch/slower when moving slow.  It looks like you will be changing direction /stopping a lot, so that should 1) give a audio clue about speed, and 2) keep it varied enough to make it more palatable.   

Love the art BTW!  Super interested to see how it plays

5 days left (2 for me)

I'll release on Friday so I don't have much time left. I implemented a basic main menu and a simple particle system for visual feedback on various actions and events - colored pixels with position, velocity and acceleration values in x, y, z spawned with some kind of randomness baked in depending on the action. Simple, looks great and gets the job done. 

Needed to add some necessary functionality to my train system which looked almost trivial but still took some time nonetheless: Coupling and decoupling waggons i.e. merging and splitting trains at the correct positions without blowing anything up. Individual waggons already worked fine but it didn't work for collections of waggons. For that I also had to implement a way for the players to choose where to decouple waggons from their trains: I went for a semi-transparent indicator "plane" sprite which gets Y-sorted and inserted between waggons to hopefully make clear what's going to happen (3 different plane sprites depending on waggon angle). That in turn involved getting the mouse working, translating real screen coordinates to world coordinates and finding out which waggon is hovered by the cursor. And while I was at it, I coded a button which slides out of the view but comes back in if you touch it with the mouse. Don't know if I really need additional buttons yet, but I'm sure I can find some uses for that. Originally I wanted my game to be playable only using a keyboard or gamepad but the learning curve of a jam game needs to be low, and using the mouse is really intuitive. Could probably think of something usable "keyboard only" but that's pretty low on my priority list now.


Added some sound effects and reworked the locomotive sound effect. I decided to just take a tiny sample out of a recording of a real locomotive and edit that slightly in Audacity. Seems much more fitting than trying to generate something suitable in BFXR. Started working on the background music but I think that I need to push that to the very final hours and decide from there if I want to make something myself or look for royalty free music. I could really use that time for some code or features instead, but also I take pride in doing everything myself...

Fortunately I got hit with a 0xc000007b error before releasing when I tried to launch my game's .exe on my secondary computer (laptop). Clearly there was some weird stuff going on, and I'm glad that the Raylib discord helped me out with that: A threading library was missing - I think the audio module of Raylib somehow depends on it. Anyway, adding that .dll to the game's folder or just adding  "-static -static-libgcc" to the GCC build flags resolved that problem so I don't have to worry about releasing faulty builds anymore. Always lovely to see those errors pop up before the deadline.

What's left now? Heads down and finishing the game including tying all the loose ends: Fleshing out the actual gameplay, adding music, new map, minimap, polishing. Lots of work to do in those two days.

Next up:

  • Release