Don't wanna be here? Send us removal request.
Text
I add tests for the builder logic. This means I need to add builder.h to the build logic for the test runner. This means I need to redesign my builder to avoid needing to maintain three seperate builds, one for the kernel, one for the game dylib, and one for the tests
While we're at it it would be nice to set the builder up in such a way that it can itself hot-reload, which feels like an unsolvable bootstrapping problem??
I guess I can move the build logic into a data file, which can reload without needing a code update, and that covers everything sane I could do with a hot-reloading code-rebuilder
Yo dawg, I herd u like infrastructure, so imma need better infrastructure before I can work on improving this infrastructure
0 notes
Text
When you're making a game engine, there's a tradeoff when it comes to working on dev quality-of-life features vs game-facing features.
A factor I don't usually see discussed is the project's PITA Budget. It may take more swe-hours to implement a shortcut that saves 5 seconds at a time than it will ever pay back, but the qualitative difference in working without that 5s annoyance reduces the mental-strain cost of continuing to work on the project at all. As a motivation-bottlenecked solo dev, having good tools not only makes asset creation more efficient, but effectively gives you more hours in the month to work with.
today's small thing is interpreting a UI Label of just "\n" to be a shortcut for a linefeed, which lets us change this from
to
cutting lines-of-code in half
It's the sort of thing that makes actually* zero difference in the finished product, but it makes the daily life of being a game developer fractionally less frustrating, so that's satisfying.
*: If compilers can optimize across strcmp, which I strongly suspect they can, then they would literally compile into the same code
For funsies (because I'm stoned) let's pretend we're compiler engineers again. Why should this be optimizable? ui.labels() is a variadic template function that emits one call to ui.label() per argument, which has overloads for each type. the const char* overload has a clause at the top of it:
With inlining, we would get if (strcmp("\n", "\n") == 0), which a human programmer can clearly see is always true. Can compilers? Well, given that strcmp is a C Standard Library function, compilers are free to make special assumptions about how they work that may not be obvious from the type signature. We also would not be able to inline strcmp, because it's compiled separately (unless you're building your own libc from source, or if your libc ships bitcode which would be weird)
Anyway! So it stands to reason that some enterprising young lad may have realized that they can save 0.2ms on some benchmark by constant-folding strcmps, or by precomputing string manipulations, or whatever. How can we check? Godbolt.
Sure enough, even at -O1 clang is able to constant-fold the strcmps and turn test("foo") into just doSomethingElse()
This ramble didn't really have much of a point. C++ is a language built around zero-cost abstractions. There's something really satisfying about knowing enough about how the optimizer works, and what the language's semantics are, including its cost model, and thus being able to reason about what will actually get optimized away in practice.
0 notes
Text
simple black-blue-white gradient
greebly
here's an example of intentionally using lower resolution to imply details/texture
vs the high-res version is clearly kinda nonsense
and now to get dirty
this is pretty much pushing the limits of what can be done with one single gradient map alone
zoomed out + params (8 colors! that's probably too many!)
so the next steps for generating things would be generating multiple noise maps and combining them. We can use thresholding, multiplication/masking, and different noise resolutions in order to make more coherent generated features
finally, stars
gradients
so now i've got it set up so it lerps between two arbitrary colors
this is far less dramatic than the patterns we would see previously, but that's because that was using a 3-color gradient, blue-black-red
I'm excited to mess around with arbitrary-number-of-colors gradients, but not excited about the hacky UIs I'll need to make along the way
(I guess I could use the same colors as the hardcoded 3color version for a better apples-to-apples comparison huh)
2 notes
·
View notes
Text
There we go
it is a pain in the butt to use but oo shiny pretty colors
we're basically doing the same thing as a Gradient Map (pictured: Krita's version), which is a more direct way to manipulate the relevant data, but this works as a v0 because we have (awkward) access to all the bits (and bytes)
gradients
so now i've got it set up so it lerps between two arbitrary colors
this is far less dramatic than the patterns we would see previously, but that's because that was using a 3-color gradient, blue-black-red
I'm excited to mess around with arbitrary-number-of-colors gradients, but not excited about the hacky UIs I'll need to make along the way
(I guess I could use the same colors as the hardcoded 3color version for a better apples-to-apples comparison huh)
2 notes
·
View notes
Text
gradients
so now i've got it set up so it lerps between two arbitrary colors
this is far less dramatic than the patterns we would see previously, but that's because that was using a 3-color gradient, blue-black-red
I'm excited to mess around with arbitrary-number-of-colors gradients, but not excited about the hacky UIs I'll need to make along the way
(I guess I could use the same colors as the hardcoded 3color version for a better apples-to-apples comparison huh)
2 notes
·
View notes
Text
quick UI update
I finally added some sliders and visually this is way more effective than I was expecting
specifically the decision to try just drawing a box outline instead of a filled in rect for the range indicator; I wasn't expecting much but it actually looks kinda competent? in stark contrast to the rest of it
the rendering code is super straightforward
the update code is less so, and I don't feel like going into it in any detail
I also took the liberty of aligning everything, which was dummy-simple
... though getting the text right-aligned was less automatic than I'd prefer (for now)
it's nice to have features built-in, so you can say text.alignment = RightAlign and have that more clearly communicate your intent (plus not having to think about how to write or read a math expression can be a boon), but being able to just cobble something together with a minimum spanning set of functionality is important for Good Enough engineering
0 notes
Text
UI Crescendo
So now I gotta extend the UI to draw more than just buttons I suppose
My first thought is to do the obvious thing and have a UIElement base class, from which individual elements can derive. The problem with this is that we're hot-reloading the game from a .dll, so vtables have a tendency to get thrashed when we reload, so we'll jump into stale memory and try to execute malformed instructions (what we professionals call a "big yikes").
We're persisting UI elements because we've split the update and render steps, so we just need to clear the stale state on reload. So we'll add a callback that we can fire right before unloading the dll, and use that to clear all the stale elements
We also add an Allocator struct so that we can bundle malloc AND free and simplify that plumbing.
Then in the main (non-dylib) part,
and when we unload the game, clear the UI elements
(a simpler thing to do would be to clear the buffered UI elements after rendering them so that we never persist them across frames regardless of whether we reload the dll. However we *do* persist interaction data (button state) from frame to frame, so it's pretty important that we not do that. This does mean we lose clicks in progress when the dll reloads, but that should never happen to non-devs (and is impractical to reproduce for devs, at that), so #NAB #wontfix)
Then I realize that we still have problems, because we need to store some kind of runtime type information (RTTI) so we know how to actually update the elements in question. So if we have to add an enum per derived class, store that in the base class, and make sure that stays in sync, that seems way messier than just using an ADT-like design in the first place. (Dr. Hindsight here: dynamic_cast would probably have worked just fine, I just didn't think about it until typing "RTTI" just now)
So uh what are ADTs? Well if you go full expanding brain memes and do pure-functional programming, you wind up in the land of Algebraic Data Types. Specifically Sum types, so called because the members of a type are the SUM of possibilities of its components. Which is to say a UIElement can be a Button OR a text Label. And another way to say sum/or is "union"
this is basically the mechanism behind how Haskell's sum types work, only with more footguns because we're dealing with the plumbing directly. Fortunately we're using these as essentially an implementation detail, so we control access to a very limited number of public interfaces, so what could possibly go wrong?
(I should probably split some of this stuff out of being all in one big game.cpp file, which would let me actually hide these implementation details, but we'll deal with that cleanup when it becomes more relevant)
back in the UI class, let's pull out the code that validates our element index, because we'll need to duplicate this logic for every type of thing
and now our button preamble looks like this,
that elem.kind jiggery-pokery is something we'll need to repeat at least once per element, which is annoying, but the cleanest way to reduce the repetition is with a macro, and frankly I'd rather not
Now then we just add a label method to add text elements,
and some helper methods to handle formatting numbers (todo: formatting)
All of that leads us to this rather-svelte result for actually building a UI
which rendered in-engine, finally, nets us,
If you can't tell, I am very much of the "MIT approach" to software, where I will spend tremendous amounts of effort in the Implementation, in order to have a cleaner Interface. I did used to be a compiler engineer after all. This is probably related to why I have yet to ship a commercial product solo, isn't it.
anyhow, all that was a bunch of architectural gobbledygook. Mostly just figuring out how to partition the problem and what mechanisms to use in order to not explode within the confines of dll hot-reloading, while still scaling linearly with additional elements. In theory, adding different types of elements at this point should be straightforward, not requiring much additional infrastructure. (In theory, theory and practice are identical. In practice...)
Now, the thing that's cool about the imgui-style of UI architecture is that it lets you make compound custom widgets via simple composition of existing elements. Meaning that application logic doesn't need to deal with any of the complexity of the underlying system.
So the specific thing I wanted a UI for was to tweak the parameters of the texture generator. At some point I pulled out all the different constants I was using, so I could have them all collated in the same space
I don't have default values there, because I set the values in the onLoad callback, so they update whenever I change the code and rebuild
(and I do mean whenever I change the code; I have it watching game.cpp for changes and rebuilding in the main .exe, so that's automated too)
So that's *pretty good*, but it's still a two second compile every time I want to make a tweak to the parameters, along with being in a separate window from where the textures are actually displayed. It would be way smoother to get that down to near-instantaneous
yeah yeah you get the idea
so now I can create a custom widget for each parameter I want to change, consisting of a button each to increase and decrease the value, plus displaying the current value
this is just a normal function that takes parameters totally orthogonal to the UI, that does whatever arbitrary logic it wants, with no custom rendering logic or anything. (Ideally this would use a Slider element, and lo/hi would dictate the range of values, but that would be a new element type, and I'm sick of dealing with UI internals and just want to move on to doing other stuff for a bit)
so now we can remove the onload logic ("I fucking love deleting //todo:s" etc), set those values as defaults, and update our UI code:
which does Pretty Much What You'd Expect
reducing iteration time down ot the 50ms it takes to generate a new batch of 16 textures. amazing
note that we're setting min/max manually, and we could probably reduce repetition by factoring out the common case where we just +/- one with some minimum, this is Good Enough and sufficiently obvious that I'm fine with it
so of course I start playing around with it (noise size = 7, tex size = 16, noise scale = 1, mode = 3, tex repetitions = 8)
ok this one too (noise size 31, noise scale 4, tex size 1024, tex repetitions 1)
and mode 2, noise scale 9 (3 repeating bands of blue, red, green)
mode 3, noise scale 3, noise size 13, repetitions 16
so, the moral here I guess is that by making it more fun to mess around with, I'm more likely to explore the possibility space. Which was the whole point of investing in better tooling in the first place :)
0 notes
Text
UI Buttons
ok actually making some UI
going with an immediate mode UI (imgui style) mostly cause I haven't played with those yet
not thrilled about the idea of doing rendering logic in the update() method, nor update logic in the render() method, so I'll build and react to the UI inside of update(), then stash the results into some data structure (oh look, a VDOM) and render it later at the appropriate time
started writing code to check if the mouse was inside the button, then realized I'm going to want this all over the place so make it general
then realized I can (ab)use C++ templates to further generalize this to any rect-like objects (anything with position+size), which will save marginal amounts of typing!
And I was going to say more but then it turns out I wrote the whole thing in a single pass. This may not be my first rodeo. I have probably written too many game UIs at this point.
(oops forgot to actually increment _buttonIdx so that causes problems trying to use multiple buttons in one frame, alas)
Of special note for ppl new to is the feedback we give the user on hover and on click; in particular note the half-click, in between mouse down and mouse up. It feels super jank to have a click happen right when the mouse goes down.
There's a UX thing here too, where you might accidentally click on something, or start clicking and then change your mind before you let go of the mouse. By not counting clicks that end with the cursor off the button, we let users cancel the click mid-action.
This also helps in cases where you tried to click button A and missed and hit button B; having a dedicated "button is pressed" state will show what you're about to do before you do it, which is extremely valuable for usable UI. Ditto having the button highlight before you even click down, giving you added feedback.
(Side-note, this is why mobile UI always feels jank to me. You can't hover over parts of the UI to discover what's clickable, so you lose that feedback vector)
Some people go further in terms of cancelable clicks, by canceling any click that doesn't terminate within a certain (e.g. 10 pixel) radius of where it originates, but I much prefer allowing the user to drag off and then back on. It's simpler to implement, allows you to change your mind about whether you want to cancel a click without needing to repress, and eliminates the case of someone with shaky hands being unable to keep still enough to click things. (This is more of a problem on mobile, because fingers are less precise then mice, and the pixel density of modern phones is such that a fixed 10pixel radius is smaller than you'd think)
anyway, let's add code that lets you use a vertical layout,
and test it out with a few buttons,
et voila
6 notes
·
View notes
Text
let's start by adding timing information to the texture generation; I don't notice it being slow now, but that can quickly change as I start stacking noise textures
that seems entirely reasonable, 4 128x128 textures/frame means we could do limited realtime generation, which is cool
now I should work on the UI. First up I need to get more symbols rendering
the issue is that I was lazy and didn't draw my glyphs aligned to their ASCII offsets
well I'd put that off until later, and later is now
.... ok that was tedious, but it's done
we'll also need to change the code from this ad-hoc bollocks
to this simpler and more complete version
et voila
1 note
·
View note
Text
Working (through it) in Public
I want to be about something again
It's hard to know where to start finding one's voice, how to publish
So I turn to tumblr, where I have some public-facing artifact I can link to if I ever feel the need, but can rest confident knowing that nobody will ever find this on accident :)
So I'm working on a game engine in C++ because why the fuck not
I've got randomized tile generation, because I think that shit's neat
In general my approach is "just do stuff I think is cool and have fun with it". A big part of this project is just, finding a reason to carry on doing anything, and game dev is something I can't seem to get away from
It's also the only constructive way I could theoretically make a living at this point, with the skillset I have and the pathological inability to work for someone else. So self-publishing indie games... yeah this is gonna go well
6 notes
·
View notes
Text
listening for the voice of God in all things my sense of Narrative has been stripped away misunderstanding the causality underlying the world feeling the vibrations on all the wrong channels I'm crying out for you, but I've forgot who you are
0 notes
Text
micropublishing
back in my day posting something to the internet seemed like a much bigger deal
part of it was UIs were clunkier, but there was also the wariness of new technology
"everything you write is public record"
"you never know who might see that"
these days with smartphones and social media, we lower the barrier to entry tremendously. This is a good thing
the more people have access to a technology, the more likely that someone will use it for something important
the ability to control one's privacy is essential. For those who don't need it, it doesn't matter. For those who do, they REALLY really need it
0 notes
Text
I feel like the best blog format is hastily taken lab notes
my blog format is just a scrawled mess of clawings on a wall I think that's just tumblr I keep wanting to make a public-facing blog and I keep on just not writing stuff to put in public
so I think, ok self let's just start actually rawdogging the internet and dumping freewrites straight onto a scrap pile realistically if anyone ever finds this it'll be because I did something more important that mattered, so hi future historians
there's a distinction between writing things for yourself and things that other people can ever see
as soon as the possibility of scrutiny comes into being, the tension comes into the bones, and it's hard to put words to digital page
but when you remember the other kinds of deranged crap people post on here, you just laugh and hit "publish"
0 notes
Text
dear tumblr diary, today I hung chumblo dung fuffle wumbus
which is to say, seeing how I feel about inchoate schizoposting, to just, let my inner whatsits breathe
0 notes