I’ve now implemented the .size operator for arrays
Tag: SpaceEngine
Arrays Completed 75%
Arrays can now be declared separately from it’s definition. So
1 2 |
int[] vals; vals = {23,24,25}; |
will now work.
I’ve also added two new operators; “array push front”, which looks like
-> , and “array push back” which looks like
<- . The idea is that it’s pointing from the left to the left-most element, meaning push front and the alternative pointing from the right to the right-most element.
Next; because arrays are now effectively dynamically sized, I need some inherent way to find the size of an array. Like a .size member or something…
Arrays Completed 50%
I’ve now finished implementing arrays in my scripting language. For now, they’re not dynamically sized, and they need all their arguments when first created.
I.e. you need to write:
1 |
int[] anArray = {24,252,-24}; |
You can’t write
1 2 |
int[] anArray; anArray = {24,252,-24}; |
You can otherwise access array elements and update them as necessary. All regular automatic type conversion is handled. So:
1 |
int[] anArray = {"2949",234,52}; |
Will work. However, it does it a bit of a round-about way. The first element in an array value, everything between the { and } is used to determine the type of the array. In the above case it’s a string array.
Then, each subsequent element is type-converted to this type. So this becomes an array of strings.
Then the assignment of a string array value to an int array variable will cause a conversion from a string array to an int array.
If the first element was an int and not a string, only the first conversion takes place.
But, this allows me to keep the any-type to any-type conversion I wanted.
Lexer
I’ve just completed a Lexer in order to produce a stream of tokens for the Parser.
There’s some parts of the Dragon Book which I don’t really understand the reasoning. Such as the Lexer adding info to the Symbol Table… why would it do that? It’s the Parsers job to build a syntax-tree, that seems like the correct place to add symbols. The Lexer is complicated enough just producing a stream of tokens.
Multithreading
I’ve now completed some additional engine work and implemented some architectural changes. The “Engine” is now decoupled quite a bit from the other components. So at the moment I have:
- SDLGLHandler which manages the creation and management of the SDL_Window* and the SDL_GLContext* . Essentially this does the buffer swapping, it also applies a spaceengine::PostProcessingEffectChain to the buffer before being swapped to the window.
- PostProcessingEffectChain which is made up of a series of PostProcessingEffect s. This applies things like bloom and the real-time lens flare and bokeh. This is currently handled by external SpaceFormatFiles.
-
SpaceEngine and a
GameObject class which are closely linked. Basically, the
SpaceEngine object holds an
SDLGLHandler and a
GameObject* . When the
SpaceEngine instance is started, it throws the
GameObject s
Start method into a new thread and then drops into a standard SDL message pump.
The only message explicitly handled is SDL_WINDOWEVENT_CLOSE , everything that’s input related is thrown to the InputManager .
After processing the messages in the queue the draw method of the GameObject is called followed by the swapping method of SDLGLHandler .
For it’s part, the GameObject is an abstract class. When using my engine, you need to derive your own game class from GameObject and implement the drawing method and update method. You also need to register your own callbacks via the InputManager . This GameObject features a single std::mutex as all methods need to be thread-safe.
- A static
InputManager class. This class features methods to push events onto a queue and to register callback functors that will be called if a specific
SDL_EventType is processed.
The InputManager finally has a Start method which creates a new thread to process the internal queue, looking at each SDL_Event.type and calling the correlated functors from the registered callbacks.
I’ve gone with a multi-threaded approach for a couple of reasons. Firstly I wanted to learn how to do it. Secondly, I anticipate needing the decoupled processing to handle the various aspects of the simulation.
I’m pretty happy, aside from any exception handling, the main function looks like this:
1 2 3 4 5 6 7 8 |
int main(int argc, char* argv[]) { spaceengine::SpaceEngine spaceEngine(glm::ivec2(SCREENWIDTH,SCREENHEIGHT)); SpecificGame game(&spaceEngine); spaceEngine.HookGame(&game); spaceEngine.Start(new spaceengine::PostProcessingEffectChain(glm::vec2(SCREENWIDTH,SCREENHEIGHT),"../../Assets/postprocesschain.json")); exit(EXIT_SUCCESS); } |
SpecificGame is derived from GameObject . You can see how both the SpaceEngine and the GameObject hold a pointer to each other. This allows the Engine to ask the game to quit as well as allow the game to ask the engine to quit.
My reasoning for this is that the game window could be closed, which I consider an engine responsibility, vs game specific menu options which would also need to close the game. I believe I could manually push the window close event onto the queue from the game if I needed to; but I think this method works well enough as it doesn’t require the game to suddenly need to be cognisant of SDL2.