Impulsing: #devtober Day 31

Today ended up being the most productive Saturday I have had in a while.

After my write up last night, my brain was branching off on new ideas and thoughts. I began envisioning the levels being platforms in a broad 3D space, and when you finished one, it would whisk you off to another with slick animation. And you could see in some form the levels around, beside, below, etc.

It was a really cool idea. And it was far too ambitious to try to do right now.

Granted, it would be fun to try, and there’s nothing ruling out doing something like that ultimately. But what stuck in my mind was this image of the puzzle level sitting on this thin sort of platform floating in space or air or whatever you like.

First thing this morning, I swapped out the plane used as a background with a thin, broad cube – the same side, but not with some depth. Given the angle of view, it was hard to see the depth, so I bumped it up a bit.

Then I bumped it up a bit more.

Then for grins, I bumped it up a whole lot more, and suddenly I saw something I really liked. It’s probably not fantastic in the gaming world, and it’s not what I would go with if I had more artistic talent or a better vision. But I like it enough that I’m happy to settle on this for now. I mean, this is just a passion indie game that likely will never go anywhere. I want to get it to the point where people can play it. I just need something to build on.

I sometimes feel like I’m avoiding working on the levels. Perhaps I am. But there is also a rationale for getting some of the more global details settled: it’s easier recreating a few levels than a few hundred. With what I have now, I feel like the idea is solid enough that (hopefully) things won’t change too drastically, at least for this first round.

Here is the first level, reworked:

Initially, it was just a cube (properly sized). That was easy to deal with. The next level has jog in it, and I wanted the background to follow. If nothing else, it helps to see the 3D.

Man, that was a pain.

Sure, you can resize and move blocks fairly easily in Godot, but trying to butt blocks up against each other isn’t easy. And if you want to make a block larger, it resizes from the center, so then you have to guess around how large it will be and then reposition it. Not bad for one or a few, but after completing the even more complex third level (which wasn’t even that complex), I was at the point where I knew that approach wasn’t going to be workable.

Then it occurred to me that perhaps this was a good time to re-evaluate GridMaps, now that I know what will actually be in them. I started out with a simple tall narrow cube, full height but 1×1. Immediately, I hit the problem that the GridMap was centering this tall cube in the Y direction, whereas I wanted it to be top aligned.

I tried offsetting the GridMap to make the top line up, but then the perspective camera made painting the blocks impossible. (You would need to figure out where the bottom way down there would be.) Thinking now, a top view or orthogonal camera would have solved that. What I did instead was to research the problem, and I learned that others had it, and there was no solution in Godot for it besides creating your own mesh in an external tool like Blender.

That’s what I ended up doing. A lot more transpired after that, including some really frustrating things around getting the mesh to show up and the fact that the GridMap plane can be moved up and down, and to pan the view you use the middle mouse button plus shift, and it turns out that if you hold down the shift key and spin the mouse wheel, the plane moves up and down. I noticed the grid had changed colors, and the objects being placed no longer lined up. It took me too long to figure out what the heck was going on. Even now, even being careful, when panning I will occasionally move the plane by accident and have to reposition it.

On the positive side, being able to move the plane up and down opened up some creative possibilities in terms of level design (“artistic” more than functional), so it ended up net positive after quite a while of hair tearing. You can see how the corners in the above picture are a little lower. It’s not worthy of hanging in a museum, but it does break up the monotony a bit.

Some more levels:

I finally feel like I know it string the levels together now.

Time to start building out the game more.

Day 31: A bit scary at times. But also something sweet in the end. (Ok, corny. I know.)

The final day of devtober. The month has flown. Not every day was great advances, but a little each day gets you somewhere.

I’ll write up the post mortem tomorrow.

Impulsing: #devtober Day 30

Another one of those days. Not much done externally, but a lot of time spent thinking and doing some research. I have a set of levels, but I need to work out how to connect them together – the overall strategy to build the game from the levels.

I could just string them all together, but that makes for a linear game. And it creates a situation where if someone can’t solve a puzzle, they’re just stuck and can’t proceed. I’m determined that the player will have more freedom, but that means some sort of navigation of puzzles, as well as being able to exit a level or more to a different puzzle or section of puzzle in the same level. Also, some levels might have different puzzle sections, so it’s important to be able to come back to a level and continue it. (At this point, I plan on every level you visit remembering its state. The entire game is in play at once. That has some interesting ramifications in terms of whether you can do irreversible actions.)

To list that out explicitly:

  1. There are multiple levels that can be navigated among.
  2. You can exit a level and come back to it, and the level will be in the same state.
  3. There may be multiple ways to leave a level (places to go to), with some being easier and some being more advanced.
  4. There is some sort of overworld or other type of hub level that the other levels branch off from.

Of course, that could all change. I had originally envisioned a single level with all parts of the machine on it, which you would massage into operation as a single master goal. My reluctance to do that has two aspects: 1) having smaller “bites” to focus on might be easier for the novice player to deal with in the beginning, and 2) I have concerns about performance for something that large all operating as a single network.

