Preparing the Future


For this log, the plan was to cover the torpedo launching sequence at last. As it happens, life got in the way of game development. My 12 year old cat fell ill and a big part of my energy shifted to taking care of her. Seeing that creature so miserable, all thoughts of Atlantic ‘41 vanished, and the game seemed to matter very little.

But I haven’t been idle. Creative work was difficult, so I took the opportunity to better structure the code. And before going further, I should stress again that these devlogs don’t mean to teach anyone about game design or programming. The goal is to document the journey, including mistakes and learnings. On many aspects, code in particular, I still fumble in the dark. I may read back on this in a year and realize how wrong it is.

So if you’re new to the Playdate or game development, please take anything I say with caution. And if you’re a seasoned veteran, maybe have a good laugh.

The simplest program you can make in Assembly language for the Commodore 64

I come from a time when Assembly allowed  to directly access memory addresses. The language was simple, contrary to its reputation. And since we used to program everything with a set of limited instructions,  not much could happen without the programmer knowing about it.

But modern programming is an altogether different beast. Memory is now this fuzzy entity, which fills up and clears out at the discretion of functions and operating systems one has little control over. The multiple layers above cpu level, from Lua functions to SDK libraries, make difficult to know what code runs at any moment. I only wrote a fraction of the code necessary to run Atlantic’41. And I’m sure that I would struggle  to understand it all.

My first game, programmed in 68k Assembly language for the Atari 520ST

It’s not all bad. Not having to program everything from scratch speeds up development. But it comes at the expense of control, and consequently performance and stability. Casey Muratori has excellent videos attempting to explain why, since the late 80’s, we saw spectacular hardware improvement, while software saw a significant decline in performance and stability. His opinions on clean code and object oriented programming didn’t make him many friends, which tends to happen when challenging common assumptions.

Casey Muratori 
"Clean" Code, Horrible Performance

The point is not to rant about the vicissitudes of modern software development, but explain what led me to operate structural changes; lacking modern programming knowledge, I became worried about memory management. In particular, the risk of memory leaks when scaling up the program.

The concern came from setting up a soft reset; for testing purposes, press down on the pad to generate a new engagement scenario, roll for weather at a random date and time, and spawn a fresh convoy. It’s an interesting challenge, as the game must refresh the data and make runtime preparations, for instance pre caching waves and filling clouds arrays. It should happen quickly and avoid conflicts and memory leaks.

Memory fluctuations during soft resets before code restructuration

It seemed to work fine in the simulator, and even on the Playdate itself. But the device window real time graphs revealed temporary spikes in memory usage, coinciding with large image tables being flushed out and re rendered. I had structured the code based on wrong assumptions about Lua memory management, and a poor understanding of the garbage collector. 

The memory graph and mapping window showed me what Lua does at any given moment. It was different from what years of programming Motorola 68k Assembly language on Atari ST, and the C64 before that had taught me, mainly to think of memory as static and predictable.


Atari 520ST memory map 

For instance you knew that the Atari 520 ST addressable RAM started at, if I remember well, #$080000. From there, roughly 512 kb to fill up as you will. Need two frame buffers? Pick two arbitrary chunks of 32 kb, and point alternatively the Video Base Address register to either section. All registers pointed to fixed places in memory, and nothing would change without a specific instruction.

This simplicity deceived me on Playdate. For instance, to replace the content of an image table, I would send a nil value to it, and then create it anew. But until the next garbage collector cycle, both new and old content exist simultaneously in memory, even though, from the program’s perspective, only the new content can be accessed.


Memory fluctuations during soft resets after code restructuration

Thankfully, I programmed Atlantic ‘41 the old way from the ground up. I don’t use any classes, and very few dynamic arrays. Most of the data is declared once at startup. Fixed size image tables are stored in memory at launch and their content changed using load() functions. I tried to program like my old Assembly language projects, avoiding dynamic data and only calling the basic library functions.

Casey Muratori
The Thirty-Million Line Problem

It still took a few days to fix memory usage spikes. At this time, I can soft reset the game in about two to three seconds, while keeping the memory in control. At any given time, the garbage collector never takes more than 5% cpu. I observe memory usage fluctuations up to 1 Mb in rare cases of heavy activity, to settle back down within a few seconds. This can be improved. At least, I should now have enough understanding to scale the game without ever stepping outside these boundaries. 

I’m glad I did this before the game becomes more complex. It’s no fun job, but I would hate even more dealing with crashes and memory leaks later. It’s hard to predict the solidity of the system long term, but it should compartmentalize data and keep things under control.


Bad Weather Is Good

Aside from that, I figured now would be a good time to implement two features I had postponed until now: rain and seasons.

In the past We’ve covered the vital importance of weather in naval operations, in particular for U-boats, which rely on stealth and the element of surprise. This is why Atlantic ‘41 simulates daylight cycles and weather, and how they relate to navigation, visibility, and combat effectiveness.

