Pipe Dreams

Max Novak
4 min readAug 30, 2023

--

Well, it has been a minute since I wrote part 1 of this journey. Fun fact, life and work can get very busy and as I started making my second game things got hectic, for 3 months… But that break didn’t mean that I didn’t want to do this project anymore and after a break I am back again, with a Pipe Dream clone!

My intent here was to explore and understand a bit more about how spritesheets work in PixiJS. So, to start off I blatantly stole the NES pipedream spritesheet to give myself an art asset to work with. My first task was to build out a 9x9 board and tile “feed” to just put stuff on the page. This meant I had to take that PNG and build out the sprite metadata. Because I was doing this manually, I definitely made mistakes and did way more work than I needed too. I plan on finding tools to compose spritesheet metadata in a future project instead of just manually writing out JSON data. But with that done I was able to take all that tile data and build out a board.

Pipe Dream board and tile feed

For those unfamiliar with how pipedream works, your goal is to place pipes so that the startS tile connects to the endE tile. You have a slight time limit on this, because after a few seconds water starts to flow into the pipes from the start and if there is ever not a pipe to flow into then its game over.

After putting the board together, I started to figure out how I can “fill” the pipes with water. Rather than build some kind of complex system to determine whether the pipes are full or not, I opted for the easy solution, smart naming! I appended either Full or Empty to the end of each of my sprites names and made a little utility method to swap the textures based on their name and location. After that I was able to put tiles on the board and have them “fill” up.

// Utility method for switching to the "full" textured pipes
const fillTile = (
container: Container<DisplayObject>,
spritesheet: Spritesheet,
tile: Tile,
) => {
const tileToFill = container.getChildByName(`${tile.locationX}x${tile.locationY}`) as Sprite;
const fullTextureName = tile.name + "Full"
tileToFill.texture = spritesheet.textures[fullTextureName]
}

After that I hit my life roadblock and put the game aside, thinking about it occasionally but never really having the energy to sit down and pick it up again. Finally after three months, I had quit my job (Yay! Funemployment!) and had time on my hands to reflect on side projects more. So I took a look at this code again, fearing for the worst that I would need to start over fresh. But to my surprise the code wasn’t half bad! Although I did realize I needed to stop being lazy and create some game state so I could add actual logic.

I took the board which only existed as sprites in the game engine and created a “board” object that could hold onto the tiles “full” state, and what was placed where. This allowed me to then add logic around if pieces were placed in “valid” places (e.g. not on top of another tile) and if they would allow water to flow through them (e.g. if water was coming from the left and the pipe had space for it to the left). From there it was as simple as adding a win and loss state and voila a simple pipedream clone was done!

Final Game in action!

While it might feel weird having two different things managing state, the game engine itself and my “board” object. In my mind, they structurally do very different things. One is purely for rendering & interaction and one is for game logic. However, this might not be the right mindset for game development or for working with PixiJS. I did debate putting pointers to each tile in my board object so that I wouldn’t need to search for them by name (like you can see in my util method). But I still feel pretty amateur in my PixiJS implementations, so I went with something easy instead of something that might be “right”. With my intention to be to learn what is “right” through my explorations and building of new games.

The biggest hurdle with this project was really the gap in time and the feeling that this project would never end. Is this a project that has a ton of polish and is something that I want to show off? Nope, not really. But it is something that has let me explore and learn new skills, which is a huge success! Now, with two games under my belt I feel like I can explore more complex systems and create something a bit more interesting. And now with more time on my hands, I’m hoping I can actually continue this series bi-weekly like I originally intended!

Check out the game here and the code here and hopefully I’ll have some other small games to write about soon!

--

--

Max Novak

Software Engineer that likes to futz about with games