In my previous report, I had started working on raycasting the floor.
Sprints 15 & 16: Pre-production and initialization
Planned and complete:
- Move player in dungeon view
Unplanned and complete:
- Walls block movement
I was out of town last week and so I didn’t have a report for last Monday, but it would have just been me saying that I was running into trouble rendering floors correctly.
I was surprised because I was combing through my rendering code backwards and forwards, but I couldn’t find anything wrong, yet clearly something was wrong since the floors kept showing up as horizontal lines.
Basically, I suspected three things could potentially be wrong:
- I was calculating the location of the source pixels incorrectly
- I was putting the color of the source into the wrong target pixel buffer
- I was updating the target image with the buffer incorrectly
I spent a lot of time looking at how SDL_Surface and its pixel formats could work.
I did find out that my source image was somehow the only one of my images that was loading as RGB and not RGBA, so I fixed that, and I found out that my original texture was not a perfect square of 1024×1024, so I fixed that, too.
I made sure that the pixel formats between the source and target textures were the same.
I tried to experiment with the pitch values to see when and if I should use them.
I tried to figure out if I was using the wrong byte size, or using the wrong values as an index through my pixel buffer when it should represent the bytes.
At the suggestion of the fantastically helpful Joel Davis, I tried smaller test images that were easier to work with. So instead of a 1024×1024 image that I was trying to map to the bottom half of my rendering target, I used a 16×16 test image and tried to map it to a different 16×16 target, then a 32×32 target.
So after a bunch of experiments and tests, I was able to determine that the code that updates my texture’s pixel buffer using the texture’s pitch is most likely fine.
And I was able to determine that my target pixel calculations were straightforward and easy. For a pixel buffer that is a one-dimensional array, the current target pixel index = current row * width of target + current column.
But I was seeing strangeness with my calculated source pixel index. It looked like only a subset of the source pixels were being checked.
And after rechecking and rechecking my rendering code, I found a line that wasn’t doing anything.
See, I recently changed a very basic data structure in my code base so that it was easier to work with.
GBLib::Point is represented as three floating point values: x, y, and z.
And if I wanted to make a new point that represented an offset of an existing point, I used to have to do something like this:
Point newPoint(oldPoint); newPoint.moveBy(x, y, z);
Early in this project, I decided to finally change Point so that it worked more like this:
Point newPoint = oldPoint.moveBy(x, y, z);
So oldPoint stays the same, and moveBy() returns a new Point object with the values.
Joel Davis says that it is a confusing name, and I think I agree, because I completely missed a line of code that was doing nothing:
Basically, theFloor represented which part of the floor a ray was hitting, and this line was supposed to change it so that each column pointed at a different part. But it was not changing things at all, which explains why I was always getting horizontal lines rendered.
The code should have been:
theFloor = theFloor.moveBy(floorStep);
After banging my head against this for so long, I was worried I was going to need to give up on this raycasting experiment and either switch to a true 3D engine (and probably use Godot instead of trying to write my own from scratch) or give up on a 1st-person perspective altogether.
But I quickly got the ceiling rendering in, which required both flipping the perspective upside down so that it projected backwards instead of looking like a second floor AND making sure that it rotates correctly. I had a trippy experience in which the ceiling rotated the wrong way initially, and I really should write down how these bugs make cool effects because doing it on purpose might come in handy some day.
In the end, I spent much longer on this raycasting rendering code than I would have liked, and I now feel like I need to defend myself for continuing to use my own GBLib codebase instead of switching to something like Godot. If I’m going to be struggling with idiosyncratic code, why not something more established with better documentation and lots of other developers who can help each other?
And, yes, if I was starting out, or more specifically if I was giving advice to someone who was starting out, I would say without hesitation, “Learn Godot.” In fact, if you are someone I am giving advice to, go here: https://godotengine.org/
But why don’t I take my own advice? Because I’m not the target audience of that advice.
I’ve been working with my code base for a very long time. I’m excited about this raycasting code because it does not require more than merely USING my existing code, give or take the effort to access SDL2’s functionality that I need. I don’t need to implement an entire 3D engine, and if I needed a true 3D engine, I think I would in fact use an existing game engine rather than roll my own (unless…no…I mean, if I went for a simple, retro look…no…).
Anyway, call me stubborn or silly, but I’m sticking with GBLib.
Once I got walls, floors, and ceilings rendering correctly, my next task was to make sure that movement in the dungeon respected walls. That is, instead of allowing movement through walls as if they weren’t there, I needed to prevent movement if there was a wall.
If you recall from a previous update, I opted to represent the dungeon grid cells in a way that allows for more flexibility. Most tutorials have a cell as either empty or solid, which makes rendering and obstacle avoidance easy: if cellType == SOLID, then doSomething().
My code needed to determine that a wall was an obstacle only if the movement would go through that wall in particular. That is, if I am in a cell facing east and attempted to move forward into a target cell, an eastern wall of the original cell should prevent that movement, but a western wall in the original cell wouldn’t.
That seemed simple enough. But I also then tried to think ahead to what else counts as an obstacle. Walls are internal to a given cell, but what if there was a large object or entity blocking the way? Those kinds of obstacles would be in the target cell, since if they were in the original cell, the player wouldn’t be occupying it. So checking for obstacles would first check for walls preventing movement out of an origin cell and then check for things blocking the way in the target cell.
Before I thought too hard about how to make such an obstacle check elegant, I reminded myself that there are no objects or entities in the game yet, so I only checked for walls for now.
But I liked the idea of knowing what type of obstacle was preventing movement. I wanted to give the player feedback based on the type of obstacle, and so I added the first dialogue script in-game based on whether or not you tried to walk through a wall:
Which reminded me that I wanted to replace the art for the party members in the game.
Also, the dialogue/script code is very much hardcoded at the moment, and I eventually want to make it less so.
I now want to populate the dungeon with things, like doors, ladders/stairs, and items.
Thanks for reading!
Want to learn when I release The Dungeon Under My House, or about future Freshly Squeezed games I am creating? Sign up for the GBGames Curiosities newsletter, and download the full color Player’s Guides to my existing and future games for free!