Rat With a Gun – Devlog #4: Maps and Particles

Ack! It’s been a little bit! Writing these devlogs takes time though, which takes away from the time I spend actually developing, so usually when I get in the mood to work I’d rather spend that energy actually working…whatever, I’ve got some stuff to show off now, anyways.

Development has been a bit slow for a few personal reasons lately, so you’ll have to forgive me for not making leaps and bounds since the last update. Regardless there’s a few things to go over, first of which is particles! These are placeholders for now, but they allow me to get a feel on what problems might pop up and how I should work with visual elements to promote game feel. I set up a buffer that holds the enum of the last player state that will allow me to handle some more complex behaviors upon state switches, first of which was playing dust animations as the player hits the ground. While I could monitor the grounded state every frame to similar effects, this approach allows must more robust control (say for example I don’t want dust to pop out when a player is grounded in the hurt state). The particles themselves all inherit from a particle class that can be called to play and stop, and will send out signals when they are finished. This is structured enough to be utilized throughout my project easily, and abstract enough to allow for implementation of specified behaviors. I spent a bit of time fighting against some of the nuances of the particle systems (for example, if you have a one-shot particle that doesn’t complete its animation before you run out of its rendering zone, it will reset and play over again when you return to it…maddening when you’re not aware of the cause!), but I’m pretty confident that my implementation will serve we well enough here. I set up a similar system for Dispelling Bee, and it mostly worked out there, too.

				
					class_name ParticleAnimation
extends Node

signal finished

func play() -> void:
	emit_signal("finished")
	pass

func stop() -> void:
	emit_signal("finished")
	pass
				
			
				
					#average implementation of a particle animation
func animate_land_puff() -> void:
	var dust_particles : ParticleAnimation =  
	    landing_dust_puff_scene.instantiate()
	add_child(dust_particles)
	dust_particles.play()
	await dust_particles.finished
	dust_particles.queue_free()

				
			
				
					extends ParticleAnimation

#with a substantial amount of particles, these can be an array
@onready var dust_1 := $Dust1
@onready var dust_2 := $Dust2

func _process(_delta) -> void:
	if !dust_1.emitting and !dust_2.emitting:
		finished.emit()

func play() -> void:
	dust_1.emitting = true
	dust_2.emitting = true

func stop() -> void:
	dust_1.emitting = false
	dust_2.emitting = false
				
			

Simple implementation. Instantiate particles from a PackedScene, play them, wait for them to finish (the conditions of completion are completely variable), and the parent deletes the particle at the end.

Other than being able to corral groups of particles together and easily define their behavior, my particle system is designed with garbage collection in mind, as in Godot you’ve got to decide when you free the particle nodes. I’ve found that there are situations where it is more advantageous for the parent node to handle deletion (like in a case where the particle emission might be extended through player actions), but other times it’s better for the particles to delete themselves (say, after a bullet explodes on impact). Obviously this means that there is a bit of a competing philosophy in the code design here, which I’m fully aware of. I’m still playing around with my design style here, which I plan on refining as the project takes greater shape.

Anyways all these particles are placeholders for the time being, as I still haven’t gotten around to doing any art development. To be honest making the art is the part I loathe most; while I usually like to draw, it drains a lot of my motivation when I do it for game dev, and I try to cut as many corners as I can as a result. I’m just super frustrated that I don’t have nearly the same productivity with art as I do with code. There’s also some anxiety there, as once you start making assets the game begins to take real shape, leading to the collapsing of the game’s potentials. Once those assets are made, it’s hard to toss away that work and try to change direction, and everything cements itself, regardless of quality. Positively terrifying.

As you can see from the video above, I also implemented screen shake! It’s a pretty simple system where I generated some noise and used that to set the camera offset each frame. The shake is defined in an autoload script that can be applied to any camera on the fly, though right now it only holds one camera at a time. I’m not sure I need functionality outside of that, but scaling it would be pretty simple if I need to shake a bunch of cameras for some reason. Gets me thinking about possible features…

I also made a global events singleton that I’m using to track certain ubiquitous actions, like upping a combo counter or adding currency. I’m keeping track of both these variables in a resource that I don’t want to give global access to. Right now a child node of the player subscribes itself to the combo event, for example, and updates the count when it fires off. The combo event can be accessed by anyone, such as an enemy as it is killed, to raise the score. If the need arises I can have pertinent info passed into the combo event that the player can then sanitize. I’m thinking of extending this approach to my GUI systems, where changes to player values are sent to the singleton, and are globally accessible. It’s a pretty safe use of the singleton, and because I pass references to the calling objects through the signals, it’s not to hard to identify problems when things go haywire. This really isn’t something that’s feasible in super large projects, but I think it’ll work for the scale of mine.

Very simple to spit out a bunch of levels with this setup.

I finally got around to setting up some basic level gen! I started fiddling around with some procedural generation with tilemaps, but after some work I decided to take a more curative approach; while there’s a lot of cool procedures you can employ for all manners of systems (this video is a rad overview of some awesome procgen techniques), as the game is essentially a platformer, I decided I wanted to have a bit more control over the layouts. I set up a base room scene that contained a tilemap, entry and exit locations, and spawn locations for enemies. The rooms parse their information and send it back to the level, which connects them all into a wacky tower, with an exit room at the top and a entrance at the bottom. Currently I throw the rooms into an array that the level generator picks from randomly to form the tower, but this can be refined as needed. A mostly simple implementation, all things considered. Some functions to regen the levels when the top is reached, and you have an infinite “game” to play! Kind of!

Honestly I’m not too sure I like this approach too much. I could make a whole bunch of rooms, but any uniqueness will immediately become recognizable on repeated play. While that’s not necessarily a bad thing in the long run, I’d still need to personally craft a whole bunch of rooms in order to give the game a proper amount of depth. I’m considering keeping the curated aspect but setting the width of the rooms to a constant value. This would make the levels feel more open and contribute more effectively to the combo philosophy, and for some reason I don’t think it would be as jarring if you encountered recognizable terrain this way. Additionally, I could always run some noise through the tiles to mess with them just slightly, though I’d have to be careful to ensure that it would not impede progression. I suppose it’d be possible to run a pathfinder through the level geometry to discern if a generated room is traversable…

Anyways, some other things I did were tweaking the player physics systems some more (I’m leaning into the gun generating crazy horizontal velocities, as there won’t be a lot of space to more horizontally anyways) and setting up some basic enemy boilerplate that future creatures will be able to inherit. I also set up a system for enemy spawning that only loads in enemies when the player approaches their spawn positions, though I still need to handle unloading them when the player moves far enough away. Things are definitely starting to feel a little more “gamey” now!

My next goal is setting up the HUD. I tend to get really into the weeds with this type of thing, so hopefully it won’t take me too long. Later gator, and please hire me. C’mon, do it.

Leave a Comment

Your email address will not be published. Required fields are marked *