This is my 2D scene manager written in Python. The aim for this project was to be able to serialize/deserialize entities on screen, control them through a UI and control the program with a drop down console, all written from scratch (as scratch as can be with Python). The goal is to be able to replicate all of this functionality once I transition to C/C++ using either a software or hardware rendered, written from scratch.
I began with some simple rects being rendered (my Entities) and with some functionality to moving them around with WASD and a mouse.
Once I was able to get a couple of Entities rendering on the screen, since this is a scene manager, the majority of the rest of the work was going to go into the UI and being able to transform save/load and transform these entities in various ways. So I began with a basic UI that shows me what group and layer my UI element was sitting on. This was going to help me render things in front or behind other objects by sorting.
Once that was done, began introducing the the concept of an Immediate Mode UI (Something I had just learned about at the time, thanks to Casey Muratori). In short, the UI is build and discarded every frame. Every frame, iterate over a portion of the code, and build/draw the UI in place as needed. I also introduced the concept of hot/active where you are either about to interact with a UI item, or you are interactive with a UI element. This helped a lot in being able to work with the various different situations where your mouse is over an element, but you are already typing in another element. Or you click on a button and drag our mouse out to let go of that button.
The code I wrote for this is my first poor attempt at doing something like this, and it took me many conversations with people who knew how to do this to understand the concepts and give it my best shot. The code looks something like this:
UI.do_canvas(UIID(), pygame.Rect(0, 910, 250, 400), 18, padding=v4(10,10,10,10))
UI.do_label(UIID(), "level - %s" % str(_globals.level_loaded), 18)
UI.do_canvas(UIID(), pygame.Rect(1026, 0, 250, 400), 18, padding=v4(10,10,10,10))
UI.do_label(UIID(), "Entities", 18)
for e in EM.entities.values():
if UI.do_button(DUIID(), e._id, 18, tab=True):
_globals.selection = e
UI.do_canvas(UIID(), pygame.Rect(0, 610, 250, 400), 18, padding=v4(10,10,10,10))
UI.do_label(UIID(), "Data", 18)
if _globals.selection:
UI.do_label(UIID(), "_____________", 18)
for label, value in _globals.selection.__dict__.items():
if label == "sprite_source":
UI.do_label(UIID(), value, 18)
if label == "position":
UI.do_label(UIID(), label, 18)
_globals.selection.__dict__[label].x = UI.do_param(UIID(), 18, value[0], align=2)
_globals.selection.__dict__[label].y = UI.do_param(UIID(), 18, value[1])
if label == "scale":
UI.do_label(UIID(), label, 18)
_globals.selection.__dict__[label].x = UI.do_param(UIID(), 18, value[0], align=2)
_globals.selection.__dict__[label].y = UI.do_param(UIID(), 18, value[1])
From this I was able to create a canvas, and populate the canvas with UI elements like (labels, buttons, text input boxes, …). So I created a couple canvases, one to show the list of entities I was working with, which behaved as buttons, and one to show which individual entity I was editing.
After this I began working on a drop down console. The goal was to make sure the console behaved just like any console that you might find in some games (Quake, CS) and somewhat Jonathan Blow inspired from watching his youtube channel. You can type input, move your cursor around, edit text anywhere in the input string, view history, auto complete known commands and a few other features. But most of the value comes from being able to serialize and deserialize the “level” I was working with.
Finally I wanted to put it all together. What this meant for the project was being able to load a level, make modifications to the entities that were loaded, and save them to disk with my own simple data format, to be loaded again later. The result is this.
Project done! the goal now is to be able to replicate this in C/C++, using DirectX and writing everything by hand myself.