But the system lacked rain. This was by design, as I was concerned with the impact of many small sprites on frame rate. Instead, I focused on storms, with high waves activity, lighting strikes and sound. Rain was implied.


Dry storm

But real weather didn’t always impact sea conditions. It decreased visibility. Overcast skies prevented sun or star fixes, which made navigation more challenging. Without visibility on the sky, navigators fell back to less precise means of calculating their position, like dead reckoning. It resulted in delays and increased fuel consumption. That uncertainty also forced U-boats to more frequent radio communications, with the risk of giving away their position to allied shore based stations equipped with high frequency direction finding. 

To sum up: mere overcast weather could lead to a series of cascading events and important decisions, all of which make for interesting gameplay.

U-boat Attack Logs made me appreciate the importance of weather

U-boat logs tell how bad the weather was in the Atlantic, in particular during winter in the North. But bad conditions didn’t always mean storms and raging seas. More often, the world was under a low, monotonous gray ceiling. It could rain for days. During the winter months, north of the Bay of Biscay, you could sail for an entire week without ever seeing a break in the clouds.

It made me realize that I was missing the essential component of overcast conditions in calm seas. And consequently, with so much overcast, it seemed odd to never see rainfall. I think part of me initially didn’t want to inflict the most boring conditions to the player, when I had worked hard to make interesting looking skies.


Perfect weather

But then I thought about a core concept of Atlantic ‘41: contrast. To sum it up (you can read all about in detail in an early log), it says that life aboard a U-boat was long stretches of uneventful mundane activity punctuated by short bursts of extreme tension and fear. Contrast is at the core of effective entertainment. It creates interest, whether you write a story or a piece of music. Now apply that concept to the game’s visuals: days of bland, quiet overcast weather help making the sunny days seem beautiful, and the storms scarier and louder.


Quiet life aboard

Instead of avoiding flat looking skies, it’s best to embrace them, and have them serve the purpose of contrast. Not only that, but days of uncertainty about the boat’s position in bad weather create a sense of relief when finally able to take a navigation fix.


Overcast through the periscope

From a development standpoint, overcast came for free, since it’s based on the storm system, from which I removed the lightning. Rain, however, was a different story, as you’re about to see.


Hard Rain

If I’m honest, the lack of rain is due to laziness on my part, or at least my concern about potential performance issues. 

In his excellent “Into the Breach Design Postmortem” talk at GDC, Matthew Davis explained that when facing a problem, he always tries the simplest solution first, and stops there if it works. Like all good ideas, that one seems obvious. But many developers I knew (including myself) often overthink, because simple solutions are plain suspicious. They trigger the anguish of missing an opportunity, of wondering if every option has been explored. 

Matthew Davis 
Into the Breach Design Postmortem

Back to the rain, my first instinct was to try the obvious; randomly draw a few single pixel lines at a slight angle over the entire screen. Whom am I trying to impress? It turned Unexpectedly convincing. But rather than quitting ahead, I took on myself to improve things.

Varying angles, single pixel lines, double pixel lines, short lines, long lines, static lines, moving lines, double the frequency, triple the frequency, half the frequency, and everything in between. None of it looked any better, and they all performed the same or worse.

First version of the rain

It took me an entire day of hopeful stubbornness to concede, and revert to the first version. I would say lesson learned, but let’s not fool ourselves. It wasn’t all lost though, as it gave me the idea of linking the angle of the rain to the wind force. I cobbled together a proof of concept, dividing the wind force into three brackets corresponding to three pre determined rain angles. 

Drizzle and low wind force

It worked great first try. So I spent the next few hours trying a number of equations that would calculate a multitude of angles I didn’t need. Not only that, but the aliasing made the line’s thickness feel inconsistent. All this wasted time could have been avoided, the frustration spared, had I learned my lesson.


Showers and high wind force

Still, it would have been a shame not to set up a few variants, from drizzle to heavy rain. In particular since it simply required adjusting the number of lines, and their length. This time though, I ignored the temptation of coding a useless and most likely disastrous rain force system, and just stored three presets.

One concern remained: the unperturbed ocean made the rain look like a mere screen space 2D effect, which it is. Since the waves are pre rendered, I figured that I could add raindrops at runtime. This came relatively easy, even if a bit time consuming. I made three variants to fit with the screen effects presets. 


Final rain effect with raindrops on the water

In the end, the effect was worth the work. The added overcast, combined with the various kinds of rains, multiply the weather patterns. The rain is cosmetic, but overcast serves the design, as I explained. I just wish I didn’t make it so difficult for myself.


The Passage of Seasons

Back when working on weather effects, I wrote a crude function to roll for random conditions. But in reality, weather patterns vary significantly based on location and season. A crew assigned to the North Sea in December would likely sail under a lead sky for the majority of the patrol. But boats patrolling the Mediterranean Sea in July would often enjoy sunny skies (or curse them, since high visibility was the enemy of the U-boat).