Stating things like this, in and of itself, has stirred some thoughts. Perhaps I should do this more often, even if only to myself.

Penultimate but not. Life will continue beyond devtober.

Impulsing: #devtober Day 29

Today, I got the rest of the components converted over to KinematicBodies. I also fixed up the collision layers and masks. That wraps up that part of the recent changes.

I spent the rest of the time playing around with some levels. Nothing has come of it yet. What I find is that I have some ideas for level layout, which is interesting to look at, but which doesn’t have any puzzle elements to it. I’m hoping I’ll get better at this as I go along…

Here’s one, for example:

Sort of an upside-down butterfly. Fun to watch, but no real challenge. Yet.

Day 29: two days left.

Impulsing: #devtober Day 28

I worked a bit today on wrapping up the new physics-based dragging, and it’s working rather nicely. The main trick was to integrate the snapping into the movement, which initially didn’t work properly. It was snapping based on (at least one axis of) where the mouse was, not where the actual component was, which could be different if the component was blocked from following the mouse. It looked really odd (wrong) to see the component snap when the mouse was near a grid position far away.

The solution was to do two different move_and_slides. The first would try to move the component to the mouse. Then the component’s new position would be used to calculate a snapped position. Then the component would move_and_slide again to the snapped position. Either move_and_slide could end up blocked. The final snapped grid position would then be calculated from the ultimate world position of the component (either an actual grid coordinate or null if not within snapping distance of one).

There were a couple of minor things to fix (including changing the spherical collision shape to a cylindrical one, as snapping and setting into the sparse nodes would crash the code if there was already one there), but in the end, it seems to be working. Which makes me happy, especially given where I was a couple of days ago: on the verge of giving up and putting it off.

It’s hard to believe the month is almost up.

Day 28: four weeks in.

Impulsing: #devtober Day 27

I had some relatively amazing progress today. I had zero luck trying to move things myself in _physics_process. So I decided to play around a bit with Godot physics. Besides being fun, it worked out more or less how I wanted. It’s not perfect yet, but it’s a lot closer than it was before today.

I started out by changing the level boundaries from Areas to StaticBodies. Then I changed the Herbert from an Area to a KinematicBody. This caused a few things to break, but only because I had set “collide_with_bodies” to false in my raycast in a couple of places. Once I fixed that, I was happy to see that everything still worked the way it had before. Still no collisions (sadly), but my code generally worked despite it being a different type.

My first step at getting collisions working was to move the node in _physics_process, by simply setting the new translation as I had before. That didn’t work. So I tried reading up on move_and_slide and move_and_collide.

Both of them take a directional vector instead of a new position. So I tried simply taking the vector between the current position and where I wanted it to be. When I tried move_and_collide, it worked – except when it touched something. Then it started moving reeeeaaallllyy slowly. That’s because, I assume, move_and_collide stops the motion when it touches something.

That wasn’t going to work.

I tried move_and_slide next, since it sounded more like what I wanted, and it sort of did work, but it was slower in general. After some more reading and thinking, I decided to simply try multiplying the delta vector, and suddenly things started working much better. I’m not sure what the difference is (the docs mention taking delta into account or not), but I don’t really want there to be too much slop anyway. I just want it to meet the mouse position.

One interesting side effect is that the component with the cylinder collision object worked as I wanted, but the one with spherical collision object allowed the dragged component to slip up and over it. Fun to watch, but not what I want. I should be able to change the spherical shape to a cylinder one to solve it. Seeing this behavior though means that if I ever get into physics full time in a game, it’s going to be a lot of fun. (Maybe I can find a use for the “up and over” behavior.)

There is some quirkiness to work out, and I still have to re-enable the grid setting code (which wants to snap the position – that could be a lot of “fun”), but it’s an incredible breakthrough for me, and I’m happy to have learned something new today as well as move it closer to what I have been wanting.

Day 27: A glimpse of heaven.

Impulsing: #devtober Day 26

Not much today, but I did experiment a bit with moving a dragged object during the _physics_process. It worked, but it had a slightly different feel to it. It probably wouldn’t be bad, but it’s interesting that a delay of maybe a frame makes a cognitive difference.

More than that, I realized that my planned approach wasn’t going to work. A dragged object isn’t a single point moving to a single point. It’s a rectangle moving to a rectangle. You can’t do a single raycast. You need to work out the progress for the entire object.

I may have to put this on the back burner for now. It’s not critical, and I thought it might be a relatively straightforward win. It doesn’t look like it’s going to be, and there are more important things to get resolved and working.

Impulsing: #devtober Day 25

Sometimes it helps to step back and step away for a while. It can be tempting to keep at it, to just keep pushing. Work ethic… Just push through… But sometimes, giving yourself a break, even for a while, can give your brain a chance to work in the background.

I started out this morning playing around with Godot areas, looking to see how I could determine the bounds for a collision. The player can move items around, but it’s important they not be able to move them just anywhere. In particular, we don’t want the pieces to leave whatever is the main area for a level. I also want to have a puzzle mechanic where components can be limited to certain parts of the level or forced to move along certain paths. It’s not something I have worried about so far (apart from overlapping components) because I just don’t do goofy things. But ultimately, players will probably be trying just about anything, and it won’t be considered goofy at all.

