written by owen on 2022-Dec-04.
The main reason i don't open source my homebrew is because the code is super messy and experimental. Its wasn't written to ported like doom or quake. Lets look at newozero; #thread
- Coupling: In NZ the track, the player and the camera are basically co dependent structures. Joined at the hip. Though drawing the track is separate strangely enough. Modern coding best practices discourage this kind of coupling but suggest no good way to actually make the thing work without spending 10yrs writing MVVM code.
- Hacks: The track is using a spatial grid, frustum, distance list and a big array to figure out what to draw and collide with the player. It also tells the camera where to look. This is a big song and dance of code.
- Basically everything is stored in structs because C. Whenever i can store a value instead of calculating it i will pin it to the struct without mercy. It would take all day to explain why this is good, bad or ugly but it gets the job done.
- Every module has a init(), animate(), draw(), gethits() and an exit() function. There are roughly 40 c files including wind, road, building, sign, grid and stars. There are better ways to code this but they do not matter.
- Everything runs on a timer. All the time. Definitely not frame rate independent so it has to be > 60fps or not at all. Timers are everywhere waiting to fire all the time.
- The only reason most if it even works is by knowing exactly which systems interact with each other and ensuring that none of them end up blocking or stepping on the others.
- There is no threading. A Lot if it is time splicing and ensuring that the per frame cost of everything is low. If a tree falls in the forest and there is no one there to see it, does it make a sound? There are no falling trees.
- There can be no intersections on the track because of how its drawn. I could refactor it in the future but the change will not be pretty. Too many things to change. Too many optimizations to undo. I accept this shortcoming and I keep it warm at night when I go to sleep.
- All the signs know which building they are posted on. So since there are 4x more signs than buildings they all check if the building they are on is visible instead of using the frustum. Saves so sqrts. This is an optimization that is probably only done in one place because of the unique relationship between the signs and buildings.
- The menu is a recursive function that draws itself similar to how you print out a directory structure. Over all my years of coding its the only time i have found such a simple solution to UI. All the menu options are pointers to in game global variables. #gamedev This is another one of those unique optimizations that only work in one situation. So I am juggling multiple constructs in my head that solve problems in different ways. This is the antithesis to modern OOP which tries to convert everything into a million classes that eat others dog food.
- I rebase often once i find a cheaper/better/harder/faster way to complete a task. Because i am the only one working on it i can add/remove optimizations based on what i want to achieve instead of what's more compatible. break fast.
- Git doesn't add anything to my workflow. I don't miss any of the code I delete. It either works or it doesn't. Sometimes I tinker with multiple modules to get one thing to work. If it doesn't work then I rebase. I don't have a team of coders and even if I did I would not allow them to step on each other randomly.
- Even the math functions i use are crude. I took a look at the standard frustum culling code and was like; nope i am just gonna do everything with AABB. Octree? Nope, spatial grid. Linked list? Nope fixed arrays. If I had a team I would assign someone to code that module and never think about it again.
- The spatial grid is a bit of magic so I would avoid asking about that just do an octree. Basically it divides world space into an integer that is +- 50 on all axis and stores the indexes that fall into each bucket into a 3d array that needs to be recenter if the player moves out of the range.
- I do put comments into a few sections but it's mostly for things NOT to do as opposed to the thing being done. If there is a section which look like it can be easily optimized but shouldn't I will put a comment there to stop myself from doing the obvious. #gamedev
- I use multiple paradigms. depending on the situation. Sometimes they converge+overlap. Modern code tends to try to stuff everything into OOP. I like to wild out ex; procedural, functional, passing by val, by ref, time splicing, partitioning, whatever gets the job done
- Predicting which paradigms I use for which problem depends on several things; the time it will take, speed, complexity, code size, maintainability, error rate, magic etc. So you cannot blanket the code with ECS or something like that it will just break in mystical ways.
- Some systems (the track) have multiple variables scattered over the code that can be tweaked for a desired result. This means that there are multiple places where you could upset the system or cause a domino effect on performance. It's like climate change in video game code.
- look at this; why am I doing this?
This is a circle loop instead of the normal typewriter loop. Moving forward then back through the list is an optimization I use when updating visible track pieces. Will explain at some point.
static bool seek_back=false;
seek_back = !seek_back;
if(!seek_back) for(int i=0; i<TRACK_MAX; i++) game_track_seek(i);
if(seek_back) for(int i=TRACK_MAX-1; i>0; i--) game_track_seek(i);
I would rather share parts of my codebase for those who are interested. Having random pull requests and questions is not going to be worth my time when people only have a passing interest. Having more trash code on git doesn't make anything better or code more accessible, it only creates clutter. Create code with purpose, create new code and most of all stop wasting time being entertained by pie in the sky technology.