Vecren: The Story Up Until Now. Part 2 of ??

(Note: this dev blog was started some 3 months into the process. These posts will share a tag, and are my attempt to reconstruct how we got to Present Day.)

So - now we had a renderer. It draws lines and points. Wrote some quick test programs hardcoding coordinates. Wrote a little exe test program - flies a triangle around on the screen, etc.- pretty standard stuff.

I started wondering what I might need to add, or build on top of this thing to actually make a game out of it. So I took Asteroids as my mental starting point. What do I need to make Asteroids? Being a dev for 25 years and a chronic overthinker - gonna need some way to debug, right? So let's write a library to render text to the screen. Plus if you recall from Part 1, I managed to get the little renderer I wrote back in the day drawing some text, so I was at least familiar with the task.

My goals were:

  • Aside from setup, basically ONE call: drawText(x, y, text, font)
  • a human-readable, and human-authorable font format. (since I had no tooling at this point, and as subsequent posts will indicate: I LOVE tooling, and writing tools. One of my favorite things EVER to program.)
  • since I was going to have a human readable format - I felt like making sure it is diffable would be a good thing to have.

One of the first programming things I ever learned was Logo, in the 8th grade. If you don't know, Logo is a language designed for teaching programming and programming logic. You write small scripts which move a Turtle around on the screen, as it draws a trail behind it. There are some basic loop operators, and things - but it's enough to let you understand flow and logic. The linked page shows this example script to draw a Square:

or basically "do the following 4 times: move forward 50 units, turn right 90 degrees" - a nicely terse, readable loop. So I wanted something similar, even MORE simple, and even MORE terse. So I basically framed it according to how I would want to use the renderer to draw lines and points, in a way that is quickly readable/scannable/understandable, and I came up with the following:

# three utility commands:
C <r> <g> <b>              # change the pen Color to rgb (0-255)
I <i>                      # set Intensity (brightness of the pen) to i (0.0 - 2.0)
E                          # End current draw operation.

# and the draw commands:
P <x> <y>                  # draw a Point at (x, y) 
M <x> <y>                  # Move to (x, y) without drawing.
L <x> <y>                  # draw a Line from your current position to (x, y)

# the same square example from Logo, in vecdsl 
#   (assumes we are starting at 0,0 or screen center):
L 0 50
L 50 50
L 50 0
L 0 0
E

As you can see, I have no way to loop - and despite considering it a must-have, I have yet to have a need for it - which will be covered later.

Turns out some of my design decisions were validated when - I went looking for old arcade screenshots to see if I could recreate a font, and then it occurred to me that being the internet - the source code of some of those games is likely up somewhere. Friend, I was NOT disappointed, as not only did I find source code - I found an incredibly thorough annotated disassembly of Battlezone, with more information than you could possibly get through. After poking around, I found the glyph definitions for the font used, with a comment I LOVED:


"Character glyphs. Each glyph fits in a 16x24 cell whose origin is the bottom-left corner. The beam will be left at the initial Y coordinate, 8 units to the right of the character cell."

Hell ye, bottom-left origin bros! Bolstered by this, I pressed on.

# all of the properties in the header can be adjusted at runtime.
[Font]     # only one of these allowed per file, obvs.
w 4        # all glyphs in a font must be the same w/h
h 4        # and....
kern 1     # all int params are specified in local grid (0,0) at bottom left
           #   coordinates.  
scale 3.0
C 1.00 1.00 1.00

[A]        # the blank line is a separator.  this starts a glyph.  
M 0 0      # reset location to origin
L 0 3      # draw up...
L 1 4      # ...and over...
L 3 4      # ...the...
L 4 3      # ...top, and...
L 4 0      # ...back down.
M 0 1      # move in position to cross the A
L 4 1      # Cross it.
E          # glyph DONE. (also required.)

which gives us:

a wide block letter A

Fun, right? So, make an internal lookup of char 'A' to a set of draw ops, and boom.[1] Easy peasy. Now I can draw a letter A anywhere. The header specifies h/w and a value for kern - which does NOT mean what I thought it means, because I thought it meant how letters are spaced, so I intended it as "the number of grid units between each glyph" - which since all fonts are fixed width, should be straight forward. And it still DOES mean that, kinda - but it should just be called "spacing" or something else here, because kern is not quite right. My bad. ANYWAY - since I know all that stuff, I can draw strings - just do a little measuring based on scale/screen size, and soon I had a happy little text lib, drawing strings like it aint no thang. Time to grab some graph paper and go to WORK!

I quickly sketch out a graph paper font, open a text editor and start typing. Hmm. While the human readability is nice and it's easy to follow and understand, typing in all this stuff by hand was not a great authoring experience, but hey. Wasn't like I needed a million fonts, right? For now I just needed one I could print debug info with, and I had that. Remember? That's how ^^^ ALL THAT ^^^ got started: "I need a way to debug things, so some text would be nice" and here we are - WHOLE ASS meta language later. Like I said - I overthink things. And apparently, over engineer them.

Next up! Sprites.

    [1] keener eyes among you will have spotted my first pretty sizable design flaw, and it was only the first lib. :(