Been job hunting, dealing with jeets, attended my carry class with my sister for her permit renewal. Oh, and state fair.
Anyway, found some time and
got the polygon depth sorting working.
Then I cleaned the code up, packaged it into modules, and I have a
shitty little repo and demo of it. Most of the interesting part of my work was the
(egggame tile-collection) module.
So the basic structure/design is that there's a top level
tile-collection structure. A tile-collection contains multiple
tilesets. A
tileset is just an image that might contain some tiles/sprites you want to render. One unique aspect of this is that a lot of other game libraries will overlay a fixed grid over your spritesheet, whereas in your client code using this module, you need to loop over your grid and pick out each possible sprite separately. This is more tedious if you have sprites in a traditional grid, but it's more flexible in case you have some one-off graphics in the corner you want to grab. When you pick out a sprite, you are creating a
tile-spec (basically saying which
tileset you're picking from, and the start position and the dimensions).
And then finally you can create
tiles from those
tile-specs and they'll be rendered by the toplevel
tile-collection.
So that's four levels of abstraction:
1.
tile-collection
2.
tileset
3.
tile-spec
4.
tile
Using them would look sorta like this:
Code:
(define *tile-coll* (make-tile-collection screen-dimensions: '(640 480)))
(define *tilesets* (tile-collection-add-tilesets!
*tile-coll*
'((outdoors-1 . "outdoors1.png")
(outdoors-2 . "outdoors2.png")
(indoors . "indoors.png")
(people . "people.png")
)))
(define *outdoors-1* (alist-ref 'outdoors-1 *tilesets*))
(define *outdoors-2* (alist-ref 'outdoors-2 *tilesets*))
(define *indoors* (alist-ref 'indoors *tilesets*))
(define *people* (alist-ref 'people *tilesets*))
(define *person-left-spec* (tileset-add-tile-spec!
*people*
start: '(0 0)
dimensions: '(32 32))
(define *tile-1* (create-tile! *person-left-spec*))
;; position it on the screen somewhere
(set! (tile-position *tile-1*) '(200 150))
BTW, there's a technical reason for why you have to add the tilesets all in one go.
So
I am implementing the multiple textures in
OpenGL as an array texture. Basically, it's a collection of textures, say X by Y and there's Z layers, allocated in one single texture object. This way I'm not hard coding say, 5 textures and hitting that limit at some point. But in order to do this, you need to know what the biggest texture in the stack will be and allocate the array texture to accommodate that up front. It's OK if smaller layers don't use up all the layer, but you need to be able to handle the biggest one.
I'm not sure of a way to dynamically resize an array texture. It's not a huge deal, but it does make the client API a little funky.
As a demonstration of this module, I created
a little script that will render files created by the
freeware Tiled map editor.
Nothing particularly profound. I wrote bindings for SDL3, glew, DevIL that would need to be compiled. The makefile is pretty basic, and btw it uses the script names for the chicken tools on Arch (chicken-csc or chicken-csi), because that's what I use.
I used a few external chicken libraries I'm used to. Off the top of my head, defstruct, ssax, and filepath.
SSAX is nice, and I think it exists for other schemes too. It's an xml parser and it just parses everything to very easy-to-understand sexprs, and with Tiled's XML format,
parsing the level data was very straightforward.
Oh, and I
wrote a very simple little 3d matrix math module. The
tile-collection has a camera matrix that gets used in its shaders, so moving the camera is just a matter of updating a few entries in its matrix.
The
shaders are pretty simple. Just writing out their inputs, and their inputs are just four verts (and associated texture data) for each rect (which is ultimately made up of two triangles).