I decided today to take a stab at making the sparse nodes truly sparse. And I had a bit of inspiration about how to do it that didn’t seem too onerous (using a simple dictionary with key of x combined with z). I did some reading about performance, and it didn’t sound too bad. The code ended up being fairly easy as well, after I got past an initial issue where, despite looking like integers (and even printing as such), the calculated indices weren’t, and they subtly didn’t equate. Quite frustrating until I threw an “int” cast around the index calculation, and things became sane again.
The idea is that all levels will have the same virtual space, which more or less defines the coordinates, but only the points used get recorded. Quite sparse. I just have to pick a good max size that will work with all levels. (Although… since the dimensions are stored, they could be made to vary per level without breaking old levels.)
Sometimes I wonder if I’m doing this to avoid working on levels. But actually, this stuff needs to be worked out now. In fact, I’m going to have to recreate the (few) levels I have so far, as the format has changed. I wouldn’t want to have to migrate a lot of them.
Once that is all in place, I’ll be ready to start recreating some of the more expansive levels from the 2D game.
It all came together today, which was nice. At least, the part I have been working on did. So I can now edit a level’s “space” (the number of steps you can scroll in any direction). And rather than inputting numbers, it uses a more visually intuitive way, which I’m happy about. It works nicely. I might still show the values at some point, if that makes sense, but for now it doesn’t seem to need it.
You can see the new controls in the following screen shot:
When you’re on the edge of the world in edit mode, you can click the “+” to add more space in that direction. Then the navigation arrow appears. When you’re not on an edge, you get the navigation arrow and, when in edit mode, you get a “-” button that makes the world space one smaller in that direction. The arrows update appropriately.
It works more spatially than, say, entering the bounds as numeric values.
The only niggle for me is the use of the “-” button when not one away from the edge of the world. If the world space is, say, three steps away, hitting the “-” will make it one smaller in that direction, but you won’t see any change (since you’re still not on the edge). The alternative, which might make more sense in the way it’s going to be used, is that the “-” simply removes all space in that direction. (Basically, why would you only want to delete only one in a direction if you’re not on the edge?) I’ll keep that in mind as I move forward as an option. Have to see how it all feels!
In the process of implementing the above, I created some new mechanisms that I’m happy about. A scene when loaded notifies the editor (if it exists). This is handy as things are quite stable at that point, so the game state will exist with loaded data.
The edit buttons are now in their own separate component. Nice and self contained.
Good stuff like that.
I had a thought last night when I was lying in bed “not sleeping” (which occurred when I should have been not “not sleeping” – or, as it’s known more positively, “sleeping”) about a new use for an existing potential component that I only had one use for before. I wasn’t going to bother with it, even though it seemed cool, as I could only think of one use, in one puzzle. But last night, I realized that the new Soojin component behavior opened the door to another use for this other component, which is really cool. Makes it worth implementing.
On a walk tonight, I had a small breakthrough in terms of how the “overworld” could be structured. I have been struggling with this for a while. I knew there would be puzzle levels, but how are they all hooked together? How do you get back to a level (something I wanted)? How are you constrained in the beginning and then set free once you get past the initial training levels? I don’t have all of that worked out, but I have the germ of an idea now, which is quite exciting.
This was one of those days where you do a certain amount of work, and then it ends up not feeling like much. The goal was to get the buttons hooked up for adding and removing space from a level. In the end, I spent all my time getting the button signals set up only to realize they were in the wrong place (the main game controls scene instead of the editor scene).
I moved the buttons (easy enough) and the code (also easy enough), but then I found I had no way to trigger the updates. I then spent the rest of my time plumbing in a new signal to fire when the camera moves. That worked well in the level base scene itself.
The editor raised an interesting problem. It is a Godot “autoload” scene, which means it gets loaded once into the overall tree (outside any loaded level). But its “_ready” method is only called once, when it gets initially added at game startup. As such, I can’t hook the editor up to the game state at that point because there is none.
What I need is some sort of signal saying “level has been loaded”. I haven’t found something like that yet in Godot, which means I’ll probably end up doing it myself – which is annoying, as I have to sort of force it in certain places and hope I have caught them all.
I’ll keep looking. Maybe there is what I need. Otherwise, I know what I’ll be doing tomorrow…
Today ended up being interesting. Taking a long walk at one point, I gave some thought to next steps, and they yielded some fruit.
Breaking the connection between the extent of scrolling in the world and the extent of the grid simplifies things a lot. I was able to make some minor progress in terms of getting buttons to appear and disappear (which involved some minor rearchitecting to put the game state in a more accessible place, since the controls needed access to it in a more dynamic way). So not much in terms of code.
The interesting part in my thinking was realizing it would be really cool if you didn’t have to specify the grid size – that it would actually be arbitrarily large and would figure out the minimal size based on where you place points. In other words, have it be truly sparse as I had originally intended. If I could do that, besides possibly being interesting to code, it could lead to the potential for larger levels, since it wouldn’t need a full grid of internal positions. That was my initial intention, before expedience pushed me to simply get something simple working.
Wow. The month is over half done. It has been a good experience doing these daily updates, as it forces me to do something. Well, two things actually: both writing the entry and doing something worth writing about to begin with.
I began diving deeper on the underlying grid in relation to the grid points and sparse nodes. It ended up simplifying the code. The issue is that the grid is hexagonal, which means that constant x, y, or z, all run in diagonal lines. Trying to treat it as a rectangle means things weren’t syncing up. What I ended up doing was iterating directly over the x’s and z’s. The result was a parallelogram, which (strictly speaking) the player will never directly see, as the actual game level grid points defined won’t typically be the full grid.
It did raise the question for me about the relationship between the number of screen steps and the grid. I originally planned to have the game level editor input the number of screen “steps”, and then it would compute the grid from that. But the grid being slanted makes it ambiguous about the grid should be. And perhaps some levels want a much smaller grid than screen space (e.g. if the background is quite extensive beyond the grid).
So now I’m thinking I might just make them separate. They already are internally. I’ll just have to expose them that way.
In some ways, it simplifies things. I don’t have to work out the “right” way to relate the two, given there might not even be one.
That will come tomorrow, hopefully. Go with editing the number of screen steps and then move onto sizing the underlying grid.
Another Sunday bites the dust. And it’s getting so dark and dismal these days. It’s not even close to winter yet.
Today was satisfying, ultimately, after a slow start that seemed would end badly. Later in the day, though, I just started trying some things, and it started to come together.
I ended up at the point where I have multimesh instances for both the edit grid and the game’s grid points. That was one difference that simplified things: instead of having a single set of grid points that appear, disappear and change sizes depending on state, I went with one multimesh instance with the full set of grid points for editing (which can be shown or not shown) and then another multimesh instance for the game’s grid points. And instead of trying to augment the multimesh points incrementally, the code just rebuilds them from the sparse nodes whenever a change is made. It seems fast enough, it only happens during editing (so it doesn’t have to work everywhere), and I haven’t seen any negative effects to it.
Some of today was spent looking at draw calls and other display statistics, and some simple changes made quite a difference in the numbers.
A significant change was the realization (after reading some Godot reddit posts) that I had shadows turned on for grid points, grid lines, conduits and pulses, none of which need shadows. Turning off shadows reduced draw calls for each by 75% (4 calls to 1 call). Adding a conduit now only adds one draw call, whereas before it added four.
Pulses also still had the full default settings for a cylinder (4 rings and 64 radial segments), so reducing those down improved the number of vertices used greatly where a source is starting up.
The next step in being able to resize a level is to resize the sparse nodes to the new size. I can think of a brute force way to do that, which I might just go with to start.
Not much today besides some research. I didn’t find what I was looking for in terms of a solution to my problem, but I guess I at least know now what isn’t part of the solution. One advantage to having separate mesh instances for each grid dot is that you can detect when they’re clicked on (for editing). The multimesh instance doesn’t have that, not even (as far as I can see) for the thing as a whole. So I’ll need some sort of solution.
I had some good luck today with the Godot MultiMesh and MultiMeshInstance, after some initial stumbles. Creating each instance within the mesh is straightforward – I was able to take the code that currently generates instances of the grid dot scene and just set a new “instance” in the multi mesh at that position instead. You get to specify the transform for each instance, which means I can represent active and non-active points.
I still need to see if I can hide an instance as opposed to having to rebuild the instances for each visibility change – or change its scale to 0 or something horrible like move it sufficiently below the ground. I’ll have to think about that a bit… use the right technique.
This image shows some playing around. All the grid dots are held by a single multi-mesh instance, with different scales applied.
Since you can rotate instances as well, I may be able to use another multi-mesh to hold all the grid segments and just rotate each one and position it as needed. Neat stuff.
After I wrote yesterday, I worked a little bit more and tried out the new pulse width code. It ended up being what I was going for, so I think that’s good for now.
The next step is back to being able to set the size of each level. However, that raises the issue of the grid points. Right now, each possible grid point has a separately instanced prototype scene. The single screen so far has over 100 points. A level with 6 x 6 screens (as an example) would have 3600 individual nodes, each with its own mesh. I think that would be a potential problem, performance wise. Also, I’d like to be able to support even larger levels, if desired.
I began looking into a Godot’s MultiMesh(Instance). So far, it sounds like what I want. It has to support:
Different sizes of grid point.
Showing and hiding individual grid points.
Being able to click on a grid point
I don’t know yet if it supports those. That will be tomorrow.