Last Year we shared our plans about Violet from a logistical front. The TL;DR of our post was we’d be shooting for a Kickstarter in February 2024. And unfortunately we have some bad news:
Our original, high level plan was to launch our Kickstarter in February 2024. I (Dan) would continue working full time until January 2025 where we’d develop the game for two full years. In 2027, we’d polish the game and release. After meeting with the advisor, it was encouraged for us to set a lower target dollar amount, as well as to spend less time post Kickstarter developing the game. With less budget and less time (the double whammy), we decided to postpone the Kickstarter. Though we know we’d have a good demo / be ready by Kickstarter in 2024, my (Dan’s) gut was telling me to wait another year (2025) for personal financial reasons.
Above everything else, I (Dan) need to make sure my family is provided for. And second, I want to make sure those involved on the project don’t have to worry about if / when they are getting paid. This is a big risk and I need to make sure everyone involved can get through two years without my full time salary. Of course this is disappointing, but I think the pros might be better in the long run (I always say slow and steady wins the race). For one, this gives us another year of development / design / art, which should help reduce time post Kickstarter developing the game. It also means we naturally wouldn’t need as much from a Kickstarter, making a lower target actually workable from a budget standpoint. And obviously, one more year will make the demo even that much better.
From a logistic standpoint, the timeline stays the same, but we are just shifting the Kickstarter out a year. The Violet team was graciously more than accepting with postponing the Kickstarter.
However, the “bad news bears” doesn’t stop there:
Shortly after we decided to push the Kickstarter out, our lawyer informed us that not only is Last Life Studios (the LLC I (Dan) am operating under) is trademarked, the game name of “Violet” is also already trademarked. Essentially what this means is the name of the game is going to have change, as well as some of the branding we have developed (e.g. the website, violetthegame.com, our logo, etc.).
March has not been a good month for Violet. It has really got me (Dan) discouraged. Tony, our artist did give me a little encouragement and said “For what it’s worth, those are all normal setbacks. If it means anything, none of those have discouraged my faith in the project.” The next couple of months will be a bit of a rebranding time, so we may go a bit silent as we try to figure out what to rename / rebrand too.
Setbacks yield pruning, and then growth. Right now the pruning phase sucks. But we believe the growth will be exponential.
This is another one of those hodgepodge of updates posts. However, we already named a post “A Hodgepodge of Updates”. Therefore, we decided to name the post with one of the main new features we added in the last couple of months — the beginning of the game (i.e. the tutorial)!
I (Dan) hate tutorials in games. I’m the type of gamer who just wants to play the game and figure things out on my own. Therefore, we made the tutorial succinct and to the point (and there’s even a way to quit early if an experienced player knows how!). We believe the tutorial shows the game loop well, while explaining the basics of the game. The best thing to do is experience it for yourself! Until then, a few screenshots will have to do:
A couple of other updates you may have noticed in the most recent build of the game:
The minimap icons have been updated.
We have a cave tileset, with a looping cave texture that repeats for those “out of bounds” areas (i.e. the void).
We added a concept of gates and locks, which require unlocking by clearing out enemies, a key, or by some other means.
We added hit effects (slashes) as well as changed the damage color an enemy takes. This in turn means we removed the blood particle effect.
We also added many UI sound effects to help the player know which buttons / inputs are being pressed / used! Unfortunately, those are not observable with these screenshots.
We made quite a bit of progress on the Minotaur these past couple of months. Firstly, the Minotaur dying:
Next, we’ll showcase the Minotaur’s dash attack:
As usual, progress can create setbacks. After we created our stun animations and tested with a few players, we realized the startup of both the dash attack (as seen above) and the stun animation were very similar, causing players to be confused whether the Minotaur was going to attack or be vulnerable. I (Dan) personally don’t like in games when an enemies telegraphed animation is similar — especially in making split second decisions. So, sympathetically, we decided to change all of our stun animations to correct this problem. Below is an old version (which we’ll see how similar it is to the left dash attacks above):
And below is the current, updated Minotaur stunned animation:
Other Art Updates
While taking a break from the Minotaur, we also did a lot of color and effect updates. We’ve mentioned some of these updates already (the slash effects). Therefore, the rest of this section will show off the remaining updates:
Clouds and cloud colors
We removed the cloud effect particle and replaced it with our own “cloud particle”. We won’t go into all the details of how the clouds work, but we will show off a few updated screenshots:
When it is sunny, clouds will cast a nice, dark blue shadow. When it is cloudy, these colors become a mix between a nice dark blue and a cloudy gray. Inside, we change the void color to be a mix between dark brown and cloudy gray. That is a great segue into:
The Void Color
When we are inside a building, and we don’t have a texture, we will use a void color. This color used to be black, but we decided to give it a little flavor, depending on the building. For example, when we are inside a house, we get this nice dark brown:
As we saw above, when it is cloudy, we mix the two colors. When it is night, we replace the void color with our night time dark blue:
This is a good segue into:
The Nighttime Color
Much like the void color, we were using black to represent night time. However, we changed this color to be a dark blue as shown below:
The difference is night and day… or, comparatively between some of our other nighttime screenshots, more pleasant looking.
Removed the Fort Fog
Fort fog was one of those things we added very early in development with Violet to test being “inside a building”, before we actually had artwork for buildings. Now that we have buildings and concepts of being inside / outside, we decided to remove the fort fog. The difference is night and day:
Finally, we added some random decor to the cliffs to give it a little less of a pattern look:
Other Notable Updates
Unlike our two recent posts, there isn’t as much to share in detail within this section. Below is our bulleted list:
Performance Increases. These past few months have increased performance in our game dramatically. We added a couple of optimizations as well as a few fixes. For the first time in a while, I (Dan) feel proud to show off the game.
Slow Motion Debugging. We came across a function game_set_speed that let’s us toggle the framerate at which the game is running. Before, we’d change this value in the game settings if we needed to see if animations we’re running correctly / at the correct speeds / etc. However, running at 2 frames per second made this very cumbersome to test. With this find, we can toggle the framerate at the moment we need to!
Fix Playback Input. With the updates to our path finding, we broke quite a bit with the input recorder / playback. We finally got around to fixing this (again). It was really cool to “watch” someone play through the tutorial without having to sit next to them!
Made Picking Up “A Too Heavy Object” Not as Punishable. One thing that was frustrating me and other players was that if A were pressed to try to pick up a dropped item, and instead we were close enough to an enemy, we’d try to pick them up instead! The game would not let the player move until the “too heavy” animation was done. For really heavy enemies, this could last a couple of seconds, where the player would then get punished. We made this so the player could “interrupt early” by simply moving during any “too heavy” animations. That way if an accidental A press occurs, they won’t be punished by it.
These past couple months have seen many art updates, font cleanup, stability fixes and performance increases. We’re excited to showcase the highlights below.
At the beginning of 2023, we began coming up with a few designs for the newest member of the Soldier enemies, the Minotaur. Tony, as always, comes up with several great designs that we then have to decide which fits best with the game, story, etc. Below are a few of the initial designs:
Without going into all of our rationale, we decided to go with the head of the second design and the body of the first design. This yielded the fifth design:
After some further tweaking, we ended up with our final design:
The Minotaur has more HP and strength compared to the Orc and the Reptile. To illustrate this from a gameplay perspective, we decided to make the Minotaur significantly larger. We originally went with 1.5 times larger, but that seemed a bit large comparatively. We also tried 1.25 times larger, but that seemed a bit small. Therefore, at about 1.33 times, the Minotaur stands tall at 42 pixels (our hero is 32 pixels for reference (~5’9″)). We also wanted to showcase the strength with its intimidating walk cycle, as shown below:
December found us finishing up some Reptile animations with the bow and two-handed walk cycle. However, as we were preparing the Minotaur, we had realized we forgot to do idle animations for the reptile. Below are those animations:
We also decided to touch up the cliff walls a bit, as well as add a few palette updates for the cliffs in different biomes shown below:
We finally decided to give an overhaul to the targeting system, both artistically and functionally. Functionally, we added a “pause” concept that would only change the targetable enemies every one second, or when the player made a selection. Even though the computer could calculate accurately which targets were “next”, having them change so rapidly made it really hard for the player to choose. This delay or pause concept really helps in selecting.
Visually, we added three states:
Light blue bracket — the “next” target if we quickly let go of L-Trigger and press it again
Yellow bracket — the current target we are locked on to
Directional arrows — if we press the R-Stick in the direction of the arrow, that enemy will become the “next” target
Below is an illustration of what this looks like:
You may have also noticed in the above screenshots that we moved the HUD elements. We haven’t officially decided if we like this, but moving these elements to the corners of the screen really helps with the vertical screen real-estate.
You might have noticed in the above screenshots that the font is much different than before. We overhauled the font systems and used / touched up some elegant pixel art fonts, as showcased below:
Other Notable Updates
Path Finding Part Two
In our previous post, we explained our problems with the path finding / grid system native to Game Maker and our current solution of using our own grid system. By using our own, faster grid system, we also had to use our own implementation of A*, which essentially made us break even with the performance gains (because Game Maker’s mp_grid_path function is impossibly fast). However, we hinted at our backburner task of rolling out the path finding (A*) on a different thread. Game Maker Studio has the ability to pull in from external resources, and we finally got around to offloading the path finding in C++ as well as on a different thread (spoiler, this gave us extreme performance boosts).
I (Dan) finally had a few consecutive days off after Christmas where I was able to dedicate 100% of my time with no interruptions learning how to make external DLLs that Game Maker Studio could then import and make calls out too. The journey was tough — and honestly, I thought it was going to take longer to figure out than it did. Most of our learnings came from these forum posts / guides:
We ended up going with Visual Studio instead of Eclipse, mainly because we already had Visual Studio installed for Game Maker’s YYC compiler. After about the first day, we had a simple “Hello World” program made where we were able to talk to Game Maker and back to the DLL. Day 1 and 2 also saw us converting our A* from GML to C++. I haven’t touched C++ since my college days, so it took a little bit of time to remember how good ole pointers work. After about Day 2, the A* algorithm was able to compute within a main program within the DLL (meaning, it was converted successfully).
Day 3 was the trickiest as we had to convert our aforementioned grid system of the previous blog post to buffers. The only way to send large amounts of data to a DLL is to send the memory address (pointer) of the buffer from Game Maker. Therefore, we had to convert our ds_grids to buffers. Even though this was a pain, buffers are not only faster in Game Maker, but the size in memory was about half, so we inadvertently gained some performance. Day 3 also saw us learning how to use threading properly in C++. Even though native C++ is faster than GML, it still didn’t really make since for us to spend all of these new CPU cycles sending to the DLL if we were still running the A* on the same / main thread as the game. Therefore, we had to figure out how to implement C++ threads.
There were two threading libraries we looked at. The first was pthread, which we ultimately scrapped as it is meant for Unix computers. Though there are ways to make it work on Windows (the eclipse link / we had it kind of working at one point in Visual Studio by importing the pthread library, but eventually gave up) we finally went with thread, which seems like it is native to C++ now. After a few hours of experimenting, we were able to get threads working within C++.
By the fourth day, we were able to successfully send the grid data to the DLL, create a thread, compute the A* path and send the path data back to Game Maker! When I first got the logs of the path data in Game Maker, I screamed at the top of my lungs and scared my wife! A few minutes later, we were able to convert that data so that our Soldier enemies could move again! We also were able to figure out ways to cancel computations / threads in the DLL. For example, if an Orc dies while computing its path, we no longer need to compute the path anymore, since its dead.
Seeing the fruits of this labor paid of majorly. We’re even thinking of moving our boid enemies (Globs, Bats, Violets) so that the computation is also threaded / asynchronous, as we eventually want tens of Violets on screen.
If we recall from previous posts, as our hero / camera moves from one location to another, we load in zones that are slightly off screen the direction we are moving and unload zones that are slightly off screen in the opposite direction. If the whole world was computing all at once, our game’s speed would crawl. In the spirit of async, we decided to make our loading / unloading of zones “asynchronous”. We put asynchronous in quotes because, unlike the aforementioned A* which is truly asynchronous, the loading and unloading of zones is spread (vs. done all at once) amongst several frames. For example, if we need to load in 4 zones, and it has taken more than 500ms to do so, we tell that system “hey, you’re taking a long time. Let’s continue loading the next frame”. So, instead of loading 4 zones in 2000ms (remember, a frame should compute and draw in ~16000ms), we load 1 zone each frame for about 500ms. Since these zones are slightly off screen, there is still no need to worry about a jarring “pop in” effect. By offloading this task to several frames of the game, we are able to prevent stutter slow.
Other Stability Increases
We also improved the frame rate of the game by removing a few particle effects that we were planning on replacing anyway. For example, blood was a particle effect that we don’t believe was getting removed properly. Recently, if an enemy dies, its body remains until the player unloads the zone and then comes back to that zone a few minutes later. However, if the player never returns back to the zone, the body and its particle system remained in memory until the game ended. By removing the particle (we’re eventually going to create a system for handling dead bodies / other objects that linger around like this), this in turned freed up memory, which in turn, frees up the CPU.
We also improved the draw_container routine for the inventory. All other draw_container calls cached the container that is being drawn, except the inventory screen. This was mainly because refactoring this was going to take a bit of time and was easier to just leave alone. However, keeping the inventory from redrawing the container each frame would create hundreds of structs per frame, which would cause memory to spike. Game Maker’s automatic garbage would eventually clean this. However, the amount of time garbage collection has taken on Game Maker’s part has become quite burdensome for Violet recently. Essentially, there have been stutter slow downs, similar to the aforementioned loading / unloading of zones. However, unlike loading / unloading of zones, we don’t control Game Maker’s automatic garbage collector. Therefore, anything we can do to help avoid unnecessary structs / memory on our part we’ve noticed helps keep Game Maker’s garbage collector more stable.
Weapon Degrading / Capacity / Shards
On Christmas Eve, I (Dan) was playing an old build from 2019. I was mainly doing this just to see how far the game had progressed. While playing, I realized a mechanic that functioned differently than it does now. When a weapon degraded, it only moved down by 1 damage point. Though there was nothing in 2019 indicating that it had degraded, it was interesting to play the game with weapons that slowly degraded vs. now where degrades go down a full letter (therefore, like 20 damage points). We began thinking “what if we degraded by a smaller amount”?
We decided to try this theory and liked this so much better. Now, instead of a C ranked weapon dropping to a D, it goes from C to C-, C- to D+ and then D+ to D. We feel this really helps with the damage output in mid-battle. However, with degrading only going down by thirds, the amount of weapon shards (which can be used to increase weapon capacity) had not changed, being way more abundant. We decided to decrease this, as well as decrease the amount hits it takes to degrade a weapon. Therefore, the degrading happens more (but the damage output is not cut has bad), which yield more weapon shards (but not too abundant). With these two new systems at play, we also decided to make starting inventory capacity for each type 2. With more weapon shards, it becomes pretty easy to increase weapon capacity. With the primary mechanic of merging degraded weapons together for better weapons, we feel like we’ve made better a really interesting game loop with the weapon system.
It’s been a bit longer between posts than we were hoping for. I (Dan) usually write these posts on the weekends. But here in development land and in the real world, it has been pretty busy. A couple of quick stories before we proceed:
Two weekends ago, my computer started micro stuttering the game weirdly. At first I thought it might have been something to do with my graphics card. However, after some testing, it was concluded that my hard drive was starting to go bad. More on this story later.
We were able to show the game to some familiar faces who hadn’t seen the game in a couple years, as well as new faces who were seeing the game for the first time. The feedback was amazing as, for the first time, we were able to see a lot of the game mechanics for others actually come together and work as intended. This was really exciting as it reinvigorated us and reinforced that we have something good here!
The good news in the extra time between posts means there is a bit more to show!
The title of this post is two-fold. First, there were several concepts and functionalities that we experimented, or, “horsed around” with. Second, of course, the horse!
The prototype horse has been in the game for a couple of years now. It’s hidden away in a barn and we usually don’t tell players about it and let them discover it on their own. For those players that do discover the horse, they absolutely love it (even in its terrible, prototype state). We decided to have Tony create a walk and gallop animation for us to test ideas and concepts out with. Below are those animations:
The animations feel very good in the game. However, one of the reasons we haven’t gone “full force” into the horse is of course the additions of all the new different kinds of animations we would need. We had an idea to potentially save time by cropping out the bottom half of our hero’s animations and replace them with a sitting / riding animation. However, this idea in practice is probably not going to end up working. We’re still “horsing around” with ways to make animations go quickly as we know everyone loves the horse. However, we still have a lot of other art / animations to get done before our Kickstarter goal and reworking current animations just to ride a horse may not be time best spent.
We also have a few more animations to share about our good friend (rather, enemy), the Reptile, that we introduced in our previous post:
It’s been long overdue, but at the beginning of July, Noah Flack began replacing many of the old, crusty placeholder sound effects with some new, polished sound effects. We started down the road of creating sound effects from scratch, but it seemed more feasible to use a library of sounds and update / alter them according to our needs. The library we are using in their licensing agreement has a restriction of “redistributing” these sound effects elsewhere. Though this post isn’t intended to be a place where we are redistributing sounds (as well as many of these are heavily modified and would be unrecognizable from the original source), we felt it would be safer not to showcase any of those sounds from that library. However, we do have a couple of sound effects that we created from scratch. Here are a few below:
Knight Ghost Hit
Knight Ghost Death
Raining Outside Perspective
Raining Inside Perspective
UI / UX Updates
There was a reason why we wanted to tell the stories at the beginning of the post that we did, and here is the first reason. While watching folks play the game, we noticed a few reoccurring behaviors that needed to be addressed:
1. Players had a hard time seeing how much damage they were taking in the “heart bar” in the top left of the screen. Though Violet’s iframes are intentionally small, we still found a way to communicate how much damage the player was taking by making an animation of the heart breaking. The more damage / combo damage that happens, the longer the delay for the start heart animation to playback.
2. In the same vain, players had a hard time seeing how much health they recovered when eating a recovery item. Now granted, we still don’t have an eating animation (which we plan to add). However, adding a heart animation that shows the player how much health they’ve recovered should help with this problem. The delay of the start of the animation works similarly to taking damage, in that the more health / combo health recovered will delay the start of the animation.
3. We’ve also observed many folks in the colder regions wonder why they began taking damage. After the initial cold damage popup (which players may forget or ignore), there was no way of knowing why the player was taking damage other than seeing the temperature gauge down in the bottom right. However, before these updates, the needle was very small and hard to see. We’ve addressed this with a few things:
Made the needle bigger as well as change colors the closer it gets to the cold / warm zones
When taking cold damage, use a blue color instead of red
When taking heat damage, use a red color (like before)
When taking any other damage, use a violet color instead of red
When taking cold or heat damage, make the gauge turn colors and have a cold / heat animation playback
In cold / warm zones, shake the temperature gauge a little to grab the players attention
We ended up reworking doorframes a bit. We noticed when going through a doorframe with a weapon that the weapon was drawn in the wrong layer. This is because all we were doing to sell the illusion that our hero was going through a doorframe was turning the visible property on and off at the right times. This was a bit hacky and we went back and redid the way this works so that doorframes / doorways are on the appropriate layer now:
Other Notable Updates
At the crux of our Soldier AI is Game Maker’s path finding algorithm. In hindsight, Game Maker’s mp_grid_path function is impossibly fast (seriously, if anyone knows how this is implemented, hit us up) — which is unfortunate because our problem wasn’t with this function. Game Maker has two requirements for creating paths:
Grids, which is essentially “the world” made up of walls and not walls.
The aforementioned mp_grid_path function, which takes a grid and generates a path, most likely using an extremely optimized version of the A* algorithm.
The problem we were encountering was the time it takes to create grids. Though paths are extremely fast to compute, grids are on the slower side (perhaps Game Maker pre-optimizes the grids, which is why paths are so fast? We’re still not sure). We’ll explain why this is a problem soon, but we really need a solution from Game Maker described in this Reddit post.
The way Game Maker Studio encourages developers to use these functions is to create a “world grid” one time and be done. For the average developer using Game Maker’s mp_grid_* functions, this would be more than adequate. However, in our case, our world is huge and each enemy has a slightly unique grid. We’ve still been able to make great use of these functions though by doing a few workarounds.
To get around these problems, each Soldier enemy would create their grid based on the “chunk” of the world they resided in, as well as a few trade secrets. In the above link to the Reddit post, since there wasn’t a simple mp_grid_copy function, we were manually creating grids every time we needed them (smarter enemies would make a grid every 30 frames for example). This was costly on the CPU. We had several tricks to make this less intensive, such as offsetting each enemy to create their grid, essentially preventing multiple grids from being created on every frame. But this trick can only go so far.
One other problem we were wanting to solve for is the built in path finding deals with points, where our enemies and their hitboxes are bigger than one pixel (essentially wanting clearance-based path finding). It wasn’t a big deal until recently when we discovered our Centaur enemy, with its big hitboxes, kept running into the wall on a tight turn, and not able to clear it. With this problem and all the other aforementioned problems, we decided to roll out our own grid and pathing implementation.
We knew we needed to update the path finding algorithm itself, but the first thing we focused on was creating fast grids. Since we were able to roll out whatever solution we could build, we wanted to solve three things with our implementation of grids:
Have a “master” grid that all enemy / etc. grids would inherit from.
Only update enemy grids with necessary updates (i.e. only update when needed, don’t update everything every n frames).
It needs to be fast.
It took many nights and weekends to solve for this, but the solution we came up with for grids is ten times as fast as Game Maker’s grids!
We even solved a bonus problem introduced by our layerDepth system: instead of one grid per enemy, it was however many layerDepths there were per enemy. We ended up solving this problem with our friend, binary! Our grids represented a wall as 1 and a walkable tile as 0. We could express walls in layerDepth0 as simply 1 (0001). In layerDepth1, we would express walls as 2 (0010). But, what if we had a wall in both layerDepth0 and 1? We could express walls as 3 (0011). When we needed to know if a cell in a particular layerDepth had a wall, we could simply pull out the bits of a number and determine whether a wall was there or not.
Our last task was to actually implement our own variation of A* and clearance based path finding. Rolling this out in pure Game Maker has actually been more of a hindrance than a help. With the performance gains we’ve obtained from the grids, we’ve essentially lost that and then some with our implementation of A*. This isn’t because our implementation is bad — it’s actually really clever all things considering. It’s simply that GML is a language above C++, and the further we get from writing code in native 1‘s and 0‘s, the slower the code becomes.
We did implement one trick with our A* which essentially only computes an enemies path for 100ms for a given frame. If it takes longer, we store the computations its made and move on until the next frame, where we continue from where we left off. It’s essentially fake threading and is a good compromise for now. At the very least, we “break even” with our net performance gains / losses from all of this.
Where do we go from here? Well, one of the backburner tasks we’ve been wanting to implement is rolling out path finding on a different thread. Game Maker Studio has the ability to pull in from external resources, and this is the next logical step for us. Offloading the path finding in C++, as well as on a different thread is going to give us extreme performance boosts — especially with our Soldier AI being so integral to the game behind-the-scenes.
Another update we had to do was in regards to our build scripts. Game Maker 2022.8 introduced TileCompressedData to the IDE, which increases build times by tenfold. Before, Game Maker Studio did not compress tile data for rooms, which made the file size for larger rooms with many tiles in the megabytes. When doing builds, Game Maker would do comparison with these rather large room files, taking additional time for each build. So what’s the problem — this seems like a great thing?! Well, our build scripts were built with the uncompressed tile data, so we had to account for that. This was a weekend project, but we were able to reverse engineer how Game Maker Studio stores its TileCompressedData. That code is available here in our GMS-Tasks helper project.
Recorder / Playback Input
I bet you’re still wondering about the juicy details of what happened to my computer’s hard drive and what caused the micro stuttering? Well, if we recall back in December of 2020 we introduced a dev feature called “Playback Input” which lets us record all of the inputs we make and if something crashes, we can “playback” those inputs exactly to get the exact crash. Well the way we were handling this was less than optimal. The reason for this is not because we are incompetent, but at the time, Game Maker did not have a means of handling crashes. For example, if the game crashed, there was no way to “save” our input recorder since there was no way of handling unexpected crashes. Our hacky solution around this was to write out one file every frame of the game the input the player made. That way if the game crashed, we were able to “glue” those files together to create one big file.
The problem is writing 60 files every second while the game plays for the past two years has been taxing on my hard drive. Out-of-the blue one fine Saturday morning, our game finally had enough and began running terribly slow. I didn’t realize at the time that the problem was the Input Recorder. My brother-in-law was in town and as I was explaining the issue, he happened to noticed the SSD in task manager was maxing out at 100%. Once I remembered we were writing those files every frame, I turned the setting off and the game played normally again. After testing on my laptop, confirming no one else was having issues, and reinstalling Windows, that’s when I knew I was beginning to see a hardware failure.
Good news in all of this is I decided to buy the AMD Ryzen 5700x processor and the Corsair MP600 1TB M.2 drive. The processor cuts build times IN HALF and I’ve seen about 20% increase in build times with the faster read times on the M.2 drive. The other good news in all of this is unbeknownst to me, Game Maker Studio added the exception_unhandled_handler which lets us handle unexpected crashes gracefully. This lets us, for example, save out our input recorder, as one, sane normal file! All of that to say, we needed to update our Recorder Input to handle writing just one file and our Playback Input to read in this one file. Hopefully we won’t have to worry about future drive issues now 😛
Since our last post, there were a lot of concurrent systems that got resolved that came together to form what felt like some of the most productive months in a while! On top of that, we have quite a few animations to show off — mostly centered around the Reptile. Without further ado, let’s get into it!
The Reptile is another one of our soldier enemies. The Reptile has very fast, unpredictable movement. Tony Wojnar has really captured the chaotic nature of this guy and it has been fun seeing it come to life.
Walking / Running Weapon Animations
The next major addition from our art department is walking / running weapon animations. Actually, this was a joint effort between programming and art! Currently there are 12 different types of walking animations for each soldier enemy, as well as our hero. Plus, each of the aforementioned have four run animations and four stand frames. Multiply this for each weapon type and the number of unique animations would spiral out of control real fast. We ended up coming up with an idea where each weapon, for each direction, has a couple of reusable still frames to choose from. Then, programmatically, we specify the exact frame and position for our enemies / hero. We save on the number of assets, but we do have to tediously figure this out by hand. Luckily, this is the type of work I like to do on vacation during travel / downtime, so this worked out perfectly. Tony concepted out a few of these, so below are what we’d see in game programmatically:
We did run into a problem though. One of the big things we’ve been conveying from a gameplay standpoint is the difference between balanced and agile / heavy weapons is the fact that balanced are one-handed weapons (that can be used with a shield) and agile / heavy weapons are two handed weapons (that cannot be used with a shield). Unfortunately all of our current walking animations assumed one-handed animations. We tried to “cheat” with an example animation below:
However, it didn’t quiet feel right. We ended up resolving this by adding two-handed stand, walk and run animations. Luckily we could reuse a lot of the base animations and change the positions of the hands to make holding a two-handed weapon work. Below are a few examples of all of this coming together (again, positioning programmatically, these are just references):
Other Notable Updates
As mentioned at the beginning of the post, we’ve made a lot of progress / resolved a lot of work on concurrent systems that have been in development for several months now. The biggest of which was the layerDepth system. The layerDepth system was introduced back in 2019 when we added bridges. The simplest way to convey this system is if we are standing on a bridge, layerDepth is most likely 1, and if we are supposed to be under the bridge, layerDepth is most likely 0. As mentioned in the linked post back as early as 2019, this system worked well for the hero, but applying this to all objects ended up reworking a lot.
Now, we didn’t just work on this system for three years (which we hope is obvious by the other posts on our blog 🙂 ), but integrating with all of the other systems took a bit of work. Not only that, but when we discovered that the collision system we built originally was not going to handle layerDepth very optimally, we decided to update other systems (e.g. which is one of the reasons the Tile Object was introduced, to help speed up the collision system). We’ve been talking on an off about all of these systems for a while now, but the final frontier was making all of this work with enemies, weapons, and other interactable objects.
Some interactable objects were pretty easy to update while others (like our soldier enemy) were a bit more involved. For example, we had to update the path finding algorithm to account for different layerDepths. This took a bit of reworking, especially around the transitions areas, which we called rampTile. RampTiles used to be single individual tiles, but we ended up merging that with our Tile Object system for optimizations. As a reminder, RampTiles are essentially the triggers for switching an object’s layerDepth. So, for an enemy to find a point in a different layerDepth, the enemy first needs to find the nearest ramp to toggle the enemy’s layerDepth, then the enemy needs to find the best path to that ramp, and finally, the enemy needs to compute the best path to the target. Whew!
That was a more complicated interaction to figure out. Below are a few other interactions we had to update and fix to make the layerDepth system work:
Updating an enemy’s “seeing” and “hearing” logic (i.e. enemies can normally see targets in different layerDepth, unless the target is under a bridge, for example)
Determining how collisions should behave with different layerDepths / trigger points / RampTiles
Updating the Renderable system to draw objects correctly
Updating the Buildingable system to determine whether an enemy is inside / outside and accounting its layerDepth
If an enemy / hero is using a weapon or holding something, determining how that interaction accounts for layerDepth, as well as interacting with the Buildingable system
These bullet points are not an exhaustive list either! As you can see, there were A LOT of complicated systems converging! Of course, we’re now play testing the game again and encountering some bugs / edge cases / minor issues (which is normal with a system as complex as this). However, we’re extremely proud of how well it all came together!
Finally, here’s a list of some other important updates we’ve completed:
Sound Effects / Pitching
We are in the beginning stages of updating our old, crusty sound effects with some new ones. With new sound effects came the need to pitch the base sound effect randomly when needed. Therefore, we did two things:
Figured out how to take Game Maker Studio’s pitch functions and use them with industry terms, such as octaves, semitones and cents.
Added a string definition (for easier reading, not optimally performance-wise) to indicate how we want to randomize these (e.g. snd_SlashBalanced: "semitone:-1..1;1" would take the balanced sound effect and give us three pitches relative to the base, between -1 semitone and 1 semitone (0 would be no change, so really giving us two “new” pitches)).
Transitions of Regions
As an example, the transitions from the green grasslands to the lower mountains was a pretty hard line. Now we’ve added some softer / smoother transitions into regions. This will be expanded on once all of the art for backgrounds has been finalized, but that will be a long ways out. Below are a couple of screenshots:
Updated Long Grass Tiles
We also updated the art for the long grass tiles. Below are a couple of screenshots:
When cutting the grass tiles, there was no concept of changing the neighbor tiles to visually look correct. This was quite the undertaking to understand how auto-tile systems work. Before we actually understood it, we were hardcoding different values with this cheat-sheet:
However, we soon recognized a pattern. We’ll try to keep this brief and simple, but it does deal with binary, so hold on to your seat!
The first tile from the cheatsheet, 255 in binary is 11111111. This tile has 8 neighbors, because it is a full tile. Each of the 1 represent a neighbor. The last tile from the cheatsheet, 0 in binary is 00000000. This tile has 0 neighbors, because it is an empty tile. Each of the 0 represent a neighbor.
So, for example, if we wanted to represent a tile visually with a left neighbor only, 8 would be that number, and could be represented by 0001000. Here is a visual: 000 1 0 000
So, if we wanted to represent a tile visually, with a left and right neighbor, visually it would look like: 000 1 1 000
The binary string would be 00011000, which in our normal system would be the number 24.
Projectiles Shot Off Cliffs
For a while, we’ve had the ability to shoot projectiles / throw objects over cliffs. However, with the introduction of the z, zPseudo, we were trying to figure out how the shadow should be drawn relative to going over a cliff. We spent a couple of weeks playing with different approaches:
When the object landed, continue bouncing / falling off the cliff
Increasing the z when moving over a cliff top and then decreasing by the same amount after moving past another cliff top of the same height
We ended up settling on keeping everything the same, as the fake 3/4‘s perspective didn’t seem to make it feel off in anyway. However, we added a new construct that when a projectile lands within the cliff, if something broke from the projectile (i.e. rock breaks and a treasure came out) that this treasure will fall to the bottom of the cliff. That way, these types of things won’t get stuck in the cliffs for the player to never be able to collect.
We’ve been trying to get away from the “Art Updates Part n” titles to be a bit more descriptive with our recent posts. But this update could very well be a continuation of those parts with the hodgepodge of art and functionality that were updated the last couple of months. They say the best way to explain is to show, not tell. So without further ado, let’s show and not tell:
Updated Weapon Icons
When picking up a weapon, or seeing it in our inventory, a different graphic is represented than the graphic used for combat. We’ve updated all the icons at 4 different resolutions: the smallest for the HUD, 1x for normal gameplay, 2x for the inventory and 3x for seeing the item in descriptive dialog page. Our artist, Tony, had to draw out each of these essentially four times — it was a blast 😉 Below are a few of these icons at the highest resolution:
Docks and Bridges
Below are a couple of screenshots in the northern mountains with the newly updated bridge and docks graphics:
Bow and Arrow Animations
As simple as the bow an arrow animations look, there were quite a bit of updates from a functionality standpoint that we had do in order to pull all of this off in game. In the Other Notable Updates section, we’ll go into more detail about all of that. Below are a few animations for the hero and Orc:
Hero Stun and Death Animations
We also decided it was finally time to have our hero get hurt and die:
Pickup / Throw / Setback Down
We also added all the animations associated with picking up and throwing objects. Similar to the bow and arrow, we also had to add walking animations for holding and running. Below are a few updated animations:
Other Notable Updates
Last time we introduced a new system called Renderable, which in summary, gives us a better way of handling shadows and the z axis. There’s been a lot of work behind-the-scenes on improving on / expanding this system further. For example, we have added on to this system by giving objects a bouncing property. There are a few updates that are not quite yet done, but it’s progressing well nonetheless.
We mentioned earlier in this post that there were updates we had to make for the bow to work properly. For as long as the bow-and-arrow mechanic have been in the game, walking / running while holding the bow has always been a still image. This worked fine for prototyping purposes. Once the pull-back the bow string animation is complete, the object goes into a hold state of the bow until the arrow is released. With our current system we can be in a stand, walking or running state while holding the bow, and the new animations work great!
However there was one problem with the current system — the pull-back of the bow string animation. This animation is about a second long, which without moving the feet, made our hero seem like he was floating. We did not want to remove the mechanic of being able to walk / run while equipping (i.e. the bow string animation), and we did not want to add a pull-back animation for each different combinations of walking / running directions we could do. Therefore, we got creative with the animation and decided to shuffle the feet when doing the pull-back of the bow string animation. This seemed to solve the problem for the most part, and works well with transitioning into the walk / run while holding bow animations!
The rest of the updates, including the updates to the bow animations, are pretty simple and can be summarized in a quick bulleted list:
We updated our soldier enemies to account for the new bow animations. Mostly, we wanted to make sure that once a soldier enemy had committed to telegraphing which direction they were facing to shoot, that they couldn’t change directions until the animation had played out completely.
Speaking of telegraphing animations, we finally got around to updating the soldier enemy to telegraph its attacks. This essentially amounts to pausing on a specific wind up frame for a given amount of time (easier enemies pause longer than harder enemies).
Last time we also mentioned how we allow the player to merge anywhere instead of going back to the town to merge weapons. This new feature is great, but was definitely becoming a tedious chore after a while. Therefore, by pressing a button in the inventory screen, we can simply “auto merge” our weapons to automatically rank them up!
Finally, we’ve had our hero be able to pickup and throw things for a while. However, we also needed the ability to set objects back down without them breaking (so we can do the infamous set an object on a switch puzzle). We updated the holdable system so that they can be setback down (as well as concurrently getting the animations to make this look right at the same time!).
It’s been a while since we shared our plans about Violet from a logistical front. Obviously we’ve been making a lot of progress with art and functionality, but there’s been a lingering question: “when are we going to do Kickstarter”? We haven’t revealed the when for a while in case what we shared was premature and then to go back with our tail between our legs. However, in 2019 we wrote a post attempting to explain the current plan at that point in time, and we believe we’re long overdue to share an update.
This past year, Tony and I have been trying to shoot for a date of February 2023. Recently though, after realizing just how much art and animations we still need to do to make the demo “the best it can be”, we decided to move that back to an April 2023 window. This gave me some time to reflect on what all I need to actually get done before then.
Meanwhile, the adviser I met with back in 2019 just so happened to reach out to me to see how the game was coming. It was crazy as I was considering reaching out as well to get some feedback and opinions. We setup a time to meet and during that week of prep, I had this uneasy feeling about all the work that still needed to be done if attempting to do a Kickstarter by April, 2023.
There’s a long list of improvements, features, lore implementations, demo direction, among other things that need to be done. If this project was a full time job, this list would be a piece of cake to complete. However, this game is a project that I do on nights and weekends which makes this deadline seem daunting. Therefore, I started having an uneasy feeling about the April 2023 deadline. As I was struggling with that uneasy feeling, I kept dreaming of the opportunity where I could do this project full time, as that has been the long term goal. As the game and its systems continue to grow and become more complex, it gets harder to revisit those systems to fix bugs / make improvements / etc. And I know that eventually, nights and weekends just won’t cut it.
Nevertheless, the adviser and I had our meeting. The first half of the meeting was showing the progress of Violet since 2019, which if you have been following along, you’ll know it’s been night and day. The second half of the meeting was to get some advice and opinions on what to do next on the logistical front. The main information I was hoping to obtain was three things: “when is a good time to do a Kickstarter”, “what should I prioritize before then” and “what is a reasonable goal number to reach”.
The when to do a Kickstarter for the first question aligned with what I had originally thought: sometime in the months of February – April. “So far so good” I thought. The advice I received for the second question wasn’t as daunting as I thought it would be. There were several tips and tricks we could do to pull off a April 2023 window. This had me excited as the fantasy was starting to become a reality. However, when I had mentioned my goal amount versus the expectation, the actuality set in.
With the uneasy feeling I was already having about trying to meet the April 2023 window, and then the news about how much to money to statistically expect to receive, I unofficially decided to pivot from the April 2023 window. This was not an easy decision, especially because doing this project full time is something I have been hoping to do for a while now. However, even though the systems of the game continue to get more complex, I still am able to make progress during nights and weekends — even if it’s not at the pace I always want.
As I have been writing these posts, it’s been awesome to go back to previous posts whenever I feel discouraged to see the progress the game has made. Back in 2019 we wrote a post attempting to explain the current plan at that point in time. I actually reread this post and this quote still can’t be more true:
…If the game became a “chore” or another “job” at this point in time, I would not have the motivation to fight for this project like I want to. I do eventually want to make this something I do full time, but not by sacrificing myself mentally in the meantime.
The biggest thing, even now in 2022, is making sure that the game doesn’t become a chore or a job while I have a full time job. If I knew pushing for a year meant getting to a point where I could do this full time, then I would definitely go for it. However, pushing to get this game across a finish line to only receive a fraction of the amount I would really need to fund this project full time is not worth it to me. And as it currently stands, I am able to fund different aspects of the game (art, sound, etc.) out of my own pocket, albeit at a much slower pace.
As I was accepting this decision, Nintendo announced that The Legend of Zelda: Breath of the Wild Sequel had been pushed back to spring 2023. If the signs weren’t already clear to me to wait on doing a Kickstarter, this was the final confirmation I needed. Even though Violet would “compete” in a much different way than one of the most anticipated AAA sequels by Nintendo, running a Kickstarter for a game with many similarities would be foolish, let alone, most likely getting absorbed by the hype surrounding Zelda.
So if not 2023, then when? And if I can continue the fund the game out of my own pocket, why do a Kickstarter at all? Those are very good questions and I am glad you asked. 😀
I do think it is wise in setting some “target date” as otherwise, iteration of Violet will go on forever. At some point, we need to show the game to the general public to get some reception and feedback — and what better way to do it than a Kickstarter where we can hopefully get future fans invested in the project. After all, we’re not just making this game for ourselves. And we are not Nintendo, where we can drop a trailer, and that alone can drive buzz and hype for months. Therefore, we are now targeting a date of February 2024.
Meanwhile, another two years “cooking in the oven” is a lot of time to make progress on the development front, as well as the art and sound. With more of the game complete and polished, we’ll hopefully be in a better spot where that fractional amount can be doubled or maybe even tripled. To summarize, here’s something from the same post in 2019:
…I think it is wise to continue being patient, and slowly building out concepts and features at a pace that is fun and rewarding, rather than something that feels like “work”.
These past couple months we’ve been focusing on updating one of our soldier enemies, the Orc. The Orc is one of the more simpler soldier enemies in that it’s weaker, generally slower and its reaction time lacks. It’s been fun seeing this guy come to life and we can’t wait to share some of the animations here:
Other Notable Updates
In our previous post, we did not have a notable updates section, mainly because we were still in the middle of updating two major systems. Both of these systems have to do with depth, or, where the object is drawn on the screen. The default depth system in Game Maker Studio has served us well, but with the ever increasing complexity of the layer depth system, draw order gets blurry when dealing with transitions between on layer depth and another. This link explains some of the same issues we’ve been running into.
We started down the road of implementing a z-tilt shader, similar to how this blog implements it. However, we found a bug with rendering tiles. As we continue to wait for the resolution, we simply decided to update the default depth system, as well as update different places where layer depth was being triggered. For example, previously we were triggering the layer depth at the two openings of a bridge over water. We decided to make all land and bridges over water layerDepth1 and all water and inside caves layerDepth0.
When we jump off a cliff and into water, we trigger layerDepth1 to layerDepth0 and simply multiply the depth by a negative room_height. When entering a cave, there is a fade transition that sort of masks (or hides) the fact that there is a draw depth problem. This solved most of the draw depth problems for now — though it won’t solve issues like stair transitions from a lower area to an upper area. We still plan on using z-tilt in some way as that is really the only to solve for properly drawing objects with this complexity properly.
In the same vein, we’ve also been updating our drawing system (the pipeline to be more consistent) and shadow system, as well as adding a z axis. z gets complicated when we’re in a 2D space. What is z? Where should it be drawn? Where is the collision with respect to the visuals? All of these things are very hard to get right, so most of the time, we don’t use z, but something we made up called zPseudo.
zPseudo keeps the current visuals and the collision at its normal y position. Therefore, the purpose of zPseudo is to render the shadow zPseudo units down from the y position. z on the other hand renders the shadow at the current y position and renders the image of the object z units up. In 95% of our use cases, we found that using zPseudo was a better system for visuals and collisions. For example, a bat that is flying uses zPseudo casting a shadow 32 pixels from y. If the bat collides with a sword, it made more sense for the sword’s visual to overlap the bat’s visual, so that the bat would get hit. The 2.5D perspective is very odd, but our brains believe it to be true!
This system was complicated to come up with, but it’s proven to be very useful in making all of the game use shadows, heights, z positioning consistently across the board. For example, each projectile (arrow, fireball, ice particle, etc.) all implemented their own way of shadows. Now they all inherited from the parent object, Renderable! We were also able to therefore implement a consistent gravity function, giving objects a sense of falling or being dropped. The possibilities are now endless of what we can do with this system!
That last section was complicated, so thanks for sticking through our ramblings! The rest is straight forward and more fun “game” things!
Our weapon ranking system has been revamped! We used to use letters E – A, S, where E was low quality and A, S was best quality. We now added the letter F as worst quality, and added +/- rankings for each letter grade as well (except for F and S)! The reason for this was we found that merging weapons that were not the same letter grade was cumbersome. Therefore, we added the ability to merge any letter grade. We still have to merge the same weapon type though.
We also added the ability to merge anywhere instead of going back to the town to merge weapons. We found merging anywhere to be more rewarding help the player continue to adventure. With that said, we’ve had to update our merge system quite a bit. Even though we’ve made some updates to our merge system, we still find it tedious after a while to merge many weapons and will probably implement some sort of “auto merge” system.
Finally, we didn’t want to leave our town’s blacksmith without a job, so we added the ability for our blacksmith to take weapon shards and expand inventory! Weapon shards can be found when a weapon deranks. This helps the player to not feel like using a good weapon is a bad thing because they’ll be able to use those shards to make further progress within the game! Plus the higher the ranked weapon is, the more shards that will drop from a degraded weapon. Plus, we can easily remerge our lower-ranked weapon back up!
In adding our Orc, we’ve updated our soldier enemy a bit to be smarter! One thing we added was for the enemy to move out of the way of moving projectiles. Smarter enemies are better at doing this than others. We still have a lot of rework to do with the new animation updates to make the soldier enemy a bit more seamless.
We’ve merged the way our tile layer system handles the tiles so that each layer has three sub layers: the base, overlay and animated overlay. For example, what we were once calling decor tiles is now in the animated overlay. Paths on the grass tile would be in the overlay sub layer. And of course, the grass tile would be in the base sub layer.
We noticed an issue when swinging our balanced weapons up that sometimes smaller enemies would not get hit if they were between the balanced weapon’s visual and the hero. We fixed this so that the balanced weapon has a bit more collision below its normal visual.
We updated the targeting system to be a bit more accurate. Plus, using the R-Stick to move between enemies should be a bit more intuitive and better to use. Also, instead of using the “Ocarina of Time” triangles, we’ve resorted to a more simpler “triangle over the targets’ head”.
Finally, we added a system called “animation position” that let’s us move an object to a position based on the animation frame an instance is on. For example, our Ambush’s protection can now more accurately be position on the Ambush’s head for each frame of the animation.
As we have been updating friends and family about the progress of Violet, we’ve been depicting how great the new art is looking. But then, when we show the demo, the player is greeted with the old art of the Castle Town — not a very good first impression. Therefore, this past month we’ve been focusing on updating the Castle Town art. This included everything from roads, exterior / interior houses, the castle town walls and decor for inside the houses. Below are some screenshots of the updated Castle Town:
Violet has made quite a bit of progress in the last year. As we reflect on where we were at starting the year vs. where we are at now, the game is starting to actually look like a retro 2D pixel art game of the past. Tony Wojnar has really been “killing it” these past few months, and the update we are about to share today is no different.
The majority of the art updates focuses on three of our enemies in the game: Glob, Ambush and Knight Ghost. Without further ado, let’s get into art!
Our first enemy update is the Glob. They are simple, Chu Chu like enemies that move together and attack our hero when threatened. They come in the colors of green, blue, red and black. However, only green is showcased below:
Our next enemy update is the Ambush. They are Octorok like enemies that, as the name implies, “ambush” our hero by hiding under rocks and bushes. They come in the colors of green, blue, red and black. However, only green is showcased below:
Our last enemy update is the Knight Ghost. They are Wizzrobe like enemies that use element rods to attack and then quickly warp to a nearby location. They use the three element rods: fire, water and ice. These elements represent there base colors and then their strength comes in the colors of green, blue, red and black — resulting in 12 different color variations. Below are the fire / green variations:
New Element Rod Animations
We also added animations for the Fire, Ice, and Water Rod. These rods work much like agile weapons where they can be used in any direction. Unlike agile weapons, rods only have one combo to them as well as shoot an element projectile from them. Fire rods shoot a fireball that deals good damage, knocks an opponent over, and of course, creates fire that uses the normal fire properties (like spreading and burning). Water rods shoot a gust of water out of them to push enemies, and eventually objects. The force of the water can also knock enemies into a wall to deal damage. This water can also “heal” damaged plants. Ice rods shoot an ice particle out that can freeze opponents into an ice crystal, dealing freeze damage until the opponent can break free.
Below are the Knight Ghost using the different element rods, as well as the projectile and effect from these rods:
Other Notable Updates
This past month we finally merged our GMS 2.3branch into the code. This merge essentially included everything in the “Other Notable Updates” section we have mentioned in our posts the last year (as well as the new art of course). We made an official stable build we’ve been calling “The Thanksgiving Build” for friends and family to try out.
The reason for all of the optimizations, refactoring and improvements is for our objects to be able to handle collisions within different layer depths better. To recap, since our game engine only exists in two axis, we have to “fake” the third dimension of depth with a system we are calling “layer depth” — which dictates where an object is in the third dimension. We were noticing before we underwent the refactoring / optimizations that the collision system for layer depths was chugging a bit.
We did add one change to our Tile Object system (yes, that same system we’ve been discussing the past year) to support ice tiles. Since ice tiles can be created dynamically on-the-fly, we needed a way for our system to handle this type of tile creation. Before, all tiles were created before runtime. But now our Tile Object system is even more robust supporting mass ice tiles and we even were able to remove a few TODO items from that list.
With new art updates for a couple of our enemies came some code updates to handle the new art, as well as more accurately depict certain actions. The changes most notable here were in the Ambush and Knight Ghost enemy, because they share the same parent object, RandomMovement.
We also added a system for objects that have “tails”, or graphics that trail behind the parent object, giving the parent object the illusion that it has motion. Since objects like fire, water, ice, Ambush dirt, etc. all have this “tail” concept, we decided to optimize this as whenever one of these objects were spawned, the tails were getting a little expensive to process. Now, we have graphical representation of these tails instead of creating n number of objects to represent these tails.
With our new water and ice projectile graphics, we also added a couple more features around these elements. With water, if an object gets hit by the projectile, the force of the water pushes an object greatly. The new feature that we added was if the object hits a wall at a fast enough speed, it will take “crush” damage against the wall.
With ice, we added the ability to be “frozen” in ice crystals (as seen by an earlier screenshot). The enemy will be silhouetted blue against the ice crystal and taking freeze damage until they are able to break free.
Finally, we updated the weather system so that hopefully it rains a bit less. If it still continues to rain more than we’d like, the code should be a bit easier to update to reflect the weather patterns we’d like each region to have.