20 weather rolls on December 7, 1941,  at the Bay of Biscay latitude

It wouldn’t make sense to build a weather system and submit it to random generation. So I programmed a simple system to take into account the seasons and the location. First, the program picks the cloud coverage from a 3D array, with the latitude of the boat on one axis, the month on the second, and the available conditions on the third.

The location looks up a latitude bracket, from the Norwegian Sea up north, down to the Caribbean in the South. Cloud coverage can take one of the following states:
/Sunny
/Scattered clouds
/Cloudy
/Overcast
/Storm

From the cloud coverage, wind force is rolled, with the amount of clouds weighting the intensity of the wind. The same is done for rain, with probability and intensity calculated from the cloud coverage scale. On one hand of the scale, sunny weather has no rain. On the other , storm has the most probability of rain. Other patterns, like broken clouds, can have anything from no rain, drizzle, normal rain or showers.

20 weather rolls on June 6, 1944,  at the Bay of Biscay latitude

I rolled dozens of weather conditions, changing the location and season, and the system seems to work well, but gameplay will be the real test.  

One more thing to add on this: for now, the system only rolls for initial weather. But the final version will calculate weather changes, with for instance summer and winter featuring the most stable patterns, and mid seasons being more prone to frequent variations.

In addition to weather, location and season affect sunrise and sunset times. My hope is to create the sense of a real world. You can imagine winter months in the deep North conveying a feeling of dread. The sky is a perpetual gray slate. Rain seems to never stop. Long nights and twilight take over normal daylight. Survive until summer, and maybe you’ll be assigned to the Mediterranean, with long sunny days bringing a welcome relief, and a false sense of security.

20 weather rolls on May 8, 1945,  at the Bay of Biscay latitude

 

Deck Gun Sequence Changes

Before wrapping this up, here’s a change I implemented in the deck gun sequence. Until now, it worked as follows:

1. Targeting a ship with the deck gun launches the deck gun attack sequence. The camera centers on the target, and 40 rounds (or what remains onboard) are committed.
2. The crew starts firing on the target, with every round combining four actual shells. Watch all 10 rounds fired (In reality 40), until the 1st Officer gives the result of the attack, including the number of hits and target damage.
3. Depending on its armament and condition, the target retaliates a number of rounds of their own.

The change only affects the first part, when the boat fires at the target. “Playing” on my own, I realized that watching 10 rounds fired in real time grew old fast. Even with deck gun attacks relatively rare, this could be a problem.


Multiple hits per round was confusing

I considered a skip button, but this will likely result in the player systematically skipping, which defies the purpose. Combining four shells into one round fired was done to spare the player from watching all 40 shots. But this is confusing.

I wanted to go back to one shot / one round, but still show the realistic fire frequency. This is the current solution I came up with:

When the sequence starts, the 1st Officer gives the order, and firing commences. The game shows four shots in real time. If the target is sunk, or the boat runs out of ammo within this time, the 1st officer gives his report, and the sequence moves to the retaliation phase.


Less than four shots fire, no time skip

If firing goes on beyond four shots, the game fades to black on the fifth shot, pauses for a brief moment, and fades back from black. Then the 1st Officer reports how many shots were fired, as well as the number of hits. The idea is that the fade to black represents a time skip to the end of the firing sequence. I like this solution for multiple reasons:

. One shot fired is one round spent. No more weird combining.
. The player doesn’t have to watch 40 real time shots, or even 10, but only four.
. The four shots give a sense of real time action. They also inform the player on accuracy. For instance, three or four misses imply poor accuracy. Four hits show an effective attack. 
. Fading to black is a well understood convention for time skip. Sound will reinforce the effect. I’m considering fading the volume down, or applying a low pass filter.
. The 1st Officer reports the number of shots fired and the number of hits, which tells you all you need without watching the whole sequence.


Time skip 

So far I like this much better. I intend to use the same time skip effect for the torpedo firing sequence. This should make for a consistent system. But I’ll explain this in the next log. This time for real! 

This log is on the shorter side, and not the most exciting read, but at least I’m not weeks late. The point of the devlogs is to document as much of the development as possible. Showing the more boring aspects of code maintenance and design changes will make a more faithful account of the entire journey. Hopefully some of you may still find value in that approach.

So as usual, more soon.

Comments

Log in with itch.io to leave a comment.

(2 edits)

I wish your cat a good and speedy recovery and please take your time. THX for all the insights into the development. I have just made a weather mod for the PC simulation UBOAT with a friend and find the topic very interesting, especially in the historical context. Best regards A.U.

I wish your kitty a good recovery and your work on this is amazing.

First-rate devlog as always! Rain system looks very impressive and I think you made a good call on the shortened deck gun sequence. Hope your cat gets better!

(1 edit)

I love that you made a game that was sold in what's possibly the most 80s/90s game box ever

Thanks for the great update and I hope your cat is feeling better. I love the approach to the weather system and I think it is really going to add to the depth of the simulation.