Why?
Well, why not? I had recently wanted to dig into the Ebiten game framkework for Go and I wanted to create something that was not annoyingly complex for a first complete attempt. I had previously tried to make a Terraria clone in Ebiten but whatever I did was not working performance wise and I determined that to fix it I would likely need to re-write everything. So a text based game it is.
The Game
The game is called Dark Cave. It is a very short look-and-examine adventure where you uncover a short narrative about the place you "wake up" at.
You can find all of the code here on our Github: https://github.com/LetsEatLabs/dark_cave
Lessons Learned
There are a few lessons I learned from doing this project and I want to organize my thoughts for my own benefit below.
Design A System Before You Start Building It
This is something that I have started thinking about a lot recently, and have started doing in my life. Sure, the subtitle sounds obvious but a lot of the time I am just so excited to work on something that I just dive right in and try to do what I can immediately. This leads to some series technical debt that I introduce myself - especially when I get stuck and decide I "have to go with what I already have".
In this project I had an idea of how I was going to organize the levels, in loc.json
.
Here is an example area from loc.json
:
{ "name": "cave_interior", "current_description": 0, "descriptions": [ "You are actually in the back of the cave. It does not go back very deep, it is just incredibly dark." ], "connected_locations": { "cave_entrance": true }, "objects": [ { "name": "blue_box", "can_pickup": true, "is_visible": true, "interacted": false, "total": 1 }, { "name": "dirt_pile", "can_pickup": true, "is_visible": true, "interacted": false, "total": 1 } ] }
I would load all of these items from loc.json
into a massive array in the Ebiten Game struct. Each location was inferred like this:
type Location struct { Name string `json:"name"` CurrentDescription int `json:"current_description"` Decriptions []string `json:"descriptions"` ConnectedLocations map[string]bool `json:"connected_locations"` Objects []Object `json:"objects"` }
Each object that is present in each location in loc.json
has a corresponding description in objs.json
"blue_book": "The book has periwinkle blue pages, bound in what appears to be some form of ancient leather. The book feels warm as if it was held recently. The contents are in some language you do not understand.",
I would combine the object information from loc.json
and objs.json
into an Object struct and add that to an array of objects found at each location to the Location struct
type Object struct { Name string `json:"name"` Pickup bool `json:"can_pickup"` Visible bool `json:"is_visible"` Interacted bool `json:"interacted"` Total int `json:"total"` Details string }
The Details were added in a separate method because they were in a separate file, with the idea of keeping loc.json
"cleaner".
This was all a terrible idea.
Firstly, if I did actually intend to keep location and object information separate I should have done all off the information separate - excluding a list of items found at each location. Alternatively, I could have included that in objs.json
as well.
Second the way I had put things into arrays meant that I had ti iterate over everything anytime I wanted to select a specific item or location. While this was not an issue on my 8c/16t PC in 2022 on a small text based game - if this was an older PC or a much larger game then we may begin to see performance issues. In the future I would like to better plan my schemas ahead of time.
Summary
In the end, I had a great time making this game. It was a blast, and I might even continue to sharpen it up overtime.