Hello everyone,
I would like to share my recent experience because I really felt completely broken today. And I would welcome any advice or comments.
So far, I've 'done' live-code editing and game-platform split (~ Day30), and now I skipped a little towards implementing fonts (~ Day160).
And I bravely decided to
do it on my own and write strings on the screen without watching Casey - by reading Sean's library.
I failed abysmally. When I tried writing small prototypes to check my understanding, I got super overwhelmed and ended up staring blankly at the screen, not understanding anything. The problem was super easy to understand. It's just that my brain... it just couldn't hold the idea and what needs to be done, simultaneously. And by what needs to be done, I mean the situation where I started writing a function to draw two consecutive glyphs, for example, but all the things that would now need to be repaired elsewhere started filling my mental space up to a point where the idea just.. poof, disappeared.
I admit, my capacity to hold a lot of things in my head is pretty bad (maybe I shouldn't be a programmer) but I can actually handle difficult concepts.
Do you experience this as well? I am perplexed how Casey can start writing and editing new code, and then pretty much remember where the changes propagated - without breaking everything and ending up staring blankly at the screen.
The only way forward was obviously simplifying the code, so that I could 'handle' thinking about it... And I think that the thing that was stalling me were actually
the structures. I felt a slight discomfort already because I used a lot of cumbersome things, such as:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 | "handmade.h"
struct Win32_Bitmap
{
BITMAPINFO info;
int width;
int height;
int bytes_per_pixel;
int memory_size;
void *memory;
};
struct Game_Bitmap
{
int width;
int height;
int bytes_per_pixel;
int memory_size;
void *memory;
};
....
"win32_handmade.cpp"
void UpdateGameBitmapSubset() // on ResizeDB
{
game_bitmap.memory = win32_bitmap.memory;
game_bitmap.memory_size = win32_bitmap.memory_size;
game_bitmap.width = win32_bitmap.width;
game_bitmap.height = win32_bitmap.height;
}
DrawRectangleDLL(&game_bitmap, input, &game);
WriteSineWaveDLL(&game_bitmap, &game, phase);
|
I think that it was solely my discomfort with the naming and the way the data was structured, that my brain was just stalling...
I ended up making two big structures, Win32 and Game, and decided that everything in Game is a pointer to the memory that is 'controlled' by the platform. Even ints... how bad is that?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 | "handmade.h"
struct Win32
{
BITMAPINFO screen_bitmap_info;
Bitmap screen_bitmap;
Bitmap letter_bitmap;
Bitmap font_bitmap;
uint64 memory_size;
int mouse_x;
int mouse_y;
float phase;
Game_Memory game_memory;
HMODULE game_code;
};
struct Game // game has access to things which get updated by the platform
{
Bitmap *screen_bitmap;
Bitmap *letter_bitmap;
Bitmap *font_bitmap;
int *mouse_x;
int *mouse_y;
float *phase;
Game_Memory *game_memory;
HMODULE *game_code;
// here is platform-dependent code exposed to the game code
PlatformDebugFreeFileMemoryType *PlatformDebugFreeFileMemory;
PlatformDebugReadEntireFileType *PlatformDebugReadEntireFile;
PlatformDebugOverwriteCreateFileType *PlatformDebugOverwriteCreateFile;
};
struct Bitmap
{
int width;
int height;
int bytes_per_pixel;
int memory_size;
void *memory;
};
struct RGB
{
uint8 r;
uint8 g;
uint8 b;
};
struct DebugReadResult
{
void *content;
uint64 content_size;
};
"handmade.cpp"
...
void WriteSineWave(Game *game)
{
Bitmap screen_bitmap = game->screen_bitmap;
int width = screen_bitmap->width;
int height = screen_bitmap->height;
|
I am much more at ease with this than with passing more than one obscure structures. When the platform calls game functions, it just passes
&game as the argument.
Furthermore, the functions always start by extracting the things that it will be working with (renaming ->->) which improves readability for me.
When I thought about this, I noticed how easy to use the library was and I fell in love with
Sean Berret's style of
functions with 'many arguments'.
We are basically forced to write down all the variables that actually form a logical structure, before plugging them in. This might seem like a neat practice to follow but I think it goes deeper.
Basically, I think that Sean Berret is kind of 'screaming' that just because we think in structures, writing them down does no good. (It spoils readability which seems to matter immensely.) It becomes really evident, once you observe how much he uses concepts which are actually structures, e.g. 'bounding box', 'glyph metrics', but
he never 'defines' them in code, i.e. never groups them inside a structure!
What I also noticed is that he seems to try to avoid returning structures. (In some cases he does but these structures are pretty large.)
So I am thinking that Sean's way solves the structure problem extremely elegantly:
You have comments and function names to prepare the mental structures/concepts, and the function names are immediately followed by arguments which remind you of their more precise definitions, which you may choose to read (and load into your brain if desired).
In the post title, I ask if flat is generally better. Right now, I think that the correct answer is the Sean's 100%. Work with any mental concepts/structures you want but don't write them down in code because it will be less readable.
So do you guys struggle with structures too? Do you like Sean's style? Is it too purist? Am I getting it wrong? How does Johnathan Blow approach this?