-
π Devlog
Dialogues
A post describing a playful dialogue styling feature, inspired by rubber-duck debugging, and discusses the process and potential uses for it. Continue reading β
-
π Devlog
Godot Game Update
A brief status update on that Godot game. I think we’re pretty close to a finished 4-1 level. The underground section has been built, and the level has been decorated. I’ve also added a couple of secrets, which needed a few new mechanics β like doorways, which are used to transport the player around the level β plus some refinement to existing ones. I am a little concerned about the amount of waiting involved near the end of the first half, where the player will need to make their way across a large gap by jumping on the slow cycling “layer 2” tile layer. Continue reading β
-
Made some more progress on that Godot game. I haven’t gotten any further with the first level of world 2, so I’ve been spending much of my time making mechanics. One of them was the slow moving “level 2” mechanic that I stole wholesale from Super Mario World. That mechanic, despite it being frustrating to speed-runners, was always slightly interesting to me. To have areas of a level become accessible or hazardous just due to a layer of it oscillate up and down, it promised to make for some interesting timing challenges. At least in theory.
Portion of the new level showing all three new mechanics. I decided to put that theory to the test, and start work on one the later levels. And despite being a little skeptical about whether the mechanic could carry through a level on it’s own, I came up with one that I’m reasonably happy with. The mechanic is introduce slowly, and in a rather non-threatening way, proving the player the means to get to higher ground. This leads into the second half, which will be a long underground section which will ramp up the difficulty by introducing the risk of getting crushed or missing platforms.
To compliment this is a new enemy that rushes the player. The player cannot do anything to defeat this enemy: combat is not really a thing in this game. All they could do is evade it before the enemy gives up. I am reusing the same “green slime” sprite for this but I’m hoping that the differing animations provide some hints of how this enemy’s behaviour differs from that of the simpler one.
Finally, it was time to consider checkpoints. While the first few levels were too short to justify adding them in, this one is just that bit too long without one. And given the difficulty ramp-up in the second half, having the player go through the slower first half every time they died would probably lead to frustration. So checkpoints are now a thing. They’re not free β costing 5 coins to activate β and they are sometimes mandatory, blocking the player from progressing until they pay the toll. But I think their presence helps with eliminating the areas of the level that would just be boring to play through again and again.
So yeah, I’m quite happy with this level. And I’m also happy in realising that I’m not bound to building this game in the same progression that the player will experience it. It’s better sometimes to just work on the areas that you’re ready to. I mean, it’s sounds obvious to say that now. Not sure why it took me this long to actually do so.
-
Spent some time over the last few days working on that Godot game, mainly building new mechanics. This evening I started working on an interceptor, something that would jump out of the quicksand in order to disrupt the player’s jump. Here’s an example of how they look in the test bed:
And yeah, they’re pretty much a carbon-copy of the Podoboos from Mario. But I think there’s a reason they’re still making an appearance in games, years after their debut in Super Mario Brothers. They’re quite a versatile enemy, making jumping challenges a bit more interesting than just seeing whether the player the clear a gap. Plus they’re reasonably easy to make.
Another mechanic taken from Mario was a switch that revealed coins and tiles for a limited time. Hit it once and the child nodes of this “timed_limited_visible” scene are displayed and activated for 10 seconds, before they disappear again:
Much like the blue P switch this mechanic takes inspiration from, the switch can only be activated once. So it may be only useful for bonuses and areas the player can afford to miss.
I had to do some special handling for nested
TileMapnodes, since the player could still collide with them even when they’re hidden. How I solved this was nothing too spectacular: basically I just walk the child tree looking forTileMapinstances, and when encountering one, just enabling or disabling the first layer:func _show_and_activate_children(): visible = true process_mode = Node.PROCESS_MODE_INHERIT for tm in find_children("*", "TileMap", false): tm.set_layer_enabled(0, true) func _hide_and_deactivate_children(): visible = false process_mode = Node.PROCESS_MODE_DISABLED for tm in find_children("*", "TileMap", false): tm.set_layer_enabled(0, false)Building these elements was fun, but the main problem is that I’m struggling to come up with a centrepiece mechanic for level 2-1, something that defines the level in some way. I have an idea for level 2-2 β this world is set in a desert so I’m hoping to introduce a thirst mechanic β but level 2-1 I’m hoping to keep relatively plain so as to avoid overwhelming the player with too many new things. The fear is to avoid making it little more than what the player encountered in world 1: a series of jumping puzzles over pits. Sure, that’s pretty much the entire game in a way, but some variety would be nice.
I’m hoping one of these mechanics could help here. I guess I’ll find one once I’ve start seriously building the level.
-
Started working on world 2, and one of the main mechanics of this world: quicksand. It won’t kill the player directly, but it will make it difficult for them to manoeuvre, and getting too low could cause death. Might be one of the more annoying mechanics in the game, but that’s kind of the point.
-
The results of my first play-test are in. And overall, they were pretty positive: movement was good, hit-boxes were fair, and it was described as “quite fun,” which was better than I was hoping for.
One thing I’ll need to look out for is telegraphing secrets. The number of secrets is indicated at the end of the level, and based on the play-tester’s feedback, they seemed to have spent a lot of time running against walls trying to find them. There is one secret in the level 1-1 that I thought was telegraphed well, and I can confirm that the player found them all. But I will concede the others required the player to make a leap of faith, and fall into an area that the player will usually want to avoid, which is pretty unfair. So I’ll need to fix that.
-
A bit more on the Godot game this morning, this time working on background tiles artwork. Made some grey masonry tiles for the end castle sequences. Also tried some background rocks for underground areas. I’m pretty rubbish at anything organic, but they didn’t turn out too bad.
Right side has the background tiles surrounded with their complementary foreground tiles on the left. -
Added a few final things to my Godot game, such as a really boring title and end-title screen, before preparing a release for play testers (or play tester, I’ve got exactly one lined up). I think we’re ready.
-
A bit more on Godot this evening, mainly working on pausing the game, and the end-of-level sequence. Have got something pretty close to what I was looking for: a very Mario-esc sequence where the player enters a castle, it start auto-walking the character, and the level stats show up and “spin” for a bit. Not too bad, although I may need to adjust the timing and camera a little to keep the stats from being unreadable.
-
A bit more Godot work this evening. I wanted to add a foreground layer of tiles that obscured in the player. This is for making false walls to hide secrets around the level. It took me a while to work out how to turn off collision of this foreground layer: there wasn’t really any way to do so within the designer.
Fortunately, this Github comment showing how to do so using a script worked like a charm:
extends TileMap const foreground_layer = 1 func _use_tile_data_runtime_update(layer: int, coords: Vector2i) -> bool: return layer == foreground_layer func _tile_data_runtime_update(layer: int, coords: Vector2i, tile_data: TileData) -> void: tile_data.set_collision_polygons_count(0, 0)Only limitation seems to be that it will disable collision for the whole layer, but that’s perfectly fine with me.
-
Spent more time on my Godot platformer yesterday, mainly rebuilding the first level from scratch. The previous version was rush and was just not fun (it didn’t look good either, but I didn’t dress it up yet). This new one is much nicer, and allowed me to use a few new mechanics I’ve built.
I still need to build out the level ending sequence. I’ve got less than the basics at the moment: a drawbridge descends and that’s pretty much it. I need to add the conditions for when the bridge descends (specifically, a minimum number of coins), stopping the player movement for a brief scripted sequence, then transition to the next level. I think I know how I want to do this, it’s just a matter of implementing it. Once level 1 is dressed up and working, I think that’ll be the next thing I do.
I’m enjoying working on this project so far, although part of me is a little afraid that I’m spending time on something that just isn’t good, like there’s some sunk cost with the time I’m spending on this that’s better put to use on something else. Of course, when I give in to these feelings, I usually just spend the time watching TV. So which activity is the real waste of time? Is producing something that may be crap (or worse, just boring) better than not producing anything at all?
Anyway, not should how this became a feeling post. This game might be rubbish, but I still enjoy working on it: Godot is actually quite fun to use. Surely that is reason enough to continue.
-
Was looking at how I could add hazards to my Godot project, such as spikes. My first idea was to find a way to detect collisions with tiles in a
TileMapin Godot. But there was no real obvious way to do so, suggesting to me that this was not the way I should be going about this. Many suggested simply using anArea2Dnode to detect when a play touches a hazard.I was hesitant to copy and paste the scene node I had which handled the collision signal and kill the player β the so-called “kill zone” scene βbut today I learnt that it’s possible to add multiple
CollisionShape2Dnodes to anArea2Dnode. This meant I needed only a single “kill zone” scene node, and just draw out the kill zones over the spikes as children. The TileMap simply provides the graphics.
This discovery may seem a little trivial, but I’d prefer to duplicate as few nodes as a can, just so I’ve got less to touch when I want to change something.
-
Tried opening my Godot project this morning and was greeted with the following error:
scene/resources/resource_format_text.cpp:284 - res://scenes/falling_platform.tscn:14 - Parse Error: Failed loading resource: res://scenes/falling_platform.tscn. Make sure resources have been imported by opening the project in the editor at least once. Failed to instantiate scene state of "res://scenes/falling_platform.tscn", node count is 0. Make sure the PackedScene resource is valid. Failed to load scene dependency: "res://scenes/falling_platform.tscn". Make sure the required scene is valid.Traced it back to the technique I was using to respawn the falling platform. Looks like Godot didn’t like the two preloads I included:
# file: scripts/falling_platform.gd @onready var FallingPlatform = preload("res://scenes/falling_platform.tscn") @onready var FallingPlatformScript = preload("res://scripts/falling_platform.gd")This resulted in a parse error and Godot thinking the level scene was corrupted. In retrospect, this kinda makes sense. What I doing was technically a circular dependency, where the scene and script was trying to preload itself. I was hoping Godot was smart enough to recognise this, but I guess not.
So I had to change the respawn code. I modified it to make use the duplicate method. Here’s the revised version:
func respawn(): var dup = self.duplicate() dup.global_position = init_global_position # Duplicate also copies the velocity so zero it out here dup.velocity = Vector2(0, 0) get_tree().current_scene.add_child(dup)After some limited testing, this seems to work. One good thing about this approach is that it looks like duplicate copies the script, so I no longer need to do anything special here.
So I guess the lesson here is don’t try to preload the script within itself.
-
Adventures In Godot: Respawning A Falling Platform
My taste of going through a Godot tutorial last week has got me wanting more, so I’ve set about building a game with it. Thanks to my limited art skills, I’m using the same asset pack that was used in the video, although I am planning to add a bit of my own here and there. But it’s the new mechanics I enjoy working on, such as adding falling platforms. If you’ve played any platformer, you know what these look like: platforms that are suspended in air, until the player lands on them, at which point gravity takes a hold and they start falling, usually into a pit killing the player in the process: Continue reading β
-
Prototyped a game I had in mind, sort of a 2D Sokoban-like thing, where you control a robot with a retractable pushing arm that is to push gems to a “receiver” tile. Not entirely sure if it’s fun enough to actually build.
Used PixiJS to build it. Not a bad framework.
-
Making A Small Two-Letter Country Code Lookup Page
A small evening project where I made a simple site designed for Vivalidi’s sidebar to quickly lookup two-letter country codes defined in ISO-3166-1 alpha 2. Continue reading β
-
Dusted off Podcast Favourites (last commit 25 April 2022) and fixed a longstanding issue of thumbnails being lost when they’re changed in the feed. Editing the feed properties will now force a refresh of the thumbnail URLs. Didn’t need to change anything else, which was a nice change.
-
UCL: Some Updates
Made a few minor changes to UCL. Well, actually, I made one large change. I’ve renamed the foreach builtin to for. I was originally planning to have a for loop that worked much like other languages: you have a variable, a start value, and an end value, and you’d just iterate over the loop until you reach the end. I don’t know how this would’ve looked, but I imagined something like this: Continue reading β
-
Idea for UCL: Methods
I’m toying with the idea of adding methods to UCL. This will be similar to the methods that exist in Lua, in that they’re essentially functions that pass in the receiver as the first argument, although methods would only be definable by the native layer for the first version. Much like Lua though, methods would be invokable using the : “pair” operator. strs:to-upper "Hello" --> HELLO The idea is to make some of these methods on the types themselves, allowing their use on literals and the result of pipelines, as well as variables: Continue reading β
-
UCL: Iterators
Still working on UCL in my spare time, mainly filling out the standard library a little, like adding utility functions for lists and CSV files. Largest change made recently was the adding iterators to the mix of core types. These worked a lot like the streams of old, where you had a potentially unbounded source of values that could only be consumed one at a time. The difference with streams is that there is not magic to this: iterators work like any other type, so they could be stored in variables, passed around methods, etc (streams could only be consumed via pipes). Continue reading β
-
This week’s distraction: building a Wordle clone. No particular reason for doing this other than I felt like building one, although I did miss the small time waster of the original Wordle, and watching a game show with my parents that had a similar concept just made those feelings stronger. Main difference between this and Wordle classic: board randomly selects between 4-letter, 5-letter, and 6-letter words; no daily limit or social-media sharing when you guessed the word correctly; and the biggest one: UK English spelling.
Some remarks on how this was built: I used 11ty to build the static site. It originally started as just a HTML page with some JavaScript, but I wanted to leave the option open for bundling and minifying the JS with Stimulus. The dictionary I got from Hunspell, which is apparently the spell checker Apple has based their work on. There is a little bit of Go to filter and sort the dictionary of words. The words are in sorted order for the binary search algorithm to check if a word exists or not. The puzzle order is predetermined and was done by “shuffling” the indices in a separate array. Base styles are, of course, from simple.css.
If you’re interested in checking it out, you can find it here. Just be aware that it may not be as polished as much of the other stuff you find out there. Turns out that I can tolerate a fair few shortcomings in things that I build for my own amusement.
-
Learnt a very import thing about Stimulus outlets this evening: the outlet name must match the controller name of the outlet target. If this is not the case, the outlet will not bind and you’d be beside yourself struggling to find out why the outlet target cannot be found.
The outlet identifier in the host controller must be the same as the target controllerβs identifier.
Took me 30 minutes and stepping through with code with the debugger to find this out.
-
Started filling out the UCL website, mainly by documenting the core modules. It might be a little unnecessary to have a full website for this, given that the only person who’ll get any use from it right now will be myself. But who knows how useful it could be in the future? If nothing else, it’s a showcase on what I’ve been working on for this project.
-
I’ve been using UCL a lot recently, which is driving additional development on it. Spent a fair bit of time this evening fixing bugs and adding small features like string interpolation. Fix a number of grammar bugs too, that only popped up when I started writing multi-line scripts with it.
-
Project Update: DSL Formats For Interactive Fiction
Still bouncing around things to work on at the moment. Most of the little features have been addressed, and I have little need to add anything pressing for the things I’ve been working on recently. As for the large features, well apathy’s taking care of those. But there is one project that is tugging at my attention. And it’s a bit of a strange one, as part of me just wants to kill it. Continue reading β