My plan was to check for overlapping areas as the component moves. If the component being dragged impacts a boundary or otherwise solid area, it should not be allowed to pass through. This something that is normally handled in Godot with physics, but I’m not using physics. I would need to determine how a component can move and keep it from moving where it’s not supposed to. I even set up a new unit test to begin working it through.

Then I went out in the garden and did some other work, including building a planter box (really a planter “U” since it butts up against a brick wall).

Later, when pondering things a bit more, I realized that my approach has some issues. It would only really work if the player doesn’t move a component too quickly. If they do, they could – in theory – manage to get a component through a wall and to the other side without having a collision be detected. Not likely, but possible. And if you get enough people hitting something, the possible happens.

What came to me instead is the thought that as I’m moving a component, I have its old position and new one. If I can do a raycast, I can see if there is anything between those two positions. And then go from there to force the component to not pass through if there is. It’s more about travel than instantaneous position.

This caused me to shift gears, especially since the raycast functions are supposed to happen during the “process physics” phase. This means I’ll probably need to capture the mouse position, remember it for the next physics phase, and then have that code update the component as it can. At least that’s the theory.

Unless some more ideas occur to me between now and then. Maybe I’ll run into a wall. Then I’d have to see the best way to proceed, to get around it. Maybe I can just go over.

Day 25: Avoiding acting too soon.

Impulsing: #devtober Day 24

Today was one of those days.

After having some success the past couple of days, I hit a bit of a wall. When it comes to writing code or making something work behaviorally, I’m right there. When it comes to how things should look, I’m less so. I keep hoping something will coalesce, come together in a scheme. But perhaps it doesn’t work that way. Perhaps you have to actually make it happen. It ended up just being frustrating, almost to the point of wondering if you can even do it in the end.

I spent some time looking at 3D tile sets, many of which were nice.

I tried a couple of things in the game itself, including some block CG meshes, a stab at hexagonal tiles (won’t work for numerous reasons) and even just having a straight up texture behind the level.

Looks nice, but I’m not sure if it’s what I want. I’m not really interested in having either the same texture behind everything or just random ones not tied to some sort of purpose. But I don’t know what the purpose is yet.

Hexagonal tiles pose some interesting problems. First, you have to be really careful to make sure they meet up properly, which involves some mathematics that may not end up precise. Whereas rectangular tiles are really straightforward. The main problem with hex tiles, though, is UV mapping. The default mapping of a texture is… well, let’s just say it’s interesting. And unusable. And I haven’t worked out either how I’d want it to be or how to do what I don’t know yet. Overall, it adds complication with no perceivable benefit.

The game graph is on a hex grid, but there is absolutely no reason that the environment needs to be as well. And keeping them separate that way (in terms of different methodologies) helps to reinforce how unnatural the game’s “machine” is.

I had planned on exploring restricting node dragging, but I didn’t get to that experimentation. I did go through the 2D game and document all the levels I have so far, because those are the sort of “training” levels so far, and it will be important to have those, but also to have them organized properly. At least now I know what I’m starting with.

Day 24: time to sleep, perhaps to dream. Aye, there’s the rub. Where’s your muse when you need one?

Impulsing: #devtober Day 23

With the extension of the sparse nodes yesterday to allow a much larger working space, I set out to try recreating one of the more extensive levels today. For the most part, it worked nicely, but it did expose some things I needed to fix.

Fortunately, they were small.

First, I remembered (when it happened) that an item selected from the editor palette was being dropped near 0, 0 in the level – no matter where you were looking at the time. That obviously is not desirable, and I was able to fix it fairly quickly.

Another issue is that the Soojin node wasn’t serializing its “locked” property. Again, it didn’t take long to fix.

Both of those were nice in that the system more or less worked, and where it didn’t, I had all I need at my fingertips to make it work. It’s really satisfying when you have built a system enough that it eventually starts to make your life easier. You put in the work, and it pays off at some point.

There was another issue, but it’s not something affecting the level design at the moment. I did try it out, and it failed at first and then did something weird. The case was where you save a level when you’re not at camera position 0, 0. It does serialize the values, but there is no code positioning the camera on load. Then when I went to move, it whipped me off to where it should have been. That should be an easy enough fix, but I haven’t done it yet. A new card for the Trello board perhaps.

The new level shows up a glaring problem, though: I have no idea what I want these things to look like. And some of the nodes are really dark, even when on a bright background:

On a darker background (where the sidewalk ends), it’s even worse:

So I need to work out what these will look like, as that will determine what the nodes will look like. Or I’ll need to good layouts that work with the existing nodes, possibly with colors tweaked.

Still, exciting to be able to bring the level together and see it work. Progress is being made. If only I had more of an artistic vision for this.

(For contrast – and for fun – here’s what the 2D version looked like:

Perhaps even something simple like that could work.)

Day 23 – Friday before a Saturday!

Impulsing: #devtober Day 22

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.

Progress on day 22.