Under the hood
23 October 2019
Yay, so basic rendering of a blob works. It was pretty straightforward, so far.
)
(Image courtesy of much hassle in Jekyll... think I have it sussed now)
Now on to the next step, which is to inject some motion into the blob.
Engine
Motion, however, implies control. In game terms, ‘control’ means a main loop handling time-sliced Input and generating something to be rendered. And here we get into another area of choices.
What I’m planning to build is a simple two-layer system: one Rendering layer, and one Engine layer. The Engine will consume Input, produce a view of the world and pass that as a ViewModel to the Renderer. The Renderer will turn the model of the world into drawing commands in the VisualCollection.
Threading
Too much threading is an extreme evil in my book; however, in this case, I can see a clear value in having two threads. With this being a game, we should expect frequent changes in the display, so we can reasonably assume the UI thread will be heavily loaded. And also the modelling of the world could result in a lot of processing operations (hit-testing and motion updates). So a thread for the UI and a thread for the Engine seems about right to start with. It gives us some advantage from a multicore system without introducing immense complexity.
There needs to be some data transfer between these threads.
One direction is obvious: Engine=>ViewModel=>Renderer. Each time the game loop spins, it can create an object representing the current view of the world and drop that over to the Rendering subsystem. The Renderer will pick up the latest available view model to render. A simple InterlockedExchange can readily handle view model transfer.
Windows Input occurs on the UI thread as well, but that is mostly not where we want it. The control system should be in control, so the Input needs to be on the control thread. The way I plan to solve this is somewhat controversial (at least in my head). I’m just going to batch up input operations between each control iteration and grab them all as a bunch.
But when you put the two together in loops you see:
UI | Engine |
---|---|
Input | Generate View Model |
Grab View Model | Grab Input |
Render | Process |
Which leads me to think it would be smoother to skip the InterlockedExchange and have a single critical section that exchanges the stored inputs for the last generated view model. Over and over again.
So that’s what I’m going to try.
Two threads: UI, Engine. The UI thread will render and push input into a buffer. The engine thread will loop, swapping the input buffer for a view model, then updating the world based on the user actions and creating a new view model. As long as this happens often enough, it should be ok.
Getting something working
My approach here is always to get one small step working before considering the next, so now that I know how I want to get my central architecture flowing, what is a minor step I can take?
Goal: Input -> Update Model -> Render Changes -> Loop
I’m already aware that mouse movement is going to a pain to handle (more in a later post), so that isn’t something I want to pay attention to yet. So I think the input I’ll consider keypresses. To be super simple I’ll use the WASD keys to move the blob. I don’t want this in the final product, but it lets me make progress quickly.
So, WASD input buffer, capture on control thread, move blob, generate view model, capture on UI thread, render changes.
Plan formed, time to code.